diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 22439d300a..8f047b4968 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -6,6 +6,7 @@ const fs = std.fs; const log = std.log.scoped(.archive); const macho = std.macho; const mem = std.mem; +const fat = @import("fat.zig"); const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; @@ -19,6 +20,10 @@ file: ?fs.File = null, header: ?ar_hdr = null, name: ?[]const u8 = null, +// The actual contents we care about linking with will be embedded at +// an offset within a file if we are linking against a fat lib +library_offset: u64 = 0, + /// Parsed table of contents. /// Each symbol name points to a list of all definition /// sites within the current static archive. @@ -139,6 +144,10 @@ pub fn closeFile(self: Archive) void { } pub fn parse(self: *Archive) !void { + self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); + + try self.file.?.seekTo(self.library_offset); + var reader = self.file.?.reader(); const magic = try reader.readBytesNoEof(SARMAG); @@ -226,7 +235,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { /// Caller owns the Object instance. pub fn parseObject(self: Archive, offset: u32) !*Object { var reader = self.file.?.reader(); - try reader.context.seekTo(offset); + try reader.context.seekTo(offset + self.library_offset); const object_header = try reader.readStruct(ar_hdr); diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 2ecd2a20ed..b1c55ba689 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -1,7 +1,6 @@ const Dylib = @This(); const std = @import("std"); -const builtin = std.builtin; const assert = std.debug.assert; const fs = std.fs; const fmt = std.fmt; @@ -9,7 +8,7 @@ const log = std.log.scoped(.dylib); const macho = std.macho; const math = std.math; const mem = std.mem; -const native_endian = builtin.target.cpu.arch.endian(); +const fat = @import("fat.zig"); const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; @@ -211,42 +210,10 @@ pub fn closeFile(self: Dylib) void { } } -fn decodeArch(cputype: macho.cpu_type_t) !std.Target.Cpu.Arch { - const arch: Arch = switch (cputype) { - macho.CPU_TYPE_ARM64 => .aarch64, - macho.CPU_TYPE_X86_64 => .x86_64, - else => { - return error.UnsupportedCpuArchitecture; - }, - }; - return arch; -} - pub fn parse(self: *Dylib) !void { log.debug("parsing shared library '{s}'", .{self.name.?}); - self.library_offset = offset: { - const fat_header = try readFatStruct(self.file.?.reader(), macho.fat_header); - if (fat_header.magic != macho.FAT_MAGIC) break :offset 0; - - var fat_arch_index: u32 = 0; - while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { - const fat_arch = try readFatStruct(self.file.?.reader(), macho.fat_arch); - // If we come across an architecture that we do not know how to handle, that's - // fine because we can keep looking for one that might match. - const lib_arch = decodeArch(fat_arch.cputype) catch |err| switch (err) { - error.UnsupportedCpuArchitecture => continue, - else => |e| return e, - }; - if (lib_arch == self.arch.?) { - // We have found a matching architecture! - break :offset fat_arch.offset; - } - } else { - log.err("Could not find matching cpu architecture in fat library: expected {s}", .{self.arch.?}); - return error.MismatchedCpuArchitecture; - } - }; + self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); try self.file.?.seekTo(self.library_offset); @@ -258,13 +225,7 @@ pub fn parse(self: *Dylib) !void { return error.NotDylib; } - const this_arch: Arch = decodeArch(self.header.?.cputype) catch |err| switch (err) { - error.UnsupportedCpuArchitecture => |e| { - log.err("unsupported cpu architecture 0x{x}", .{self.header.?.cputype}); - return e; - }, - else => |e| return e, - }; + const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); if (this_arch != self.arch.?) { log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); @@ -276,16 +237,6 @@ pub fn parse(self: *Dylib) !void { try self.parseSymbols(); } -fn readFatStruct(reader: anytype, comptime T: type) !T { - // Fat structures (fat_header & fat_arch) are always written and read to/from - // disk in big endian order. - var res: T = try reader.readStruct(T); - if (native_endian != builtin.Endian.Big) { - mem.bswapAllFields(T, &res); - } - return res; -} - fn readLoadCommands(self: *Dylib, reader: anytype) !void { try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig new file mode 100644 index 0000000000..61592c88a5 --- /dev/null +++ b/src/link/MachO/fat.zig @@ -0,0 +1,55 @@ +const std = @import("std"); +const builtin = std.builtin; +const log = std.log.scoped(.archive); +const macho = std.macho; +const mem = std.mem; +const native_endian = builtin.target.cpu.arch.endian(); + +const Arch = std.Target.Cpu.Arch; + +pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { + const arch: Arch = switch (cputype) { + macho.CPU_TYPE_ARM64 => .aarch64, + macho.CPU_TYPE_X86_64 => .x86_64, + else => { + if (logError) { + log.err("unsupported cpu architecture 0x{x}", .{cputype}); + } + return error.UnsupportedCpuArchitecture; + }, + }; + return arch; +} + +fn readFatStruct(reader: anytype, comptime T: type) !T { + // Fat structures (fat_header & fat_arch) are always written and read to/from + // disk in big endian order. + var res = try reader.readStruct(T); + if (native_endian != builtin.Endian.Big) { + mem.bswapAllFields(T, &res); + } + return res; +} + +pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { + const fat_header = try readFatStruct(reader, macho.fat_header); + if (fat_header.magic != macho.FAT_MAGIC) return 0; + + var fat_arch_index: u32 = 0; + while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { + const fat_arch = try readFatStruct(reader, macho.fat_arch); + // If we come across an architecture that we do not know how to handle, that's + // fine because we can keep looking for one that might match. + const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) { + error.UnsupportedCpuArchitecture => continue, + else => |e| return e, + }; + if (lib_arch == arch) { + // We have found a matching architecture! + return fat_arch.offset; + } + } else { + log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch}); + return error.MismatchedCpuArchitecture; + } +}