authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-29 08:05:32+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-01 22:05:22+02:00
logb79884eaf003ad32e800213c20da5c6b8935af34
treef596eba3c20978c557c87db40b06076377812153
parentee01dd40322db39dcbb0e791d0885d6da9f4150a

macho: implement pruning of unused segments and sections

This is a prelude to a more elaborate work which will implement `-dead_strip` flag - garbage collection of unreachable atoms. Here, when sorting sections, we also check that the section is actually populated with some atoms, and if not, we exclude it from the final linked image. This can happen when we do not import any symbols from dynamic libraries in which case we will not be populating the stubs sections or the GOT table, implying we can skip allocating those sections. Furthermore, we also make a check that a segment is actually occupied too, with the exception of `__TEXT` segment which is non-optional given that it wraps the header and load commands and thus is required by the `dyld` to perform dynamic linking, and `__PAGEZERO` which is generally non-optional when the linked image is an executable. For any other segment, if its section count is zero, we mark it as dead and skip allocating it and generating a load command for it. This commit also includes some minor improvements to the linker such as refactoring of the segment allocating codepaths, skipping `__PAGEZERO` generation for dylibs, and skipping generation of zero-sized atoms for special symbols such as `__mh_execute_header` and `___dso_handle`. These special symbols are only allocated local and global symbol pair and their VM addresses is set to the start of the `__TEXT` segment, but no `Atom` is created, as it's not necessary given that they never carry any machine code. Finally, we now always force-link against `libSystem` which turns out to be required for `dyld` to properly handle `LC_MAIN` load command on older macOS versions such as 10.15.7.

3 files changed, 339 insertions(+), 335 deletions(-)

