| author | |
| committer | |
| log | 186577225f2a026fea01b48c7bcca47f36efc95c |
| tree | adad450658e16f6f7252764aa99c85f626cadb2e |
| parent | 6d47b4f39e8cdd95ead71b1efdbf67a737174e97 |
This is an extension of adding fat dylib support to zig ld, pulling out
the functionality needed to support fat headers & offsets and applying
it to zig archives.
Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>3 files changed, 68 insertions(+), 53 deletions(-)
src/link/MachO/Archive.zig+10-1| ... | ... | @@ -6,6 +6,7 @@ const fs = std.fs; |
| 6 | 6 | const log = std.log.scoped(.archive); |
| 7 | 7 | const macho = std.macho; |
| 8 | 8 | const mem = std.mem; |
| 9 | const fat = @import("fat.zig"); | |
| 9 | 10 | |
| 10 | 11 | const Allocator = mem.Allocator; |
| 11 | 12 | const Arch = std.Target.Cpu.Arch; |
| ... | ... | @@ -19,6 +20,10 @@ file: ?fs.File = null, |
| 19 | 20 | header: ?ar_hdr = null, |
| 20 | 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 | 27 | /// Parsed table of contents. |
| 23 | 28 | /// Each symbol name points to a list of all definition |
| 24 | 29 | /// sites within the current static archive. |
| ... | ... | @@ -139,6 +144,10 @@ pub fn closeFile(self: Archive) void { |
| 139 | 144 | } |
| 140 | 145 | |
| 141 | 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 | 151 | var reader = self.file.?.reader(); |
| 143 | 152 | const magic = try reader.readBytesNoEof(SARMAG); |
| 144 | 153 | |
| ... | ... | @@ -226,7 +235,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { |
| 226 | 235 | /// Caller owns the Object instance. |
| 227 | 236 | pub fn parseObject(self: Archive, offset: u32) !*Object { |
| 228 | 237 | var reader = self.file.?.reader(); |
| 229 | try reader.context.seekTo(offset); | |
| 238 | try reader.context.seekTo(offset + self.library_offset); | |
| 230 | 239 | |
| 231 | 240 | const object_header = try reader.readStruct(ar_hdr); |
| 232 | 241 |
src/link/MachO/Dylib.zig+3-52| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | const Dylib = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const builtin = std.builtin; | |
| 5 | 4 | const assert = std.debug.assert; |
| 6 | 5 | const fs = std.fs; |
| 7 | 6 | const fmt = std.fmt; |
| ... | ... | @@ -9,7 +8,7 @@ const log = std.log.scoped(.dylib); |
| 9 | 8 | const macho = std.macho; |
| 10 | 9 | const math = std.math; |
| 11 | 10 | const mem = std.mem; |
| 12 | const native_endian = builtin.target.cpu.arch.endian(); | |
| 11 | const fat = @import("fat.zig"); | |
| 13 | 12 | |
| 14 | 13 | const Allocator = mem.Allocator; |
| 15 | 14 | const Arch = std.Target.Cpu.Arch; |
| ... | ... | @@ -211,42 +210,10 @@ pub fn closeFile(self: Dylib) void { |
| 211 | 210 | } |
| 212 | 211 | } |
| 213 | 212 | |
| 214 | fn decodeArch(cputype: macho.cpu_type_t) !std.Target.Cpu.Arch { | |
| 215 | const arch: Arch = switch (cputype) { | |
| 216 | macho.CPU_TYPE_ARM64 => .aarch64, | |
| 217 | macho.CPU_TYPE_X86_64 => .x86_64, | |
| 218 | else => { | |
| 219 | return error.UnsupportedCpuArchitecture; | |
| 220 | }, | |
| 221 | }; | |
| 222 | return arch; | |
| 223 | } | |
| 224 | ||
| 225 | 213 | pub fn parse(self: *Dylib) !void { |
| 226 | 214 | log.debug("parsing shared library '{s}'", .{self.name.?}); |
| 227 | 215 | |
| 228 | self.library_offset = offset: { | |
| 229 | const fat_header = try readFatStruct(self.file.?.reader(), macho.fat_header); | |
| 230 | if (fat_header.magic != macho.FAT_MAGIC) break :offset 0; | |
| 231 | ||
| 232 | var fat_arch_index: u32 = 0; | |
| 233 | while (fat_arch_index < fat_header.nfat_arch) : (fat_arch_index += 1) { | |
| 234 | const fat_arch = try readFatStruct(self.file.?.reader(), macho.fat_arch); | |
| 235 | // If we come across an architecture that we do not know how to handle, that's | |
| 236 | // fine because we can keep looking for one that might match. | |
| 237 | const lib_arch = decodeArch(fat_arch.cputype) catch |err| switch (err) { | |
| 238 | error.UnsupportedCpuArchitecture => continue, | |
| 239 | else => |e| return e, | |
| 240 | }; | |
| 241 | if (lib_arch == self.arch.?) { | |
| 242 | // We have found a matching architecture! | |
| 243 | break :offset fat_arch.offset; | |
| 244 | } | |
| 245 | } else { | |
| 246 | log.err("Could not find matching cpu architecture in fat library: expected {s}", .{self.arch.?}); | |
| 247 | return error.MismatchedCpuArchitecture; | |
| 248 | } | |
| 249 | }; | |
| 216 | self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); | |
| 250 | 217 | |
| 251 | 218 | try self.file.?.seekTo(self.library_offset); |
| 252 | 219 | |
| ... | ... | @@ -258,13 +225,7 @@ pub fn parse(self: *Dylib) !void { |
| 258 | 225 | return error.NotDylib; |
| 259 | 226 | } |
| 260 | 227 | |
| 261 | const this_arch: Arch = decodeArch(self.header.?.cputype) catch |err| switch (err) { | |
| 262 | error.UnsupportedCpuArchitecture => |e| { | |
| 263 | log.err("unsupported cpu architecture 0x{x}", .{self.header.?.cputype}); | |
| 264 | return e; | |
| 265 | }, | |
| 266 | else => |e| return e, | |
| 267 | }; | |
| 228 | const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); | |
| 268 | 229 | |
| 269 | 230 | if (this_arch != self.arch.?) { |
| 270 | 231 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); |
| ... | ... | @@ -276,16 +237,6 @@ pub fn parse(self: *Dylib) !void { |
| 276 | 237 | try self.parseSymbols(); |
| 277 | 238 | } |
| 278 | 239 | |
| 279 | fn readFatStruct(reader: anytype, comptime T: type) !T { | |
| 280 | // Fat structures (fat_header & fat_arch) are always written and read to/from | |
| 281 | // disk in big endian order. | |
| 282 | var res: T = try reader.readStruct(T); | |
| 283 | if (native_endian != builtin.Endian.Big) { | |
| 284 | mem.bswapAllFields(T, &res); | |
| 285 | } | |
| 286 | return res; | |
| 287 | } | |
| 288 | ||
| 289 | 240 | fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 290 | 241 | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); |
| 291 | 242 |
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 | } |