aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO/zld.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/link/MachO/zld.zig')
-rw-r--r--src/link/MachO/zld.zig265
1 files changed, 105 insertions, 160 deletions
diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig
index 60d0f853ab..5ca1afd98c 100644
--- a/src/link/MachO/zld.zig
+++ b/src/link/MachO/zld.zig
@@ -38,6 +38,14 @@ pub const Zld = struct {
page_size: u16,
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 = .{},
+ codesig_cmd: macho.linkedit_data_command = .{ .cmd = .CODE_SIGNATURE },
+
objects: std.ArrayListUnmanaged(Object) = .{},
archives: std.ArrayListUnmanaged(Archive) = .{},
dylibs: std.ArrayListUnmanaged(Dylib) = .{},
@@ -1728,7 +1736,7 @@ pub const Zld = struct {
return (@intCast(u8, segment_precedence) << 4) + section_precedence;
}
- fn writeSegmentHeaders(self: *Zld, ncmds: *u32, writer: anytype) !void {
+ fn writeSegmentHeaders(self: *Zld, writer: anytype) !void {
for (self.segments.items) |seg, i| {
const indexes = self.getSectionIndexes(@intCast(u8, i));
var out_seg = seg;
@@ -1752,16 +1760,14 @@ pub const Zld = struct {
if (header.size == 0) continue;
try writer.writeStruct(header);
}
-
- ncmds.* += 1;
}
}
- fn writeLinkeditSegmentData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
- try self.writeDyldInfoData(ncmds, lc_writer, reverse_lookups);
- try self.writeFunctionStarts(ncmds, lc_writer);
- try self.writeDataInCode(ncmds, lc_writer);
- try self.writeSymtabs(ncmds, lc_writer);
+ fn writeLinkeditSegmentData(self: *Zld, reverse_lookups: [][]u32) !void {
+ try self.writeDyldInfoData(reverse_lookups);
+ try self.writeFunctionStarts();
+ try self.writeDataInCode();
+ try self.writeSymtabs();
const seg = self.getLinkeditSegmentPtr();
seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
@@ -2150,7 +2156,7 @@ pub const Zld = struct {
try trie.finalize(gpa);
}
- fn writeDyldInfoData(self: *Zld, ncmds: *u32, lc_writer: anytype, reverse_lookups: [][]u32) !void {
+ fn writeDyldInfoData(self: *Zld, reverse_lookups: [][]u32) !void {
const gpa = self.gpa;
var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
@@ -2219,21 +2225,14 @@ pub const Zld = struct {
const size = math.cast(usize, lazy_bind_size) orelse return error.Overflow;
try self.populateLazyBindOffsetsInStubHelper(buffer[offset..][0..size]);
- try lc_writer.writeStruct(macho.dyld_info_command{
- .cmd = .DYLD_INFO_ONLY,
- .cmdsize = @sizeOf(macho.dyld_info_command),
- .rebase_off = @intCast(u32, rebase_off),
- .rebase_size = @intCast(u32, rebase_size),
- .bind_off = @intCast(u32, bind_off),
- .bind_size = @intCast(u32, bind_size),
- .weak_bind_off = 0,
- .weak_bind_size = 0,
- .lazy_bind_off = @intCast(u32, lazy_bind_off),
- .lazy_bind_size = @intCast(u32, lazy_bind_size),
- .export_off = @intCast(u32, export_off),
- .export_size = @intCast(u32, export_size),
- });
- ncmds.* += 1;
+ self.dyld_info_cmd.rebase_off = @intCast(u32, rebase_off);
+ self.dyld_info_cmd.rebase_size = @intCast(u32, rebase_size);
+ self.dyld_info_cmd.bind_off = @intCast(u32, bind_off);
+ self.dyld_info_cmd.bind_size = @intCast(u32, bind_size);
+ self.dyld_info_cmd.lazy_bind_off = @intCast(u32, lazy_bind_off);
+ self.dyld_info_cmd.lazy_bind_size = @intCast(u32, lazy_bind_size);
+ self.dyld_info_cmd.export_off = @intCast(u32, export_off);
+ self.dyld_info_cmd.export_size = @intCast(u32, export_size);
}
fn populateLazyBindOffsetsInStubHelper(self: *Zld, buffer: []const u8) !void {
@@ -2351,7 +2350,7 @@ pub const Zld = struct {
const asc_u64 = std.sort.asc(u64);
- fn writeFunctionStarts(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
+ fn writeFunctionStarts(self: *Zld) !void {
const text_seg_index = self.getSegmentByName("__TEXT") orelse return;
const text_sect_index = self.getSectionByName("__TEXT", "__text") orelse return;
const text_seg = self.segments.items[text_seg_index];
@@ -2410,13 +2409,8 @@ pub const Zld = struct {
try self.file.pwriteAll(buffer.items, offset);
- try lc_writer.writeStruct(macho.linkedit_data_command{
- .cmd = .FUNCTION_STARTS,
- .cmdsize = @sizeOf(macho.linkedit_data_command),
- .dataoff = @intCast(u32, offset),
- .datasize = @intCast(u32, needed_size),
- });
- ncmds.* += 1;
+ self.function_starts_cmd.dataoff = @intCast(u32, offset);
+ self.function_starts_cmd.datasize = @intCast(u32, needed_size);
}
fn filterDataInCode(
@@ -2438,7 +2432,7 @@ pub const Zld = struct {
return dices[start..end];
}
- fn writeDataInCode(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
+ fn writeDataInCode(self: *Zld) !void {
var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.gpa);
defer out_dice.deinit();
@@ -2488,54 +2482,19 @@ pub const Zld = struct {
log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
try self.file.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
- try lc_writer.writeStruct(macho.linkedit_data_command{
- .cmd = .DATA_IN_CODE,
- .cmdsize = @sizeOf(macho.linkedit_data_command),
- .dataoff = @intCast(u32, offset),
- .datasize = @intCast(u32, needed_size),
- });
- ncmds.* += 1;
+
+ self.data_in_code_cmd.dataoff = @intCast(u32, offset);
+ self.data_in_code_cmd.datasize = @intCast(u32, needed_size);
}
- fn writeSymtabs(self: *Zld, ncmds: *u32, lc_writer: anytype) !void {
- var symtab_cmd = macho.symtab_command{
- .cmdsize = @sizeOf(macho.symtab_command),
- .symoff = 0,
- .nsyms = 0,
- .stroff = 0,
- .strsize = 0,
- };
- var dysymtab_cmd = macho.dysymtab_command{
- .cmdsize = @sizeOf(macho.dysymtab_command),
- .ilocalsym = 0,
- .nlocalsym = 0,
- .iextdefsym = 0,
- .nextdefsym = 0,
- .iundefsym = 0,
- .nundefsym = 0,
- .tocoff = 0,
- .ntoc = 0,
- .modtaboff = 0,
- .nmodtab = 0,
- .extrefsymoff = 0,
- .nextrefsyms = 0,
- .indirectsymoff = 0,
- .nindirectsyms = 0,
- .extreloff = 0,
- .nextrel = 0,
- .locreloff = 0,
- .nlocrel = 0,
- };
- var ctx = try self.writeSymtab(&symtab_cmd);
+ fn writeSymtabs(self: *Zld) !void {
+ var ctx = try self.writeSymtab();
defer ctx.imports_table.deinit();
- try self.writeDysymtab(ctx, &dysymtab_cmd);
- try self.writeStrtab(&symtab_cmd);
- try lc_writer.writeStruct(symtab_cmd);
- try lc_writer.writeStruct(dysymtab_cmd);
- ncmds.* += 2;
+ try self.writeDysymtab(ctx);
+ try self.writeStrtab();
}
- fn writeSymtab(self: *Zld, lc: *macho.symtab_command) !SymtabCtx {
+ fn writeSymtab(self: *Zld) !SymtabCtx {
const gpa = self.gpa;
var locals = std.ArrayList(macho.nlist_64).init(gpa);
@@ -2618,8 +2577,8 @@ pub const Zld = struct {
log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
try self.file.pwriteAll(buffer.items, offset);
- lc.symoff = @intCast(u32, offset);
- lc.nsyms = nsyms;
+ self.symtab_cmd.symoff = @intCast(u32, offset);
+ self.symtab_cmd.nsyms = nsyms;
return SymtabCtx{
.nlocalsym = nlocals,
@@ -2629,7 +2588,7 @@ pub const Zld = struct {
};
}
- fn writeStrtab(self: *Zld, lc: *macho.symtab_command) !void {
+ fn writeStrtab(self: *Zld) !void {
const seg = self.getLinkeditSegmentPtr();
const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
const needed_size = self.strtab.buffer.items.len;
@@ -2639,8 +2598,8 @@ pub const Zld = struct {
try self.file.pwriteAll(self.strtab.buffer.items, offset);
- lc.stroff = @intCast(u32, offset);
- lc.strsize = @intCast(u32, needed_size);
+ self.symtab_cmd.stroff = @intCast(u32, offset);
+ self.symtab_cmd.strsize = @intCast(u32, needed_size);
}
const SymtabCtx = struct {
@@ -2650,7 +2609,7 @@ pub const Zld = struct {
imports_table: std.AutoHashMap(SymbolWithLoc, u32),
};
- fn writeDysymtab(self: *Zld, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
+ fn writeDysymtab(self: *Zld, ctx: SymtabCtx) !void {
const gpa = self.gpa;
const nstubs = @intCast(u32, self.stubs.items.len);
const ngot_entries = @intCast(u32, self.got_entries.items.len);
@@ -2706,21 +2665,33 @@ pub const Zld = struct {
assert(buf.items.len == needed_size);
try self.file.pwriteAll(buf.items, offset);
- lc.nlocalsym = ctx.nlocalsym;
- lc.iextdefsym = iextdefsym;
- lc.nextdefsym = ctx.nextdefsym;
- lc.iundefsym = iundefsym;
- lc.nundefsym = ctx.nundefsym;
- lc.indirectsymoff = @intCast(u32, offset);
- lc.nindirectsyms = nindirectsyms;
+ 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 = @intCast(u32, offset);
+ self.dysymtab_cmd.nindirectsyms = nindirectsyms;
}
- fn writeCodeSignaturePadding(
- self: *Zld,
- code_sig: *CodeSignature,
- ncmds: *u32,
- lc_writer: anytype,
- ) !u32 {
+ fn writeUuid(self: *Zld, comp: *const Compilation, offset: u32) !void {
+ switch (self.options.optimize_mode) {
+ .Debug => {
+ // In Debug we don't really care about reproducibility, so put in a random value
+ // and be done with it.
+ std.crypto.random.bytes(&self.uuid_cmd.uuid);
+ },
+ else => {
+ const seg = self.getLinkeditSegmentPtr();
+ const file_size = seg.fileoff + seg.filesize;
+ try uuid.calcUuidParallel(comp, self.file, file_size, &self.uuid_cmd.uuid);
+ },
+ }
+ const in_file = @sizeOf(macho.mach_header_64) + offset + @sizeOf(macho.load_command);
+ try self.file.pwriteAll(&self.uuid_cmd.uuid, in_file);
+ }
+
+ 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
@@ -2733,23 +2704,11 @@ pub const Zld = struct {
// except for code signature data.
try self.file.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
- try lc_writer.writeStruct(macho.linkedit_data_command{
- .cmd = .CODE_SIGNATURE,
- .cmdsize = @sizeOf(macho.linkedit_data_command),
- .dataoff = @intCast(u32, offset),
- .datasize = @intCast(u32, needed_size),
- });
- ncmds.* += 1;
-
- return @intCast(u32, offset);
+ self.codesig_cmd.dataoff = @intCast(u32, offset);
+ self.codesig_cmd.datasize = @intCast(u32, needed_size);
}
- fn writeCodeSignature(
- self: *Zld,
- comp: *const Compilation,
- code_sig: *CodeSignature,
- offset: u32,
- ) !void {
+ fn writeCodeSignature(self: *Zld, comp: *const Compilation, code_sig: *CodeSignature) !void {
const seg_id = self.getSegmentByName("__TEXT").?;
const seg = self.segments.items[seg_id];
@@ -2760,17 +2719,17 @@ pub const Zld = struct {
.file = self.file,
.exec_seg_base = seg.fileoff,
.exec_seg_limit = seg.filesize,
- .file_size = offset,
+ .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}", .{
- offset,
- offset + buffer.items.len,
+ self.codesig_cmd.dataoff,
+ self.codesig_cmd.dataoff + buffer.items.len,
});
- try self.file.pwriteAll(buffer.items, offset);
+ try self.file.pwriteAll(buffer.items, self.codesig_cmd.dataoff);
}
/// Writes Mach-O file header.
@@ -3986,13 +3945,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
}
try zld.writeAtoms(reverse_lookups);
-
- var lc_buffer = std.ArrayList(u8).init(arena);
- const lc_writer = lc_buffer.writer();
-
- var ncmds: u32 = 0;
-
- try zld.writeLinkeditSegmentData(&ncmds, lc_writer, reverse_lookups);
+ try zld.writeLinkeditSegmentData(reverse_lookups);
// 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
@@ -4017,47 +3970,48 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
}
}
- try load_commands.writeDylinkerLC(&ncmds, lc_writer);
+ // Write load commands
+ var lc_buffer = std.ArrayList(u8).init(arena);
+ const lc_writer = lc_buffer.writer();
+
+ try zld.writeSegmentHeaders(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 load_commands.writeDylinkerLC(lc_writer);
if (zld.options.output_mode == .Exe) {
const seg_id = zld.getSegmentByName("__TEXT").?;
const seg = zld.segments.items[seg_id];
const global = zld.getEntryPoint();
const sym = zld.getSymbol(global);
- try load_commands.writeMainLC(@intCast(u32, sym.n_value - seg.vmaddr), options, &ncmds, lc_writer);
+ try lc_writer.writeStruct(macho.entry_point_command{
+ .entryoff = @intCast(u32, sym.n_value - seg.vmaddr),
+ .stacksize = options.stack_size_override orelse 0,
+ });
} else {
assert(zld.options.output_mode == .Lib);
- try load_commands.writeDylibIdLC(zld.gpa, zld.options, &ncmds, lc_writer);
+ try load_commands.writeDylibIdLC(zld.gpa, zld.options, lc_writer);
}
- try load_commands.writeRpathLCs(zld.gpa, zld.options, &ncmds, lc_writer);
- try load_commands.writeSourceVersionLC(&ncmds, lc_writer);
- try load_commands.writeBuildVersionLC(zld.options, &ncmds, lc_writer);
-
- // Looking forward into the future, we will want to offer `-no_uuid` support in which case
- // there will be nothing to backpatch.
- const uuid_offset_backpatch: ?usize = blk: {
- const index = lc_buffer.items.len;
- var uuid_buf: [16]u8 = [_]u8{0} ** 16;
-
- if (zld.options.optimize_mode == .Debug) {
- // In Debug we don't really care about reproducibility, so put in a random value
- // and be done with it.
- std.crypto.random.bytes(&uuid_buf);
- }
+ try load_commands.writeRpathLCs(zld.gpa, zld.options, lc_writer);
+ try lc_writer.writeStruct(macho.source_version_command{
+ .version = 0,
+ });
+ try load_commands.writeBuildVersionLC(zld.options, lc_writer);
- try load_commands.writeUuidLC(&uuid_buf, &ncmds, lc_writer);
- break :blk if (zld.options.optimize_mode == .Debug) null else index;
- };
+ const uuid_offset = @intCast(u32, lc_buffer.items.len);
+ try lc_writer.writeStruct(zld.uuid_cmd);
- try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), &ncmds, lc_writer);
+ try load_commands.writeLoadDylibLCs(zld.dylibs.items, zld.referenced_dylibs.keys(), lc_writer);
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_offset: ?u32 = null;
var codesig: ?CodeSignature = if (requires_codesig) 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
@@ -4069,29 +4023,20 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
if (options.entitlements) |path| {
try codesig.addEntitlements(gpa, path);
}
- codesig_offset = try zld.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
+ try zld.writeCodeSignaturePadding(&codesig);
+ try lc_writer.writeStruct(zld.codesig_cmd);
break :blk codesig;
} else null;
defer if (codesig) |*csig| csig.deinit(gpa);
- var headers_buf = std.ArrayList(u8).init(arena);
- try zld.writeSegmentHeaders(&ncmds, headers_buf.writer());
+ const ncmds = load_commands.calcNumOfLCs(lc_buffer.items);
+ try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64));
+ try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len));
- try zld.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
- try zld.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
- try zld.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
-
- if (uuid_offset_backpatch) |backpatch| {
- const seg = zld.getLinkeditSegmentPtr();
- const file_size = seg.fileoff + seg.filesize;
- var uuid_buf: [16]u8 = undefined;
- try uuid.calcUuidParallel(comp, zld.file, file_size, &uuid_buf);
- const offset = @sizeOf(macho.mach_header_64) + headers_buf.items.len + backpatch + @sizeOf(macho.load_command);
- try zld.file.pwriteAll(&uuid_buf, offset);
- }
+ try zld.writeUuid(comp, uuid_offset);
if (codesig) |*csig| {
- try zld.writeCodeSignature(comp, csig, codesig_offset.?); // code signing always comes last
+ try zld.writeCodeSignature(comp, csig); // code signing always comes last
}
}