lib/std/macho.zig+3
......@@ -912,6 +912,9 @@ pub const relocation_info = packed struct {
912912pub const LC_REQ_DYLD = 0x80000000;
913913
914914pub const LC = enum(u32) {
915 /// No load command - invalid
916 NONE = 0x0,
917
915918 /// segment of this file to be mapped
916919 SEGMENT = 0x1,
917920
src/link/MachO.zig+332-332
......@@ -164,11 +164,13 @@ tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
164164locals_free_list: std.ArrayListUnmanaged(u32) = .{},
165165globals_free_list: std.ArrayListUnmanaged(u32) = .{},
166166
167mh_execute_header_index: ?u32 = null,
168167dyld_stub_binder_index: ?u32 = null,
169168dyld_private_atom: ?*Atom = null,
170169stub_helper_preamble_atom: ?*Atom = null,
171170
171mh_execute_header_sym_index: ?u32 = null,
172dso_handle_sym_index: ?u32 = null,
173
172174strtab: std.ArrayListUnmanaged(u8) = .{},
173175strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
174176
......@@ -856,7 +858,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
856858 // re-exports every single symbol definition.
857859 for (lib_dirs.items) |dir| {
858860 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
859 try libs.put(full_path, .{ .needed = false });
861 try libs.put(full_path, .{ .needed = true });
860862 libsystem_available = true;
861863 break :blk;
862864 }
......@@ -866,8 +868,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
866868 for (lib_dirs.items) |dir| {
867869 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
868870 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
869 try libs.put(libsystem_path, .{ .needed = false });
870 try libs.put(libc_path, .{ .needed = false });
871 try libs.put(libsystem_path, .{ .needed = true });
872 try libs.put(libc_path, .{ .needed = true });
871873 libsystem_available = true;
872874 break :blk;
873875 }
......@@ -881,7 +883,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
881883 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
882884 "libc", "darwin", libsystem_name,
883885 });
884 try libs.put(full_path, .{ .needed = false });
886 try libs.put(full_path, .{ .needed = true });
885887 }
886888
887889 // frameworks
......@@ -1090,7 +1092,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
10901092 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
10911093 }
10921094
1093 try self.createMhExecuteHeaderAtom();
1095 try self.createMhExecuteHeaderSymbol();
10941096 for (self.objects.items) |*object, object_id| {
10951097 if (object.analyzed) continue;
10961098 try self.resolveSymbolsInObject(@intCast(u16, object_id));
......@@ -1101,7 +1103,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11011103 try self.createDyldPrivateAtom();
11021104 try self.createStubHelperPreambleAtom();
11031105 try self.resolveSymbolsInDylibs();
1104 try self.createDsoHandleAtom();
1106 try self.createDsoHandleSymbol();
11051107 try self.addCodeSignatureLC();
11061108
11071109 {
......@@ -1156,14 +1158,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11561158
11571159 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
11581160 if (use_llvm or use_stage1) {
1159 try self.sortSections();
1160 try self.allocateTextSegment();
1161 try self.allocateDataConstSegment();
1162 try self.allocateDataSegment();
1163 self.allocateLinkeditSegment();
1161 try self.pruneAndSortSections();
1162 try self.allocateSegments();
11641163 try self.allocateLocals();
11651164 }
11661165
1166 try self.allocateSpecialSymbols();
11671167 try self.allocateGlobals();
11681168
11691169 if (build_options.enable_logging) {
......@@ -2261,6 +2261,27 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
22612261 }
22622262}
22632263
2264fn allocateSpecialSymbols(self: *MachO) !void {
2265 for (&[_]?u32{
2266 self.mh_execute_header_sym_index,
2267 self.dso_handle_sym_index,
2268 }) |maybe_sym_index| {
2269 const sym_index = maybe_sym_index orelse continue;
2270 const sym = &self.locals.items[sym_index];
2271 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2272 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{
2273 .seg = self.text_segment_cmd_index.?,
2274 .sect = 0,
2275 }).? + 1);
2276 sym.n_value = seg.inner.vmaddr;
2277
2278 log.debug("allocating {s} at the start of {s}", .{
2279 self.getString(sym.n_strx),
2280 seg.inner.segName(),
2281 });
2282 }
2283}
2284
22642285fn allocateGlobals(self: *MachO) !void {
22652286 log.debug("allocating global symbols", .{});
22662287
......@@ -2442,7 +2463,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
24422463}
24432464
24442465fn createDyldPrivateAtom(self: *MachO) !void {
2466 if (self.dyld_stub_binder_index == null) return;
24452467 if (self.dyld_private_atom != null) return;
2468
24462469 const local_sym_index = @intCast(u32, self.locals.items.len);
24472470 const sym = try self.locals.addOne(self.base.allocator);
24482471 sym.* = .{
......@@ -2468,7 +2491,9 @@ fn createDyldPrivateAtom(self: *MachO) !void {
24682491}
24692492
24702493fn createStubHelperPreambleAtom(self: *MachO) !void {
2494 if (self.dyld_stub_binder_index == null) return;
24712495 if (self.stub_helper_preamble_atom != null) return;
2496
24722497 const arch = self.base.options.target.cpu.arch;
24732498 const size: u64 = switch (arch) {
24742499 .x86_64 => 15,
......@@ -2816,57 +2841,46 @@ fn createTentativeDefAtoms(self: *MachO) !void {
28162841 }
28172842}
28182843
2819fn createDsoHandleAtom(self: *MachO) !void {
2820 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
2844fn createDsoHandleSymbol(self: *MachO) !void {
2845 if (self.dso_handle_sym_index != null) return;
2846
2847 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
28212848 .bytes = &self.strtab,
2822 })) |n_strx| blk: {
2823 const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk;
2824 if (resolv.where != .undef) break :blk;
2849 }) orelse return;
28252850
2826 const undef = &self.undefs.items[resolv.where_index];
2827 const match: MatchingSection = .{
2828 .seg = self.text_segment_cmd_index.?,
2829 .sect = self.text_section_index.?,
2830 };
2831 const local_sym_index = @intCast(u32, self.locals.items.len);
2832 var nlist = macho.nlist_64{
2833 .n_strx = undef.n_strx,
2834 .n_type = macho.N_SECT,
2835 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1),
2836 .n_desc = 0,
2837 .n_value = 0,
2838 };
2839 try self.locals.append(self.base.allocator, nlist);
2840 const global_sym_index = @intCast(u32, self.globals.items.len);
2841 nlist.n_type |= macho.N_EXT;
2842 nlist.n_desc = macho.N_WEAK_DEF;
2843 try self.globals.append(self.base.allocator, nlist);
2851 const resolv = self.symbol_resolver.getPtr(n_strx) orelse return;
2852 if (resolv.where != .undef) return;
28442853
2845 assert(self.unresolved.swapRemove(resolv.where_index));
2854 const undef = &self.undefs.items[resolv.where_index];
2855 const local_sym_index = @intCast(u32, self.locals.items.len);
2856 var nlist = macho.nlist_64{
2857 .n_strx = undef.n_strx,
2858 .n_type = macho.N_SECT,
2859 .n_sect = 0,
2860 .n_desc = 0,
2861 .n_value = 0,
2862 };
2863 try self.locals.append(self.base.allocator, nlist);
2864 const global_sym_index = @intCast(u32, self.globals.items.len);
2865 nlist.n_type |= macho.N_EXT;
2866 nlist.n_desc = macho.N_WEAK_DEF;
2867 try self.globals.append(self.base.allocator, nlist);
2868 self.dso_handle_sym_index = local_sym_index;
28462869
2847 undef.* = .{
2848 .n_strx = 0,
2849 .n_type = macho.N_UNDF,
2850 .n_sect = 0,
2851 .n_desc = 0,
2852 .n_value = 0,
2853 };
2854 resolv.* = .{
2855 .where = .global,
2856 .where_index = global_sym_index,
2857 .local_sym_index = local_sym_index,
2858 };
2870 assert(self.unresolved.swapRemove(resolv.where_index));
28592871
2860 // We create an empty atom for this symbol.
2861 // TODO perhaps we should special-case special symbols? Create a separate
2862 // linked list of atoms?
2863 const atom = try self.createEmptyAtom(local_sym_index, 0, 0);
2864 if (self.needs_prealloc) {
2865 const sym = &self.locals.items[local_sym_index];
2866 const vaddr = try self.allocateAtom(atom, 0, 1, match);
2867 sym.n_value = vaddr;
2868 } else try self.addAtomToSection(atom, match);
2869 }
2872 undef.* = .{
2873 .n_strx = 0,
2874 .n_type = macho.N_UNDF,
2875 .n_sect = 0,
2876 .n_desc = 0,
2877 .n_value = 0,
2878 };
2879 resolv.* = .{
2880 .where = .global,
2881 .where_index = global_sym_index,
2882 .local_sym_index = local_sym_index,
2883 };
28702884}
28712885
28722886fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
......@@ -3183,27 +3197,21 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31833197 }
31843198}
31853199
3186fn createMhExecuteHeaderAtom(self: *MachO) !void {
3187 if (self.mh_execute_header_index != null) return;
3188
3189 const match: MatchingSection = .{
3190 .seg = self.text_segment_cmd_index.?,
3191 .sect = self.text_section_index.?,
3192 };
3193 const seg = self.load_commands.items[match.seg].segment;
3194 const sect = seg.sections.items[match.sect];
3200fn createMhExecuteHeaderSymbol(self: *MachO) !void {
3201 if (self.base.options.output_mode != .Exe) return;
3202 if (self.mh_execute_header_sym_index != null) return;
31953203
31963204 const n_strx = try self.makeString("__mh_execute_header");
31973205 const local_sym_index = @intCast(u32, self.locals.items.len);
31983206 var nlist = macho.nlist_64{
31993207 .n_strx = n_strx,
32003208 .n_type = macho.N_SECT,
3201 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1),
3209 .n_sect = 0,
32023210 .n_desc = 0,
3203 .n_value = sect.addr,
3211 .n_value = 0,
32043212 };
32053213 try self.locals.append(self.base.allocator, nlist);
3206 self.mh_execute_header_index = local_sym_index;
3214 self.mh_execute_header_sym_index = local_sym_index;
32073215
32083216 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
32093217 const global = &self.globals.items[resolv.where_index];
......@@ -3223,23 +3231,11 @@ fn createMhExecuteHeaderAtom(self: *MachO) !void {
32233231 .file = null,
32243232 });
32253233 }
3226
3227 // We always set the __mh_execute_header to point to the beginning of the __TEXT,__text section
3228 const atom = try self.createEmptyAtom(local_sym_index, 0, 0);
3229 if (self.atoms.get(match)) |last| {
3230 var first = last;
3231 while (first.prev) |prev| {
3232 first = prev;
3233 }
3234 atom.next = first;
3235 first.prev = atom;
3236 } else {
3237 try self.atoms.putNoClobber(self.base.allocator, match, atom);
3238 }
32393234}
32403235
32413236fn resolveDyldStubBinder(self: *MachO) !void {
32423237 if (self.dyld_stub_binder_index != null) return;
3238 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
32433239
32443240 const n_strx = try self.makeString("dyld_stub_binder");
32453241 const sym_index = @intCast(u32, self.undefs.items.len);
......@@ -3295,7 +3291,12 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32953291 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
32963292 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
32973293 atom_sym.n_value = vaddr;
3298 } else try self.addAtomToSection(atom, match);
3294 } else {
3295 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
3296 const sect = &seg.sections.items[self.got_section_index.?];
3297 sect.size += atom.size;
3298 try self.addAtomToSection(atom, match);
3299 }
32993300
33003301 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
33013302}
......@@ -4512,6 +4513,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45124513 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
45134514
45144515 if (self.pagezero_segment_cmd_index == null) blk: {
4516 if (self.base.options.output_mode == .Lib) break :blk;
45154517 if (aligned_pagezero_vmsize == 0) break :blk;
45164518 if (aligned_pagezero_vmsize != pagezero_vmsize) {
45174519 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
......@@ -4636,9 +4638,9 @@ fn populateMissingMetadata(self: *MachO) !void {
46364638 var fileoff: u64 = 0;
46374639 var needed_size: u64 = 0;
46384640 if (self.needs_prealloc) {
4639 const address_and_offset = self.nextSegmentAddressAndOffset();
4640 vmaddr = address_and_offset.address;
4641 fileoff = address_and_offset.offset;
4641 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
4642 vmaddr = base.vmaddr;
4643 fileoff = base.fileoff;
46424644 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
46434645 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
46444646 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
......@@ -4686,9 +4688,9 @@ fn populateMissingMetadata(self: *MachO) !void {
46864688 var fileoff: u64 = 0;
46874689 var needed_size: u64 = 0;
46884690 if (self.needs_prealloc) {
4689 const address_and_offset = self.nextSegmentAddressAndOffset();
4690 vmaddr = address_and_offset.address;
4691 fileoff = address_and_offset.offset;
4691 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
4692 vmaddr = base.vmaddr;
4693 fileoff = base.fileoff;
46924694 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
46934695 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
46944696 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{
......@@ -4803,9 +4805,9 @@ fn populateMissingMetadata(self: *MachO) !void {
48034805 var vmaddr: u64 = 0;
48044806 var fileoff: u64 = 0;
48054807 if (self.needs_prealloc) {
4806 const address_and_offset = self.nextSegmentAddressAndOffset();
4807 vmaddr = address_and_offset.address;
4808 fileoff = address_and_offset.offset;
4808 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
4809 vmaddr = base.vmaddr;
4810 fileoff = base.fileoff;
48094811 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
48104812 }
48114813 try self.load_commands.append(self.base.allocator, .{
......@@ -5030,17 +5032,10 @@ fn populateMissingMetadata(self: *MachO) !void {
50305032 self.cold_start = true;
50315033}
50325034
5033fn allocateTextSegment(self: *MachO) !void {
5034 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
5035 const base_vmaddr = if (self.pagezero_segment_cmd_index) |index|
5036 self.load_commands.items[index].segment.inner.vmsize
5037 else
5038 0;
5039 seg.inner.fileoff = 0;
5040 seg.inner.vmaddr = base_vmaddr;
5041
5035fn calcMinHeaderpad(self: *MachO) u64 {
50425036 var sizeofcmds: u32 = 0;
50435037 for (self.load_commands.items) |lc| {
5038 if (lc.cmd() == .NONE) continue;
50445039 sizeofcmds += lc.cmdsize();
50455040 }
50465041
......@@ -5067,60 +5062,74 @@ fn allocateTextSegment(self: *MachO) !void {
50675062 }
50685063 const offset = @sizeOf(macho.mach_header_64) + padding;
50695064 log.debug("actual headerpad size 0x{x}", .{offset});
5070 try self.allocateSegment(self.text_segment_cmd_index.?, offset);
50715065
5072 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
5073 var min_alignment: u32 = 0;
5074 for (seg.sections.items) |sect| {
5075 const alignment = try math.powi(u32, 2, sect.@"align");
5076 min_alignment = math.max(min_alignment, alignment);
5077 }
5066 return offset;
5067}
50785068
5079 assert(min_alignment > 0);
5080 const last_sect_idx = seg.sections.items.len - 1;
5081 const last_sect = seg.sections.items[last_sect_idx];
5082 const shift: u32 = blk: {
5083 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
5084 const factor = @divTrunc(diff, min_alignment);
5085 break :blk @intCast(u32, factor * min_alignment);
5086 };
5069fn allocateSegments(self: *MachO) !void {
5070 try self.allocateSegment(self.text_segment_cmd_index, &.{
5071 self.pagezero_segment_cmd_index,
5072 }, self.calcMinHeaderpad());
5073
5074 if (self.text_segment_cmd_index) |index| blk: {
5075 const seg = &self.load_commands.items[index].segment;
5076 if (seg.sections.items.len == 0) break :blk;
50875077
5088 if (shift > 0) {
5089 for (seg.sections.items) |*sect| {
5090 sect.offset += shift;
5091 sect.addr += shift;
5078 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
5079 var min_alignment: u32 = 0;
5080 for (seg.sections.items) |sect| {
5081 const alignment = try math.powi(u32, 2, sect.@"align");
5082 min_alignment = math.max(min_alignment, alignment);
5083 }
5084
5085 assert(min_alignment > 0);
5086 const last_sect_idx = seg.sections.items.len - 1;
5087 const last_sect = seg.sections.items[last_sect_idx];
5088 const shift: u32 = shift: {
5089 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;
5090 const factor = @divTrunc(diff, min_alignment);
5091 break :shift @intCast(u32, factor * min_alignment);
5092 };
5093
5094 if (shift > 0) {
5095 for (seg.sections.items) |*sect| {
5096 sect.offset += shift;
5097 sect.addr += shift;
5098 }
50925099 }
50935100 }
5094}
50955101
5096fn allocateDataConstSegment(self: *MachO) !void {
5097 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
5098 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5099 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;
5100 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
5101 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
5102}
5102 try self.allocateSegment(self.data_const_segment_cmd_index, &.{
5103 self.text_segment_cmd_index,
5104 self.pagezero_segment_cmd_index,
5105 }, 0);
51035106
5104fn allocateDataSegment(self: *MachO) !void {
5105 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
5106 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
5107 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;
5108 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;
5109 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
5110}
5107 try self.allocateSegment(self.data_segment_cmd_index, &.{
5108 self.data_const_segment_cmd_index,
5109 self.text_segment_cmd_index,
5110 self.pagezero_segment_cmd_index,
5111 }, 0);
51115112
5112fn allocateLinkeditSegment(self: *MachO) void {
5113 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
5114 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
5115 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;
5116 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;
5113 try self.allocateSegment(self.linkedit_segment_cmd_index, &.{
5114 self.data_segment_cmd_index,
5115 self.data_const_segment_cmd_index,
5116 self.text_segment_cmd_index,
5117 self.pagezero_segment_cmd_index,
5118 }, 0);
51175119}
51185120
5119fn allocateSegment(self: *MachO, index: u16, offset: u64) !void {
5121fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_size: u64) !void {
5122 const index = maybe_index orelse return;
51205123 const seg = &self.load_commands.items[index].segment;
51215124
5125 const base = self.getSegmentAllocBase(indices);
5126 seg.inner.vmaddr = base.vmaddr;
5127 seg.inner.fileoff = base.fileoff;
5128 seg.inner.filesize = init_size;
5129 seg.inner.vmsize = init_size;
5130
51225131 // Allocate the sections according to their alignment at the beginning of the segment.
5123 var start: u64 = offset;
5132 var start = init_size;
51245133 for (seg.sections.items) |*sect, sect_id| {
51255134 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
51265135 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
......@@ -5524,6 +5533,9 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
55245533 } else {
55255534 try self.atoms.putNoClobber(self.base.allocator, match, atom);
55265535 }
5536 const seg = &self.load_commands.items[match.seg].segment;
5537 const sect = &seg.sections.items[match.sect];
5538 sect.size += atom.size;
55275539}
55285540
55295541pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
......@@ -5551,180 +5563,145 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
55515563 return n_strx;
55525564}
55535565
5554const NextSegmentAddressAndOffset = struct {
5555 address: u64,
5556 offset: u64,
5557};
5558
5559fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
5560 var prev_segment_idx: ?usize = null; // We use optional here for safety.
5561 for (self.load_commands.items) |cmd, i| {
5562 if (cmd == .segment) {
5563 prev_segment_idx = i;
5564 }
5566fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
5567 for (indices) |maybe_prev_id| {
5568 const prev_id = maybe_prev_id orelse continue;
5569 const prev = self.load_commands.items[prev_id].segment;
5570 return .{
5571 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,
5572 .fileoff = prev.inner.fileoff + prev.inner.filesize,
5573 };
55655574 }
5566 const prev_segment = self.load_commands.items[prev_segment_idx.?].segment;
5567 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
5568 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
5569 return .{
5570 .address = address,
5571 .offset = offset,
5572 };
5575 return .{ .vmaddr = 0, .fileoff = 0 };
55735576}
55745577
5575fn sortSections(self: *MachO) !void {
5576 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
5577 defer text_index_mapping.deinit();
5578 var data_const_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
5579 defer data_const_index_mapping.deinit();
5580 var data_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);
5581 defer data_index_mapping.deinit();
5578fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {
5579 const seg_id = maybe_seg_id.* orelse return;
55825580
5583 {
5584 // __TEXT segment
5585 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
5586 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5587 defer self.base.allocator.free(sections);
5588 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5589
5590 const indices = &[_]*?u16{
5591 &self.text_section_index,
5592 &self.stubs_section_index,
5593 &self.stub_helper_section_index,
5594 &self.gcc_except_tab_section_index,
5595 &self.cstring_section_index,
5596 &self.ustring_section_index,
5597 &self.text_const_section_index,
5598 &self.objc_methlist_section_index,
5599 &self.objc_methname_section_index,
5600 &self.objc_methtype_section_index,
5601 &self.objc_classname_section_index,
5602 &self.eh_frame_section_index,
5603 };
5604 for (indices) |maybe_index| {
5605 const new_index: u16 = if (maybe_index.*) |index| blk: {
5606 const idx = @intCast(u16, seg.sections.items.len);
5607 seg.sections.appendAssumeCapacity(sections[index]);
5608 try text_index_mapping.putNoClobber(index, idx);
5609 break :blk idx;
5610 } else continue;
5611 maybe_index.* = new_index;
5581 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);
5582 defer mapping.deinit();
5583
5584 const seg = &self.load_commands.items[seg_id].segment;
5585 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5586 defer self.base.allocator.free(sections);
5587 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5588
5589 for (indices) |maybe_index| {
5590 const old_idx = maybe_index.* orelse continue;
5591 const sect = sections[old_idx];
5592 if (sect.size == 0) {
5593 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5594 maybe_index.* = null;
5595 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5596 seg.inner.nsects -= 1;
5597 } else {
5598 maybe_index.* = @intCast(u16, seg.sections.items.len);
5599 seg.sections.appendAssumeCapacity(sect);
56125600 }
5601 try mapping.putNoClobber(old_idx, maybe_index.*);
56135602 }
56145603
5615 {
5616 // __DATA_CONST segment
5617 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
5618 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5619 defer self.base.allocator.free(sections);
5620 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5621
5622 const indices = &[_]*?u16{
5623 &self.got_section_index,
5624 &self.mod_init_func_section_index,
5625 &self.mod_term_func_section_index,
5626 &self.data_const_section_index,
5627 &self.objc_cfstring_section_index,
5628 &self.objc_classlist_section_index,
5629 &self.objc_imageinfo_section_index,
5604 var atoms = std.ArrayList(struct { match: MatchingSection, atom: *Atom }).init(self.base.allocator);
5605 defer atoms.deinit();
5606 try atoms.ensureTotalCapacity(mapping.count());
5607
5608 for (mapping.keys()) |old_sect| {
5609 const new_sect = mapping.get(old_sect).? orelse {
5610 _ = self.atoms.remove(.{ .seg = seg_id, .sect = old_sect });
5611 continue;
56305612 };
5631 for (indices) |maybe_index| {
5632 const new_index: u16 = if (maybe_index.*) |index| blk: {
5633 const idx = @intCast(u16, seg.sections.items.len);
5634 seg.sections.appendAssumeCapacity(sections[index]);
5635 try data_const_index_mapping.putNoClobber(index, idx);
5636 break :blk idx;
5637 } else continue;
5638 maybe_index.* = new_index;
5639 }
5613 const kv = self.atoms.fetchRemove(.{ .seg = seg_id, .sect = old_sect }).?;
5614 atoms.appendAssumeCapacity(.{
5615 .match = .{ .seg = seg_id, .sect = new_sect },
5616 .atom = kv.value,
5617 });
56405618 }
56415619
5642 {
5643 // __DATA segment
5644 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
5645 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5646 defer self.base.allocator.free(sections);
5647 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5648
5649 // __DATA segment
5650 const indices = &[_]*?u16{
5651 &self.rustc_section_index,
5652 &self.la_symbol_ptr_section_index,
5653 &self.objc_const_section_index,
5654 &self.objc_selrefs_section_index,
5655 &self.objc_classrefs_section_index,
5656 &self.objc_data_section_index,
5657 &self.data_section_index,
5658 &self.tlv_section_index,
5659 &self.tlv_ptrs_section_index,
5660 &self.tlv_data_section_index,
5661 &self.tlv_bss_section_index,
5662 &self.bss_section_index,
5663 };
5664 for (indices) |maybe_index| {
5665 const new_index: u16 = if (maybe_index.*) |index| blk: {
5666 const idx = @intCast(u16, seg.sections.items.len);
5667 seg.sections.appendAssumeCapacity(sections[index]);
5668 try data_index_mapping.putNoClobber(index, idx);
5669 break :blk idx;
5670 } else continue;
5671 maybe_index.* = new_index;
5672 }
5620 while (atoms.popOrNull()) |next| {
5621 try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom);
56735622 }
56745623
5675 {
5676 var transient: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{};
5677 try transient.ensureTotalCapacity(self.base.allocator, self.atoms.count());
5624 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5625 // Segment has now become empty, so mark it as such
5626 log.debug("marking segment {s} as dead", .{seg.inner.segName()});
5627 seg.inner.cmd = @intToEnum(macho.LC, 0);
5628 maybe_seg_id.* = null;
5629 }
5630}
56785631
5679 var it = self.atoms.iterator();
5680 while (it.next()) |entry| {
5681 const old = entry.key_ptr.*;
5682 const sect = if (old.seg == self.text_segment_cmd_index.?)
5683 text_index_mapping.get(old.sect).?
5684 else if (old.seg == self.data_const_segment_cmd_index.?)
5685 data_const_index_mapping.get(old.sect).?
5686 else
5687 data_index_mapping.get(old.sect).?;
5688 transient.putAssumeCapacityNoClobber(.{
5689 .seg = old.seg,
5690 .sect = sect,
5691 }, entry.value_ptr.*);
5692 }
5632fn pruneAndSortSections(self: *MachO) !void {
5633 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{
5634 &self.text_section_index,
5635 &self.stubs_section_index,
5636 &self.stub_helper_section_index,
5637 &self.gcc_except_tab_section_index,
5638 &self.cstring_section_index,
5639 &self.ustring_section_index,
5640 &self.text_const_section_index,
5641 &self.objc_methlist_section_index,
5642 &self.objc_methname_section_index,
5643 &self.objc_methtype_section_index,
5644 &self.objc_classname_section_index,
5645 &self.eh_frame_section_index,
5646 });
56935647
5694 self.atoms.clearAndFree(self.base.allocator);
5695 self.atoms.deinit(self.base.allocator);
5696 self.atoms = transient;
5697 }
5648 try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{
5649 &self.got_section_index,
5650 &self.mod_init_func_section_index,
5651 &self.mod_term_func_section_index,
5652 &self.data_const_section_index,
5653 &self.objc_cfstring_section_index,
5654 &self.objc_classlist_section_index,
5655 &self.objc_imageinfo_section_index,
5656 });
56985657
5699 {
5700 // Create new section ordinals.
5701 self.section_ordinals.clearRetainingCapacity();
5702 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5703 for (text_seg.sections.items) |_, sect_id| {
5658 try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{
5659 &self.rustc_section_index,
5660 &self.la_symbol_ptr_section_index,
5661 &self.objc_const_section_index,
5662 &self.objc_selrefs_section_index,
5663 &self.objc_classrefs_section_index,
5664 &self.objc_data_section_index,
5665 &self.data_section_index,
5666 &self.tlv_section_index,
5667 &self.tlv_ptrs_section_index,
5668 &self.tlv_data_section_index,
5669 &self.tlv_bss_section_index,
5670 &self.bss_section_index,
5671 });
5672
5673 // Create new section ordinals.
5674 self.section_ordinals.clearRetainingCapacity();
5675 if (self.text_segment_cmd_index) |seg_id| {
5676 const seg = self.load_commands.items[seg_id].segment;
5677 for (seg.sections.items) |_, sect_id| {
57045678 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5705 .seg = self.text_segment_cmd_index.?,
5679 .seg = seg_id,
57065680 .sect = @intCast(u16, sect_id),
57075681 });
57085682 assert(!res.found_existing);
57095683 }
5710 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
5711 for (data_const_seg.sections.items) |_, sect_id| {
5684 }
5685 if (self.data_const_segment_cmd_index) |seg_id| {
5686 const seg = self.load_commands.items[seg_id].segment;
5687 for (seg.sections.items) |_, sect_id| {
57125688 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5713 .seg = self.data_const_segment_cmd_index.?,
5689 .seg = seg_id,
57145690 .sect = @intCast(u16, sect_id),
57155691 });
57165692 assert(!res.found_existing);
57175693 }
5718 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
5719 for (data_seg.sections.items) |_, sect_id| {
5694 }
5695 if (self.data_segment_cmd_index) |seg_id| {
5696 const seg = self.load_commands.items[seg_id].segment;
5697 for (seg.sections.items) |_, sect_id| {
57205698 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5721 .seg = self.data_segment_cmd_index.?,
5699 .seg = seg_id,
57225700 .sect = @intCast(u16, sect_id),
57235701 });
57245702 assert(!res.found_existing);
57255703 }
57265704 }
5727
57285705 self.sections_order_dirty = false;
57295706}
57305707
......@@ -5739,12 +5716,16 @@ fn updateSectionOrdinals(self: *MachO) !void {
57395716 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
57405717
57415718 var new_ordinal: u8 = 0;
5742 for (self.load_commands.items) |lc, lc_id| {
5743 if (lc != .segment) break;
5744
5745 for (lc.segment.sections.items) |_, sect_id| {
5719 for (&[_]?u16{
5720 self.text_segment_cmd_index,
5721 self.data_const_segment_cmd_index,
5722 self.data_segment_cmd_index,
5723 }) |maybe_index| {
5724 const index = maybe_index orelse continue;
5725 const seg = self.load_commands.items[index].segment;
5726 for (seg.sections.items) |_, sect_id| {
57465727 const match = MatchingSection{
5747 .seg = @intCast(u16, lc_id),
5728 .seg = @intCast(u16, index),
57485729 .sect = @intCast(u16, sect_id),
57495730 };
57505731 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
......@@ -5783,7 +5764,9 @@ fn writeDyldInfoData(self: *MachO) !void {
57835764 const match = entry.key_ptr.*;
57845765 var atom: *Atom = entry.value_ptr.*;
57855766
5786 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
5767 if (self.text_segment_cmd_index) |seg| {
5768 if (match.seg == seg) continue; // __TEXT is non-writable
5769 }
57875770
57885771 const seg = self.load_commands.items[match.seg].segment;
57895772
......@@ -5865,6 +5848,7 @@ fn writeDyldInfoData(self: *MachO) !void {
58655848 {
58665849 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
58675850 log.debug("generating export trie", .{});
5851
58685852 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
58695853 const base_address = text_segment.inner.vmaddr;
58705854
......@@ -5957,10 +5941,13 @@ fn writeDyldInfoData(self: *MachO) !void {
59575941}
59585942
59595943fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5944 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
5945 const stub_helper_section_index = self.stub_helper_section_index orelse return;
59605946 const last_atom = self.atoms.get(.{
5961 .seg = self.text_segment_cmd_index.?,
5962 .sect = self.stub_helper_section_index.?,
5947 .seg = text_segment_cmd_index,
5948 .sect = stub_helper_section_index,
59635949 }) orelse return;
5950 if (self.stub_helper_preamble_atom == null) return;
59645951 if (last_atom == self.stub_helper_preamble_atom.?) return;
59655952
59665953 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);
......@@ -6036,8 +6023,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
60366023 }
60376024
60386025 const sect = blk: {
6039 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6040 break :blk seg.sections.items[self.stub_helper_section_index.?];
6026 const seg = self.load_commands.items[text_segment_cmd_index].segment;
6027 break :blk seg.sections.items[stub_helper_section_index];
60416028 };
60426029 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
60436030 .x86_64 => 1,
......@@ -6326,15 +6313,8 @@ fn writeSymbolTable(self: *MachO) !void {
63266313 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
63276314 dysymtab.nundefsym = @intCast(u32, nundefs);
63286315
6329 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].segment;
6330 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
6331 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
6332 const got = &data_const_segment.sections.items[self.got_section_index.?];
6333 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
6334 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
6335
6336 const nstubs = @intCast(u32, self.stubs_table.keys().len);
6337 const ngot_entries = @intCast(u32, self.got_entries_table.keys().len);
6316 const nstubs = @intCast(u32, self.stubs_table.count());
6317 const ngot_entries = @intCast(u32, self.got_entries_table.count());
63386318
63396319 const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
63406320 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);
......@@ -6352,35 +6332,50 @@ fn writeSymbolTable(self: *MachO) !void {
63526332 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
63536333 const writer = buf.writer();
63546334
6355 stubs.reserved1 = 0;
6356 for (self.stubs_table.keys()) |key| {
6357 const resolv = self.symbol_resolver.get(key).?;
6358 switch (resolv.where) {
6359 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6360 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6335 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6336 const stubs_section_index = self.stubs_section_index orelse break :blk;
6337 const text_segment = &self.load_commands.items[text_segment_cmd_index].segment;
6338 const stubs = &text_segment.sections.items[stubs_section_index];
6339 stubs.reserved1 = 0;
6340 for (self.stubs_table.keys()) |key| {
6341 const resolv = self.symbol_resolver.get(key).?;
6342 switch (resolv.where) {
6343 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6344 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6345 }
63616346 }
63626347 }
63636348
6364 got.reserved1 = nstubs;
6365 for (self.got_entries_table.keys()) |key| {
6366 switch (key) {
6367 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6368 .global => |n_strx| {
6369 const resolv = self.symbol_resolver.get(n_strx).?;
6370 switch (resolv.where) {
6371 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6372 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6373 }
6374 },
6349 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6350 const got_section_index = self.got_section_index orelse break :blk;
6351 const data_const_segment = &self.load_commands.items[data_const_segment_cmd_index].segment;
6352 const got = &data_const_segment.sections.items[got_section_index];
6353 got.reserved1 = nstubs;
6354 for (self.got_entries_table.keys()) |key| {
6355 switch (key) {
6356 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6357 .global => |n_strx| {
6358 const resolv = self.symbol_resolver.get(n_strx).?;
6359 switch (resolv.where) {
6360 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6361 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6362 }
6363 },
6364 }
63756365 }
63766366 }
63776367
6378 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;
6379 for (self.stubs_table.keys()) |key| {
6380 const resolv = self.symbol_resolver.get(key).?;
6381 switch (resolv.where) {
6382 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6383 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6368 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6369 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6370 const data_segment = &self.load_commands.items[data_segment_cmd_index].segment;
6371 const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index];
6372 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6373 for (self.stubs_table.keys()) |key| {
6374 const resolv = self.symbol_resolver.get(key).?;
6375 switch (resolv.where) {
6376 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6377 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6378 }
63846379 }
63856380 }
63866381
......@@ -6452,15 +6447,16 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
64526447 const tracy = trace(@src());
64536448 defer tracy.end();
64546449
6455 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
64566450 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;
6451 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
64576452
64586453 var buffer = std.ArrayList(u8).init(self.base.allocator);
64596454 defer buffer.deinit();
64606455 try buffer.ensureTotalCapacityPrecise(code_sig.size());
64616456 try code_sig.writeAdhocSignature(self.base.allocator, .{
64626457 .file = self.base.file.?,
6463 .text_segment = text_segment.inner,
6458 .exec_seg_base = seg.inner.fileoff,
6459 .exec_seg_limit = seg.inner.filesize,
64646460 .code_sig_cmd = code_sig_cmd,
64656461 .output_mode = self.base.options.output_mode,
64666462 }, buffer.writer());
......@@ -6480,6 +6476,7 @@ fn writeLoadCommands(self: *MachO) !void {
64806476
64816477 var sizeofcmds: u32 = 0;
64826478 for (self.load_commands.items) |lc| {
6479 if (lc.cmd() == .NONE) continue;
64836480 sizeofcmds += lc.cmdsize();
64846481 }
64856482
......@@ -6488,12 +6485,13 @@ fn writeLoadCommands(self: *MachO) !void {
64886485 var fib = std.io.fixedBufferStream(buffer);
64896486 const writer = fib.writer();
64906487 for (self.load_commands.items) |lc| {
6488 if (lc.cmd() == .NONE) continue;
64916489 try lc.write(writer);
64926490 }
64936491
64946492 const off = @sizeOf(macho.mach_header_64);
64956493
6496 log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
6494 log.debug("writing load commands from 0x{x} to 0x{x}", .{ off, off + sizeofcmds });
64976495
64986496 try self.base.file.?.pwriteAll(buffer, off);
64996497 self.load_commands_dirty = false;
......@@ -6532,11 +6530,13 @@ fn writeHeader(self: *MachO) !void {
65326530 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
65336531 }
65346532
6535 header.ncmds = @intCast(u32, self.load_commands.items.len);
6533 header.ncmds = 0;
65366534 header.sizeofcmds = 0;
65376535
65386536 for (self.load_commands.items) |cmd| {
6537 if (cmd.cmd() == .NONE) continue;
65396538 header.sizeofcmds += cmd.cmdsize();
6539 header.ncmds += 1;
65406540 }
65416541
65426542 log.debug("writing Mach-O header {}", .{header});
src/link/MachO/CodeSignature.zig+4-3
......@@ -250,7 +250,8 @@ pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const
250250
251251pub const WriteOpts = struct {
252252 file: fs.File,
253 text_segment: macho.segment_command_64,
253 exec_seg_base: u64,
254 exec_seg_limit: u64,
254255 code_sig_cmd: macho.linkedit_data_command,
255256 output_mode: std.builtin.OutputMode,
256257};
......@@ -270,8 +271,8 @@ pub fn writeAdhocSignature(
270271 var blobs = std.ArrayList(Blob).init(allocator);
271272 defer blobs.deinit();
272273
273 self.code_directory.inner.execSegBase = opts.text_segment.fileoff;
274 self.code_directory.inner.execSegLimit = opts.text_segment.filesize;
274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
275276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
276277 const file_size = opts.code_sig_cmd.dataoff;
277278 self.code_directory.inner.codeLimit = file_size;