aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/link/MachO.zig1391
-rw-r--r--src/link/MachO/Archive.zig24
-rw-r--r--src/link/MachO/Atom.zig394
-rw-r--r--src/link/MachO/CodeSignature.zig364
-rw-r--r--src/link/MachO/DebugSymbols.zig46
-rw-r--r--src/link/MachO/DwarfInfo.zig28
-rw-r--r--src/link/MachO/Dylib.zig40
-rw-r--r--src/link/MachO/Object.zig155
-rw-r--r--src/link/MachO/Trie.zig484
-rw-r--r--src/link/MachO/UnwindInfo.zig140
-rw-r--r--src/link/MachO/dead_strip.zig251
-rw-r--r--src/link/MachO/dyld_info/Rebase.zig22
-rw-r--r--src/link/MachO/dyld_info/bind.zig18
-rw-r--r--src/link/MachO/eh_frame.zig127
-rw-r--r--src/link/MachO/fat.zig12
-rw-r--r--src/link/MachO/hasher.zig18
-rw-r--r--src/link/MachO/load_commands.zig20
-rw-r--r--src/link/MachO/stubs.zig10
-rw-r--r--src/link/MachO/thunks.zig169
-rw-r--r--src/link/MachO/uuid.zig18
-rw-r--r--src/link/MachO/zld.zig3430
21 files changed, 3042 insertions, 4119 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index de723639f1..ac7b4af988 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -405,9 +405,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
- parseLibrary(
- self,
- self.base.allocator,
+ self.parseLibrary(
in_file,
path,
lib,
@@ -421,24 +419,15 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
};
}
- parseDependentLibs(self, self.base.allocator, &dependent_libs, &self.base.options) catch |err| {
+ self.parseDependentLibs(&dependent_libs, &self.base.options) catch |err| {
// TODO convert to error
log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
};
}
- if (self.dyld_stub_binder_index == null) {
- self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
- }
- if (!self.base.options.single_threaded) {
- _ = try self.addUndefined("__tlv_bootstrap", .none);
- }
-
- try self.createMhExecuteHeaderSymbol();
-
var actions = std.ArrayList(ResolveAction).init(self.base.allocator);
defer actions.deinit();
- try self.resolveSymbolsInDylibs(&actions);
+ try self.resolveSymbols(&actions);
if (self.getEntryPoint() == null) {
self.error_flags.no_entry_point_found = true;
@@ -527,14 +516,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
try self.writeLinkeditSegmentData();
- const target = self.base.options.target;
- const requires_codesig = blk: {
- if (self.base.options.entitlements) |_| break :blk true;
- if (target.cpu.arch == .aarch64 and (target.os.tag == .macos or target.abi == .simulator))
- break :blk true;
- break :blk false;
- };
- var codesig: ?CodeSignature = if (requires_codesig) blk: {
+ var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
@@ -596,14 +578,14 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
try load_commands.writeLoadDylibLCs(self.dylibs.items, self.referenced_dylibs.keys(), lc_writer);
- if (requires_codesig) {
+ if (codesig != null) {
try lc_writer.writeStruct(self.codesig_cmd);
}
const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
try self.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
- try self.writeUuid(comp, uuid_cmd_offset, requires_codesig);
+ try self.writeUuid(comp, uuid_cmd_offset, codesig != null);
if (codesig) |*csig| {
try self.writeCodeSignature(comp, csig); // code signing always comes last
@@ -729,8 +711,7 @@ fn resolveLib(
}
pub fn parsePositional(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
must_link: bool,
@@ -741,9 +722,9 @@ pub fn parsePositional(
defer tracy.end();
if (Object.isObject(file)) {
- try parseObject(ctx, gpa, file, path, link_options);
+ try self.parseObject(file, path, link_options);
} else {
- try parseLibrary(ctx, gpa, file, path, .{
+ try self.parseLibrary(file, path, .{
.path = null,
.needed = false,
.weak = false,
@@ -752,8 +733,7 @@ pub fn parsePositional(
}
fn parseObject(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
link_options: *const link.Options,
@@ -761,6 +741,7 @@ fn parseObject(
const tracy = trace(@src());
defer tracy.end();
+ const gpa = self.base.allocator;
const mtime: u64 = mtime: {
const stat = file.stat() catch break :mtime 0;
break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000)));
@@ -776,7 +757,7 @@ fn parseObject(
};
errdefer object.deinit(gpa);
try object.parse(gpa);
- try ctx.objects.append(gpa, object);
+ try self.objects.append(gpa, object);
const cpu_arch: std.Target.Cpu.Arch = switch (object.header.cputype) {
macho.CPU_TYPE_ARM64 => .aarch64,
@@ -796,8 +777,7 @@ fn parseObject(
}
pub fn parseLibrary(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
lib: link.SystemLib,
@@ -811,16 +791,16 @@ pub fn parseLibrary(
const cpu_arch = link_options.target.cpu.arch;
if (fat.isFatLibrary(file)) {
- const offset = parseFatLibrary(ctx, file, path, cpu_arch) catch |err| switch (err) {
+ const offset = self.parseFatLibrary(file, path, cpu_arch) catch |err| switch (err) {
error.MissingArch => return,
else => |e| return e,
};
try file.seekTo(offset);
if (Archive.isArchive(file, offset)) {
- try parseArchive(ctx, gpa, path, offset, must_link, cpu_arch);
+ try self.parseArchive(path, offset, must_link, cpu_arch);
} else if (Dylib.isDylib(file, offset)) {
- try parseDylib(ctx, gpa, file, path, offset, dependent_libs, link_options, .{
+ try self.parseDylib(file, path, offset, dependent_libs, link_options, .{
.needed = lib.needed,
.weak = lib.weak,
});
@@ -830,14 +810,14 @@ pub fn parseLibrary(
return;
}
} else if (Archive.isArchive(file, 0)) {
- try parseArchive(ctx, gpa, path, 0, must_link, cpu_arch);
+ try self.parseArchive(path, 0, must_link, cpu_arch);
} else if (Dylib.isDylib(file, 0)) {
- try parseDylib(ctx, gpa, file, path, 0, dependent_libs, link_options, .{
+ try self.parseDylib(file, path, 0, dependent_libs, link_options, .{
.needed = lib.needed,
.weak = lib.weak,
});
} else {
- parseLibStub(ctx, gpa, file, path, dependent_libs, link_options, .{
+ self.parseLibStub(file, path, dependent_libs, link_options, .{
.needed = lib.needed,
.weak = lib.weak,
}) catch |err| switch (err) {
@@ -852,12 +832,12 @@ pub fn parseLibrary(
}
pub fn parseFatLibrary(
- ctx: anytype,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
cpu_arch: std.Target.Cpu.Arch,
) !u64 {
- _ = ctx;
+ _ = self;
var buffer: [2]fat.Arch = undefined;
const fat_archs = try fat.parseArchs(file, &buffer);
const offset = for (fat_archs) |arch| {
@@ -871,13 +851,13 @@ pub fn parseFatLibrary(
}
fn parseArchive(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
path: []const u8,
fat_offset: u64,
must_link: bool,
cpu_arch: std.Target.Cpu.Arch,
) !void {
+ const gpa = self.base.allocator;
// We take ownership of the file so that we can store it for the duration of symbol resolution.
// TODO we shouldn't need to do that and could pre-parse the archive like we do for zld/ELF?
@@ -929,10 +909,10 @@ fn parseArchive(
}
for (offsets.keys()) |off| {
const object = try archive.parseObject(gpa, off);
- try ctx.objects.append(gpa, object);
+ try self.objects.append(gpa, object);
}
} else {
- try ctx.archives.append(gpa, archive);
+ try self.archives.append(gpa, archive);
}
}
@@ -944,8 +924,7 @@ const DylibOpts = struct {
};
fn parseDylib(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
offset: u64,
@@ -953,6 +932,7 @@ fn parseDylib(
link_options: *const link.Options,
dylib_options: DylibOpts,
) !void {
+ const gpa = self.base.allocator;
const self_cpu_arch = link_options.target.cpu.arch;
const file_stat = try file.stat();
@@ -968,7 +948,7 @@ fn parseDylib(
try dylib.parseFromBinary(
gpa,
- @intCast(ctx.dylibs.items.len), // TODO defer it till later
+ @intCast(self.dylibs.items.len), // TODO defer it till later
dependent_libs,
path,
contents,
@@ -991,7 +971,7 @@ fn parseDylib(
// TODO verify platform
- addDylib(ctx, gpa, dylib, link_options, .{
+ self.addDylib(dylib, link_options, .{
.needed = dylib_options.needed,
.weak = dylib_options.weak,
}) catch |err| switch (err) {
@@ -1001,14 +981,14 @@ fn parseDylib(
}
fn parseLibStub(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
file: std.fs.File,
path: []const u8,
dependent_libs: anytype,
link_options: *const link.Options,
dylib_options: DylibOpts,
) !void {
+ const gpa = self.base.allocator;
var lib_stub = try LibStub.loadFromFile(gpa, file);
defer lib_stub.deinit();
@@ -1023,12 +1003,12 @@ fn parseLibStub(
gpa,
link_options.target,
lib_stub,
- @intCast(ctx.dylibs.items.len), // TODO defer it till later
+ @intCast(self.dylibs.items.len), // TODO defer it till later
dependent_libs,
path,
);
- addDylib(ctx, gpa, dylib, link_options, .{
+ self.addDylib(dylib, link_options, .{
.needed = dylib_options.needed,
.weak = dylib_options.weak,
}) catch |err| switch (err) {
@@ -1038,8 +1018,7 @@ fn parseLibStub(
}
fn addDylib(
- ctx: anytype,
- gpa: Allocator,
+ self: *MachO,
dylib: Dylib,
link_options: *const link.Options,
dylib_options: DylibOpts,
@@ -1055,28 +1034,24 @@ fn addDylib(
}
}
- const gop = try ctx.dylibs_map.getOrPut(gpa, dylib.id.?.name);
+ const gpa = self.base.allocator;
+ const gop = try self.dylibs_map.getOrPut(gpa, dylib.id.?.name);
if (gop.found_existing) return error.DylibAlreadyExists;
- gop.value_ptr.* = @as(u16, @intCast(ctx.dylibs.items.len));
- try ctx.dylibs.append(gpa, dylib);
+ gop.value_ptr.* = @as(u16, @intCast(self.dylibs.items.len));
+ try self.dylibs.append(gpa, dylib);
const should_link_dylib_even_if_unreachable = blk: {
if (link_options.dead_strip_dylibs and !dylib_options.needed) break :blk false;
- break :blk !(dylib_options.dependent or ctx.referenced_dylibs.contains(gop.value_ptr.*));
+ break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*));
};
if (should_link_dylib_even_if_unreachable) {
- try ctx.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
+ try self.referenced_dylibs.putNoClobber(gpa, gop.value_ptr.*, {});
}
}
-pub fn parseDependentLibs(
- ctx: anytype,
- gpa: Allocator,
- dependent_libs: anytype,
- link_options: *const link.Options,
-) !void {
+pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype, link_options: *const link.Options) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -1085,6 +1060,7 @@ pub fn parseDependentLibs(
// 2) afterwards, we parse dependents of the included dylibs
// TODO this should not be performed if the user specifies `-flat_namespace` flag.
// See ld64 manpages.
+ const gpa = self.base.allocator;
var arena_alloc = std.heap.ArenaAllocator.init(gpa);
const arena = arena_alloc.allocator();
defer arena_alloc.deinit();
@@ -1092,9 +1068,9 @@ pub fn parseDependentLibs(
outer: while (dependent_libs.readItem()) |dep_id| {
defer dep_id.id.deinit(gpa);
- if (ctx.dylibs_map.contains(dep_id.id.name)) continue;
+ if (self.dylibs_map.contains(dep_id.id.name)) continue;
- const weak = ctx.dylibs.items[dep_id.parent].weak;
+ const weak = self.dylibs.items[dep_id.parent].weak;
const has_ext = blk: {
const basename = fs.path.basename(dep_id.id.name);
break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
@@ -1121,7 +1097,7 @@ pub fn parseDependentLibs(
log.debug("trying dependency at fully resolved path {s}", .{full_path});
const offset: u64 = if (fat.isFatLibrary(file)) blk: {
- const offset = parseFatLibrary(ctx, file, full_path, link_options.target.cpu.arch) catch |err| switch (err) {
+ const offset = self.parseFatLibrary(file, full_path, link_options.target.cpu.arch) catch |err| switch (err) {
error.MissingArch => break,
else => |e| return e,
};
@@ -1130,12 +1106,12 @@ pub fn parseDependentLibs(
} else 0;
if (Dylib.isDylib(file, offset)) {
- try parseDylib(ctx, gpa, file, full_path, offset, dependent_libs, link_options, .{
+ try self.parseDylib(file, full_path, offset, dependent_libs, link_options, .{
.dependent = true,
.weak = weak,
});
} else {
- parseLibStub(ctx, gpa, file, full_path, dependent_libs, link_options, .{
+ self.parseLibStub(file, full_path, dependent_libs, link_options, .{
.dependent = true,
.weak = weak,
}) catch |err| switch (err) {
@@ -1394,7 +1370,7 @@ fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
}
}
-pub fn allocateSpecialSymbols(self: anytype) !void {
+pub fn allocateSpecialSymbols(self: *MachO) !void {
for (&[_][]const u8{
"___dso_handle",
"__mh_execute_header",
@@ -1432,24 +1408,82 @@ pub fn createAtom(self: *MachO, sym_index: u32, opts: CreateAtomOpts) !Atom.Inde
return index;
}
+pub fn createTentativeDefAtoms(self: *MachO) !void {
+ const gpa = self.base.allocator;
+
+ for (self.globals.items) |global| {
+ const sym = self.getSymbolPtr(global);
+ if (!sym.tentative()) continue;
+ if (sym.n_desc == N_DEAD) continue;
+
+ log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
+ global.sym_index, self.getSymbolName(global), global.file,
+ });
+
+ // Convert any tentative definition into a regular symbol and allocate
+ // text blocks for each tentative definition.
+ const size = sym.n_value;
+ const alignment = (sym.n_desc >> 8) & 0x0f;
+
+ if (self.bss_section_index == null) {
+ self.bss_section_index = try self.initSection("__DATA", "__bss", .{
+ .flags = macho.S_ZEROFILL,
+ });
+ }
+
+ sym.* = .{
+ .n_strx = sym.n_strx,
+ .n_type = macho.N_SECT | macho.N_EXT,
+ .n_sect = self.bss_section_index.? + 1,
+ .n_desc = 0,
+ .n_value = 0,
+ };
+
+ const atom_index = try self.createAtom(global.sym_index, .{
+ .size = size,
+ .alignment = alignment,
+ });
+ const atom = self.getAtomPtr(atom_index);
+ atom.file = global.file;
+
+ self.addAtomToSection(atom_index);
+
+ assert(global.getFile() != null);
+ const object = &self.objects.items[global.getFile().?];
+ try object.atoms.append(gpa, atom_index);
+ object.atom_by_index_table[global.sym_index] = atom_index;
+ }
+}
+
fn createDyldPrivateAtom(self: *MachO) !void {
if (self.dyld_private_atom_index != null) return;
const sym_index = try self.allocateSymbol();
- const atom_index = try self.createAtom(sym_index, .{});
+ const atom_index = try self.createAtom(sym_index, .{
+ .size = @sizeOf(u64),
+ .alignment = 3,
+ });
try self.atom_by_index_table.putNoClobber(self.base.allocator, sym_index, atom_index);
- const atom = self.getAtomPtr(atom_index);
- atom.size = @sizeOf(u64);
+ if (self.data_section_index == null) {
+ self.data_section_index = try self.initSection("__DATA", "__data", .{});
+ }
+
+ const atom = self.getAtom(atom_index);
const sym = atom.getSymbolPtr(self);
sym.n_type = macho.N_SECT;
sym.n_sect = self.data_section_index.? + 1;
self.dyld_private_atom_index = atom_index;
- sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
- log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
- var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
- try self.writeAtom(atom_index, &buffer);
+ switch (self.mode) {
+ .zld => self.addAtomToSection(atom_index),
+ .incremental => {
+ sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
+ log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
+ var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
+ try self.writeAtom(atom_index, &buffer);
+ },
+ }
}
fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: SymbolWithLoc) !Atom.Index {
@@ -1485,7 +1519,7 @@ fn createThreadLocalDescriptorAtom(self: *MachO, sym_name: []const u8, target: S
return atom_index;
}
-fn createMhExecuteHeaderSymbol(self: *MachO) !void {
+pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
if (self.base.options.output_mode != .Exe) return;
const gpa = self.base.allocator;
@@ -1501,10 +1535,17 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
};
const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
+ if (gop.found_existing) {
+ const global = gop.value_ptr.*;
+ if (global.getFile()) |file| {
+ const global_object = &self.objects.items[file];
+ global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
+ }
+ }
gop.value_ptr.* = sym_loc;
}
-fn createDsoHandleSymbol(self: *MachO) !void {
+pub fn createDsoHandleSymbol(self: *MachO) !void {
const global = self.getGlobalPtr("___dso_handle") orelse return;
if (!self.getSymbol(global.*).undf()) return;
@@ -1519,10 +1560,51 @@ fn createDsoHandleSymbol(self: *MachO) !void {
.n_desc = macho.N_WEAK_DEF,
.n_value = 0,
};
+ const global_index = self.getGlobalIndex("___dso_handle").?;
+ if (global.getFile()) |file| {
+ const global_object = &self.objects.items[file];
+ global_object.globals_lookup[global.sym_index] = global_index;
+ }
global.* = sym_loc;
_ = self.unresolved.swapRemove(self.getGlobalIndex("___dso_handle").?);
}
+pub fn resolveSymbols(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
+ // We add the specified entrypoint as the first unresolved symbols so that
+ // we search for it in libraries should there be no object files specified
+ // on the linker line.
+ if (self.base.options.output_mode == .Exe) {
+ const entry_name = self.base.options.entry orelse load_commands.default_entry_point;
+ _ = try self.addUndefined(entry_name, .none);
+ }
+
+ // Force resolution of any symbols requested by the user.
+ for (self.base.options.force_undefined_symbols.keys()) |sym_name| {
+ _ = try self.addUndefined(sym_name, .none);
+ }
+
+ for (self.objects.items, 0..) |_, object_id| {
+ try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
+ }
+
+ try self.resolveSymbolsInArchives();
+
+ // Finally, force resolution of dyld_stub_binder if there are imports
+ // requested.
+ if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) {
+ self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .add_got);
+ }
+ if (!self.base.options.single_threaded and self.mode == .incremental) {
+ _ = try self.addUndefined("__tlv_bootstrap", .none);
+ }
+
+ try self.resolveSymbolsInDylibs(actions);
+
+ try self.createMhExecuteHeaderSymbol();
+ try self.createDsoHandleSymbol();
+ try self.resolveSymbolsAtLoading();
+}
+
fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
const gpa = self.base.allocator;
const sym = self.getSymbol(current);
@@ -1536,6 +1618,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
}
return;
}
+ const global_index = self.getGlobalIndex(sym_name).?;
const global = gop.value_ptr.*;
const global_sym = self.getSymbol(global);
@@ -1566,7 +1649,22 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
- if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions;
+ if (sym_is_strong and global_is_strong) {
+ log.err("symbol '{s}' defined multiple times", .{sym_name});
+ if (global.getFile()) |file| {
+ log.err(" first definition in '{s}'", .{self.objects.items[file].name});
+ }
+ if (current.getFile()) |file| {
+ log.err(" next definition in '{s}'", .{self.objects.items[file].name});
+ }
+ return error.MultipleSymbolDefinitions;
+ }
+
+ if (current.getFile()) |file| {
+ const object = &self.objects.items[file];
+ object.globals_lookup[current.sym_index] = global_index;
+ }
+
if (global_is_strong) return;
if (sym_is_weak and global_is_weak) return;
if (sym.tentative() and global_sym.tentative()) {
@@ -1574,11 +1672,88 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
}
if (sym.undf() and !sym.tentative()) return;
- _ = self.unresolved.swapRemove(self.getGlobalIndex(sym_name).?);
+ if (global.getFile()) |file| {
+ const global_object = &self.objects.items[file];
+ global_object.globals_lookup[global.sym_index] = global_index;
+ }
+ _ = self.unresolved.swapRemove(global_index);
gop.value_ptr.* = current;
}
+fn resolveSymbolsInObject(self: *MachO, object_id: u32) !void {
+ const object = &self.objects.items[object_id];
+ const in_symtab = object.in_symtab orelse return;
+
+ log.debug("resolving symbols in '{s}'", .{object.name});
+
+ var sym_index: u32 = 0;
+ while (sym_index < in_symtab.len) : (sym_index += 1) {
+ const sym = &object.symtab[sym_index];
+ const sym_name = object.getSymbolName(sym_index);
+
+ if (sym.stab()) {
+ log.err("unhandled symbol type: stab", .{});
+ log.err(" symbol '{s}'", .{sym_name});
+ log.err(" first definition in '{s}'", .{object.name});
+ return error.UnhandledSymbolType;
+ }
+
+ if (sym.indr()) {
+ log.err("unhandled symbol type: indirect", .{});
+ log.err(" symbol '{s}'", .{sym_name});
+ log.err(" first definition in '{s}'", .{object.name});
+ return error.UnhandledSymbolType;
+ }
+
+ if (sym.abs()) {
+ log.err("unhandled symbol type: absolute", .{});
+ log.err(" symbol '{s}'", .{sym_name});
+ log.err(" first definition in '{s}'", .{object.name});
+ return error.UnhandledSymbolType;
+ }
+
+ if (sym.sect() and !sym.ext()) {
+ log.debug("symbol '{s}' local to object {s}; skipping...", .{
+ sym_name,
+ object.name,
+ });
+ continue;
+ }
+
+ try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 });
+ }
+}
+
+fn resolveSymbolsInArchives(self: *MachO) !void {
+ if (self.archives.items.len == 0) return;
+
+ const gpa = self.base.allocator;
+ var next_sym: usize = 0;
+ loop: while (next_sym < self.unresolved.count()) {
+ const global = self.globals.items[self.unresolved.keys()[next_sym]];
+ const sym_name = self.getSymbolName(global);
+
+ for (self.archives.items) |archive| {
+ // Check if the entry exists in a static archive.
+ const offsets = archive.toc.get(sym_name) orelse {
+ // No hit.
+ continue;
+ };
+ assert(offsets.items.len > 0);
+
+ const object_id = @as(u16, @intCast(self.objects.items.len));
+ const object = try archive.parseObject(gpa, offsets.items[0]);
+ try self.objects.append(gpa, object);
+ try self.resolveSymbolsInObject(object_id);
+
+ continue :loop;
+ }
+
+ next_sym += 1;
+ }
+}
+
fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction)) !void {
if (self.dylibs.items.len == 0) return;
@@ -1608,6 +1783,7 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
if (!sym.undf()) break :blk;
+ if (self.mode == .zld) break :blk;
try actions.append(.{ .kind = entry.value, .target = global });
}
@@ -1618,6 +1794,42 @@ fn resolveSymbolsInDylibs(self: *MachO, actions: *std.ArrayList(ResolveAction))
}
}
+fn resolveSymbolsAtLoading(self: *MachO) !void {
+ const is_lib = self.base.options.output_mode == .Lib;
+ const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
+ const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
+
+ var next_sym: usize = 0;
+ while (next_sym < self.unresolved.count()) {
+ const global_index = self.unresolved.keys()[next_sym];
+ const global = self.globals.items[global_index];
+ const sym = self.getSymbolPtr(global);
+
+ if (sym.discarded()) {
+ sym.* = .{
+ .n_strx = 0,
+ .n_type = macho.N_UNDF,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ _ = self.unresolved.swapRemove(global_index);
+ continue;
+ } else if (allow_undef) {
+ const n_desc = @as(
+ u16,
+ @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
+ );
+ sym.n_type = macho.N_EXT;
+ sym.n_desc = n_desc;
+ _ = self.unresolved.swapRemove(global_index);
+ continue;
+ }
+
+ next_sym += 1;
+ }
+}
+
pub fn deinit(self: *MachO) void {
const gpa = self.base.allocator;
@@ -1638,7 +1850,6 @@ pub fn deinit(self: *MachO) void {
self.thunks.deinit(gpa);
self.strtab.deinit(gpa);
-
self.locals.deinit(gpa);
self.globals.deinit(gpa);
self.locals_free_list.deinit(gpa);
@@ -1653,6 +1864,14 @@ pub fn deinit(self: *MachO) void {
self.resolver.deinit(gpa);
}
+ for (self.objects.items) |*object| {
+ object.deinit(gpa);
+ }
+ self.objects.deinit(gpa);
+ for (self.archives.items) |*archive| {
+ archive.deinit(gpa);
+ }
+ self.archives.deinit(gpa);
for (self.dylibs.items) |*dylib| {
dylib.deinit(gpa);
}
@@ -1842,20 +2061,55 @@ fn allocateGlobal(self: *MachO) !u32 {
return index;
}
-fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
+pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void {
if (self.got_table.lookup.contains(target)) return;
const got_index = try self.got_table.allocateEntry(self.base.allocator, target);
- try self.writeOffsetTableEntry(got_index);
- self.got_table_count_dirty = true;
- self.markRelocsDirtyByTarget(target);
+ if (self.got_section_index == null) {
+ self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{
+ .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
+ });
+ }
+ if (self.mode == .incremental) {
+ try self.writeOffsetTableEntry(got_index);
+ self.got_table_count_dirty = true;
+ self.markRelocsDirtyByTarget(target);
+ }
}
-fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
+pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
if (self.stub_table.lookup.contains(target)) return;
const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target);
- try self.writeStubTableEntry(stub_index);
- self.stub_table_count_dirty = true;
- self.markRelocsDirtyByTarget(target);
+ if (self.stubs_section_index == null) {
+ self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{
+ .flags = macho.S_SYMBOL_STUBS |
+ macho.S_ATTR_PURE_INSTRUCTIONS |
+ macho.S_ATTR_SOME_INSTRUCTIONS,
+ .reserved2 = stubs.stubSize(self.base.options.target.cpu.arch),
+ });
+ self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{
+ .flags = macho.S_REGULAR |
+ macho.S_ATTR_PURE_INSTRUCTIONS |
+ macho.S_ATTR_SOME_INSTRUCTIONS,
+ });
+ self.la_symbol_ptr_section_index = try self.initSection("__DATA", "__la_symbol_ptr", .{
+ .flags = macho.S_LAZY_SYMBOL_POINTERS,
+ });
+ }
+ if (self.mode == .incremental) {
+ try self.writeStubTableEntry(stub_index);
+ self.stub_table_count_dirty = true;
+ self.markRelocsDirtyByTarget(target);
+ }
+}
+
+pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void {
+ if (self.tlv_ptr_table.lookup.contains(target)) return;
+ _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target);
+ if (self.tlv_ptr_section_index == null) {
+ self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{
+ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
+ });
+ }
}
pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
@@ -2758,16 +3012,10 @@ const InitSectionOpts = struct {
reserved2: u32 = 0,
};
-pub fn initSection(
- gpa: Allocator,
- ctx: anytype,
- segname: []const u8,
- sectname: []const u8,
- opts: InitSectionOpts,
-) !u8 {
+pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
log.debug("creating section '{s},{s}'", .{ segname, sectname });
- const index = @as(u8, @intCast(ctx.sections.slice().len));
- try ctx.sections.append(gpa, .{
+ const index = @as(u8, @intCast(self.sections.slice().len));
+ try self.sections.append(self.base.allocator, .{
.segment_index = undefined, // Segments will be created automatically later down the pipeline
.header = .{
.sectname = makeStaticString(sectname),
@@ -2822,7 +3070,7 @@ fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts
.cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
};
- const sect_id = try initSection(gpa, self, sectname, segname, .{
+ const sect_id = try self.initSection(sectname, segname, .{
.flags = opts.flags,
.reserved2 = opts.reserved2,
});
@@ -2918,10 +3166,29 @@ fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
}
}
+pub fn addAtomToSection(self: *MachO, atom_index: Atom.Index) void {
+ assert(self.mode == .zld);
+ const atom = self.getAtomPtr(atom_index);
+ const sym = self.getSymbol(atom.getSymbolWithLoc());
+ var section = self.sections.get(sym.n_sect - 1);
+ if (section.header.size > 0) {
+ const last_atom = self.getAtomPtr(section.last_atom_index.?);
+ last_atom.next_index = atom_index;
+ atom.prev_index = section.last_atom_index;
+ } else {
+ section.first_atom_index = atom_index;
+ }
+ section.last_atom_index = atom_index;
+ section.header.size += atom.size;
+ self.sections.set(sym.n_sect - 1, section);
+}
+
fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
const tracy = trace(@src());
defer tracy.end();
+ assert(self.mode == .incremental);
+
const atom = self.getAtom(atom_index);
const sect_id = atom.getSymbol(self).n_sect - 1;
const segment = self.getSegmentPtr(sect_id);
@@ -3048,7 +3315,7 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8, lib_name: ?[]const u8) !u
return self.addUndefined(sym_name, .add_stub);
}
-pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {
+pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void {
for (self.segments.items, 0..) |seg, i| {
const indexes = self.getSectionIndexes(@as(u8, @intCast(i)));
var out_seg = seg;
@@ -3075,7 +3342,7 @@ pub fn writeSegmentHeaders(self: anytype, writer: anytype) !void {
}
}
-fn writeLinkeditSegmentData(self: *MachO) !void {
+pub fn writeLinkeditSegmentData(self: *MachO) !void {
const page_size = getPageSize(self.base.options.target.cpu.arch);
const seg = self.getLinkeditSegmentPtr();
seg.filesize = 0;
@@ -3092,29 +3359,29 @@ fn writeLinkeditSegmentData(self: *MachO) !void {
}
try self.writeDyldInfoData();
+ // TODO handle this better
+ if (self.mode == .zld) {
+ try self.writeFunctionStarts();
+ try self.writeDataInCode();
+ }
try self.writeSymtabs();
seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
}
-pub fn collectRebaseDataFromTableSection(
- gpa: Allocator,
- ctx: anytype,
- sect_id: u8,
- rebase: *Rebase,
- table: anytype,
-) !void {
- const header = ctx.sections.items(.header)[sect_id];
- const segment_index = ctx.sections.items(.segment_index)[sect_id];
- const segment = ctx.segments.items[segment_index];
+fn collectRebaseDataFromTableSection(self: *MachO, sect_id: u8, rebase: *Rebase, table: anytype) !void {
+ const gpa = self.base.allocator;
+ const header = self.sections.items(.header)[sect_id];
+ const segment_index = self.sections.items(.segment_index)[sect_id];
+ const segment = self.segments.items[segment_index];
const base_offset = header.addr - segment.vmaddr;
- const is_got = if (ctx.got_section_index) |index| index == sect_id else false;
+ const is_got = if (self.got_section_index) |index| index == sect_id else false;
try rebase.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
for (table.entries.items, 0..) |entry, i| {
if (!table.lookup.contains(entry)) continue;
- const sym = ctx.getSymbol(entry);
+ const sym = self.getSymbol(entry);
if (is_got and sym.undf()) continue;
const offset = i * @sizeOf(u64);
log.debug(" | rebase at {x}", .{base_offset + offset});
@@ -3152,34 +3419,105 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
}
}
- try collectRebaseDataFromTableSection(gpa, self, self.got_section_index.?, rebase, self.got_table);
- try collectRebaseDataFromTableSection(gpa, self, self.la_symbol_ptr_section_index.?, rebase, self.stub_table);
+ // Unpack GOT entries
+ if (self.got_section_index) |sect_id| {
+ try self.collectRebaseDataFromTableSection(sect_id, rebase, self.got_table);
+ }
+
+ // Next, unpack __la_symbol_ptr entries
+ if (self.la_symbol_ptr_section_index) |sect_id| {
+ try self.collectRebaseDataFromTableSection(sect_id, rebase, self.stub_table);
+ }
+
+ // Finally, unpack the rest.
+ const cpu_arch = self.base.options.target.cpu.arch;
+ for (self.objects.items) |*object| {
+ for (object.atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const sym = self.getSymbol(atom.getSymbolWithLoc());
+ if (sym.n_desc == N_DEAD) continue;
+
+ const sect_id = sym.n_sect - 1;
+ const section = self.sections.items(.header)[sect_id];
+ const segment_id = self.sections.items(.segment_index)[sect_id];
+ const segment = self.segments.items[segment_id];
+ if (segment.maxprot & macho.PROT.WRITE == 0) continue;
+ switch (section.type()) {
+ macho.S_LITERAL_POINTERS,
+ macho.S_REGULAR,
+ macho.S_MOD_INIT_FUNC_POINTERS,
+ macho.S_MOD_TERM_FUNC_POINTERS,
+ => {},
+ else => continue,
+ }
+
+ log.debug(" ATOM({d}, %{d}, '{s}')", .{
+ atom_index,
+ atom.sym_index,
+ self.getSymbolName(atom.getSymbolWithLoc()),
+ });
+
+ const code = Atom.getAtomCode(self, atom_index);
+ const relocs = Atom.getAtomRelocs(self, atom_index);
+ const ctx = Atom.getRelocContext(self, atom_index);
+
+ for (relocs) |rel| {
+ switch (cpu_arch) {
+ .aarch64 => {
+ const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
+ if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
+ if (rel.r_length != 3) continue;
+ },
+ .x86_64 => {
+ const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
+ if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
+ if (rel.r_length != 3) continue;
+ },
+ else => unreachable,
+ }
+ const target = Atom.parseRelocTarget(self, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = code,
+ .base_offset = ctx.base_offset,
+ .base_addr = ctx.base_addr,
+ });
+ const target_sym = self.getSymbol(target);
+ if (target_sym.undf()) continue;
+
+ const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
+ const rel_offset = rel.r_address - ctx.base_offset;
+ const offset = @as(u64, @intCast(base_offset + rel_offset));
+ log.debug(" | rebase at {x}", .{offset});
+
+ try rebase.entries.append(self.gpa, .{
+ .offset = offset,
+ .segment_id = segment_id,
+ });
+ }
+ }
+ }
try rebase.finalize(gpa);
}
-pub fn collectBindDataFromTableSection(
- gpa: Allocator,
- ctx: anytype,
- sect_id: u8,
- bind: anytype,
- table: anytype,
-) !void {
- const header = ctx.sections.items(.header)[sect_id];
- const segment_index = ctx.sections.items(.segment_index)[sect_id];
- const segment = ctx.segments.items[segment_index];
+fn collectBindDataFromTableSection(self: *MachO, sect_id: u8, bind: anytype, table: anytype) !void {
+ const gpa = self.base.allocator;
+ const header = self.sections.items(.header)[sect_id];
+ const segment_index = self.sections.items(.segment_index)[sect_id];
+ const segment = self.segments.items[segment_index];
const base_offset = header.addr - segment.vmaddr;
try bind.entries.ensureUnusedCapacity(gpa, table.entries.items.len);
for (table.entries.items, 0..) |entry, i| {
if (!table.lookup.contains(entry)) continue;
- const bind_sym = ctx.getSymbol(entry);
+ const bind_sym = self.getSymbol(entry);
if (!bind_sym.undf()) continue;
const offset = i * @sizeOf(u64);
log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
base_offset + offset,
- ctx.getSymbolName(entry),
+ self.getSymbolName(entry),
@divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER),
});
if (bind_sym.weakRef()) {
@@ -3235,13 +3573,105 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
}
}
- // Gather GOT pointers
- try collectBindDataFromTableSection(gpa, self, self.got_section_index.?, bind, self.got_table);
+ // Unpack GOT pointers
+ if (self.got_section_index) |sect_id| {
+ try self.collectBindDataFromTableSection(sect_id, bind, self.got_table);
+ }
+
+ // Next, unpack TLV pointers section
+ if (self.tlv_ptr_section_index) |sect_id| {
+ try self.collectBindDataFromTableSection(sect_id, bind, self.tlv_ptr_table);
+ }
+
+ // Finally, unpack the rest.
+ const cpu_arch = self.base.options.target.cpu.arch;
+ for (self.objects.items) |*object| {
+ for (object.atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const sym = self.getSymbol(atom.getSymbolWithLoc());
+ if (sym.n_desc == N_DEAD) continue;
+
+ const sect_id = sym.n_sect - 1;
+ const section = self.sections.items(.header)[sect_id];
+ const segment_id = self.sections.items(.segment_index)[sect_id];
+ const segment = self.segments.items[segment_id];
+ if (segment.maxprot & macho.PROT.WRITE == 0) continue;
+ switch (section.type()) {
+ macho.S_LITERAL_POINTERS,
+ macho.S_REGULAR,
+ macho.S_MOD_INIT_FUNC_POINTERS,
+ macho.S_MOD_TERM_FUNC_POINTERS,
+ => {},
+ else => continue,
+ }
+
+ log.debug(" ATOM({d}, %{d}, '{s}')", .{
+ atom_index,
+ atom.sym_index,
+ self.getSymbolName(atom.getSymbolWithLoc()),
+ });
+
+ const code = Atom.getAtomCode(self, atom_index);
+ const relocs = Atom.getAtomRelocs(self, atom_index);
+ const ctx = Atom.getRelocContext(self, atom_index);
+
+ for (relocs) |rel| {
+ switch (cpu_arch) {
+ .aarch64 => {
+ const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
+ if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
+ if (rel.r_length != 3) continue;
+ },
+ .x86_64 => {
+ const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
+ if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
+ if (rel.r_length != 3) continue;
+ },
+ else => unreachable,
+ }
+
+ const global = Atom.parseRelocTarget(self, .{
+ .object_id = atom.getFile().?,
+ .rel = rel,
+ .code = code,
+ .base_offset = ctx.base_offset,
+ .base_addr = ctx.base_addr,
+ });
+ const bind_sym_name = self.getSymbolName(global);
+ const bind_sym = self.getSymbol(global);
+ if (!bind_sym.undf()) continue;
+
+ const base_offset = sym.n_value - segment.vmaddr;
+ const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
+ const offset = @as(u64, @intCast(base_offset + rel_offset));
+ const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
+
+ const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
+ log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
+ base_offset,
+ bind_sym_name,
+ dylib_ordinal,
+ });
+ log.debug(" | with addend {x}", .{addend});
+ if (bind_sym.weakRef()) {
+ log.debug(" | marking as weak ref ", .{});
+ }
+ try bind.entries.append(self.gpa, .{
+ .target = global,
+ .offset = offset,
+ .segment_id = segment_id,
+ .addend = addend,
+ });
+ }
+ }
+ }
+
try bind.finalize(gpa, self);
}
fn collectLazyBindData(self: *MachO, bind: anytype) !void {
- try collectBindDataFromTableSection(self.base.allocator, self, self.la_symbol_ptr_section_index.?, bind, self.stub_table);
+ const sect_id = self.la_symbol_ptr_section_index orelse return;
+ try self.collectBindDataFromTableSection(sect_id, bind, self.stub_table);
try bind.finalize(self.base.allocator, self);
}
@@ -3259,6 +3689,7 @@ fn collectExportData(self: *MachO, trie: *Trie) !void {
if (sym.undf()) continue;
assert(sym.ext());
+ if (sym.n_desc == N_DEAD) continue;
const sym_name = self.getSymbolName(global);
log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
@@ -3349,12 +3780,7 @@ fn writeDyldInfoData(self: *MachO) !void {
});
try self.base.file.?.pwriteAll(buffer, rebase_off);
- try populateLazyBindOffsetsInStubHelper(
- self,
- self.base.options.target.cpu.arch,
- self.base.file.?,
- lazy_bind,
- );
+ try self.populateLazyBindOffsetsInStubHelper(lazy_bind);
self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
@@ -3366,19 +3792,15 @@ fn writeDyldInfoData(self: *MachO) !void {
self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
}
-pub fn populateLazyBindOffsetsInStubHelper(
- ctx: anytype,
- cpu_arch: std.Target.Cpu.Arch,
- file: fs.File,
- lazy_bind: anytype,
-) !void {
+fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void {
if (lazy_bind.size() == 0) return;
- const stub_helper_section_index = ctx.stub_helper_section_index.?;
+ const stub_helper_section_index = self.stub_helper_section_index.?;
// assert(ctx.stub_helper_preamble_allocated);
- const header = ctx.sections.items(.header)[stub_helper_section_index];
+ const header = self.sections.items(.header)[stub_helper_section_index];
+ const cpu_arch = self.base.options.target.cpu.arch;
const preamble_size = stubs.stubHelperPreambleSize(cpu_arch);
const stub_size = stubs.stubHelperSize(cpu_arch);
const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch);
@@ -3389,14 +3811,175 @@ pub fn populateLazyBindOffsetsInStubHelper(
log.debug("writing lazy bind offset 0x{x} ({s}) in stub helper at 0x{x}", .{
bind_offset,
- ctx.getSymbolName(lazy_bind.entries.items[index].target),
+ self.getSymbolName(lazy_bind.entries.items[index].target),
file_offset,
});
- try file.pwriteAll(mem.asBytes(&bind_offset), file_offset);
+ try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
}
}
+const asc_u64 = std.sort.asc(u64);
+
+fn addSymbolToFunctionStarts(self: *MachO, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
+ const sym = self.getSymbol(sym_loc);
+ if (sym.n_strx == 0) return;
+ if (sym.n_desc == MachO.N_DEAD) return;
+ if (self.symbolIsTemp(sym_loc)) return;
+ try addresses.append(sym.n_value);
+}
+
+fn writeFunctionStarts(self: *MachO) !void {
+ const gpa = self.base.allocator;
+ const seg = self.segments.items[self.header_segment_cmd_index.?];
+
+ // We need to sort by address first
+ var addresses = std.ArrayList(u64).init(gpa);
+ defer addresses.deinit();
+
+ for (self.objects.items) |object| {
+ for (object.exec_atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const sym_loc = atom.getSymbolWithLoc();
+ try self.addSymbolToFunctionStarts(sym_loc, &addresses);
+
+ var it = Atom.getInnerSymbolsIterator(self, atom_index);
+ while (it.next()) |inner_sym_loc| {
+ try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
+ }
+ }
+ }
+
+ mem.sort(u64, addresses.items, {}, asc_u64);
+
+ var offsets = std.ArrayList(u32).init(gpa);
+ defer offsets.deinit();
+ try offsets.ensureTotalCapacityPrecise(addresses.items.len);
+
+ var last_off: u32 = 0;
+ for (addresses.items) |addr| {
+ const offset = @as(u32, @intCast(addr - seg.vmaddr));
+ const diff = offset - last_off;
+
+ if (diff == 0) continue;
+
+ offsets.appendAssumeCapacity(diff);
+ last_off = offset;
+ }
+
+ var buffer = std.ArrayList(u8).init(gpa);
+ defer buffer.deinit();
+
+ const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
+ try buffer.ensureTotalCapacity(max_size);
+
+ for (offsets.items) |offset| {
+ try std.leb.writeULEB128(buffer.writer(), offset);
+ }
+
+ const link_seg = self.getLinkeditSegmentPtr();
+ const offset = link_seg.fileoff + link_seg.filesize;
+ assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
+ const needed_size = buffer.items.len;
+ const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
+ const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
+ if (padding > 0) {
+ try buffer.ensureUnusedCapacity(padding);
+ buffer.appendNTimesAssumeCapacity(0, padding);
+ }
+ link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
+
+ log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
+
+ try self.base.file.?.pwriteAll(buffer.items, offset);
+
+ self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
+ self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
+}
+
+fn filterDataInCode(
+ dices: []const macho.data_in_code_entry,
+ start_addr: u64,
+ end_addr: u64,
+) []const macho.data_in_code_entry {
+ const Predicate = struct {
+ addr: u64,
+
+ pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
+ return dice.offset >= self.addr;
+ }
+ };
+
+ const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
+ const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
+
+ return dices[start..end];
+}
+
+pub fn writeDataInCode(self: *MachO) !void {
+ const gpa = self.base.allocator;
+ var out_dice = std.ArrayList(macho.data_in_code_entry).init(gpa);
+ defer out_dice.deinit();
+
+ const text_sect_id = self.text_section_index orelse return;
+ const text_sect_header = self.sections.items(.header)[text_sect_id];
+
+ for (self.objects.items) |object| {
+ if (!object.hasDataInCode()) continue;
+ const dice = object.data_in_code.items;
+ try out_dice.ensureUnusedCapacity(dice.len);
+
+ for (object.exec_atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const sym = self.getSymbol(atom.getSymbolWithLoc());
+ if (sym.n_desc == MachO.N_DEAD) continue;
+
+ const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
+ source_sym.n_value
+ else blk: {
+ const nbase = @as(u32, @intCast(object.in_symtab.?.len));
+ const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
+ break :blk object.getSourceSection(source_sect_id).addr;
+ };
+ const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
+ const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
+ return error.Overflow;
+
+ for (filtered_dice) |single| {
+ const offset = math.cast(u32, single.offset - source_addr + base) orelse
+ return error.Overflow;
+ out_dice.appendAssumeCapacity(.{
+ .offset = offset,
+ .length = single.length,
+ .kind = single.kind,
+ });
+ }
+ }
+ }
+
+ const seg = self.getLinkeditSegmentPtr();
+ const offset = seg.fileoff + seg.filesize;
+ assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
+ const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
+ const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
+ seg.filesize = offset + needed_size_aligned - seg.fileoff;
+
+ const buffer = try gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
+ defer gpa.free(buffer);
+ {
+ const src = mem.sliceAsBytes(out_dice.items);
+ @memcpy(buffer[0..src.len], src);
+ @memset(buffer[src.len..], 0);
+ }
+
+ log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
+
+ try self.base.file.?.pwriteAll(buffer, offset);
+
+ self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
+ self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
+}
+
fn writeSymtabs(self: *MachO) !void {
var ctx = try self.writeSymtab();
defer ctx.imports_table.deinit();
@@ -3404,18 +3987,38 @@ fn writeSymtabs(self: *MachO) !void {
try self.writeStrtab();
}
+fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
+ const sym = self.getSymbol(sym_loc);
+ if (sym.n_strx == 0) return; // no name, skip
+ if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
+ if (sym.ext()) return; // an export lands in its own symtab section, skip
+ if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
+ var out_sym = sym;
+ out_sym.n_strx = try self.strtab.insert(self.base.allocator, self.getSymbolName(sym_loc));
+ try locals.append(out_sym);
+}
+
fn writeSymtab(self: *MachO) !SymtabCtx {
const gpa = self.base.allocator;
var locals = std.ArrayList(macho.nlist_64).init(gpa);
defer locals.deinit();
- for (self.locals.items, 0..) |sym, sym_id| {
- if (sym.n_strx == 0) continue; // no name, skip
- const sym_loc = SymbolWithLoc{ .sym_index = @as(u32, @intCast(sym_id)) };
- if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
- if (self.getGlobal(self.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
- try locals.append(sym);
+ for (0..self.locals.items) |sym_id| {
+ try self.addLocalToSymtab(.{ .sym_index = @intCast(sym_id) });
+ }
+
+ for (self.objects.items) |object| {
+ for (object.atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const sym_loc = atom.getSymbolWithLoc();
+ try self.addLocalToSymtab(sym_loc, &locals);
+
+ var it = Atom.getInnerSymbolsIterator(self, atom_index);
+ while (it.next()) |inner_sym_loc| {
+ try self.addLocalToSymtab(inner_sym_loc, &locals);
+ }
+ }
}
var exports = std.ArrayList(macho.nlist_64).init(gpa);
@@ -3424,6 +4027,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
for (self.globals.items) |global| {
const sym = self.getSymbol(global);
if (sym.undf()) continue; // import, skip
+ if (sym.n_desc == N_DEAD) continue;
var out_sym = sym;
out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
try exports.append(out_sym);
@@ -3438,6 +4042,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
const sym = self.getSymbol(global);
if (sym.n_strx == 0) continue; // no name, skip
if (!sym.undf()) continue; // not an import, skip
+ if (sym.n_desc == N_DEAD) continue;
const new_index = @as(u32, @intCast(imports.items.len));
var out_sym = sym;
out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
@@ -3445,6 +4050,15 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
try imports_table.putNoClobber(global, new_index);
}
+ // We generate stabs last in order to ensure that the strtab always has debug info
+ // strings trailing
+ if (!self.base.options.strip) {
+ assert(self.d_sym == null); // TODO
+ for (self.objects.items) |object| {
+ try self.generateSymbolStabs(object, &locals);
+ }
+ }
+
const nlocals = @as(u32, @intCast(locals.items.len));
const nexports = @as(u32, @intCast(exports.items.len));
const nimports = @as(u32, @intCast(imports.items.len));
@@ -3478,7 +4092,218 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
};
}
-fn writeStrtab(self: *MachO) !void {
+fn generateSymbolStabs(
+ self: *MachO,
+ object: Object,
+ locals: *std.ArrayList(macho.nlist_64),
+) !void {
+ log.debug("generating stabs for '{s}'", .{object.name});
+
+ const gpa = self.base.allocator;
+ var debug_info = object.parseDwarfInfo();
+
+ var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
+ defer lookup.deinit();
+ try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
+
+ // We assume there is only one CU.
+ var cu_it = debug_info.getCompileUnitIterator();
+ const compile_unit = while (try cu_it.next()) |cu| {
+ const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
+ try debug_info.genAbbrevLookupByKind(offset, &lookup);
+ break cu;
+ } else {
+ log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
+ return;
+ };
+
+ var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
+ const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
+ dwarf.TAG.compile_unit => break entry,
+ else => continue,
+ } else {
+ log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
+ return;
+ };
+
+ var maybe_tu_name: ?[]const u8 = null;
+ var maybe_tu_comp_dir: ?[]const u8 = null;
+ var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
+
+ while (try attr_it.next()) |attr| switch (attr.name) {
+ dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
+ dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
+ else => continue,
+ };
+
+ if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
+ log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
+ return;
+ }
+
+ const tu_name = maybe_tu_name.?;
+ const tu_comp_dir = maybe_tu_comp_dir.?;
+
+ // Open scope
+ try locals.ensureUnusedCapacity(3);
+ locals.appendAssumeCapacity(.{
+ .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
+ .n_type = macho.N_SO,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+ locals.appendAssumeCapacity(.{
+ .n_strx = try self.strtab.insert(gpa, tu_name),
+ .n_type = macho.N_SO,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+ locals.appendAssumeCapacity(.{
+ .n_strx = try self.strtab.insert(gpa, object.name),
+ .n_type = macho.N_OSO,
+ .n_sect = 0,
+ .n_desc = 1,
+ .n_value = object.mtime,
+ });
+
+ var stabs_buf: [4]macho.nlist_64 = undefined;
+
+ var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
+ var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
+ errdefer name_lookup.deinit();
+ try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
+ try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
+ break :blk name_lookup;
+ } else null;
+ defer if (name_lookup) |*nl| nl.deinit();
+
+ for (object.atoms.items) |atom_index| {
+ const atom = self.getAtom(atom_index);
+ const stabs = try self.generateSymbolStabsForSymbol(
+ atom_index,
+ atom.getSymbolWithLoc(),
+ name_lookup,
+ &stabs_buf,
+ );
+ try locals.appendSlice(stabs);
+
+ var it = Atom.getInnerSymbolsIterator(self, atom_index);
+ while (it.next()) |sym_loc| {
+ const contained_stabs = try self.generateSymbolStabsForSymbol(
+ atom_index,
+ sym_loc,
+ name_lookup,
+ &stabs_buf,
+ );
+ try locals.appendSlice(contained_stabs);
+ }
+ }
+
+ // Close scope
+ try locals.append(.{
+ .n_strx = 0,
+ .n_type = macho.N_SO,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = 0,
+ });
+}
+
+fn generateSymbolStabsForSymbol(
+ self: *MachO,
+ atom_index: Atom.Index,
+ sym_loc: SymbolWithLoc,
+ lookup: ?DwarfInfo.SubprogramLookupByName,
+ buf: *[4]macho.nlist_64,
+) ![]const macho.nlist_64 {
+ const gpa = self.base.allocator;
+ const object = self.objects.items[sym_loc.getFile().?];
+ const sym = self.getSymbol(sym_loc);
+ const sym_name = self.getSymbolName(sym_loc);
+ const header = self.sections.items(.header)[sym.n_sect - 1];
+
+ if (sym.n_strx == 0) return buf[0..0];
+ if (self.symbolIsTemp(sym_loc)) return buf[0..0];
+
+ if (!header.isCode()) {
+ // Since we are not dealing with machine code, it's either a global or a static depending
+ // on the linkage scope.
+ if (sym.sect() and sym.ext()) {
+ // Global gets an N_GSYM stab type.
+ buf[0] = .{
+ .n_strx = try self.strtab.insert(gpa, sym_name),
+ .n_type = macho.N_GSYM,
+ .n_sect = sym.n_sect,
+ .n_desc = 0,
+ .n_value = 0,
+ };
+ } else {
+ // Local static gets an N_STSYM stab type.
+ buf[0] = .{
+ .n_strx = try self.strtab.insert(gpa, sym_name),
+ .n_type = macho.N_STSYM,
+ .n_sect = sym.n_sect,
+ .n_desc = 0,
+ .n_value = sym.n_value,
+ };
+ }
+ return buf[0..1];
+ }
+
+ const size: u64 = size: {
+ if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
+ break :size self.getAtom(atom_index).size;
+ }
+
+ // Since we don't have subsections to work with, we need to infer the size of each function
+ // the slow way by scanning the debug info for matching symbol names and extracting
+ // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
+ const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
+ const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
+
+ if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
+ break :size subprogram.size;
+ } else {
+ log.debug("no stab found for {s}", .{sym_name});
+ return buf[0..0];
+ }
+ };
+
+ buf[0] = .{
+ .n_strx = 0,
+ .n_type = macho.N_BNSYM,
+ .n_sect = sym.n_sect,
+ .n_desc = 0,
+ .n_value = sym.n_value,
+ };
+ buf[1] = .{
+ .n_strx = try self.strtab.insert(gpa, sym_name),
+ .n_type = macho.N_FUN,
+ .n_sect = sym.n_sect,
+ .n_desc = 0,
+ .n_value = sym.n_value,
+ };
+ buf[2] = .{
+ .n_strx = 0,
+ .n_type = macho.N_FUN,
+ .n_sect = 0,
+ .n_desc = 0,
+ .n_value = size,
+ };
+ buf[3] = .{
+ .n_strx = 0,
+ .n_type = macho.N_ENSYM,
+ .n_sect = sym.n_sect,
+ .n_desc = 0,
+ .n_value = size,
+ };
+
+ return buf;
+}
+
+pub fn writeStrtab(self: *MachO) !void {
const gpa = self.base.allocator;
const seg = self.getLinkeditSegmentPtr();
const offset = seg.fileoff + seg.filesize;
@@ -3507,7 +4332,7 @@ const SymtabCtx = struct {
imports_table: std.AutoHashMap(SymbolWithLoc, u32),
};
-fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
+pub fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
const gpa = self.base.allocator;
const nstubs = @as(u32, @intCast(self.stub_table.lookup.count()));
const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
@@ -3582,7 +4407,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx) !void {
self.dysymtab_cmd.nindirectsyms = nindirectsyms;
}
-fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
+pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
const file_size = if (!has_codesig) blk: {
const seg = self.getLinkeditSegmentPtr();
break :blk seg.fileoff + seg.filesize;
@@ -3592,7 +4417,7 @@ fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, has_c
try self.base.file.?.pwriteAll(&self.uuid_cmd.uuid, offset);
}
-fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
+pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
const seg = self.getLinkeditSegmentPtr();
// Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
// https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
@@ -3609,8 +4434,9 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void {
self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
}
-fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
- const seg = self.getSegment(self.text_section_index.?);
+pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void {
+ const seg_id = self.header_segment_cmd_index.?;
+ const seg = self.segments.items[seg_id];
const offset = self.codesig_cmd.dataoff;
var buffer = std.ArrayList(u8).init(self.base.allocator);
@@ -3634,14 +4460,10 @@ fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSig
}
/// Writes Mach-O file header.
-fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
+pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
var header: macho.mach_header_64 = .{};
header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
- if (!self.base.options.single_threaded) {
- header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
- }
-
switch (self.base.options.target.cpu.arch) {
.aarch64 => {
header.cputype = macho.CPU_TYPE_ARM64;
@@ -3666,6 +4488,13 @@ fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void {
else => unreachable,
}
+ if (self.thread_vars_section_index) |sect_id| {
+ header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
+ if (self.sections.items(.header)[sect_id].size > 0) {
+ header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
+ }
+ }
+
header.ncmds = ncmds;
header.sizeofcmds = sizeofcmds;
@@ -3830,20 +4659,33 @@ pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
- assert(sym_with_loc.getFile() == null);
- return &self.locals.items[sym_with_loc.sym_index];
+ if (sym_with_loc.getFile()) |file| {
+ const object = &self.objects.items[file];
+ return &object.symtab[sym_with_loc.sym_index];
+ } else {
+ return &self.locals.items[sym_with_loc.sym_index];
+ }
}
/// Returns symbol described by `sym_with_loc` descriptor.
pub fn getSymbol(self: *const MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
- assert(sym_with_loc.getFile() == null);
- return self.locals.items[sym_with_loc.sym_index];
+ if (sym_with_loc.getFile()) |file| {
+ const object = &self.objects.items[file];
+ return object.symtab[sym_with_loc.sym_index];
+ } else {
+ return self.locals.items[sym_with_loc.sym_index];
+ }
}
/// Returns name of the symbol described by `sym_with_loc` descriptor.
pub fn getSymbolName(self: *const MachO, sym_with_loc: SymbolWithLoc) []const u8 {
- const sym = self.getSymbol(sym_with_loc);
- return self.strtab.get(sym.n_strx).?;
+ if (sym_with_loc.getFile()) |file| {
+ const object = self.objects.items[file];
+ return object.getSymbolName(sym_with_loc.sym_index);
+ } else {
+ const sym = self.locals.items[sym_with_loc.sym_index];
+ return self.strtab.get(sym.n_strx).?;
+ }
}
/// Returns pointer to the global entry for `name` if one exists.
@@ -3945,6 +4787,19 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 {
};
}
+pub inline fn requiresThunks(self: MachO) bool {
+ return self.base.options.target.cpu.arch == .aarch64;
+}
+
+pub fn requiresCodeSignature(self: MachO) bool {
+ if (self.base.options.entitlements) |_| return true;
+ const cpu_arch = self.base.options.target.cpu.arch;
+ const os_tag = self.base.options.target.os.tag;
+ const abi = self.base.options.target.abi;
+ if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true;
+ return false;
+}
+
pub fn getSegmentPrecedence(segname: []const u8) u4 {
if (mem.eql(u8, segname, "__PAGEZERO")) return 0x0;
if (mem.eql(u8, segname, "__TEXT")) return 0x1;
@@ -3988,24 +4843,26 @@ pub fn getSectionPrecedence(header: macho.section_64) u8 {
return (@as(u8, @intCast(segment_precedence)) << 4) + section_precedence;
}
-pub fn reportUndefined(self: *MachO, ctx: anytype) !void {
- const count = ctx.unresolved.count();
+pub fn reportUndefined(self: *MachO) !void {
+ const count = self.unresolved.count();
if (count == 0) return;
const gpa = self.base.allocator;
try self.misc_errors.ensureUnusedCapacity(gpa, count);
- for (ctx.unresolved.keys()) |global_index| {
- const global = ctx.globals.items[global_index];
- const sym_name = ctx.getSymbolName(global);
+ for (self.unresolved.keys()) |global_index| {
+ const global = self.globals.items[global_index];
+ const sym_name = self.getSymbolName(global);
const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1;
var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes);
defer notes.deinit();
if (global.getFile()) |file| {
- const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{ctx.objects.items[file].name});
+ const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{
+ self.objects.items[file].name,
+ });
notes.appendAssumeCapacity(.{ .msg = note });
}
@@ -4051,6 +4908,19 @@ pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytyp
return i;
}
+pub fn logSegments(self: *MachO) void {
+ log.debug("segments:", .{});
+ for (self.segments.items, 0..) |segment, i| {
+ log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
+ i,
+ segment.segName(),
+ segment.fileoff,
+ segment.vmaddr,
+ segment.vmsize,
+ });
+ }
+}
+
pub fn logSections(self: *MachO) void {
log.debug("sections:", .{});
for (self.sections.items(.header), 0..) |header, i| {
@@ -4065,9 +4935,7 @@ pub fn logSections(self: *MachO) void {
}
}
-fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
- @memset(buf[0..4], '_');
- @memset(buf[4..], ' ');
+fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
if (sym.sect()) {
buf[0] = 's';
}
@@ -4090,56 +4958,110 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
pub fn logSymtab(self: *MachO) void {
var buf: [4]u8 = undefined;
- log.debug("symtab:", .{});
+ const scoped_log = std.log.scoped(.symtab);
+
+ scoped_log.debug("locals:", .{});
+ for (self.objects.items, 0..) |object, id| {
+ scoped_log.debug(" object({d}): {s}", .{ id, object.name });
+ if (object.in_symtab == null) continue;
+ for (object.symtab, 0..) |sym, sym_id| {
+ @memset(&buf, '_');
+ scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
+ sym_id,
+ object.getSymbolName(@as(u32, @intCast(sym_id))),
+ sym.n_value,
+ sym.n_sect,
+ logSymAttributes(sym, &buf),
+ });
+ }
+ }
+ scoped_log.debug(" object(-1)", .{});
for (self.locals.items, 0..) |sym, sym_id| {
- const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
- const def_index = if (sym.undf() and !sym.tentative())
- @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
- else
- sym.n_sect + 1;
- log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
+ if (sym.undf()) continue;
+ scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
sym_id,
- self.strtab.get(sym.n_strx),
+ self.strtab.get(sym.n_strx).?,
sym.n_value,
- where,
- def_index,
+ sym.n_sect,
logSymAttributes(sym, &buf),
});
}
- log.debug("globals table:", .{});
- for (self.globals.items) |global| {
- const name = self.getSymbolName(global);
- log.debug(" {s} => %{d} in object({?d})", .{ name, global.sym_index, global.file });
+ scoped_log.debug("exports:", .{});
+ for (self.globals.items, 0..) |global, i| {
+ const sym = self.getSymbol(global);
+ if (sym.undf()) continue;
+ if (sym.n_desc == MachO.N_DEAD) continue;
+ scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
+ i,
+ self.getSymbolName(global),
+ sym.n_value,
+ sym.n_sect,
+ logSymAttributes(sym, &buf),
+ global.file,
+ });
+ }
+
+ scoped_log.debug("imports:", .{});
+ for (self.globals.items, 0..) |global, i| {
+ const sym = self.getSymbol(global);
+ if (!sym.undf()) continue;
+ if (sym.n_desc == MachO.N_DEAD) continue;
+ const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
+ scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
+ i,
+ self.getSymbolName(global),
+ sym.n_value,
+ ord,
+ logSymAttributes(sym, &buf),
+ });
}
- log.debug("GOT entries:", .{});
- log.debug("{}", .{self.got_table});
+ scoped_log.debug("GOT entries:", .{});
+ scoped_log.debug("{}", .{self.got_table});
+
+ scoped_log.debug("TLV pointers:", .{});
+ scoped_log.debug("{}", .{self.tlv_ptr_table});
- log.debug("stubs entries:", .{});
- log.debug("{}", .{self.stub_table});
+ scoped_log.debug("stubs entries:", .{});
+ scoped_log.debug("{}", .{self.stubs_table});
+
+ scoped_log.debug("thunks:", .{});
+ for (self.thunks.items, 0..) |thunk, i| {
+ scoped_log.debug(" thunk({d})", .{i});
+ const slice = thunk.targets.slice();
+ for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
+ const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
+ const atom = self.getAtom(atom_index);
+ const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
+ const target_addr = switch (tag) {
+ .stub => self.getStubsEntryAddress(target).?,
+ .atom => self.getSymbol(target).n_value,
+ };
+ scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
+ j,
+ atom_sym.n_value,
+ @tagName(tag),
+ self.getSymbolName(target),
+ target_addr,
+ });
+ }
+ }
}
pub fn logAtoms(self: *MachO) void {
log.debug("atoms:", .{});
-
const slice = self.sections.slice();
- for (slice.items(.last_atom_index), 0..) |last_atom_index, i| {
- var atom_index = last_atom_index orelse continue;
- const header = slice.items(.header)[i];
-
- while (true) {
- const atom = self.getAtom(atom_index);
- if (atom.prev_index) |prev_index| {
- atom_index = prev_index;
- } else break;
- }
+ for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
+ var atom_index = first_atom_index orelse continue;
+ const header = slice.items(.header)[sect_id];
log.debug("{s},{s}", .{ header.segName(), header.sectName() });
while (true) {
- self.logAtom(atom_index);
const atom = self.getAtom(atom_index);
+ self.logAtom(atom_index, log);
+
if (atom.next_index) |next_index| {
atom_index = next_index;
} else break;
@@ -4147,18 +5069,50 @@ pub fn logAtoms(self: *MachO) void {
}
}
-pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
+pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void {
+ if (!build_options.enable_logging) return;
+
const atom = self.getAtom(atom_index);
- const sym = atom.getSymbol(self);
- const sym_name = atom.getName(self);
- log.debug(" ATOM(%{?d}, '{s}') @ {x} sizeof({x}) in object({?d}) in sect({d})", .{
- atom.getSymbolIndex(),
+ const sym = self.getSymbol(atom.getSymbolWithLoc());
+ const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
+ logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
+ atom_index,
+ atom.sym_index,
sym_name,
sym.n_value,
atom.size,
- atom.file,
- sym.n_sect + 1,
+ atom.alignment,
+ atom.getFile(),
+ sym.n_sect,
});
+
+ if (atom.getFile() != null) {
+ var it = Atom.getInnerSymbolsIterator(self, atom_index);
+ while (it.next()) |sym_loc| {
+ const inner = self.getSymbol(sym_loc);
+ const inner_name = self.getSymbolName(sym_loc);
+ const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
+
+ logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
+ sym_loc.sym_index,
+ inner_name,
+ inner.n_value,
+ offset,
+ });
+ }
+
+ if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
+ const alias = self.getSymbol(sym_loc);
+ const alias_name = self.getSymbolName(sym_loc);
+
+ logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
+ sym_loc.sym_index,
+ alias_name,
+ alias.n_value,
+ 0,
+ });
+ }
+ }
}
const MachO = @This();
@@ -4197,6 +5151,7 @@ const Cache = std.Build.Cache;
const CodeSignature = @import("MachO/CodeSignature.zig");
const Compilation = @import("../Compilation.zig");
const Dwarf = File.Dwarf;
+const DwarfInfo = @import("DwarfInfo.zig");
const Dylib = @import("MachO/Dylib.zig");
const File = link.File;
const Object = @import("MachO/Object.zig");
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index f3922f6ff9..20a191281e 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -1,15 +1,3 @@
-const Archive = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Object = @import("Object.zig");
-
file: fs.File,
fat_offset: u64,
name: []const u8,
@@ -215,3 +203,15 @@ pub fn parseObject(self: Archive, gpa: Allocator, offset: u32) !Object {
return object;
}
+
+const Archive = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Object = @import("Object.zig");
diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig
index 73099184e0..bde6b09583 100644
--- a/src/link/MachO/Atom.zig
+++ b/src/link/MachO/Atom.zig
@@ -105,8 +105,7 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
return surplus >= MachO.min_text_capacity;
}
-pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
- const gpa = zld.gpa;
+pub fn getOutputSection(macho_file: *MachO, sect: macho.section_64) !?u8 {
const segname = sect.segName();
const sectname = sect.sectName();
const res: ?u8 = blk: {
@@ -126,20 +125,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
}
if (sect.isCode()) {
- if (zld.text_section_index == null) {
- zld.text_section_index = try MachO.initSection(
- gpa,
- zld,
- "__TEXT",
- "__text",
- .{
- .flags = macho.S_REGULAR |
- macho.S_ATTR_PURE_INSTRUCTIONS |
- macho.S_ATTR_SOME_INSTRUCTIONS,
- },
- );
+ if (macho_file.text_section_index == null) {
+ macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
+ .flags = macho.S_REGULAR |
+ macho.S_ATTR_PURE_INSTRUCTIONS |
+ macho.S_ATTR_SOME_INSTRUCTIONS,
+ });
}
- break :blk zld.text_section_index.?;
+ break :blk macho_file.text_section_index.?;
}
if (sect.isDebug()) {
@@ -151,42 +144,26 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
macho.S_8BYTE_LITERALS,
macho.S_16BYTE_LITERALS,
=> {
- break :blk zld.getSectionByName("__TEXT", "__const") orelse try MachO.initSection(
- gpa,
- zld,
- "__TEXT",
- "__const",
- .{},
- );
+ break :blk macho_file.getSectionByName("__TEXT", "__const") orelse
+ try macho_file.initSection("__TEXT", "__const", .{});
},
macho.S_CSTRING_LITERALS => {
if (mem.startsWith(u8, sectname, "__objc")) {
- break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
- gpa,
- zld,
- segname,
- sectname,
- .{},
- );
+ break :blk macho_file.getSectionByName(segname, sectname) orelse
+ try macho_file.initSection(segname, sectname, .{});
}
- break :blk zld.getSectionByName("__TEXT", "__cstring") orelse try MachO.initSection(
- gpa,
- zld,
- "__TEXT",
- "__cstring",
- .{ .flags = macho.S_CSTRING_LITERALS },
- );
+ break :blk macho_file.getSectionByName("__TEXT", "__cstring") orelse
+ try macho_file.initSection("__TEXT", "__cstring", .{
+ .flags = macho.S_CSTRING_LITERALS,
+ });
},
macho.S_MOD_INIT_FUNC_POINTERS,
macho.S_MOD_TERM_FUNC_POINTERS,
=> {
- break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(
- gpa,
- zld,
- "__DATA_CONST",
- sectname,
- .{ .flags = sect.flags },
- );
+ break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
+ try macho_file.initSection("__DATA_CONST", sectname, .{
+ .flags = sect.flags,
+ });
},
macho.S_LITERAL_POINTERS,
macho.S_ZEROFILL,
@@ -195,23 +172,14 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
macho.S_THREAD_LOCAL_REGULAR,
macho.S_THREAD_LOCAL_ZEROFILL,
=> {
- break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
- gpa,
- zld,
- segname,
- sectname,
- .{ .flags = sect.flags },
- );
+ break :blk macho_file.getSectionByName(segname, sectname) orelse
+ try macho_file.initSection(segname, sectname, .{
+ .flags = sect.flags,
+ });
},
macho.S_COALESCED => {
- break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
- gpa,
- zld,
-
- segname,
- sectname,
- .{},
- );
+ break :blk macho_file.getSectionByName(segname, sectname) orelse
+ try macho_file.initSection(segname, sectname, .{});
},
macho.S_REGULAR => {
if (mem.eql(u8, segname, "__TEXT")) {
@@ -221,13 +189,8 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
mem.eql(u8, sectname, "__gosymtab") or
mem.eql(u8, sectname, "__gopclntab"))
{
- break :blk zld.getSectionByName("__TEXT", sectname) orelse try MachO.initSection(
- gpa,
- zld,
- "__TEXT",
- sectname,
- .{},
- );
+ break :blk macho_file.getSectionByName("__TEXT", sectname) orelse
+ try macho_file.initSection("__TEXT", sectname, .{});
}
}
if (mem.eql(u8, segname, "__DATA")) {
@@ -236,33 +199,17 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
mem.eql(u8, sectname, "__objc_classlist") or
mem.eql(u8, sectname, "__objc_imageinfo"))
{
- break :blk zld.getSectionByName("__DATA_CONST", sectname) orelse try MachO.initSection(
- gpa,
- zld,
- "__DATA_CONST",
- sectname,
- .{},
- );
+ break :blk macho_file.getSectionByName("__DATA_CONST", sectname) orelse
+ try macho_file.initSection("__DATA_CONST", sectname, .{});
} else if (mem.eql(u8, sectname, "__data")) {
- if (zld.data_section_index == null) {
- zld.data_section_index = try MachO.initSection(
- gpa,
- zld,
- "__DATA",
- "__data",
- .{},
- );
+ if (macho_file.data_section_index == null) {
+ macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
}
- break :blk zld.data_section_index.?;
+ break :blk macho_file.data_section_index.?;
}
}
- break :blk zld.getSectionByName(segname, sectname) orelse try MachO.initSection(
- gpa,
- zld,
- segname,
- sectname,
- .{},
- );
+ break :blk macho_file.getSectionByName(segname, sectname) orelse
+ try macho_file.initSection(segname, sectname, .{});
},
else => break :blk null,
}
@@ -270,29 +217,29 @@ pub fn getOutputSection(zld: *Zld, sect: macho.section_64) !?u8 {
// TODO we can do this directly in the selection logic above.
// Or is it not worth it?
- if (zld.data_const_section_index == null) {
- if (zld.getSectionByName("__DATA_CONST", "__const")) |index| {
- zld.data_const_section_index = index;
+ if (macho_file.data_const_section_index == null) {
+ if (macho_file.getSectionByName("__DATA_CONST", "__const")) |index| {
+ macho_file.data_const_section_index = index;
}
}
- if (zld.thread_vars_section_index == null) {
- if (zld.getSectionByName("__DATA", "__thread_vars")) |index| {
- zld.thread_vars_section_index = index;
+ if (macho_file.thread_vars_section_index == null) {
+ if (macho_file.getSectionByName("__DATA", "__thread_vars")) |index| {
+ macho_file.thread_vars_section_index = index;
}
}
- if (zld.thread_data_section_index == null) {
- if (zld.getSectionByName("__DATA", "__thread_data")) |index| {
- zld.thread_data_section_index = index;
+ if (macho_file.thread_data_section_index == null) {
+ if (macho_file.getSectionByName("__DATA", "__thread_data")) |index| {
+ macho_file.thread_data_section_index = index;
}
}
- if (zld.thread_bss_section_index == null) {
- if (zld.getSectionByName("__DATA", "__thread_bss")) |index| {
- zld.thread_bss_section_index = index;
+ if (macho_file.thread_bss_section_index == null) {
+ if (macho_file.getSectionByName("__DATA", "__thread_bss")) |index| {
+ macho_file.thread_bss_section_index = index;
}
}
- if (zld.bss_section_index == null) {
- if (zld.getSectionByName("__DATA", "__bss")) |index| {
- zld.bss_section_index = index;
+ if (macho_file.bss_section_index == null) {
+ if (macho_file.getSectionByName("__DATA", "__bss")) |index| {
+ macho_file.bss_section_index = index;
}
}
@@ -383,8 +330,8 @@ const InnerSymIterator = struct {
/// Returns an iterator over potentially contained symbols.
/// Panics when called on a synthetic Atom.
-pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
- const atom = zld.getAtom(atom_index);
+pub fn getInnerSymbolsIterator(macho_file: *MachO, atom_index: Index) InnerSymIterator {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null);
return .{
.sym_index = atom.inner_sym_index,
@@ -397,11 +344,11 @@ pub fn getInnerSymbolsIterator(zld: *Zld, atom_index: Index) InnerSymIterator {
/// An alias symbol is used to represent the start of an input section
/// if there were no symbols defined within that range.
/// Alias symbols are only used on x86_64.
-pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
- const atom = zld.getAtom(atom_index);
+pub fn getSectionAlias(macho_file: *MachO, atom_index: Index) ?SymbolWithLoc {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null);
- const object = zld.objects.items[atom.getFile().?];
+ const object = macho_file.objects.items[atom.getFile().?];
const nbase = @as(u32, @intCast(object.in_symtab.?.len));
const ntotal = @as(u32, @intCast(object.symtab.len));
var sym_index: u32 = nbase;
@@ -418,13 +365,13 @@ pub fn getSectionAlias(zld: *Zld, atom_index: Index) ?SymbolWithLoc {
/// Given an index into a contained symbol within, calculates an offset wrt
/// the start of this Atom.
-pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
- const atom = zld.getAtom(atom_index);
+pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u32) u64 {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null);
if (atom.sym_index == sym_index) return 0;
- const object = zld.objects.items[atom.getFile().?];
+ const object = macho_file.objects.items[atom.getFile().?];
const source_sym = object.getSourceSymbol(sym_index).?;
const base_addr = if (object.getSourceSymbol(atom.sym_index)) |sym|
sym.n_value
@@ -437,14 +384,14 @@ pub fn calcInnerSymbolOffset(zld: *Zld, atom_index: Index, sym_index: u32) u64 {
return source_sym.n_value - base_addr;
}
-pub fn scanAtomRelocs(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
- const arch = zld.options.target.cpu.arch;
- const atom = zld.getAtom(atom_index);
+pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+ const arch = macho_file.base.options.target.cpu.arch;
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null); // synthetic atoms do not have relocs
return switch (arch) {
- .aarch64 => scanAtomRelocsArm64(zld, atom_index, relocs),
- .x86_64 => scanAtomRelocsX86(zld, atom_index, relocs),
+ .aarch64 => scanAtomRelocsArm64(macho_file, atom_index, relocs),
+ .x86_64 => scanAtomRelocsX86(macho_file, atom_index, relocs),
else => unreachable,
};
}
@@ -454,11 +401,11 @@ const RelocContext = struct {
base_offset: i32 = 0,
};
-pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
- const atom = zld.getAtom(atom_index);
+pub fn getRelocContext(macho_file: *MachO, atom_index: Index) RelocContext {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null); // synthetic atoms do not have relocs
- const object = zld.objects.items[atom.getFile().?];
+ const object = macho_file.objects.items[atom.getFile().?];
if (object.getSourceSymbol(atom.sym_index)) |source_sym| {
const source_sect = object.getSourceSection(source_sym.n_sect - 1);
return .{
@@ -475,7 +422,7 @@ pub fn getRelocContext(zld: *Zld, atom_index: Index) RelocContext {
};
}
-pub fn parseRelocTarget(zld: *Zld, ctx: struct {
+pub fn parseRelocTarget(macho_file: *MachO, ctx: struct {
object_id: u32,
rel: macho.relocation_info,
code: []const u8,
@@ -485,7 +432,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
const tracy = trace(@src());
defer tracy.end();
- const object = &zld.objects.items[ctx.object_id];
+ const object = &macho_file.objects.items[ctx.object_id];
log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name });
const sym_index = if (ctx.rel.r_extern == 0) sym_index: {
@@ -498,7 +445,7 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
else
mem.readIntLittle(u32, ctx.code[rel_offset..][0..4]);
} else blk: {
- assert(zld.options.target.cpu.arch == .x86_64);
+ assert(macho_file.base.options.target.cpu.arch == .x86_64);
const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) {
.X86_64_RELOC_SIGNED => 0,
.X86_64_RELOC_SIGNED_1 => 1,
@@ -517,35 +464,39 @@ pub fn parseRelocTarget(zld: *Zld, ctx: struct {
} else object.reverse_symtab_lookup[ctx.rel.r_symbolnum];
const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 };
- const sym = zld.getSymbol(sym_loc);
+ const sym = macho_file.getSymbol(sym_loc);
const target = if (sym.sect() and !sym.ext())
sym_loc
else if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
+ macho_file.globals.items[global_index]
else
sym_loc;
log.debug(" | target %{d} ('{s}') in object({?d})", .{
target.sym_index,
- zld.getSymbolName(target),
+ macho_file.getSymbolName(target),
target.getFile(),
});
return target;
}
-pub fn getRelocTargetAtomIndex(zld: *Zld, target: SymbolWithLoc) ?Index {
+pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index {
if (target.getFile() == null) {
- const target_sym_name = zld.getSymbolName(target);
+ const target_sym_name = macho_file.getSymbolName(target);
if (mem.eql(u8, "__mh_execute_header", target_sym_name)) return null;
if (mem.eql(u8, "___dso_handle", target_sym_name)) return null;
unreachable; // referenced symbol not found
}
- const object = zld.objects.items[target.getFile().?];
+ const object = macho_file.objects.items[target.getFile().?];
return object.getAtomIndexForSymbol(target.sym_index);
}
-fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+fn scanAtomRelocsArm64(
+ macho_file: *MachO,
+ atom_index: Index,
+ relocs: []align(1) const macho.relocation_info,
+) !void {
for (relocs) |rel| {
const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
@@ -556,8 +507,8 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
if (rel.r_extern == 0) continue;
- const atom = zld.getAtom(atom_index);
- const object = &zld.objects.items[atom.getFile().?];
+ const atom = macho_file.getAtom(atom_index);
+ const object = &macho_file.objects.items[atom.getFile().?];
const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
const sym_loc = SymbolWithLoc{
.sym_index = sym_index,
@@ -565,35 +516,39 @@ fn scanAtomRelocsArm64(zld: *Zld, atom_index: Index, relocs: []align(1) const ma
};
const target = if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
+ macho_file.globals.items[global_index]
else
sym_loc;
switch (rel_type) {
.ARM64_RELOC_BRANCH26 => {
// TODO rewrite relocation
- const sym = zld.getSymbol(target);
- if (sym.undf()) try zld.addStubEntry(target);
+ const sym = macho_file.getSymbol(target);
+ if (sym.undf()) try macho_file.addStubEntry(target);
},
.ARM64_RELOC_GOT_LOAD_PAGE21,
.ARM64_RELOC_GOT_LOAD_PAGEOFF12,
.ARM64_RELOC_POINTER_TO_GOT,
=> {
// TODO rewrite relocation
- try zld.addGotEntry(target);
+ try macho_file.addGotEntry(target);
},
.ARM64_RELOC_TLVP_LOAD_PAGE21,
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
=> {
- const sym = zld.getSymbol(target);
- if (sym.undf()) try zld.addTlvPtrEntry(target);
+ const sym = macho_file.getSymbol(target);
+ if (sym.undf()) try macho_file.addTlvPtrEntry(target);
},
else => {},
}
}
}
-fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void {
+fn scanAtomRelocsX86(
+ macho_file: *MachO,
+ atom_index: Index,
+ relocs: []align(1) const macho.relocation_info,
+) !void {
for (relocs) |rel| {
const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
@@ -604,8 +559,8 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
if (rel.r_extern == 0) continue;
- const atom = zld.getAtom(atom_index);
- const object = &zld.objects.items[atom.getFile().?];
+ const atom = macho_file.getAtom(atom_index);
+ const object = &macho_file.objects.items[atom.getFile().?];
const sym_index = object.reverse_symtab_lookup[rel.r_symbolnum];
const sym_loc = SymbolWithLoc{
.sym_index = sym_index,
@@ -613,23 +568,23 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
};
const target = if (object.getGlobal(sym_index)) |global_index|
- zld.globals.items[global_index]
+ macho_file.globals.items[global_index]
else
sym_loc;
switch (rel_type) {
.X86_64_RELOC_BRANCH => {
// TODO rewrite relocation
- const sym = zld.getSymbol(target);
- if (sym.undf()) try zld.addStubEntry(target);
+ const sym = macho_file.getSymbol(target);
+ if (sym.undf()) try macho_file.addStubEntry(target);
},
.X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
// TODO rewrite relocation
- try zld.addGotEntry(target);
+ try macho_file.addGotEntry(target);
},
.X86_64_RELOC_TLV => {
- const sym = zld.getSymbol(target);
- if (sym.undf()) try zld.addTlvPtrEntry(target);
+ const sym = macho_file.getSymbol(target);
+ if (sym.undf()) try macho_file.addTlvPtrEntry(target);
},
else => {},
}
@@ -637,53 +592,53 @@ fn scanAtomRelocsX86(zld: *Zld, atom_index: Index, relocs: []align(1) const mach
}
pub fn resolveRelocs(
- zld: *Zld,
+ macho_file: *MachO,
atom_index: Index,
atom_code: []u8,
atom_relocs: []align(1) const macho.relocation_info,
) !void {
- const arch = zld.options.target.cpu.arch;
- const atom = zld.getAtom(atom_index);
+ const arch = macho_file.base.options.target.cpu.arch;
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null); // synthetic atoms do not have relocs
log.debug("resolving relocations in ATOM(%{d}, '{s}')", .{
atom.sym_index,
- zld.getSymbolName(atom.getSymbolWithLoc()),
+ macho_file.getSymbolName(atom.getSymbolWithLoc()),
});
- const ctx = getRelocContext(zld, atom_index);
+ const ctx = getRelocContext(macho_file, atom_index);
return switch (arch) {
- .aarch64 => resolveRelocsArm64(zld, atom_index, atom_code, atom_relocs, ctx),
- .x86_64 => resolveRelocsX86(zld, atom_index, atom_code, atom_relocs, ctx),
+ .aarch64 => resolveRelocsArm64(macho_file, atom_index, atom_code, atom_relocs, ctx),
+ .x86_64 => resolveRelocsX86(macho_file, atom_index, atom_code, atom_relocs, ctx),
else => unreachable,
};
}
-pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u64 {
- const target_atom_index = getRelocTargetAtomIndex(zld, target) orelse {
+pub fn getRelocTargetAddress(macho_file: *MachO, target: SymbolWithLoc, is_tlv: bool) !u64 {
+ const target_atom_index = getRelocTargetAtomIndex(macho_file, target) orelse {
// If there is no atom for target, we still need to check for special, atom-less
// symbols such as `___dso_handle`.
- const target_name = zld.getSymbolName(target);
- const atomless_sym = zld.getSymbol(target);
+ const target_name = macho_file.getSymbolName(target);
+ const atomless_sym = macho_file.getSymbol(target);
log.debug(" | atomless target '{s}'", .{target_name});
return atomless_sym.n_value;
};
- const target_atom = zld.getAtom(target_atom_index);
+ const target_atom = macho_file.getAtom(target_atom_index);
log.debug(" | target ATOM(%{d}, '{s}') in object({?})", .{
target_atom.sym_index,
- zld.getSymbolName(target_atom.getSymbolWithLoc()),
+ macho_file.getSymbolName(target_atom.getSymbolWithLoc()),
target_atom.getFile(),
});
- const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
+ const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
assert(target_sym.n_desc != MachO.N_DEAD);
// If `target` is contained within the target atom, pull its address value.
const offset = if (target_atom.getFile() != null) blk: {
- const object = zld.objects.items[target_atom.getFile().?];
+ const object = macho_file.objects.items[target_atom.getFile().?];
break :blk if (object.getSourceSymbol(target.sym_index)) |_|
- Atom.calcInnerSymbolOffset(zld, target_atom_index, target.sym_index)
+ Atom.calcInnerSymbolOffset(macho_file, target_atom_index, target.sym_index)
else
0; // section alias
} else 0;
@@ -694,9 +649,9 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
// * wrt to __thread_data if defined, then
// * wrt to __thread_bss
const sect_id: u16 = sect_id: {
- if (zld.thread_data_section_index) |i| {
+ if (macho_file.thread_data_section_index) |i| {
break :sect_id i;
- } else if (zld.thread_bss_section_index) |i| {
+ } else if (macho_file.thread_bss_section_index) |i| {
break :sect_id i;
} else {
log.err("threadlocal variables present but no initializer sections found", .{});
@@ -705,20 +660,20 @@ pub fn getRelocTargetAddress(zld: *Zld, target: SymbolWithLoc, is_tlv: bool) !u6
return error.FailedToResolveRelocationTarget;
}
};
- break :base_address zld.sections.items(.header)[sect_id].addr;
+ break :base_address macho_file.sections.items(.header)[sect_id].addr;
} else 0;
return target_sym.n_value + offset - base_address;
}
fn resolveRelocsArm64(
- zld: *Zld,
+ macho_file: *MachO,
atom_index: Index,
atom_code: []u8,
atom_relocs: []align(1) const macho.relocation_info,
context: RelocContext,
) !void {
- const atom = zld.getAtom(atom_index);
- const object = zld.objects.items[atom.getFile().?];
+ const atom = macho_file.getAtom(atom_index);
+ const object = macho_file.objects.items[atom.getFile().?];
var addend: ?i64 = null;
var subtractor: ?SymbolWithLoc = null;
@@ -745,7 +700,7 @@ fn resolveRelocsArm64(
atom.getFile(),
});
- subtractor = parseRelocTarget(zld, .{
+ subtractor = parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = atom_code,
@@ -757,7 +712,7 @@ fn resolveRelocsArm64(
else => {},
}
- const target = parseRelocTarget(zld, .{
+ const target = parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = atom_code,
@@ -770,26 +725,26 @@ fn resolveRelocsArm64(
@tagName(rel_type),
rel.r_address,
target.sym_index,
- zld.getSymbolName(target),
+ macho_file.getSymbolName(target),
target.getFile(),
});
const source_addr = blk: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
break :blk source_sym.n_value + rel_offset;
};
const target_addr = blk: {
- if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;
- if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
- break :blk zld.getTlvPtrEntryAddress(target).?;
- if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())
- break :blk zld.getStubsEntryAddress(target).?;
+ if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
+ if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
+ break :blk macho_file.getTlvPtrEntryAddress(target).?;
+ if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
+ break :blk macho_file.getStubsEntryAddress(target).?;
const is_tlv = is_tlv: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[source_sym.n_sect - 1];
+ const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
};
- break :blk try getRelocTargetAddress(zld, target, is_tlv);
+ break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
};
log.debug(" | source_addr = 0x{x}", .{source_addr});
@@ -797,9 +752,9 @@ fn resolveRelocsArm64(
switch (rel_type) {
.ARM64_RELOC_BRANCH26 => {
log.debug(" source {s} (object({?})), target {s}", .{
- zld.getSymbolName(atom.getSymbolWithLoc()),
+ macho_file.getSymbolName(atom.getSymbolWithLoc()),
atom.getFile(),
- zld.getSymbolName(target),
+ macho_file.getSymbolName(target),
});
const displacement = if (Relocation.calcPcRelativeDisplacementArm64(
@@ -809,13 +764,13 @@ fn resolveRelocsArm64(
log.debug(" | target_addr = 0x{x}", .{target_addr});
break :blk disp;
} else |_| blk: {
- const thunk_index = zld.thunk_table.get(atom_index).?;
- const thunk = zld.thunks.items[thunk_index];
- const thunk_sym_loc = if (zld.getSymbol(target).undf())
- thunk.getTrampoline(zld, .stub, target).?
+ const thunk_index = macho_file.thunk_table.get(atom_index).?;
+ const thunk = macho_file.thunks.items[thunk_index];
+ const thunk_sym_loc = if (macho_file.getSymbol(target).undf())
+ thunk.getTrampoline(macho_file, .stub, target).?
else
- thunk.getTrampoline(zld, .atom, target).?;
- const thunk_addr = zld.getSymbol(thunk_sym_loc).n_value;
+ thunk.getTrampoline(macho_file, .atom, target).?;
+ const thunk_addr = macho_file.getSymbol(thunk_sym_loc).n_value;
log.debug(" | target_addr = 0x{x} (thunk)", .{thunk_addr});
break :blk try Relocation.calcPcRelativeDisplacementArm64(source_addr, thunk_addr);
};
@@ -944,7 +899,7 @@ fn resolveRelocsArm64(
}
};
- var inst = if (zld.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
+ var inst = if (macho_file.tlv_ptr_table.lookup.contains(target)) aarch64.Instruction{
.load_store_register = .{
.rt = reg_info.rd,
.rn = reg_info.rn,
@@ -992,7 +947,7 @@ fn resolveRelocsArm64(
const result = blk: {
if (subtractor) |sub| {
- const sym = zld.getSymbol(sub);
+ const sym = macho_file.getSymbol(sub);
break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + ptr_addend;
} else {
break :blk @as(i64, @intCast(target_addr)) + ptr_addend;
@@ -1016,14 +971,14 @@ fn resolveRelocsArm64(
}
fn resolveRelocsX86(
- zld: *Zld,
+ macho_file: *MachO,
atom_index: Index,
atom_code: []u8,
atom_relocs: []align(1) const macho.relocation_info,
context: RelocContext,
) !void {
- const atom = zld.getAtom(atom_index);
- const object = zld.objects.items[atom.getFile().?];
+ const atom = macho_file.getAtom(atom_index);
+ const object = macho_file.objects.items[atom.getFile().?];
var subtractor: ?SymbolWithLoc = null;
@@ -1041,7 +996,7 @@ fn resolveRelocsX86(
atom.getFile(),
});
- subtractor = parseRelocTarget(zld, .{
+ subtractor = parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = atom_code,
@@ -1053,7 +1008,7 @@ fn resolveRelocsX86(
else => {},
}
- const target = parseRelocTarget(zld, .{
+ const target = parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = atom_code,
@@ -1066,26 +1021,26 @@ fn resolveRelocsX86(
@tagName(rel_type),
rel.r_address,
target.sym_index,
- zld.getSymbolName(target),
+ macho_file.getSymbolName(target),
target.getFile(),
});
const source_addr = blk: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
+ const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
break :blk source_sym.n_value + rel_offset;
};
const target_addr = blk: {
- if (relocRequiresGot(zld, rel)) break :blk zld.getGotEntryAddress(target).?;
- if (relocIsStub(zld, rel) and zld.getSymbol(target).undf())
- break :blk zld.getStubsEntryAddress(target).?;
- if (relocIsTlv(zld, rel) and zld.getSymbol(target).undf())
- break :blk zld.getTlvPtrEntryAddress(target).?;
+ if (relocRequiresGot(macho_file, rel)) break :blk macho_file.getGotEntryAddress(target).?;
+ if (relocIsStub(macho_file, rel) and macho_file.getSymbol(target).undf())
+ break :blk macho_file.getStubsEntryAddress(target).?;
+ if (relocIsTlv(macho_file, rel) and macho_file.getSymbol(target).undf())
+ break :blk macho_file.getTlvPtrEntryAddress(target).?;
const is_tlv = is_tlv: {
- const source_sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[source_sym.n_sect - 1];
+ const source_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
break :is_tlv header.type() == macho.S_THREAD_LOCAL_VARIABLES;
};
- break :blk try getRelocTargetAddress(zld, target, is_tlv);
+ break :blk try getRelocTargetAddress(macho_file, target, is_tlv);
};
log.debug(" | source_addr = 0x{x}", .{source_addr});
@@ -1115,7 +1070,7 @@ fn resolveRelocsX86(
log.debug(" | target_addr = 0x{x}", .{adjusted_target_addr});
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
- if (zld.tlv_ptr_table.lookup.get(target) == null) {
+ if (macho_file.tlv_ptr_table.lookup.get(target) == null) {
// We need to rewrite the opcode from movq to leaq.
atom_code[rel_offset - 2] = 0x8d;
}
@@ -1170,7 +1125,7 @@ fn resolveRelocsX86(
const result = blk: {
if (subtractor) |sub| {
- const sym = zld.getSymbol(sub);
+ const sym = macho_file.getSymbol(sub);
break :blk @as(i64, @intCast(target_addr)) - @as(i64, @intCast(sym.n_value)) + addend;
} else {
break :blk @as(i64, @intCast(target_addr)) + addend;
@@ -1192,10 +1147,10 @@ fn resolveRelocsX86(
}
}
-pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
- const atom = zld.getAtom(atom_index);
+pub fn getAtomCode(macho_file: *MachO, atom_index: Index) []const u8 {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null); // Synthetic atom shouldn't need to inquire for code.
- const object = zld.objects.items[atom.getFile().?];
+ const object = macho_file.objects.items[atom.getFile().?];
const source_sym = object.getSourceSymbol(atom.sym_index) orelse {
// If there was no matching symbol present in the source symtab, this means
// we are dealing with either an entire section, or part of it, but also
@@ -1216,10 +1171,10 @@ pub fn getAtomCode(zld: *Zld, atom_index: Index) []const u8 {
return code[offset..][0..code_len];
}
-pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info {
- const atom = zld.getAtom(atom_index);
+pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.relocation_info {
+ const atom = macho_file.getAtom(atom_index);
assert(atom.getFile() != null); // Synthetic atom shouldn't need to unique for relocs.
- const object = zld.objects.items[atom.getFile().?];
+ const object = macho_file.objects.items[atom.getFile().?];
const cache = object.relocs_lookup[atom.sym_index];
const source_sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
@@ -1238,8 +1193,8 @@ pub fn getAtomRelocs(zld: *Zld, atom_index: Index) []const macho.relocation_info
return relocs[cache.start..][0..cache.len];
}
-pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
- switch (zld.options.target.cpu.arch) {
+pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool {
+ switch (macho_file.base.options.target.cpu.arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_GOT_LOAD_PAGE21,
.ARM64_RELOC_GOT_LOAD_PAGEOFF12,
@@ -1257,8 +1212,8 @@ pub fn relocRequiresGot(zld: *Zld, rel: macho.relocation_info) bool {
}
}
-pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
- switch (zld.options.target.cpu.arch) {
+pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool {
+ switch (macho_file.base.options.target.cpu.arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_TLVP_LOAD_PAGE21,
.ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
@@ -1273,8 +1228,8 @@ pub fn relocIsTlv(zld: *Zld, rel: macho.relocation_info) bool {
}
}
-pub fn relocIsStub(zld: *Zld, rel: macho.relocation_info) bool {
- switch (zld.options.target.cpu.arch) {
+pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool {
+ switch (macho_file.base.options.target.cpu.arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_BRANCH26 => return true,
else => return false,
@@ -1305,4 +1260,3 @@ const Arch = std.Target.Cpu.Arch;
const MachO = @import("../MachO.zig");
pub const Relocation = @import("Relocation.zig");
const SymbolWithLoc = MachO.SymbolWithLoc;
-const Zld = @import("zld.zig").Zld;
diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig
index f527ca3581..973d9a2591 100644
--- a/src/link/MachO/CodeSignature.zig
+++ b/src/link/MachO/CodeSignature.zig
@@ -1,17 +1,175 @@
-const CodeSignature = @This();
+page_size: u16,
+code_directory: CodeDirectory,
+requirements: ?Requirements = null,
+entitlements: ?Entitlements = null,
+signature: ?Signature = null,
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const mem = std.mem;
-const testing = std.testing;
+pub fn init(page_size: u16) CodeSignature {
+ return .{
+ .page_size = page_size,
+ .code_directory = CodeDirectory.init(page_size),
+ };
+}
-const Allocator = mem.Allocator;
-const Compilation = @import("../../Compilation.zig");
-const Hasher = @import("hasher.zig").ParallelHasher;
-const Sha256 = std.crypto.hash.sha2.Sha256;
+pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
+ self.code_directory.deinit(allocator);
+ if (self.requirements) |*req| {
+ req.deinit(allocator);
+ }
+ if (self.entitlements) |*ents| {
+ ents.deinit(allocator);
+ }
+ if (self.signature) |*sig| {
+ sig.deinit(allocator);
+ }
+}
+
+pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
+ const file = try fs.cwd().openFile(path, .{});
+ defer file.close();
+ const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
+ self.entitlements = .{ .inner = inner };
+}
+
+pub const WriteOpts = struct {
+ file: fs.File,
+ exec_seg_base: u64,
+ exec_seg_limit: u64,
+ file_size: u32,
+ output_mode: std.builtin.OutputMode,
+};
+
+pub fn writeAdhocSignature(
+ self: *CodeSignature,
+ comp: *const Compilation,
+ opts: WriteOpts,
+ writer: anytype,
+) !void {
+ const gpa = comp.gpa;
+
+ var header: macho.SuperBlob = .{
+ .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
+ .length = @sizeOf(macho.SuperBlob),
+ .count = 0,
+ };
+
+ var blobs = std.ArrayList(Blob).init(gpa);
+ defer blobs.deinit();
+
+ self.code_directory.inner.execSegBase = opts.exec_seg_base;
+ self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
+ self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
+ self.code_directory.inner.codeLimit = opts.file_size;
+
+ const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
+
+ try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
+ self.code_directory.code_slots.items.len = total_pages;
+ self.code_directory.inner.nCodeSlots = total_pages;
+
+ // Calculate hash for each page (in file) and write it to the buffer
+ var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
+ try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
+ .chunk_size = self.page_size,
+ .max_file_size = opts.file_size,
+ });
+
+ try blobs.append(.{ .code_directory = &self.code_directory });
+ header.length += @sizeOf(macho.BlobIndex);
+ header.count += 1;
+
+ var hash: [hash_size]u8 = undefined;
+
+ if (self.requirements) |*req| {
+ var buf = std.ArrayList(u8).init(gpa);
+ defer buf.deinit();
+ try req.write(buf.writer());
+ Sha256.hash(buf.items, &hash, .{});
+ self.code_directory.addSpecialHash(req.slotType(), hash);
+
+ try blobs.append(.{ .requirements = req });
+ header.count += 1;
+ header.length += @sizeOf(macho.BlobIndex) + req.size();
+ }
+
+ if (self.entitlements) |*ents| {
+ var buf = std.ArrayList(u8).init(gpa);
+ defer buf.deinit();
+ try ents.write(buf.writer());
+ Sha256.hash(buf.items, &hash, .{});
+ self.code_directory.addSpecialHash(ents.slotType(), hash);
+
+ try blobs.append(.{ .entitlements = ents });
+ header.count += 1;
+ header.length += @sizeOf(macho.BlobIndex) + ents.size();
+ }
+
+ if (self.signature) |*sig| {
+ try blobs.append(.{ .signature = sig });
+ header.count += 1;
+ header.length += @sizeOf(macho.BlobIndex) + sig.size();
+ }
+
+ self.code_directory.inner.hashOffset =
+ @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
+ self.code_directory.inner.length = self.code_directory.size();
+ header.length += self.code_directory.size();
+
+ try writer.writeIntBig(u32, header.magic);
+ try writer.writeIntBig(u32, header.length);
+ try writer.writeIntBig(u32, header.count);
+
+ var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
+ for (blobs.items) |blob| {
+ try writer.writeIntBig(u32, blob.slotType());
+ try writer.writeIntBig(u32, offset);
+ offset += blob.size();
+ }
+
+ for (blobs.items) |blob| {
+ try blob.write(writer);
+ }
+}
+
+pub fn size(self: CodeSignature) u32 {
+ var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
+ if (self.requirements) |req| {
+ ssize += @sizeOf(macho.BlobIndex) + req.size();
+ }
+ if (self.entitlements) |ent| {
+ ssize += @sizeOf(macho.BlobIndex) + ent.size();
+ }
+ if (self.signature) |sig| {
+ ssize += @sizeOf(macho.BlobIndex) + sig.size();
+ }
+ return ssize;
+}
+
+pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
+ var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
+ // Approx code slots
+ const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
+ ssize += total_pages * hash_size;
+ var n_special_slots: u32 = 0;
+ if (self.requirements) |req| {
+ ssize += @sizeOf(macho.BlobIndex) + req.size();
+ n_special_slots = @max(n_special_slots, req.slotType());
+ }
+ if (self.entitlements) |ent| {
+ ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
+ n_special_slots = @max(n_special_slots, ent.slotType());
+ }
+ if (self.signature) |sig| {
+ ssize += @sizeOf(macho.BlobIndex) + sig.size();
+ }
+ ssize += n_special_slots * hash_size;
+ return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
+}
+
+pub fn clear(self: *CodeSignature, allocator: Allocator) void {
+ self.code_directory.deinit(allocator);
+ self.code_directory = CodeDirectory.init(self.page_size);
+}
const hash_size = Sha256.digest_length;
@@ -218,175 +376,17 @@ const Signature = struct {
}
};
-page_size: u16,
-code_directory: CodeDirectory,
-requirements: ?Requirements = null,
-entitlements: ?Entitlements = null,
-signature: ?Signature = null,
-
-pub fn init(page_size: u16) CodeSignature {
- return .{
- .page_size = page_size,
- .code_directory = CodeDirectory.init(page_size),
- };
-}
-
-pub fn deinit(self: *CodeSignature, allocator: Allocator) void {
- self.code_directory.deinit(allocator);
- if (self.requirements) |*req| {
- req.deinit(allocator);
- }
- if (self.entitlements) |*ents| {
- ents.deinit(allocator);
- }
- if (self.signature) |*sig| {
- sig.deinit(allocator);
- }
-}
-
-pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const u8) !void {
- const file = try fs.cwd().openFile(path, .{});
- defer file.close();
- const inner = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
- self.entitlements = .{ .inner = inner };
-}
-
-pub const WriteOpts = struct {
- file: fs.File,
- exec_seg_base: u64,
- exec_seg_limit: u64,
- file_size: u32,
- output_mode: std.builtin.OutputMode,
-};
-
-pub fn writeAdhocSignature(
- self: *CodeSignature,
- comp: *const Compilation,
- opts: WriteOpts,
- writer: anytype,
-) !void {
- const gpa = comp.gpa;
-
- var header: macho.SuperBlob = .{
- .magic = macho.CSMAGIC_EMBEDDED_SIGNATURE,
- .length = @sizeOf(macho.SuperBlob),
- .count = 0,
- };
-
- var blobs = std.ArrayList(Blob).init(gpa);
- defer blobs.deinit();
-
- self.code_directory.inner.execSegBase = opts.exec_seg_base;
- self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
- self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
- self.code_directory.inner.codeLimit = opts.file_size;
-
- const total_pages = @as(u32, @intCast(mem.alignForward(usize, opts.file_size, self.page_size) / self.page_size));
-
- try self.code_directory.code_slots.ensureTotalCapacityPrecise(gpa, total_pages);
- self.code_directory.code_slots.items.len = total_pages;
- self.code_directory.inner.nCodeSlots = total_pages;
-
- // Calculate hash for each page (in file) and write it to the buffer
- var hasher = Hasher(Sha256){ .allocator = gpa, .thread_pool = comp.thread_pool };
- try hasher.hash(opts.file, self.code_directory.code_slots.items, .{
- .chunk_size = self.page_size,
- .max_file_size = opts.file_size,
- });
-
- try blobs.append(.{ .code_directory = &self.code_directory });
- header.length += @sizeOf(macho.BlobIndex);
- header.count += 1;
-
- var hash: [hash_size]u8 = undefined;
-
- if (self.requirements) |*req| {
- var buf = std.ArrayList(u8).init(gpa);
- defer buf.deinit();
- try req.write(buf.writer());
- Sha256.hash(buf.items, &hash, .{});
- self.code_directory.addSpecialHash(req.slotType(), hash);
-
- try blobs.append(.{ .requirements = req });
- header.count += 1;
- header.length += @sizeOf(macho.BlobIndex) + req.size();
- }
-
- if (self.entitlements) |*ents| {
- var buf = std.ArrayList(u8).init(gpa);
- defer buf.deinit();
- try ents.write(buf.writer());
- Sha256.hash(buf.items, &hash, .{});
- self.code_directory.addSpecialHash(ents.slotType(), hash);
-
- try blobs.append(.{ .entitlements = ents });
- header.count += 1;
- header.length += @sizeOf(macho.BlobIndex) + ents.size();
- }
-
- if (self.signature) |*sig| {
- try blobs.append(.{ .signature = sig });
- header.count += 1;
- header.length += @sizeOf(macho.BlobIndex) + sig.size();
- }
-
- self.code_directory.inner.hashOffset =
- @sizeOf(macho.CodeDirectory) + @as(u32, @intCast(self.code_directory.ident.len + 1 + self.code_directory.inner.nSpecialSlots * hash_size));
- self.code_directory.inner.length = self.code_directory.size();
- header.length += self.code_directory.size();
-
- try writer.writeIntBig(u32, header.magic);
- try writer.writeIntBig(u32, header.length);
- try writer.writeIntBig(u32, header.count);
-
- var offset: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) * @as(u32, @intCast(blobs.items.len));
- for (blobs.items) |blob| {
- try writer.writeIntBig(u32, blob.slotType());
- try writer.writeIntBig(u32, offset);
- offset += blob.size();
- }
-
- for (blobs.items) |blob| {
- try blob.write(writer);
- }
-}
-
-pub fn size(self: CodeSignature) u32 {
- var ssize: u32 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
- if (self.requirements) |req| {
- ssize += @sizeOf(macho.BlobIndex) + req.size();
- }
- if (self.entitlements) |ent| {
- ssize += @sizeOf(macho.BlobIndex) + ent.size();
- }
- if (self.signature) |sig| {
- ssize += @sizeOf(macho.BlobIndex) + sig.size();
- }
- return ssize;
-}
+const CodeSignature = @This();
-pub fn estimateSize(self: CodeSignature, file_size: u64) u32 {
- var ssize: u64 = @sizeOf(macho.SuperBlob) + @sizeOf(macho.BlobIndex) + self.code_directory.size();
- // Approx code slots
- const total_pages = mem.alignForward(u64, file_size, self.page_size) / self.page_size;
- ssize += total_pages * hash_size;
- var n_special_slots: u32 = 0;
- if (self.requirements) |req| {
- ssize += @sizeOf(macho.BlobIndex) + req.size();
- n_special_slots = @max(n_special_slots, req.slotType());
- }
- if (self.entitlements) |ent| {
- ssize += @sizeOf(macho.BlobIndex) + ent.size() + hash_size;
- n_special_slots = @max(n_special_slots, ent.slotType());
- }
- if (self.signature) |sig| {
- ssize += @sizeOf(macho.BlobIndex) + sig.size();
- }
- ssize += n_special_slots * hash_size;
- return @as(u32, @intCast(mem.alignForward(u64, ssize, @sizeOf(u64))));
-}
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const mem = std.mem;
+const testing = std.testing;
-pub fn clear(self: *CodeSignature, allocator: Allocator) void {
- self.code_directory.deinit(allocator);
- self.code_directory = CodeDirectory.init(self.page_size);
-}
+const Allocator = mem.Allocator;
+const Compilation = @import("../../Compilation.zig");
+const Hasher = @import("hasher.zig").ParallelHasher;
+const Sha256 = std.crypto.hash.sha2.Sha256;
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index 602ee1ed63..d20f32c14c 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -1,26 +1,3 @@
-const DebugSymbols = @This();
-
-const std = @import("std");
-const build_options = @import("build_options");
-const assert = std.debug.assert;
-const fs = std.fs;
-const link = @import("../../link.zig");
-const load_commands = @import("load_commands.zig");
-const log = std.log.scoped(.dsym);
-const macho = std.macho;
-const makeStaticString = MachO.makeStaticString;
-const math = std.math;
-const mem = std.mem;
-const padToIdeal = MachO.padToIdeal;
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Dwarf = @import("../Dwarf.zig");
-const MachO = @import("../MachO.zig");
-const Module = @import("../../Module.zig");
-const StringTable = @import("../strtab.zig").StringTable;
-const Type = @import("../../type.zig").Type;
-
allocator: Allocator,
dwarf: Dwarf,
file: fs.File,
@@ -569,3 +546,26 @@ pub fn getSection(self: DebugSymbols, sect: u8) macho.section_64 {
assert(sect < self.sections.items.len);
return self.sections.items[sect];
}
+
+const DebugSymbols = @This();
+
+const std = @import("std");
+const build_options = @import("build_options");
+const assert = std.debug.assert;
+const fs = std.fs;
+const link = @import("../../link.zig");
+const load_commands = @import("load_commands.zig");
+const log = std.log.scoped(.dsym);
+const macho = std.macho;
+const makeStaticString = MachO.makeStaticString;
+const math = std.math;
+const mem = std.mem;
+const padToIdeal = MachO.padToIdeal;
+const trace = @import("../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const Dwarf = @import("../Dwarf.zig");
+const MachO = @import("../MachO.zig");
+const Module = @import("../../Module.zig");
+const StringTable = @import("../strtab.zig").StringTable;
+const Type = @import("../../type.zig").Type;
diff --git a/src/link/MachO/DwarfInfo.zig b/src/link/MachO/DwarfInfo.zig
index 07d98e8e94..7b0536f60f 100644
--- a/src/link/MachO/DwarfInfo.zig
+++ b/src/link/MachO/DwarfInfo.zig
@@ -1,17 +1,3 @@
-const DwarfInfo = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const dwarf = std.dwarf;
-const leb = std.leb;
-const log = std.log.scoped(.macho);
-const math = std.math;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
-pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
-
debug_info: []const u8,
debug_abbrev: []const u8,
debug_str: []const u8,
@@ -501,3 +487,17 @@ fn getString(self: DwarfInfo, off: u64) []const u8 {
assert(off < self.debug_str.len);
return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.debug_str.ptr + @as(usize, @intCast(off)))), 0);
}
+
+const DwarfInfo = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const dwarf = std.dwarf;
+const leb = std.leb;
+const log = std.log.scoped(.macho);
+const math = std.math;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+pub const AbbrevLookupTable = std.AutoHashMap(u64, struct { pos: usize, len: usize });
+pub const SubprogramLookupByName = std.StringHashMap(struct { addr: u64, size: u64 });
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index c424343a4e..19a9eb8cd4 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -1,23 +1,3 @@
-const Dylib = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const fmt = std.fmt;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const fat = @import("fat.zig");
-const tapi = @import("../tapi.zig");
-
-const Allocator = mem.Allocator;
-const CrossTarget = std.zig.CrossTarget;
-const LibStub = tapi.LibStub;
-const LoadCommandIterator = macho.LoadCommandIterator;
-const MachO = @import("../MachO.zig");
-const Tbd = tapi.Tbd;
-
id: ?Id = null,
weak: bool = false,
/// Header is only set if Dylib is parsed directly from a binary and not a stub file.
@@ -546,3 +526,23 @@ pub fn parseFromStub(
}
}
}
+
+const Dylib = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const fmt = std.fmt;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const fat = @import("fat.zig");
+const tapi = @import("../tapi.zig");
+
+const Allocator = mem.Allocator;
+const CrossTarget = std.zig.CrossTarget;
+const LibStub = tapi.LibStub;
+const LoadCommandIterator = macho.LoadCommandIterator;
+const MachO = @import("../MachO.zig");
+const Tbd = tapi.Tbd;
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 3ab62ec191..4af0c3e7aa 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -2,31 +2,6 @@
//! Each Object is fully loaded into memory for easier
//! access into different data within.
-const Object = @This();
-
-const std = @import("std");
-const build_options = @import("build_options");
-const assert = std.debug.assert;
-const dwarf = std.dwarf;
-const eh_frame = @import("eh_frame.zig");
-const fs = std.fs;
-const io = std.io;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const sort = std.sort;
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const DwarfInfo = @import("DwarfInfo.zig");
-const LoadCommandIterator = macho.LoadCommandIterator;
-const MachO = @import("../MachO.zig");
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const UnwindInfo = @import("UnwindInfo.zig");
-const Zld = @import("zld.zig").Zld;
-
name: []const u8,
mtime: u64,
contents: []align(@alignOf(u64)) const u8,
@@ -359,25 +334,25 @@ fn sectionLessThanByAddress(ctx: void, lhs: SortedSection, rhs: SortedSection) b
return lhs.header.addr < rhs.header.addr;
}
-pub fn splitIntoAtoms(self: *Object, zld: *Zld, object_id: u32) !void {
+pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name });
- try self.splitRegularSections(zld, object_id);
- try self.parseEhFrameSection(zld, object_id);
- try self.parseUnwindInfo(zld, object_id);
- try self.parseDataInCode(zld.gpa);
+ try self.splitRegularSections(macho_file, object_id);
+ try self.parseEhFrameSection(macho_file, object_id);
+ try self.parseUnwindInfo(macho_file, object_id);
+ try self.parseDataInCode(macho_file.base.allocator);
}
/// Splits input regular sections into Atoms.
/// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section
/// into subsections where each subsection then represents an Atom.
-pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
- const gpa = zld.gpa;
+pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void {
+ const gpa = macho_file.base.allocator;
const sections = self.getSourceSections();
for (sections, 0..) |sect, id| {
if (sect.isDebug()) continue;
- const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse {
+ const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse {
log.debug(" unhandled section '{s},{s}'", .{ sect.segName(), sect.sectName() });
continue;
};
@@ -397,13 +372,13 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
if (self.in_symtab == null) {
for (sections, 0..) |sect, id| {
if (sect.isDebug()) continue;
- const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue;
+ const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
if (sect.size == 0) continue;
const sect_id = @as(u8, @intCast(id));
const sym_index = self.getSectionAliasSymbolIndex(sect_id);
const atom_index = try self.createAtomFromSubsection(
- zld,
+ macho_file,
object_id,
sym_index,
sym_index,
@@ -412,7 +387,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
sect.@"align",
out_sect_id,
);
- zld.addAtomToSection(atom_index);
+ macho_file.addAtomToSection(atom_index);
}
return;
}
@@ -456,17 +431,17 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
// Get output segment/section in the final artifact.
- const out_sect_id = (try Atom.getOutputSection(zld, sect)) orelse continue;
+ const out_sect_id = (try Atom.getOutputSection(macho_file, sect)) orelse continue;
log.debug(" output sect({d}, '{s},{s}')", .{
out_sect_id + 1,
- zld.sections.items(.header)[out_sect_id].segName(),
- zld.sections.items(.header)[out_sect_id].sectName(),
+ macho_file.sections.items(.header)[out_sect_id].segName(),
+ macho_file.sections.items(.header)[out_sect_id].sectName(),
});
try self.parseRelocs(gpa, section.id);
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1);
const sect_start_index = sect_sym_index + sect_loc.index;
@@ -482,7 +457,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
const sym_index = self.getSectionAliasSymbolIndex(sect_id);
const atom_size = first_sym.n_value - sect.addr;
const atom_index = try self.createAtomFromSubsection(
- zld,
+ macho_file,
object_id,
sym_index,
sym_index,
@@ -492,9 +467,9 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
out_sect_id,
);
if (!sect.isZerofill()) {
- try self.cacheRelocs(zld, atom_index);
+ try self.cacheRelocs(macho_file, atom_index);
}
- zld.addAtomToSection(atom_index);
+ macho_file.addAtomToSection(atom_index);
}
var next_sym_index = sect_start_index;
@@ -518,7 +493,7 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
sect.@"align";
const atom_index = try self.createAtomFromSubsection(
- zld,
+ macho_file,
object_id,
atom_sym_index,
atom_sym_index,
@@ -537,14 +512,14 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
self.atom_by_index_table[alias_index] = atom_index;
}
if (!sect.isZerofill()) {
- try self.cacheRelocs(zld, atom_index);
+ try self.cacheRelocs(macho_file, atom_index);
}
- zld.addAtomToSection(atom_index);
+ macho_file.addAtomToSection(atom_index);
}
} else {
const alias_index = self.getSectionAliasSymbolIndex(sect_id);
const atom_index = try self.createAtomFromSubsection(
- zld,
+ macho_file,
object_id,
alias_index,
sect_start_index,
@@ -554,16 +529,16 @@ pub fn splitRegularSections(self: *Object, zld: *Zld, object_id: u32) !void {
out_sect_id,
);
if (!sect.isZerofill()) {
- try self.cacheRelocs(zld, atom_index);
+ try self.cacheRelocs(macho_file, atom_index);
}
- zld.addAtomToSection(atom_index);
+ macho_file.addAtomToSection(atom_index);
}
}
}
fn createAtomFromSubsection(
self: *Object,
- zld: *Zld,
+ macho_file: *MachO,
object_id: u32,
sym_index: u32,
inner_sym_index: u32,
@@ -572,9 +547,9 @@ fn createAtomFromSubsection(
alignment: u32,
out_sect_id: u8,
) !Atom.Index {
- const gpa = zld.gpa;
- const atom_index = try zld.createAtom(sym_index, .{ .size = size, .alignment = alignment });
- const atom = zld.getAtomPtr(atom_index);
+ const gpa = macho_file.base.allocator;
+ const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment });
+ const atom = macho_file.getAtomPtr(atom_index);
atom.inner_sym_index = inner_sym_index;
atom.inner_nsyms_trailing = inner_nsyms_trailing;
atom.file = object_id + 1;
@@ -584,22 +559,22 @@ fn createAtomFromSubsection(
sym_index,
self.getSymbolName(sym_index),
out_sect_id + 1,
- zld.sections.items(.header)[out_sect_id].segName(),
- zld.sections.items(.header)[out_sect_id].sectName(),
+ macho_file.sections.items(.header)[out_sect_id].segName(),
+ macho_file.sections.items(.header)[out_sect_id].sectName(),
object_id,
});
try self.atoms.append(gpa, atom_index);
self.atom_by_index_table[sym_index] = atom_index;
- var it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (it.next()) |sym_loc| {
- const inner = zld.getSymbolPtr(sym_loc);
+ const inner = macho_file.getSymbolPtr(sym_loc);
inner.n_sect = out_sect_id + 1;
self.atom_by_index_table[sym_loc.sym_index] = atom_index;
}
- const out_sect = zld.sections.items(.header)[out_sect_id];
+ const out_sect = macho_file.sections.items(.header)[out_sect_id];
if (out_sect.isCode() and
mem.eql(u8, "__TEXT", out_sect.segName()) and
mem.eql(u8, "__text", out_sect.sectName()))
@@ -651,8 +626,8 @@ fn parseRelocs(self: *Object, gpa: Allocator, sect_id: u8) !void {
self.section_relocs_lookup.items[sect_id] = start;
}
-fn cacheRelocs(self: *Object, zld: *Zld, atom_index: Atom.Index) !void {
- const atom = zld.getAtom(atom_index);
+fn cacheRelocs(self: *Object, macho_file: *MachO, atom_index: Atom.Index) !void {
+ const atom = macho_file.getAtom(atom_index);
const source_sect_id = if (self.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
break :blk source_sym.n_sect - 1;
@@ -679,19 +654,19 @@ fn relocGreaterThan(ctx: void, lhs: macho.relocation_info, rhs: macho.relocation
return lhs.r_address > rhs.r_address;
}
-fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
+fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void {
const sect_id = self.eh_frame_sect_id orelse return;
const sect = self.getSourceSection(sect_id);
log.debug("parsing __TEXT,__eh_frame section", .{});
- const gpa = zld.gpa;
+ const gpa = macho_file.base.allocator;
- if (zld.eh_frame_section_index == null) {
- zld.eh_frame_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__eh_frame", .{});
+ if (macho_file.eh_frame_section_index == null) {
+ macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{});
}
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
try self.parseRelocs(gpa, sect_id);
const relocs = self.getRelocs(sect_id);
@@ -729,7 +704,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED)
break rel;
} else unreachable;
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = it.data[offset..],
@@ -744,7 +719,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
});
const target_sym_index = self.getSymbolByAddress(target_address, null);
const target = if (self.getGlobal(target_sym_index)) |global_index|
- zld.globals.items[global_index]
+ macho_file.globals.items[global_index]
else
SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 };
break :blk target;
@@ -770,7 +745,7 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
};
log.debug("FDE at offset {x} tracks {s}", .{
offset,
- zld.getSymbolName(actual_target),
+ macho_file.getSymbolName(actual_target),
});
try self.eh_frame_records_lookup.putNoClobber(gpa, actual_target, offset);
}
@@ -779,19 +754,17 @@ fn parseEhFrameSection(self: *Object, zld: *Zld, object_id: u32) !void {
}
}
-fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
- const gpa = zld.gpa;
- const cpu_arch = zld.options.target.cpu.arch;
+fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void {
+ const gpa = macho_file.base.allocator;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
const sect_id = self.unwind_info_sect_id orelse {
// If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`,
// we will try fully synthesising unwind info records to somewhat match Apple ld's
// approach. However, we will only synthesise DWARF records and nothing more. For this reason,
// we still create the output `__TEXT,__unwind_info` section.
if (self.hasEhFrameRecords()) {
- if (zld.unwind_info_section_index == null) {
- zld.unwind_info_section_index = try MachO.initSection(
- gpa,
- zld,
+ if (macho_file.unwind_info_section_index == null) {
+ macho_file.unwind_info_section_index = try macho_file.initSection(
"__TEXT",
"__unwind_info",
.{},
@@ -803,8 +776,8 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
log.debug("parsing unwind info in {s}", .{self.name});
- if (zld.unwind_info_section_index == null) {
- zld.unwind_info_section_index = try MachO.initSection(gpa, zld, "__TEXT", "__unwind_info", .{});
+ if (macho_file.unwind_info_section_index == null) {
+ macho_file.unwind_info_section_index = try macho_file.initSection("__TEXT", "__unwind_info", .{});
}
const unwind_records = self.getUnwindRecords();
@@ -839,7 +812,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
// Find function symbol that this record describes
const rel = relocs[rel_pos.start..][rel_pos.len - 1];
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = mem.asBytes(&record),
@@ -863,7 +836,7 @@ fn parseUnwindInfo(self: *Object, zld: *Zld, object_id: u32) !void {
};
log.debug("unwind record {d} tracks {s}", .{
record_id,
- zld.getSymbolName(actual_target),
+ macho_file.getSymbolName(actual_target),
});
try self.unwind_records_lookup.putNoClobber(gpa, actual_target, @intCast(record_id));
}
@@ -1094,3 +1067,27 @@ pub fn getEhFrameRecordsIterator(self: Object) eh_frame.Iterator {
pub fn hasDataInCode(self: Object) bool {
return self.data_in_code.items.len > 0;
}
+
+const Object = @This();
+
+const std = @import("std");
+const build_options = @import("build_options");
+const assert = std.debug.assert;
+const dwarf = std.dwarf;
+const eh_frame = @import("eh_frame.zig");
+const fs = std.fs;
+const io = std.io;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const sort = std.sort;
+const trace = @import("../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const DwarfInfo = @import("DwarfInfo.zig");
+const LoadCommandIterator = macho.LoadCommandIterator;
+const MachO = @import("../MachO.zig");
+const SymbolWithLoc = MachO.SymbolWithLoc;
+const UnwindInfo = @import("UnwindInfo.zig");
diff --git a/src/link/MachO/Trie.zig b/src/link/MachO/Trie.zig
index 962ead72fa..d86338f84b 100644
--- a/src/link/MachO/Trie.zig
+++ b/src/link/MachO/Trie.zig
@@ -28,248 +28,6 @@
//! After the optional exported symbol information is a byte of how many edges (0-255) that
//! this node has leaving it, followed by each edge. Each edge is a zero terminated UTF8 of
//! the addition chars in the symbol, followed by a uleb128 offset for the node that edge points to.
-const Trie = @This();
-
-const std = @import("std");
-const mem = std.mem;
-const leb = std.leb;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const testing = std.testing;
-const assert = std.debug.assert;
-const Allocator = mem.Allocator;
-
-pub const Node = struct {
- base: *Trie,
-
- /// Terminal info associated with this node.
- /// If this node is not a terminal node, info is null.
- terminal_info: ?struct {
- /// Export flags associated with this exported symbol.
- export_flags: u64,
- /// VM address offset wrt to the section this symbol is defined against.
- vmaddr_offset: u64,
- } = null,
-
- /// Offset of this node in the trie output byte stream.
- trie_offset: ?u64 = null,
-
- /// List of all edges originating from this node.
- edges: std.ArrayListUnmanaged(Edge) = .{},
-
- node_dirty: bool = true,
-
- /// Edge connecting to nodes in the trie.
- pub const Edge = struct {
- from: *Node,
- to: *Node,
- label: []u8,
-
- fn deinit(self: *Edge, allocator: Allocator) void {
- self.to.deinit(allocator);
- allocator.destroy(self.to);
- allocator.free(self.label);
- self.from = undefined;
- self.to = undefined;
- self.label = undefined;
- }
- };
-
- fn deinit(self: *Node, allocator: Allocator) void {
- for (self.edges.items) |*edge| {
- edge.deinit(allocator);
- }
- self.edges.deinit(allocator);
- }
-
- /// Inserts a new node starting from `self`.
- fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
- // Check for match with edges from this node.
- for (self.edges.items) |*edge| {
- const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
- if (match == 0) continue;
- if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
-
- // Found a match, need to splice up nodes.
- // From: A -> B
- // To: A -> C -> B
- const mid = try allocator.create(Node);
- mid.* = .{ .base = self.base };
- var to_label = try allocator.dupe(u8, edge.label[match..]);
- allocator.free(edge.label);
- const to_node = edge.to;
- edge.to = mid;
- edge.label = try allocator.dupe(u8, label[0..match]);
- self.base.node_count += 1;
-
- try mid.edges.append(allocator, .{
- .from = mid,
- .to = to_node,
- .label = to_label,
- });
-
- return if (match == label.len) mid else mid.put(allocator, label[match..]);
- }
-
- // Add a new node.
- const node = try allocator.create(Node);
- node.* = .{ .base = self.base };
- self.base.node_count += 1;
-
- try self.edges.append(allocator, .{
- .from = self,
- .to = node,
- .label = try allocator.dupe(u8, label),
- });
-
- return node;
- }
-
- /// Recursively parses the node from the input byte stream.
- fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
- self.node_dirty = true;
- const trie_offset = try reader.context.getPos();
- self.trie_offset = trie_offset;
-
- var nread: usize = 0;
-
- const node_size = try leb.readULEB128(u64, reader);
- if (node_size > 0) {
- const export_flags = try leb.readULEB128(u64, reader);
- // TODO Parse special flags.
- assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
- export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
-
- const vmaddr_offset = try leb.readULEB128(u64, reader);
-
- self.terminal_info = .{
- .export_flags = export_flags,
- .vmaddr_offset = vmaddr_offset,
- };
- }
-
- const nedges = try reader.readByte();
- self.base.node_count += nedges;
-
- nread += (try reader.context.getPos()) - trie_offset;
-
- var i: usize = 0;
- while (i < nedges) : (i += 1) {
- const edge_start_pos = try reader.context.getPos();
-
- const label = blk: {
- var label_buf = std.ArrayList(u8).init(allocator);
- while (true) {
- const next = try reader.readByte();
- if (next == @as(u8, 0))
- break;
- try label_buf.append(next);
- }
- break :blk try label_buf.toOwnedSlice();
- };
-
- const seek_to = try leb.readULEB128(u64, reader);
- const return_pos = try reader.context.getPos();
-
- nread += return_pos - edge_start_pos;
- try reader.context.seekTo(seek_to);
-
- const node = try allocator.create(Node);
- node.* = .{ .base = self.base };
-
- nread += try node.read(allocator, reader);
- try self.edges.append(allocator, .{
- .from = self,
- .to = node,
- .label = label,
- });
- try reader.context.seekTo(return_pos);
- }
-
- return nread;
- }
-
- /// Writes this node to a byte stream.
- /// The children of this node *are* not written to the byte stream
- /// recursively. To write all nodes to a byte stream in sequence,
- /// iterate over `Trie.ordered_nodes` and call this method on each node.
- /// This is one of the requirements of the MachO.
- /// Panics if `finalize` was not called before calling this method.
- fn write(self: Node, writer: anytype) !void {
- assert(!self.node_dirty);
- if (self.terminal_info) |info| {
- // Terminal node info: encode export flags and vmaddr offset of this symbol.
- var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
- var info_stream = std.io.fixedBufferStream(&info_buf);
- // TODO Implement for special flags.
- assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
- info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
- try leb.writeULEB128(info_stream.writer(), info.export_flags);
- try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
-
- // Encode the size of the terminal node info.
- var size_buf: [@sizeOf(u64)]u8 = undefined;
- var size_stream = std.io.fixedBufferStream(&size_buf);
- try leb.writeULEB128(size_stream.writer(), info_stream.pos);
-
- // Now, write them to the output stream.
- try writer.writeAll(size_buf[0..size_stream.pos]);
- try writer.writeAll(info_buf[0..info_stream.pos]);
- } else {
- // Non-terminal node is delimited by 0 byte.
- try writer.writeByte(0);
- }
- // Write number of edges (max legal number of edges is 256).
- try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
-
- for (self.edges.items) |edge| {
- // Write edge label and offset to next node in trie.
- try writer.writeAll(edge.label);
- try writer.writeByte(0);
- try leb.writeULEB128(writer, edge.to.trie_offset.?);
- }
- }
-
- const FinalizeResult = struct {
- /// Current size of this node in bytes.
- node_size: u64,
-
- /// True if the trie offset of this node in the output byte stream
- /// would need updating; false otherwise.
- updated: bool,
- };
-
- /// Updates offset of this node in the output byte stream.
- fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
- var stream = std.io.countingWriter(std.io.null_writer);
- var writer = stream.writer();
-
- var node_size: u64 = 0;
- if (self.terminal_info) |info| {
- try leb.writeULEB128(writer, info.export_flags);
- try leb.writeULEB128(writer, info.vmaddr_offset);
- try leb.writeULEB128(writer, stream.bytes_written);
- } else {
- node_size += 1; // 0x0 for non-terminal nodes
- }
- node_size += 1; // 1 byte for edge count
-
- for (self.edges.items) |edge| {
- const next_node_offset = edge.to.trie_offset orelse 0;
- node_size += edge.label.len + 1;
- try leb.writeULEB128(writer, next_node_offset);
- }
-
- const trie_offset = self.trie_offset orelse 0;
- const updated = offset_in_trie != trie_offset;
- self.trie_offset = offset_in_trie;
- self.node_dirty = false;
- node_size += stream.bytes_written;
-
- return FinalizeResult{ .node_size = node_size, .updated = updated };
- }
-};
-
/// The root node of the trie.
root: ?*Node = null,
@@ -611,3 +369,245 @@ test "ordering bug" {
_ = try trie.write(stream.writer());
try expectEqualHexStrings(&exp_buffer, buffer);
}
+
+pub const Node = struct {
+ base: *Trie,
+
+ /// Terminal info associated with this node.
+ /// If this node is not a terminal node, info is null.
+ terminal_info: ?struct {
+ /// Export flags associated with this exported symbol.
+ export_flags: u64,
+ /// VM address offset wrt to the section this symbol is defined against.
+ vmaddr_offset: u64,
+ } = null,
+
+ /// Offset of this node in the trie output byte stream.
+ trie_offset: ?u64 = null,
+
+ /// List of all edges originating from this node.
+ edges: std.ArrayListUnmanaged(Edge) = .{},
+
+ node_dirty: bool = true,
+
+ /// Edge connecting to nodes in the trie.
+ pub const Edge = struct {
+ from: *Node,
+ to: *Node,
+ label: []u8,
+
+ fn deinit(self: *Edge, allocator: Allocator) void {
+ self.to.deinit(allocator);
+ allocator.destroy(self.to);
+ allocator.free(self.label);
+ self.from = undefined;
+ self.to = undefined;
+ self.label = undefined;
+ }
+ };
+
+ fn deinit(self: *Node, allocator: Allocator) void {
+ for (self.edges.items) |*edge| {
+ edge.deinit(allocator);
+ }
+ self.edges.deinit(allocator);
+ }
+
+ /// Inserts a new node starting from `self`.
+ fn put(self: *Node, allocator: Allocator, label: []const u8) !*Node {
+ // Check for match with edges from this node.
+ for (self.edges.items) |*edge| {
+ const match = mem.indexOfDiff(u8, edge.label, label) orelse return edge.to;
+ if (match == 0) continue;
+ if (match == edge.label.len) return edge.to.put(allocator, label[match..]);
+
+ // Found a match, need to splice up nodes.
+ // From: A -> B
+ // To: A -> C -> B
+ const mid = try allocator.create(Node);
+ mid.* = .{ .base = self.base };
+ var to_label = try allocator.dupe(u8, edge.label[match..]);
+ allocator.free(edge.label);
+ const to_node = edge.to;
+ edge.to = mid;
+ edge.label = try allocator.dupe(u8, label[0..match]);
+ self.base.node_count += 1;
+
+ try mid.edges.append(allocator, .{
+ .from = mid,
+ .to = to_node,
+ .label = to_label,
+ });
+
+ return if (match == label.len) mid else mid.put(allocator, label[match..]);
+ }
+
+ // Add a new node.
+ const node = try allocator.create(Node);
+ node.* = .{ .base = self.base };
+ self.base.node_count += 1;
+
+ try self.edges.append(allocator, .{
+ .from = self,
+ .to = node,
+ .label = try allocator.dupe(u8, label),
+ });
+
+ return node;
+ }
+
+ /// Recursively parses the node from the input byte stream.
+ fn read(self: *Node, allocator: Allocator, reader: anytype) Trie.ReadError!usize {
+ self.node_dirty = true;
+ const trie_offset = try reader.context.getPos();
+ self.trie_offset = trie_offset;
+
+ var nread: usize = 0;
+
+ const node_size = try leb.readULEB128(u64, reader);
+ if (node_size > 0) {
+ const export_flags = try leb.readULEB128(u64, reader);
+ // TODO Parse special flags.
+ assert(export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
+ export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
+
+ const vmaddr_offset = try leb.readULEB128(u64, reader);
+
+ self.terminal_info = .{
+ .export_flags = export_flags,
+ .vmaddr_offset = vmaddr_offset,
+ };
+ }
+
+ const nedges = try reader.readByte();
+ self.base.node_count += nedges;
+
+ nread += (try reader.context.getPos()) - trie_offset;
+
+ var i: usize = 0;
+ while (i < nedges) : (i += 1) {
+ const edge_start_pos = try reader.context.getPos();
+
+ const label = blk: {
+ var label_buf = std.ArrayList(u8).init(allocator);
+ while (true) {
+ const next = try reader.readByte();
+ if (next == @as(u8, 0))
+ break;
+ try label_buf.append(next);
+ }
+ break :blk try label_buf.toOwnedSlice();
+ };
+
+ const seek_to = try leb.readULEB128(u64, reader);
+ const return_pos = try reader.context.getPos();
+
+ nread += return_pos - edge_start_pos;
+ try reader.context.seekTo(seek_to);
+
+ const node = try allocator.create(Node);
+ node.* = .{ .base = self.base };
+
+ nread += try node.read(allocator, reader);
+ try self.edges.append(allocator, .{
+ .from = self,
+ .to = node,
+ .label = label,
+ });
+ try reader.context.seekTo(return_pos);
+ }
+
+ return nread;
+ }
+
+ /// Writes this node to a byte stream.
+ /// The children of this node *are* not written to the byte stream
+ /// recursively. To write all nodes to a byte stream in sequence,
+ /// iterate over `Trie.ordered_nodes` and call this method on each node.
+ /// This is one of the requirements of the MachO.
+ /// Panics if `finalize` was not called before calling this method.
+ fn write(self: Node, writer: anytype) !void {
+ assert(!self.node_dirty);
+ if (self.terminal_info) |info| {
+ // Terminal node info: encode export flags and vmaddr offset of this symbol.
+ var info_buf: [@sizeOf(u64) * 2]u8 = undefined;
+ var info_stream = std.io.fixedBufferStream(&info_buf);
+ // TODO Implement for special flags.
+ assert(info.export_flags & macho.EXPORT_SYMBOL_FLAGS_REEXPORT == 0 and
+ info.export_flags & macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER == 0);
+ try leb.writeULEB128(info_stream.writer(), info.export_flags);
+ try leb.writeULEB128(info_stream.writer(), info.vmaddr_offset);
+
+ // Encode the size of the terminal node info.
+ var size_buf: [@sizeOf(u64)]u8 = undefined;
+ var size_stream = std.io.fixedBufferStream(&size_buf);
+ try leb.writeULEB128(size_stream.writer(), info_stream.pos);
+
+ // Now, write them to the output stream.
+ try writer.writeAll(size_buf[0..size_stream.pos]);
+ try writer.writeAll(info_buf[0..info_stream.pos]);
+ } else {
+ // Non-terminal node is delimited by 0 byte.
+ try writer.writeByte(0);
+ }
+ // Write number of edges (max legal number of edges is 256).
+ try writer.writeByte(@as(u8, @intCast(self.edges.items.len)));
+
+ for (self.edges.items) |edge| {
+ // Write edge label and offset to next node in trie.
+ try writer.writeAll(edge.label);
+ try writer.writeByte(0);
+ try leb.writeULEB128(writer, edge.to.trie_offset.?);
+ }
+ }
+
+ const FinalizeResult = struct {
+ /// Current size of this node in bytes.
+ node_size: u64,
+
+ /// True if the trie offset of this node in the output byte stream
+ /// would need updating; false otherwise.
+ updated: bool,
+ };
+
+ /// Updates offset of this node in the output byte stream.
+ fn finalize(self: *Node, offset_in_trie: u64) !FinalizeResult {
+ var stream = std.io.countingWriter(std.io.null_writer);
+ var writer = stream.writer();
+
+ var node_size: u64 = 0;
+ if (self.terminal_info) |info| {
+ try leb.writeULEB128(writer, info.export_flags);
+ try leb.writeULEB128(writer, info.vmaddr_offset);
+ try leb.writeULEB128(writer, stream.bytes_written);
+ } else {
+ node_size += 1; // 0x0 for non-terminal nodes
+ }
+ node_size += 1; // 1 byte for edge count
+
+ for (self.edges.items) |edge| {
+ const next_node_offset = edge.to.trie_offset orelse 0;
+ node_size += edge.label.len + 1;
+ try leb.writeULEB128(writer, next_node_offset);
+ }
+
+ const trie_offset = self.trie_offset orelse 0;
+ const updated = offset_in_trie != trie_offset;
+ self.trie_offset = offset_in_trie;
+ self.node_dirty = false;
+ node_size += stream.bytes_written;
+
+ return FinalizeResult{ .node_size = node_size, .updated = updated };
+ }
+};
+
+const Trie = @This();
+
+const std = @import("std");
+const mem = std.mem;
+const leb = std.leb;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const testing = std.testing;
+const assert = std.debug.assert;
+const Allocator = mem.Allocator;
diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig
index 3cd72fd64e..e3612c6948 100644
--- a/src/link/MachO/UnwindInfo.zig
+++ b/src/link/MachO/UnwindInfo.zig
@@ -1,25 +1,3 @@
-const UnwindInfo = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const eh_frame = @import("eh_frame.zig");
-const fs = std.fs;
-const leb = std.leb;
-const log = std.log.scoped(.unwind_info);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const AtomIndex = @import("zld.zig").AtomIndex;
-const EhFrameRecord = eh_frame.EhFrameRecord;
-const MachO = @import("../MachO.zig");
-const Object = @import("Object.zig");
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const Zld = @import("zld.zig").Zld;
-
gpa: Allocator,
/// List of all unwind records gathered from all objects and sorted
@@ -203,28 +181,28 @@ pub fn deinit(info: *UnwindInfo) void {
info.lsdas_lookup.deinit(info.gpa);
}
-pub fn scanRelocs(zld: *Zld) !void {
- if (zld.unwind_info_section_index == null) return;
+pub fn scanRelocs(macho_file: *MachO) !void {
+ if (macho_file.unwind_info_section_index == null) return;
- const cpu_arch = zld.options.target.cpu.arch;
- for (zld.objects.items, 0..) |*object, object_id| {
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ for (macho_file.objects.items, 0..) |*object, object_id| {
const unwind_records = object.getUnwindRecords();
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_syms_it.next()) |sym| {
const record_id = object.unwind_records_lookup.get(sym) orelse continue;
if (object.unwind_relocs_lookup[record_id].dead) continue;
const record = unwind_records[record_id];
if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
- if (getPersonalityFunctionReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
+ if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
// Personality function; add GOT pointer.
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = @as(u32, @intCast(object_id)),
.rel = rel,
.code = mem.asBytes(&record),
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
});
- try zld.addGotEntry(target);
+ try macho_file.addGotEntry(target);
}
}
}
@@ -232,10 +210,10 @@ pub fn scanRelocs(zld: *Zld) !void {
}
}
-pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
- if (zld.unwind_info_section_index == null) return;
+pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void {
+ if (macho_file.unwind_info_section_index == null) return;
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa);
defer records.deinit();
@@ -244,7 +222,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
defer sym_indexes.deinit();
// TODO handle dead stripping
- for (zld.objects.items, 0..) |*object, object_id| {
+ for (macho_file.objects.items, 0..) |*object, object_id| {
log.debug("collecting unwind records in {s} ({d})", .{ object.name, object_id });
const unwind_records = object.getUnwindRecords();
@@ -254,7 +232,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
try sym_indexes.ensureUnusedCapacity(object.exec_atoms.items.len);
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
var prev_symbol: ?SymbolWithLoc = null;
while (inner_syms_it.next()) |symbol| {
var record = if (object.unwind_records_lookup.get(symbol)) |record_id| blk: {
@@ -262,14 +240,14 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
var record = unwind_records[record_id];
if (UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
- try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
+ try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
} else {
if (getPersonalityFunctionReloc(
- zld,
+ macho_file,
@as(u32, @intCast(object_id)),
record_id,
)) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = @as(u32, @intCast(object_id)),
.rel = rel,
.code = mem.asBytes(&record),
@@ -286,8 +264,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
UnwindEncoding.setPersonalityIndex(&record.compactUnwindEncoding, personality_index + 1);
}
- if (getLsdaReloc(zld, @as(u32, @intCast(object_id)), record_id)) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| {
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = @as(u32, @intCast(object_id)),
.rel = rel,
.code = mem.asBytes(&record),
@@ -298,7 +276,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
}
break :blk record;
} else blk: {
- const sym = zld.getSymbol(symbol);
+ const sym = macho_file.getSymbol(symbol);
if (sym.n_desc == MachO.N_DEAD) continue;
if (prev_symbol) |prev_sym| {
const prev_addr = object.getSourceSymbol(prev_sym.sym_index).?.n_value;
@@ -310,7 +288,7 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
if (object.eh_frame_records_lookup.get(symbol)) |fde_offset| {
if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
var record = nullRecord();
- try info.collectPersonalityFromDwarf(zld, @as(u32, @intCast(object_id)), symbol, &record);
+ try info.collectPersonalityFromDwarf(macho_file, @as(u32, @intCast(object_id)), symbol, &record);
switch (cpu_arch) {
.aarch64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_ARM64_MODE.DWARF),
.x86_64 => UnwindEncoding.setMode(&record.compactUnwindEncoding, macho.UNWIND_X86_64_MODE.DWARF),
@@ -323,8 +301,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
break :blk nullRecord();
};
- const atom = zld.getAtom(atom_index);
- const sym = zld.getSymbol(symbol);
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbol(symbol);
assert(sym.n_desc != MachO.N_DEAD);
const size = if (inner_syms_it.next()) |next_sym| blk: {
// All this trouble to account for symbol aliases.
@@ -336,8 +314,8 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
const curr_addr = object.getSourceSymbol(symbol.sym_index).?.n_value;
const next_addr = object.getSourceSymbol(next_sym.sym_index).?.n_value;
if (next_addr > curr_addr) break :blk next_addr - curr_addr;
- break :blk zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
- } else zld.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
+ break :blk macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
+ } else macho_file.getSymbol(atom.getSymbolWithLoc()).n_value + atom.size - sym.n_value;
record.rangeStart = sym.n_value;
record.rangeLength = @as(u32, @intCast(size));
@@ -518,23 +496,23 @@ pub fn collect(info: *UnwindInfo, zld: *Zld) !void {
fn collectPersonalityFromDwarf(
info: *UnwindInfo,
- zld: *Zld,
+ macho_file: *MachO,
object_id: u32,
sym_loc: SymbolWithLoc,
record: *macho.compact_unwind_entry,
) !void {
- const object = &zld.objects.items[object_id];
+ const object = &macho_file.objects.items[object_id];
var it = object.getEhFrameRecordsIterator();
const fde_offset = object.eh_frame_records_lookup.get(sym_loc).?;
it.seekTo(fde_offset);
const fde = (try it.next()).?;
- const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
+ const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
const cie_offset = fde_offset + 4 - cie_ptr;
it.seekTo(cie_offset);
const cie = (try it.next()).?;
if (cie.getPersonalityPointerReloc(
- zld,
+ macho_file,
@as(u32, @intCast(object_id)),
cie_offset,
)) |target| {
@@ -550,9 +528,9 @@ fn collectPersonalityFromDwarf(
}
}
-pub fn calcSectionSize(info: UnwindInfo, zld: *Zld) !void {
- const sect_id = zld.unwind_info_section_index orelse return;
- const sect = &zld.sections.items(.header)[sect_id];
+pub fn calcSectionSize(info: UnwindInfo, macho_file: *MachO) !void {
+ const sect_id = macho_file.unwind_info_section_index orelse return;
+ const sect = &macho_file.sections.items(.header)[sect_id];
sect.@"align" = 2;
sect.size = info.calcRequiredSize();
}
@@ -569,23 +547,23 @@ fn calcRequiredSize(info: UnwindInfo) usize {
return total_size;
}
-pub fn write(info: *UnwindInfo, zld: *Zld) !void {
- const sect_id = zld.unwind_info_section_index orelse return;
- const sect = &zld.sections.items(.header)[sect_id];
- const seg_id = zld.sections.items(.segment_index)[sect_id];
- const seg = zld.segments.items[seg_id];
+pub fn write(info: *UnwindInfo, macho_file: *MachO) !void {
+ const sect_id = macho_file.unwind_info_section_index orelse return;
+ const sect = &macho_file.sections.items(.header)[sect_id];
+ const seg_id = macho_file.sections.items(.segment_index)[sect_id];
+ const seg = macho_file.segments.items[seg_id];
- const text_sect_id = zld.text_section_index.?;
- const text_sect = zld.sections.items(.header)[text_sect_id];
+ const text_sect_id = macho_file.text_section_index.?;
+ const text_sect = macho_file.sections.items(.header)[text_sect_id];
var personalities: [max_personalities]u32 = undefined;
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
log.debug("Personalities:", .{});
for (info.personalities[0..info.personalities_count], 0..) |target, i| {
- const addr = zld.getGotEntryAddress(target).?;
+ const addr = macho_file.getGotEntryAddress(target).?;
personalities[i] = @as(u32, @intCast(addr - seg.vmaddr));
- log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], zld.getSymbolName(target) });
+ log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) });
}
for (info.records.items) |*rec| {
@@ -599,7 +577,7 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
if (rec.compactUnwindEncoding > 0 and !UnwindEncoding.isDwarf(rec.compactUnwindEncoding, cpu_arch)) {
const lsda_target = @as(SymbolWithLoc, @bitCast(rec.lsda));
if (lsda_target.getFile()) |_| {
- const sym = zld.getSymbol(lsda_target);
+ const sym = macho_file.getSymbol(lsda_target);
rec.lsda = sym.n_value - seg.vmaddr;
}
}
@@ -689,11 +667,11 @@ pub fn write(info: *UnwindInfo, zld: *Zld) !void {
@memset(buffer.items[offset..], 0);
}
- try zld.file.pwriteAll(buffer.items, sect.offset);
+ try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
}
-fn getRelocs(zld: *Zld, object_id: u32, record_id: usize) []const macho.relocation_info {
- const object = &zld.objects.items[object_id];
+fn getRelocs(macho_file: *MachO, object_id: u32, record_id: usize) []const macho.relocation_info {
+ const object = &macho_file.objects.items[object_id];
assert(object.hasUnwindRecords());
const rel_pos = object.unwind_relocs_lookup[record_id].reloc;
const relocs = object.getRelocs(object.unwind_info_sect_id.?);
@@ -707,11 +685,11 @@ fn isPersonalityFunction(record_id: usize, rel: macho.relocation_info) bool {
}
pub fn getPersonalityFunctionReloc(
- zld: *Zld,
+ macho_file: *MachO,
object_id: u32,
record_id: usize,
) ?macho.relocation_info {
- const relocs = getRelocs(zld, object_id, record_id);
+ const relocs = getRelocs(macho_file, object_id, record_id);
for (relocs) |rel| {
if (isPersonalityFunction(record_id, rel)) return rel;
}
@@ -735,8 +713,8 @@ fn isLsda(record_id: usize, rel: macho.relocation_info) bool {
return rel_offset == 24;
}
-pub fn getLsdaReloc(zld: *Zld, object_id: u32, record_id: usize) ?macho.relocation_info {
- const relocs = getRelocs(zld, object_id, record_id);
+pub fn getLsdaReloc(macho_file: *MachO, object_id: u32, record_id: usize) ?macho.relocation_info {
+ const relocs = getRelocs(macho_file, object_id, record_id);
for (relocs) |rel| {
if (isLsda(record_id, rel)) return rel;
}
@@ -828,3 +806,23 @@ pub const UnwindEncoding = struct {
enc.* |= offset;
}
};
+
+const UnwindInfo = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const eh_frame = @import("eh_frame.zig");
+const fs = std.fs;
+const leb = std.leb;
+const log = std.log.scoped(.unwind_info);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const trace = @import("../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const EhFrameRecord = eh_frame.EhFrameRecord;
+const MachO = @import("../MachO.zig");
+const Object = @import("Object.zig");
+const SymbolWithLoc = MachO.SymbolWithLoc;
diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig
index 42a2e0cbd8..5e99ad2270 100644
--- a/src/link/MachO/dead_strip.zig
+++ b/src/link/MachO/dead_strip.zig
@@ -1,89 +1,72 @@
//! An algorithm for dead stripping of unreferenced Atoms.
-const std = @import("std");
-const assert = std.debug.assert;
-const eh_frame = @import("eh_frame.zig");
-const log = std.log.scoped(.dead_strip);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const MachO = @import("../MachO.zig");
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const UnwindInfo = @import("UnwindInfo.zig");
-const Zld = @import("zld.zig").Zld;
-
-const AtomTable = std.AutoHashMap(Atom.Index, void);
-
-pub fn gcAtoms(zld: *Zld) !void {
- const gpa = zld.gpa;
+pub fn gcAtoms(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
var roots = AtomTable.init(arena.allocator());
- try roots.ensureUnusedCapacity(@as(u32, @intCast(zld.globals.items.len)));
+ try roots.ensureUnusedCapacity(@as(u32, @intCast(macho_file.globals.items.len)));
var alive = AtomTable.init(arena.allocator());
- try alive.ensureTotalCapacity(@as(u32, @intCast(zld.atoms.items.len)));
+ try alive.ensureTotalCapacity(@as(u32, @intCast(macho_file.atoms.items.len)));
- try collectRoots(zld, &roots);
- try mark(zld, roots, &alive);
- prune(zld, alive);
+ try collectRoots(macho_file, &roots);
+ try mark(macho_file, roots, &alive);
+ prune(macho_file, alive);
}
-fn addRoot(zld: *Zld, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
- const sym = zld.getSymbol(sym_loc);
+fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWithLoc) !void {
+ const sym = macho_file.getSymbol(sym_loc);
assert(!sym.undf());
- const object = &zld.objects.items[file];
+ const object = &macho_file.objects.items[file];
const atom_index = object.getAtomIndexForSymbol(sym_loc.sym_index).?; // panic here means fatal error
log.debug("root(ATOM({d}, %{d}, {d}))", .{
atom_index,
- zld.getAtom(atom_index).sym_index,
+ macho_file.getAtom(atom_index).sym_index,
file,
});
_ = try roots.getOrPut(atom_index);
}
-fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
+fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void {
log.debug("collecting roots", .{});
- switch (zld.options.output_mode) {
+ switch (macho_file.base.options.output_mode) {
.Exe => {
// Add entrypoint as GC root
- const global: SymbolWithLoc = zld.getEntryPoint();
+ const global: SymbolWithLoc = macho_file.getEntryPoint();
if (global.getFile()) |file| {
- try addRoot(zld, roots, file, global);
+ try addRoot(macho_file, roots, file, global);
} else {
- assert(zld.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
+ assert(macho_file.getSymbol(global).undf()); // Stub as our entrypoint is in a dylib.
}
},
else => |other| {
assert(other == .Lib);
// Add exports as GC roots
- for (zld.globals.items) |global| {
- const sym = zld.getSymbol(global);
+ for (macho_file.globals.items) |global| {
+ const sym = macho_file.getSymbol(global);
if (sym.undf()) continue;
if (global.getFile()) |file| {
- try addRoot(zld, roots, file, global);
+ try addRoot(macho_file, roots, file, global);
}
}
},
}
// Add all symbols force-defined by the user.
- for (zld.options.force_undefined_symbols.keys()) |sym_name| {
- const global_index = zld.resolver.get(sym_name).?;
- const global = zld.globals.items[global_index];
- const sym = zld.getSymbol(global);
+ for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| {
+ const global_index = macho_file.resolver.get(sym_name).?;
+ const global = macho_file.globals.items[global_index];
+ const sym = macho_file.getSymbol(global);
assert(!sym.undf());
- try addRoot(zld, roots, global.getFile().?, global);
+ try addRoot(macho_file, roots, global.getFile().?, global);
}
- for (zld.objects.items) |object| {
+ for (macho_file.objects.items) |object| {
const has_subsections = object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
for (object.atoms.items) |atom_index| {
@@ -92,7 +75,7 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
// as a root.
if (!has_subsections) break :blk true;
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
source_sym.n_sect - 1
else sect_id: {
@@ -115,39 +98,39 @@ fn collectRoots(zld: *Zld, roots: *AtomTable) !void {
log.debug("root(ATOM({d}, %{d}, {?d}))", .{
atom_index,
- zld.getAtom(atom_index).sym_index,
- zld.getAtom(atom_index).getFile(),
+ macho_file.getAtom(atom_index).sym_index,
+ macho_file.getAtom(atom_index).getFile(),
});
}
}
}
}
-fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
+fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void {
if (alive.contains(atom_index)) return;
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
const sym_loc = atom.getSymbolWithLoc();
log.debug("mark(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
alive.putAssumeCapacityNoClobber(atom_index, {});
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.options.target.cpu.arch;
- const sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[sym.n_sect - 1];
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const header = macho_file.sections.items(.header)[sym.n_sect - 1];
if (header.isZerofill()) return;
- const code = Atom.getAtomCode(zld, atom_index);
- const relocs = Atom.getAtomRelocs(zld, atom_index);
- const ctx = Atom.getRelocContext(zld, atom_index);
+ const code = Atom.getAtomCode(macho_file, atom_index);
+ const relocs = Atom.getAtomRelocs(macho_file, atom_index);
+ const ctx = Atom.getRelocContext(macho_file, atom_index);
for (relocs) |rel| {
const target = switch (cpu_arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_ADDEND => continue,
- else => Atom.parseRelocTarget(zld, .{
+ else => Atom.parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = code,
@@ -155,7 +138,7 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
.base_addr = ctx.base_addr,
}),
},
- .x86_64 => Atom.parseRelocTarget(zld, .{
+ .x86_64 => Atom.parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = code,
@@ -164,50 +147,50 @@ fn markLive(zld: *Zld, atom_index: Atom.Index, alive: *AtomTable) void {
}),
else => unreachable,
};
- const target_sym = zld.getSymbol(target);
+ const target_sym = macho_file.getSymbol(target);
if (target_sym.undf()) continue;
if (target.getFile() == null) {
- const target_sym_name = zld.getSymbolName(target);
+ const target_sym_name = macho_file.getSymbolName(target);
if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue;
if (mem.eql(u8, "___dso_handle", target_sym_name)) continue;
unreachable; // referenced symbol not found
}
- const object = zld.objects.items[target.getFile().?];
+ const object = macho_file.objects.items[target.getFile().?];
const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?;
log.debug(" following ATOM({d}, %{d}, {?d})", .{
target_atom_index,
- zld.getAtom(target_atom_index).sym_index,
- zld.getAtom(target_atom_index).getFile(),
+ macho_file.getAtom(target_atom_index).sym_index,
+ macho_file.getAtom(target_atom_index).getFile(),
});
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
}
-fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
- const atom = zld.getAtom(atom_index);
+fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool {
+ const atom = macho_file.getAtom(atom_index);
const sym_loc = atom.getSymbolWithLoc();
log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() });
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
- const sym = zld.getSymbol(sym_loc);
- const header = zld.sections.items(.header)[sym.n_sect - 1];
+ const sym = macho_file.getSymbol(sym_loc);
+ const header = macho_file.sections.items(.header)[sym.n_sect - 1];
assert(!header.isZerofill());
- const code = Atom.getAtomCode(zld, atom_index);
- const relocs = Atom.getAtomRelocs(zld, atom_index);
- const ctx = Atom.getRelocContext(zld, atom_index);
+ const code = Atom.getAtomCode(macho_file, atom_index);
+ const relocs = Atom.getAtomRelocs(macho_file, atom_index);
+ const ctx = Atom.getRelocContext(macho_file, atom_index);
for (relocs) |rel| {
const target = switch (cpu_arch) {
.aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) {
.ARM64_RELOC_ADDEND => continue,
- else => Atom.parseRelocTarget(zld, .{
+ else => Atom.parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = code,
@@ -215,7 +198,7 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
.base_addr = ctx.base_addr,
}),
},
- .x86_64 => Atom.parseRelocTarget(zld, .{
+ .x86_64 => Atom.parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = code,
@@ -225,16 +208,16 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
else => unreachable,
};
- const object = zld.objects.items[target.getFile().?];
+ const object = macho_file.objects.items[target.getFile().?];
const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse {
- log.debug("atom for symbol '{s}' not found; skipping...", .{zld.getSymbolName(target)});
+ log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)});
continue;
};
if (alive.contains(target_atom_index)) {
log.debug(" refers live ATOM({d}, %{d}, {?d})", .{
target_atom_index,
- zld.getAtom(target_atom_index).sym_index,
- zld.getAtom(target_atom_index).getFile(),
+ macho_file.getAtom(target_atom_index).sym_index,
+ macho_file.getAtom(target_atom_index).getFile(),
});
return true;
}
@@ -243,21 +226,21 @@ fn refersLive(zld: *Zld, atom_index: Atom.Index, alive: AtomTable) bool {
return false;
}
-fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
+fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) !void {
var it = roots.keyIterator();
while (it.next()) |root| {
- markLive(zld, root.*, alive);
+ markLive(macho_file, root.*, alive);
}
var loop: bool = true;
while (loop) {
loop = false;
- for (zld.objects.items) |object| {
+ for (macho_file.objects.items) |object| {
for (object.atoms.items) |atom_index| {
if (alive.contains(atom_index)) continue;
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
const sect_id = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
source_sym.n_sect - 1
else blk: {
@@ -268,8 +251,8 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
const source_sect = object.getSourceSection(sect_id);
if (source_sect.isDontDeadStripIfReferencesLive()) {
- if (refersLive(zld, atom_index, alive.*)) {
- markLive(zld, atom_index, alive);
+ if (refersLive(macho_file, atom_index, alive.*)) {
+ markLive(macho_file, atom_index, alive);
loop = true;
}
}
@@ -277,26 +260,26 @@ fn mark(zld: *Zld, roots: AtomTable, alive: *AtomTable) !void {
}
}
- for (zld.objects.items, 0..) |_, object_id| {
+ for (macho_file.objects.items, 0..) |_, object_id| {
// Traverse unwind and eh_frame records noting if the source symbol has been marked, and if so,
// marking all references as live.
- try markUnwindRecords(zld, @as(u32, @intCast(object_id)), alive);
+ try markUnwindRecords(macho_file, @as(u32, @intCast(object_id)), alive);
}
}
-fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
- const object = &zld.objects.items[object_id];
- const cpu_arch = zld.options.target.cpu.arch;
+fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) !void {
+ const object = &macho_file.objects.items[object_id];
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
const unwind_records = object.getUnwindRecords();
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
if (!object.hasUnwindRecords()) {
if (alive.contains(atom_index)) {
// Mark references live and continue.
- try markEhFrameRecords(zld, object_id, atom_index, alive);
+ try markEhFrameRecords(macho_file, object_id, atom_index, alive);
} else {
while (inner_syms_it.next()) |sym| {
if (object.eh_frame_records_lookup.get(sym)) |fde_offset| {
@@ -322,51 +305,51 @@ fn markUnwindRecords(zld: *Zld, object_id: u32, alive: *AtomTable) !void {
const record = unwind_records[record_id];
if (UnwindInfo.UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) {
- try markEhFrameRecords(zld, object_id, atom_index, alive);
+ try markEhFrameRecords(macho_file, object_id, atom_index, alive);
} else {
- if (UnwindInfo.getPersonalityFunctionReloc(zld, object_id, record_id)) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| {
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = mem.asBytes(&record),
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
});
- const target_sym = zld.getSymbol(target);
+ const target_sym = macho_file.getSymbol(target);
if (!target_sym.undf()) {
- const target_object = zld.objects.items[target.getFile().?];
+ const target_object = macho_file.objects.items[target.getFile().?];
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
}
- if (UnwindInfo.getLsdaReloc(zld, object_id, record_id)) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| {
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = mem.asBytes(&record),
.base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))),
});
- const target_object = zld.objects.items[target.getFile().?];
+ const target_object = macho_file.objects.items[target.getFile().?];
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
}
}
}
}
-fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {
- const cpu_arch = zld.options.target.cpu.arch;
- const object = &zld.objects.items[object_id];
+fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) !void {
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const object = &macho_file.objects.items[object_id];
var it = object.getEhFrameRecordsIterator();
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_syms_it.next()) |sym| {
const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue; // Continue in case we hit a temp symbol alias
it.seekTo(fde_offset);
const fde = (try it.next()).?;
- const cie_ptr = fde.getCiePointerSource(object_id, zld, fde_offset);
+ const cie_ptr = fde.getCiePointerSource(object_id, macho_file, fde_offset);
const cie_offset = fde_offset + 4 - cie_ptr;
it.seekTo(cie_offset);
const cie = (try it.next()).?;
@@ -374,20 +357,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
switch (cpu_arch) {
.aarch64 => {
// Mark FDE references which should include any referenced LSDA record
- const relocs = eh_frame.getRelocs(zld, object_id, fde_offset);
+ const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset);
for (relocs) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = fde.data,
.base_offset = @as(i32, @intCast(fde_offset)) + 4,
});
- const target_sym = zld.getSymbol(target);
+ const target_sym = macho_file.getSymbol(target);
if (!target_sym.undf()) blk: {
- const target_object = zld.objects.items[target.getFile().?];
+ const target_object = macho_file.objects.items[target.getFile().?];
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse
break :blk;
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
}
},
@@ -401,7 +384,7 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
// Mark LSDA record as live
const sym_index = object.getSymbolByAddress(lsda_address, null);
const target_atom_index = object.getAtomIndexForSymbol(sym_index).?;
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
},
else => unreachable,
@@ -409,20 +392,20 @@ fn markEhFrameRecords(zld: *Zld, object_id: u32, atom_index: Atom.Index, alive:
// Mark CIE references which should include any referenced personalities
// that are defined locally.
- if (cie.getPersonalityPointerReloc(zld, object_id, cie_offset)) |target| {
- const target_sym = zld.getSymbol(target);
+ if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| {
+ const target_sym = macho_file.getSymbol(target);
if (!target_sym.undf()) {
- const target_object = zld.objects.items[target.getFile().?];
+ const target_object = macho_file.objects.items[target.getFile().?];
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
- markLive(zld, target_atom_index, alive);
+ markLive(macho_file, target_atom_index, alive);
}
}
}
}
-fn prune(zld: *Zld, alive: AtomTable) void {
+fn prune(macho_file: *MachO, alive: AtomTable) void {
log.debug("pruning dead atoms", .{});
- for (zld.objects.items) |*object| {
+ for (macho_file.objects.items) |*object| {
var i: usize = 0;
while (i < object.atoms.items.len) {
const atom_index = object.atoms.items[i];
@@ -431,7 +414,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
continue;
}
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
const sym_loc = atom.getSymbolWithLoc();
log.debug("prune(ATOM({d}, %{d}, {?d}))", .{
@@ -439,15 +422,15 @@ fn prune(zld: *Zld, alive: AtomTable) void {
sym_loc.sym_index,
sym_loc.getFile(),
});
- log.debug(" {s} in {s}", .{ zld.getSymbolName(sym_loc), object.name });
+ log.debug(" {s} in {s}", .{ macho_file.getSymbolName(sym_loc), object.name });
- const sym = zld.getSymbolPtr(sym_loc);
+ const sym = macho_file.getSymbolPtr(sym_loc);
const sect_id = sym.n_sect - 1;
- var section = zld.sections.get(sect_id);
+ var section = macho_file.sections.get(sect_id);
section.header.size -= atom.size;
if (atom.prev_index) |prev_index| {
- const prev = zld.getAtomPtr(prev_index);
+ const prev = macho_file.getAtomPtr(prev_index);
prev.next_index = atom.next_index;
} else {
if (atom.next_index) |next_index| {
@@ -455,7 +438,7 @@ fn prune(zld: *Zld, alive: AtomTable) void {
}
}
if (atom.next_index) |next_index| {
- const next = zld.getAtomPtr(next_index);
+ const next = macho_file.getAtomPtr(next_index);
next.prev_index = atom.prev_index;
} else {
if (atom.prev_index) |prev_index| {
@@ -467,21 +450,37 @@ fn prune(zld: *Zld, alive: AtomTable) void {
}
}
- zld.sections.set(sect_id, section);
+ macho_file.sections.set(sect_id, section);
_ = object.atoms.swapRemove(i);
sym.n_desc = MachO.N_DEAD;
- var inner_sym_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_sym_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_sym_it.next()) |inner| {
- const inner_sym = zld.getSymbolPtr(inner);
+ const inner_sym = macho_file.getSymbolPtr(inner);
inner_sym.n_desc = MachO.N_DEAD;
}
- if (Atom.getSectionAlias(zld, atom_index)) |alias| {
- const alias_sym = zld.getSymbolPtr(alias);
+ if (Atom.getSectionAlias(macho_file, atom_index)) |alias| {
+ const alias_sym = macho_file.getSymbolPtr(alias);
alias_sym.n_desc = MachO.N_DEAD;
}
}
}
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const eh_frame = @import("eh_frame.zig");
+const log = std.log.scoped(.dead_strip);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const MachO = @import("../MachO.zig");
+const SymbolWithLoc = MachO.SymbolWithLoc;
+const UnwindInfo = @import("UnwindInfo.zig");
+
+const AtomTable = std.AutoHashMap(Atom.Index, void);
diff --git a/src/link/MachO/dyld_info/Rebase.zig b/src/link/MachO/dyld_info/Rebase.zig
index 0f3e96b02f..512e23eddb 100644
--- a/src/link/MachO/dyld_info/Rebase.zig
+++ b/src/link/MachO/dyld_info/Rebase.zig
@@ -1,14 +1,3 @@
-const Rebase = @This();
-
-const std = @import("std");
-const assert = std.debug.assert;
-const leb = std.leb;
-const log = std.log.scoped(.dyld_info);
-const macho = std.macho;
-const testing = std.testing;
-
-const Allocator = std.mem.Allocator;
-
entries: std.ArrayListUnmanaged(Entry) = .{},
buffer: std.ArrayListUnmanaged(u8) = .{},
@@ -572,3 +561,14 @@ test "rebase - composite" {
macho.REBASE_OPCODE_DONE,
}, rebase.buffer.items);
}
+
+const Rebase = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const leb = std.leb;
+const log = std.log.scoped(.dyld_info);
+const macho = std.macho;
+const testing = std.testing;
+
+const Allocator = std.mem.Allocator;
diff --git a/src/link/MachO/dyld_info/bind.zig b/src/link/MachO/dyld_info/bind.zig
index f804c6466d..ca4e73a283 100644
--- a/src/link/MachO/dyld_info/bind.zig
+++ b/src/link/MachO/dyld_info/bind.zig
@@ -1,12 +1,3 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const leb = std.leb;
-const log = std.log.scoped(.dyld_info);
-const macho = std.macho;
-const testing = std.testing;
-
-const Allocator = std.mem.Allocator;
-
pub fn Bind(comptime Ctx: type, comptime Target: type) type {
return struct {
entries: std.ArrayListUnmanaged(Entry) = .{},
@@ -738,3 +729,12 @@ test "lazy bind" {
macho.BIND_OPCODE_DONE,
}, bind.buffer.items);
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const leb = std.leb;
+const log = std.log.scoped(.dyld_info);
+const macho = std.macho;
+const testing = std.testing;
+
+const Allocator = std.mem.Allocator;
diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig
index 2bcf23bff5..332aea08e5 100644
--- a/src/link/MachO/eh_frame.zig
+++ b/src/link/MachO/eh_frame.zig
@@ -1,68 +1,52 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-const leb = std.leb;
-const log = std.log.scoped(.eh_frame);
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const MachO = @import("../MachO.zig");
-const Relocation = @import("Relocation.zig");
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const UnwindInfo = @import("UnwindInfo.zig");
-const Zld = @import("zld.zig").Zld;
-
-pub fn scanRelocs(zld: *Zld) !void {
- const gpa = zld.gpa;
+pub fn scanRelocs(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
- for (zld.objects.items, 0..) |*object, object_id| {
+ for (macho_file.objects.items, 0..) |*object, object_id| {
var cies = std.AutoHashMap(u32, void).init(gpa);
defer cies.deinit();
var it = object.getEhFrameRecordsIterator();
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_syms_it.next()) |sym| {
const fde_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
if (object.eh_frame_relocs_lookup.get(fde_offset).?.dead) continue;
it.seekTo(fde_offset);
const fde = (try it.next()).?;
- const cie_ptr = fde.getCiePointerSource(@intCast(object_id), zld, fde_offset);
+ const cie_ptr = fde.getCiePointerSource(@intCast(object_id), macho_file, fde_offset);
const cie_offset = fde_offset + 4 - cie_ptr;
if (!cies.contains(cie_offset)) {
try cies.putNoClobber(cie_offset, {});
it.seekTo(cie_offset);
const cie = (try it.next()).?;
- try cie.scanRelocs(zld, @as(u32, @intCast(object_id)), cie_offset);
+ try cie.scanRelocs(macho_file, @as(u32, @intCast(object_id)), cie_offset);
}
}
}
}
}
-pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
- const sect_id = zld.eh_frame_section_index orelse return;
- const sect = &zld.sections.items(.header)[sect_id];
+pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) !void {
+ const sect_id = macho_file.eh_frame_section_index orelse return;
+ const sect = &macho_file.sections.items(.header)[sect_id];
sect.@"align" = 3;
sect.size = 0;
- const cpu_arch = zld.options.target.cpu.arch;
- const gpa = zld.gpa;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const gpa = macho_file.base.allocator;
var size: u32 = 0;
- for (zld.objects.items, 0..) |*object, object_id| {
+ for (macho_file.objects.items, 0..) |*object, object_id| {
var cies = std.AutoHashMap(u32, u32).init(gpa);
defer cies.deinit();
var eh_it = object.getEhFrameRecordsIterator();
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_syms_it.next()) |sym| {
const fde_record_offset = object.eh_frame_records_lookup.get(sym) orelse continue;
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
@@ -77,7 +61,7 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
eh_it.seekTo(fde_record_offset);
const source_fde_record = (try eh_it.next()).?;
- const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
+ const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
const cie_offset = fde_record_offset + 4 - cie_ptr;
const gop = try cies.getOrPut(cie_offset);
@@ -96,14 +80,14 @@ pub fn calcSectionSize(zld: *Zld, unwind_info: *const UnwindInfo) !void {
}
}
-pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
- const sect_id = zld.eh_frame_section_index orelse return;
- const sect = zld.sections.items(.header)[sect_id];
- const seg_id = zld.sections.items(.segment_index)[sect_id];
- const seg = zld.segments.items[seg_id];
+pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void {
+ const sect_id = macho_file.eh_frame_section_index orelse return;
+ const sect = macho_file.sections.items(.header)[sect_id];
+ const seg_id = macho_file.sections.items(.segment_index)[sect_id];
+ const seg = macho_file.segments.items[seg_id];
- const cpu_arch = zld.options.target.cpu.arch;
- const gpa = zld.gpa;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const gpa = macho_file.base.allocator;
var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa);
defer {
@@ -115,7 +99,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
var eh_frame_offset: u32 = 0;
- for (zld.objects.items, 0..) |*object, object_id| {
+ for (macho_file.objects.items, 0..) |*object, object_id| {
try eh_records.ensureUnusedCapacity(2 * @as(u32, @intCast(object.exec_atoms.items.len)));
var cies = std.AutoHashMap(u32, u32).init(gpa);
@@ -124,7 +108,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
var eh_it = object.getEhFrameRecordsIterator();
for (object.exec_atoms.items) |atom_index| {
- var inner_syms_it = Atom.getInnerSymbolsIterator(zld, atom_index);
+ var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
while (inner_syms_it.next()) |target| {
const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue;
if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue;
@@ -139,7 +123,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
eh_it.seekTo(fde_record_offset);
const source_fde_record = (try eh_it.next()).?;
- const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), zld, fde_record_offset);
+ const cie_ptr = source_fde_record.getCiePointerSource(@intCast(object_id), macho_file, fde_record_offset);
const cie_offset = fde_record_offset + 4 - cie_ptr;
const gop = try cies.getOrPut(cie_offset);
@@ -147,7 +131,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
eh_it.seekTo(cie_offset);
const source_cie_record = (try eh_it.next()).?;
var cie_record = try source_cie_record.toOwned(gpa);
- try cie_record.relocate(zld, @as(u32, @intCast(object_id)), .{
+ try cie_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
.source_offset = cie_offset,
.out_offset = eh_frame_offset,
.sect_addr = sect.addr,
@@ -158,7 +142,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
}
var fde_record = try source_fde_record.toOwned(gpa);
- try fde_record.relocate(zld, @as(u32, @intCast(object_id)), .{
+ try fde_record.relocate(macho_file, @as(u32, @intCast(object_id)), .{
.source_offset = fde_record_offset,
.out_offset = eh_frame_offset,
.sect_addr = sect.addr,
@@ -169,7 +153,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
.aarch64 => {}, // relocs take care of LSDA pointers
.x86_64 => {
// We need to relocate target symbol address ourselves.
- const atom_sym = zld.getSymbol(target);
+ const atom_sym = macho_file.getSymbol(target);
try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{
.base_addr = sect.addr,
.base_offset = eh_frame_offset,
@@ -229,7 +213,7 @@ pub fn write(zld: *Zld, unwind_info: *UnwindInfo) !void {
try buffer.appendSlice(record.data);
}
- try zld.file.pwriteAll(buffer.items, sect.offset);
+ try macho_file.base.file.?.pwriteAll(buffer.items, sect.offset);
}
const EhFrameRecordTag = enum { cie, fde };
@@ -261,12 +245,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
pub fn scanRelocs(
rec: Record,
- zld: *Zld,
+ macho_file: *MachO,
object_id: u32,
source_offset: u32,
) !void {
- if (rec.getPersonalityPointerReloc(zld, object_id, source_offset)) |target| {
- try zld.addGotEntry(target);
+ if (rec.getPersonalityPointerReloc(macho_file, object_id, source_offset)) |target| {
+ try macho_file.addGotEntry(target);
}
}
@@ -290,12 +274,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
pub fn getPersonalityPointerReloc(
rec: Record,
- zld: *Zld,
+ macho_file: *MachO,
object_id: u32,
source_offset: u32,
) ?SymbolWithLoc {
- const cpu_arch = zld.options.target.cpu.arch;
- const relocs = getRelocs(zld, object_id, source_offset);
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const relocs = getRelocs(macho_file, object_id, source_offset);
for (relocs) |rel| {
switch (cpu_arch) {
.aarch64 => {
@@ -317,7 +301,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
},
else => unreachable,
}
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = rec.data,
@@ -328,18 +312,18 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
return null;
}
- pub fn relocate(rec: *Record, zld: *Zld, object_id: u32, ctx: struct {
+ pub fn relocate(rec: *Record, macho_file: *MachO, object_id: u32, ctx: struct {
source_offset: u32,
out_offset: u32,
sect_addr: u64,
}) !void {
comptime assert(is_mutable);
- const cpu_arch = zld.options.target.cpu.arch;
- const relocs = getRelocs(zld, object_id, ctx.source_offset);
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const relocs = getRelocs(macho_file, object_id, ctx.source_offset);
for (relocs) |rel| {
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = object_id,
.rel = rel,
.code = rec.data,
@@ -356,14 +340,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
// Address of the __eh_frame in the source object file
},
.ARM64_RELOC_POINTER_TO_GOT => {
- const target_addr = zld.getGotEntryAddress(target).?;
+ const target_addr = macho_file.getGotEntryAddress(target).?;
const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse
return error.Overflow;
mem.writeIntLittle(i32, rec.data[rel_offset..][0..4], result);
},
.ARM64_RELOC_UNSIGNED => {
assert(rel.r_extern == 1);
- const target_addr = try Atom.getRelocTargetAddress(zld, target, false);
+ const target_addr = try Atom.getRelocTargetAddress(macho_file, target, false);
const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr));
mem.writeIntLittle(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)));
},
@@ -374,7 +358,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
switch (rel_type) {
.X86_64_RELOC_GOT => {
- const target_addr = zld.getGotEntryAddress(target).?;
+ const target_addr = macho_file.getGotEntryAddress(target).?;
const addend = mem.readIntLittle(i32, rec.data[rel_offset..][0..4]);
const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend));
const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0);
@@ -388,20 +372,20 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
}
}
- pub fn getCiePointerSource(rec: Record, object_id: u32, zld: *Zld, offset: u32) u32 {
+ pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 {
assert(rec.tag == .fde);
- const cpu_arch = zld.options.target.cpu.arch;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
const addend = mem.readIntLittle(u32, rec.data[0..4]);
switch (cpu_arch) {
.aarch64 => {
- const relocs = getRelocs(zld, object_id, offset);
+ const relocs = getRelocs(macho_file, object_id, offset);
const maybe_rel = for (relocs) |rel| {
if (rel.r_address - @as(i32, @intCast(offset)) == 4 and
@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_SUBTRACTOR)
break rel;
} else null;
const rel = maybe_rel orelse return addend;
- const object = &zld.objects.items[object_id];
+ const object = &macho_file.objects.items[object_id];
const target_addr = object.in_symtab.?[rel.r_symbolnum].n_value;
const sect = object.getSourceSection(object.eh_frame_sect_id.?);
return @intCast(sect.addr + offset - target_addr + addend);
@@ -583,8 +567,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type {
};
}
-pub fn getRelocs(zld: *Zld, object_id: u32, source_offset: u32) []const macho.relocation_info {
- const object = &zld.objects.items[object_id];
+pub fn getRelocs(macho_file: *MachO, object_id: u32, source_offset: u32) []const macho.relocation_info {
+ const object = &macho_file.objects.items[object_id];
assert(object.hasEhFrameRecords());
const urel = object.eh_frame_relocs_lookup.get(source_offset) orelse
return &[0]macho.relocation_info{};
@@ -650,3 +634,18 @@ pub const EH_PE = struct {
pub const indirect = 0x80;
pub const omit = 0xFF;
};
+
+const std = @import("std");
+const assert = std.debug.assert;
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+const leb = std.leb;
+const log = std.log.scoped(.eh_frame);
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const MachO = @import("../MachO.zig");
+const Relocation = @import("Relocation.zig");
+const SymbolWithLoc = MachO.SymbolWithLoc;
+const UnwindInfo = @import("UnwindInfo.zig");
diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig
index 751e49f651..6dd32e2251 100644
--- a/src/link/MachO/fat.zig
+++ b/src/link/MachO/fat.zig
@@ -1,9 +1,3 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const log = std.log.scoped(.archive);
-const macho = std.macho;
-const mem = std.mem;
-
pub fn isFatLibrary(file: std.fs.File) bool {
const reader = file.reader();
const hdr = reader.readStructBig(macho.fat_header) catch return false;
@@ -38,3 +32,9 @@ pub fn parseArchs(file: std.fs.File, buffer: *[2]Arch) ![]const Arch {
return buffer[0..count];
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const log = std.log.scoped(.archive);
+const macho = std.macho;
+const mem = std.mem;
diff --git a/src/link/MachO/hasher.zig b/src/link/MachO/hasher.zig
index 40c034c90c..45847689f3 100644
--- a/src/link/MachO/hasher.zig
+++ b/src/link/MachO/hasher.zig
@@ -1,12 +1,3 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const fs = std.fs;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const ThreadPool = std.Thread.Pool;
-const WaitGroup = std.Thread.WaitGroup;
-
pub fn ParallelHasher(comptime Hasher: type) type {
const hash_size = Hasher.digest_length;
@@ -69,3 +60,12 @@ pub fn ParallelHasher(comptime Hasher: type) type {
const Self = @This();
};
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const fs = std.fs;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const ThreadPool = std.Thread.Pool;
+const WaitGroup = std.Thread.WaitGroup;
diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig
index c980a764a2..b548bee2fc 100644
--- a/src/link/MachO/load_commands.zig
+++ b/src/link/MachO/load_commands.zig
@@ -1,13 +1,3 @@
-const std = @import("std");
-const assert = std.debug.assert;
-const link = @import("../../link.zig");
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Dylib = @import("Dylib.zig");
-
/// Default implicit entrypoint symbol name.
pub const default_entry_point: []const u8 = "_main";
@@ -374,3 +364,13 @@ test "parseSdkVersion" {
try expect(parseSdkVersion("11") == null);
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const link = @import("../../link.zig");
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Dylib = @import("Dylib.zig");
diff --git a/src/link/MachO/stubs.zig b/src/link/MachO/stubs.zig
index 077d6c9303..5478dd15f8 100644
--- a/src/link/MachO/stubs.zig
+++ b/src/link/MachO/stubs.zig
@@ -1,8 +1,3 @@
-const std = @import("std");
-const aarch64 = @import("../../arch/aarch64/bits.zig");
-
-const Relocation = @import("Relocation.zig");
-
pub inline fn stubHelperPreambleSize(cpu_arch: std.Target.Cpu.Arch) u8 {
return switch (cpu_arch) {
.x86_64 => 15,
@@ -167,3 +162,8 @@ pub fn writeStubCode(args: struct {
else => unreachable,
}
}
+
+const std = @import("std");
+const aarch64 = @import("../../arch/aarch64/bits.zig");
+
+const Relocation = @import("Relocation.zig");
diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig
index c5debcc1fa..726fbdf2a6 100644
--- a/src/link/MachO/thunks.zig
+++ b/src/link/MachO/thunks.zig
@@ -5,22 +5,6 @@
//! The algorithm works pessimistically and assumes that any reference to an Atom in
//! another output section is out of range.
-const std = @import("std");
-const assert = std.debug.assert;
-const log = std.log.scoped(.thunks);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-
-const aarch64 = @import("../../arch/aarch64/bits.zig");
-
-const Allocator = mem.Allocator;
-const Atom = @import("Atom.zig");
-const MachO = @import("../MachO.zig");
-const Relocation = @import("Relocation.zig");
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const Zld = @import("zld.zig").Zld;
-
/// Branch instruction has 26 bits immediate but 4 byte aligned.
const jump_bits = @bitSizeOf(i28);
@@ -74,18 +58,18 @@ pub const Thunk = struct {
return @alignOf(u32);
}
- pub fn getTrampoline(self: Thunk, zld: *Zld, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
+ pub fn getTrampoline(self: Thunk, macho_file: *MachO, tag: Tag, target: SymbolWithLoc) ?SymbolWithLoc {
const atom_index = self.lookup.get(.{ .tag = tag, .target = target }) orelse return null;
- return zld.getAtom(atom_index).getSymbolWithLoc();
+ return macho_file.getAtom(atom_index).getSymbolWithLoc();
}
};
-pub fn createThunks(zld: *Zld, sect_id: u8) !void {
- const header = &zld.sections.items(.header)[sect_id];
+pub fn createThunks(macho_file: *MachO, sect_id: u8) !void {
+ const header = &macho_file.sections.items(.header)[sect_id];
if (header.size == 0) return;
- const gpa = zld.gpa;
- const first_atom_index = zld.sections.items(.first_atom_index)[sect_id].?;
+ const gpa = macho_file.base.allocator;
+ const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?;
header.size = 0;
header.@"align" = 0;
@@ -95,8 +79,8 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
{
var atom_index = first_atom_index;
while (true) {
- const atom = zld.getAtom(atom_index);
- const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
sym.n_value = 0;
atom_count += 1;
@@ -115,24 +99,24 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
var offset: u64 = 0;
while (true) {
- const group_start_atom = zld.getAtom(group_start);
+ const group_start_atom = macho_file.getAtom(group_start);
log.debug("GROUP START at {d}", .{group_start});
while (true) {
- const atom = zld.getAtom(group_end);
+ const atom = macho_file.getAtom(group_end);
offset = mem.alignForward(u64, offset, try math.powi(u32, 2, atom.alignment));
- const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
+ const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
sym.n_value = offset;
offset += atom.size;
- zld.logAtom(group_end, log);
+ macho_file.logAtom(group_end, log);
header.@"align" = @max(header.@"align", atom.alignment);
allocated.putAssumeCapacityNoClobber(group_end, {});
- const group_start_sym = zld.getSymbol(group_start_atom.getSymbolWithLoc());
+ const group_start_sym = macho_file.getSymbol(group_start_atom.getSymbolWithLoc());
if (offset - group_start_sym.n_value >= max_allowed_distance) break;
if (atom.next_index) |next_index| {
@@ -142,15 +126,15 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
log.debug("GROUP END at {d}", .{group_end});
// Insert thunk at group_end
- const thunk_index = @as(u32, @intCast(zld.thunks.items.len));
- try zld.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
+ const thunk_index = @as(u32, @intCast(macho_file.thunks.items.len));
+ try macho_file.thunks.append(gpa, .{ .start_index = undefined, .len = 0 });
// Scan relocs in the group and create trampolines for any unreachable callsite.
var atom_index = group_start;
while (true) {
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
try scanRelocs(
- zld,
+ macho_file,
atom_index,
allocated,
thunk_index,
@@ -165,19 +149,19 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
}
offset = mem.alignForward(u64, offset, Thunk.getAlignment());
- allocateThunk(zld, thunk_index, offset, header);
- offset += zld.thunks.items[thunk_index].getSize();
+ allocateThunk(macho_file, thunk_index, offset, header);
+ offset += macho_file.thunks.items[thunk_index].getSize();
- const thunk = zld.thunks.items[thunk_index];
+ const thunk = macho_file.thunks.items[thunk_index];
if (thunk.len == 0) {
- const group_end_atom = zld.getAtom(group_end);
+ const group_end_atom = macho_file.getAtom(group_end);
if (group_end_atom.next_index) |next_index| {
group_start = next_index;
group_end = next_index;
} else break;
} else {
const thunk_end_atom_index = thunk.getEndAtomIndex();
- const thunk_end_atom = zld.getAtom(thunk_end_atom_index);
+ const thunk_end_atom = macho_file.getAtom(thunk_end_atom_index);
if (thunk_end_atom.next_index) |next_index| {
group_start = next_index;
group_end = next_index;
@@ -189,12 +173,12 @@ pub fn createThunks(zld: *Zld, sect_id: u8) !void {
}
fn allocateThunk(
- zld: *Zld,
+ macho_file: *MachO,
thunk_index: Thunk.Index,
base_offset: u64,
header: *macho.section_64,
) void {
- const thunk = zld.thunks.items[thunk_index];
+ const thunk = macho_file.thunks.items[thunk_index];
if (thunk.len == 0) return;
const first_atom_index = thunk.getStartAtomIndex();
@@ -203,14 +187,14 @@ fn allocateThunk(
var atom_index = first_atom_index;
var offset = base_offset;
while (true) {
- const atom = zld.getAtom(atom_index);
+ const atom = macho_file.getAtom(atom_index);
offset = mem.alignForward(u64, offset, Thunk.getAlignment());
- const sym = zld.getSymbolPtr(atom.getSymbolWithLoc());
+ const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
sym.n_value = offset;
offset += atom.size;
- zld.logAtom(atom_index, log);
+ macho_file.logAtom(atom_index, log);
header.@"align" = @max(header.@"align", atom.alignment);
@@ -223,69 +207,69 @@ fn allocateThunk(
}
fn scanRelocs(
- zld: *Zld,
+ macho_file: *MachO,
atom_index: Atom.Index,
allocated: std.AutoHashMap(Atom.Index, void),
thunk_index: Thunk.Index,
group_end: Atom.Index,
) !void {
- const atom = zld.getAtom(atom_index);
- const object = zld.objects.items[atom.getFile().?];
+ const atom = macho_file.getAtom(atom_index);
+ const object = macho_file.objects.items[atom.getFile().?];
const base_offset = if (object.getSourceSymbol(atom.sym_index)) |source_sym| blk: {
const source_sect = object.getSourceSection(source_sym.n_sect - 1);
break :blk @as(i32, @intCast(source_sym.n_value - source_sect.addr));
} else 0;
- const code = Atom.getAtomCode(zld, atom_index);
- const relocs = Atom.getAtomRelocs(zld, atom_index);
- const ctx = Atom.getRelocContext(zld, atom_index);
+ const code = Atom.getAtomCode(macho_file, atom_index);
+ const relocs = Atom.getAtomRelocs(macho_file, atom_index);
+ const ctx = Atom.getRelocContext(macho_file, atom_index);
for (relocs) |rel| {
if (!relocNeedsThunk(rel)) continue;
- const target = Atom.parseRelocTarget(zld, .{
+ const target = Atom.parseRelocTarget(macho_file, .{
.object_id = atom.getFile().?,
.rel = rel,
.code = code,
.base_offset = ctx.base_offset,
.base_addr = ctx.base_addr,
});
- if (isReachable(zld, atom_index, rel, base_offset, target, allocated)) continue;
+ if (isReachable(macho_file, atom_index, rel, base_offset, target, allocated)) continue;
log.debug("{x}: source = {s}@{x}, target = {s}@{x} unreachable", .{
rel.r_address - base_offset,
- zld.getSymbolName(atom.getSymbolWithLoc()),
- zld.getSymbol(atom.getSymbolWithLoc()).n_value,
- zld.getSymbolName(target),
- zld.getSymbol(target).n_value,
+ macho_file.getSymbolName(atom.getSymbolWithLoc()),
+ macho_file.getSymbol(atom.getSymbolWithLoc()).n_value,
+ macho_file.getSymbolName(target),
+ macho_file.getSymbol(target).n_value,
});
- const gpa = zld.gpa;
- const target_sym = zld.getSymbol(target);
- const thunk = &zld.thunks.items[thunk_index];
+ const gpa = macho_file.base.allocator;
+ const target_sym = macho_file.getSymbol(target);
+ const thunk = &macho_file.thunks.items[thunk_index];
const tag: Thunk.Tag = if (target_sym.undf()) .stub else .atom;
const thunk_target: Thunk.Target = .{ .tag = tag, .target = target };
const gop = try thunk.lookup.getOrPut(gpa, thunk_target);
if (!gop.found_existing) {
- gop.value_ptr.* = try pushThunkAtom(zld, thunk, group_end);
+ gop.value_ptr.* = try pushThunkAtom(macho_file, thunk, group_end);
try thunk.targets.append(gpa, thunk_target);
}
- try zld.thunk_table.put(gpa, atom_index, thunk_index);
+ try macho_file.thunk_table.put(gpa, atom_index, thunk_index);
}
}
-fn pushThunkAtom(zld: *Zld, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
- const thunk_atom_index = try createThunkAtom(zld);
+fn pushThunkAtom(macho_file: *MachO, thunk: *Thunk, group_end: Atom.Index) !Atom.Index {
+ const thunk_atom_index = try createThunkAtom(macho_file);
- const thunk_atom = zld.getAtomPtr(thunk_atom_index);
+ const thunk_atom = macho_file.getAtomPtr(thunk_atom_index);
const end_atom_index = if (thunk.len == 0) group_end else thunk.getEndAtomIndex();
- const end_atom = zld.getAtomPtr(end_atom_index);
+ const end_atom = macho_file.getAtomPtr(end_atom_index);
if (end_atom.next_index) |first_after_index| {
- const first_after_atom = zld.getAtomPtr(first_after_index);
+ const first_after_atom = macho_file.getAtomPtr(first_after_index);
first_after_atom.prev_index = thunk_atom_index;
thunk_atom.next_index = first_after_index;
}
@@ -308,58 +292,58 @@ inline fn relocNeedsThunk(rel: macho.relocation_info) bool {
}
fn isReachable(
- zld: *Zld,
+ macho_file: *MachO,
atom_index: Atom.Index,
rel: macho.relocation_info,
base_offset: i32,
target: SymbolWithLoc,
allocated: std.AutoHashMap(Atom.Index, void),
) bool {
- if (zld.stubs_table.lookup.contains(target)) return false;
+ if (macho_file.stub_table.lookup.contains(target)) return false;
- const source_atom = zld.getAtom(atom_index);
- const source_sym = zld.getSymbol(source_atom.getSymbolWithLoc());
+ const source_atom = macho_file.getAtom(atom_index);
+ const source_sym = macho_file.getSymbol(source_atom.getSymbolWithLoc());
- const target_object = zld.objects.items[target.getFile().?];
+ const target_object = macho_file.objects.items[target.getFile().?];
const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?;
- const target_atom = zld.getAtom(target_atom_index);
- const target_sym = zld.getSymbol(target_atom.getSymbolWithLoc());
+ const target_atom = macho_file.getAtom(target_atom_index);
+ const target_sym = macho_file.getSymbol(target_atom.getSymbolWithLoc());
if (source_sym.n_sect != target_sym.n_sect) return false;
if (!allocated.contains(target_atom_index)) return false;
const source_addr = source_sym.n_value + @as(u32, @intCast(rel.r_address - base_offset));
- const target_addr = if (Atom.relocRequiresGot(zld, rel))
- zld.getGotEntryAddress(target).?
+ const target_addr = if (Atom.relocRequiresGot(macho_file, rel))
+ macho_file.getGotEntryAddress(target).?
else
- Atom.getRelocTargetAddress(zld, target, false) catch unreachable;
+ Atom.getRelocTargetAddress(macho_file, target, false) catch unreachable;
_ = Relocation.calcPcRelativeDisplacementArm64(source_addr, target_addr) catch
return false;
return true;
}
-fn createThunkAtom(zld: *Zld) !Atom.Index {
- const sym_index = try zld.allocateSymbol();
- const atom_index = try zld.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
- const sym = zld.getSymbolPtr(.{ .sym_index = sym_index });
+fn createThunkAtom(macho_file: *MachO) !Atom.Index {
+ const sym_index = try macho_file.allocateSymbol();
+ const atom_index = try macho_file.createAtom(sym_index, .{ .size = @sizeOf(u32) * 3, .alignment = 2 });
+ const sym = macho_file.getSymbolPtr(.{ .sym_index = sym_index });
sym.n_type = macho.N_SECT;
- sym.n_sect = zld.text_section_index.? + 1;
+ sym.n_sect = macho_file.text_section_index.? + 1;
return atom_index;
}
-pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {
+pub fn writeThunkCode(macho_file: *MachO, thunk: *const Thunk, writer: anytype) !void {
const slice = thunk.targets.slice();
for (thunk.getStartAtomIndex()..thunk.getEndAtomIndex(), 0..) |atom_index, target_index| {
- const atom = zld.getAtom(@intCast(atom_index));
- const sym = zld.getSymbol(atom.getSymbolWithLoc());
+ const atom = macho_file.getAtom(@intCast(atom_index));
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
const source_addr = sym.n_value;
const tag = slice.items(.tag)[target_index];
const target = slice.items(.target)[target_index];
const target_addr = switch (tag) {
- .stub => zld.getStubsEntryAddress(target).?,
- .atom => zld.getSymbol(target).n_value,
+ .stub => macho_file.getStubsEntryAddress(target).?,
+ .atom => macho_file.getSymbol(target).n_value,
};
const pages = Relocation.calcNumberOfPages(source_addr, target_addr);
try writer.writeIntLittle(u32, aarch64.Instruction.adrp(.x16, pages).toU32());
@@ -368,3 +352,18 @@ pub fn writeThunkCode(zld: *Zld, thunk: *const Thunk, writer: anytype) !void {
try writer.writeIntLittle(u32, aarch64.Instruction.br(.x16).toU32());
}
}
+
+const std = @import("std");
+const assert = std.debug.assert;
+const log = std.log.scoped(.thunks);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+
+const aarch64 = @import("../../arch/aarch64/bits.zig");
+
+const Allocator = mem.Allocator;
+const Atom = @import("Atom.zig");
+const MachO = @import("../MachO.zig");
+const Relocation = @import("Relocation.zig");
+const SymbolWithLoc = MachO.SymbolWithLoc;
diff --git a/src/link/MachO/uuid.zig b/src/link/MachO/uuid.zig
index 8cef0693bf..bfd602d62a 100644
--- a/src/link/MachO/uuid.zig
+++ b/src/link/MachO/uuid.zig
@@ -1,12 +1,3 @@
-const std = @import("std");
-const fs = std.fs;
-const mem = std.mem;
-
-const Allocator = mem.Allocator;
-const Compilation = @import("../../Compilation.zig");
-const Md5 = std.crypto.hash.Md5;
-const Hasher = @import("hasher.zig").ParallelHasher;
-
/// Calculates Md5 hash of each chunk in parallel and then hashes all Md5 hashes to produce
/// the final digest.
/// While this is NOT a correct MD5 hash of the contents, this methodology is used by LLVM/LLD
@@ -43,3 +34,12 @@ inline fn conform(out: *[Md5.digest_length]u8) void {
out[6] = (out[6] & 0x0F) | (3 << 4);
out[8] = (out[8] & 0x3F) | 0x80;
}
+
+const std = @import("std");
+const fs = std.fs;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const Compilation = @import("../../Compilation.zig");
+const Md5 = std.crypto.hash.Md5;
+const Hasher = @import("hasher.zig").ParallelHasher;
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index 28a8e9b8a8..86bf14bdb2 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -1,2584 +1,8 @@
-const std = @import("std");
-const build_options = @import("build_options");
-const assert = std.debug.assert;
-const dwarf = std.dwarf;
-const fs = std.fs;
-const log = std.log.scoped(.link);
-const macho = std.macho;
-const math = std.math;
-const mem = std.mem;
-
-const aarch64 = @import("../../arch/aarch64/bits.zig");
-const calcUuid = @import("uuid.zig").calcUuid;
-const dead_strip = @import("dead_strip.zig");
-const eh_frame = @import("eh_frame.zig");
-const fat = @import("fat.zig");
-const link = @import("../../link.zig");
-const load_commands = @import("load_commands.zig");
-const stubs = @import("stubs.zig");
-const thunks = @import("thunks.zig");
-const trace = @import("../../tracy.zig").trace;
-
-const Allocator = mem.Allocator;
-const Archive = @import("Archive.zig");
-const Atom = @import("Atom.zig");
-const Cache = std.Build.Cache;
-const CodeSignature = @import("CodeSignature.zig");
-const Compilation = @import("../../Compilation.zig");
-const DwarfInfo = @import("DwarfInfo.zig");
-const Dylib = @import("Dylib.zig");
-const MachO = @import("../MachO.zig");
-const Md5 = std.crypto.hash.Md5;
-const LibStub = @import("../tapi.zig").LibStub;
-const Object = @import("Object.zig");
-const Section = MachO.Section;
-const StringTable = @import("../strtab.zig").StringTable;
-const SymbolWithLoc = MachO.SymbolWithLoc;
-const TableSection = @import("../table_section.zig").TableSection;
-const Trie = @import("Trie.zig");
-const UnwindInfo = @import("UnwindInfo.zig");
-
-const Bind = @import("dyld_info/bind.zig").Bind(*const Zld, SymbolWithLoc);
-const LazyBind = @import("dyld_info/bind.zig").LazyBind(*const Zld, SymbolWithLoc);
-const Rebase = @import("dyld_info/Rebase.zig");
-
-pub const Zld = struct {
- gpa: Allocator,
- file: fs.File,
- options: *const link.Options,
-
- dyld_info_cmd: macho.dyld_info_command = .{},
- symtab_cmd: macho.symtab_command = .{},
- dysymtab_cmd: macho.dysymtab_command = .{},
- function_starts_cmd: macho.linkedit_data_command = .{ .cmd = .FUNCTION_STARTS },
- data_in_code_cmd: macho.linkedit_data_command = .{ .cmd = .DATA_IN_CODE },
- uuid_cmd: macho.uuid_command = .{
- .uuid = [_]u8{0} ** 16,
- },
- codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
-
- objects: std.ArrayListUnmanaged(Object) = .{},
- archives: std.ArrayListUnmanaged(Archive) = .{},
- dylibs: std.ArrayListUnmanaged(Dylib) = .{},
- dylibs_map: std.StringHashMapUnmanaged(u16) = .{},
- referenced_dylibs: std.AutoArrayHashMapUnmanaged(u16, void) = .{},
-
- segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
- sections: std.MultiArrayList(Section) = .{},
-
- pagezero_segment_cmd_index: ?u8 = null,
- header_segment_cmd_index: ?u8 = null,
- text_segment_cmd_index: ?u8 = null,
- data_const_segment_cmd_index: ?u8 = null,
- data_segment_cmd_index: ?u8 = null,
- linkedit_segment_cmd_index: ?u8 = null,
-
- text_section_index: ?u8 = null,
- data_const_section_index: ?u8 = null,
- data_section_index: ?u8 = null,
- bss_section_index: ?u8 = null,
- thread_vars_section_index: ?u8 = null,
- thread_data_section_index: ?u8 = null,
- thread_bss_section_index: ?u8 = null,
- eh_frame_section_index: ?u8 = null,
- unwind_info_section_index: ?u8 = null,
- got_section_index: ?u8 = null,
- tlv_ptr_section_index: ?u8 = null,
- stubs_section_index: ?u8 = null,
- stub_helper_section_index: ?u8 = null,
- la_symbol_ptr_section_index: ?u8 = null,
-
- locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
- globals: std.ArrayListUnmanaged(SymbolWithLoc) = .{},
- resolver: std.StringHashMapUnmanaged(u32) = .{},
- unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
-
- locals_free_list: std.ArrayListUnmanaged(u32) = .{},
- globals_free_list: std.ArrayListUnmanaged(u32) = .{},
-
- entry_index: ?u32 = null,
- dyld_stub_binder_index: ?u32 = null,
- dyld_private_atom_index: ?Atom.Index = null,
-
- strtab: StringTable(.strtab) = .{},
-
- tlv_ptr_table: TableSection(SymbolWithLoc) = .{},
- got_table: TableSection(SymbolWithLoc) = .{},
- stubs_table: TableSection(SymbolWithLoc) = .{},
-
- thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{},
- thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{},
-
- atoms: std.ArrayListUnmanaged(Atom) = .{},
-
- pub fn addAtomToSection(self: *Zld, atom_index: Atom.Index) void {
- const atom = self.getAtomPtr(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- var section = self.sections.get(sym.n_sect - 1);
- if (section.header.size > 0) {
- const last_atom = self.getAtomPtr(section.last_atom_index.?);
- last_atom.next_index = atom_index;
- atom.prev_index = section.last_atom_index;
- } else {
- section.first_atom_index = atom_index;
- }
- section.last_atom_index = atom_index;
- section.header.size += atom.size;
- self.sections.set(sym.n_sect - 1, section);
- }
-
- const CreateAtomOpts = struct {
- size: u64 = 0,
- alignment: u32 = 0,
- };
-
- pub fn createAtom(self: *Zld, sym_index: u32, opts: CreateAtomOpts) !Atom.Index {
- const gpa = self.gpa;
- const index = @as(Atom.Index, @intCast(self.atoms.items.len));
- const atom = try self.atoms.addOne(gpa);
- atom.* = .{};
- atom.sym_index = sym_index;
- atom.size = opts.size;
- atom.alignment = opts.alignment;
- log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, index });
- return index;
- }
-
- fn createDyldPrivateAtom(self: *Zld) !void {
- const sym_index = try self.allocateSymbol();
- const atom_index = try self.createAtom(sym_index, .{ .size = @sizeOf(u64), .alignment = 3 });
- const sym = self.getSymbolPtr(.{ .sym_index = sym_index });
- sym.n_type = macho.N_SECT;
-
- if (self.data_section_index == null) {
- self.data_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__data", .{});
- }
- sym.n_sect = self.data_section_index.? + 1;
- self.dyld_private_atom_index = atom_index;
-
- self.addAtomToSection(atom_index);
- }
-
- fn createTentativeDefAtoms(self: *Zld) !void {
- const gpa = self.gpa;
-
- for (self.globals.items) |global| {
- const sym = self.getSymbolPtr(global);
- if (!sym.tentative()) continue;
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({?})", .{
- global.sym_index, self.getSymbolName(global), global.file,
- });
-
- // Convert any tentative definition into a regular symbol and allocate
- // text blocks for each tentative definition.
- const size = sym.n_value;
- const alignment = (sym.n_desc >> 8) & 0x0f;
-
- if (self.bss_section_index == null) {
- self.bss_section_index = try MachO.initSection(gpa, self, "__DATA", "__bss", .{
- .flags = macho.S_ZEROFILL,
- });
- }
-
- sym.* = .{
- .n_strx = sym.n_strx,
- .n_type = macho.N_SECT | macho.N_EXT,
- .n_sect = self.bss_section_index.? + 1,
- .n_desc = 0,
- .n_value = 0,
- };
-
- const atom_index = try self.createAtom(global.sym_index, .{
- .size = size,
- .alignment = alignment,
- });
- const atom = self.getAtomPtr(atom_index);
- atom.file = global.file;
-
- self.addAtomToSection(atom_index);
-
- assert(global.getFile() != null);
- const object = &self.objects.items[global.getFile().?];
- try object.atoms.append(gpa, atom_index);
- object.atom_by_index_table[global.sym_index] = atom_index;
- }
- }
-
- fn addUndefined(self: *Zld, name: []const u8) !u32 {
- const gop = try self.getOrPutGlobalPtr(name);
- const global_index = self.getGlobalIndex(name).?;
-
- if (gop.found_existing) return global_index;
-
- const sym_index = try self.allocateSymbol();
- const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
- gop.value_ptr.* = sym_loc;
-
- const sym = self.getSymbolPtr(sym_loc);
- sym.n_strx = try self.strtab.insert(self.gpa, name);
- sym.n_type = macho.N_UNDF;
-
- try self.unresolved.putNoClobber(self.gpa, global_index, {});
-
- return global_index;
- }
-
- fn resolveSymbols(self: *Zld) !void {
- // We add the specified entrypoint as the first unresolved symbols so that
- // we search for it in libraries should there be no object files specified
- // on the linker line.
- if (self.options.output_mode == .Exe) {
- const entry_name = self.options.entry orelse load_commands.default_entry_point;
- _ = try self.addUndefined(entry_name);
- }
-
- // Force resolution of any symbols requested by the user.
- for (self.options.force_undefined_symbols.keys()) |sym_name| {
- _ = try self.addUndefined(sym_name);
- }
-
- for (self.objects.items, 0..) |_, object_id| {
- try self.resolveSymbolsInObject(@as(u32, @intCast(object_id)));
- }
-
- try self.resolveSymbolsInArchives();
-
- // Finally, force resolution of dyld_stub_binder if there are imports
- // requested.
- if (self.unresolved.count() > 0) {
- self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder");
- }
-
- try self.resolveSymbolsInDylibs();
-
- try self.createMhExecuteHeaderSymbol();
- try self.createDsoHandleSymbol();
- try self.resolveSymbolsAtLoading();
- }
-
- fn resolveGlobalSymbol(self: *Zld, current: SymbolWithLoc) !void {
- const gpa = self.gpa;
- const sym = self.getSymbol(current);
- const sym_name = self.getSymbolName(current);
-
- const gop = try self.getOrPutGlobalPtr(sym_name);
- if (!gop.found_existing) {
- gop.value_ptr.* = current;
- if (sym.undf() and !sym.tentative()) {
- try self.unresolved.putNoClobber(gpa, self.getGlobalIndex(sym_name).?, {});
- }
- return;
- }
- const global_index = self.getGlobalIndex(sym_name).?;
- const global = gop.value_ptr.*;
- const global_sym = self.getSymbol(global);
-
- // Cases to consider: sym vs global_sym
- // 1. strong(sym) and strong(global_sym) => error
- // 2. strong(sym) and weak(global_sym) => sym
- // 3. strong(sym) and tentative(global_sym) => sym
- // 4. strong(sym) and undf(global_sym) => sym
- // 5. weak(sym) and strong(global_sym) => global_sym
- // 6. weak(sym) and tentative(global_sym) => sym
- // 7. weak(sym) and undf(global_sym) => sym
- // 8. tentative(sym) and strong(global_sym) => global_sym
- // 9. tentative(sym) and weak(global_sym) => global_sym
- // 10. tentative(sym) and tentative(global_sym) => pick larger
- // 11. tentative(sym) and undf(global_sym) => sym
- // 12. undf(sym) and * => global_sym
- //
- // Reduces to:
- // 1. strong(sym) and strong(global_sym) => error
- // 2. * and strong(global_sym) => global_sym
- // 3. weak(sym) and weak(global_sym) => global_sym
- // 4. tentative(sym) and tentative(global_sym) => pick larger
- // 5. undf(sym) and * => global_sym
- // 6. else => sym
-
- const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
- const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
- const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
- const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
-
- if (sym_is_strong and global_is_strong) {
- log.err("symbol '{s}' defined multiple times", .{sym_name});
- if (global.getFile()) |file| {
- log.err(" first definition in '{s}'", .{self.objects.items[file].name});
- }
- if (current.getFile()) |file| {
- log.err(" next definition in '{s}'", .{self.objects.items[file].name});
- }
- return error.MultipleSymbolDefinitions;
- }
-
- if (current.getFile()) |file| {
- const object = &self.objects.items[file];
- object.globals_lookup[current.sym_index] = global_index;
- }
-
- if (global_is_strong) return;
- if (sym_is_weak and global_is_weak) return;
- if (sym.tentative() and global_sym.tentative()) {
- if (global_sym.n_value >= sym.n_value) return;
- }
- if (sym.undf() and !sym.tentative()) return;
-
- if (global.getFile()) |file| {
- const global_object = &self.objects.items[file];
- global_object.globals_lookup[global.sym_index] = global_index;
- }
- _ = self.unresolved.swapRemove(global_index);
-
- gop.value_ptr.* = current;
- }
-
- fn resolveSymbolsInObject(self: *Zld, object_id: u32) !void {
- const object = &self.objects.items[object_id];
- const in_symtab = object.in_symtab orelse return;
-
- log.debug("resolving symbols in '{s}'", .{object.name});
-
- var sym_index: u32 = 0;
- while (sym_index < in_symtab.len) : (sym_index += 1) {
- const sym = &object.symtab[sym_index];
- const sym_name = object.getSymbolName(sym_index);
-
- if (sym.stab()) {
- log.err("unhandled symbol type: stab", .{});
- log.err(" symbol '{s}'", .{sym_name});
- log.err(" first definition in '{s}'", .{object.name});
- return error.UnhandledSymbolType;
- }
-
- if (sym.indr()) {
- log.err("unhandled symbol type: indirect", .{});
- log.err(" symbol '{s}'", .{sym_name});
- log.err(" first definition in '{s}'", .{object.name});
- return error.UnhandledSymbolType;
- }
-
- if (sym.abs()) {
- log.err("unhandled symbol type: absolute", .{});
- log.err(" symbol '{s}'", .{sym_name});
- log.err(" first definition in '{s}'", .{object.name});
- return error.UnhandledSymbolType;
- }
-
- if (sym.sect() and !sym.ext()) {
- log.debug("symbol '{s}' local to object {s}; skipping...", .{
- sym_name,
- object.name,
- });
- continue;
- }
-
- try self.resolveGlobalSymbol(.{ .sym_index = sym_index, .file = object_id + 1 });
- }
- }
-
- fn resolveSymbolsInArchives(self: *Zld) !void {
- if (self.archives.items.len == 0) return;
-
- const gpa = self.gpa;
-
- var next_sym: usize = 0;
- loop: while (next_sym < self.unresolved.count()) {
- const global = self.globals.items[self.unresolved.keys()[next_sym]];
- const sym_name = self.getSymbolName(global);
-
- for (self.archives.items) |archive| {
- // Check if the entry exists in a static archive.
- const offsets = archive.toc.get(sym_name) orelse {
- // No hit.
- continue;
- };
- assert(offsets.items.len > 0);
-
- const object_id = @as(u16, @intCast(self.objects.items.len));
- const object = try archive.parseObject(gpa, offsets.items[0]);
- try self.objects.append(gpa, object);
- try self.resolveSymbolsInObject(object_id);
-
- continue :loop;
- }
-
- next_sym += 1;
- }
- }
-
- fn resolveSymbolsInDylibs(self: *Zld) !void {
- if (self.dylibs.items.len == 0) return;
-
- var next_sym: usize = 0;
- loop: while (next_sym < self.unresolved.count()) {
- const global_index = self.unresolved.keys()[next_sym];
- const global = self.globals.items[global_index];
- const sym = self.getSymbolPtr(global);
- const sym_name = self.getSymbolName(global);
-
- for (self.dylibs.items, 0..) |dylib, id| {
- if (!dylib.symbols.contains(sym_name)) continue;
-
- const dylib_id = @as(u16, @intCast(id));
- if (!self.referenced_dylibs.contains(dylib_id)) {
- try self.referenced_dylibs.putNoClobber(self.gpa, dylib_id, {});
- }
-
- const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
- sym.n_type |= macho.N_EXT;
- sym.n_desc = @as(u16, @intCast(ordinal + 1)) * macho.N_SYMBOL_RESOLVER;
-
- if (dylib.weak) {
- sym.n_desc |= macho.N_WEAK_REF;
- }
-
- assert(self.unresolved.swapRemove(global_index));
- continue :loop;
- }
-
- next_sym += 1;
- }
- }
-
- fn resolveSymbolsAtLoading(self: *Zld) !void {
- const is_lib = self.options.output_mode == .Lib;
- const is_dyn_lib = self.options.link_mode == .Dynamic and is_lib;
- const allow_undef = is_dyn_lib and (self.options.allow_shlib_undefined orelse false);
-
- var next_sym: usize = 0;
- while (next_sym < self.unresolved.count()) {
- const global_index = self.unresolved.keys()[next_sym];
- const global = self.globals.items[global_index];
- const sym = self.getSymbolPtr(global);
-
- if (sym.discarded()) {
- sym.* = .{
- .n_strx = 0,
- .n_type = macho.N_UNDF,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- _ = self.unresolved.swapRemove(global_index);
- continue;
- } else if (allow_undef) {
- const n_desc = @as(
- u16,
- @bitCast(macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @as(i16, @intCast(macho.N_SYMBOL_RESOLVER))),
- );
- sym.n_type = macho.N_EXT;
- sym.n_desc = n_desc;
- _ = self.unresolved.swapRemove(global_index);
- continue;
- }
-
- next_sym += 1;
- }
- }
-
- fn createMhExecuteHeaderSymbol(self: *Zld) !void {
- if (self.options.output_mode != .Exe) return;
-
- const gpa = self.gpa;
- const sym_index = try self.allocateSymbol();
- const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
- const sym = self.getSymbolPtr(sym_loc);
- sym.* = .{
- .n_strx = try self.strtab.insert(gpa, "__mh_execute_header"),
- .n_type = macho.N_SECT | macho.N_EXT,
- .n_sect = 0,
- .n_desc = macho.REFERENCED_DYNAMICALLY,
- .n_value = 0,
- };
-
- const gop = try self.getOrPutGlobalPtr("__mh_execute_header");
- if (gop.found_existing) {
- const global = gop.value_ptr.*;
- if (global.getFile()) |file| {
- const global_object = &self.objects.items[file];
- global_object.globals_lookup[global.sym_index] = self.getGlobalIndex("__mh_execute_header").?;
- }
- }
- gop.value_ptr.* = sym_loc;
- }
-
- fn createDsoHandleSymbol(self: *Zld) !void {
- const global = self.getGlobalPtr("___dso_handle") orelse return;
- if (!self.getSymbol(global.*).undf()) return;
-
- const sym_index = try self.allocateSymbol();
- const sym_loc = SymbolWithLoc{ .sym_index = sym_index };
- const sym = self.getSymbolPtr(sym_loc);
- sym.* = .{
- .n_strx = try self.strtab.insert(self.gpa, "___dso_handle"),
- .n_type = macho.N_SECT | macho.N_EXT,
- .n_sect = 0,
- .n_desc = macho.N_WEAK_DEF,
- .n_value = 0,
- };
- const global_index = self.getGlobalIndex("___dso_handle").?;
- if (global.getFile()) |file| {
- const global_object = &self.objects.items[file];
- global_object.globals_lookup[global.sym_index] = global_index;
- }
- global.* = sym_loc;
- _ = self.unresolved.swapRemove(global_index);
- }
-
- pub fn deinit(self: *Zld) void {
- const gpa = self.gpa;
-
- self.tlv_ptr_table.deinit(gpa);
- self.got_table.deinit(gpa);
- self.stubs_table.deinit(gpa);
- self.thunk_table.deinit(gpa);
-
- for (self.thunks.items) |*thunk| {
- thunk.deinit(gpa);
- }
- self.thunks.deinit(gpa);
-
- self.strtab.deinit(gpa);
- self.locals.deinit(gpa);
- self.globals.deinit(gpa);
- self.resolver.deinit(gpa);
- self.unresolved.deinit(gpa);
- self.locals_free_list.deinit(gpa);
- self.globals_free_list.deinit(gpa);
-
- for (self.objects.items) |*object| {
- object.deinit(gpa);
- }
- self.objects.deinit(gpa);
- for (self.archives.items) |*archive| {
- archive.deinit(gpa);
- }
- self.archives.deinit(gpa);
- for (self.dylibs.items) |*dylib| {
- dylib.deinit(gpa);
- }
- self.dylibs.deinit(gpa);
- self.dylibs_map.deinit(gpa);
- self.referenced_dylibs.deinit(gpa);
-
- self.segments.deinit(gpa);
- self.sections.deinit(gpa);
- self.atoms.deinit(gpa);
- }
-
- fn createSegments(self: *Zld) !void {
- const pagezero_vmsize = self.options.pagezero_size orelse MachO.default_pagezero_vmsize;
- const page_size = MachO.getPageSize(self.options.target.cpu.arch);
- const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
- if (self.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
- if (aligned_pagezero_vmsize != pagezero_vmsize) {
- log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
- log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
- }
- self.pagezero_segment_cmd_index = @intCast(self.segments.items.len);
- try self.segments.append(self.gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__PAGEZERO"),
- .vmsize = aligned_pagezero_vmsize,
- });
- }
-
- // __TEXT segment is non-optional
- {
- const protection = MachO.getSegmentMemoryProtection("__TEXT");
- self.text_segment_cmd_index = @intCast(self.segments.items.len);
- self.header_segment_cmd_index = self.text_segment_cmd_index.?;
- try self.segments.append(self.gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__TEXT"),
- .maxprot = protection,
- .initprot = protection,
- });
- }
-
- for (self.sections.items(.header), 0..) |header, sect_id| {
- if (header.size == 0) continue; // empty section
-
- const segname = header.segName();
- const segment_id = self.getSegmentByName(segname) orelse blk: {
- log.debug("creating segment '{s}'", .{segname});
- const segment_id = @as(u8, @intCast(self.segments.items.len));
- const protection = MachO.getSegmentMemoryProtection(segname);
- try self.segments.append(self.gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString(segname),
- .maxprot = protection,
- .initprot = protection,
- });
- break :blk segment_id;
- };
- const segment = &self.segments.items[segment_id];
- segment.cmdsize += @sizeOf(macho.section_64);
- segment.nsects += 1;
- self.sections.items(.segment_index)[sect_id] = segment_id;
- }
-
- if (self.getSegmentByName("__DATA_CONST")) |index| {
- self.data_const_segment_cmd_index = index;
- }
-
- if (self.getSegmentByName("__DATA")) |index| {
- self.data_segment_cmd_index = index;
- }
-
- // __LINKEDIT always comes last
- {
- const protection = MachO.getSegmentMemoryProtection("__LINKEDIT");
- self.linkedit_segment_cmd_index = @intCast(self.segments.items.len);
- try self.segments.append(self.gpa, .{
- .cmdsize = @sizeOf(macho.segment_command_64),
- .segname = makeStaticString("__LINKEDIT"),
- .maxprot = protection,
- .initprot = protection,
- });
- }
- }
-
- pub fn allocateSymbol(self: *Zld) !u32 {
- try self.locals.ensureUnusedCapacity(self.gpa, 1);
- log.debug(" (allocating symbol index {d})", .{self.locals.items.len});
- const index = @as(u32, @intCast(self.locals.items.len));
- _ = self.locals.addOneAssumeCapacity();
- self.locals.items[index] = .{
- .n_strx = 0,
- .n_type = 0,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- };
- return index;
- }
-
- fn allocateGlobal(self: *Zld) !u32 {
- try self.globals.ensureUnusedCapacity(self.gpa, 1);
-
- const index = blk: {
- if (self.globals_free_list.popOrNull()) |index| {
- log.debug(" (reusing global index {d})", .{index});
- break :blk index;
- } else {
- log.debug(" (allocating symbol index {d})", .{self.globals.items.len});
- const index = @as(u32, @intCast(self.globals.items.len));
- _ = self.globals.addOneAssumeCapacity();
- break :blk index;
- }
- };
-
- self.globals.items[index] = .{ .sym_index = 0 };
-
- return index;
- }
-
- pub fn addGotEntry(self: *Zld, target: SymbolWithLoc) !void {
- if (self.got_table.lookup.contains(target)) return;
- _ = try self.got_table.allocateEntry(self.gpa, target);
- if (self.got_section_index == null) {
- self.got_section_index = try MachO.initSection(self.gpa, self, "__DATA_CONST", "__got", .{
- .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
- });
- }
- }
-
- pub fn addTlvPtrEntry(self: *Zld, target: SymbolWithLoc) !void {
- if (self.tlv_ptr_table.lookup.contains(target)) return;
- _ = try self.tlv_ptr_table.allocateEntry(self.gpa, target);
- if (self.tlv_ptr_section_index == null) {
- self.tlv_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__thread_ptrs", .{
- .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
- });
- }
- }
-
- pub fn addStubEntry(self: *Zld, target: SymbolWithLoc) !void {
- if (self.stubs_table.lookup.contains(target)) return;
- _ = try self.stubs_table.allocateEntry(self.gpa, target);
- if (self.stubs_section_index == null) {
- self.stubs_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stubs", .{
- .flags = macho.S_SYMBOL_STUBS |
- macho.S_ATTR_PURE_INSTRUCTIONS |
- macho.S_ATTR_SOME_INSTRUCTIONS,
- .reserved2 = stubs.stubSize(self.options.target.cpu.arch),
- });
- self.stub_helper_section_index = try MachO.initSection(self.gpa, self, "__TEXT", "__stub_helper", .{
- .flags = macho.S_REGULAR |
- macho.S_ATTR_PURE_INSTRUCTIONS |
- macho.S_ATTR_SOME_INSTRUCTIONS,
- });
- self.la_symbol_ptr_section_index = try MachO.initSection(self.gpa, self, "__DATA", "__la_symbol_ptr", .{
- .flags = macho.S_LAZY_SYMBOL_POINTERS,
- });
- }
- }
-
- fn writeAtoms(self: *Zld) !void {
- const gpa = self.gpa;
- const slice = self.sections.slice();
-
- for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
- const header = slice.items(.header)[sect_id];
- if (header.isZerofill()) continue;
-
- var atom_index = first_atom_index orelse continue;
-
- var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
- defer gpa.free(buffer);
- @memset(buffer, 0); // TODO with NOPs
-
- log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
-
- while (true) {
- const atom = self.getAtom(atom_index);
- if (atom.getFile()) |file| {
- const this_sym = self.getSymbol(atom.getSymbolWithLoc());
- const padding_size: usize = if (atom.next_index) |next_index| blk: {
- const next_sym = self.getSymbol(self.getAtom(next_index).getSymbolWithLoc());
- const size = next_sym.n_value - (this_sym.n_value + atom.size);
- break :blk math.cast(usize, size) orelse return error.Overflow;
- } else 0;
-
- log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
- atom.sym_index,
- self.getSymbolName(atom.getSymbolWithLoc()),
- file,
- });
- if (padding_size > 0) {
- log.debug(" (with padding {x})", .{padding_size});
- }
-
- const offset = this_sym.n_value - header.addr;
- log.debug(" (at offset 0x{x})", .{offset});
-
- const code = Atom.getAtomCode(self, atom_index);
- const relocs = Atom.getAtomRelocs(self, atom_index);
- const size = math.cast(usize, atom.size) orelse return error.Overflow;
- @memcpy(buffer[offset .. offset + size], code);
- try Atom.resolveRelocs(
- self,
- atom_index,
- buffer[offset..][0..size],
- relocs,
- );
- }
-
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
-
- log.debug(" (writing at file offset 0x{x})", .{header.offset});
- try self.file.pwriteAll(buffer, header.offset);
- }
- }
-
- fn writeDyldPrivateAtom(self: *Zld) !void {
- const atom_index = self.dyld_private_atom_index orelse return;
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- const sect_id = self.data_section_index.?;
- const header = self.sections.items(.header)[sect_id];
- const offset = sym.n_value - header.addr + header.offset;
- log.debug("writing __dyld_private at offset 0x{x}", .{offset});
- const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
- try self.file.pwriteAll(&buffer, offset);
- }
-
- fn writeThunks(self: *Zld) !void {
- assert(self.requiresThunks());
- const gpa = self.gpa;
-
- const sect_id = self.text_section_index orelse return;
- const header = self.sections.items(.header)[sect_id];
-
- for (self.thunks.items, 0..) |*thunk, i| {
- if (thunk.getSize() == 0) continue;
- var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize());
- defer buffer.deinit();
- try thunks.writeThunkCode(self, thunk, buffer.writer());
- const thunk_atom = self.getAtom(thunk.getStartAtomIndex());
- const thunk_sym = self.getSymbol(thunk_atom.getSymbolWithLoc());
- const offset = thunk_sym.n_value - header.addr + header.offset;
- log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset });
- try self.file.pwriteAll(buffer.items, offset);
- }
- }
-
- fn writePointerEntries(self: *Zld, sect_id: u8, table: anytype) !void {
- const header = self.sections.items(.header)[sect_id];
- var buffer = try std.ArrayList(u8).initCapacity(self.gpa, header.size);
- defer buffer.deinit();
- for (table.entries.items) |entry| {
- const sym = self.getSymbol(entry);
- buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
- }
- log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset});
- try self.file.pwriteAll(buffer.items, header.offset);
- }
-
- fn writeStubs(self: *Zld) !void {
- const gpa = self.gpa;
- const cpu_arch = self.options.target.cpu.arch;
- const stubs_header = self.sections.items(.header)[self.stubs_section_index.?];
- const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?];
-
- var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size);
- defer buffer.deinit();
-
- for (0..self.stubs_table.count()) |index| {
- try stubs.writeStubCode(.{
- .cpu_arch = cpu_arch,
- .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index,
- .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64),
- }, buffer.writer());
- }
-
- log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
- try self.file.pwriteAll(buffer.items, stubs_header.offset);
- }
-
- fn writeStubHelpers(self: *Zld) !void {
- const gpa = self.gpa;
- const cpu_arch = self.options.target.cpu.arch;
- const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?];
-
- var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size);
- defer buffer.deinit();
-
- {
- const dyld_private_addr = blk: {
- const atom = self.getAtom(self.dyld_private_atom_index.?);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- break :blk sym.n_value;
- };
- const dyld_stub_binder_got_addr = blk: {
- const sym_loc = self.globals.items[self.dyld_stub_binder_index.?];
- break :blk self.getGotEntryAddress(sym_loc).?;
- };
- try stubs.writeStubHelperPreambleCode(.{
- .cpu_arch = cpu_arch,
- .source_addr = stub_helper_header.addr,
- .dyld_private_addr = dyld_private_addr,
- .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
- }, buffer.writer());
- }
-
- for (0..self.stubs_table.count()) |index| {
- const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
- stubs.stubHelperSize(cpu_arch) * index;
- try stubs.writeStubHelperCode(.{
- .cpu_arch = cpu_arch,
- .source_addr = source_addr,
- .target_addr = stub_helper_header.addr,
- }, buffer.writer());
- }
-
- log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
- stub_helper_header.offset,
- });
- try self.file.pwriteAll(buffer.items, stub_helper_header.offset);
- }
-
- fn writeLaSymbolPtrs(self: *Zld) !void {
- const gpa = self.gpa;
- const cpu_arch = self.options.target.cpu.arch;
- const la_symbol_ptr_header = self.sections.items(.header)[self.la_symbol_ptr_section_index.?];
- const stub_helper_header = self.sections.items(.header)[self.stub_helper_section_index.?];
-
- var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size);
- defer buffer.deinit();
-
- for (0..self.stubs_table.count()) |index| {
- const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
- stubs.stubHelperSize(cpu_arch) * index;
- buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
- }
-
- log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
- la_symbol_ptr_header.offset,
- });
- try self.file.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
- }
-
- fn pruneAndSortSections(self: *Zld) !void {
- const Entry = struct {
- index: u8,
-
- pub fn lessThan(zld: *Zld, lhs: @This(), rhs: @This()) bool {
- const lhs_header = zld.sections.items(.header)[lhs.index];
- const rhs_header = zld.sections.items(.header)[rhs.index];
- return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header);
- }
- };
-
- const gpa = self.gpa;
-
- var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.slice().len);
- defer entries.deinit();
-
- for (0..self.sections.slice().len) |index| {
- const section = self.sections.get(index);
- if (section.header.size == 0) {
- log.debug("pruning section {s},{s} {?d}", .{
- section.header.segName(),
- section.header.sectName(),
- section.first_atom_index,
- });
- for (&[_]*?u8{
- &self.text_section_index,
- &self.data_const_section_index,
- &self.data_section_index,
- &self.bss_section_index,
- &self.thread_vars_section_index,
- &self.thread_data_section_index,
- &self.thread_bss_section_index,
- &self.eh_frame_section_index,
- &self.unwind_info_section_index,
- &self.got_section_index,
- &self.tlv_ptr_section_index,
- &self.stubs_section_index,
- &self.stub_helper_section_index,
- &self.la_symbol_ptr_section_index,
- }) |maybe_index| {
- if (maybe_index.* != null and maybe_index.*.? == index) {
- maybe_index.* = null;
- }
- }
- continue;
- }
- entries.appendAssumeCapacity(.{ .index = @intCast(index) });
- }
-
- mem.sort(Entry, entries.items, self, Entry.lessThan);
-
- var slice = self.sections.toOwnedSlice();
- defer slice.deinit(gpa);
-
- const backlinks = try gpa.alloc(u8, slice.len);
- defer gpa.free(backlinks);
- for (entries.items, 0..) |entry, i| {
- backlinks[entry.index] = @as(u8, @intCast(i));
- }
-
- try self.sections.ensureTotalCapacity(gpa, entries.items.len);
- for (entries.items) |entry| {
- self.sections.appendAssumeCapacity(slice.get(entry.index));
- }
-
- for (&[_]*?u8{
- &self.text_section_index,
- &self.data_const_section_index,
- &self.data_section_index,
- &self.bss_section_index,
- &self.thread_vars_section_index,
- &self.thread_data_section_index,
- &self.thread_bss_section_index,
- &self.eh_frame_section_index,
- &self.unwind_info_section_index,
- &self.got_section_index,
- &self.tlv_ptr_section_index,
- &self.stubs_section_index,
- &self.stub_helper_section_index,
- &self.la_symbol_ptr_section_index,
- }) |maybe_index| {
- if (maybe_index.*) |*index| {
- index.* = backlinks[index.*];
- }
- }
- }
-
- fn calcSectionSizes(self: *Zld) !void {
- const slice = self.sections.slice();
- for (slice.items(.header), 0..) |*header, sect_id| {
- if (header.size == 0) continue;
- if (self.text_section_index) |txt| {
- if (txt == sect_id and self.requiresThunks()) continue;
- }
-
- var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
-
- header.size = 0;
- header.@"align" = 0;
-
- while (true) {
- const atom = self.getAtom(atom_index);
- const atom_alignment = try math.powi(u32, 2, atom.alignment);
- const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
- const padding = atom_offset - header.size;
-
- const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
- sym.n_value = atom_offset;
-
- header.size += padding + atom.size;
- header.@"align" = @max(header.@"align", atom.alignment);
-
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
- }
-
- if (self.text_section_index != null and self.requiresThunks()) {
- // Create jump/branch range extenders if needed.
- try thunks.createThunks(self, self.text_section_index.?);
- }
-
- // Update offsets of all symbols contained within each Atom.
- // We need to do this since our unwind info synthesiser relies on
- // traversing the symbols when synthesising unwind info and DWARF CFI records.
- for (slice.items(.first_atom_index)) |first_atom_index| {
- var atom_index = first_atom_index orelse continue;
-
- while (true) {
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
-
- if (atom.getFile() != null) {
- // Update each symbol contained within the atom
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |sym_loc| {
- const inner_sym = self.getSymbolPtr(sym_loc);
- inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
- self,
- atom_index,
- sym_loc.sym_index,
- );
- }
-
- // If there is a section alias, update it now too
- if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
- const alias = self.getSymbolPtr(sym_loc);
- alias.n_value = sym.n_value;
- }
- }
-
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
- }
-
- if (self.got_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.size = self.got_table.count() * @sizeOf(u64);
- header.@"align" = 3;
- }
-
- if (self.tlv_ptr_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.size = self.tlv_ptr_table.count() * @sizeOf(u64);
- header.@"align" = 3;
- }
-
- const cpu_arch = self.options.target.cpu.arch;
-
- if (self.stubs_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.size = self.stubs_table.count() * stubs.stubSize(cpu_arch);
- header.@"align" = stubs.stubAlignment(cpu_arch);
- }
-
- if (self.stub_helper_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.size = self.stubs_table.count() * stubs.stubHelperSize(cpu_arch) +
- stubs.stubHelperPreambleSize(cpu_arch);
- header.@"align" = stubs.stubAlignment(cpu_arch);
- }
-
- if (self.la_symbol_ptr_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.size = self.stubs_table.count() * @sizeOf(u64);
- header.@"align" = 3;
- }
- }
-
- fn allocateSegments(self: *Zld) !void {
- for (self.segments.items, 0..) |*segment, segment_index| {
- const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
- const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(self.gpa, self.options, .{
- .segments = self.segments.items,
- .dylibs = self.dylibs.items,
- .referenced_dylibs = self.referenced_dylibs.keys(),
- }) else 0;
- try self.allocateSegment(@as(u8, @intCast(segment_index)), base_size);
- }
- }
-
- fn getSegmentAllocBase(self: Zld, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
- if (segment_index > 0) {
- const prev_segment = self.segments.items[segment_index - 1];
- return .{
- .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
- .fileoff = prev_segment.fileoff + prev_segment.filesize,
- };
- }
- return .{ .vmaddr = 0, .fileoff = 0 };
- }
-
- fn allocateSegment(self: *Zld, segment_index: u8, init_size: u64) !void {
- const segment = &self.segments.items[segment_index];
-
- if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
-
- const base = self.getSegmentAllocBase(segment_index);
- segment.vmaddr = base.vmaddr;
- segment.fileoff = base.fileoff;
- segment.filesize = init_size;
- segment.vmsize = init_size;
-
- // Allocate the sections according to their alignment at the beginning of the segment.
- const indexes = self.getSectionIndexes(segment_index);
- var start = init_size;
-
- const slice = self.sections.slice();
- for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
- const alignment = try math.powi(u32, 2, header.@"align");
- const start_aligned = mem.alignForward(u64, start, alignment);
- const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
-
- header.offset = if (header.isZerofill())
- 0
- else
- @as(u32, @intCast(segment.fileoff + start_aligned));
- header.addr = segment.vmaddr + start_aligned;
-
- if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| {
- var atom_index = first_atom_index;
-
- log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
- n_sect,
- header.segName(),
- header.sectName(),
- });
-
- while (true) {
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbolPtr(atom.getSymbolWithLoc());
- sym.n_value += header.addr;
- sym.n_sect = n_sect;
-
- log.debug(" ATOM(%{d}, '{s}') @{x}", .{
- atom.sym_index,
- self.getSymbolName(atom.getSymbolWithLoc()),
- sym.n_value,
- });
-
- if (atom.getFile() != null) {
- // Update each symbol contained within the atom
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |sym_loc| {
- const inner_sym = self.getSymbolPtr(sym_loc);
- inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
- self,
- atom_index,
- sym_loc.sym_index,
- );
- inner_sym.n_sect = n_sect;
- }
-
- // If there is a section alias, update it now too
- if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
- const alias = self.getSymbolPtr(sym_loc);
- alias.n_value = sym.n_value;
- alias.n_sect = n_sect;
- }
- }
-
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
- }
-
- start = start_aligned + header.size;
-
- if (!header.isZerofill()) {
- segment.filesize = start;
- }
- segment.vmsize = start;
- }
-
- const page_size = MachO.getPageSize(self.options.target.cpu.arch);
- segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
- segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
- }
-
- fn writeLinkeditSegmentData(self: *Zld) !void {
- const page_size = MachO.getPageSize(self.options.target.cpu.arch);
- const seg = self.getLinkeditSegmentPtr();
- seg.filesize = 0;
- seg.vmsize = 0;
-
- for (self.segments.items, 0..) |segment, id| {
- if (self.linkedit_segment_cmd_index.? == @as(u8, @intCast(id))) continue;
- if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
- seg.vmaddr = mem.alignForward(u64, segment.vmaddr + segment.vmsize, page_size);
- }
- if (seg.fileoff < segment.fileoff + segment.filesize) {
- seg.fileoff = mem.alignForward(u64, segment.fileoff + segment.filesize, page_size);
- }
- }
- try self.writeDyldInfoData();
- try self.writeFunctionStarts();
- try self.writeDataInCode();
- try self.writeSymtabs();
-
- seg.vmsize = mem.alignForward(u64, seg.filesize, page_size);
- }
-
- fn collectRebaseData(self: *Zld, rebase: *Rebase) !void {
- log.debug("collecting rebase data", .{});
-
- // First, unpack GOT entries
- if (self.got_section_index) |sect_id| {
- try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.got_table);
- }
-
- // Next, unpack __la_symbol_ptr entries
- if (self.la_symbol_ptr_section_index) |sect_id| {
- try MachO.collectRebaseDataFromTableSection(self.gpa, self, sect_id, rebase, self.stubs_table);
- }
-
- // Finally, unpack the rest.
- const cpu_arch = self.options.target.cpu.arch;
- for (self.objects.items) |*object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- const sect_id = sym.n_sect - 1;
- const section = self.sections.items(.header)[sect_id];
- const segment_id = self.sections.items(.segment_index)[sect_id];
- const segment = self.segments.items[segment_id];
- if (segment.maxprot & macho.PROT.WRITE == 0) continue;
- switch (section.type()) {
- macho.S_LITERAL_POINTERS,
- macho.S_REGULAR,
- macho.S_MOD_INIT_FUNC_POINTERS,
- macho.S_MOD_TERM_FUNC_POINTERS,
- => {},
- else => continue,
- }
-
- log.debug(" ATOM({d}, %{d}, '{s}')", .{
- atom_index,
- atom.sym_index,
- self.getSymbolName(atom.getSymbolWithLoc()),
- });
-
- const code = Atom.getAtomCode(self, atom_index);
- const relocs = Atom.getAtomRelocs(self, atom_index);
- const ctx = Atom.getRelocContext(self, atom_index);
-
- for (relocs) |rel| {
- switch (cpu_arch) {
- .aarch64 => {
- const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
- if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
- if (rel.r_length != 3) continue;
- },
- .x86_64 => {
- const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
- if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
- if (rel.r_length != 3) continue;
- },
- else => unreachable,
- }
- const target = Atom.parseRelocTarget(self, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = code,
- .base_offset = ctx.base_offset,
- .base_addr = ctx.base_addr,
- });
- const target_sym = self.getSymbol(target);
- if (target_sym.undf()) continue;
-
- const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr));
- const rel_offset = rel.r_address - ctx.base_offset;
- const offset = @as(u64, @intCast(base_offset + rel_offset));
- log.debug(" | rebase at {x}", .{offset});
-
- try rebase.entries.append(self.gpa, .{
- .offset = offset,
- .segment_id = segment_id,
- });
- }
- }
- }
-
- try rebase.finalize(self.gpa);
- }
-
- fn collectBindData(
- self: *Zld,
- bind: *Bind,
- ) !void {
- log.debug("collecting bind data", .{});
-
- // First, unpack GOT section
- if (self.got_section_index) |sect_id| {
- try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.got_table);
- }
-
- // Next, unpack TLV pointers section
- if (self.tlv_ptr_section_index) |sect_id| {
- try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, bind, self.tlv_ptr_table);
- }
-
- // Finally, unpack the rest.
- const cpu_arch = self.options.target.cpu.arch;
- for (self.objects.items) |*object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- const sect_id = sym.n_sect - 1;
- const section = self.sections.items(.header)[sect_id];
- const segment_id = self.sections.items(.segment_index)[sect_id];
- const segment = self.segments.items[segment_id];
- if (segment.maxprot & macho.PROT.WRITE == 0) continue;
- switch (section.type()) {
- macho.S_LITERAL_POINTERS,
- macho.S_REGULAR,
- macho.S_MOD_INIT_FUNC_POINTERS,
- macho.S_MOD_TERM_FUNC_POINTERS,
- => {},
- else => continue,
- }
-
- log.debug(" ATOM({d}, %{d}, '{s}')", .{
- atom_index,
- atom.sym_index,
- self.getSymbolName(atom.getSymbolWithLoc()),
- });
-
- const code = Atom.getAtomCode(self, atom_index);
- const relocs = Atom.getAtomRelocs(self, atom_index);
- const ctx = Atom.getRelocContext(self, atom_index);
-
- for (relocs) |rel| {
- switch (cpu_arch) {
- .aarch64 => {
- const rel_type = @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type));
- if (rel_type != .ARM64_RELOC_UNSIGNED) continue;
- if (rel.r_length != 3) continue;
- },
- .x86_64 => {
- const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type));
- if (rel_type != .X86_64_RELOC_UNSIGNED) continue;
- if (rel.r_length != 3) continue;
- },
- else => unreachable,
- }
-
- const global = Atom.parseRelocTarget(self, .{
- .object_id = atom.getFile().?,
- .rel = rel,
- .code = code,
- .base_offset = ctx.base_offset,
- .base_addr = ctx.base_addr,
- });
- const bind_sym_name = self.getSymbolName(global);
- const bind_sym = self.getSymbol(global);
- if (!bind_sym.undf()) continue;
-
- const base_offset = sym.n_value - segment.vmaddr;
- const rel_offset = @as(u32, @intCast(rel.r_address - ctx.base_offset));
- const offset = @as(u64, @intCast(base_offset + rel_offset));
- const addend = mem.readIntLittle(i64, code[rel_offset..][0..8]);
-
- const dylib_ordinal = @divTrunc(@as(i16, @bitCast(bind_sym.n_desc)), macho.N_SYMBOL_RESOLVER);
- log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
- base_offset,
- bind_sym_name,
- dylib_ordinal,
- });
- log.debug(" | with addend {x}", .{addend});
- if (bind_sym.weakRef()) {
- log.debug(" | marking as weak ref ", .{});
- }
- try bind.entries.append(self.gpa, .{
- .target = global,
- .offset = offset,
- .segment_id = segment_id,
- .addend = addend,
- });
- }
- }
- }
-
- try bind.finalize(self.gpa, self);
- }
-
- fn collectLazyBindData(self: *Zld, lazy_bind: *LazyBind) !void {
- const sect_id = self.la_symbol_ptr_section_index orelse return;
- try MachO.collectBindDataFromTableSection(self.gpa, self, sect_id, lazy_bind, self.stubs_table);
- try lazy_bind.finalize(self.gpa, self);
- }
-
- fn collectExportData(self: *Zld, trie: *Trie) !void {
- const gpa = self.gpa;
-
- // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
- log.debug("collecting export data", .{});
-
- const exec_segment = self.segments.items[self.header_segment_cmd_index.?];
- const base_address = exec_segment.vmaddr;
-
- for (self.globals.items) |global| {
- const sym = self.getSymbol(global);
- if (sym.undf()) continue;
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- const sym_name = self.getSymbolName(global);
- log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
- try trie.put(gpa, .{
- .name = sym_name,
- .vmaddr_offset = sym.n_value - base_address,
- .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
- });
- }
-
- try trie.finalize(gpa);
- }
-
- fn writeDyldInfoData(self: *Zld) !void {
- const gpa = self.gpa;
-
- var rebase = Rebase{};
- defer rebase.deinit(gpa);
- try self.collectRebaseData(&rebase);
-
- var bind = Bind{};
- defer bind.deinit(gpa);
- try self.collectBindData(&bind);
-
- var lazy_bind = LazyBind{};
- defer lazy_bind.deinit(gpa);
- try self.collectLazyBindData(&lazy_bind);
-
- var trie = Trie{};
- defer trie.deinit(gpa);
- try trie.init(gpa);
- try self.collectExportData(&trie);
-
- const link_seg = self.getLinkeditSegmentPtr();
- assert(mem.isAlignedGeneric(u64, link_seg.fileoff, @alignOf(u64)));
- const rebase_off = link_seg.fileoff;
- const rebase_size = rebase.size();
- const rebase_size_aligned = mem.alignForward(u64, rebase_size, @alignOf(u64));
- log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size_aligned });
-
- const bind_off = rebase_off + rebase_size_aligned;
- const bind_size = bind.size();
- const bind_size_aligned = mem.alignForward(u64, bind_size, @alignOf(u64));
- log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size_aligned });
-
- const lazy_bind_off = bind_off + bind_size_aligned;
- const lazy_bind_size = lazy_bind.size();
- const lazy_bind_size_aligned = mem.alignForward(u64, lazy_bind_size, @alignOf(u64));
- log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{
- lazy_bind_off,
- lazy_bind_off + lazy_bind_size_aligned,
- });
-
- const export_off = lazy_bind_off + lazy_bind_size_aligned;
- const export_size = trie.size;
- const export_size_aligned = mem.alignForward(u64, export_size, @alignOf(u64));
- log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size_aligned });
-
- const needed_size = math.cast(usize, export_off + export_size_aligned - rebase_off) orelse
- return error.Overflow;
- link_seg.filesize = needed_size;
- assert(mem.isAlignedGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64)));
-
- var buffer = try gpa.alloc(u8, needed_size);
- defer gpa.free(buffer);
- @memset(buffer, 0);
-
- var stream = std.io.fixedBufferStream(buffer);
- const writer = stream.writer();
-
- try rebase.write(writer);
- try stream.seekTo(bind_off - rebase_off);
-
- try bind.write(writer);
- try stream.seekTo(lazy_bind_off - rebase_off);
-
- try lazy_bind.write(writer);
- try stream.seekTo(export_off - rebase_off);
-
- _ = try trie.write(writer);
-
- log.debug("writing dyld info from 0x{x} to 0x{x}", .{
- rebase_off,
- rebase_off + needed_size,
- });
-
- try self.file.pwriteAll(buffer, rebase_off);
- try MachO.populateLazyBindOffsetsInStubHelper(
- self,
- self.options.target.cpu.arch,
- self.file,
- lazy_bind,
- );
-
- self.dyld_info_cmd.rebase_off = @as(u32, @intCast(rebase_off));
- self.dyld_info_cmd.rebase_size = @as(u32, @intCast(rebase_size_aligned));
- self.dyld_info_cmd.bind_off = @as(u32, @intCast(bind_off));
- self.dyld_info_cmd.bind_size = @as(u32, @intCast(bind_size_aligned));
- self.dyld_info_cmd.lazy_bind_off = @as(u32, @intCast(lazy_bind_off));
- self.dyld_info_cmd.lazy_bind_size = @as(u32, @intCast(lazy_bind_size_aligned));
- self.dyld_info_cmd.export_off = @as(u32, @intCast(export_off));
- self.dyld_info_cmd.export_size = @as(u32, @intCast(export_size_aligned));
- }
-
- const asc_u64 = std.sort.asc(u64);
-
- fn addSymbolToFunctionStarts(self: *Zld, sym_loc: SymbolWithLoc, addresses: *std.ArrayList(u64)) !void {
- const sym = self.getSymbol(sym_loc);
- if (sym.n_strx == 0) return;
- if (sym.n_desc == MachO.N_DEAD) return;
- if (self.symbolIsTemp(sym_loc)) return;
- try addresses.append(sym.n_value);
- }
-
- fn writeFunctionStarts(self: *Zld) !void {
- const gpa = self.gpa;
- const seg = self.segments.items[self.header_segment_cmd_index.?];
-
- // We need to sort by address first
- var addresses = std.ArrayList(u64).init(gpa);
- defer addresses.deinit();
-
- for (self.objects.items) |object| {
- for (object.exec_atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const sym_loc = atom.getSymbolWithLoc();
- try self.addSymbolToFunctionStarts(sym_loc, &addresses);
-
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |inner_sym_loc| {
- try self.addSymbolToFunctionStarts(inner_sym_loc, &addresses);
- }
- }
- }
-
- mem.sort(u64, addresses.items, {}, asc_u64);
-
- var offsets = std.ArrayList(u32).init(gpa);
- defer offsets.deinit();
- try offsets.ensureTotalCapacityPrecise(addresses.items.len);
-
- var last_off: u32 = 0;
- for (addresses.items) |addr| {
- const offset = @as(u32, @intCast(addr - seg.vmaddr));
- const diff = offset - last_off;
-
- if (diff == 0) continue;
-
- offsets.appendAssumeCapacity(diff);
- last_off = offset;
- }
-
- var buffer = std.ArrayList(u8).init(gpa);
- defer buffer.deinit();
-
- const max_size = @as(usize, @intCast(offsets.items.len * @sizeOf(u64)));
- try buffer.ensureTotalCapacity(max_size);
-
- for (offsets.items) |offset| {
- try std.leb.writeULEB128(buffer.writer(), offset);
- }
-
- const link_seg = self.getLinkeditSegmentPtr();
- const offset = link_seg.fileoff + link_seg.filesize;
- assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
- const needed_size = buffer.items.len;
- const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
- const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
- if (padding > 0) {
- try buffer.ensureUnusedCapacity(padding);
- buffer.appendNTimesAssumeCapacity(0, padding);
- }
- link_seg.filesize = offset + needed_size_aligned - link_seg.fileoff;
-
- log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
-
- try self.file.pwriteAll(buffer.items, offset);
-
- self.function_starts_cmd.dataoff = @as(u32, @intCast(offset));
- self.function_starts_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
- }
-
- fn filterDataInCode(
- dices: []const macho.data_in_code_entry,
- start_addr: u64,
- end_addr: u64,
- ) []const macho.data_in_code_entry {
- const Predicate = struct {
- addr: u64,
-
- pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
- return dice.offset >= self.addr;
- }
- };
-
- const start = MachO.lsearch(macho.data_in_code_entry, dices, Predicate{ .addr = start_addr });
- const end = MachO.lsearch(macho.data_in_code_entry, dices[start..], Predicate{ .addr = end_addr }) + start;
-
- return dices[start..end];
- }
-
- fn writeDataInCode(self: *Zld) !void {
- var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
- defer out_dice.deinit();
-
- const text_sect_id = self.text_section_index orelse return;
- const text_sect_header = self.sections.items(.header)[text_sect_id];
-
- for (self.objects.items) |object| {
- if (!object.hasDataInCode()) continue;
- const dice = object.data_in_code.items;
- try out_dice.ensureUnusedCapacity(dice.len);
-
- for (object.exec_atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- const source_addr = if (object.getSourceSymbol(atom.sym_index)) |source_sym|
- source_sym.n_value
- else blk: {
- const nbase = @as(u32, @intCast(object.in_symtab.?.len));
- const source_sect_id = @as(u8, @intCast(atom.sym_index - nbase));
- break :blk object.getSourceSection(source_sect_id).addr;
- };
- const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
- const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
- return error.Overflow;
-
- for (filtered_dice) |single| {
- const offset = math.cast(u32, single.offset - source_addr + base) orelse
- return error.Overflow;
- out_dice.appendAssumeCapacity(.{
- .offset = offset,
- .length = single.length,
- .kind = single.kind,
- });
- }
- }
- }
-
- const seg = self.getLinkeditSegmentPtr();
- const offset = seg.fileoff + seg.filesize;
- assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
- const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
- const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
- seg.filesize = offset + needed_size_aligned - seg.fileoff;
-
- const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
- defer self.gpa.free(buffer);
- {
- const src = mem.sliceAsBytes(out_dice.items);
- @memcpy(buffer[0..src.len], src);
- @memset(buffer[src.len..], 0);
- }
-
- log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
-
- try self.file.pwriteAll(buffer, offset);
-
- self.data_in_code_cmd.dataoff = @as(u32, @intCast(offset));
- self.data_in_code_cmd.datasize = @as(u32, @intCast(needed_size_aligned));
- }
-
- fn writeSymtabs(self: *Zld) !void {
- var ctx = try self.writeSymtab();
- defer ctx.imports_table.deinit();
- try self.writeDysymtab(ctx);
- try self.writeStrtab();
- }
-
- fn addLocalToSymtab(self: *Zld, sym_loc: SymbolWithLoc, locals: *std.ArrayList(macho.nlist_64)) !void {
- const sym = self.getSymbol(sym_loc);
- if (sym.n_strx == 0) return; // no name, skip
- if (sym.n_desc == MachO.N_DEAD) return; // garbage-collected, skip
- if (sym.ext()) return; // an export lands in its own symtab section, skip
- if (self.symbolIsTemp(sym_loc)) return; // local temp symbol, skip
-
- var out_sym = sym;
- out_sym.n_strx = try self.strtab.insert(self.gpa, self.getSymbolName(sym_loc));
- try locals.append(out_sym);
- }
-
- fn writeSymtab(self: *Zld) !SymtabCtx {
- const gpa = self.gpa;
-
- var locals = std.ArrayList(macho.nlist_64).init(gpa);
- defer locals.deinit();
-
- for (self.objects.items) |object| {
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const sym_loc = atom.getSymbolWithLoc();
- try self.addLocalToSymtab(sym_loc, &locals);
-
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |inner_sym_loc| {
- try self.addLocalToSymtab(inner_sym_loc, &locals);
- }
- }
- }
-
- var exports = std.ArrayList(macho.nlist_64).init(gpa);
- defer exports.deinit();
-
- for (self.globals.items) |global| {
- const sym = self.getSymbol(global);
- if (sym.undf()) continue; // import, skip
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- var out_sym = sym;
- out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
- try exports.append(out_sym);
- }
-
- var imports = std.ArrayList(macho.nlist_64).init(gpa);
- defer imports.deinit();
-
- var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
-
- for (self.globals.items) |global| {
- const sym = self.getSymbol(global);
- if (!sym.undf()) continue; // not an import, skip
- if (sym.n_desc == MachO.N_DEAD) continue;
-
- const new_index = @as(u32, @intCast(imports.items.len));
- var out_sym = sym;
- out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
- try imports.append(out_sym);
- try imports_table.putNoClobber(global, new_index);
- }
-
- // We generate stabs last in order to ensure that the strtab always has debug info
- // strings trailing
- if (!self.options.strip) {
- for (self.objects.items) |object| {
- try self.generateSymbolStabs(object, &locals);
- }
- }
-
- const nlocals = @as(u32, @intCast(locals.items.len));
- const nexports = @as(u32, @intCast(exports.items.len));
- const nimports = @as(u32, @intCast(imports.items.len));
- const nsyms = nlocals + nexports + nimports;
-
- const seg = self.getLinkeditSegmentPtr();
- const offset = seg.fileoff + seg.filesize;
- assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
- const needed_size = nsyms * @sizeOf(macho.nlist_64);
- seg.filesize = offset + needed_size - seg.fileoff;
- assert(mem.isAlignedGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64)));
-
- var buffer = std.ArrayList(u8).init(gpa);
- defer buffer.deinit();
- try buffer.ensureTotalCapacityPrecise(needed_size);
- buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
- buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
- buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
-
- log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
- try self.file.pwriteAll(buffer.items, offset);
-
- self.symtab_cmd.symoff = @as(u32, @intCast(offset));
- self.symtab_cmd.nsyms = nsyms;
-
- return SymtabCtx{
- .nlocalsym = nlocals,
- .nextdefsym = nexports,
- .nundefsym = nimports,
- .imports_table = imports_table,
- };
- }
-
- fn writeStrtab(self: *Zld) !void {
- const seg = self.getLinkeditSegmentPtr();
- const offset = seg.fileoff + seg.filesize;
- assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
- const needed_size = self.strtab.buffer.items.len;
- const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
- seg.filesize = offset + needed_size_aligned - seg.fileoff;
-
- log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
-
- const buffer = try self.gpa.alloc(u8, math.cast(usize, needed_size_aligned) orelse return error.Overflow);
- defer self.gpa.free(buffer);
- @memcpy(buffer[0..self.strtab.buffer.items.len], self.strtab.buffer.items);
- @memset(buffer[self.strtab.buffer.items.len..], 0);
-
- try self.file.pwriteAll(buffer, offset);
-
- self.symtab_cmd.stroff = @as(u32, @intCast(offset));
- self.symtab_cmd.strsize = @as(u32, @intCast(needed_size_aligned));
- }
-
- const SymtabCtx = struct {
- nlocalsym: u32,
- nextdefsym: u32,
- nundefsym: u32,
- imports_table: std.AutoHashMap(SymbolWithLoc, u32),
- };
-
- fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
- const gpa = self.gpa;
- const nstubs = @as(u32, @intCast(self.stubs_table.lookup.count()));
- const ngot_entries = @as(u32, @intCast(self.got_table.lookup.count()));
- const nindirectsyms = nstubs * 2 + ngot_entries;
- const iextdefsym = ctx.nlocalsym;
- const iundefsym = iextdefsym + ctx.nextdefsym;
-
- const seg = self.getLinkeditSegmentPtr();
- const offset = seg.fileoff + seg.filesize;
- assert(mem.isAlignedGeneric(u64, offset, @alignOf(u64)));
- const needed_size = nindirectsyms * @sizeOf(u32);
- const needed_size_aligned = mem.alignForward(u64, needed_size, @alignOf(u64));
- seg.filesize = offset + needed_size_aligned - seg.fileoff;
-
- log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size_aligned });
-
- var buf = std.ArrayList(u8).init(gpa);
- defer buf.deinit();
- try buf.ensureTotalCapacityPrecise(math.cast(usize, needed_size_aligned) orelse return error.Overflow);
- const writer = buf.writer();
-
- if (self.stubs_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.reserved1 = 0;
- for (self.stubs_table.entries.items) |entry| {
- if (!self.stubs_table.lookup.contains(entry)) continue;
- const target_sym = self.getSymbol(entry);
- assert(target_sym.undf());
- try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
- }
- }
-
- if (self.got_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.reserved1 = nstubs;
- for (self.got_table.entries.items) |entry| {
- if (!self.got_table.lookup.contains(entry)) continue;
- const target_sym = self.getSymbol(entry);
- if (target_sym.undf()) {
- try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
- } else {
- try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
- }
- }
- }
-
- if (self.la_symbol_ptr_section_index) |sect_id| {
- const header = &self.sections.items(.header)[sect_id];
- header.reserved1 = nstubs + ngot_entries;
- for (self.stubs_table.entries.items) |entry| {
- if (!self.stubs_table.lookup.contains(entry)) continue;
- const target_sym = self.getSymbol(entry);
- assert(target_sym.undf());
- try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry).?);
- }
- }
-
- const padding = math.cast(usize, needed_size_aligned - needed_size) orelse return error.Overflow;
- if (padding > 0) {
- buf.appendNTimesAssumeCapacity(0, padding);
- }
-
- assert(buf.items.len == needed_size_aligned);
- try self.file.pwriteAll(buf.items, offset);
-
- self.dysymtab_cmd.nlocalsym = ctx.nlocalsym;
- self.dysymtab_cmd.iextdefsym = iextdefsym;
- self.dysymtab_cmd.nextdefsym = ctx.nextdefsym;
- self.dysymtab_cmd.iundefsym = iundefsym;
- self.dysymtab_cmd.nundefsym = ctx.nundefsym;
- self.dysymtab_cmd.indirectsymoff = @as(u32, @intCast(offset));
- self.dysymtab_cmd.nindirectsyms = nindirectsyms;
- }
-
- fn writeUuid(self: *Zld, comp: *const Compilation, uuid_cmd_offset: u32, has_codesig: bool) !void {
- const file_size = if (!has_codesig) blk: {
- const seg = self.getLinkeditSegmentPtr();
- break :blk seg.fileoff + seg.filesize;
- } else self.codesig_cmd.dataoff;
- try calcUuid(comp, self.file, file_size, &self.uuid_cmd.uuid);
- const offset = uuid_cmd_offset + @sizeOf(macho.load_command);
- try self.file.pwriteAll(&self.uuid_cmd.uuid, offset);
- }
-
- fn writeCodeSignaturePadding(self: *Zld, code_sig: *CodeSignature) !void {
- const seg = self.getLinkeditSegmentPtr();
- // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
- // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
- const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16);
- const needed_size = code_sig.estimateSize(offset);
- seg.filesize = offset + needed_size - seg.fileoff;
- seg.vmsize = mem.alignForward(u64, seg.filesize, MachO.getPageSize(self.options.target.cpu.arch));
- log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
- // Pad out the space. We need to do this to calculate valid hashes for everything in the file
- // except for code signature data.
- try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
-
- self.codesig_cmd.dataoff = @as(u32, @intCast(offset));
- self.codesig_cmd.datasize = @as(u32, @intCast(needed_size));
- }
-
- fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
- const seg_id = self.header_segment_cmd_index.?;
- const seg = self.segments.items[seg_id];
-
- var buffer = std.ArrayList(u8).init(self.gpa);
- defer buffer.deinit();
- try buffer.ensureTotalCapacityPrecise(code_sig.size());
- try code_sig.writeAdhocSignature(comp, .{
- .file = self.file,
- .exec_seg_base = seg.fileoff,
- .exec_seg_limit = seg.filesize,
- .file_size = self.codesig_cmd.dataoff,
- .output_mode = self.options.output_mode,
- }, buffer.writer());
- assert(buffer.items.len == code_sig.size());
-
- log.debug("writing code signature from 0x{x} to 0x{x}", .{
- self.codesig_cmd.dataoff,
- self.codesig_cmd.dataoff + buffer.items.len,
- });
-
- try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
- }
-
- /// Writes Mach-O file header.
- fn writeHeader(self: *Zld, ncmds: u32, sizeofcmds: u32) !void {
- var header: macho.mach_header_64 = .{};
- header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
-
- switch (self.options.target.cpu.arch) {
- .aarch64 => {
- header.cputype = macho.CPU_TYPE_ARM64;
- header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
- },
- .x86_64 => {
- header.cputype = macho.CPU_TYPE_X86_64;
- header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
- },
- else => return error.UnsupportedCpuArchitecture,
- }
-
- switch (self.options.output_mode) {
- .Exe => {
- header.filetype = macho.MH_EXECUTE;
- },
- .Lib => {
- // By this point, it can only be a dylib.
- header.filetype = macho.MH_DYLIB;
- header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
- },
- else => unreachable,
- }
-
- if (self.thread_vars_section_index) |sect_id| {
- header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
- if (self.sections.items(.header)[sect_id].size > 0) {
- header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
- }
- }
-
- header.ncmds = ncmds;
- header.sizeofcmds = sizeofcmds;
-
- log.debug("writing Mach-O header {}", .{header});
-
- try self.file.pwriteAll(mem.asBytes(&header), 0);
- }
-
- pub fn makeStaticString(bytes: []const u8) [16]u8 {
- var buf = [_]u8{0} ** 16;
- @memcpy(buf[0..bytes.len], bytes);
- return buf;
- }
-
- pub fn getAtomPtr(self: *Zld, atom_index: Atom.Index) *Atom {
- assert(atom_index < self.atoms.items.len);
- return &self.atoms.items[atom_index];
- }
-
- pub fn getAtom(self: Zld, atom_index: Atom.Index) Atom {
- assert(atom_index < self.atoms.items.len);
- return self.atoms.items[atom_index];
- }
-
- fn getSegmentByName(self: Zld, segname: []const u8) ?u8 {
- for (self.segments.items, 0..) |seg, i| {
- if (mem.eql(u8, segname, seg.segName())) return @as(u8, @intCast(i));
- } else return null;
- }
-
- pub fn getSegment(self: Zld, sect_id: u8) macho.segment_command_64 {
- const index = self.sections.items(.segment_index)[sect_id];
- return self.segments.items[index];
- }
-
- pub fn getSegmentPtr(self: *Zld, sect_id: u8) *macho.segment_command_64 {
- const index = self.sections.items(.segment_index)[sect_id];
- return &self.segments.items[index];
- }
-
- pub fn getLinkeditSegmentPtr(self: *Zld) *macho.segment_command_64 {
- assert(self.segments.items.len > 0);
- const seg = &self.segments.items[self.segments.items.len - 1];
- assert(mem.eql(u8, seg.segName(), "__LINKEDIT"));
- return seg;
- }
-
- pub fn getSectionByName(self: Zld, segname: []const u8, sectname: []const u8) ?u8 {
- // TODO investigate caching with a hashmap
- for (self.sections.items(.header), 0..) |header, i| {
- if (mem.eql(u8, header.segName(), segname) and mem.eql(u8, header.sectName(), sectname))
- return @as(u8, @intCast(i));
- } else return null;
- }
-
- pub fn getSectionIndexes(self: Zld, segment_index: u8) struct { start: u8, end: u8 } {
- var start: u8 = 0;
- const nsects = for (self.segments.items, 0..) |seg, i| {
- if (i == segment_index) break @as(u8, @intCast(seg.nsects));
- start += @as(u8, @intCast(seg.nsects));
- } else 0;
- return .{ .start = start, .end = start + nsects };
- }
-
- pub fn symbolIsTemp(self: *Zld, sym_with_loc: SymbolWithLoc) bool {
- const sym = self.getSymbol(sym_with_loc);
- if (!sym.sect()) return false;
- if (sym.ext()) return false;
- const sym_name = self.getSymbolName(sym_with_loc);
- return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
- }
-
- /// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
- pub fn getSymbolPtr(self: *Zld, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
- if (sym_with_loc.getFile()) |file| {
- const object = &self.objects.items[file];
- return &object.symtab[sym_with_loc.sym_index];
- } else {
- return &self.locals.items[sym_with_loc.sym_index];
- }
- }
-
- /// Returns symbol described by `sym_with_loc` descriptor.
- pub fn getSymbol(self: *const Zld, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
- if (sym_with_loc.getFile()) |file| {
- const object = &self.objects.items[file];
- return object.symtab[sym_with_loc.sym_index];
- } else {
- return self.locals.items[sym_with_loc.sym_index];
- }
- }
-
- /// Returns name of the symbol described by `sym_with_loc` descriptor.
- pub fn getSymbolName(self: *const Zld, sym_with_loc: SymbolWithLoc) []const u8 {
- if (sym_with_loc.getFile()) |file| {
- const object = self.objects.items[file];
- return object.getSymbolName(sym_with_loc.sym_index);
- } else {
- const sym = self.locals.items[sym_with_loc.sym_index];
- return self.strtab.get(sym.n_strx).?;
- }
- }
-
- pub fn getGlobalIndex(self: *const Zld, name: []const u8) ?u32 {
- return self.resolver.get(name);
- }
-
- pub fn getGlobalPtr(self: *Zld, name: []const u8) ?*SymbolWithLoc {
- const global_index = self.resolver.get(name) orelse return null;
- return &self.globals.items[global_index];
- }
-
- pub fn getGlobal(self: *const Zld, name: []const u8) ?SymbolWithLoc {
- const global_index = self.resolver.get(name) orelse return null;
- return self.globals.items[global_index];
- }
-
- const GetOrPutGlobalPtrResult = struct {
- found_existing: bool,
- value_ptr: *SymbolWithLoc,
- };
-
- pub fn getOrPutGlobalPtr(self: *Zld, name: []const u8) !GetOrPutGlobalPtrResult {
- if (self.getGlobalPtr(name)) |ptr| {
- return GetOrPutGlobalPtrResult{ .found_existing = true, .value_ptr = ptr };
- }
- const global_index = try self.allocateGlobal();
- const global_name = try self.gpa.dupe(u8, name);
- _ = try self.resolver.put(self.gpa, global_name, global_index);
- const ptr = &self.globals.items[global_index];
- return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
- }
-
- pub fn getGotEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
- const index = self.got_table.lookup.get(sym_with_loc) orelse return null;
- const header = self.sections.items(.header)[self.got_section_index.?];
- return header.addr + @sizeOf(u64) * index;
- }
-
- pub fn getTlvPtrEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
- const index = self.tlv_ptr_table.lookup.get(sym_with_loc) orelse return null;
- const header = self.sections.items(.header)[self.tlv_ptr_section_index.?];
- return header.addr + @sizeOf(u64) * index;
- }
-
- pub fn getStubsEntryAddress(self: *Zld, sym_with_loc: SymbolWithLoc) ?u64 {
- const index = self.stubs_table.lookup.get(sym_with_loc) orelse return null;
- const header = self.sections.items(.header)[self.stubs_section_index.?];
- return header.addr + stubs.stubSize(self.options.target.cpu.arch) * index;
- }
-
- /// Returns symbol location corresponding to the set entrypoint.
- /// Asserts output mode is executable.
- pub fn getEntryPoint(self: Zld) SymbolWithLoc {
- assert(self.options.output_mode == .Exe);
- const global_index = self.entry_index.?;
- return self.globals.items[global_index];
- }
-
- inline fn requiresThunks(self: Zld) bool {
- return self.options.target.cpu.arch == .aarch64;
- }
-
- pub fn generateSymbolStabs(self: *Zld, object: Object, locals: *std.ArrayList(macho.nlist_64)) !void {
- log.debug("generating stabs for '{s}'", .{object.name});
-
- const gpa = self.gpa;
- var debug_info = object.parseDwarfInfo();
-
- var lookup = DwarfInfo.AbbrevLookupTable.init(gpa);
- defer lookup.deinit();
- try lookup.ensureUnusedCapacity(std.math.maxInt(u8));
-
- // We assume there is only one CU.
- var cu_it = debug_info.getCompileUnitIterator();
- const compile_unit = while (try cu_it.next()) |cu| {
- const offset = math.cast(usize, cu.cuh.debug_abbrev_offset) orelse return error.Overflow;
- try debug_info.genAbbrevLookupByKind(offset, &lookup);
- break cu;
- } else {
- log.debug("no compile unit found in debug info in {s}; skipping", .{object.name});
- return;
- };
-
- var abbrev_it = compile_unit.getAbbrevEntryIterator(debug_info);
- const cu_entry: DwarfInfo.AbbrevEntry = while (try abbrev_it.next(lookup)) |entry| switch (entry.tag) {
- dwarf.TAG.compile_unit => break entry,
- else => continue,
- } else {
- log.debug("missing DWARF_TAG_compile_unit tag in {s}; skipping", .{object.name});
- return;
- };
-
- var maybe_tu_name: ?[]const u8 = null;
- var maybe_tu_comp_dir: ?[]const u8 = null;
- var attr_it = cu_entry.getAttributeIterator(debug_info, compile_unit.cuh);
-
- while (try attr_it.next()) |attr| switch (attr.name) {
- dwarf.AT.comp_dir => maybe_tu_comp_dir = attr.getString(debug_info, compile_unit.cuh) orelse continue,
- dwarf.AT.name => maybe_tu_name = attr.getString(debug_info, compile_unit.cuh) orelse continue,
- else => continue,
- };
-
- if (maybe_tu_name == null or maybe_tu_comp_dir == null) {
- log.debug("missing DWARF_AT_comp_dir and DWARF_AT_name attributes {s}; skipping", .{object.name});
- return;
- }
-
- const tu_name = maybe_tu_name.?;
- const tu_comp_dir = maybe_tu_comp_dir.?;
-
- // Open scope
- try locals.ensureUnusedCapacity(3);
- locals.appendAssumeCapacity(.{
- .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
- .n_type = macho.N_SO,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- });
- locals.appendAssumeCapacity(.{
- .n_strx = try self.strtab.insert(gpa, tu_name),
- .n_type = macho.N_SO,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- });
- locals.appendAssumeCapacity(.{
- .n_strx = try self.strtab.insert(gpa, object.name),
- .n_type = macho.N_OSO,
- .n_sect = 0,
- .n_desc = 1,
- .n_value = object.mtime,
- });
-
- var stabs_buf: [4]macho.nlist_64 = undefined;
-
- var name_lookup: ?DwarfInfo.SubprogramLookupByName = if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS == 0) blk: {
- var name_lookup = DwarfInfo.SubprogramLookupByName.init(gpa);
- errdefer name_lookup.deinit();
- try name_lookup.ensureUnusedCapacity(@as(u32, @intCast(object.atoms.items.len)));
- try debug_info.genSubprogramLookupByName(compile_unit, lookup, &name_lookup);
- break :blk name_lookup;
- } else null;
- defer if (name_lookup) |*nl| nl.deinit();
-
- for (object.atoms.items) |atom_index| {
- const atom = self.getAtom(atom_index);
- const stabs = try self.generateSymbolStabsForSymbol(
- atom_index,
- atom.getSymbolWithLoc(),
- name_lookup,
- &stabs_buf,
- );
- try locals.appendSlice(stabs);
-
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |sym_loc| {
- const contained_stabs = try self.generateSymbolStabsForSymbol(
- atom_index,
- sym_loc,
- name_lookup,
- &stabs_buf,
- );
- try locals.appendSlice(contained_stabs);
- }
- }
-
- // Close scope
- try locals.append(.{
- .n_strx = 0,
- .n_type = macho.N_SO,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = 0,
- });
- }
-
- fn generateSymbolStabsForSymbol(
- self: *Zld,
- atom_index: Atom.Index,
- sym_loc: SymbolWithLoc,
- lookup: ?DwarfInfo.SubprogramLookupByName,
- buf: *[4]macho.nlist_64,
- ) ![]const macho.nlist_64 {
- const gpa = self.gpa;
- const object = self.objects.items[sym_loc.getFile().?];
- const sym = self.getSymbol(sym_loc);
- const sym_name = self.getSymbolName(sym_loc);
- const header = self.sections.items(.header)[sym.n_sect - 1];
-
- if (sym.n_strx == 0) return buf[0..0];
- if (self.symbolIsTemp(sym_loc)) return buf[0..0];
-
- if (!header.isCode()) {
- // Since we are not dealing with machine code, it's either a global or a static depending
- // on the linkage scope.
- if (sym.sect() and sym.ext()) {
- // Global gets an N_GSYM stab type.
- buf[0] = .{
- .n_strx = try self.strtab.insert(gpa, sym_name),
- .n_type = macho.N_GSYM,
- .n_sect = sym.n_sect,
- .n_desc = 0,
- .n_value = 0,
- };
- } else {
- // Local static gets an N_STSYM stab type.
- buf[0] = .{
- .n_strx = try self.strtab.insert(gpa, sym_name),
- .n_type = macho.N_STSYM,
- .n_sect = sym.n_sect,
- .n_desc = 0,
- .n_value = sym.n_value,
- };
- }
- return buf[0..1];
- }
-
- const size: u64 = size: {
- if (object.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0) {
- break :size self.getAtom(atom_index).size;
- }
-
- // Since we don't have subsections to work with, we need to infer the size of each function
- // the slow way by scanning the debug info for matching symbol names and extracting
- // the symbol's DWARF_AT_low_pc and DWARF_AT_high_pc values.
- const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
- const subprogram = lookup.?.get(sym_name[1..]) orelse return buf[0..0];
-
- if (subprogram.addr <= source_sym.n_value and source_sym.n_value < subprogram.addr + subprogram.size) {
- break :size subprogram.size;
- } else {
- log.debug("no stab found for {s}", .{sym_name});
- return buf[0..0];
- }
- };
-
- buf[0] = .{
- .n_strx = 0,
- .n_type = macho.N_BNSYM,
- .n_sect = sym.n_sect,
- .n_desc = 0,
- .n_value = sym.n_value,
- };
- buf[1] = .{
- .n_strx = try self.strtab.insert(gpa, sym_name),
- .n_type = macho.N_FUN,
- .n_sect = sym.n_sect,
- .n_desc = 0,
- .n_value = sym.n_value,
- };
- buf[2] = .{
- .n_strx = 0,
- .n_type = macho.N_FUN,
- .n_sect = 0,
- .n_desc = 0,
- .n_value = size,
- };
- buf[3] = .{
- .n_strx = 0,
- .n_type = macho.N_ENSYM,
- .n_sect = sym.n_sect,
- .n_desc = 0,
- .n_value = size,
- };
-
- return buf;
- }
-
- fn logSegments(self: *Zld) void {
- log.debug("segments:", .{});
- for (self.segments.items, 0..) |segment, i| {
- log.debug(" segment({d}): {s} @{x} ({x}), sizeof({x})", .{
- i,
- segment.segName(),
- segment.fileoff,
- segment.vmaddr,
- segment.vmsize,
- });
- }
- }
-
- fn logSections(self: *Zld) void {
- log.debug("sections:", .{});
- for (self.sections.items(.header), 0..) |header, i| {
- log.debug(" sect({d}): {s},{s} @{x} ({x}), sizeof({x})", .{
- i + 1,
- header.segName(),
- header.sectName(),
- header.offset,
- header.addr,
- header.size,
- });
- }
- }
-
- fn logSymAttributes(sym: macho.nlist_64, buf: []u8) []const u8 {
- if (sym.sect()) {
- buf[0] = 's';
- }
- if (sym.ext()) {
- if (sym.weakDef() or sym.pext()) {
- buf[1] = 'w';
- } else {
- buf[1] = 'e';
- }
- }
- if (sym.tentative()) {
- buf[2] = 't';
- }
- if (sym.undf()) {
- buf[3] = 'u';
- }
- return buf[0..];
- }
-
- fn logSymtab(self: *Zld) void {
- var buf: [4]u8 = undefined;
-
- const scoped_log = std.log.scoped(.symtab);
-
- scoped_log.debug("locals:", .{});
- for (self.objects.items, 0..) |object, id| {
- scoped_log.debug(" object({d}): {s}", .{ id, object.name });
- if (object.in_symtab == null) continue;
- for (object.symtab, 0..) |sym, sym_id| {
- @memset(&buf, '_');
- scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
- sym_id,
- object.getSymbolName(@as(u32, @intCast(sym_id))),
- sym.n_value,
- sym.n_sect,
- logSymAttributes(sym, &buf),
- });
- }
- }
- scoped_log.debug(" object(-1)", .{});
- for (self.locals.items, 0..) |sym, sym_id| {
- if (sym.undf()) continue;
- scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s}", .{
- sym_id,
- self.strtab.get(sym.n_strx).?,
- sym.n_value,
- sym.n_sect,
- logSymAttributes(sym, &buf),
- });
- }
-
- scoped_log.debug("exports:", .{});
- for (self.globals.items, 0..) |global, i| {
- const sym = self.getSymbol(global);
- if (sym.undf()) continue;
- if (sym.n_desc == MachO.N_DEAD) continue;
- scoped_log.debug(" %{d}: {s} @{x} in sect({d}), {s} (def in object({?}))", .{
- i,
- self.getSymbolName(global),
- sym.n_value,
- sym.n_sect,
- logSymAttributes(sym, &buf),
- global.file,
- });
- }
-
- scoped_log.debug("imports:", .{});
- for (self.globals.items, 0..) |global, i| {
- const sym = self.getSymbol(global);
- if (!sym.undf()) continue;
- if (sym.n_desc == MachO.N_DEAD) continue;
- const ord = @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER);
- scoped_log.debug(" %{d}: {s} @{x} in ord({d}), {s}", .{
- i,
- self.getSymbolName(global),
- sym.n_value,
- ord,
- logSymAttributes(sym, &buf),
- });
- }
-
- scoped_log.debug("GOT entries:", .{});
- scoped_log.debug("{}", .{self.got_table});
-
- scoped_log.debug("TLV pointers:", .{});
- scoped_log.debug("{}", .{self.tlv_ptr_table});
-
- scoped_log.debug("stubs entries:", .{});
- scoped_log.debug("{}", .{self.stubs_table});
-
- scoped_log.debug("thunks:", .{});
- for (self.thunks.items, 0..) |thunk, i| {
- scoped_log.debug(" thunk({d})", .{i});
- const slice = thunk.targets.slice();
- for (slice.items(.tag), slice.items(.target), 0..) |tag, target, j| {
- const atom_index = @as(u32, @intCast(thunk.getStartAtomIndex() + j));
- const atom = self.getAtom(atom_index);
- const atom_sym = self.getSymbol(atom.getSymbolWithLoc());
- const target_addr = switch (tag) {
- .stub => self.getStubsEntryAddress(target).?,
- .atom => self.getSymbol(target).n_value,
- };
- scoped_log.debug(" {d}@{x} => {s}({s}@{x})", .{
- j,
- atom_sym.n_value,
- @tagName(tag),
- self.getSymbolName(target),
- target_addr,
- });
- }
- }
- }
-
- fn logAtoms(self: *Zld) void {
- log.debug("atoms:", .{});
- const slice = self.sections.slice();
- for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
- var atom_index = first_atom_index orelse continue;
- const header = slice.items(.header)[sect_id];
-
- log.debug("{s},{s}", .{ header.segName(), header.sectName() });
-
- while (true) {
- const atom = self.getAtom(atom_index);
- self.logAtom(atom_index, log);
-
- if (atom.next_index) |next_index| {
- atom_index = next_index;
- } else break;
- }
- }
- }
-
- pub fn logAtom(self: *Zld, atom_index: Atom.Index, logger: anytype) void {
- if (!build_options.enable_logging) return;
-
- const atom = self.getAtom(atom_index);
- const sym = self.getSymbol(atom.getSymbolWithLoc());
- const sym_name = self.getSymbolName(atom.getSymbolWithLoc());
- logger.debug(" ATOM({d}, %{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?}) in sect({d})", .{
- atom_index,
- atom.sym_index,
- sym_name,
- sym.n_value,
- atom.size,
- atom.alignment,
- atom.getFile(),
- sym.n_sect,
- });
-
- if (atom.getFile() != null) {
- var it = Atom.getInnerSymbolsIterator(self, atom_index);
- while (it.next()) |sym_loc| {
- const inner = self.getSymbol(sym_loc);
- const inner_name = self.getSymbolName(sym_loc);
- const offset = Atom.calcInnerSymbolOffset(self, atom_index, sym_loc.sym_index);
-
- logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
- sym_loc.sym_index,
- inner_name,
- inner.n_value,
- offset,
- });
- }
-
- if (Atom.getSectionAlias(self, atom_index)) |sym_loc| {
- const alias = self.getSymbol(sym_loc);
- const alias_name = self.getSymbolName(sym_loc);
-
- logger.debug(" (%{d}, '{s}') @ {x} ({x})", .{
- sym_loc.sym_index,
- alias_name,
- alias.n_value,
- 0,
- });
- }
- }
- }
-};
-
-pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
+pub fn linkWithZld(
+ macho_file: *MachO,
+ comp: *Compilation,
+ prog_node: *std.Progress.Node,
+) link.File.FlushError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -2611,8 +35,6 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
defer sub_prog_node.end();
const cpu_arch = target.cpu.arch;
- const os_tag = target.os.tag;
- const abi = target.abi;
const is_lib = options.output_mode == .Lib;
const is_dyn_lib = options.link_mode == .Dynamic and is_lib;
const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe;
@@ -2730,29 +152,26 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
} else {
const sub_path = options.emit.?.sub_path;
+ const old_file = macho_file.base.file; // TODO is this needed at all?
+ defer macho_file.base.file = old_file;
+
const file = try directory.handle.createFile(sub_path, .{
.truncate = true,
.read = true,
.mode = link.determineMode(options.*),
});
defer file.close();
-
- var zld = Zld{
- .gpa = gpa,
- .file = file,
- .options = options,
- };
- defer zld.deinit();
+ macho_file.base.file = file;
// Index 0 is always a null symbol.
- try zld.locals.append(gpa, .{
+ try macho_file.locals.append(gpa, .{
.n_strx = 0,
.n_type = 0,
.n_sect = 0,
.n_desc = 0,
.n_value = 0,
});
- try zld.strtab.buffer.append(gpa, 0);
+ try macho_file.strtab.buffer.append(gpa, 0);
// Positional arguments to the linker such as object files and static archives.
var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
@@ -2930,9 +349,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
const in_file = try std.fs.cwd().openFile(obj.path, .{});
defer in_file.close();
- MachO.parsePositional(
- &zld,
- gpa,
+ macho_file.parsePositional(
in_file,
obj.path,
obj.must_link,
@@ -2949,9 +366,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
const in_file = try std.fs.cwd().openFile(path, .{});
defer in_file.close();
- MachO.parseLibrary(
- &zld,
- gpa,
+ macho_file.parseLibrary(
in_file,
path,
lib,
@@ -2965,198 +380,199 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
};
}
- MachO.parseDependentLibs(&zld, gpa, &dependent_libs, options) catch |err| {
+ macho_file.parseDependentLibs(&dependent_libs, options) catch |err| {
// TODO convert to error
log.err("parsing dependent libraries failed with err {s}", .{@errorName(err)});
};
- try zld.resolveSymbols();
- try macho_file.reportUndefined(&zld);
-
- if (options.output_mode == .Exe) {
- const entry_name = options.entry orelse load_commands.default_entry_point;
- const global_index = zld.resolver.get(entry_name).?; // Error was flagged earlier
- zld.entry_index = global_index;
- }
+ var actions = std.ArrayList(MachO.ResolveAction).init(gpa);
+ defer actions.deinit();
+ try macho_file.resolveSymbols(&actions);
+ try macho_file.reportUndefined();
- for (zld.objects.items, 0..) |*object, object_id| {
- try object.splitIntoAtoms(&zld, @as(u32, @intCast(object_id)));
+ for (macho_file.objects.items, 0..) |*object, object_id| {
+ try object.splitIntoAtoms(macho_file, @as(u32, @intCast(object_id)));
}
if (gc_sections) {
- try dead_strip.gcAtoms(&zld);
+ try dead_strip.gcAtoms(macho_file);
}
- try zld.createDyldPrivateAtom();
- try zld.createTentativeDefAtoms();
+ try macho_file.createDyldPrivateAtom();
+ try macho_file.createTentativeDefAtoms();
- if (zld.options.output_mode == .Exe) {
- const global = zld.getEntryPoint();
- if (zld.getSymbol(global).undf()) {
+ if (macho_file.options.output_mode == .Exe) {
+ const global = macho_file.getEntryPoint().?;
+ if (macho_file.getSymbol(global).undf()) {
// We do one additional check here in case the entry point was found in one of the dylibs.
// (I actually have no idea what this would imply but it is a possible outcome and so we
// support it.)
- try zld.addStubEntry(global);
+ try macho_file.addStubEntry(global);
}
}
- for (zld.objects.items) |object| {
+ for (macho_file.objects.items) |object| {
for (object.atoms.items) |atom_index| {
- const atom = zld.getAtom(atom_index);
- const sym = zld.getSymbol(atom.getSymbolWithLoc());
- const header = zld.sections.items(.header)[sym.n_sect - 1];
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const header = macho_file.sections.items(.header)[sym.n_sect - 1];
if (header.isZerofill()) continue;
- const relocs = Atom.getAtomRelocs(&zld, atom_index);
- try Atom.scanAtomRelocs(&zld, atom_index, relocs);
+ const relocs = Atom.getAtomRelocs(macho_file, atom_index);
+ try Atom.scanAtomRelocs(macho_file, atom_index, relocs);
}
}
- try eh_frame.scanRelocs(&zld);
- try UnwindInfo.scanRelocs(&zld);
+ try eh_frame.scanRelocs(macho_file);
+ try UnwindInfo.scanRelocs(macho_file);
- if (zld.dyld_stub_binder_index) |index| try zld.addGotEntry(zld.globals.items[index]);
+ if (macho_file.dyld_stub_binder_index) |index|
+ try macho_file.addGotEntry(macho_file.globals.items[index]);
- try zld.calcSectionSizes();
+ try macho_file.calcSectionSizes();
- var unwind_info = UnwindInfo{ .gpa = zld.gpa };
+ var unwind_info = UnwindInfo{ .gpa = gpa };
defer unwind_info.deinit();
- try unwind_info.collect(&zld);
+ try unwind_info.collect(macho_file);
- try eh_frame.calcSectionSize(&zld, &unwind_info);
- try unwind_info.calcSectionSize(&zld);
+ try eh_frame.calcSectionSize(macho_file, &unwind_info);
+ try unwind_info.calcSectionSize(macho_file);
- try zld.pruneAndSortSections();
- try zld.createSegments();
- try zld.allocateSegments();
+ try pruneAndSortSections(macho_file);
+ try createSegments(macho_file);
+ try allocateSegments(macho_file);
- try MachO.allocateSpecialSymbols(&zld);
+ try macho_file.allocateSpecialSymbols();
if (build_options.enable_logging) {
- zld.logSymtab();
- zld.logSegments();
- zld.logSections();
- zld.logAtoms();
+ macho_file.logSymtab();
+ macho_file.logSegments();
+ macho_file.logSections();
+ macho_file.logAtoms();
}
- try zld.writeAtoms();
- if (zld.requiresThunks()) try zld.writeThunks();
- try zld.writeDyldPrivateAtom();
+ try writeAtoms(macho_file);
+ if (macho_file.requiresThunks()) try writeThunks(macho_file);
+ try writeDyldPrivateAtom(macho_file);
- if (zld.stubs_section_index) |_| {
- try zld.writeStubs();
- try zld.writeStubHelpers();
- try zld.writeLaSymbolPtrs();
+ if (macho_file.stubs_section_index) |_| {
+ try writeStubs(macho_file);
+ try writeStubHelpers(macho_file);
+ try writeLaSymbolPtrs(macho_file);
}
- if (zld.got_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.got_table);
- if (zld.tlv_ptr_section_index) |sect_id| try zld.writePointerEntries(sect_id, &zld.tlv_ptr_table);
+ if (macho_file.got_section_index) |sect_id|
+ try macho_file.writePointerEntries(sect_id, &macho_file.got_table);
+ if (macho_file.tlv_ptr_section_index) |sect_id|
+ try macho_file.writePointerEntries(sect_id, &macho_file.tlv_ptr_table);
- try eh_frame.write(&zld, &unwind_info);
- try unwind_info.write(&zld);
- try zld.writeLinkeditSegmentData();
+ try eh_frame.write(macho_file, &unwind_info);
+ try unwind_info.write(macho_file);
+ try macho_file.writeLinkeditSegmentData();
// If the last section of __DATA segment is zerofill section, we need to ensure
// that the free space between the end of the last non-zerofill section of __DATA
// segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
// copy-paste this space into memory for quicker zerofill operation.
- if (zld.data_segment_cmd_index) |data_seg_id| blk: {
+ if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
var physical_zerofill_start: ?u64 = null;
- const section_indexes = zld.getSectionIndexes(data_seg_id);
- for (zld.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
+ const section_indexes = macho_file.getSectionIndexes(data_seg_id);
+ for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
if (header.isZerofill() and header.size > 0) break;
physical_zerofill_start = header.offset + header.size;
} else break :blk;
const start = physical_zerofill_start orelse break :blk;
- const linkedit = zld.getLinkeditSegmentPtr();
+ const linkedit = macho_file.getLinkeditSegmentPtr();
const size = math.cast(usize, linkedit.fileoff - start) orelse return error.Overflow;
if (size > 0) {
log.debug("zeroing out zerofill area of length {x} at {x}", .{ size, start });
- var padding = try zld.gpa.alloc(u8, size);
- defer zld.gpa.free(padding);
+ var padding = try gpa.alloc(u8, size);
+ defer gpa.free(padding);
@memset(padding, 0);
- try zld.file.pwriteAll(padding, start);
+ try macho_file.base.file.?.pwriteAll(padding, start);
}
}
// Write code signature padding if required
- const requires_codesig = blk: {
- if (options.entitlements) |_| break :blk true;
- if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
- break :blk false;
- };
- var codesig: ?CodeSignature = if (requires_codesig) blk: {
+ var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: {
// Preallocate space for the code signature.
// We need to do this at this stage so that we have the load commands with proper values
// written out to the file.
// The most important here is to have the correct vm and filesize of the __LINKEDIT segment
// where the code signature goes into.
- var codesig = CodeSignature.init(MachO.getPageSize(zld.options.target.cpu.arch));
+ var codesig = CodeSignature.init(MachO.getPageSize(cpu_arch));
codesig.code_directory.ident = fs.path.basename(full_out_path);
if (options.entitlements) |path| {
- try codesig.addEntitlements(zld.gpa, path);
+ try codesig.addEntitlements(gpa, path);
}
- try zld.writeCodeSignaturePadding(&codesig);
+ try macho_file.writeCodeSignaturePadding(&codesig);
break :blk codesig;
} else null;
- defer if (codesig) |*csig| csig.deinit(zld.gpa);
+ defer if (codesig) |*csig| csig.deinit(gpa);
// Write load commands
var lc_buffer = std.ArrayList(u8).init(arena);
const lc_writer = lc_buffer.writer();
- try MachO.writeSegmentHeaders(&zld, lc_writer);
- try lc_writer.writeStruct(zld.dyld_info_cmd);
- try lc_writer.writeStruct(zld.function_starts_cmd);
- try lc_writer.writeStruct(zld.data_in_code_cmd);
- try lc_writer.writeStruct(zld.symtab_cmd);
- try lc_writer.writeStruct(zld.dysymtab_cmd);
+ try macho_file.writeSegmentHeaders(lc_writer);
+ try lc_writer.writeStruct(macho_file.dyld_info_cmd);
+ try lc_writer.writeStruct(macho_file.function_starts_cmd);
+ try lc_writer.writeStruct(macho_file.data_in_code_cmd);
+ try lc_writer.writeStruct(macho_file.symtab_cmd);
+ try lc_writer.writeStruct(macho_file.dysymtab_cmd);
try load_commands.writeDylinkerLC(lc_writer);
- if (zld.options.output_mode == .Exe) {
- const seg_id = zld.header_segment_cmd_index.?;
- const seg = zld.segments.items[seg_id];
- const global = zld.getEntryPoint();
- const sym = zld.getSymbol(global);
-
- const addr: u64 = if (sym.undf())
- // In this case, the symbol has been resolved in one of dylibs and so we point
- // to the stub as its vmaddr value.
- zld.getStubsEntryAddress(global).?
- else
- sym.n_value;
-
- try lc_writer.writeStruct(macho.entry_point_command{
- .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
- .stacksize = options.stack_size_override orelse 0,
- });
- } else {
- assert(zld.options.output_mode == .Lib);
- try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
+ switch (macho_file.base.options.output_mode) {
+ .Exe => blk: {
+ const seg_id = macho_file.header_segment_cmd_index.?;
+ const seg = macho_file.segments.items[seg_id];
+ const global = macho_file.getEntryPoint() orelse break :blk;
+ const sym = macho_file.getSymbol(global);
+
+ const addr: u64 = if (sym.undf())
+ // In this case, the symbol has been resolved in one of dylibs and so we point
+ // to the stub as its vmaddr value.
+ macho_file.getStubsEntryAddress(global).?
+ else
+ sym.n_value;
+
+ try lc_writer.writeStruct(macho.entry_point_command{
+ .entryoff = @as(u32, @intCast(addr - seg.vmaddr)),
+ .stacksize = macho_file.base.options.stack_size_override orelse 0,
+ });
+ },
+ .Lib => if (macho_file.base.options.link_mode == .Dynamic) {
+ try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer);
+ },
+ else => {},
}
- try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);
+ try load_commands.writeRpathLCs(gpa, macho_file.base.options, lc_writer);
try lc_writer.writeStruct(macho.source_version_command{
.version = 0,
});
- try load_commands.writeBuildVersionLC(zld.options, lc_writer);
+ try load_commands.writeBuildVersionLC(macho_file.base.options, lc_writer);
const uuid_cmd_offset = @sizeOf(macho.mach_header_64) + @as(u32, @intCast(lc_buffer.items.len));
- try lc_writer.writeStruct(zld.uuid_cmd);
+ try lc_writer.writeStruct(macho_file.uuid_cmd);
- try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
+ try load_commands.writeLoadDylibLCs(
+ macho_file.dylibs.items,
+ macho_file.referenced_dylibs.keys(),
+ lc_writer,
+ );
- if (requires_codesig) {
- try lc_writer.writeStruct(zld.codesig_cmd);
+ if (codesig != null) {
+ try lc_writer.writeStruct(macho_file.codesig_cmd);
}
const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
- try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
- try zld.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
- try zld.writeUuid(comp, uuid_cmd_offset, requires_codesig);
+ try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
+ try macho_file.writeHeader(ncmds, @as(u32, @intCast(lc_buffer.items.len)));
+ try macho_file.writeUuid(comp, uuid_cmd_offset, codesig != null);
if (codesig) |*csig| {
- try zld.writeCodeSignature(comp, csig); // code signing always comes last
- try MachO.invalidateKernelCache(directory.handle, zld.options.emit.?.sub_path);
+ try macho_file.writeCodeSignature(comp, csig); // code signing always comes last
+ try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path);
}
}
@@ -3177,3 +593,609 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
macho_file.base.lock = man.toOwnedLock();
}
}
+
+fn createSegments(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize;
+ const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
+ const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size);
+ if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) {
+ if (aligned_pagezero_vmsize != pagezero_vmsize) {
+ log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
+ log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
+ }
+ macho_file.pagezero_segment_cmd_index = @intCast(macho_file.segments.items.len);
+ try macho_file.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = MachO.makeStaticString("__PAGEZERO"),
+ .vmsize = aligned_pagezero_vmsize,
+ });
+ }
+
+ // __TEXT segment is non-optional
+ {
+ const protection = MachO.getSegmentMemoryProtection("__TEXT");
+ macho_file.text_segment_cmd_index = @intCast(macho_file.segments.items.len);
+ macho_file.header_segment_cmd_index = macho_file.text_segment_cmd_index.?;
+ try macho_file.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = MachO.makeStaticString("__TEXT"),
+ .maxprot = protection,
+ .initprot = protection,
+ });
+ }
+
+ for (macho_file.sections.items(.header), 0..) |header, sect_id| {
+ if (header.size == 0) continue; // empty section
+
+ const segname = header.segName();
+ const segment_id = macho_file.getSegmentByName(segname) orelse blk: {
+ log.debug("creating segment '{s}'", .{segname});
+ const segment_id = @as(u8, @intCast(macho_file.segments.items.len));
+ const protection = MachO.getSegmentMemoryProtection(segname);
+ try macho_file.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = MachO.makeStaticString(segname),
+ .maxprot = protection,
+ .initprot = protection,
+ });
+ break :blk segment_id;
+ };
+ const segment = &macho_file.segments.items[segment_id];
+ segment.cmdsize += @sizeOf(macho.section_64);
+ segment.nsects += 1;
+ macho_file.sections.items(.segment_index)[sect_id] = segment_id;
+ }
+
+ if (macho_file.getSegmentByName("__DATA_CONST")) |index| {
+ macho_file.data_const_segment_cmd_index = index;
+ }
+
+ if (macho_file.getSegmentByName("__DATA")) |index| {
+ macho_file.data_segment_cmd_index = index;
+ }
+
+ // __LINKEDIT always comes last
+ {
+ const protection = MachO.getSegmentMemoryProtection("__LINKEDIT");
+ macho_file.linkedit_segment_cmd_index = @intCast(macho_file.segments.items.len);
+ try macho_file.segments.append(gpa, .{
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ .segname = MachO.makeStaticString("__LINKEDIT"),
+ .maxprot = protection,
+ .initprot = protection,
+ });
+ }
+}
+
+fn writeAtoms(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ const slice = macho_file.sections.slice();
+
+ for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| {
+ const header = slice.items(.header)[sect_id];
+ if (header.isZerofill()) continue;
+
+ var atom_index = first_atom_index orelse continue;
+
+ var buffer = try gpa.alloc(u8, math.cast(usize, header.size) orelse return error.Overflow);
+ defer gpa.free(buffer);
+ @memset(buffer, 0); // TODO with NOPs
+
+ log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
+
+ while (true) {
+ const atom = macho_file.getAtom(atom_index);
+ if (atom.getFile()) |file| {
+ const this_sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const padding_size: usize = if (atom.next_index) |next_index| blk: {
+ const next_sym = macho_file.getSymbol(macho_file.getAtom(next_index).getSymbolWithLoc());
+ const size = next_sym.n_value - (this_sym.n_value + atom.size);
+ break :blk math.cast(usize, size) orelse return error.Overflow;
+ } else 0;
+
+ log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
+ atom.sym_index,
+ macho_file.getSymbolName(atom.getSymbolWithLoc()),
+ file,
+ });
+ if (padding_size > 0) {
+ log.debug(" (with padding {x})", .{padding_size});
+ }
+
+ const offset = this_sym.n_value - header.addr;
+ log.debug(" (at offset 0x{x})", .{offset});
+
+ const code = Atom.getAtomCode(macho_file, atom_index);
+ const relocs = Atom.getAtomRelocs(macho_file, atom_index);
+ const size = math.cast(usize, atom.size) orelse return error.Overflow;
+ @memcpy(buffer[offset .. offset + size], code);
+ try Atom.resolveRelocs(
+ macho_file,
+ atom_index,
+ buffer[offset..][0..size],
+ relocs,
+ );
+ }
+
+ if (atom.next_index) |next_index| {
+ atom_index = next_index;
+ } else break;
+ }
+
+ log.debug(" (writing at file offset 0x{x})", .{header.offset});
+ try macho_file.base.file.?.pwriteAll(buffer, header.offset);
+ }
+}
+
+fn writeDyldPrivateAtom(macho_file: *MachO) !void {
+ const atom_index = macho_file.dyld_private_atom_index orelse return;
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ const sect_id = macho_file.data_section_index.?;
+ const header = macho_file.sections.items(.header)[sect_id];
+ const offset = sym.n_value - header.addr + header.offset;
+ log.debug("writing __dyld_private at offset 0x{x}", .{offset});
+ const buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
+ try macho_file.base.file.?.pwriteAll(&buffer, offset);
+}
+
+fn writeThunks(macho_file: *MachO) !void {
+ assert(macho_file.requiresThunks());
+ const gpa = macho_file.base.allocator;
+
+ const sect_id = macho_file.text_section_index orelse return;
+ const header = macho_file.sections.items(.header)[sect_id];
+
+ for (macho_file.thunks.items, 0..) |*thunk, i| {
+ if (thunk.getSize() == 0) continue;
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, thunk.getSize());
+ defer buffer.deinit();
+ try thunks.writeThunkCode(macho_file, thunk, buffer.writer());
+ const thunk_atom = macho_file.getAtom(thunk.getStartAtomIndex());
+ const thunk_sym = macho_file.getSymbol(thunk_atom.getSymbolWithLoc());
+ const offset = thunk_sym.n_value - header.addr + header.offset;
+ log.debug("writing thunk({d}) at offset 0x{x}", .{ i, offset });
+ try macho_file.base.file.?.pwriteAll(buffer.items, offset);
+ }
+}
+
+fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void {
+ const gpa = macho_file.base.allocator;
+ const header = macho_file.sections.items(.header)[sect_id];
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, header.size);
+ defer buffer.deinit();
+ for (table.entries.items) |entry| {
+ const sym = macho_file.getSymbol(entry);
+ buffer.writer().writeIntLittle(u64, sym.n_value) catch unreachable;
+ }
+ log.debug("writing __DATA_CONST,__got contents at file offset 0x{x}", .{header.offset});
+ try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
+}
+
+fn writeStubs(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?];
+ const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
+
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, stubs_header.size);
+ defer buffer.deinit();
+
+ for (0..macho_file.stub_table.count()) |index| {
+ try stubs.writeStubCode(.{
+ .cpu_arch = cpu_arch,
+ .source_addr = stubs_header.addr + stubs.stubSize(cpu_arch) * index,
+ .target_addr = la_symbol_ptr_header.addr + index * @sizeOf(u64),
+ }, buffer.writer());
+ }
+
+ log.debug("writing __TEXT,__stubs contents at file offset 0x{x}", .{stubs_header.offset});
+ try macho_file.base.file.?.pwriteAll(buffer.items, stubs_header.offset);
+}
+
+fn writeStubHelpers(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
+
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, stub_helper_header.size);
+ defer buffer.deinit();
+
+ {
+ const dyld_private_addr = blk: {
+ const atom = macho_file.getAtom(macho_file.dyld_private_atom_index.?);
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+ break :blk sym.n_value;
+ };
+ const dyld_stub_binder_got_addr = blk: {
+ const sym_loc = macho_file.globals.items[macho_file.dyld_stub_binder_index.?];
+ break :blk macho_file.getGotEntryAddress(sym_loc).?;
+ };
+ try stubs.writeStubHelperPreambleCode(.{
+ .cpu_arch = cpu_arch,
+ .source_addr = stub_helper_header.addr,
+ .dyld_private_addr = dyld_private_addr,
+ .dyld_stub_binder_got_addr = dyld_stub_binder_got_addr,
+ }, buffer.writer());
+ }
+
+ for (0..macho_file.stub_table.count()) |index| {
+ const source_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
+ stubs.stubHelperSize(cpu_arch) * index;
+ try stubs.writeStubHelperCode(.{
+ .cpu_arch = cpu_arch,
+ .source_addr = source_addr,
+ .target_addr = stub_helper_header.addr,
+ }, buffer.writer());
+ }
+
+ log.debug("writing __TEXT,__stub_helper contents at file offset 0x{x}", .{
+ stub_helper_header.offset,
+ });
+ try macho_file.base.file.?.pwriteAll(buffer.items, stub_helper_header.offset);
+}
+
+fn writeLaSymbolPtrs(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+ const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?];
+ const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?];
+
+ var buffer = try std.ArrayList(u8).initCapacity(gpa, la_symbol_ptr_header.size);
+ defer buffer.deinit();
+
+ for (0..macho_file.stub_table.count()) |index| {
+ const target_addr = stub_helper_header.addr + stubs.stubHelperPreambleSize(cpu_arch) +
+ stubs.stubHelperSize(cpu_arch) * index;
+ buffer.writer().writeIntLittle(u64, target_addr) catch unreachable;
+ }
+
+ log.debug("writing __DATA,__la_symbol_ptr contents at file offset 0x{x}", .{
+ la_symbol_ptr_header.offset,
+ });
+ try macho_file.base.file.?.pwriteAll(buffer.items, la_symbol_ptr_header.offset);
+}
+
+fn pruneAndSortSections(macho_file: *MachO) !void {
+ const Entry = struct {
+ index: u8,
+
+ pub fn lessThan(ctx: *MachO, lhs: @This(), rhs: @This()) bool {
+ const lhs_header = ctx.sections.items(.header)[lhs.index];
+ const rhs_header = ctx.sections.items(.header)[rhs.index];
+ return MachO.getSectionPrecedence(lhs_header) < MachO.getSectionPrecedence(rhs_header);
+ }
+ };
+
+ const gpa = macho_file.base.allocator;
+
+ var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len);
+ defer entries.deinit();
+
+ for (0..macho_file.sections.slice().len) |index| {
+ const section = macho_file.sections.get(index);
+ if (section.header.size == 0) {
+ log.debug("pruning section {s},{s} {?d}", .{
+ section.header.segName(),
+ section.header.sectName(),
+ section.first_atom_index,
+ });
+ for (&[_]*?u8{
+ &macho_file.text_section_index,
+ &macho_file.data_const_section_index,
+ &macho_file.data_section_index,
+ &macho_file.bss_section_index,
+ &macho_file.thread_vars_section_index,
+ &macho_file.thread_data_section_index,
+ &macho_file.thread_bss_section_index,
+ &macho_file.eh_frame_section_index,
+ &macho_file.unwind_info_section_index,
+ &macho_file.got_section_index,
+ &macho_file.tlv_ptr_section_index,
+ &macho_file.stubs_section_index,
+ &macho_file.stub_helper_section_index,
+ &macho_file.la_symbol_ptr_section_index,
+ }) |maybe_index| {
+ if (maybe_index.* != null and maybe_index.*.? == index) {
+ maybe_index.* = null;
+ }
+ }
+ continue;
+ }
+ entries.appendAssumeCapacity(.{ .index = @intCast(index) });
+ }
+
+ mem.sort(Entry, entries.items, macho_file, Entry.lessThan);
+
+ var slice = macho_file.sections.toOwnedSlice();
+ defer slice.deinit(gpa);
+
+ const backlinks = try gpa.alloc(u8, slice.len);
+ defer gpa.free(backlinks);
+ for (entries.items, 0..) |entry, i| {
+ backlinks[entry.index] = @as(u8, @intCast(i));
+ }
+
+ try macho_file.sections.ensureTotalCapacity(gpa, entries.items.len);
+ for (entries.items) |entry| {
+ macho_file.sections.appendAssumeCapacity(slice.get(entry.index));
+ }
+
+ for (&[_]*?u8{
+ &macho_file.text_section_index,
+ &macho_file.data_const_section_index,
+ &macho_file.data_section_index,
+ &macho_file.bss_section_index,
+ &macho_file.thread_vars_section_index,
+ &macho_file.thread_data_section_index,
+ &macho_file.thread_bss_section_index,
+ &macho_file.eh_frame_section_index,
+ &macho_file.unwind_info_section_index,
+ &macho_file.got_section_index,
+ &macho_file.tlv_ptr_section_index,
+ &macho_file.stubs_section_index,
+ &macho_file.stub_helper_section_index,
+ &macho_file.la_symbol_ptr_section_index,
+ }) |maybe_index| {
+ if (maybe_index.*) |*index| {
+ index.* = backlinks[index.*];
+ }
+ }
+}
+
+fn calcSectionSizes(macho_file: *MachO) !void {
+ const slice = macho_file.sections.slice();
+ for (slice.items(.header), 0..) |*header, sect_id| {
+ if (header.size == 0) continue;
+ if (macho_file.text_section_index) |txt| {
+ if (txt == sect_id and macho_file.requiresThunks()) continue;
+ }
+
+ var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue;
+
+ header.size = 0;
+ header.@"align" = 0;
+
+ while (true) {
+ const atom = macho_file.getAtom(atom_index);
+ const atom_alignment = try math.powi(u32, 2, atom.alignment);
+ const atom_offset = mem.alignForward(u64, header.size, atom_alignment);
+ const padding = atom_offset - header.size;
+
+ const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
+ sym.n_value = atom_offset;
+
+ header.size += padding + atom.size;
+ header.@"align" = @max(header.@"align", atom.alignment);
+
+ if (atom.next_index) |next_index| {
+ atom_index = next_index;
+ } else break;
+ }
+ }
+
+ if (macho_file.text_section_index != null and macho_file.requiresThunks()) {
+ // Create jump/branch range extenders if needed.
+ try thunks.createThunks(macho_file, macho_file.text_section_index.?);
+ }
+
+ // Update offsets of all symbols contained within each Atom.
+ // We need to do this since our unwind info synthesiser relies on
+ // traversing the symbols when synthesising unwind info and DWARF CFI records.
+ for (slice.items(.first_atom_index)) |first_atom_index| {
+ var atom_index = first_atom_index orelse continue;
+
+ while (true) {
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbol(atom.getSymbolWithLoc());
+
+ if (atom.getFile() != null) {
+ // Update each symbol contained within the atom
+ var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
+ while (it.next()) |sym_loc| {
+ const inner_sym = macho_file.getSymbolPtr(sym_loc);
+ inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
+ macho_file,
+ atom_index,
+ sym_loc.sym_index,
+ );
+ }
+
+ // If there is a section alias, update it now too
+ if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
+ const alias = macho_file.getSymbolPtr(sym_loc);
+ alias.n_value = sym.n_value;
+ }
+ }
+
+ if (atom.next_index) |next_index| {
+ atom_index = next_index;
+ } else break;
+ }
+ }
+
+ if (macho_file.got_section_index) |sect_id| {
+ const header = &macho_file.sections.items(.header)[sect_id];
+ header.size = macho_file.got_table.count() * @sizeOf(u64);
+ header.@"align" = 3;
+ }
+
+ if (macho_file.tlv_ptr_section_index) |sect_id| {
+ const header = &macho_file.sections.items(.header)[sect_id];
+ header.size = macho_file.tlv_ptr_table.count() * @sizeOf(u64);
+ header.@"align" = 3;
+ }
+
+ const cpu_arch = macho_file.base.options.target.cpu.arch;
+
+ if (macho_file.stubs_section_index) |sect_id| {
+ const header = &macho_file.sections.items(.header)[sect_id];
+ header.size = macho_file.stub_table.count() * stubs.stubSize(cpu_arch);
+ header.@"align" = stubs.stubAlignment(cpu_arch);
+ }
+
+ if (macho_file.stub_helper_section_index) |sect_id| {
+ const header = &macho_file.sections.items(.header)[sect_id];
+ header.size = macho_file.stub_table.count() * stubs.stubHelperSize(cpu_arch) +
+ stubs.stubHelperPreambleSize(cpu_arch);
+ header.@"align" = stubs.stubAlignment(cpu_arch);
+ }
+
+ if (macho_file.la_symbol_ptr_section_index) |sect_id| {
+ const header = &macho_file.sections.items(.header)[sect_id];
+ header.size = macho_file.stub_table.count() * @sizeOf(u64);
+ header.@"align" = 3;
+ }
+}
+
+fn allocateSegments(macho_file: *MachO) !void {
+ const gpa = macho_file.base.allocator;
+ for (macho_file.segments.items, 0..) |*segment, segment_index| {
+ const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT");
+ const base_size = if (is_text_segment) try load_commands.calcMinHeaderPad(gpa, macho_file.base.options, .{
+ .segments = macho_file.segments.items,
+ .dylibs = macho_file.dylibs.items,
+ .referenced_dylibs = macho_file.referenced_dylibs.keys(),
+ }) else 0;
+ try allocateSegment(macho_file, @as(u8, @intCast(segment_index)), base_size);
+ }
+}
+
+fn getSegmentAllocBase(macho_file: *MachO, segment_index: u8) struct { vmaddr: u64, fileoff: u64 } {
+ if (segment_index > 0) {
+ const prev_segment = macho_file.segments.items[segment_index - 1];
+ return .{
+ .vmaddr = prev_segment.vmaddr + prev_segment.vmsize,
+ .fileoff = prev_segment.fileoff + prev_segment.filesize,
+ };
+ }
+ return .{ .vmaddr = 0, .fileoff = 0 };
+}
+
+fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void {
+ const segment = &macho_file.segments.items[segment_index];
+
+ if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation
+
+ const base = getSegmentAllocBase(macho_file, segment_index);
+ segment.vmaddr = base.vmaddr;
+ segment.fileoff = base.fileoff;
+ segment.filesize = init_size;
+ segment.vmsize = init_size;
+
+ // Allocate the sections according to their alignment at the beginning of the segment.
+ const indexes = macho_file.getSectionIndexes(segment_index);
+ var start = init_size;
+
+ const slice = macho_file.sections.slice();
+ for (slice.items(.header)[indexes.start..indexes.end], 0..) |*header, sect_id| {
+ const alignment = try math.powi(u32, 2, header.@"align");
+ const start_aligned = mem.alignForward(u64, start, alignment);
+ const n_sect = @as(u8, @intCast(indexes.start + sect_id + 1));
+
+ header.offset = if (header.isZerofill())
+ 0
+ else
+ @as(u32, @intCast(segment.fileoff + start_aligned));
+ header.addr = segment.vmaddr + start_aligned;
+
+ if (slice.items(.first_atom_index)[indexes.start + sect_id]) |first_atom_index| {
+ var atom_index = first_atom_index;
+
+ log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
+ n_sect,
+ header.segName(),
+ header.sectName(),
+ });
+
+ while (true) {
+ const atom = macho_file.getAtom(atom_index);
+ const sym = macho_file.getSymbolPtr(atom.getSymbolWithLoc());
+ sym.n_value += header.addr;
+ sym.n_sect = n_sect;
+
+ log.debug(" ATOM(%{d}, '{s}') @{x}", .{
+ atom.sym_index,
+ macho_file.getSymbolName(atom.getSymbolWithLoc()),
+ sym.n_value,
+ });
+
+ if (atom.getFile() != null) {
+ // Update each symbol contained within the atom
+ var it = Atom.getInnerSymbolsIterator(macho_file, atom_index);
+ while (it.next()) |sym_loc| {
+ const inner_sym = macho_file.getSymbolPtr(sym_loc);
+ inner_sym.n_value = sym.n_value + Atom.calcInnerSymbolOffset(
+ macho_file,
+ atom_index,
+ sym_loc.sym_index,
+ );
+ inner_sym.n_sect = n_sect;
+ }
+
+ // If there is a section alias, update it now too
+ if (Atom.getSectionAlias(macho_file, atom_index)) |sym_loc| {
+ const alias = macho_file.getSymbolPtr(sym_loc);
+ alias.n_value = sym.n_value;
+ alias.n_sect = n_sect;
+ }
+ }
+
+ if (atom.next_index) |next_index| {
+ atom_index = next_index;
+ } else break;
+ }
+ }
+
+ start = start_aligned + header.size;
+
+ if (!header.isZerofill()) {
+ segment.filesize = start;
+ }
+ segment.vmsize = start;
+ }
+
+ const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch);
+ segment.filesize = mem.alignForward(u64, segment.filesize, page_size);
+ segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size);
+}
+
+const std = @import("std");
+const build_options = @import("build_options");
+const assert = std.debug.assert;
+const dwarf = std.dwarf;
+const fs = std.fs;
+const log = std.log.scoped(.link);
+const macho = std.macho;
+const math = std.math;
+const mem = std.mem;
+
+const aarch64 = @import("../../arch/aarch64/bits.zig");
+const calcUuid = @import("uuid.zig").calcUuid;
+const dead_strip = @import("dead_strip.zig");
+const eh_frame = @import("eh_frame.zig");
+const fat = @import("fat.zig");
+const link = @import("../../link.zig");
+const load_commands = @import("load_commands.zig");
+const stubs = @import("stubs.zig");
+const thunks = @import("thunks.zig");
+const trace = @import("../../tracy.zig").trace;
+
+const Allocator = mem.Allocator;
+const Archive = @import("Archive.zig");
+const Atom = @import("Atom.zig");
+const Cache = std.Build.Cache;
+const CodeSignature = @import("CodeSignature.zig");
+const Compilation = @import("../../Compilation.zig");
+const Dylib = @import("Dylib.zig");
+const MachO = @import("../MachO.zig");
+const Md5 = std.crypto.hash.Md5;
+const LibStub = @import("../tapi.zig").LibStub;
+const Object = @import("Object.zig");
+const Section = MachO.Section;
+const StringTable = @import("../strtab.zig").StringTable;
+const SymbolWithLoc = MachO.SymbolWithLoc;
+const TableSection = @import("../table_section.zig").TableSection;
+const Trie = @import("Trie.zig");
+const UnwindInfo = @import("UnwindInfo.zig");