diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-04 22:34:16 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-04 22:59:16 +0200 |
| commit | 7e87f93e068af192a3c39bdeebfe8e9b612311b0 (patch) | |
| tree | 20d608f7b80dccd7be5d3fde7d72772d0ee312b2 /src | |
| parent | 80e1c244b6c9d5f3e855878d92ecc09dc8eb970a (diff) | |
| download | zig-7e87f93e068af192a3c39bdeebfe8e9b612311b0.tar.gz zig-7e87f93e068af192a3c39bdeebfe8e9b612311b0.zip | |
macho: unfortunately, LINKEDIT commands NEED to be in order
Otherwise, Apple's tooling goes mental and reports that the executable
is malformed/fails strict validation. We absolutely have to get it
right to support tools such `codesign` which are required to
successfully launch an app on an iOS device for instance. When Zig
matures enough so that we can ditch any Apple tooling and still be
able to successfully codesign for iOS and other, we can revisit this
area. Until then however, we are stuck in having to rewrite the LINKEDIT
segment at every update run of the self-hosted.
FYI, the strict layout for the MachO binary apparently is (please,
read this with a pinch of salt as this is inferred by me):
* __TEXT segment
* __DATA_CONST segment
* __DATA segment
* __LINKEDIT segment
* dyld info (rebase, bind, weak bind, lazy bind, export)
* symbol table
* dynamic symbol table
* string table
* code signature (if expected)
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 912 | ||||
| -rw-r--r-- | src/link/MachO/TextBlock.zig | 1 |
2 files changed, 343 insertions, 570 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index c1fb6977c5..4ee81e7c43 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -162,10 +162,6 @@ stubs_map: std.AutoArrayHashMapUnmanaged(u32, *TextBlock) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, load_commands_dirty: bool = false, -dyld_info_dirty: bool = false, - -strtab_dirty: bool = false, -strtab_needs_relocation: bool = false, has_dices: bool = false, has_stabs: bool = false, @@ -368,13 +364,12 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio .n_desc = 0, .n_value = 0, }); + try self.strtab.append(allocator, 0); try self.populateMissingMetadata(); - try self.writeLocalSymbol(0); if (self.d_sym) |*ds| { try ds.populateMissingMetadata(allocator); - try ds.writeLocalSymbol(0); } return self; @@ -553,6 +548,7 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { .n_desc = 0, .n_value = 0, }); + try self.strtab.append(self.base.allocator, 0); } // Positional arguments to the linker such as object files and static archives. @@ -784,7 +780,6 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { } } try self.writeAtoms(); - try self.writeDices(); try self.flushModule(comp); } @@ -792,11 +787,11 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); + log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); + log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. @@ -811,11 +806,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { defer tracy.end(); try self.setEntryPoint(); - try self.writeDyldInfoData(); - try self.writeAllGlobalAndUndefSymbols(); - try self.writeIndirectSymbolTable(); - try self.writeStringTable(); - try self.updateLinkeditSegmentSizes(); + try self.writeLinkeditSegment(); if (self.d_sym) |*ds| { // Flush debug symbols bundle. @@ -843,9 +834,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } assert(!self.load_commands_dirty); - assert(!self.dyld_info_dirty); - assert(!self.strtab_dirty); - assert(!self.strtab_needs_relocation); if (self.requires_adhoc_codesig) { try self.writeCodeSignature(); // code signing always comes last @@ -1769,7 +1757,6 @@ pub fn writeAtom(self: *MachO, atom: *TextBlock, match: MatchingSection) !void { try atom.resolveRelocs(self); log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset }); try self.base.file.?.pwriteAll(atom.code.items, file_offset); - try self.writeLocalSymbol(atom.local_sym_index); } fn allocateLocalSymbols(self: *MachO, match: MatchingSection, old_base_addr: u64) !void { @@ -1830,7 +1817,7 @@ fn writeAtoms(self: *MachO) !void { pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*TextBlock { const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("got_entry"), + .n_strx = try self.makeString("l_zld_got_entry"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -1866,7 +1853,7 @@ pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*TextBlock { fn createDyldPrivateAtom(self: *MachO) !*TextBlock { const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("dyld_private"), + .n_strx = try self.makeString("l_zld_dyld_private"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -1890,7 +1877,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*TextBlock { }; const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("stub_preamble"), + .n_strx = try self.makeString("l_zld_stub_preamble"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -2023,7 +2010,7 @@ pub fn createStubHelperAtom(self: *MachO) !*TextBlock { }; const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("stub_in_stub_helper"), + .n_strx = try self.makeString("l_zld_stub_in_stub_helper"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -2078,7 +2065,7 @@ pub fn createStubHelperAtom(self: *MachO) !*TextBlock { pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym_index: u32) !*TextBlock { const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("lazy_ptr"), + .n_strx = try self.makeString("l_zld_lazy_ptr"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -2102,7 +2089,6 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym .local_sym_index = lazy_binding_sym_index, .offset = 0, }); - self.dyld_info_dirty = true; return atom; } @@ -2120,7 +2106,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*TextBlock { }; const local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.base.allocator, .{ - .n_strx = try self.makeString("stub"), + .n_strx = try self.makeString("l_zld_stub"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, @@ -2303,7 +2289,6 @@ fn resolveSymbolsInObject( .local_sym_index = local_sym_index, .file = object_id, }; - self.dyld_info_dirty = true; } else if (symbolIsTentative(sym)) { // Symbol is a tentative definition. const resolv = self.symbol_resolver.getPtr(n_strx) orelse { @@ -2638,7 +2623,6 @@ fn resolveDyldStubBinder(self: *MachO) !void { .sect = self.got_section_index.?, }; _ = try self.allocateAtom(atom, match); - self.dyld_info_dirty = true; } fn parseTextBlocks(self: *MachO) !void { @@ -2658,27 +2642,21 @@ fn addDataInCodeLC(self: *MachO) !void { .datasize = 0, }, }); - const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; - const needed_size = 10 * @sizeOf(macho.data_in_code_entry); - const dataoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.data_in_code_entry), null); - log.debug("found data-in-code free space 0x{x} to 0x{x}", .{ dataoff, dataoff + needed_size }); - dice_cmd.dataoff = @intCast(u32, dataoff); - dice_cmd.datasize = needed_size; self.load_commands_dirty = true; } fn addCodeSignatureLC(self: *MachO) !void { - if (self.code_signature_cmd_index == null and self.requires_adhoc_codesig) { - self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); - try self.load_commands.append(self.base.allocator, .{ - .LinkeditData = .{ - .cmd = macho.LC_CODE_SIGNATURE, - .cmdsize = @sizeOf(macho.linkedit_data_command), - .dataoff = 0, - .datasize = 0, - }, - }); - } + if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return; + self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); + try self.load_commands.append(self.base.allocator, .{ + .LinkeditData = .{ + .cmd = macho.LC_CODE_SIGNATURE, + .cmdsize = @sizeOf(macho.linkedit_data_command), + .dataoff = 0, + .datasize = 0, + }, + }); + self.load_commands_dirty = true; } fn addRpathLCs(self: *MachO, rpaths: []const []const u8) !void { @@ -2697,6 +2675,7 @@ fn addRpathLCs(self: *MachO, rpaths: []const []const u8) !void { mem.set(u8, rpath_cmd.data, 0); mem.copy(u8, rpath_cmd.data, rpath); try self.load_commands.append(self.base.allocator, .{ .Rpath = rpath_cmd }); + self.load_commands_dirty = true; } } @@ -2713,6 +2692,7 @@ fn addLoadDylibLCs(self: *MachO) !void { ); errdefer dylib_cmd.deinit(self.base.allocator); try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); + self.load_commands_dirty = true; } } @@ -2937,7 +2917,6 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { }; const got_atom = try self.createGotAtom(key); try self.got_entries_map.put(self.base.allocator, key, got_atom); - self.dyld_info_dirty = true; } pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { @@ -3138,10 +3117,6 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 symbol.n_type = macho.N_SECT; symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1; symbol.n_desc = 0; - - try self.writeLocalSymbol(decl.link.macho.local_sym_index); - if (self.d_sym) |*ds| - try ds.writeLocalSymbol(decl.link.macho.local_sym_index); } else { const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)}); defer self.base.allocator.free(decl_name); @@ -3168,10 +3143,6 @@ fn placeDecl(self: *MachO, decl: *Module.Decl, code_len: usize) !*macho.nlist_64 .seg = self.data_const_segment_cmd_index.?, .sect = self.got_section_index.?, }); - - try self.writeLocalSymbol(decl.link.macho.local_sym_index); - if (self.d_sym) |*ds| - try ds.writeLocalSymbol(decl.link.macho.local_sym_index); } return symbol; @@ -3257,7 +3228,6 @@ pub fn updateDeclExports( const name_str_index = try self.makeString(exp_name); const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { _ = self.globals.addOneAssumeCapacity(); - self.dyld_info_dirty = true; break :blk @intCast(u32, self.globals.items.len - 1); }; self.globals.items[i] = .{ @@ -3347,7 +3317,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { const program_code_size_hint = self.base.options.program_code_size_hint; // const program_code_size_hint = 10; const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint; - const ideal_size = self.header_pad + program_code_size_hint + got_size_hint; + const ideal_size = self.header_pad + (program_code_size_hint + got_size_hint) * 5; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size }); @@ -3441,7 +3411,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.data_const_segment_cmd_index == null) { self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const address_and_offset = self.nextSegmentAddressAndOffset(); - const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint; + const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint * 1000; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ @@ -3482,7 +3452,7 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.data_segment_cmd_index == null) { self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len); const address_and_offset = self.nextSegmentAddressAndOffset(); - const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint; + const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint * 1000; const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size); log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size }); @@ -3621,7 +3591,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.dyld_info_cmd_index == null) { self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); - try self.load_commands.append(self.base.allocator, .{ .DyldInfoOnly = .{ .cmd = macho.LC_DYLD_INFO_ONLY, @@ -3638,42 +3607,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { .export_size = 0, }, }); - - // Preallocate rebase, binding, lazy binding info, and export info. - const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - const subsection_size = 128; // TODO this is totally random - const needed_size = 4 * subsection_size; - const offset = self.findFreeSpaceLinkedit(needed_size, 1, null); - - const rebase_off = @intCast(u32, offset); - log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + subsection_size }); - dyld.rebase_off = rebase_off; - dyld.rebase_size = subsection_size; - - const bind_off = rebase_off + subsection_size; - log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + subsection_size }); - dyld.bind_off = bind_off; - dyld.bind_size = subsection_size; - - const lazy_bind_off = bind_off + subsection_size; - log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ - lazy_bind_off, - lazy_bind_off + subsection_size, - }); - dyld.lazy_bind_off = lazy_bind_off; - dyld.lazy_bind_size = subsection_size; - - const export_off = lazy_bind_off + subsection_size; - log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + subsection_size }); - dyld.export_off = export_off; - dyld.export_size = subsection_size; - self.load_commands_dirty = true; } if (self.symtab_cmd_index == null) { self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); - try self.load_commands.append(self.base.allocator, .{ .Symtab = .{ .cmd = macho.LC_SYMTAB, @@ -3684,35 +3622,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { .strsize = 0, }, }); - - const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - - const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); - const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64), null); - log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); - symtab.symoff = @intCast(u32, symtab_off); - symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint); - - try self.strtab.append(self.base.allocator, 0); - const strtab_size = self.strtab.items.len; - const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off); - log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); - symtab.stroff = @intCast(u32, strtab_off); - symtab.strsize = @intCast(u32, strtab_size); - self.load_commands_dirty = true; - self.strtab_dirty = true; } if (self.dysymtab_cmd_index == null) { self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); - - // Preallocate space for indirect symbol table. - const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64. - const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64), null); - - log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size }); - try self.load_commands.append(self.base.allocator, .{ .Dysymtab = .{ .cmd = macho.LC_DYSYMTAB, @@ -3729,8 +3643,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { .nmodtab = 0, .extrefsymoff = 0, .nextrefsyms = 0, - .indirectsymoff = @intCast(u32, indsymtab_off), - .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint), + .indirectsymoff = 0, + .nindirectsyms = 0, .extreloff = 0, .nextrel = 0, .locreloff = 0, @@ -4084,387 +3998,7 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset { }; } -fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { - assert(start > 0); - var min_pos: u64 = std.math.maxInt(u64); - - // __LINKEDIT is a weird segment where sections get their own load commands so we - // special-case it. - if (self.dyld_info_cmd_index) |idx| { - const dyld_info = self.load_commands.items[idx].DyldInfoOnly; - if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off; - } - - if (self.function_starts_cmd_index) |idx| { - const fstart = self.load_commands.items[idx].LinkeditData; - if (fstart.dataoff > start and fstart.dataoff < min_pos) min_pos = fstart.dataoff; - } - - if (self.data_in_code_cmd_index) |idx| { - const dic = self.load_commands.items[idx].LinkeditData; - if (dic.dataoff > start and dic.dataoff < min_pos) min_pos = dic.dataoff; - } - - if (self.dysymtab_cmd_index) |idx| { - const dysymtab = self.load_commands.items[idx].Dysymtab; - if (dysymtab.indirectsymoff > start and dysymtab.indirectsymoff < min_pos) min_pos = dysymtab.indirectsymoff; - // TODO Handle more dynamic symbol table sections. - } - - if (self.symtab_cmd_index) |idx| { - const symtab = self.load_commands.items[idx].Symtab; - if (symtab.symoff > start and symtab.symoff < min_pos) min_pos = symtab.symoff; - if (symtab.stroff > start and symtab.stroff < min_pos) min_pos = symtab.stroff; - } - - return min_pos - start; -} -inline fn checkForCollision(start: u64, end: u64, off: u64, size: u64) ?u64 { - const increased_size = padToIdeal(size); - const test_end = off + increased_size; - if (end > off and start < test_end) { - return test_end; - } - return null; -} - -fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { - const end = start + padToIdeal(size); - - // __LINKEDIT is a weird segment where sections get their own load commands so we - // special-case it. - if (self.dyld_info_cmd_index) |idx| { - const dyld_info = self.load_commands.items[idx].DyldInfoOnly; - const offset = dyld_info.rebase_off; - const actual_size = dyld_info.export_off + dyld_info.export_size - offset; - const increased_size = padToIdeal(actual_size); - const test_end = offset + increased_size; - if (end > offset and start < test_end) { - return test_end; - } - } - - if (self.function_starts_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; - const fstart = self.load_commands.items[idx].LinkeditData; - if (checkForCollision(start, end, fstart.dataoff, fstart.datasize)) |pos| { - return pos; - } - } - - if (self.data_in_code_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; - const dic = self.load_commands.items[idx].LinkeditData; - if (checkForCollision(start, end, dic.dataoff, dic.datasize)) |pos| { - return pos; - } - } - - if (self.dysymtab_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; - const dysymtab = self.load_commands.items[idx].Dysymtab; - // Indirect symbol table - const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32); - if (checkForCollision(start, end, dysymtab.indirectsymoff, nindirectsize)) |pos| { - return pos; - } - // TODO Handle more dynamic symbol table sections. - } - - if (self.symtab_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; - const symtab = self.load_commands.items[idx].Symtab; - // Symbol table - const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); - if (checkForCollision(start, end, symtab.symoff, symsize)) |pos| { - return pos; - } - // String table - if (checkForCollision(start, end, symtab.stroff, symtab.strsize)) |pos| { - return pos; - } - } - - return null; -} - -fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, start: ?u64) u64 { - const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - var st: u64 = start orelse linkedit.inner.fileoff; - while (self.detectAllocCollisionLinkedit(st, object_size)) |item_end| { - st = mem.alignForwardGeneric(u64, item_end, min_alignment); - } - return st; -} - -fn relocateSymbolTable(self: *MachO) !void { - const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - const nlocals = self.locals.items.len; - const nglobals = self.globals.items.len; - const nundefs = self.undefs.items.len; - const nsyms = nlocals + nglobals + nundefs; - - if (symtab.nsyms < nsyms) { - const needed_size = nsyms * @sizeOf(macho.nlist_64); - if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) { - // Move the entire symbol table to a new location - const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64), null); - const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64); - - log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{ - symtab.symoff, - symtab.symoff + existing_size, - new_symoff, - new_symoff + existing_size, - }); - - // TODO copyRangeAll doesn't seem to extend the file beyond its allocated size - try self.base.file.?.pwriteAll(&[_]u8{0}, new_symoff + existing_size - 1); - const amt = try self.base.file.?.copyRangeAll( - symtab.symoff, - self.base.file.?, - new_symoff, - existing_size, - ); - if (amt != existing_size) return error.InputOutput; - symtab.symoff = @intCast(u32, new_symoff); - self.strtab_needs_relocation = true; - } - symtab.nsyms = @intCast(u32, nsyms); - self.load_commands_dirty = true; - } -} - -fn writeLocalSymbol(self: *MachO, index: usize) !void { - const tracy = trace(@src()); - defer tracy.end(); - try self.relocateSymbolTable(); - const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - const off = symtab.symoff + @sizeOf(macho.nlist_64) * index; - const sym = self.locals.items[index]; - log.debug("writing local symbol {s}: {} at 0x{x}", .{ self.getString(sym.n_strx), sym, off }); - try self.base.file.?.pwriteAll(mem.asBytes(&sym), off); -} - -fn writeAllGlobalAndUndefSymbols(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - - try self.relocateSymbolTable(); - const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - const nlocals = self.locals.items.len; - const nglobals = self.globals.items.len; - const nundefs = self.undefs.items.len; - - const locals_off = symtab.symoff; - const locals_size = nlocals * @sizeOf(macho.nlist_64); - - const globals_off = locals_off + locals_size; - const globals_size = nglobals * @sizeOf(macho.nlist_64); - log.debug("writing global symbols from 0x{x} to 0x{x}", .{ globals_off, globals_size + globals_off }); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), globals_off); - - const undefs_off = globals_off + globals_size; - const undefs_size = nundefs * @sizeOf(macho.nlist_64); - log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); - try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off); - - // Update dynamic symbol table. - const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; - dysymtab.nlocalsym = @intCast(u32, nlocals); - dysymtab.iextdefsym = @intCast(u32, nlocals); - dysymtab.nextdefsym = @intCast(u32, nglobals); - dysymtab.iundefsym = @intCast(u32, nlocals + nglobals); - dysymtab.nundefsym = @intCast(u32, nundefs); - self.load_commands_dirty = true; -} - -fn writeIndirectSymbolTable(self: *MachO) !void { - // TODO figure out a way not to rewrite the table every time if - // no new undefs are not added. - const tracy = trace(@src()); - defer tracy.end(); - - const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = &text_segment.sections.items[self.stubs_section_index.?]; - const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const got = &data_const_seg.sections.items[self.got_section_index.?]; - const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; - const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; - - const nstubs = @intCast(u32, self.stubs_map.keys().len); - const ngot_entries = @intCast(u32, self.got_entries_map.keys().len); - const allocated_size = self.allocatedSizeLinkedit(dysymtab.indirectsymoff); - const nindirectsyms = nstubs * 2 + ngot_entries; - const needed_size = @intCast(u32, nindirectsyms * @sizeOf(u32)); - - if (needed_size > allocated_size) { - dysymtab.nindirectsyms = 0; - dysymtab.indirectsymoff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, @sizeOf(u32), null)); - } - dysymtab.nindirectsyms = nindirectsyms; - log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ - dysymtab.indirectsymoff, - dysymtab.indirectsymoff + needed_size, - }); - - var buf = try self.base.allocator.alloc(u8, needed_size); - defer self.base.allocator.free(buf); - var stream = std.io.fixedBufferStream(buf); - var writer = stream.writer(); - - stubs.reserved1 = 0; - for (self.stubs_map.keys()) |key| { - try writer.writeIntLittle(u32, dysymtab.iundefsym + key); - } - - got.reserved1 = nstubs; - for (self.got_entries_map.keys()) |key| { - switch (key.where) { - .undef => { - try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index); - }, - .local => { - try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); - }, - } - } - - la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; - for (self.stubs_map.keys()) |key| { - try writer.writeIntLittle(u32, dysymtab.iundefsym + key); - } - - try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); - self.load_commands_dirty = true; -} - -fn writeDices(self: *MachO) !void { - if (!self.has_dices) return; - - const tracy = trace(@src()); - defer tracy.end(); - - var buf = std.ArrayList(u8).init(self.base.allocator); - defer buf.deinit(); - - var block: *TextBlock = self.blocks.get(.{ - .seg = self.text_segment_cmd_index orelse return, - .sect = self.text_section_index orelse return, - }) orelse return; - - while (block.prev) |prev| { - block = prev; - } - - const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const text_sect = text_seg.sections.items[self.text_section_index.?]; - - while (true) { - if (block.dices.items.len > 0) { - const sym = self.locals.items[block.local_sym_index]; - const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); - - try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry)); - for (block.dices.items) |dice| { - const rebased_dice = macho.data_in_code_entry{ - .offset = base_off + dice.offset, - .length = dice.length, - .kind = dice.kind, - }; - buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice)); - } - } - - if (block.next) |next| { - block = next; - } else break; - } - - const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; - const allocated_size = self.allocatedSizeLinkedit(dice_cmd.dataoff); - const needed_size = @intCast(u32, buf.items.len); - - if (needed_size > allocated_size) { - dice_cmd.datasize = 0; - dice_cmd.dataoff = @intCast(u32, self.findFreeSpaceLinkedit( - needed_size, - @alignOf(macho.data_in_code_entry), - dice_cmd.dataoff, - )); - } - dice_cmd.datasize = needed_size; - log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ - dice_cmd.dataoff, - dice_cmd.dataoff + dice_cmd.datasize, - }); - - try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff); - self.load_commands_dirty = true; -} - -fn writeCodeSignaturePadding(self: *MachO) !void { - // TODO figure out how not to rewrite padding every single time. - const tracy = trace(@src()); - defer tracy.end(); - - const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; - const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize; - const needed_size = CodeSignature.calcCodeSignaturePaddingSize( - self.base.options.emit.?.sub_path, - fileoff, - self.page_size, - ); - code_sig_cmd.dataoff = @intCast(u32, fileoff); - code_sig_cmd.datasize = needed_size; - - // Advance size of __LINKEDIT segment - linkedit_segment.inner.filesize += needed_size; - if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { - linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); - } - log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + 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.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); - self.load_commands_dirty = true; -} - -fn writeCodeSignature(self: *MachO) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; - - var code_sig: CodeSignature = .{}; - defer code_sig.deinit(self.base.allocator); - - try code_sig.calcAdhocSignature( - self.base.allocator, - self.base.file.?, - self.base.options.emit.?.sub_path, - text_segment.inner, - code_sig_cmd, - self.base.options.output_mode, - self.page_size, - ); - - var buffer = try self.base.allocator.alloc(u8, code_sig.size()); - defer self.base.allocator.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try code_sig.write(stream.writer()); - - log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); - - try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); -} - fn writeDyldInfoData(self: *MachO) !void { - if (!self.dyld_info_dirty) return; - const tracy = trace(@src()); defer tracy.end(); @@ -4528,7 +4062,7 @@ fn writeDyldInfoData(self: *MachO) !void { { // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. - log.debug("writing export trie", .{}); + log.debug("generating export trie", .{}); const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; const base_address = text_segment.inner.vmaddr; @@ -4546,28 +4080,30 @@ fn writeDyldInfoData(self: *MachO) !void { try trie.finalize(self.base.allocator); } + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); - const rebase_size = @intCast(u32, try bind.rebaseInfoSize(rebase_pointers.items)); - const bind_size = @intCast(u32, try bind.bindInfoSize(bind_pointers.items)); - const lazy_bind_size = @intCast(u32, try bind.lazyBindInfoSize(lazy_bind_pointers.items)); - const export_size = @intCast(u32, trie.size); - const total_size = rebase_size + bind_size + lazy_bind_size + export_size; - const needed_size = mem.alignForwardGeneric(u64, total_size, @alignOf(u64)); + const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); + const bind_size = try bind.bindInfoSize(bind_pointers.items); + const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); + const export_size = trie.size; - if (needed_size > allocated_size) { - dyld_info.rebase_off = 0; - dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null)); - } + dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); + dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64))); + seg.inner.filesize += dyld_info.rebase_size; - dyld_info.rebase_size = rebase_size; dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size; - dyld_info.bind_size = bind_size; + dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, bind_size, @alignOf(u64))); + seg.inner.filesize += dyld_info.bind_size; + dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size; - dyld_info.lazy_bind_size = lazy_bind_size; + dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64))); + seg.inner.filesize += dyld_info.lazy_bind_size; + dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; - dyld_info.export_size = export_size; + dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, export_size, @alignOf(u64))); + seg.inner.filesize += dyld_info.export_size; + const needed_size = dyld_info.rebase_size + dyld_info.bind_size + dyld_info.lazy_bind_size + dyld_info.export_size; var buffer = try self.base.allocator.alloc(u8, needed_size); defer self.base.allocator.free(buffer); mem.set(u8, buffer, 0); @@ -4576,16 +4112,26 @@ fn writeDyldInfoData(self: *MachO) !void { const writer = stream.writer(); try bind.writeRebaseInfo(rebase_pointers.items, writer); + try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size)); + try bind.writeBindInfo(bind_pointers.items, writer); + try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size)); + try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); + try stream.seekBy(@intCast(i64, dyld_info.lazy_bind_size) - @intCast(i64, lazy_bind_size)); + _ = try trie.write(writer); - log.debug("writing dyld info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + needed_size }); + log.debug("writing dyld info from 0x{x} to 0x{x}", .{ + dyld_info.rebase_off, + dyld_info.rebase_off + needed_size, + }); try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); - try self.populateLazyBindOffsetsInStubHelper(buffer[rebase_size + bind_size ..][0..lazy_bind_size]); + try self.populateLazyBindOffsetsInStubHelper( + buffer[dyld_info.rebase_size + dyld_info.bind_size ..][0..dyld_info.lazy_bind_size], + ); self.load_commands_dirty = true; - self.dyld_info_dirty = false; } fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { @@ -4661,7 +4207,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { const sym = self.locals.items[atom.local_sym_index]; const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset; mem.writeIntLittle(u32, &buf, bind_offset); - log.debug("writing lazy binding offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{ + log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{ bind_offset, self.getString(sym.n_strx), file_offset, @@ -4674,79 +4220,307 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { } } -fn writeStringTable(self: *MachO) !void { - if (!self.strtab_dirty) return; +fn writeDices(self: *MachO) !void { + if (!self.has_dices) return; const tracy = trace(@src()); defer tracy.end(); + var buf = std.ArrayList(u8).init(self.base.allocator); + defer buf.deinit(); + + var block: *TextBlock = self.blocks.get(.{ + .seg = self.text_segment_cmd_index orelse return, + .sect = self.text_section_index orelse return, + }) orelse return; + + while (block.prev) |prev| { + block = prev; + } + + const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const text_sect = text_seg.sections.items[self.text_section_index.?]; + + while (true) { + if (block.dices.items.len > 0) { + const sym = self.locals.items[block.local_sym_index]; + const base_off = try math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset); + + try buf.ensureUnusedCapacity(block.dices.items.len * @sizeOf(macho.data_in_code_entry)); + for (block.dices.items) |dice| { + const rebased_dice = macho.data_in_code_entry{ + .offset = base_off + dice.offset, + .length = dice.length, + .kind = dice.kind, + }; + buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice)); + } + } + + if (block.next) |next| { + block = next; + } else break; + } + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].LinkeditData; + const needed_size = @intCast(u32, buf.items.len); + + dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + dice_cmd.datasize = needed_size; + seg.inner.filesize += needed_size; + + log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ + dice_cmd.dataoff, + dice_cmd.dataoff + dice_cmd.datasize, + }); + + try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff); + self.load_commands_dirty = true; +} + +fn writeSymbolTable(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; - const allocated_size = self.allocatedSizeLinkedit(symtab.stroff); - const needed_size = mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64)); - - if (needed_size > allocated_size or self.strtab_needs_relocation) { - symtab.strsize = 0; - symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff)); - self.strtab_needs_relocation = false; - } - symtab.strsize = @intCast(u32, needed_size); - log.debug("writing string table from 0x{x} to 0x{x}", .{ - symtab.stroff, - symtab.stroff + symtab.strsize, + symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + + var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); + defer locals.deinit(); + try locals.appendSlice(self.locals.items); + + if (self.has_stabs) { + for (self.objects.items) |object| { + if (object.debug_info == null) continue; + + // Open scope + try locals.ensureUnusedCapacity(3); + locals.appendAssumeCapacity(.{ + .n_strx = try self.makeString(object.tu_comp_dir.?), + .n_type = macho.N_SO, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + locals.appendAssumeCapacity(.{ + .n_strx = try self.makeString(object.tu_name.?), + .n_type = macho.N_SO, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + locals.appendAssumeCapacity(.{ + .n_strx = try self.makeString(object.name), + .n_type = macho.N_OSO, + .n_sect = 0, + .n_desc = 1, + .n_value = object.mtime orelse 0, + }); + + for (object.text_blocks.items) |block| { + if (block.stab) |stab| { + const nlists = try stab.asNlists(block.local_sym_index, self); + defer self.base.allocator.free(nlists); + try locals.appendSlice(nlists); + } else { + for (block.contained.items) |sym_at_off| { + const stab = sym_at_off.stab orelse continue; + const nlists = try stab.asNlists(sym_at_off.local_sym_index, self); + defer self.base.allocator.free(nlists); + try locals.appendSlice(nlists); + } + } + } + + // Close scope + try locals.append(.{ + .n_strx = 0, + .n_type = macho.N_SO, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + } + } + + const nlocals = locals.items.len; + const nexports = self.globals.items.len; + const nundefs = self.undefs.items.len; + + const locals_off = symtab.symoff; + const locals_size = nlocals * @sizeOf(macho.nlist_64); + log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off); + + const exports_off = locals_off + locals_size; + const exports_size = nexports * @sizeOf(macho.nlist_64); + log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off); + + const undefs_off = exports_off + exports_size; + const undefs_size = nundefs * @sizeOf(macho.nlist_64); + log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undefs.items), undefs_off); + + symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs); + seg.inner.filesize += locals_size + exports_size + undefs_size; + + // Update dynamic symbol table. + const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; + dysymtab.nlocalsym = @intCast(u32, nlocals); + dysymtab.iextdefsym = dysymtab.nlocalsym; + dysymtab.nextdefsym = @intCast(u32, nexports); + dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym; + dysymtab.nundefsym = @intCast(u32, nundefs); + + const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = &text_segment.sections.items[self.stubs_section_index.?]; + const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const got = &data_const_segment.sections.items[self.got_section_index.?]; + const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?]; + + const nstubs = @intCast(u32, self.stubs_map.keys().len); + const ngot_entries = @intCast(u32, self.got_entries_map.keys().len); + + dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; + + const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); + seg.inner.filesize += needed_size; + + log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ + dysymtab.indirectsymoff, + dysymtab.indirectsymoff + needed_size, }); + var buf = try self.base.allocator.alloc(u8, needed_size); + defer self.base.allocator.free(buf); + + var stream = std.io.fixedBufferStream(buf); + var writer = stream.writer(); + + stubs.reserved1 = 0; + for (self.stubs_map.keys()) |key| { + try writer.writeIntLittle(u32, dysymtab.iundefsym + key); + } + + got.reserved1 = nstubs; + for (self.got_entries_map.keys()) |key| { + switch (key.where) { + .undef => { + try writer.writeIntLittle(u32, dysymtab.iundefsym + key.where_index); + }, + .local => { + try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); + }, + } + } + + la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries; + for (self.stubs_map.keys()) |key| { + try writer.writeIntLittle(u32, dysymtab.iundefsym + key); + } + + try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); + self.load_commands_dirty = true; +} + +fn writeStringTable(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; + symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); + symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); + seg.inner.filesize += symtab.strsize; + + log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); + try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); + + if (symtab.strsize > self.strtab.items.len) { + // This is potentially the last section, so we need to pad it out. + try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); + } self.load_commands_dirty = true; - self.strtab_dirty = false; } -fn updateLinkeditSegmentSizes(self: *MachO) !void { - if (!self.load_commands_dirty) return; +fn writeLinkeditSegment(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + seg.inner.filesize = 0; + + try self.writeDyldInfoData(); + try self.writeDices(); + try self.writeSymbolTable(); + try self.writeStringTable(); + + seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); +} +fn writeCodeSignaturePadding(self: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); - // Now, we are in position to update __LINKEDIT segment sizes. - // TODO Add checkpointing so that we don't have to do this every single time. const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; - var final_offset = linkedit_segment.inner.fileoff; - - if (self.dyld_info_cmd_index) |idx| { - const dyld_info = self.load_commands.items[idx].DyldInfoOnly; - final_offset = std.math.max(final_offset, dyld_info.rebase_off + dyld_info.rebase_size); - final_offset = std.math.max(final_offset, dyld_info.bind_off + dyld_info.bind_size); - final_offset = std.math.max(final_offset, dyld_info.weak_bind_off + dyld_info.weak_bind_size); - final_offset = std.math.max(final_offset, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size); - final_offset = std.math.max(final_offset, dyld_info.export_off + dyld_info.export_size); - } - if (self.function_starts_cmd_index) |idx| { - const fstart = self.load_commands.items[idx].LinkeditData; - final_offset = std.math.max(final_offset, fstart.dataoff + fstart.datasize); - } - if (self.data_in_code_cmd_index) |idx| { - const dic = self.load_commands.items[idx].LinkeditData; - final_offset = std.math.max(final_offset, dic.dataoff + dic.datasize); - } - if (self.dysymtab_cmd_index) |idx| { - const dysymtab = self.load_commands.items[idx].Dysymtab; - const nindirectsize = dysymtab.nindirectsyms * @sizeOf(u32); - final_offset = std.math.max(final_offset, dysymtab.indirectsymoff + nindirectsize); - // TODO Handle more dynamic symbol table sections. - } - if (self.symtab_cmd_index) |idx| { - const symtab = self.load_commands.items[idx].Symtab; - const symsize = symtab.nsyms * @sizeOf(macho.nlist_64); - final_offset = std.math.max(final_offset, symtab.symoff + symsize); - final_offset = std.math.max(final_offset, symtab.stroff + symtab.strsize); - } - - const filesize = final_offset - linkedit_segment.inner.fileoff; - linkedit_segment.inner.filesize = filesize; - linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, filesize, self.page_size); - try self.base.file.?.pwriteAll(&[_]u8{0}, final_offset); + const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; + const fileoff = linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize; + const needed_size = CodeSignature.calcCodeSignaturePaddingSize( + self.base.options.emit.?.sub_path, + fileoff, + self.page_size, + ); + code_sig_cmd.dataoff = @intCast(u32, fileoff); + code_sig_cmd.datasize = needed_size; + + // Advance size of __LINKEDIT segment + linkedit_segment.inner.filesize += needed_size; + if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { + linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); + } + log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + 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.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); self.load_commands_dirty = true; } +fn writeCodeSignature(self: *MachO) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].LinkeditData; + + var code_sig: CodeSignature = .{}; + defer code_sig.deinit(self.base.allocator); + + try code_sig.calcAdhocSignature( + self.base.allocator, + self.base.file.?, + self.base.options.emit.?.sub_path, + text_segment.inner, + code_sig_cmd, + self.base.options.output_mode, + self.page_size, + ); + + var buffer = try self.base.allocator.alloc(u8, code_sig.size()); + defer self.base.allocator.free(buffer); + var stream = std.io.fixedBufferStream(buffer); + try code_sig.write(stream.writer()); + + log.debug("writing code signature from 0x{x} to 0x{x}", .{ code_sig_cmd.dataoff, code_sig_cmd.dataoff + buffer.len }); + + try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); +} + /// Writes all load commands and section headers. fn writeLoadCommands(self: *MachO) !void { if (!self.load_commands_dirty) return; diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig index e6ceed9c55..160ba5cd8c 100644 --- a/src/link/MachO/TextBlock.zig +++ b/src/link/MachO/TextBlock.zig @@ -844,7 +844,6 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R .sect = context.macho_file.got_section_index.?, }; _ = try context.macho_file.allocateAtom(atom, match); - context.macho_file.dyld_info_dirty = true; } else if (parsed_rel.payload == .unsigned) { switch (parsed_rel.where) { .undef => { |
