aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Maenan Read Cutting <readcuttingt@gmail.com>2021-06-27 15:06:08 +0100
committerTom Maenan Read Cutting <readcuttingt@gmail.com>2021-06-29 18:42:57 +0100
commit186577225f2a026fea01b48c7bcca47f36efc95c (patch)
treeadad450658e16f6f7252764aa99c85f626cadb2e /src
parent6d47b4f39e8cdd95ead71b1efdbf67a737174e97 (diff)
downloadzig-186577225f2a026fea01b48c7bcca47f36efc95c.tar.gz
zig-186577225f2a026fea01b48c7bcca47f36efc95c.zip
Add fat/universal archive support to zig ld
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>
Diffstat (limited to 'src')
-rw-r--r--src/link/MachO/Archive.zig11
-rw-r--r--src/link/MachO/Dylib.zig55
-rw-r--r--src/link/MachO/fat.zig55
3 files changed, 68 insertions, 53 deletions
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;
+ }
+}