mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 19:23:08 +00:00
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>
This commit is contained in:
parent
6d47b4f39e
commit
186577225f
@ -6,6 +6,7 @@ const fs = std.fs;
|
|||||||
const log = std.log.scoped(.archive);
|
const log = std.log.scoped(.archive);
|
||||||
const macho = std.macho;
|
const macho = std.macho;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
const fat = @import("fat.zig");
|
||||||
|
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const Arch = std.Target.Cpu.Arch;
|
const Arch = std.Target.Cpu.Arch;
|
||||||
@ -19,6 +20,10 @@ file: ?fs.File = null,
|
|||||||
header: ?ar_hdr = null,
|
header: ?ar_hdr = null,
|
||||||
name: ?[]const u8 = 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.
|
/// Parsed table of contents.
|
||||||
/// Each symbol name points to a list of all definition
|
/// Each symbol name points to a list of all definition
|
||||||
/// sites within the current static archive.
|
/// sites within the current static archive.
|
||||||
@ -139,6 +144,10 @@ pub fn closeFile(self: Archive) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(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();
|
var reader = self.file.?.reader();
|
||||||
const magic = try reader.readBytesNoEof(SARMAG);
|
const magic = try reader.readBytesNoEof(SARMAG);
|
||||||
|
|
||||||
@ -226,7 +235,7 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void {
|
|||||||
/// Caller owns the Object instance.
|
/// Caller owns the Object instance.
|
||||||
pub fn parseObject(self: Archive, offset: u32) !*Object {
|
pub fn parseObject(self: Archive, offset: u32) !*Object {
|
||||||
var reader = self.file.?.reader();
|
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);
|
const object_header = try reader.readStruct(ar_hdr);
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
const Dylib = @This();
|
const Dylib = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = std.builtin;
|
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const fs = std.fs;
|
const fs = std.fs;
|
||||||
const fmt = std.fmt;
|
const fmt = std.fmt;
|
||||||
@ -9,7 +8,7 @@ const log = std.log.scoped(.dylib);
|
|||||||
const macho = std.macho;
|
const macho = std.macho;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
const native_endian = builtin.target.cpu.arch.endian();
|
const fat = @import("fat.zig");
|
||||||
|
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const Arch = std.Target.Cpu.Arch;
|
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 {
|
pub fn parse(self: *Dylib) !void {
|
||||||
log.debug("parsing shared library '{s}'", .{self.name.?});
|
log.debug("parsing shared library '{s}'", .{self.name.?});
|
||||||
|
|
||||||
self.library_offset = offset: {
|
self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?);
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try self.file.?.seekTo(self.library_offset);
|
try self.file.?.seekTo(self.library_offset);
|
||||||
|
|
||||||
@ -258,13 +225,7 @@ pub fn parse(self: *Dylib) !void {
|
|||||||
return error.NotDylib;
|
return error.NotDylib;
|
||||||
}
|
}
|
||||||
|
|
||||||
const this_arch: Arch = decodeArch(self.header.?.cputype) catch |err| switch (err) {
|
const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true);
|
||||||
error.UnsupportedCpuArchitecture => |e| {
|
|
||||||
log.err("unsupported cpu architecture 0x{x}", .{self.header.?.cputype});
|
|
||||||
return e;
|
|
||||||
},
|
|
||||||
else => |e| return e,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this_arch != self.arch.?) {
|
if (this_arch != self.arch.?) {
|
||||||
log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_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();
|
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 {
|
fn readLoadCommands(self: *Dylib, reader: anytype) !void {
|
||||||
try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
|
try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
|
||||||
|
|
||||||
|
|||||||
55
src/link/MachO/fat.zig
Normal file
55
src/link/MachO/fat.zig
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user