| author | |
| committer | |
| log | a95ba0d10d582020dbd3e6efded53f17287ed211 |
| tree | 7f17ba0b98e18e6ff4c833d08a45779a5ce52d01 |
| parent | 908b431abeb1df5d3c3ad34d9299e720c3e06a9d |
| parent | 186577225f2a026fea01b48c7bcca47f36efc95c |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Add fat/universal archive support to zig ld3 files changed, 68 insertions(+), 53 deletions(-)
src/link/MachO/Archive.zig+10-1| ... | @@ -6,6 +6,7 @@ const fs = std.fs; | ... | @@ -6,6 +6,7 @@ const fs = std.fs; |
| 6 | const log = std.log.scoped(.archive); | 6 | const log = std.log.scoped(.archive); |
| 7 | const macho = std.macho; | 7 | const macho = std.macho; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
| 9 | const fat = @import("fat.zig"); | ||
| 9 | 10 | ||
| 10 | const Allocator = mem.Allocator; | 11 | const Allocator = mem.Allocator; |
| 11 | const Arch = std.Target.Cpu.Arch; | 12 | const Arch = std.Target.Cpu.Arch; |
| ... | @@ -19,6 +20,10 @@ file: ?fs.File = null, | ... | @@ -19,6 +20,10 @@ file: ?fs.File = null, |
| 19 | header: ?ar_hdr = null, | 20 | header: ?ar_hdr = null, |
| 20 | name: ?[]const u8 = null, | 21 | name: ?[]const u8 = null, |
| 21 | 22 | ||
| 23 | // The actual contents we care about linking with will be embedded at | ||
| 24 | // an offset within a file if we are linking against a fat lib | ||
| 25 | library_offset: u64 = 0, | ||
| 26 | |||
| 22 | /// Parsed table of contents. | 27 | /// Parsed table of contents. |
| 23 | /// Each symbol name points to a list of all definition | 28 | /// Each symbol name points to a list of all definition |
| 24 | /// sites within the current static archive. | 29 | /// sites within the current static archive. |
| ... | @@ -139,6 +144,10 @@ pub fn closeFile(self: Archive) void { | ... | @@ -139,6 +144,10 @@ pub fn closeFile(self: Archive) void { |
| 139 | } | 144 | } |
| 140 | 145 | ||
| 141 | pub fn parse(self: *Archive) !void { | 146 | pub fn parse(self: *Archive) !void { |
| 147 | self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); | ||
| 148 | |||
| 149 | try self.file.?.seekTo(self.library_offset); | ||
| 150 | |||
| 142 | var reader = self.file.?.reader(); | 151 | var reader = self.file.?.reader(); |
| 143 | const magic = try reader.readBytesNoEof(SARMAG); | 152 | const magic = try reader.readBytesNoEof(SARMAG); |
| 144 | 153 | ||
| ... | @@ -226,7 +235,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { | ... | @@ -226,7 +235,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { |
| 226 | /// Caller owns the Object instance. | 235 | /// Caller owns the Object instance. |
| 227 | pub fn parseObject(self: Archive, offset: u32) !*Object { | 236 | pub fn parseObject(self: Archive, offset: u32) !*Object { |
| 228 | var reader = self.file.?.reader(); | 237 | var reader = self.file.?.reader(); |
| 229 | try reader.context.seekTo(offset); | 238 | try reader.context.seekTo(offset + self.library_offset); |
| 230 | 239 | ||
| 231 | const object_header = try reader.readStruct(ar_hdr); | 240 | const object_header = try reader.readStruct(ar_hdr); |
| 232 | 241 |
src/link/MachO/Dylib.zig+3-52| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | const Dylib = @This(); | 1 | const Dylib = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const builtin = std.builtin; | ||
| 5 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 6 | const fs = std.fs; | 5 | const fs = std.fs; |
| 7 | const fmt = std.fmt; | 6 | const fmt = std.fmt; |
| ... | @@ -9,7 +8,7 @@ const log = std.log.scoped(.dylib); | ... | @@ -9,7 +8,7 @@ const log = std.log.scoped(.dylib); |
| 9 | const macho = std.macho; | 8 | const macho = std.macho; |
| 10 | const math = std.math; | 9 | const math = std.math; |
| 11 | const mem = std.mem; | 10 | const mem = std.mem; |
| 12 | const native_endian = builtin.target.cpu.arch.endian(); | 11 | const fat = @import("fat.zig"); |
| 13 | 12 | ||
| 14 | const Allocator = mem.Allocator; | 13 | const Allocator = mem.Allocator; |
| 15 | const Arch = std.Target.Cpu.Arch; | 14 | const Arch = std.Target.Cpu.Arch; |
| ... | @@ -238,42 +237,10 @@ pub fn closeFile(self: Dylib) void { | ... | @@ -238,42 +237,10 @@ pub fn closeFile(self: Dylib) void { |
| 238 | } | 237 | } |
| 239 | } | 238 | } |
| 240 | 239 | ||
| 241 | fn decodeArch(cputype: macho.cpu_type_t) !std.Target.Cpu.Arch { | ||
| 242 | const arch: Arch = switch (cputype) { | ||
| 243 | macho.CPU_TYPE_ARM64 => .aarch64, | ||
| 244 | macho.CPU_TYPE_X86_64 => .x86_64, | ||
| 245 | else => { | ||
| 246 | return error.UnsupportedCpuArchitecture; | ||
| 247 | }, | ||
| 248 | }; | ||
| 249 | return arch; | ||
| 250 | } | ||
| 251 | |||
| 252 | pub fn parse(self: *Dylib) !void { | 240 | pub fn parse(self: *Dylib) !void { |
| 253 | log.debug("parsing shared library '{s}'", .{self.name.?}); | 241 | log.debug("parsing shared library '{s}'", .{self.name.?}); |
| 254 | 242 | ||
| 255 | self.library_offset = offset: { | 243 | self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); |
| 256 | const fat_header = try readFatStruct(self.file.?.reader(), macho.fat_header); | ||
| 257 | if (fat_header.magic != macho.FAT_MAGIC) break :offset 0; | ||
| 258 | |||
| 259 | var fat_arch_index: u32 = 0; | ||
| 260 | while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { | ||
| 261 | const fat_arch = try readFatStruct(self.file.?.reader(), macho.fat_arch); | ||
| 262 | // If we come across an architecture that we do not know how to handle, that's | ||
| 263 | // fine because we can keep looking for one that might match. | ||
| 264 | const lib_arch = decodeArch(fat_arch.cputype) catch |err| switch (err) { | ||
| 265 | error.UnsupportedCpuArchitecture => continue, | ||
| 266 | else => |e| return e, | ||
| 267 | }; | ||
| 268 | if (lib_arch == self.arch.?) { | ||
| 269 | // We have found a matching architecture! | ||
| 270 | break :offset fat_arch.offset; | ||
| 271 | } | ||
| 272 | } else { | ||
| 273 | log.err("Could not find matching cpu architecture in fat library: expected {s}", .{self.arch.?}); | ||
| 274 | return error.MismatchedCpuArchitecture; | ||
| 275 | } | ||
| 276 | }; | ||
| 277 | 244 | ||
| 278 | try self.file.?.seekTo(self.library_offset); | 245 | try self.file.?.seekTo(self.library_offset); |
| 279 | 246 | ||
| ... | @@ -285,13 +252,7 @@ pub fn parse(self: *Dylib) !void { | ... | @@ -285,13 +252,7 @@ pub fn parse(self: *Dylib) !void { |
| 285 | return error.NotDylib; | 252 | return error.NotDylib; |
| 286 | } | 253 | } |
| 287 | 254 | ||
| 288 | const this_arch: Arch = decodeArch(self.header.?.cputype) catch |err| switch (err) { | 255 | const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); |
| 289 | error.UnsupportedCpuArchitecture => |e| { | ||
| 290 | log.err("unsupported cpu architecture 0x{x}", .{self.header.?.cputype}); | ||
| 291 | return e; | ||
| 292 | }, | ||
| 293 | else => |e| return e, | ||
| 294 | }; | ||
| 295 | 256 | ||
| 296 | if (this_arch != self.arch.?) { | 257 | if (this_arch != self.arch.?) { |
| 297 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); | 258 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); |
| ... | @@ -303,16 +264,6 @@ pub fn parse(self: *Dylib) !void { | ... | @@ -303,16 +264,6 @@ pub fn parse(self: *Dylib) !void { |
| 303 | try self.parseSymbols(); | 264 | try self.parseSymbols(); |
| 304 | } | 265 | } |
| 305 | 266 | ||
| 306 | fn readFatStruct(reader: anytype, comptime T: type) !T { | ||
| 307 | // Fat structures (fat_header & fat_arch) are always written and read to/from | ||
| 308 | // disk in big endian order. | ||
| 309 | var res: T = try reader.readStruct(T); | ||
| 310 | if (native_endian != builtin.Endian.Big) { | ||
| 311 | mem.bswapAllFields(T, &res); | ||
| 312 | } | ||
| 313 | return res; | ||
| 314 | } | ||
| 315 | |||
| 316 | fn readLoadCommands(self: *Dylib, reader: anytype) !void { | 267 | fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 317 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; | 268 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; |
| 318 | 269 |
src/link/MachO/fat.zig created+55| ... | @@ -0,0 +1,55 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = std.builtin; | ||
| 3 | const log = std.log.scoped(.archive); | ||
| 4 | const macho = std.macho; | ||
| 5 | const mem = std.mem; | ||
| 6 | const native_endian = builtin.target.cpu.arch.endian(); | ||
| 7 | |||
| 8 | const Arch = std.Target.Cpu.Arch; | ||
| 9 | |||
| 10 | pub fn decodeArch(cputype: macho.cpu_type_t, comptime logError: bool) !std.Target.Cpu.Arch { | ||
| 11 | const arch: Arch = switch (cputype) { | ||
| 12 | macho.CPU_TYPE_ARM64 => .aarch64, | ||
| 13 | macho.CPU_TYPE_X86_64 => .x86_64, | ||
| 14 | else => { | ||
| 15 | if (logError) { | ||
| 16 | log.err("unsupported cpu architecture 0x{x}", .{cputype}); | ||
| 17 | } | ||
| 18 | return error.UnsupportedCpuArchitecture; | ||
| 19 | }, | ||
| 20 | }; | ||
| 21 | return arch; | ||
| 22 | } | ||
| 23 | |||
| 24 | fn readFatStruct(reader: anytype, comptime T: type) !T { | ||
| 25 | // Fat structures (fat_header & fat_arch) are always written and read to/from | ||
| 26 | // disk in big endian order. | ||
| 27 | var res = try reader.readStruct(T); | ||
| 28 | if (native_endian != builtin.Endian.Big) { | ||
| 29 | mem.bswapAllFields(T, &res); | ||
| 30 | } | ||
| 31 | return res; | ||
| 32 | } | ||
| 33 | |||
| 34 | pub fn getLibraryOffset(reader: anytype, arch: Arch) !u64 { | ||
| 35 | const fat_header = try readFatStruct(reader, macho.fat_header); | ||
| 36 | if (fat_header.magic != macho.FAT_MAGIC) return 0; | ||
| 37 | |||
| 38 | var fat_arch_index: u32 = 0; | ||
| 39 | while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { | ||
| 40 | const fat_arch = try readFatStruct(reader, macho.fat_arch); | ||
| 41 | // If we come across an architecture that we do not know how to handle, that's | ||
| 42 | // fine because we can keep looking for one that might match. | ||
| 43 | const lib_arch = decodeArch(fat_arch.cputype, false) catch |err| switch (err) { | ||
| 44 | error.UnsupportedCpuArchitecture => continue, | ||
| 45 | else => |e| return e, | ||
| 46 | }; | ||
| 47 | if (lib_arch == arch) { | ||
| 48 | // We have found a matching architecture! | ||
| 49 | return fat_arch.offset; | ||
| 50 | } | ||
| 51 | } else { | ||
| 52 | log.err("Could not find matching cpu architecture in fat library: expected {s}", .{arch}); | ||
| 53 | return error.MismatchedCpuArchitecture; | ||
| 54 | } | ||
| 55 | } | ||