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 {...@@ -912,6 +912,9 @@ pub const relocation_info = packed struct {
912pub const LC_REQ_DYLD = 0x80000000;912pub const LC_REQ_DYLD = 0x80000000;
913913
914pub const LC = enum(u32) {914pub const LC = enum(u32) {
915 /// No load command - invalid
916 NONE = 0x0,
917
915 /// segment of this file to be mapped918 /// segment of this file to be mapped
916 SEGMENT = 0x1,919 SEGMENT = 0x1,
917920
src/link/MachO.zig+332-332
...@@ -164,11 +164,13 @@ tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},...@@ -164,11 +164,13 @@ tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
164locals_free_list: std.ArrayListUnmanaged(u32) = .{},164locals_free_list: std.ArrayListUnmanaged(u32) = .{},
165globals_free_list: std.ArrayListUnmanaged(u32) = .{},165globals_free_list: std.ArrayListUnmanaged(u32) = .{},
166166
167mh_execute_header_index: ?u32 = null,
168dyld_stub_binder_index: ?u32 = null,167dyld_stub_binder_index: ?u32 = null,
169dyld_private_atom: ?*Atom = null,168dyld_private_atom: ?*Atom = null,
170stub_helper_preamble_atom: ?*Atom = null,169stub_helper_preamble_atom: ?*Atom = null,
171170
171mh_execute_header_sym_index: ?u32 = null,
172dso_handle_sym_index: ?u32 = null,
173
172strtab: std.ArrayListUnmanaged(u8) = .{},174strtab: std.ArrayListUnmanaged(u8) = .{},
173strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},175strtab_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...@@ -856,7 +858,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
856 // re-exports every single symbol definition.858 // re-exports every single symbol definition.
857 for (lib_dirs.items) |dir| {859 for (lib_dirs.items) |dir| {
858 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {860 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 });
860 libsystem_available = true;862 libsystem_available = true;
861 break :blk;863 break :blk;
862 }864 }
...@@ -866,8 +868,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -866,8 +868,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
866 for (lib_dirs.items) |dir| {868 for (lib_dirs.items) |dir| {
867 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {869 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
868 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {870 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
869 try libs.put(libsystem_path, .{ .needed = false });871 try libs.put(libsystem_path, .{ .needed = true });
870 try libs.put(libc_path, .{ .needed = false });872 try libs.put(libc_path, .{ .needed = true });
871 libsystem_available = true;873 libsystem_available = true;
872 break :blk;874 break :blk;
873 }875 }
...@@ -881,7 +883,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -881,7 +883,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
881 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{883 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
882 "libc", "darwin", libsystem_name,884 "libc", "darwin", libsystem_name,
883 });885 });
884 try libs.put(full_path, .{ .needed = false });886 try libs.put(full_path, .{ .needed = true });
885 }887 }
886888
887 // frameworks889 // frameworks
...@@ -1090,7 +1092,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1090,7 +1092,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1090 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);1092 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1091 }1093 }
10921094
1093 try self.createMhExecuteHeaderAtom();1095 try self.createMhExecuteHeaderSymbol();
1094 for (self.objects.items) |*object, object_id| {1096 for (self.objects.items) |*object, object_id| {
1095 if (object.analyzed) continue;1097 if (object.analyzed) continue;
1096 try self.resolveSymbolsInObject(@intCast(u16, object_id));1098 try self.resolveSymbolsInObject(@intCast(u16, object_id));
...@@ -1101,7 +1103,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1101,7 +1103,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1101 try self.createDyldPrivateAtom();1103 try self.createDyldPrivateAtom();
1102 try self.createStubHelperPreambleAtom();1104 try self.createStubHelperPreambleAtom();
1103 try self.resolveSymbolsInDylibs();1105 try self.resolveSymbolsInDylibs();
1104 try self.createDsoHandleAtom();1106 try self.createDsoHandleSymbol();
1105 try self.addCodeSignatureLC();1107 try self.addCodeSignatureLC();
11061108
1107 {1109 {
...@@ -1156,14 +1158,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1156,14 +1158,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11561158
1157 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;1159 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
1158 if (use_llvm or use_stage1) {1160 if (use_llvm or use_stage1) {
1159 try self.sortSections();1161 try self.pruneAndSortSections();
1160 try self.allocateTextSegment();1162 try self.allocateSegments();
1161 try self.allocateDataConstSegment();
1162 try self.allocateDataSegment();
1163 self.allocateLinkeditSegment();
1164 try self.allocateLocals();1163 try self.allocateLocals();
1165 }1164 }
11661165
1166 try self.allocateSpecialSymbols();
1167 try self.allocateGlobals();1167 try self.allocateGlobals();
11681168
1169 if (build_options.enable_logging) {1169 if (build_options.enable_logging) {
...@@ -2261,6 +2261,27 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void...@@ -2261,6 +2261,27 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
2261 }2261 }
2262}2262}
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
2264fn allocateGlobals(self: *MachO) !void {2285fn allocateGlobals(self: *MachO) !void {
2265 log.debug("allocating global symbols", .{});2286 log.debug("allocating global symbols", .{});
22662287
...@@ -2442,7 +2463,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {...@@ -2442,7 +2463,9 @@ pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2442}2463}
24432464
2444fn createDyldPrivateAtom(self: *MachO) !void {2465fn createDyldPrivateAtom(self: *MachO) !void {
2466 if (self.dyld_stub_binder_index == null) return;
2445 if (self.dyld_private_atom != null) return;2467 if (self.dyld_private_atom != null) return;
2468
2446 const local_sym_index = @intCast(u32, self.locals.items.len);2469 const local_sym_index = @intCast(u32, self.locals.items.len);
2447 const sym = try self.locals.addOne(self.base.allocator);2470 const sym = try self.locals.addOne(self.base.allocator);
2448 sym.* = .{2471 sym.* = .{
...@@ -2468,7 +2491,9 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2468,7 +2491,9 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2468}2491}
24692492
2470fn createStubHelperPreambleAtom(self: *MachO) !void {2493fn createStubHelperPreambleAtom(self: *MachO) !void {
2494 if (self.dyld_stub_binder_index == null) return;
2471 if (self.stub_helper_preamble_atom != null) return;2495 if (self.stub_helper_preamble_atom != null) return;
2496
2472 const arch = self.base.options.target.cpu.arch;2497 const arch = self.base.options.target.cpu.arch;
2473 const size: u64 = switch (arch) {2498 const size: u64 = switch (arch) {
2474 .x86_64 => 15,2499 .x86_64 => 15,
...@@ -2816,57 +2841,46 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2816,57 +2841,46 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2816 }2841 }
2817}2842}
28182843
2819fn createDsoHandleAtom(self: *MachO) !void {2844fn createDsoHandleSymbol(self: *MachO) !void {
2820 if (self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{2845 if (self.dso_handle_sym_index != null) return;
2846
2847 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{
2821 .bytes = &self.strtab,2848 .bytes = &self.strtab,
2822 })) |n_strx| blk: {2849 }) orelse return;
2823 const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk;
2824 if (resolv.where != .undef) break :blk;
28252850
2826 const undef = &self.undefs.items[resolv.where_index];2851 const resolv = self.symbol_resolver.getPtr(n_strx) orelse return;
2827 const match: MatchingSection = .{2852 if (resolv.where != .undef) return;
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);
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.* = .{2870 assert(self.unresolved.swapRemove(resolv.where_index));
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 };
28592871
2860 // We create an empty atom for this symbol.2872 undef.* = .{
2861 // TODO perhaps we should special-case special symbols? Create a separate2873 .n_strx = 0,
2862 // linked list of atoms?2874 .n_type = macho.N_UNDF,
2863 const atom = try self.createEmptyAtom(local_sym_index, 0, 0);2875 .n_sect = 0,
2864 if (self.needs_prealloc) {2876 .n_desc = 0,
2865 const sym = &self.locals.items[local_sym_index];2877 .n_value = 0,
2866 const vaddr = try self.allocateAtom(atom, 0, 1, match);2878 };
2867 sym.n_value = vaddr;2879 resolv.* = .{
2868 } else try self.addAtomToSection(atom, match);2880 .where = .global,
2869 }2881 .where_index = global_sym_index,
2882 .local_sym_index = local_sym_index,
2883 };
2870}2884}
28712885
2872fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {2886fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
...@@ -3183,27 +3197,21 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3183,27 +3197,21 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3183 }3197 }
3184}3198}
31853199
3186fn createMhExecuteHeaderAtom(self: *MachO) !void {3200fn createMhExecuteHeaderSymbol(self: *MachO) !void {
3187 if (self.mh_execute_header_index != null) return;3201 if (self.base.options.output_mode != .Exe) return;
31883202 if (self.mh_execute_header_sym_index != null) return;
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];
31953203
3196 const n_strx = try self.makeString("__mh_execute_header");3204 const n_strx = try self.makeString("__mh_execute_header");
3197 const local_sym_index = @intCast(u32, self.locals.items.len);3205 const local_sym_index = @intCast(u32, self.locals.items.len);
3198 var nlist = macho.nlist_64{3206 var nlist = macho.nlist_64{
3199 .n_strx = n_strx,3207 .n_strx = n_strx,
3200 .n_type = macho.N_SECT,3208 .n_type = macho.N_SECT,
3201 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1),3209 .n_sect = 0,
3202 .n_desc = 0,3210 .n_desc = 0,
3203 .n_value = sect.addr,3211 .n_value = 0,
3204 };3212 };
3205 try self.locals.append(self.base.allocator, nlist);3213 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
3208 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {3216 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
3209 const global = &self.globals.items[resolv.where_index];3217 const global = &self.globals.items[resolv.where_index];
...@@ -3223,23 +3231,11 @@ fn createMhExecuteHeaderAtom(self: *MachO) !void {...@@ -3223,23 +3231,11 @@ fn createMhExecuteHeaderAtom(self: *MachO) !void {
3223 .file = null,3231 .file = null,
3224 });3232 });
3225 }3233 }
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 }
3239}3234}
32403235
3241fn resolveDyldStubBinder(self: *MachO) !void {3236fn resolveDyldStubBinder(self: *MachO) !void {
3242 if (self.dyld_stub_binder_index != null) return;3237 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
3244 const n_strx = try self.makeString("dyld_stub_binder");3240 const n_strx = try self.makeString("dyld_stub_binder");
3245 const sym_index = @intCast(u32, self.undefs.items.len);3241 const sym_index = @intCast(u32, self.undefs.items.len);
...@@ -3295,7 +3291,12 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3295,7 +3291,12 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3295 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);3291 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
3296 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });3292 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
3297 atom_sym.n_value = vaddr;3293 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
3300 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);3301 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3301}3302}
...@@ -4512,6 +4513,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4512,6 +4513,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4512 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);4513 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
45134514
4514 if (self.pagezero_segment_cmd_index == null) blk: {4515 if (self.pagezero_segment_cmd_index == null) blk: {
4516 if (self.base.options.output_mode == .Lib) break :blk;
4515 if (aligned_pagezero_vmsize == 0) break :blk;4517 if (aligned_pagezero_vmsize == 0) break :blk;
4516 if (aligned_pagezero_vmsize != pagezero_vmsize) {4518 if (aligned_pagezero_vmsize != pagezero_vmsize) {
4517 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});4519 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
...@@ -4636,9 +4638,9 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4636,9 +4638,9 @@ fn populateMissingMetadata(self: *MachO) !void {
4636 var fileoff: u64 = 0;4638 var fileoff: u64 = 0;
4637 var needed_size: u64 = 0;4639 var needed_size: u64 = 0;
4638 if (self.needs_prealloc) {4640 if (self.needs_prealloc) {
4639 const address_and_offset = self.nextSegmentAddressAndOffset();4641 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
4640 vmaddr = address_and_offset.address;4642 vmaddr = base.vmaddr;
4641 fileoff = address_and_offset.offset;4643 fileoff = base.fileoff;
4642 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;4644 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4643 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);4645 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4644 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{4646 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
...@@ -4686,9 +4688,9 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4686,9 +4688,9 @@ fn populateMissingMetadata(self: *MachO) !void {
4686 var fileoff: u64 = 0;4688 var fileoff: u64 = 0;
4687 var needed_size: u64 = 0;4689 var needed_size: u64 = 0;
4688 if (self.needs_prealloc) {4690 if (self.needs_prealloc) {
4689 const address_and_offset = self.nextSegmentAddressAndOffset();4691 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
4690 vmaddr = address_and_offset.address;4692 vmaddr = base.vmaddr;
4691 fileoff = address_and_offset.offset;4693 fileoff = base.fileoff;
4692 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;4694 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
4693 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);4695 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4694 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{4696 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{
...@@ -4803,9 +4805,9 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4803,9 +4805,9 @@ fn populateMissingMetadata(self: *MachO) !void {
4803 var vmaddr: u64 = 0;4805 var vmaddr: u64 = 0;
4804 var fileoff: u64 = 0;4806 var fileoff: u64 = 0;
4805 if (self.needs_prealloc) {4807 if (self.needs_prealloc) {
4806 const address_and_offset = self.nextSegmentAddressAndOffset();4808 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
4807 vmaddr = address_and_offset.address;4809 vmaddr = base.vmaddr;
4808 fileoff = address_and_offset.offset;4810 fileoff = base.fileoff;
4809 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});4811 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
4810 }4812 }
4811 try self.load_commands.append(self.base.allocator, .{4813 try self.load_commands.append(self.base.allocator, .{
...@@ -5030,17 +5032,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -5030,17 +5032,10 @@ fn populateMissingMetadata(self: *MachO) !void {
5030 self.cold_start = true;5032 self.cold_start = true;
5031}5033}
50325034
5033fn allocateTextSegment(self: *MachO) !void {5035fn calcMinHeaderpad(self: *MachO) u64 {
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
5042 var sizeofcmds: u32 = 0;5036 var sizeofcmds: u32 = 0;
5043 for (self.load_commands.items) |lc| {5037 for (self.load_commands.items) |lc| {
5038 if (lc.cmd() == .NONE) continue;
5044 sizeofcmds += lc.cmdsize();5039 sizeofcmds += lc.cmdsize();
5045 }5040 }
50465041
...@@ -5067,60 +5062,74 @@ fn allocateTextSegment(self: *MachO) !void {...@@ -5067,60 +5062,74 @@ fn allocateTextSegment(self: *MachO) !void {
5067 }5062 }
5068 const offset = @sizeOf(macho.mach_header_64) + padding;5063 const offset = @sizeOf(macho.mach_header_64) + padding;
5069 log.debug("actual headerpad size 0x{x}", .{offset});5064 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.5066 return offset;
5073 var min_alignment: u32 = 0;5067}
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 }
50785068
5079 assert(min_alignment > 0);5069fn allocateSegments(self: *MachO) !void {
5080 const last_sect_idx = seg.sections.items.len - 1;5070 try self.allocateSegment(self.text_segment_cmd_index, &.{
5081 const last_sect = seg.sections.items[last_sect_idx];5071 self.pagezero_segment_cmd_index,
5082 const shift: u32 = blk: {5072 }, self.calcMinHeaderpad());
5083 const diff = seg.inner.filesize - last_sect.offset - last_sect.size;5073
5084 const factor = @divTrunc(diff, min_alignment);5074 if (self.text_segment_cmd_index) |index| blk: {
5085 break :blk @intCast(u32, factor * min_alignment);5075 const seg = &self.load_commands.items[index].segment;
5086 };5076 if (seg.sections.items.len == 0) break :blk;
50875077
5088 if (shift > 0) {5078 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
5089 for (seg.sections.items) |*sect| {5079 var min_alignment: u32 = 0;
5090 sect.offset += shift;5080 for (seg.sections.items) |sect| {
5091 sect.addr += shift;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 }
5092 }5099 }
5093 }5100 }
5094}
50955101
5096fn allocateDataConstSegment(self: *MachO) !void {5102 try self.allocateSegment(self.data_const_segment_cmd_index, &.{
5097 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;5103 self.text_segment_cmd_index,
5098 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5104 self.pagezero_segment_cmd_index,
5099 seg.inner.fileoff = text_seg.inner.fileoff + text_seg.inner.filesize;5105 }, 0);
5100 seg.inner.vmaddr = text_seg.inner.vmaddr + text_seg.inner.vmsize;
5101 try self.allocateSegment(self.data_const_segment_cmd_index.?, 0);
5102}
51035106
5104fn allocateDataSegment(self: *MachO) !void {5107 try self.allocateSegment(self.data_segment_cmd_index, &.{
5105 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;5108 self.data_const_segment_cmd_index,
5106 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;5109 self.text_segment_cmd_index,
5107 seg.inner.fileoff = data_const_seg.inner.fileoff + data_const_seg.inner.filesize;5110 self.pagezero_segment_cmd_index,
5108 seg.inner.vmaddr = data_const_seg.inner.vmaddr + data_const_seg.inner.vmsize;5111 }, 0);
5109 try self.allocateSegment(self.data_segment_cmd_index.?, 0);
5110}
51115112
5112fn allocateLinkeditSegment(self: *MachO) void {5113 try self.allocateSegment(self.linkedit_segment_cmd_index, &.{
5113 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5114 self.data_segment_cmd_index,
5114 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;5115 self.data_const_segment_cmd_index,
5115 seg.inner.fileoff = data_seg.inner.fileoff + data_seg.inner.filesize;5116 self.text_segment_cmd_index,
5116 seg.inner.vmaddr = data_seg.inner.vmaddr + data_seg.inner.vmsize;5117 self.pagezero_segment_cmd_index,
5118 }, 0);
5117}5119}
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;
5120 const seg = &self.load_commands.items[index].segment;5123 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
5122 // Allocate the sections according to their alignment at the beginning of the segment.5131 // Allocate the sections according to their alignment at the beginning of the segment.
5123 var start: u64 = offset;5132 var start = init_size;
5124 for (seg.sections.items) |*sect, sect_id| {5133 for (seg.sections.items) |*sect, sect_id| {
5125 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;5134 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
5126 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;5135 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 {...@@ -5524,6 +5533,9 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5524 } else {5533 } else {
5525 try self.atoms.putNoClobber(self.base.allocator, match, atom);5534 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5526 }5535 }
5536 const seg = &self.load_commands.items[match.seg].segment;
5537 const sect = &seg.sections.items[match.sect];
5538 sect.size += atom.size;
5527}5539}
55285540
5529pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {5541pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
...@@ -5551,180 +5563,145 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -5551,180 +5563,145 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5551 return n_strx;5563 return n_strx;
5552}5564}
55535565
5554const NextSegmentAddressAndOffset = struct {5566fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
5555 address: u64,5567 for (indices) |maybe_prev_id| {
5556 offset: u64,5568 const prev_id = maybe_prev_id orelse continue;
5557};5569 const prev = self.load_commands.items[prev_id].segment;
55585570 return .{
5559fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {5571 .vmaddr = prev.inner.vmaddr + prev.inner.vmsize,
5560 var prev_segment_idx: ?usize = null; // We use optional here for safety.5572 .fileoff = prev.inner.fileoff + prev.inner.filesize,
5561 for (self.load_commands.items) |cmd, i| {5573 };
5562 if (cmd == .segment) {
5563 prev_segment_idx = i;
5564 }
5565 }5574 }
5566 const prev_segment = self.load_commands.items[prev_segment_idx.?].segment;5575 return .{ .vmaddr = 0, .fileoff = 0 };
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 };
5573}5576}
55745577
5575fn sortSections(self: *MachO) !void {5578fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*?u16) !void {
5576 var text_index_mapping = std.AutoHashMap(u16, u16).init(self.base.allocator);5579 const seg_id = maybe_seg_id.* orelse return;
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();
55825580
5583 {5581 var mapping = std.AutoArrayHashMap(u16, ?u16).init(self.base.allocator);
5584 // __TEXT segment5582 defer mapping.deinit();
5585 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].segment;5583
5586 var sections = seg.sections.toOwnedSlice(self.base.allocator);5584 const seg = &self.load_commands.items[seg_id].segment;
5587 defer self.base.allocator.free(sections);5585 var sections = seg.sections.toOwnedSlice(self.base.allocator);
5588 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);5586 defer self.base.allocator.free(sections);
55895587 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);
5590 const indices = &[_]*?u16{5588
5591 &self.text_section_index,5589 for (indices) |maybe_index| {
5592 &self.stubs_section_index,5590 const old_idx = maybe_index.* orelse continue;
5593 &self.stub_helper_section_index,5591 const sect = sections[old_idx];
5594 &self.gcc_except_tab_section_index,5592 if (sect.size == 0) {
5595 &self.cstring_section_index,5593 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5596 &self.ustring_section_index,5594 maybe_index.* = null;
5597 &self.text_const_section_index,5595 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5598 &self.objc_methlist_section_index,5596 seg.inner.nsects -= 1;
5599 &self.objc_methname_section_index,5597 } else {
5600 &self.objc_methtype_section_index,5598 maybe_index.* = @intCast(u16, seg.sections.items.len);
5601 &self.objc_classname_section_index,5599 seg.sections.appendAssumeCapacity(sect);
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;
5612 }5600 }
5601 try mapping.putNoClobber(old_idx, maybe_index.*);
5613 }5602 }
56145603
5615 {5604 var atoms = std.ArrayList(struct { match: MatchingSection, atom: *Atom }).init(self.base.allocator);
5616 // __DATA_CONST segment5605 defer atoms.deinit();
5617 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;5606 try atoms.ensureTotalCapacity(mapping.count());
5618 var sections = seg.sections.toOwnedSlice(self.base.allocator);5607
5619 defer self.base.allocator.free(sections);5608 for (mapping.keys()) |old_sect| {
5620 try seg.sections.ensureTotalCapacity(self.base.allocator, sections.len);5609 const new_sect = mapping.get(old_sect).? orelse {
56215610 _ = self.atoms.remove(.{ .seg = seg_id, .sect = old_sect });
5622 const indices = &[_]*?u16{5611 continue;
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,
5630 };5612 };
5631 for (indices) |maybe_index| {5613 const kv = self.atoms.fetchRemove(.{ .seg = seg_id, .sect = old_sect }).?;
5632 const new_index: u16 = if (maybe_index.*) |index| blk: {5614 atoms.appendAssumeCapacity(.{
5633 const idx = @intCast(u16, seg.sections.items.len);5615 .match = .{ .seg = seg_id, .sect = new_sect },
5634 seg.sections.appendAssumeCapacity(sections[index]);5616 .atom = kv.value,
5635 try data_const_index_mapping.putNoClobber(index, idx);5617 });
5636 break :blk idx;
5637 } else continue;
5638 maybe_index.* = new_index;
5639 }
5640 }5618 }
56415619
5642 {5620 while (atoms.popOrNull()) |next| {
5643 // __DATA segment5621 try self.atoms.putNoClobber(self.base.allocator, next.match, next.atom);
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 }
5673 }5622 }
56745623
5675 {5624 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5676 var transient: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{};5625 // Segment has now become empty, so mark it as such
5677 try transient.ensureTotalCapacity(self.base.allocator, self.atoms.count());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();5632fn pruneAndSortSections(self: *MachO) !void {
5680 while (it.next()) |entry| {5633 try self.pruneAndSortSectionsInSegment(&self.text_segment_cmd_index, &.{
5681 const old = entry.key_ptr.*;5634 &self.text_section_index,
5682 const sect = if (old.seg == self.text_segment_cmd_index.?)5635 &self.stubs_section_index,
5683 text_index_mapping.get(old.sect).?5636 &self.stub_helper_section_index,
5684 else if (old.seg == self.data_const_segment_cmd_index.?)5637 &self.gcc_except_tab_section_index,
5685 data_const_index_mapping.get(old.sect).?5638 &self.cstring_section_index,
5686 else5639 &self.ustring_section_index,
5687 data_index_mapping.get(old.sect).?;5640 &self.text_const_section_index,
5688 transient.putAssumeCapacityNoClobber(.{5641 &self.objc_methlist_section_index,
5689 .seg = old.seg,5642 &self.objc_methname_section_index,
5690 .sect = sect,5643 &self.objc_methtype_section_index,
5691 }, entry.value_ptr.*);5644 &self.objc_classname_section_index,
5692 }5645 &self.eh_frame_section_index,
5646 });
56935647
5694 self.atoms.clearAndFree(self.base.allocator);5648 try self.pruneAndSortSectionsInSegment(&self.data_const_segment_cmd_index, &.{
5695 self.atoms.deinit(self.base.allocator);5649 &self.got_section_index,
5696 self.atoms = transient;5650 &self.mod_init_func_section_index,
5697 }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 {5658 try self.pruneAndSortSectionsInSegment(&self.data_segment_cmd_index, &.{
5700 // Create new section ordinals.5659 &self.rustc_section_index,
5701 self.section_ordinals.clearRetainingCapacity();5660 &self.la_symbol_ptr_section_index,
5702 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5661 &self.objc_const_section_index,
5703 for (text_seg.sections.items) |_, sect_id| {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| {
5704 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5678 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5705 .seg = self.text_segment_cmd_index.?,5679 .seg = seg_id,
5706 .sect = @intCast(u16, sect_id),5680 .sect = @intCast(u16, sect_id),
5707 });5681 });
5708 assert(!res.found_existing);5682 assert(!res.found_existing);
5709 }5683 }
5710 const data_const_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].segment;5684 }
5711 for (data_const_seg.sections.items) |_, sect_id| {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| {
5712 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5688 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5713 .seg = self.data_const_segment_cmd_index.?,5689 .seg = seg_id,
5714 .sect = @intCast(u16, sect_id),5690 .sect = @intCast(u16, sect_id),
5715 });5691 });
5716 assert(!res.found_existing);5692 assert(!res.found_existing);
5717 }5693 }
5718 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;5694 }
5719 for (data_seg.sections.items) |_, sect_id| {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| {
5720 const res = self.section_ordinals.getOrPutAssumeCapacity(.{5698 const res = self.section_ordinals.getOrPutAssumeCapacity(.{
5721 .seg = self.data_segment_cmd_index.?,5699 .seg = seg_id,
5722 .sect = @intCast(u16, sect_id),5700 .sect = @intCast(u16, sect_id),
5723 });5701 });
5724 assert(!res.found_existing);5702 assert(!res.found_existing);
5725 }5703 }
5726 }5704 }
5727
5728 self.sections_order_dirty = false;5705 self.sections_order_dirty = false;
5729}5706}
57305707
...@@ -5739,12 +5716,16 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5739,12 +5716,16 @@ fn updateSectionOrdinals(self: *MachO) !void {
5739 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};5716 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
57405717
5741 var new_ordinal: u8 = 0;5718 var new_ordinal: u8 = 0;
5742 for (self.load_commands.items) |lc, lc_id| {5719 for (&[_]?u16{
5743 if (lc != .segment) break;5720 self.text_segment_cmd_index,
57445721 self.data_const_segment_cmd_index,
5745 for (lc.segment.sections.items) |_, sect_id| {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| {
5746 const match = MatchingSection{5727 const match = MatchingSection{
5747 .seg = @intCast(u16, lc_id),5728 .seg = @intCast(u16, index),
5748 .sect = @intCast(u16, sect_id),5729 .sect = @intCast(u16, sect_id),
5749 };5730 };
5750 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);5731 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
...@@ -5783,7 +5764,9 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5783,7 +5764,9 @@ fn writeDyldInfoData(self: *MachO) !void {
5783 const match = entry.key_ptr.*;5764 const match = entry.key_ptr.*;
5784 var atom: *Atom = entry.value_ptr.*;5765 var atom: *Atom = entry.value_ptr.*;
57855766
5786 if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable5767 if (self.text_segment_cmd_index) |seg| {
5768 if (match.seg == seg) continue; // __TEXT is non-writable
5769 }
57875770
5788 const seg = self.load_commands.items[match.seg].segment;5771 const seg = self.load_commands.items[match.seg].segment;
57895772
...@@ -5865,6 +5848,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5865,6 +5848,7 @@ fn writeDyldInfoData(self: *MachO) !void {
5865 {5848 {
5866 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5849 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5867 log.debug("generating export trie", .{});5850 log.debug("generating export trie", .{});
5851
5868 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;5852 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5869 const base_address = text_segment.inner.vmaddr;5853 const base_address = text_segment.inner.vmaddr;
58705854
...@@ -5957,10 +5941,13 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5957,10 +5941,13 @@ fn writeDyldInfoData(self: *MachO) !void {
5957}5941}
59585942
5959fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {5943fn 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;
5960 const last_atom = self.atoms.get(.{5946 const last_atom = self.atoms.get(.{
5961 .seg = self.text_segment_cmd_index.?,5947 .seg = text_segment_cmd_index,
5962 .sect = self.stub_helper_section_index.?,5948 .sect = stub_helper_section_index,
5963 }) orelse return;5949 }) orelse return;
5950 if (self.stub_helper_preamble_atom == null) return;
5964 if (last_atom == self.stub_helper_preamble_atom.?) return;5951 if (last_atom == self.stub_helper_preamble_atom.?) return;
59655952
5966 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);5953 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);
...@@ -6036,8 +6023,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6036,8 +6023,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
6036 }6023 }
60376024
6038 const sect = blk: {6025 const sect = blk: {
6039 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;6026 const seg = self.load_commands.items[text_segment_cmd_index].segment;
6040 break :blk seg.sections.items[self.stub_helper_section_index.?];6027 break :blk seg.sections.items[stub_helper_section_index];
6041 };6028 };
6042 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {6029 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
6043 .x86_64 => 1,6030 .x86_64 => 1,
...@@ -6326,15 +6313,8 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6326,15 +6313,8 @@ fn writeSymbolTable(self: *MachO) !void {
6326 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;6313 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6327 dysymtab.nundefsym = @intCast(u32, nundefs);6314 dysymtab.nundefsym = @intCast(u32, nundefs);
63286315
6329 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].segment;6316 const nstubs = @intCast(u32, self.stubs_table.count());
6330 const stubs = &text_segment.sections.items[self.stubs_section_index.?];6317 const ngot_entries = @intCast(u32, self.got_entries_table.count());
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);
63386318
6339 const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));6319 const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6340 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);6320 dysymtab.indirectsymoff = @intCast(u32, indirectsymoff);
...@@ -6352,35 +6332,50 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6352,35 +6332,50 @@ fn writeSymbolTable(self: *MachO) !void {
6352 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));6332 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
6353 const writer = buf.writer();6333 const writer = buf.writer();
63546334
6355 stubs.reserved1 = 0;6335 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6356 for (self.stubs_table.keys()) |key| {6336 const stubs_section_index = self.stubs_section_index orelse break :blk;
6357 const resolv = self.symbol_resolver.get(key).?;6337 const text_segment = &self.load_commands.items[text_segment_cmd_index].segment;
6358 switch (resolv.where) {6338 const stubs = &text_segment.sections.items[stubs_section_index];
6359 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6339 stubs.reserved1 = 0;
6360 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),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 }
6361 }6346 }
6362 }6347 }
63636348
6364 got.reserved1 = nstubs;6349 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6365 for (self.got_entries_table.keys()) |key| {6350 const got_section_index = self.got_section_index orelse break :blk;
6366 switch (key) {6351 const data_const_segment = &self.load_commands.items[data_const_segment_cmd_index].segment;
6367 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6352 const got = &data_const_segment.sections.items[got_section_index];
6368 .global => |n_strx| {6353 got.reserved1 = nstubs;
6369 const resolv = self.symbol_resolver.get(n_strx).?;6354 for (self.got_entries_table.keys()) |key| {
6370 switch (resolv.where) {6355 switch (key) {
6371 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6356 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6372 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),6357 .global => |n_strx| {
6373 }6358 const resolv = self.symbol_resolver.get(n_strx).?;
6374 },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 }
6375 }6365 }
6376 }6366 }
63776367
6378 la_symbol_ptr.reserved1 = got.reserved1 + ngot_entries;6368 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6379 for (self.stubs_table.keys()) |key| {6369 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6380 const resolv = self.symbol_resolver.get(key).?;6370 const data_segment = &self.load_commands.items[data_segment_cmd_index].segment;
6381 switch (resolv.where) {6371 const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index];
6382 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6372 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6383 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),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 }
6384 }6379 }
6385 }6380 }
63866381
...@@ -6452,15 +6447,16 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {...@@ -6452,15 +6447,16 @@ fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature) !void {
6452 const tracy = trace(@src());6447 const tracy = trace(@src());
6453 defer tracy.end();6448 defer tracy.end();
64546449
6455 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6456 const code_sig_cmd = self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data;6450 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
6458 var buffer = std.ArrayList(u8).init(self.base.allocator);6453 var buffer = std.ArrayList(u8).init(self.base.allocator);
6459 defer buffer.deinit();6454 defer buffer.deinit();
6460 try buffer.ensureTotalCapacityPrecise(code_sig.size());6455 try buffer.ensureTotalCapacityPrecise(code_sig.size());
6461 try code_sig.writeAdhocSignature(self.base.allocator, .{6456 try code_sig.writeAdhocSignature(self.base.allocator, .{
6462 .file = self.base.file.?,6457 .file = self.base.file.?,
6463 .text_segment = text_segment.inner,6458 .exec_seg_base = seg.inner.fileoff,
6459 .exec_seg_limit = seg.inner.filesize,
6464 .code_sig_cmd = code_sig_cmd,6460 .code_sig_cmd = code_sig_cmd,
6465 .output_mode = self.base.options.output_mode,6461 .output_mode = self.base.options.output_mode,
6466 }, buffer.writer());6462 }, buffer.writer());
...@@ -6480,6 +6476,7 @@ fn writeLoadCommands(self: *MachO) !void {...@@ -6480,6 +6476,7 @@ fn writeLoadCommands(self: *MachO) !void {
64806476
6481 var sizeofcmds: u32 = 0;6477 var sizeofcmds: u32 = 0;
6482 for (self.load_commands.items) |lc| {6478 for (self.load_commands.items) |lc| {
6479 if (lc.cmd() == .NONE) continue;
6483 sizeofcmds += lc.cmdsize();6480 sizeofcmds += lc.cmdsize();
6484 }6481 }
64856482
...@@ -6488,12 +6485,13 @@ fn writeLoadCommands(self: *MachO) !void {...@@ -6488,12 +6485,13 @@ fn writeLoadCommands(self: *MachO) !void {
6488 var fib = std.io.fixedBufferStream(buffer);6485 var fib = std.io.fixedBufferStream(buffer);
6489 const writer = fib.writer();6486 const writer = fib.writer();
6490 for (self.load_commands.items) |lc| {6487 for (self.load_commands.items) |lc| {
6488 if (lc.cmd() == .NONE) continue;
6491 try lc.write(writer);6489 try lc.write(writer);
6492 }6490 }
64936491
6494 const off = @sizeOf(macho.mach_header_64);6492 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
6498 try self.base.file.?.pwriteAll(buffer, off);6496 try self.base.file.?.pwriteAll(buffer, off);
6499 self.load_commands_dirty = false;6497 self.load_commands_dirty = false;
...@@ -6532,11 +6530,13 @@ fn writeHeader(self: *MachO) !void {...@@ -6532,11 +6530,13 @@ fn writeHeader(self: *MachO) !void {
6532 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;6530 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
6533 }6531 }
65346532
6535 header.ncmds = @intCast(u32, self.load_commands.items.len);6533 header.ncmds = 0;
6536 header.sizeofcmds = 0;6534 header.sizeofcmds = 0;
65376535
6538 for (self.load_commands.items) |cmd| {6536 for (self.load_commands.items) |cmd| {
6537 if (cmd.cmd() == .NONE) continue;
6539 header.sizeofcmds += cmd.cmdsize();6538 header.sizeofcmds += cmd.cmdsize();
6539 header.ncmds += 1;
6540 }6540 }
65416541
6542 log.debug("writing Mach-O header {}", .{header});6542 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...@@ -250,7 +250,8 @@ pub fn addEntitlements(self: *CodeSignature, allocator: Allocator, path: []const
250250
251pub const WriteOpts = struct {251pub const WriteOpts = struct {
252 file: fs.File,252 file: fs.File,
253 text_segment: macho.segment_command_64,253 exec_seg_base: u64,
254 exec_seg_limit: u64,
254 code_sig_cmd: macho.linkedit_data_command,255 code_sig_cmd: macho.linkedit_data_command,
255 output_mode: std.builtin.OutputMode,256 output_mode: std.builtin.OutputMode,
256};257};
...@@ -270,8 +271,8 @@ pub fn writeAdhocSignature(...@@ -270,8 +271,8 @@ pub fn writeAdhocSignature(
270 var blobs = std.ArrayList(Blob).init(allocator);271 var blobs = std.ArrayList(Blob).init(allocator);
271 defer blobs.deinit();272 defer blobs.deinit();
272273
273 self.code_directory.inner.execSegBase = opts.text_segment.fileoff;274 self.code_directory.inner.execSegBase = opts.exec_seg_base;
274 self.code_directory.inner.execSegLimit = opts.text_segment.filesize;275 self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
275 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;276 self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
276 const file_size = opts.code_sig_cmd.dataoff;277 const file_size = opts.code_sig_cmd.dataoff;
277 self.code_directory.inner.codeLimit = file_size;278 self.code_directory.inner.codeLimit = file_size;