authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-18 23:19:33+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-09-18 23:19:33+02:00
loge42f83825f1473661700f89ffd3060013261d605
tree925898512846a68c42de617b807d13553ae33c67
parent2698cb346abe01a978edf98ba16ab6aad506b596
parent4474f8dd6ed58875930f440aad0b893c1c9a414d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12893 from ziglang/macho-relocs-cleanup

macho: rewrite incremental linker, and init splitting of linking contexts

13 files changed, 3899 insertions(+), 2672 deletions(-)

lib/std/macho.zig+5
......@@ -798,6 +798,11 @@ pub const section_64 = extern struct {
798798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799799 }
800800
801 pub fn isSymbolStubs(sect: section_64) bool {
802 const tt = sect.@"type"();
803 return tt == S_SYMBOL_STUBS;
804 }
805
801806 pub fn isDebug(sect: section_64) bool {
802807 return sect.attrs() & S_ATTR_DEBUG != 0;
803808 }
src/arch/aarch64/Emit.zig+12-15
......@@ -680,16 +680,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
680680 break :blk offset;
681681 };
682682 // Add relocation to the decl.
683 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
683 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
684684 const target = macho_file.getGlobalByIndex(relocation.sym_index);
685 try atom.relocs.append(emit.bin_file.allocator, .{
686 .offset = offset,
685 try atom.addRelocation(macho_file, .{
686 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
687687 .target = target,
688 .offset = offset,
688689 .addend = 0,
689 .subtractor = null,
690690 .pcrel = true,
691691 .length = 2,
692 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
693692 });
694693 } else {
695694 return emit.fail("Implement call_extern for linking backends != MachO", .{});
......@@ -872,8 +871,8 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
872871 Instruction.LoadStoreOffset.imm(0),
873872 ));
874873 },
875 .load_memory_ptr_got,
876874 .load_memory_ptr_direct,
875 .load_memory_ptr_got,
877876 => {
878877 // add reg, reg, offset
879878 try emit.writeInstruction(Instruction.add(reg, reg, 0, false));
......@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882881 }
883882
884883 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
885 const atom = macho_file.atom_by_index_table.get(data.atom_index).?;
886 // Page reloc for adrp instruction.
887 try atom.relocs.append(emit.bin_file.allocator, .{
888 .offset = offset,
884 const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
885 // TODO this causes segfault in stage1
886 // try atom.addRelocations(macho_file, 2, .{
887 try atom.addRelocation(macho_file, .{
889888 .target = .{ .sym_index = data.sym_index, .file = null },
889 .offset = offset,
890890 .addend = 0,
891 .subtractor = null,
892891 .pcrel = true,
893892 .length = 2,
894893 .@"type" = switch (tag) {
......@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
901900 else => unreachable,
902901 },
903902 });
904 // Pageoff reloc for adrp instruction.
905 try atom.relocs.append(emit.bin_file.allocator, .{
906 .offset = offset + 4,
903 try atom.addRelocation(macho_file, .{
907904 .target = .{ .sym_index = data.sym_index, .file = null },
905 .offset = offset + 4,
908906 .addend = 0,
909 .subtractor = null,
910907 .pcrel = false,
911908 .length = 2,
912909 .@"type" = switch (tag) {
src/arch/x86_64/Emit.zig+10-14
......@@ -996,7 +996,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
996996 );
997997
998998 const end_offset = emit.code.items.len;
999 const gpa = emit.bin_file.allocator;
1000999
10011000 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
10021001 const reloc_type = switch (ops.flags) {
......@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10041003 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
10051004 else => unreachable,
10061005 };
1007 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
1008 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });
1009 try atom.relocs.append(gpa, .{
1010 .offset = @intCast(u32, end_offset - 4),
1006 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1007 try atom.addRelocation(macho_file, .{
1008 .@"type" = reloc_type,
10111009 .target = .{ .sym_index = relocation.sym_index, .file = null },
1010 .offset = @intCast(u32, end_offset - 4),
10121011 .addend = 0,
1013 .subtractor = null,
10141012 .pcrel = true,
10151013 .length = 2,
1016 .@"type" = reloc_type,
10171014 });
10181015 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1019 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;
1016 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
10201017 try atom.addRelocation(coff_file, .{
10211018 .@"type" = switch (ops.flags) {
10221019 0b00 => .got,
......@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11451142
11461143 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
11471144 // Add relocation to the decl.
1148 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
1145 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
11491146 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1150 try atom.relocs.append(emit.bin_file.allocator, .{
1151 .offset = offset,
1147 try atom.addRelocation(macho_file, .{
1148 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11521149 .target = target,
1150 .offset = offset,
11531151 .addend = 0,
1154 .subtractor = null,
11551152 .pcrel = true,
11561153 .length = 2,
1157 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11581154 });
11591155 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
11601156 // Add relocation to the decl.
1161 const atom = coff_file.atom_by_index_table.get(relocation.atom_index).?;
1157 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
11621158 const target = coff_file.getGlobalByIndex(relocation.sym_index);
11631159 try atom.addRelocation(coff_file, .{
11641160 .@"type" = .direct,
src/link/Coff.zig+2-1
......@@ -1135,6 +1135,7 @@ fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
11351135 }
11361136
11371137 switch (zig_ty) {
1138 // TODO: what if this is a function pointer?
11381139 .Fn => break :blk self.text_section_index.?,
11391140 else => {
11401141 if (val.castTag(.variable)) |_| {
......@@ -1527,7 +1528,7 @@ pub fn getDeclVAddr(
15271528 assert(self.llvm_object == null);
15281529 assert(decl.link.coff.sym_index != 0);
15291530
1530 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
1531 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
15311532 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
15321533 try atom.addRelocation(self, .{
15331534 .@"type" = .direct,
src/link/Dwarf.zig+15-2
......@@ -948,7 +948,7 @@ pub fn commitDeclState(
948948 new_offset,
949949 });
950950
951 try File.MachO.copyRangeAllOverlappingAlloc(
951 try copyRangeAllOverlappingAlloc(
952952 gpa,
953953 d_sym.file,
954954 debug_line_sect.offset,
......@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
12471247 new_offset,
12481248 });
12491249
1250 try File.MachO.copyRangeAllOverlappingAlloc(
1250 try copyRangeAllOverlappingAlloc(
12511251 gpa,
12521252 d_sym.file,
12531253 debug_info_sect.offset,
......@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(
23382338 // DW.AT.enumeration_type delimit children
23392339 try dbg_info_buffer.append(0);
23402340}
2341
2342fn copyRangeAllOverlappingAlloc(
2343 allocator: Allocator,
2344 file: std.fs.File,
2345 in_offset: u64,
2346 out_offset: u64,
2347 len: usize,
2348) !void {
2349 const buf = try allocator.alloc(u8, len);
2350 defer allocator.free(buf);
2351 const amt = try file.preadAll(buf, in_offset);
2352 try file.pwriteAll(buf[0..amt], out_offset);
2353}
src/link/Elf.zig+1
......@@ -2320,6 +2320,7 @@ fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {
23202320 }
23212321
23222322 switch (zig_ty) {
2323 // TODO: what if this is a function pointer?
23232324 .Fn => break :blk self.phdr_load_re_index.?,
23242325 else => {
23252326 if (val.castTag(.variable)) |_| {
src/link/MachO.zig+1485-2603
......@@ -22,6 +22,7 @@ const link = @import("../link.zig");
2222const llvm_backend = @import("../codegen/llvm.zig");
2323const target_util = @import("../target.zig");
2424const trace = @import("../tracy.zig").trace;
25const zld = @import("MachO/zld.zig");
2526
2627const Air = @import("../Air.zig");
2728const Allocator = mem.Allocator;
......@@ -38,6 +39,7 @@ const LibStub = @import("tapi.zig").LibStub;
3839const Liveness = @import("../Liveness.zig");
3940const LlvmObject = @import("../codegen/llvm.zig").Object;
4041const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");
4143const StringTable = @import("strtab.zig").StringTable;
4244const Trie = @import("MachO/Trie.zig");
4345const Type = @import("../type.zig").Type;
......@@ -55,11 +57,6 @@ pub const SearchStrategy = enum {
5557
5658pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
5759
58const SystemLib = struct {
59 needed: bool = false,
60 weak: bool = false,
61};
62
6360const Section = struct {
6461 header: macho.section_64,
6562 segment_index: u8,
......@@ -118,6 +115,7 @@ segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
118115sections: std.MultiArrayList(Section) = .{},
119116
120117pagezero_segment_cmd_index: ?u8 = null,
118header_segment_cmd_index: ?u8 = null,
121119text_segment_cmd_index: ?u8 = null,
122120data_const_segment_cmd_index: ?u8 = null,
123121data_segment_cmd_index: ?u8 = null,
......@@ -127,6 +125,7 @@ text_section_index: ?u8 = null,
127125stubs_section_index: ?u8 = null,
128126stub_helper_section_index: ?u8 = null,
129127got_section_index: ?u8 = null,
128data_const_section_index: ?u8 = null,
130129la_symbol_ptr_section_index: ?u8 = null,
131130data_section_index: ?u8 = null,
132131
......@@ -160,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
160159
161160error_flags: File.ErrorFlags = File.ErrorFlags{},
162161
162segment_table_dirty: bool = false,
163
163164/// A helper var to indicate if we are at the start of the incremental updates, or
164165/// already somewhere further along the update-and-run chain.
165166/// TODO once we add opening a prelinked output binary from file, this will become
......@@ -193,6 +194,26 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
193194/// with `Decl` `main`, and lives as long as that `Decl`.
194195unnamed_const_atoms: UnnamedConstTable = .{},
195196
197/// A table of relocations indexed by the owning them `Atom`.
198/// Note that once we refactor `Atom`'s lifetime and ownership rules,
199/// this will be a table indexed by index into the list of Atoms.
200relocs: RelocationTable = .{},
201
202/// A table of rebases indexed by the owning them `Atom`.
203/// Note that once we refactor `Atom`'s lifetime and ownership rules,
204/// this will be a table indexed by index into the list of Atoms.
205rebases: RebaseTable = .{},
206
207/// A table of bindings indexed by the owning them `Atom`.
208/// Note that once we refactor `Atom`'s lifetime and ownership rules,
209/// this will be a table indexed by index into the list of Atoms.
210bindings: BindingTable = .{},
211
212/// A table of lazy bindings indexed by the owning them `Atom`.
213/// Note that once we refactor `Atom`'s lifetime and ownership rules,
214/// this will be a table indexed by index into the list of Atoms.
215lazy_bindings: BindingTable = .{},
216
196217/// Table of Decls that are currently alive.
197218/// We store them here so that we can properly dispose of any allocated
198219/// memory within the atom in the incremental linker.
......@@ -212,8 +233,8 @@ const Entry = struct {
212233 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
213234 }
214235
215 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {
216 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;
236 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {
237 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });
217238 }
218239
219240 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
......@@ -221,7 +242,10 @@ const Entry = struct {
221242 }
222243};
223244
245const BindingTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));
224246const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
247const RebaseTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));
248const RelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
225249
226250const PendingUpdate = union(enum) {
227251 resolve_undef: u32,
......@@ -235,11 +259,21 @@ pub const SymbolWithLoc = struct {
235259
236260 // null means it's a synthetic global.
237261 file: ?u32 = null,
262
263 pub fn eql(this: SymbolWithLoc, other: SymbolWithLoc) bool {
264 if (this.file == null and other.file == null) {
265 return this.sym_index == other.sym_index;
266 }
267 if (this.file != null and other.file != null) {
268 return this.sym_index == other.sym_index and this.file.? == other.file.?;
269 }
270 return false;
271 }
238272};
239273
240274/// When allocating, the ideal_capacity is calculated by
241275/// actual_capacity + (actual_capacity / ideal_factor)
242const ideal_factor = 4;
276const ideal_factor = 3;
243277
244278/// Default path to dyld
245279const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
......@@ -301,13 +335,9 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
301335 errdefer file.close();
302336 self.base.file = file;
303337
304 if (!options.strip and options.module != null) blk: {
305 // TODO once I add support for converting (and relocating) DWARF info from relocatable
306 // object files, this check becomes unnecessary.
307 // For now, for LLVM backend we fallback to the old-fashioned stabs approach used by
308 // stage1.
309 if (build_options.have_llvm and options.use_llvm) break :blk;
338 if (self.mode == .one_shot) return self;
310339
340 if (!options.strip and options.module != null) {
311341 // Create dSYM bundle.
312342 const dir = options.module.?.zig_cache_artifact_directory;
313343 log.debug("creating {s}.dSYM bundle in {?s}", .{ emit.sub_path, dir.path });
......@@ -405,7 +435,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
405435 }
406436
407437 switch (self.mode) {
408 .one_shot => return self.linkOneShot(comp, prog_node),
438 .one_shot => return zld.linkWithZld(self, comp, prog_node),
409439 .incremental => return self.flushModule(comp, prog_node),
410440 }
411441}
......@@ -434,10 +464,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
434464 try d_sym.dwarf.flushModule(&self.base, module);
435465 }
436466
437 var libs = std.StringArrayHashMap(SystemLib).init(arena);
467 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
438468 try self.resolveLibSystem(arena, comp, &.{}, &libs);
439469
440 const id_symlink_basename = "zld.id";
470 const id_symlink_basename = "link.id";
441471
442472 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
443473 var man: Cache.Manifest = undefined;
......@@ -518,14 +548,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
518548
519549 try self.allocateSpecialSymbols();
520550
551 {
552 var it = self.relocs.keyIterator();
553 while (it.next()) |atom| {
554 try atom.*.resolveRelocations(self);
555 }
556 }
557
521558 if (build_options.enable_logging) {
522559 self.logSymtab();
523560 self.logSections();
524561 self.logAtoms();
525562 }
526563
527 try self.writeAtomsIncremental();
528
529564 var lc_buffer = std.ArrayList(u8).init(arena);
530565 const lc_writer = lc_buffer.writer();
531566 var ncmds: u32 = 0;
......@@ -624,635 +659,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
624659 self.cold_start = false;
625660}
626661
627fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
628 const tracy = trace(@src());
629 defer tracy.end();
630
631 const gpa = self.base.allocator;
632 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
633 defer arena_allocator.deinit();
634 const arena = arena_allocator.allocator();
635
636 const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
637 const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
638
639 // If there is no Zig code to compile, then we should skip flushing the output file because it
640 // will not be part of the linker line anyway.
641 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
642 if (self.base.options.use_stage1) {
643 const obj_basename = try std.zig.binNameAlloc(arena, .{
644 .root_name = self.base.options.root_name,
645 .target = self.base.options.target,
646 .output_mode = .Obj,
647 });
648 switch (self.base.options.cache_mode) {
649 .incremental => break :blk try module.zig_cache_artifact_directory.join(
650 arena,
651 &[_][]const u8{obj_basename},
652 ),
653 .whole => break :blk try fs.path.join(arena, &.{
654 fs.path.dirname(full_out_path).?, obj_basename,
655 }),
656 }
657 }
658
659 try self.flushModule(comp, prog_node);
660
661 if (fs.path.dirname(full_out_path)) |dirname| {
662 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
663 } else {
664 break :blk self.base.intermediary_basename.?;
665 }
666 } else null;
667
668 var sub_prog_node = prog_node.start("MachO Flush", 0);
669 sub_prog_node.activate();
670 sub_prog_node.context.refresh();
671 defer sub_prog_node.end();
672
673 const cpu_arch = self.base.options.target.cpu.arch;
674 const os_tag = self.base.options.target.os.tag;
675 const abi = self.base.options.target.abi;
676 const is_lib = self.base.options.output_mode == .Lib;
677 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
678 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
679 const stack_size = self.base.options.stack_size_override orelse 0;
680 const is_debug_build = self.base.options.optimize_mode == .Debug;
681 const gc_sections = self.base.options.gc_sections orelse !is_debug_build;
682
683 const id_symlink_basename = "zld.id";
684
685 var man: Cache.Manifest = undefined;
686 defer if (!self.base.options.disable_lld_caching) man.deinit();
687
688 var digest: [Cache.hex_digest_len]u8 = undefined;
689
690 if (!self.base.options.disable_lld_caching) {
691 man = comp.cache_parent.obtain();
692
693 // We are about to obtain this lock, so here we give other processes a chance first.
694 self.base.releaseLock();
695
696 comptime assert(Compilation.link_hash_implementation_version == 7);
697
698 for (self.base.options.objects) |obj| {
699 _ = try man.addFile(obj.path, null);
700 man.hash.add(obj.must_link);
701 }
702 for (comp.c_object_table.keys()) |key| {
703 _ = try man.addFile(key.status.success.object_path, null);
704 }
705 try man.addOptionalFile(module_obj_path);
706 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
707 // installation sources because they are always a product of the compiler version + target information.
708 man.hash.add(stack_size);
709 man.hash.addOptional(self.base.options.pagezero_size);
710 man.hash.addOptional(self.base.options.search_strategy);
711 man.hash.addOptional(self.base.options.headerpad_size);
712 man.hash.add(self.base.options.headerpad_max_install_names);
713 man.hash.add(gc_sections);
714 man.hash.add(self.base.options.dead_strip_dylibs);
715 man.hash.add(self.base.options.strip);
716 man.hash.addListOfBytes(self.base.options.lib_dirs);
717 man.hash.addListOfBytes(self.base.options.framework_dirs);
718 link.hashAddSystemLibs(&man.hash, self.base.options.frameworks);
719 man.hash.addListOfBytes(self.base.options.rpath_list);
720 if (is_dyn_lib) {
721 man.hash.addOptionalBytes(self.base.options.install_name);
722 man.hash.addOptional(self.base.options.version);
723 }
724 link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
725 man.hash.addOptionalBytes(self.base.options.sysroot);
726 try man.addOptionalFile(self.base.options.entitlements);
727
728 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
729 _ = try man.hit();
730 digest = man.final();
731
732 var prev_digest_buf: [digest.len]u8 = undefined;
733 const prev_digest: []u8 = Cache.readSmallFile(
734 directory.handle,
735 id_symlink_basename,
736 &prev_digest_buf,
737 ) catch |err| blk: {
738 log.debug("MachO Zld new_digest={s} error: {s}", .{
739 std.fmt.fmtSliceHexLower(&digest),
740 @errorName(err),
741 });
742 // Handle this as a cache miss.
743 break :blk prev_digest_buf[0..0];
744 };
745 if (mem.eql(u8, prev_digest, &digest)) {
746 // Hot diggity dog! The output binary is already there.
747 log.debug("MachO Zld digest={s} match - skipping invocation", .{
748 std.fmt.fmtSliceHexLower(&digest),
749 });
750 self.base.lock = man.toOwnedLock();
751 return;
752 }
753 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
754 std.fmt.fmtSliceHexLower(prev_digest),
755 std.fmt.fmtSliceHexLower(&digest),
756 });
757
758 // We are about to change the output file to be different, so we invalidate the build hash now.
759 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
760 error.FileNotFound => {},
761 else => |e| return e,
762 };
763 }
764
765 if (self.base.options.output_mode == .Obj) {
766 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
767 // here. TODO: think carefully about how we can avoid this redundant operation when doing
768 // build-obj. See also the corresponding TODO in linkAsArchive.
769 const the_object_path = blk: {
770 if (self.base.options.objects.len != 0) {
771 break :blk self.base.options.objects[0].path;
772 }
773
774 if (comp.c_object_table.count() != 0)
775 break :blk comp.c_object_table.keys()[0].status.success.object_path;
776
777 if (module_obj_path) |p|
778 break :blk p;
779
780 // TODO I think this is unreachable. Audit this situation when solving the above TODO
781 // regarding eliding redundant object -> object transformations.
782 return error.NoObjectsToLink;
783 };
784 // This can happen when using --enable-cache and using the stage1 backend. In this case
785 // we can skip the file copy.
786 if (!mem.eql(u8, the_object_path, full_out_path)) {
787 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
788 }
789 } else {
790 const sub_path = self.base.options.emit.?.sub_path;
791 if (self.base.file == null) {
792 self.base.file = try directory.handle.createFile(sub_path, .{
793 .truncate = true,
794 .read = true,
795 .mode = link.determineMode(self.base.options),
796 });
797 }
798 // Index 0 is always a null symbol.
799 try self.locals.append(gpa, .{
800 .n_strx = 0,
801 .n_type = 0,
802 .n_sect = 0,
803 .n_desc = 0,
804 .n_value = 0,
805 });
806 try self.strtab.buffer.append(gpa, 0);
807 try self.populateMissingMetadata();
808
809 var lib_not_found = false;
810 var framework_not_found = false;
811
812 // Positional arguments to the linker such as object files and static archives.
813 var positionals = std.ArrayList([]const u8).init(arena);
814 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
815
816 var must_link_archives = std.StringArrayHashMap(void).init(arena);
817 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
818
819 for (self.base.options.objects) |obj| {
820 if (must_link_archives.contains(obj.path)) continue;
821 if (obj.must_link) {
822 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
823 } else {
824 _ = positionals.appendAssumeCapacity(obj.path);
825 }
826 }
827
828 for (comp.c_object_table.keys()) |key| {
829 try positionals.append(key.status.success.object_path);
830 }
831
832 if (module_obj_path) |p| {
833 try positionals.append(p);
834 }
835
836 if (comp.compiler_rt_lib) |lib| {
837 try positionals.append(lib.full_object_path);
838 }
839
840 // libc++ dep
841 if (self.base.options.link_libcpp) {
842 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
843 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
844 }
845
846 // Shared and static libraries passed via `-l` flag.
847 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
848
849 const system_lib_names = self.base.options.system_libs.keys();
850 for (system_lib_names) |system_lib_name| {
851 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
852 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
853 // case we want to avoid prepending "-l".
854 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
855 try positionals.append(system_lib_name);
856 continue;
857 }
858
859 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
860 try candidate_libs.put(system_lib_name, .{
861 .needed = system_lib_info.needed,
862 .weak = system_lib_info.weak,
863 });
864 }
865
866 var lib_dirs = std.ArrayList([]const u8).init(arena);
867 for (self.base.options.lib_dirs) |dir| {
868 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
869 try lib_dirs.append(search_dir);
870 } else {
871 log.warn("directory not found for '-L{s}'", .{dir});
872 }
873 }
874
875 var libs = std.StringArrayHashMap(SystemLib).init(arena);
876
877 // Assume ld64 default -search_paths_first if no strategy specified.
878 const search_strategy = self.base.options.search_strategy orelse .paths_first;
879 outer: for (candidate_libs.keys()) |lib_name| {
880 switch (search_strategy) {
881 .paths_first => {
882 // Look in each directory for a dylib (stub first), and then for archive
883 for (lib_dirs.items) |dir| {
884 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
885 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
886 try libs.put(full_path, candidate_libs.get(lib_name).?);
887 continue :outer;
888 }
889 }
890 } else {
891 log.warn("library not found for '-l{s}'", .{lib_name});
892 lib_not_found = true;
893 }
894 },
895 .dylibs_first => {
896 // First, look for a dylib in each search dir
897 for (lib_dirs.items) |dir| {
898 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
899 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
900 try libs.put(full_path, candidate_libs.get(lib_name).?);
901 continue :outer;
902 }
903 }
904 } else for (lib_dirs.items) |dir| {
905 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
906 try libs.put(full_path, candidate_libs.get(lib_name).?);
907 } else {
908 log.warn("library not found for '-l{s}'", .{lib_name});
909 lib_not_found = true;
910 }
911 }
912 },
913 }
914 }
915
916 if (lib_not_found) {
917 log.warn("Library search paths:", .{});
918 for (lib_dirs.items) |dir| {
919 log.warn(" {s}", .{dir});
920 }
921 }
922
923 try self.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
924
925 // frameworks
926 var framework_dirs = std.ArrayList([]const u8).init(arena);
927 for (self.base.options.framework_dirs) |dir| {
928 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
929 try framework_dirs.append(search_dir);
930 } else {
931 log.warn("directory not found for '-F{s}'", .{dir});
932 }
933 }
934
935 outer: for (self.base.options.frameworks.keys()) |f_name| {
936 for (framework_dirs.items) |dir| {
937 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
938 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
939 const info = self.base.options.frameworks.get(f_name).?;
940 try libs.put(full_path, .{
941 .needed = info.needed,
942 .weak = info.weak,
943 });
944 continue :outer;
945 }
946 }
947 } else {
948 log.warn("framework not found for '-framework {s}'", .{f_name});
949 framework_not_found = true;
950 }
951 }
952
953 if (framework_not_found) {
954 log.warn("Framework search paths:", .{});
955 for (framework_dirs.items) |dir| {
956 log.warn(" {s}", .{dir});
957 }
958 }
959
960 if (self.base.options.verbose_link) {
961 var argv = std.ArrayList([]const u8).init(arena);
962
963 try argv.append("zig");
964 try argv.append("ld");
965
966 if (is_exe_or_dyn_lib) {
967 try argv.append("-dynamic");
968 }
969
970 if (is_dyn_lib) {
971 try argv.append("-dylib");
972
973 if (self.base.options.install_name) |install_name| {
974 try argv.append("-install_name");
975 try argv.append(install_name);
976 }
977 }
978
979 if (self.base.options.sysroot) |syslibroot| {
980 try argv.append("-syslibroot");
981 try argv.append(syslibroot);
982 }
983
984 for (self.base.options.rpath_list) |rpath| {
985 try argv.append("-rpath");
986 try argv.append(rpath);
987 }
988
989 if (self.base.options.pagezero_size) |pagezero_size| {
990 try argv.append("-pagezero_size");
991 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
992 }
993
994 if (self.base.options.search_strategy) |strat| switch (strat) {
995 .paths_first => try argv.append("-search_paths_first"),
996 .dylibs_first => try argv.append("-search_dylibs_first"),
997 };
998
999 if (self.base.options.headerpad_size) |headerpad_size| {
1000 try argv.append("-headerpad_size");
1001 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
1002 }
1003
1004 if (self.base.options.headerpad_max_install_names) {
1005 try argv.append("-headerpad_max_install_names");
1006 }
1007
1008 if (gc_sections) {
1009 try argv.append("-dead_strip");
1010 }
1011
1012 if (self.base.options.dead_strip_dylibs) {
1013 try argv.append("-dead_strip_dylibs");
1014 }
1015
1016 if (self.base.options.entry) |entry| {
1017 try argv.append("-e");
1018 try argv.append(entry);
1019 }
1020
1021 for (self.base.options.objects) |obj| {
1022 try argv.append(obj.path);
1023 }
1024
1025 for (comp.c_object_table.keys()) |key| {
1026 try argv.append(key.status.success.object_path);
1027 }
1028
1029 if (module_obj_path) |p| {
1030 try argv.append(p);
1031 }
1032
1033 if (comp.compiler_rt_lib) |lib| {
1034 try argv.append(lib.full_object_path);
1035 }
1036
1037 if (self.base.options.link_libcpp) {
1038 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1039 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1040 }
1041
1042 try argv.append("-o");
1043 try argv.append(full_out_path);
1044
1045 try argv.append("-lSystem");
1046 try argv.append("-lc");
1047
1048 for (self.base.options.system_libs.keys()) |l_name| {
1049 const info = self.base.options.system_libs.get(l_name).?;
1050 const arg = if (info.needed)
1051 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1052 else if (info.weak)
1053 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1054 else
1055 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1056 try argv.append(arg);
1057 }
1058
1059 for (self.base.options.lib_dirs) |lib_dir| {
1060 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1061 }
1062
1063 for (self.base.options.frameworks.keys()) |framework| {
1064 const info = self.base.options.frameworks.get(framework).?;
1065 const arg = if (info.needed)
1066 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1067 else if (info.weak)
1068 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1069 else
1070 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1071 try argv.append(arg);
1072 }
1073
1074 for (self.base.options.framework_dirs) |framework_dir| {
1075 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1076 }
1077
1078 if (is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false)) {
1079 try argv.append("-undefined");
1080 try argv.append("dynamic_lookup");
1081 }
1082
1083 for (must_link_archives.keys()) |lib| {
1084 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
1085 }
1086
1087 Compilation.dump_argv(argv.items);
1088 }
1089
1090 var dependent_libs = std.fifo.LinearFifo(struct {
1091 id: Dylib.Id,
1092 parent: u16,
1093 }, .Dynamic).init(arena);
1094
1095 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1096 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1097 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1098 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1099
1100 for (self.objects.items) |_, object_id| {
1101 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1102 }
1103
1104 try self.resolveSymbolsInArchives();
1105 try self.resolveDyldStubBinder();
1106 try self.resolveSymbolsInDylibs();
1107 try self.createMhExecuteHeaderSymbol();
1108 try self.createDsoHandleSymbol();
1109 try self.resolveSymbolsAtLoading();
1110
1111 if (self.unresolved.count() > 0) {
1112 return error.UndefinedSymbolReference;
1113 }
1114 if (lib_not_found) {
1115 return error.LibraryNotFound;
1116 }
1117 if (framework_not_found) {
1118 return error.FrameworkNotFound;
1119 }
1120
1121 for (self.objects.items) |*object| {
1122 try object.scanInputSections(self);
1123 }
1124
1125 try self.createDyldPrivateAtom();
1126 try self.createTentativeDefAtoms();
1127 try self.createStubHelperPreambleAtom();
1128
1129 for (self.objects.items) |*object, object_id| {
1130 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
1131 }
1132
1133 if (gc_sections) {
1134 try dead_strip.gcAtoms(self);
1135 }
1136
1137 try self.allocateSegments();
1138 try self.allocateSymbols();
1139
1140 try self.allocateSpecialSymbols();
1141
1142 if (build_options.enable_logging or true) {
1143 self.logSymtab();
1144 self.logSections();
1145 self.logAtoms();
1146 }
1147
1148 try self.writeAtomsOneShot();
1149
1150 var lc_buffer = std.ArrayList(u8).init(arena);
1151 const lc_writer = lc_buffer.writer();
1152 var ncmds: u32 = 0;
1153
1154 try self.writeLinkeditSegmentData(&ncmds, lc_writer);
1155
1156 // If the last section of __DATA segment is zerofill section, we need to ensure
1157 // that the free space between the end of the last non-zerofill section of __DATA
1158 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
1159 // copy-paste this space into memory for quicker zerofill operation.
1160 if (self.data_segment_cmd_index) |data_seg_id| blk: {
1161 var physical_zerofill_start: u64 = 0;
1162 const section_indexes = self.getSectionIndexes(data_seg_id);
1163 for (self.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
1164 if (header.isZerofill() and header.size > 0) break;
1165 physical_zerofill_start = header.offset + header.size;
1166 } else break :blk;
1167 const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
1168 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
1169 return error.Overflow;
1170 if (physical_zerofill_size > 0) {
1171 var padding = try self.base.allocator.alloc(u8, physical_zerofill_size);
1172 defer self.base.allocator.free(padding);
1173 mem.set(u8, padding, 0);
1174 try self.base.file.?.pwriteAll(padding, physical_zerofill_start);
1175 }
1176 }
1177
1178 try writeDylinkerLC(&ncmds, lc_writer);
1179 try self.writeMainLC(&ncmds, lc_writer);
1180 try self.writeDylibIdLC(&ncmds, lc_writer);
1181 try self.writeRpathLCs(&ncmds, lc_writer);
1182
1183 {
1184 try lc_writer.writeStruct(macho.source_version_command{
1185 .cmdsize = @sizeOf(macho.source_version_command),
1186 .version = 0x0,
1187 });
1188 ncmds += 1;
1189 }
1190
1191 try self.writeBuildVersionLC(&ncmds, lc_writer);
1192
1193 {
1194 var uuid_lc = macho.uuid_command{
1195 .cmdsize = @sizeOf(macho.uuid_command),
1196 .uuid = undefined,
1197 };
1198 std.crypto.random.bytes(&uuid_lc.uuid);
1199 try lc_writer.writeStruct(uuid_lc);
1200 ncmds += 1;
1201 }
1202
1203 try self.writeLoadDylibLCs(&ncmds, lc_writer);
1204
1205 const requires_codesig = blk: {
1206 if (self.base.options.entitlements) |_| break :blk true;
1207 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
1208 break :blk false;
1209 };
1210 var codesig_offset: ?u32 = null;
1211 var codesig: ?CodeSignature = if (requires_codesig) blk: {
1212 // Preallocate space for the code signature.
1213 // We need to do this at this stage so that we have the load commands with proper values
1214 // written out to the file.
1215 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
1216 // where the code signature goes into.
1217 var codesig = CodeSignature.init(self.page_size);
1218 codesig.code_directory.ident = self.base.options.emit.?.sub_path;
1219 if (self.base.options.entitlements) |path| {
1220 try codesig.addEntitlements(arena, path);
1221 }
1222 codesig_offset = try self.writeCodeSignaturePadding(&codesig, &ncmds, lc_writer);
1223 break :blk codesig;
1224 } else null;
1225
1226 var headers_buf = std.ArrayList(u8).init(arena);
1227 try self.writeSegmentHeaders(&ncmds, headers_buf.writer());
1228
1229 try self.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
1230 try self.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
1231
1232 try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
1233
1234 if (codesig) |*csig| {
1235 try self.writeCodeSignature(csig, codesig_offset.?); // code signing always comes last
1236 }
1237 }
1238
1239 if (!self.base.options.disable_lld_caching) {
1240 // Update the file with the digest. If it fails we can continue; it only
1241 // means that the next invocation will have an unnecessary cache miss.
1242 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1243 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
1244 };
1245 // Again failure here only means an unnecessary cache miss.
1246 man.writeManifest() catch |err| {
1247 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
1248 };
1249 // We hang on to this lock so that the output file path can be used without
1250 // other processes clobbering it.
1251 self.base.lock = man.toOwnedLock();
1252 }
1253}
1254
1255fn resolveLibSystem(
662pub fn resolveLibSystem(
1256663 self: *MachO,
1257664 arena: Allocator,
1258665 comp: *Compilation,
......@@ -1295,7 +702,7 @@ fn resolveLibSystem(
1295702 }
1296703}
1297704
1298fn resolveSearchDir(
705pub fn resolveSearchDir(
1299706 arena: Allocator,
1300707 dir: []const u8,
1301708 syslibroot: ?[]const u8,
......@@ -1337,17 +744,7 @@ fn resolveSearchDir(
1337744 return null;
1338745}
1339746
1340fn resolveSearchDirs(arena: Allocator, dirs: []const []const u8, syslibroot: ?[]const u8, out_dirs: anytype) !void {
1341 for (dirs) |dir| {
1342 if (try resolveSearchDir(arena, dir, syslibroot)) |search_dir| {
1343 try out_dirs.append(search_dir);
1344 } else {
1345 log.warn("directory not found for '-L{s}'", .{dir});
1346 }
1347 }
1348}
1349
1350fn resolveLib(
747pub fn resolveLib(
1351748 arena: Allocator,
1352749 search_dir: []const u8,
1353750 name: []const u8,
......@@ -1366,7 +763,7 @@ fn resolveLib(
1366763 return full_path;
1367764}
1368765
1369fn resolveFramework(
766pub fn resolveFramework(
1370767 arena: Allocator,
1371768 search_dir: []const u8,
1372769 name: []const u8,
......@@ -1576,7 +973,7 @@ pub fn parseDylib(
1576973 return true;
1577974}
1578975
1579fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
976pub fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1580977 for (files) |file_name| {
1581978 const full_path = full_path: {
1582979 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
......@@ -1594,7 +991,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1594991 }
1595992}
1596993
1597fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
994pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
1598995 for (files) |file_name| {
1599996 const full_path = full_path: {
1600997 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
......@@ -1607,10 +1004,10 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
16071004 }
16081005}
16091006
1610fn parseLibs(
1007pub fn parseLibs(
16111008 self: *MachO,
16121009 lib_names: []const []const u8,
1613 lib_infos: []const SystemLib,
1010 lib_infos: []const link.SystemLib,
16141011 syslibroot: ?[]const u8,
16151012 dependent_libs: anytype,
16161013) !void {
......@@ -1628,7 +1025,7 @@ fn parseLibs(
16281025 }
16291026}
16301027
1631fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1028pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
16321029 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
16331030 // 1) anything on the linker line is parsed first
16341031 // 2) afterwards, we parse dependents of the included dylibs
......@@ -1673,168 +1070,6 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
16731070 }
16741071}
16751072
1676pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
1677 const segname = sect.segName();
1678 const sectname = sect.sectName();
1679 const res: ?u8 = blk: {
1680 if (mem.eql(u8, "__LLVM", segname)) {
1681 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
1682 sect.flags, segname, sectname,
1683 });
1684 break :blk null;
1685 }
1686
1687 if (sect.isCode()) {
1688 if (self.text_section_index == null) {
1689 self.text_section_index = try self.initSection(
1690 "__TEXT",
1691 "__text",
1692 sect.size,
1693 sect.@"align",
1694 .{
1695 .flags = macho.S_REGULAR |
1696 macho.S_ATTR_PURE_INSTRUCTIONS |
1697 macho.S_ATTR_SOME_INSTRUCTIONS,
1698 },
1699 );
1700 }
1701 break :blk self.text_section_index.?;
1702 }
1703
1704 if (sect.isDebug()) {
1705 // TODO debug attributes
1706 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
1707 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
1708 sect.flags, segname, sectname,
1709 });
1710 }
1711 break :blk null;
1712 }
1713
1714 switch (sect.@"type"()) {
1715 macho.S_4BYTE_LITERALS,
1716 macho.S_8BYTE_LITERALS,
1717 macho.S_16BYTE_LITERALS,
1718 => {
1719 break :blk self.getSectionByName("__TEXT", "__const") orelse try self.initSection(
1720 "__TEXT",
1721 "__const",
1722 sect.size,
1723 sect.@"align",
1724 .{},
1725 );
1726 },
1727 macho.S_CSTRING_LITERALS => {
1728 if (mem.startsWith(u8, sectname, "__objc")) {
1729 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1730 segname,
1731 sectname,
1732 sect.size,
1733 sect.@"align",
1734 .{},
1735 );
1736 }
1737 break :blk self.getSectionByName("__TEXT", "__cstring") orelse try self.initSection(
1738 "__TEXT",
1739 "__cstring",
1740 sect.size,
1741 sect.@"align",
1742 .{ .flags = macho.S_CSTRING_LITERALS },
1743 );
1744 },
1745 macho.S_MOD_INIT_FUNC_POINTERS,
1746 macho.S_MOD_TERM_FUNC_POINTERS,
1747 => {
1748 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse try self.initSection(
1749 "__DATA_CONST",
1750 sectname,
1751 sect.size,
1752 sect.@"align",
1753 .{ .flags = sect.flags },
1754 );
1755 },
1756 macho.S_LITERAL_POINTERS,
1757 macho.S_ZEROFILL,
1758 macho.S_THREAD_LOCAL_VARIABLES,
1759 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1760 macho.S_THREAD_LOCAL_REGULAR,
1761 macho.S_THREAD_LOCAL_ZEROFILL,
1762 => {
1763 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1764 segname,
1765 sectname,
1766 sect.size,
1767 sect.@"align",
1768 .{ .flags = sect.flags },
1769 );
1770 },
1771 macho.S_COALESCED => {
1772 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1773 segname,
1774 sectname,
1775 sect.size,
1776 sect.@"align",
1777 .{},
1778 );
1779 },
1780 macho.S_REGULAR => {
1781 if (mem.eql(u8, segname, "__TEXT")) {
1782 if (mem.eql(u8, sectname, "__rodata") or
1783 mem.eql(u8, sectname, "__typelink") or
1784 mem.eql(u8, sectname, "__itablink") or
1785 mem.eql(u8, sectname, "__gosymtab") or
1786 mem.eql(u8, sectname, "__gopclntab"))
1787 {
1788 break :blk self.getSectionByName("__DATA_CONST", "__const") orelse try self.initSection(
1789 "__DATA_CONST",
1790 "__const",
1791 sect.size,
1792 sect.@"align",
1793 .{},
1794 );
1795 }
1796 }
1797 if (mem.eql(u8, segname, "__DATA")) {
1798 if (mem.eql(u8, sectname, "__const") or
1799 mem.eql(u8, sectname, "__cfstring") or
1800 mem.eql(u8, sectname, "__objc_classlist") or
1801 mem.eql(u8, sectname, "__objc_imageinfo"))
1802 {
1803 break :blk self.getSectionByName("__DATA_CONST", sectname) orelse
1804 try self.initSection(
1805 "__DATA_CONST",
1806 sectname,
1807 sect.size,
1808 sect.@"align",
1809 .{},
1810 );
1811 } else if (mem.eql(u8, sectname, "__data")) {
1812 if (self.data_section_index == null) {
1813 self.data_section_index = try self.initSection(
1814 segname,
1815 sectname,
1816 sect.size,
1817 sect.@"align",
1818 .{},
1819 );
1820 }
1821 break :blk self.data_section_index.?;
1822 }
1823 }
1824 break :blk self.getSectionByName(segname, sectname) orelse try self.initSection(
1825 segname,
1826 sectname,
1827 sect.size,
1828 sect.@"align",
1829 .{},
1830 );
1831 },
1832 else => break :blk null,
1833 }
1834 };
1835 return res;
1836}
1837
18381073pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
18391074 const size_usize = math.cast(usize, size) orelse return error.Overflow;
18401075 const atom = try gpa.create(Atom);
......@@ -1850,64 +1085,51 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32
18501085 return atom;
18511086}
18521087
1853pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {
1854 const section = self.sections.get(sect_id);
1088pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void {
1089 // TODO: temporary sanity check
1090 assert(atom.code.items.len == 0);
1091 assert(atom.relocs.items.len == 0);
1092 assert(atom.rebases.items.len == 0);
1093 assert(atom.bindings.items.len == 0);
1094 assert(atom.lazy_bindings.items.len == 0);
1095
18551096 const sym = atom.getSymbol(self);
1097 const section = self.sections.get(sym.n_sect - 1);
18561098 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1857 try atom.resolveRelocs(self);
18581099 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1859 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
1100 try self.base.file.?.pwriteAll(code, file_offset);
1101 try atom.resolveRelocations(self);
18601102}
18611103
1862fn allocateSymbols(self: *MachO) !void {
1863 const slice = self.sections.slice();
1864 for (slice.items(.last_atom)) |last_atom, sect_id| {
1865 const header = slice.items(.header)[sect_id];
1866 var atom = last_atom orelse continue;
1104fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {
1105 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1106 try self.writeAtom(atom, &buffer);
1107}
18671108
1868 while (atom.prev) |prev| {
1869 atom = prev;
1109fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1110 // TODO: reverse-lookup might come in handy here
1111 var it = self.relocs.valueIterator();
1112 while (it.next()) |relocs| {
1113 for (relocs.items) |*reloc| {
1114 if (!reloc.target.eql(target)) continue;
1115 reloc.dirty = true;
18701116 }
1117 }
1118}
18711119
1872 const n_sect = @intCast(u8, sect_id + 1);
1873 var base_vaddr = header.addr;
1874
1875 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
1876 n_sect,
1877 header.segName(),
1878 header.sectName(),
1879 });
1880
1881 while (true) {
1882 const alignment = try math.powi(u32, 2, atom.alignment);
1883 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
1884
1885 const sym = atom.getSymbolPtr(self);
1886 sym.n_value = base_vaddr;
1887 sym.n_sect = n_sect;
1888
1889 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
1890
1891 // Update each symbol contained within the atom
1892 for (atom.contained.items) |sym_at_off| {
1893 const contained_sym = self.getSymbolPtr(.{
1894 .sym_index = sym_at_off.sym_index,
1895 .file = atom.file,
1896 });
1897 contained_sym.n_value = base_vaddr + sym_at_off.offset;
1898 contained_sym.n_sect = n_sect;
1899 }
1900
1901 base_vaddr += atom.size;
1902
1903 if (atom.next) |next| {
1904 atom = next;
1905 } else break;
1120fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1121 var it = self.relocs.valueIterator();
1122 while (it.next()) |relocs| {
1123 for (relocs.items) |*reloc| {
1124 const target_atom = reloc.getTargetAtom(self) orelse continue;
1125 const target_sym = target_atom.getSymbol(self);
1126 if (target_sym.n_value < addr) continue;
1127 reloc.dirty = true;
19061128 }
19071129 }
19081130}
19091131
1910fn allocateSpecialSymbols(self: *MachO) !void {
1132pub fn allocateSpecialSymbols(self: *MachO) !void {
19111133 for (&[_][]const u8{
19121134 "___dso_handle",
19131135 "__mh_execute_header",
......@@ -1915,7 +1137,10 @@ fn allocateSpecialSymbols(self: *MachO) !void {
19151137 const global = self.getGlobal(name) orelse continue;
19161138 if (global.file != null) continue;
19171139 const sym = self.getSymbolPtr(global);
1918 const seg = self.segments.items[self.text_segment_cmd_index.?];
1140 const seg = switch (self.mode) {
1141 .incremental => self.getSegment(self.text_section_index.?),
1142 .one_shot => self.segments.items[self.text_segment_cmd_index.?],
1143 };
19191144 sym.n_sect = 1;
19201145 sym.n_value = seg.vmaddr;
19211146
......@@ -1926,214 +1151,133 @@ fn allocateSpecialSymbols(self: *MachO) !void {
19261151 }
19271152}
19281153
1929fn writeAtomsOneShot(self: *MachO) !void {
1930 assert(self.mode == .one_shot);
1931
1154pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
19321155 const gpa = self.base.allocator;
1933 const slice = self.sections.slice();
19341156
1935 for (slice.items(.last_atom)) |last_atom, sect_id| {
1936 const header = slice.items(.header)[sect_id];
1937 if (header.size == 0) continue;
1938 var atom = last_atom.?;
1157 const sym_index = try self.allocateSymbol();
1158 const atom = switch (self.mode) {
1159 .incremental => blk: {
1160 const atom = try gpa.create(Atom);
1161 atom.* = Atom.empty;
1162 atom.sym_index = sym_index;
1163 atom.size = @sizeOf(u64);
1164 atom.alignment = @alignOf(u64);
1165 break :blk atom;
1166 },
1167 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1168 };
1169 errdefer gpa.destroy(atom);
19391170
1940 if (header.isZerofill()) continue;
1171 try self.managed_atoms.append(gpa, atom);
1172 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
19411173
1942 var buffer = std.ArrayList(u8).init(gpa);
1943 defer buffer.deinit();
1944 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
1174 const sym = atom.getSymbolPtr(self);
1175 sym.n_type = macho.N_SECT;
1176 sym.n_sect = self.got_section_index.? + 1;
19451177
1946 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
1178 if (self.mode == .incremental) {
1179 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
19471180
1948 while (atom.prev) |prev| {
1949 atom = prev;
1950 }
1181 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
19511182
1952 while (true) {
1953 const this_sym = atom.getSymbol(self);
1954 const padding_size: usize = if (atom.next) |next| blk: {
1955 const next_sym = next.getSymbol(self);
1956 const size = next_sym.n_value - (this_sym.n_value + atom.size);
1957 break :blk math.cast(usize, size) orelse return error.Overflow;
1958 } else 0;
1183 try atom.addRelocation(self, .{
1184 .@"type" = switch (self.base.options.target.cpu.arch) {
1185 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1186 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1187 else => unreachable,
1188 },
1189 .target = target,
1190 .offset = 0,
1191 .addend = 0,
1192 .pcrel = false,
1193 .length = 3,
1194 });
19591195
1960 log.debug(" (adding ATOM(%{d}, '{s}') from object({?d}) to buffer)", .{
1961 atom.sym_index,
1962 atom.getName(self),
1963 atom.file,
1196 const target_sym = self.getSymbol(target);
1197 if (target_sym.undf()) {
1198 try atom.addBinding(self, .{
1199 .target = self.getGlobal(self.getSymbolName(target)).?,
1200 .offset = 0,
19641201 });
1965 if (padding_size > 0) {
1966 log.debug(" (with padding {x})", .{padding_size});
1967 }
1968
1969 try atom.resolveRelocs(self);
1970 buffer.appendSliceAssumeCapacity(atom.code.items);
1971
1972 var i: usize = 0;
1973 while (i < padding_size) : (i += 1) {
1974 // TODO with NOPs
1975 buffer.appendAssumeCapacity(0);
1976 }
1977
1978 if (atom.next) |next| {
1979 atom = next;
1980 } else {
1981 assert(buffer.items.len == header.size);
1982 log.debug(" (writing at file offset 0x{x})", .{header.offset});
1983 try self.base.file.?.pwriteAll(buffer.items, header.offset);
1984 break;
1985 }
1202 } else {
1203 try atom.addRebase(self, 0);
19861204 }
1987 }
1988}
1989
1990fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {
1991 const header = self.sections.items(.header)[sect_id];
1992 const min_alignment: u3 = if (!header.isCode())
1993 1
1994 else switch (self.base.options.target.cpu.arch) {
1995 .aarch64 => @sizeOf(u32),
1996 .x86_64 => @as(u3, 1),
1997 else => unreachable,
1998 };
1999
2000 const len = @divExact(size, min_alignment);
2001 var i: usize = 0;
2002 while (i < len) : (i += 1) {
2003 if (!header.isCode()) {
2004 try writer.writeByte(0);
2005 } else switch (self.base.options.target.cpu.arch) {
2006 .aarch64 => {
2007 const inst = aarch64.Instruction.nop();
2008 try writer.writeIntLittle(u32, inst.toU32());
2009 },
2010 .x86_64 => {
2011 try writer.writeByte(0x90);
1205 } else {
1206 try atom.relocs.append(gpa, .{
1207 .offset = 0,
1208 .target = target,
1209 .addend = 0,
1210 .subtractor = null,
1211 .pcrel = false,
1212 .length = 3,
1213 .@"type" = switch (self.base.options.target.cpu.arch) {
1214 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1215 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1216 else => unreachable,
20121217 },
2013 else => unreachable,
2014 }
2015 }
2016}
2017
2018fn writeAtomsIncremental(self: *MachO) !void {
2019 assert(self.mode == .incremental);
2020
2021 const slice = self.sections.slice();
2022 for (slice.items(.last_atom)) |last, i| {
2023 var atom: *Atom = last orelse continue;
2024 const sect_i = @intCast(u8, i);
2025 const header = slice.items(.header)[sect_i];
2026
2027 if (header.isZerofill()) continue;
2028
2029 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
2030
2031 while (true) {
2032 if (atom.dirty) {
2033 try self.writeAtom(atom, sect_i);
2034 atom.dirty = false;
2035 }
1218 });
20361219
2037 if (atom.prev) |prev| {
2038 atom = prev;
2039 } else break;
1220 const target_sym = self.getSymbol(target);
1221 if (target_sym.undf()) {
1222 const global = self.getGlobal(self.getSymbolName(target)).?;
1223 try atom.bindings.append(gpa, .{
1224 .target = global,
1225 .offset = 0,
1226 });
1227 } else {
1228 try atom.rebases.append(gpa, 0);
20401229 }
2041 }
2042}
2043
2044pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2045 const gpa = self.base.allocator;
2046 const sym_index = try self.allocateSymbol();
2047 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2048 const sym = atom.getSymbolPtr(self);
2049 sym.n_type = macho.N_SECT;
2050
2051 try atom.relocs.append(gpa, .{
2052 .offset = 0,
2053 .target = target,
2054 .addend = 0,
2055 .subtractor = null,
2056 .pcrel = false,
2057 .length = 3,
2058 .@"type" = switch (self.base.options.target.cpu.arch) {
2059 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2060 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2061 else => unreachable,
2062 },
2063 });
20641230
2065 const target_sym = self.getSymbol(target);
2066 if (target_sym.undf()) {
2067 const global = self.getGlobal(self.getSymbolName(target)).?;
2068 try atom.bindings.append(gpa, .{
2069 .target = global,
2070 .offset = 0,
2071 });
2072 } else {
2073 try atom.rebases.append(gpa, 0);
1231 try self.addAtomToSection(atom);
20741232 }
20751233
2076 try self.managed_atoms.append(gpa, atom);
2077 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2078
2079 try self.allocateAtomCommon(atom, self.got_section_index.?);
2080
2081 return atom;
2082}
2083
2084pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2085 const gpa = self.base.allocator;
2086 const sym_index = try self.allocateSymbol();
2087 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2088 const sym = atom.getSymbolPtr(self);
2089 sym.n_type = macho.N_SECT;
2090
2091 const target_sym = self.getSymbol(target);
2092 assert(target_sym.undf());
2093
2094 const global = self.getGlobal(self.getSymbolName(target)).?;
2095 try atom.bindings.append(gpa, .{
2096 .target = global,
2097 .offset = 0,
2098 });
2099
2100 try self.managed_atoms.append(gpa, atom);
2101 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2102
2103 const match = (try self.getOutputSection(.{
2104 .segname = makeStaticString("__DATA"),
2105 .sectname = makeStaticString("__thread_ptrs"),
2106 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2107 })).?;
2108 try self.allocateAtomCommon(atom, match);
2109
21101234 return atom;
21111235}
21121236
2113fn createDyldPrivateAtom(self: *MachO) !void {
1237pub fn createDyldPrivateAtom(self: *MachO) !void {
21141238 if (self.dyld_stub_binder_index == null) return;
21151239 if (self.dyld_private_atom != null) return;
21161240
21171241 const gpa = self.base.allocator;
1242
21181243 const sym_index = try self.allocateSymbol();
2119 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1244 const atom = switch (self.mode) {
1245 .incremental => blk: {
1246 const atom = try gpa.create(Atom);
1247 atom.* = Atom.empty;
1248 atom.sym_index = sym_index;
1249 atom.size = @sizeOf(u64);
1250 atom.alignment = @alignOf(u64);
1251 break :blk atom;
1252 },
1253 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1254 };
1255 errdefer gpa.destroy(atom);
1256
21201257 const sym = atom.getSymbolPtr(self);
21211258 sym.n_type = macho.N_SECT;
1259 sym.n_sect = self.data_section_index.? + 1;
21221260 self.dyld_private_atom = atom;
21231261
2124 try self.allocateAtomCommon(atom, self.data_section_index.?);
2125
21261262 try self.managed_atoms.append(gpa, atom);
21271263 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1264
1265 if (self.mode == .incremental) {
1266 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1267 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1268 try self.writePtrWidthAtom(atom);
1269 } else {
1270 try self.addAtomToSection(atom);
1271 }
21281272}
21291273
2130fn createStubHelperPreambleAtom(self: *MachO) !void {
1274pub fn createStubHelperPreambleAtom(self: *MachO) !void {
21311275 if (self.dyld_stub_binder_index == null) return;
21321276 if (self.stub_helper_preamble_atom != null) return;
21331277
21341278 const gpa = self.base.allocator;
21351279 const arch = self.base.options.target.cpu.arch;
2136 const size: u64 = switch (arch) {
1280 const size: u5 = switch (arch) {
21371281 .x86_64 => 15,
21381282 .aarch64 => 6 * @sizeOf(u32),
21391283 else => unreachable,
......@@ -2144,117 +1288,200 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
21441288 else => unreachable,
21451289 };
21461290 const sym_index = try self.allocateSymbol();
2147 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
1291 const atom = switch (self.mode) {
1292 .incremental => blk: {
1293 const atom = try gpa.create(Atom);
1294 atom.* = Atom.empty;
1295 atom.sym_index = sym_index;
1296 atom.size = size;
1297 atom.alignment = switch (arch) {
1298 .x86_64 => 1,
1299 .aarch64 => @alignOf(u32),
1300 else => unreachable,
1301 };
1302 break :blk atom;
1303 },
1304 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1305 };
1306 errdefer gpa.destroy(atom);
1307
21481308 const sym = atom.getSymbolPtr(self);
21491309 sym.n_type = macho.N_SECT;
1310 sym.n_sect = self.stub_helper_section_index.? + 1;
21501311
21511312 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
1313
1314 const code = try gpa.alloc(u8, size);
1315 defer gpa.free(code);
1316 mem.set(u8, code, 0);
1317
21521318 switch (arch) {
21531319 .x86_64 => {
2154 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
21551320 // lea %r11, [rip + disp]
2156 atom.code.items[0] = 0x4c;
2157 atom.code.items[1] = 0x8d;
2158 atom.code.items[2] = 0x1d;
2159 atom.relocs.appendAssumeCapacity(.{
2160 .offset = 3,
2161 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2162 .addend = 0,
2163 .subtractor = null,
2164 .pcrel = true,
2165 .length = 2,
2166 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
2167 });
1321 code[0] = 0x4c;
1322 code[1] = 0x8d;
1323 code[2] = 0x1d;
21681324 // push %r11
2169 atom.code.items[7] = 0x41;
2170 atom.code.items[8] = 0x53;
1325 code[7] = 0x41;
1326 code[8] = 0x53;
21711327 // jmp [rip + disp]
2172 atom.code.items[9] = 0xff;
2173 atom.code.items[10] = 0x25;
2174 atom.relocs.appendAssumeCapacity(.{
2175 .offset = 11,
2176 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2177 .addend = 0,
2178 .subtractor = null,
2179 .pcrel = true,
2180 .length = 2,
2181 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
2182 });
1328 code[9] = 0xff;
1329 code[10] = 0x25;
1330
1331 if (self.mode == .incremental) {
1332 try atom.addRelocations(self, 2, .{ .{
1333 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1334 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1335 .offset = 3,
1336 .addend = 0,
1337 .pcrel = true,
1338 .length = 2,
1339 }, .{
1340 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1341 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1342 .offset = 11,
1343 .addend = 0,
1344 .pcrel = true,
1345 .length = 2,
1346 } });
1347 } else {
1348 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
1349 atom.relocs.appendAssumeCapacity(.{
1350 .offset = 3,
1351 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1352 .addend = 0,
1353 .subtractor = null,
1354 .pcrel = true,
1355 .length = 2,
1356 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1357 });
1358 atom.relocs.appendAssumeCapacity(.{
1359 .offset = 11,
1360 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1361 .addend = 0,
1362 .subtractor = null,
1363 .pcrel = true,
1364 .length = 2,
1365 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),
1366 });
1367 }
21831368 },
1369
21841370 .aarch64 => {
2185 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 4);
21861371 // adrp x17, 0
2187 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
2188 atom.relocs.appendAssumeCapacity(.{
2189 .offset = 0,
2190 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2191 .addend = 0,
2192 .subtractor = null,
2193 .pcrel = true,
2194 .length = 2,
2195 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
2196 });
1372 mem.writeIntLittle(u32, code[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
21971373 // add x17, x17, 0
2198 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
2199 atom.relocs.appendAssumeCapacity(.{
2200 .offset = 4,
2201 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2202 .addend = 0,
2203 .subtractor = null,
2204 .pcrel = false,
2205 .length = 2,
2206 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
2207 });
1374 mem.writeIntLittle(u32, code[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
22081375 // stp x16, x17, [sp, #-16]!
2209 mem.writeIntLittle(u32, atom.code.items[8..][0..4], aarch64.Instruction.stp(
1376 mem.writeIntLittle(u32, code[8..][0..4], aarch64.Instruction.stp(
22101377 .x16,
22111378 .x17,
22121379 aarch64.Register.sp,
22131380 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
22141381 ).toU32());
22151382 // adrp x16, 0
2216 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2217 atom.relocs.appendAssumeCapacity(.{
2218 .offset = 12,
2219 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2220 .addend = 0,
2221 .subtractor = null,
2222 .pcrel = true,
2223 .length = 2,
2224 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
2225 });
1383 mem.writeIntLittle(u32, code[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
22261384 // ldr x16, [x16, 0]
2227 mem.writeIntLittle(u32, atom.code.items[16..][0..4], aarch64.Instruction.ldr(
1385 mem.writeIntLittle(u32, code[16..][0..4], aarch64.Instruction.ldr(
22281386 .x16,
22291387 .x16,
22301388 aarch64.Instruction.LoadStoreOffset.imm(0),
22311389 ).toU32());
2232 atom.relocs.appendAssumeCapacity(.{
2233 .offset = 16,
2234 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2235 .addend = 0,
2236 .subtractor = null,
2237 .pcrel = false,
2238 .length = 2,
2239 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
2240 });
22411390 // br x16
2242 mem.writeIntLittle(u32, atom.code.items[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1391 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
1392
1393 if (self.mode == .incremental) {
1394 try atom.addRelocations(self, 4, .{ .{
1395 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1396 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1397 .offset = 0,
1398 .addend = 0,
1399 .pcrel = true,
1400 .length = 2,
1401 }, .{
1402 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1403 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1404 .offset = 4,
1405 .addend = 0,
1406 .pcrel = false,
1407 .length = 2,
1408 }, .{
1409 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1410 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1411 .offset = 12,
1412 .addend = 0,
1413 .pcrel = true,
1414 .length = 2,
1415 }, .{
1416 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1417 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1418 .offset = 16,
1419 .addend = 0,
1420 .pcrel = false,
1421 .length = 2,
1422 } });
1423 } else {
1424 try atom.relocs.ensureUnusedCapacity(gpa, 4);
1425 atom.relocs.appendAssumeCapacity(.{
1426 .offset = 0,
1427 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1428 .addend = 0,
1429 .subtractor = null,
1430 .pcrel = true,
1431 .length = 2,
1432 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1433 });
1434 atom.relocs.appendAssumeCapacity(.{
1435 .offset = 4,
1436 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1437 .addend = 0,
1438 .subtractor = null,
1439 .pcrel = false,
1440 .length = 2,
1441 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1442 });
1443 atom.relocs.appendAssumeCapacity(.{
1444 .offset = 12,
1445 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1446 .addend = 0,
1447 .subtractor = null,
1448 .pcrel = true,
1449 .length = 2,
1450 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
1451 });
1452 atom.relocs.appendAssumeCapacity(.{
1453 .offset = 16,
1454 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
1455 .addend = 0,
1456 .subtractor = null,
1457 .pcrel = false,
1458 .length = 2,
1459 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
1460 });
1461 }
22431462 },
1463
22441464 else => unreachable,
22451465 }
22461466 self.stub_helper_preamble_atom = atom;
22471467
2248 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2249
22501468 try self.managed_atoms.append(gpa, atom);
22511469 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1470
1471 if (self.mode == .incremental) {
1472 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1473 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1474 try self.writeAtom(atom, code);
1475 } else {
1476 mem.copy(u8, atom.code.items, code);
1477 try self.addAtomToSection(atom);
1478 }
22521479}
22531480
22541481pub fn createStubHelperAtom(self: *MachO) !*Atom {
22551482 const gpa = self.base.allocator;
22561483 const arch = self.base.options.target.cpu.arch;
2257 const stub_size: u4 = switch (arch) {
1484 const size: u4 = switch (arch) {
22581485 .x86_64 => 10,
22591486 .aarch64 => 3 * @sizeOf(u32),
22601487 else => unreachable,
......@@ -2265,51 +1492,96 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
22651492 else => unreachable,
22661493 };
22671494 const sym_index = try self.allocateSymbol();
2268 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
1495 const atom = switch (self.mode) {
1496 .incremental => blk: {
1497 const atom = try gpa.create(Atom);
1498 atom.* = Atom.empty;
1499 atom.sym_index = sym_index;
1500 atom.size = size;
1501 atom.alignment = switch (arch) {
1502 .x86_64 => 1,
1503 .aarch64 => @alignOf(u32),
1504 else => unreachable,
1505 };
1506 break :blk atom;
1507 },
1508 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1509 };
1510 errdefer gpa.destroy(atom);
1511
22691512 const sym = atom.getSymbolPtr(self);
22701513 sym.n_type = macho.N_SECT;
1514 sym.n_sect = self.stub_helper_section_index.? + 1;
22711515
2272 try atom.relocs.ensureTotalCapacity(gpa, 1);
1516 const code = try gpa.alloc(u8, size);
1517 defer gpa.free(code);
1518 mem.set(u8, code, 0);
22731519
22741520 switch (arch) {
22751521 .x86_64 => {
22761522 // pushq
2277 atom.code.items[0] = 0x68;
1523 code[0] = 0x68;
22781524 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
22791525 // jmpq
2280 atom.code.items[5] = 0xe9;
2281 atom.relocs.appendAssumeCapacity(.{
2282 .offset = 6,
2283 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2284 .addend = 0,
2285 .subtractor = null,
2286 .pcrel = true,
2287 .length = 2,
2288 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2289 });
1526 code[5] = 0xe9;
1527
1528 if (self.mode == .incremental) {
1529 try atom.addRelocation(self, .{
1530 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1531 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1532 .offset = 6,
1533 .addend = 0,
1534 .pcrel = true,
1535 .length = 2,
1536 });
1537 } else {
1538 try atom.relocs.ensureTotalCapacity(gpa, 1);
1539 atom.relocs.appendAssumeCapacity(.{
1540 .offset = 6,
1541 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1542 .addend = 0,
1543 .subtractor = null,
1544 .pcrel = true,
1545 .length = 2,
1546 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1547 });
1548 }
22901549 },
22911550 .aarch64 => {
22921551 const literal = blk: {
2293 const div_res = try math.divExact(u64, stub_size - @sizeOf(u32), 4);
1552 const div_res = try math.divExact(u64, size - @sizeOf(u32), 4);
22941553 break :blk math.cast(u18, div_res) orelse return error.Overflow;
22951554 };
22961555 // ldr w16, literal
2297 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.ldrLiteral(
1556 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldrLiteral(
22981557 .w16,
22991558 literal,
23001559 ).toU32());
23011560 // b disp
2302 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2303 atom.relocs.appendAssumeCapacity(.{
2304 .offset = 4,
2305 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2306 .addend = 0,
2307 .subtractor = null,
2308 .pcrel = true,
2309 .length = 2,
2310 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
2311 });
1561 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
23121562 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
1563
1564 if (self.mode == .incremental) {
1565 try atom.addRelocation(self, .{
1566 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1567 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1568 .offset = 4,
1569 .addend = 0,
1570 .pcrel = true,
1571 .length = 2,
1572 });
1573 } else {
1574 try atom.relocs.ensureTotalCapacity(gpa, 1);
1575 atom.relocs.appendAssumeCapacity(.{
1576 .offset = 4,
1577 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
1578 .addend = 0,
1579 .subtractor = null,
1580 .pcrel = true,
1581 .length = 2,
1582 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1583 });
1584 }
23131585 },
23141586 else => unreachable,
23151587 }
......@@ -2317,7 +1589,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
23171589 try self.managed_atoms.append(gpa, atom);
23181590 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
23191591
2320 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
1592 if (self.mode == .incremental) {
1593 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1594 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1595 try self.writeAtom(atom, code);
1596 } else {
1597 mem.copy(u8, atom.code.items, code);
1598 try self.addAtomToSection(atom);
1599 }
23211600
23221601 return atom;
23231602}
......@@ -2325,35 +1604,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
23251604pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
23261605 const gpa = self.base.allocator;
23271606 const sym_index = try self.allocateSymbol();
2328 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1607 const atom = switch (self.mode) {
1608 .incremental => blk: {
1609 const atom = try gpa.create(Atom);
1610 atom.* = Atom.empty;
1611 atom.sym_index = sym_index;
1612 atom.size = @sizeOf(u64);
1613 atom.alignment = @alignOf(u64);
1614 break :blk atom;
1615 },
1616 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3),
1617 };
1618 errdefer gpa.destroy(atom);
1619
23291620 const sym = atom.getSymbolPtr(self);
23301621 sym.n_type = macho.N_SECT;
1622 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
23311623
2332 try atom.relocs.append(gpa, .{
2333 .offset = 0,
2334 .target = .{ .sym_index = stub_sym_index, .file = null },
2335 .addend = 0,
2336 .subtractor = null,
2337 .pcrel = false,
2338 .length = 3,
2339 .@"type" = switch (self.base.options.target.cpu.arch) {
2340 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2341 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2342 else => unreachable,
2343 },
2344 });
2345 try atom.rebases.append(gpa, 0);
2346
2347 const global = self.getGlobal(self.getSymbolName(target)).?;
2348 try atom.lazy_bindings.append(gpa, .{
2349 .target = global,
2350 .offset = 0,
2351 });
1624 if (self.mode == .incremental) {
1625 try atom.addRelocation(self, .{
1626 .@"type" = switch (self.base.options.target.cpu.arch) {
1627 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1628 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1629 else => unreachable,
1630 },
1631 .target = .{ .sym_index = stub_sym_index, .file = null },
1632 .offset = 0,
1633 .addend = 0,
1634 .pcrel = false,
1635 .length = 3,
1636 });
1637 try atom.addRebase(self, 0);
1638 try atom.addLazyBinding(self, .{
1639 .target = self.getGlobal(self.getSymbolName(target)).?,
1640 .offset = 0,
1641 });
1642 } else {
1643 try atom.relocs.append(gpa, .{
1644 .offset = 0,
1645 .target = .{ .sym_index = stub_sym_index, .file = null },
1646 .addend = 0,
1647 .subtractor = null,
1648 .pcrel = false,
1649 .length = 3,
1650 .@"type" = switch (self.base.options.target.cpu.arch) {
1651 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1652 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1653 else => unreachable,
1654 },
1655 });
1656 try atom.rebases.append(gpa, 0);
1657 const global = self.getGlobal(self.getSymbolName(target)).?;
1658 try atom.lazy_bindings.append(gpa, .{
1659 .target = global,
1660 .offset = 0,
1661 });
1662 }
23521663
23531664 try self.managed_atoms.append(gpa, atom);
23541665 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
23551666
2356 try self.allocateAtomCommon(atom, self.la_symbol_ptr_section_index.?);
1667 if (self.mode == .incremental) {
1668 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1669 log.debug("allocated lazy pointer atom at 0x{x}", .{sym.n_value});
1670 try self.writePtrWidthAtom(atom);
1671 } else {
1672 try self.addAtomToSection(atom);
1673 }
23571674
23581675 return atom;
23591676}
......@@ -2366,61 +1683,117 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
23661683 .aarch64 => 2,
23671684 else => unreachable, // unhandled architecture type
23681685 };
2369 const stub_size: u4 = switch (arch) {
1686 const size: u4 = switch (arch) {
23701687 .x86_64 => 6,
23711688 .aarch64 => 3 * @sizeOf(u32),
23721689 else => unreachable, // unhandled architecture type
23731690 };
23741691 const sym_index = try self.allocateSymbol();
2375 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
1692 const atom = switch (self.mode) {
1693 .incremental => blk: {
1694 const atom = try gpa.create(Atom);
1695 atom.* = Atom.empty;
1696 atom.sym_index = sym_index;
1697 atom.size = size;
1698 atom.alignment = switch (arch) {
1699 .x86_64 => 1,
1700 .aarch64 => @alignOf(u32),
1701 else => unreachable, // unhandled architecture type
1702
1703 };
1704 break :blk atom;
1705 },
1706 .one_shot => try MachO.createEmptyAtom(gpa, sym_index, size, alignment),
1707 };
1708 errdefer gpa.destroy(atom);
1709
23761710 const sym = atom.getSymbolPtr(self);
23771711 sym.n_type = macho.N_SECT;
1712 sym.n_sect = self.stubs_section_index.? + 1;
1713
1714 const code = try gpa.alloc(u8, size);
1715 defer gpa.free(code);
1716 mem.set(u8, code, 0);
23781717
23791718 switch (arch) {
23801719 .x86_64 => {
23811720 // jmp
2382 atom.code.items[0] = 0xff;
2383 atom.code.items[1] = 0x25;
2384 try atom.relocs.append(gpa, .{
2385 .offset = 2,
2386 .target = .{ .sym_index = laptr_sym_index, .file = null },
2387 .addend = 0,
2388 .subtractor = null,
2389 .pcrel = true,
2390 .length = 2,
2391 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2392 });
1721 code[0] = 0xff;
1722 code[1] = 0x25;
1723
1724 if (self.mode == .incremental) {
1725 try atom.addRelocation(self, .{
1726 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1727 .target = .{ .sym_index = laptr_sym_index, .file = null },
1728 .offset = 2,
1729 .addend = 0,
1730 .pcrel = true,
1731 .length = 2,
1732 });
1733 } else {
1734 try atom.relocs.append(gpa, .{
1735 .offset = 2,
1736 .target = .{ .sym_index = laptr_sym_index, .file = null },
1737 .addend = 0,
1738 .subtractor = null,
1739 .pcrel = true,
1740 .length = 2,
1741 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1742 });
1743 }
23931744 },
23941745 .aarch64 => {
2395 try atom.relocs.ensureTotalCapacity(gpa, 2);
23961746 // adrp x16, pages
2397 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2398 atom.relocs.appendAssumeCapacity(.{
2399 .offset = 0,
2400 .target = .{ .sym_index = laptr_sym_index, .file = null },
2401 .addend = 0,
2402 .subtractor = null,
2403 .pcrel = true,
2404 .length = 2,
2405 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
2406 });
1747 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
24071748 // ldr x16, x16, offset
2408 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.ldr(
1749 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.ldr(
24091750 .x16,
24101751 .x16,
24111752 aarch64.Instruction.LoadStoreOffset.imm(0),
24121753 ).toU32());
2413 atom.relocs.appendAssumeCapacity(.{
2414 .offset = 4,
2415 .target = .{ .sym_index = laptr_sym_index, .file = null },
2416 .addend = 0,
2417 .subtractor = null,
2418 .pcrel = false,
2419 .length = 2,
2420 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
2421 });
24221754 // br x16
2423 mem.writeIntLittle(u32, atom.code.items[8..12], aarch64.Instruction.br(.x16).toU32());
1755 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
1756
1757 if (self.mode == .incremental) {
1758 try atom.addRelocations(self, 2, .{
1759 .{
1760 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1761 .target = .{ .sym_index = laptr_sym_index, .file = null },
1762 .offset = 0,
1763 .addend = 0,
1764 .pcrel = true,
1765 .length = 2,
1766 },
1767 .{
1768 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1769 .target = .{ .sym_index = laptr_sym_index, .file = null },
1770 .offset = 4,
1771 .addend = 0,
1772 .pcrel = false,
1773 .length = 2,
1774 },
1775 });
1776 } else {
1777 try atom.relocs.ensureTotalCapacity(gpa, 2);
1778 atom.relocs.appendAssumeCapacity(.{
1779 .offset = 0,
1780 .target = .{ .sym_index = laptr_sym_index, .file = null },
1781 .addend = 0,
1782 .subtractor = null,
1783 .pcrel = true,
1784 .length = 2,
1785 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1786 });
1787 atom.relocs.appendAssumeCapacity(.{
1788 .offset = 4,
1789 .target = .{ .sym_index = laptr_sym_index, .file = null },
1790 .addend = 0,
1791 .subtractor = null,
1792 .pcrel = false,
1793 .length = 2,
1794 .@"type" = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12),
1795 });
1796 }
24241797 },
24251798 else => unreachable,
24261799 }
......@@ -2428,12 +1801,53 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
24281801 try self.managed_atoms.append(gpa, atom);
24291802 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
24301803
2431 try self.allocateAtomCommon(atom, self.stubs_section_index.?);
1804 if (self.mode == .incremental) {
1805 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1806 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1807 try self.writeAtom(atom, code);
1808 } else {
1809 mem.copy(u8, atom.code.items, code);
1810 try self.addAtomToSection(atom);
1811 }
1812
1813 return atom;
1814}
1815
1816pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1817 assert(self.mode == .one_shot);
1818
1819 const gpa = self.base.allocator;
1820 const sym_index = try self.allocateSymbol();
1821 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
1822
1823 const target_sym = self.getSymbol(target);
1824 assert(target_sym.undf());
1825
1826 const global = self.getGlobal(self.getSymbolName(target)).?;
1827 try atom.bindings.append(gpa, .{
1828 .target = global,
1829 .offset = 0,
1830 });
1831
1832 try self.managed_atoms.append(gpa, atom);
1833 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1834
1835 const sym = atom.getSymbolPtr(self);
1836 sym.n_type = macho.N_SECT;
1837 const sect_id = (try self.getOutputSection(.{
1838 .segname = makeStaticString("__DATA"),
1839 .sectname = makeStaticString("__thread_ptrs"),
1840 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
1841 })).?;
1842 sym.n_sect = sect_id + 1;
1843
1844 try self.addAtomToSection(atom);
24321845
24331846 return atom;
24341847}
24351848
2436fn createTentativeDefAtoms(self: *MachO) !void {
1849pub fn createTentativeDefAtoms(self: *MachO) !void {
1850 assert(self.mode == .one_shot);
24371851 const gpa = self.base.allocator;
24381852
24391853 for (self.globals.items) |global| {
......@@ -2448,16 +1862,15 @@ fn createTentativeDefAtoms(self: *MachO) !void {
24481862 // text blocks for each tentative definition.
24491863 const size = sym.n_value;
24501864 const alignment = (sym.n_desc >> 8) & 0x0f;
2451 const n_sect = (try self.getOutputSection(.{
1865 const sect_id = (try self.getOutputSection(.{
24521866 .segname = makeStaticString("__DATA"),
24531867 .sectname = makeStaticString("__bss"),
24541868 .flags = macho.S_ZEROFILL,
24551869 })).?;
2456
24571870 sym.* = .{
24581871 .n_strx = sym.n_strx,
24591872 .n_type = macho.N_SECT | macho.N_EXT,
2460 .n_sect = n_sect,
1873 .n_sect = sect_id + 1,
24611874 .n_desc = 0,
24621875 .n_value = 0,
24631876 };
......@@ -2465,7 +1878,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
24651878 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
24661879 atom.file = global.file;
24671880
2468 try self.allocateAtomCommon(atom, n_sect);
1881 try self.addAtomToSection(atom);
24691882
24701883 if (global.file) |file| {
24711884 const object = &self.objects.items[file];
......@@ -2478,7 +1891,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
24781891 }
24791892}
24801893
2481fn createMhExecuteHeaderSymbol(self: *MachO) !void {
1894pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
24821895 if (self.base.options.output_mode != .Exe) return;
24831896 if (self.getGlobal("__mh_execute_header")) |global| {
24841897 const sym = self.getSymbol(global);
......@@ -2501,7 +1914,7 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
25011914 gop.value_ptr.* = sym_loc;
25021915}
25031916
2504fn createDsoHandleSymbol(self: *MachO) !void {
1917pub fn createDsoHandleSymbol(self: *MachO) !void {
25051918 const global = self.getGlobalPtr("___dso_handle") orelse return;
25061919 if (!self.getSymbol(global.*).undf()) return;
25071920
......@@ -2576,7 +1989,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
25761989 gop.value_ptr.* = current;
25771990}
25781991
2579fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
1992pub fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
25801993 const object = &self.objects.items[object_id];
25811994 log.debug("resolving symbols in '{s}'", .{object.name});
25821995
......@@ -2629,7 +2042,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
26292042 }
26302043}
26312044
2632fn resolveSymbolsInArchives(self: *MachO) !void {
2045pub fn resolveSymbolsInArchives(self: *MachO) !void {
26332046 if (self.archives.items.len == 0) return;
26342047
26352048 const gpa = self.base.allocator;
......@@ -2660,7 +2073,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
26602073 }
26612074}
26622075
2663fn resolveSymbolsInDylibs(self: *MachO) !void {
2076pub fn resolveSymbolsInDylibs(self: *MachO) !void {
26642077 if (self.dylibs.items.len == 0) return;
26652078
26662079 const gpa = self.base.allocator;
......@@ -2697,6 +2110,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
26972110 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
26982111 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
26992112 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
2113 self.markRelocsDirtyByTarget(global);
27002114 }
27012115
27022116 continue :loop;
......@@ -2706,7 +2120,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
27062120 }
27072121}
27082122
2709fn resolveSymbolsAtLoading(self: *MachO) !void {
2123pub fn resolveSymbolsAtLoading(self: *MachO) !void {
27102124 const is_lib = self.base.options.output_mode == .Lib;
27112125 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
27122126 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
......@@ -2749,7 +2163,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
27492163 }
27502164}
27512165
2752fn resolveDyldStubBinder(self: *MachO) !void {
2166pub fn resolveDyldStubBinder(self: *MachO) !void {
27532167 if (self.dyld_stub_binder_index != null) return;
27542168 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
27552169
......@@ -2794,9 +2208,13 @@ fn resolveDyldStubBinder(self: *MachO) !void {
27942208 const got_index = try self.allocateGotEntry(global);
27952209 const got_atom = try self.createGotAtom(global);
27962210 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
2211
2212 if (self.mode == .incremental) {
2213 try self.writePtrWidthAtom(got_atom);
2214 }
27972215}
27982216
2799fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2217pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
28002218 const name_len = mem.sliceTo(default_dyld_path, 0).len;
28012219 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
28022220 u64,
......@@ -2816,9 +2234,13 @@ fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
28162234 ncmds.* += 1;
28172235}
28182236
2819fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2237pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
28202238 if (self.base.options.output_mode != .Exe) return;
2821 const seg = self.segments.items[self.text_segment_cmd_index.?];
2239 const seg_id = switch (self.mode) {
2240 .incremental => self.header_segment_cmd_index.?,
2241 .one_shot => self.text_segment_cmd_index.?,
2242 };
2243 const seg = self.segments.items[seg_id];
28222244 const global = try self.getEntryPoint();
28232245 const sym = self.getSymbol(global);
28242246 try lc_writer.writeStruct(macho.entry_point_command{
......@@ -2838,7 +2260,7 @@ const WriteDylibLCCtx = struct {
28382260 compatibility_version: u32 = 0x10000,
28392261};
28402262
2841fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2263pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
28422264 const name_len = ctx.name.len + 1;
28432265 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
28442266 u64,
......@@ -2864,7 +2286,7 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
28642286 ncmds.* += 1;
28652287}
28662288
2867fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2289pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
28682290 if (self.base.options.output_mode != .Lib) return;
28692291 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
28702292 const curr = self.base.options.version orelse std.builtin.Version{
......@@ -2910,7 +2332,7 @@ const RpathIterator = struct {
29102332 }
29112333};
29122334
2913fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2335pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
29142336 const gpa = self.base.allocator;
29152337
29162338 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
......@@ -2937,7 +2359,7 @@ fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
29372359 }
29382360}
29392361
2940fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2362pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
29412363 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
29422364 const platform_version = blk: {
29432365 const ver = self.base.options.target.os.version_range.semver.min;
......@@ -2970,7 +2392,7 @@ fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
29702392 ncmds.* += 1;
29712393}
29722394
2973fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2395pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
29742396 for (self.referenced_dylibs.keys()) |id| {
29752397 const dylib = self.dylibs.items[id];
29762398 const dylib_id = dylib.id orelse unreachable;
......@@ -3069,14 +2491,49 @@ pub fn deinit(self: *MachO) void {
30692491 }
30702492
30712493 self.atom_by_index_table.deinit(gpa);
2494
2495 {
2496 var it = self.relocs.valueIterator();
2497 while (it.next()) |relocs| {
2498 relocs.deinit(gpa);
2499 }
2500 self.relocs.deinit(gpa);
2501 }
2502
2503 {
2504 var it = self.rebases.valueIterator();
2505 while (it.next()) |rebases| {
2506 rebases.deinit(gpa);
2507 }
2508 self.rebases.deinit(gpa);
2509 }
2510
2511 {
2512 var it = self.bindings.valueIterator();
2513 while (it.next()) |bindings| {
2514 bindings.deinit(gpa);
2515 }
2516 self.bindings.deinit(gpa);
2517 }
2518
2519 {
2520 var it = self.lazy_bindings.valueIterator();
2521 while (it.next()) |bindings| {
2522 bindings.deinit(gpa);
2523 }
2524 self.lazy_bindings.deinit(gpa);
2525 }
30722526}
30732527
3074fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
2528fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
30752529 log.debug("freeAtom {*}", .{atom});
30762530 if (!owns_atom) {
30772531 atom.deinit(self.base.allocator);
30782532 }
2533 // Remove any relocs and base relocs associated with this Atom
2534 self.freeRelocationsForAtom(atom);
30792535
2536 const sect_id = atom.getSymbol(self).n_sect - 1;
30802537 const free_list = &self.sections.items(.free_list)[sect_id];
30812538 var already_have_free_list_node = false;
30822539 {
......@@ -3129,21 +2586,20 @@ fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
31292586 }
31302587}
31312588
3132fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, sect_id: u8) void {
2589fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void {
31332590 _ = self;
31342591 _ = atom;
31352592 _ = new_block_size;
3136 _ = sect_id;
31372593 // TODO check the new capacity, and if it crosses the size threshold into a big enough
31382594 // capacity, insert a free list node for it.
31392595}
31402596
3141fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, sect_id: u8) !u64 {
2597fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
31422598 const sym = atom.getSymbol(self);
31432599 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
31442600 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
31452601 if (!need_realloc) return sym.n_value;
3146 return self.allocateAtom(atom, new_atom_size, alignment, sect_id);
2602 return self.allocateAtom(atom, new_atom_size, alignment);
31472603}
31482604
31492605fn allocateSymbol(self: *MachO) !u32 {
......@@ -3282,13 +2738,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
32822738 const decl_index = func.owner_decl;
32832739 const decl = module.declPtr(decl_index);
32842740 self.freeUnnamedConsts(decl_index);
3285
3286 // TODO clearing the code and relocs buffer should probably be orchestrated
3287 // in a different, smarter, more automatic way somewhere else, in a more centralised
3288 // way than this.
3289 // If we don't clear the buffers here, we are up for some nasty surprises when
3290 // this atom is reused later on and was not freed by freeAtom().
3291 decl.link.macho.clearRetainingCapacity();
2741 self.freeRelocationsForAtom(&decl.link.macho);
32922742
32932743 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
32942744 defer code_buffer.deinit();
......@@ -3306,18 +2756,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
33062756 else
33072757 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
33082758
3309 switch (res) {
3310 .appended => {
3311 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3312 },
2759 const code = switch (res) {
2760 .appended => code_buffer.items,
33132761 .fail => |em| {
33142762 decl.analysis = .codegen_failure;
33152763 try module.failed_decls.put(module.gpa, decl_index, em);
33162764 return;
33172765 },
3318 }
2766 };
33192767
3320 const addr = try self.placeDecl(decl_index, decl.link.macho.code.items.len);
2768 const addr = try self.updateDeclCode(decl_index, code);
33212769
33222770 if (decl_state) |*ds| {
33232771 try self.d_sym.?.dwarf.commitDeclState(
......@@ -3363,20 +2811,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
33632811
33642812 log.debug("allocating symbol indexes for {?s}", .{name});
33652813
3366 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3367 const sym_index = try self.allocateSymbol();
3368 const atom = try MachO.createEmptyAtom(
3369 gpa,
3370 sym_index,
3371 @sizeOf(u64),
3372 math.log2(required_alignment),
3373 );
2814 const atom = try gpa.create(Atom);
2815 errdefer gpa.destroy(atom);
2816 atom.* = Atom.empty;
2817
2818 atom.sym_index = try self.allocateSymbol();
33742819
33752820 try self.managed_atoms.append(gpa, atom);
3376 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2821 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
33772822
33782823 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
3379 .parent_atom_index = sym_index,
2824 .parent_atom_index = atom.sym_index,
33802825 });
33812826 const code = switch (res) {
33822827 .externally_managed => |x| x,
......@@ -3389,33 +2834,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
33892834 },
33902835 };
33912836
3392 atom.code.clearRetainingCapacity();
3393 try atom.code.appendSlice(gpa, code);
2837 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2838 atom.size = code.len;
2839 atom.alignment = required_alignment;
2840 // TODO: work out logic for disambiguating functions from function pointers
2841 // const sect_id = self.getDeclOutputSection(decl);
2842 const sect_id = self.data_const_section_index.?;
2843 const symbol = atom.getSymbolPtr(self);
2844 symbol.n_strx = name_str_index;
2845 symbol.n_type = macho.N_SECT;
2846 symbol.n_sect = sect_id + 1;
2847 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2848 errdefer self.freeAtom(atom, true);
33942849
3395 const sect_id = try self.getOutputSectionAtom(
3396 atom,
3397 decl_name,
3398 typed_value.ty,
3399 typed_value.val,
3400 required_alignment,
3401 );
3402 const addr = try self.allocateAtom(atom, code.len, required_alignment, sect_id);
2850 try unnamed_consts.append(gpa, atom);
34032851
3404 log.debug("allocated atom for {?s} at 0x{x}", .{ name, addr });
2852 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
34052853 log.debug(" (required alignment 0x{x})", .{required_alignment});
34062854
3407 errdefer self.freeAtom(atom, sect_id, true);
3408
3409 const symbol = atom.getSymbolPtr(self);
3410 symbol.* = .{
3411 .n_strx = name_str_index,
3412 .n_type = macho.N_SECT,
3413 .n_sect = sect_id + 1,
3414 .n_desc = 0,
3415 .n_value = addr,
3416 };
3417
3418 try unnamed_consts.append(gpa, atom);
2855 try self.writeAtom(atom, code);
34192856
34202857 return atom.sym_index;
34212858}
......@@ -3442,6 +2879,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
34422879 }
34432880 }
34442881
2882 self.freeRelocationsForAtom(&decl.link.macho);
2883
34452884 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
34462885 defer code_buffer.deinit();
34472886
......@@ -3469,27 +2908,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
34692908 .parent_atom_index = decl.link.macho.sym_index,
34702909 });
34712910
3472 const code = blk: {
3473 switch (res) {
3474 .externally_managed => |x| break :blk x,
3475 .appended => {
3476 // TODO clearing the code and relocs buffer should probably be orchestrated
3477 // in a different, smarter, more automatic way somewhere else, in a more centralised
3478 // way than this.
3479 // If we don't clear the buffers here, we are up for some nasty surprises when
3480 // this atom is reused later on and was not freed by freeAtom().
3481 decl.link.macho.code.clearAndFree(self.base.allocator);
3482 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3483 break :blk decl.link.macho.code.items;
3484 },
3485 .fail => |em| {
3486 decl.analysis = .codegen_failure;
3487 try module.failed_decls.put(module.gpa, decl_index, em);
3488 return;
3489 },
3490 }
2911 const code = switch (res) {
2912 .externally_managed => |x| x,
2913 .appended => code_buffer.items,
2914 .fail => |em| {
2915 decl.analysis = .codegen_failure;
2916 try module.failed_decls.put(module.gpa, decl_index, em);
2917 return;
2918 },
34912919 };
3492 const addr = try self.placeDecl(decl_index, code.len);
2920 const addr = try self.updateDeclCode(decl_index, code);
34932921
34942922 if (decl_state) |*ds| {
34952923 try self.d_sym.?.dwarf.commitDeclState(
......@@ -3508,82 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
35082936 try self.updateDeclExports(module, decl_index, decl_exports);
35092937}
35102938
3511/// Checks if the value, or any of its embedded values stores a pointer, and thus requires
3512/// a rebase opcode for the dynamic linker.
3513fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {
3514 if (ty.zigTypeTag() == .Fn) {
3515 return false;
3516 }
3517 if (val.pointerDecl()) |_| {
3518 return true;
3519 }
3520
3521 switch (ty.zigTypeTag()) {
3522 .Fn => unreachable,
3523 .Pointer => return true,
3524 .Array, .Vector => {
3525 if (ty.arrayLen() == 0) return false;
3526 const elem_ty = ty.childType();
3527 var elem_value_buf: Value.ElemValueBuffer = undefined;
3528 const elem_val = val.elemValueBuffer(mod, 0, &elem_value_buf);
3529 return needsPointerRebase(elem_ty, elem_val, mod);
3530 },
3531 .Struct => {
3532 const fields = ty.structFields().values();
3533 if (fields.len == 0) return false;
3534 if (val.castTag(.aggregate)) |payload| {
3535 const field_values = payload.data;
3536 for (field_values) |field_val, i| {
3537 if (needsPointerRebase(fields[i].ty, field_val, mod)) return true;
3538 } else return false;
3539 } else return false;
3540 },
3541 .Optional => {
3542 if (val.castTag(.opt_payload)) |payload| {
3543 const sub_val = payload.data;
3544 var buffer: Type.Payload.ElemType = undefined;
3545 const sub_ty = ty.optionalChild(&buffer);
3546 return needsPointerRebase(sub_ty, sub_val, mod);
3547 } else return false;
3548 },
3549 .Union => {
3550 const union_obj = val.cast(Value.Payload.Union).?.data;
3551 const active_field_ty = ty.unionFieldType(union_obj.tag, mod);
3552 return needsPointerRebase(active_field_ty, union_obj.val, mod);
3553 },
3554 .ErrorUnion => {
3555 if (val.castTag(.eu_payload)) |payload| {
3556 const payload_ty = ty.errorUnionPayload();
3557 return needsPointerRebase(payload_ty, payload.data, mod);
3558 } else return false;
3559 },
3560 else => return false,
3561 }
3562}
3563
3564fn getOutputSectionAtom(
3565 self: *MachO,
3566 atom: *Atom,
3567 name: []const u8,
3568 ty: Type,
3569 val: Value,
3570 alignment: u32,
3571) !u8 {
3572 const code = atom.code.items;
3573 const mod = self.base.options.module.?;
3574 const align_log_2 = math.log2(alignment);
2939fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
2940 const ty = decl.ty;
2941 const val = decl.val;
35752942 const zig_ty = ty.zigTypeTag();
35762943 const mode = self.base.options.optimize_mode;
35772944 const sect_id: u8 = blk: {
35782945 // TODO finish and audit this function
35792946 if (val.isUndefDeep()) {
35802947 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3581 break :blk (try self.getOutputSection(.{
3582 .segname = makeStaticString("__DATA"),
3583 .sectname = makeStaticString("__bss"),
3584 .size = code.len,
3585 .@"align" = align_log_2,
3586 })).?;
2948 @panic("TODO __DATA,__bss");
35872949 } else {
35882950 break :blk self.data_section_index.?;
35892951 }
......@@ -3593,129 +2955,207 @@ fn getOutputSectionAtom(
35932955 break :blk self.data_section_index.?;
35942956 }
35952957
3596 if (needsPointerRebase(ty, val, mod)) {
3597 break :blk (try self.getOutputSection(.{
3598 .segname = makeStaticString("__DATA_CONST"),
3599 .sectname = makeStaticString("__const"),
3600 .size = code.len,
3601 .@"align" = align_log_2,
3602 })).?;
3603 }
3604
36052958 switch (zig_ty) {
3606 .Fn => {
3607 break :blk self.text_section_index.?;
3608 },
3609 .Array => {
3610 if (val.tag() == .bytes) {
3611 switch (ty.tag()) {
3612 .array_u8_sentinel_0,
3613 .const_slice_u8_sentinel_0,
3614 .manyptr_const_u8_sentinel_0,
3615 => {
3616 break :blk (try self.getOutputSection(.{
3617 .segname = makeStaticString("__TEXT"),
3618 .sectname = makeStaticString("__cstring"),
3619 .flags = macho.S_CSTRING_LITERALS,
3620 .size = code.len,
3621 .@"align" = align_log_2,
3622 })).?;
3623 },
3624 else => {},
3625 }
2959 // TODO: what if this is a function pointer?
2960 .Fn => break :blk self.text_section_index.?,
2961 else => {
2962 if (val.castTag(.variable)) |_| {
2963 break :blk self.data_section_index.?;
36262964 }
2965 break :blk self.data_const_section_index.?;
36272966 },
3628 else => {},
36292967 }
3630 break :blk (try self.getOutputSection(.{
3631 .segname = makeStaticString("__TEXT"),
3632 .sectname = makeStaticString("__const"),
3633 .size = code.len,
3634 .@"align" = align_log_2,
3635 })).?;
36362968 };
3637 const header = self.sections.items(.header)[sect_id];
3638 log.debug(" allocating atom '{s}' in '{s},{s}', ord({d})", .{
3639 name,
3640 header.segName(),
3641 header.sectName(),
3642 sect_id,
3643 });
36442969 return sect_id;
36452970}
36462971
3647fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
3648 const module = self.base.options.module.?;
3649 const decl = module.declPtr(decl_index);
3650 const required_alignment = decl.getAlignment(self.base.options.target);
3651 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3652
3653 const sym_name = try decl.getFullyQualifiedName(module);
3654 defer self.base.allocator.free(sym_name);
3655
3656 const decl_ptr = self.decls.getPtr(decl_index).?;
3657 if (decl_ptr.* == null) {
3658 decl_ptr.* = try self.getOutputSectionAtom(
3659 &decl.link.macho,
3660 sym_name,
3661 decl.ty,
3662 decl.val,
3663 required_alignment,
3664 );
3665 }
3666 const match = decl_ptr.*.?;
3667
3668 if (decl.link.macho.size != 0) {
3669 const symbol = decl.link.macho.getSymbolPtr(self);
3670 const capacity = decl.link.macho.capacity(self);
3671 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
3672
3673 if (need_realloc) {
3674 const vaddr = try self.growAtom(&decl.link.macho, code_len, required_alignment, match);
3675 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
3676 log.debug(" (required alignment 0x{x})", .{required_alignment});
3677 symbol.n_value = vaddr;
3678
3679 const got_atom = self.getGotAtomForSymbol(.{
3680 .sym_index = decl.link.macho.sym_index,
3681 .file = null,
3682 }).?;
3683 got_atom.dirty = true;
3684 } else if (code_len < decl.link.macho.size) {
3685 self.shrinkAtom(&decl.link.macho, code_len, match);
2972pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
2973 const segname = sect.segName();
2974 const sectname = sect.sectName();
2975 const sect_id: ?u8 = blk: {
2976 if (mem.eql(u8, "__LLVM", segname)) {
2977 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
2978 sect.flags, segname, sectname,
2979 });
2980 break :blk null;
36862981 }
3687 decl.link.macho.size = code_len;
3688 decl.link.macho.dirty = true;
3689
3690 symbol.n_strx = try self.strtab.insert(self.base.allocator, sym_name);
3691 symbol.n_type = macho.N_SECT;
3692 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
3693 symbol.n_desc = 0;
3694 } else {
3695 const name_str_index = try self.strtab.insert(self.base.allocator, sym_name);
3696 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
36972982
3698 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });
3699 log.debug(" (required alignment 0x{x})", .{required_alignment});
2983 if (sect.isCode()) {
2984 if (self.text_section_index == null) {
2985 self.text_section_index = try self.initSection("__TEXT", "__text", .{
2986 .flags = macho.S_REGULAR |
2987 macho.S_ATTR_PURE_INSTRUCTIONS |
2988 macho.S_ATTR_SOME_INSTRUCTIONS,
2989 });
2990 }
2991 break :blk self.text_section_index.?;
2992 }
37002993
3701 errdefer self.freeAtom(&decl.link.macho, match, false);
2994 if (sect.isDebug()) {
2995 // TODO debug attributes
2996 if (mem.eql(u8, "__LD", segname) and mem.eql(u8, "__compact_unwind", sectname)) {
2997 log.debug("TODO compact unwind section: type 0x{x}, name '{s},{s}'", .{
2998 sect.flags, segname, sectname,
2999 });
3000 }
3001 break :blk null;
3002 }
37023003
3703 const symbol = decl.link.macho.getSymbolPtr(self);
3704 symbol.* = .{
3705 .n_strx = name_str_index,
3706 .n_type = macho.N_SECT,
3707 .n_sect = match + 1,
3708 .n_desc = 0,
3709 .n_value = addr,
3710 };
3004 switch (sect.@"type"()) {
3005 macho.S_4BYTE_LITERALS,
3006 macho.S_8BYTE_LITERALS,
3007 macho.S_16BYTE_LITERALS,
3008 => {
3009 if (self.getSectionByName("__TEXT", "__const")) |sect_id| break :blk sect_id;
3010 break :blk try self.initSection("__TEXT", "__const", .{});
3011 },
3012 macho.S_CSTRING_LITERALS => {
3013 if (mem.startsWith(u8, sectname, "__objc")) {
3014 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3015 break :blk try self.initSection(segname, sectname, .{});
3016 }
3017 if (self.getSectionByName("__TEXT", "__cstring")) |sect_id| break :blk sect_id;
3018 break :blk try self.initSection("__TEXT", "__cstring", .{
3019 .flags = macho.S_CSTRING_LITERALS,
3020 });
3021 },
3022 macho.S_MOD_INIT_FUNC_POINTERS,
3023 macho.S_MOD_TERM_FUNC_POINTERS,
3024 => {
3025 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
3026 break :blk try self.initSection("__DATA_CONST", sectname, .{
3027 .flags = sect.flags,
3028 });
3029 },
3030 macho.S_LITERAL_POINTERS,
3031 macho.S_ZEROFILL,
3032 macho.S_THREAD_LOCAL_VARIABLES,
3033 macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
3034 macho.S_THREAD_LOCAL_REGULAR,
3035 macho.S_THREAD_LOCAL_ZEROFILL,
3036 => {
3037 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3038 break :blk try self.initSection(segname, sectname, .{ .flags = sect.flags });
3039 },
3040 macho.S_COALESCED => {
3041 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3042 break :blk try self.initSection(segname, sectname, .{});
3043 },
3044 macho.S_REGULAR => {
3045 if (mem.eql(u8, segname, "__TEXT")) {
3046 if (mem.eql(u8, sectname, "__rodata") or
3047 mem.eql(u8, sectname, "__typelink") or
3048 mem.eql(u8, sectname, "__itablink") or
3049 mem.eql(u8, sectname, "__gosymtab") or
3050 mem.eql(u8, sectname, "__gopclntab"))
3051 {
3052 if (self.getSectionByName("__DATA_CONST", "__const")) |sect_id| break :blk sect_id;
3053 break :blk try self.initSection("__DATA_CONST", "__const", .{});
3054 }
3055 }
3056 if (mem.eql(u8, segname, "__DATA")) {
3057 if (mem.eql(u8, sectname, "__const") or
3058 mem.eql(u8, sectname, "__cfstring") or
3059 mem.eql(u8, sectname, "__objc_classlist") or
3060 mem.eql(u8, sectname, "__objc_imageinfo"))
3061 {
3062 if (self.getSectionByName("__DATA_CONST", sectname)) |sect_id| break :blk sect_id;
3063 break :blk try self.initSection("__DATA_CONST", sectname, .{});
3064 } else if (mem.eql(u8, sectname, "__data")) {
3065 if (self.data_section_index == null) {
3066 self.data_section_index = try self.initSection(segname, sectname, .{});
3067 }
3068 break :blk self.data_section_index.?;
3069 }
3070 }
3071 if (self.getSectionByName(segname, sectname)) |sect_id| break :blk sect_id;
3072 break :blk try self.initSection(segname, sectname, .{});
3073 },
3074 else => break :blk null,
3075 }
3076 };
3077 return sect_id;
3078}
3079
3080fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) !u64 {
3081 const gpa = self.base.allocator;
3082 const mod = self.base.options.module.?;
3083 const decl = mod.declPtr(decl_index);
3084
3085 const required_alignment = decl.getAlignment(self.base.options.target);
3086 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3087
3088 const sym_name = try decl.getFullyQualifiedName(mod);
3089 defer self.base.allocator.free(sym_name);
3090
3091 const atom = &decl.link.macho;
3092 const decl_ptr = self.decls.getPtr(decl_index).?;
3093 if (decl_ptr.* == null) {
3094 decl_ptr.* = self.getDeclOutputSection(decl);
3095 }
3096 const sect_id = decl_ptr.*.?;
3097 const code_len = code.len;
3098
3099 if (atom.size != 0) {
3100 const sym = atom.getSymbolPtr(self);
3101 sym.n_strx = try self.strtab.insert(gpa, sym_name);
3102 sym.n_type = macho.N_SECT;
3103 sym.n_sect = sect_id + 1;
3104 sym.n_desc = 0;
3105
3106 const capacity = decl.link.macho.capacity(self);
3107 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);
3108
3109 if (need_realloc) {
3110 const vaddr = try self.growAtom(atom, code_len, required_alignment);
3111 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
3112 log.debug(" (required alignment 0x{x})", .{required_alignment});
3113
3114 if (vaddr != sym.n_value) {
3115 sym.n_value = vaddr;
3116 log.debug(" (updating GOT entry)", .{});
3117 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
3118 const got_atom = self.getGotAtomForSymbol(got_target).?;
3119 self.markRelocsDirtyByTarget(got_target);
3120 try self.writePtrWidthAtom(got_atom);
3121 }
3122 } else if (code_len < atom.size) {
3123 self.shrinkAtom(atom, code_len);
3124 } else if (atom.next == null) {
3125 const header = &self.sections.items(.header)[sect_id];
3126 const segment = self.getSegment(sect_id);
3127 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
3128 header.size = needed_size;
3129 }
3130 atom.size = code_len;
3131 } else {
3132 const name_str_index = try self.strtab.insert(gpa, sym_name);
3133 const sym = atom.getSymbolPtr(self);
3134 sym.n_strx = name_str_index;
3135 sym.n_type = macho.N_SECT;
3136 sym.n_sect = sect_id + 1;
3137 sym.n_desc = 0;
3138
3139 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
3140 errdefer self.freeAtom(atom, false);
3141
3142 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
3143 log.debug(" (required alignment 0x{x})", .{required_alignment});
3144
3145 atom.size = code_len;
3146 sym.n_value = vaddr;
37113147
3712 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
3148 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
37133149 const got_index = try self.allocateGotEntry(got_target);
37143150 const got_atom = try self.createGotAtom(got_target);
37153151 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3152 try self.writePtrWidthAtom(got_atom);
37163153 }
37173154
3718 return decl.link.macho.getSymbol(self).n_value;
3155 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
3156 try self.writeAtom(atom, code);
3157
3158 return atom.getSymbol(self).n_value;
37193159}
37203160
37213161pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
......@@ -3863,20 +3303,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
38633303 }
38643304}
38653305
3306fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {
3307 _ = self.relocs.remove(atom);
3308 _ = self.rebases.remove(atom);
3309 _ = self.bindings.remove(atom);
3310 _ = self.lazy_bindings.remove(atom);
3311}
3312
38663313fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
3314 const gpa = self.base.allocator;
38673315 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
38683316 for (unnamed_consts.items) |atom| {
3869 // TODO
3870 // const sect_id = atom.getSymbol(self).n_sect;
3871 const sect_id = self.getSectionByName("__TEXT", "__const").?;
3872 self.freeAtom(atom, sect_id, true);
3873 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
3317 self.freeAtom(atom, true);
3318 self.locals_free_list.append(gpa, atom.sym_index) catch {};
38743319 self.locals.items[atom.sym_index].n_type = 0;
38753320 _ = self.atom_by_index_table.remove(atom.sym_index);
38763321 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
38773322 atom.sym_index = 0;
38783323 }
3879 unnamed_consts.clearAndFree(self.base.allocator);
3324 unnamed_consts.clearAndFree(gpa);
38803325}
38813326
38823327pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
......@@ -3885,20 +3330,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
38853330 }
38863331 const mod = self.base.options.module.?;
38873332 const decl = mod.declPtr(decl_index);
3333
38883334 log.debug("freeDecl {*}", .{decl});
3335
38893336 const kv = self.decls.fetchSwapRemove(decl_index);
3890 if (kv.?.value) |match| {
3891 self.freeAtom(&decl.link.macho, match, false);
3337 if (kv.?.value) |_| {
3338 self.freeAtom(&decl.link.macho, false);
38923339 self.freeUnnamedConsts(decl_index);
38933340 }
3341
38943342 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3895 if (decl.link.macho.sym_index != 0) {
3896 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};
3343 const gpa = self.base.allocator;
3344 const sym_index = decl.link.macho.sym_index;
3345 if (sym_index != 0) {
3346 self.locals_free_list.append(gpa, sym_index) catch {};
38973347
38983348 // Try freeing GOT atom if this decl had one
3899 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
3349 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
39003350 if (self.got_entries_table.get(got_target)) |got_index| {
3901 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
3351 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
39023352 self.got_entries.items[got_index] = .{
39033353 .target = .{ .sym_index = 0, .file = null },
39043354 .sym_index = 0,
......@@ -3906,20 +3356,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
39063356 _ = self.got_entries_table.remove(got_target);
39073357
39083358 if (self.d_sym) |*d_sym| {
3909 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);
3359 d_sym.swapRemoveRelocs(sym_index);
39103360 }
39113361
3912 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
3913 got_index,
3914 decl.link.macho.sym_index,
3915 });
3362 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
39163363 }
39173364
3918 self.locals.items[decl.link.macho.sym_index].n_type = 0;
3919 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);
3920 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});
3365 self.locals.items[sym_index].n_type = 0;
3366 _ = self.atom_by_index_table.remove(sym_index);
3367 log.debug(" adding local symbol index {d} to free list", .{sym_index});
39213368 decl.link.macho.sym_index = 0;
39223369 }
3370
39233371 if (self.d_sym) |*d_sym| {
39243372 d_sym.dwarf.freeDecl(decl);
39253373 }
......@@ -3932,260 +3380,155 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
39323380 assert(self.llvm_object == null);
39333381 assert(decl.link.macho.sym_index != 0);
39343382
3935 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
3936 try atom.relocs.append(self.base.allocator, .{
3937 .offset = @intCast(u32, reloc_info.offset),
3938 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
3939 .addend = reloc_info.addend,
3940 .subtractor = null,
3941 .pcrel = false,
3942 .length = 3,
3383 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
3384 try atom.addRelocation(self, .{
39433385 .@"type" = switch (self.base.options.target.cpu.arch) {
39443386 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
39453387 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
39463388 else => unreachable,
39473389 },
3390 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
3391 .offset = @intCast(u32, reloc_info.offset),
3392 .addend = reloc_info.addend,
3393 .pcrel = false,
3394 .length = 3,
39483395 });
3949 try atom.rebases.append(self.base.allocator, reloc_info.offset);
3396 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
39503397
39513398 return 0;
39523399}
39533400
3954fn populateMissingMetadata(self: *MachO) !void {
3401pub fn populateMissingMetadata(self: *MachO) !void {
3402 assert(self.mode == .incremental);
3403
39553404 const gpa = self.base.allocator;
39563405 const cpu_arch = self.base.options.target.cpu.arch;
3957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
3958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
3959
3960 if (self.pagezero_segment_cmd_index == null) blk: {
3961 if (self.base.options.output_mode == .Lib) break :blk;
3962 if (aligned_pagezero_vmsize == 0) break :blk;
3963 if (aligned_pagezero_vmsize != pagezero_vmsize) {
3964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
3965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
3406 const pagezero_vmsize = self.calcPagezeroSize();
3407
3408 if (self.pagezero_segment_cmd_index == null) {
3409 if (pagezero_vmsize > 0) {
3410 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
3411 try self.segments.append(gpa, .{
3412 .segname = makeStaticString("__PAGEZERO"),
3413 .vmsize = pagezero_vmsize,
3414 .cmdsize = @sizeOf(macho.segment_command_64),
3415 });
39663416 }
3967 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
3968 try self.segments.append(gpa, .{
3969 .segname = makeStaticString("__PAGEZERO"),
3970 .vmsize = aligned_pagezero_vmsize,
3971 .cmdsize = @sizeOf(macho.segment_command_64),
3972 });
39733417 }
39743418
3975 if (self.text_segment_cmd_index == null) {
3976 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);
3977 const needed_size = if (self.mode == .incremental) blk: {
3978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
3979 const program_code_size_hint = self.base.options.program_code_size_hint;
3980 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
3981 const ideal_size = headerpad_size + program_code_size_hint + got_size_hint;
3982 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
3984 break :blk needed_size;
3985 } else 0;
3419 if (self.header_segment_cmd_index == null) {
3420 // The first __TEXT segment is immovable and covers MachO header and load commands.
3421 self.header_segment_cmd_index = @intCast(u8, self.segments.items.len);
3422 const ideal_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
3423 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3424
3425 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
3426
39863427 try self.segments.append(gpa, .{
39873428 .segname = makeStaticString("__TEXT"),
3988 .vmaddr = aligned_pagezero_vmsize,
3429 .vmaddr = pagezero_vmsize,
39893430 .vmsize = needed_size,
39903431 .filesize = needed_size,
39913432 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
39923433 .initprot = macho.PROT.READ | macho.PROT.EXEC,
39933434 .cmdsize = @sizeOf(macho.segment_command_64),
39943435 });
3436 self.segment_table_dirty = true;
39953437 }
39963438
39973439 if (self.text_section_index == null) {
3998 const alignment: u2 = switch (cpu_arch) {
3999 .x86_64 => 0,
4000 .aarch64 => 2,
4001 else => unreachable, // unhandled architecture type
4002 };
4003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
4004 self.text_section_index = try self.initSection(
4005 "__TEXT",
4006 "__text",
4007 needed_size,
4008 alignment,
4009 .{
4010 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3440 // Sadly, segments need unique string identfiers for some reason.
3441 self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{
3442 .size = self.base.options.program_code_size_hint,
3443 .alignment = switch (cpu_arch) {
3444 .x86_64 => 1,
3445 .aarch64 => @sizeOf(u32),
3446 else => unreachable, // unhandled architecture type
40113447 },
4012 );
3448 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3449 .prot = macho.PROT.READ | macho.PROT.EXEC,
3450 });
3451 self.segment_table_dirty = true;
40133452 }
40143453
40153454 if (self.stubs_section_index == null) {
4016 const alignment: u2 = switch (cpu_arch) {
4017 .x86_64 => 0,
4018 .aarch64 => 2,
4019 else => unreachable, // unhandled architecture type
4020 };
4021 const stub_size: u4 = switch (cpu_arch) {
3455 const stub_size: u32 = switch (cpu_arch) {
40223456 .x86_64 => 6,
40233457 .aarch64 => 3 * @sizeOf(u32),
40243458 else => unreachable, // unhandled architecture type
40253459 };
4026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
4027 self.stubs_section_index = try self.initSection(
4028 "__TEXT",
4029 "__stubs",
4030 needed_size,
4031 alignment,
4032 .{
4033 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
4034 .reserved2 = stub_size,
3460 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
3461 .size = stub_size,
3462 .alignment = switch (cpu_arch) {
3463 .x86_64 => 1,
3464 .aarch64 => @sizeOf(u32),
3465 else => unreachable, // unhandled architecture type
40353466 },
4036 );
3467 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3468 .reserved2 = stub_size,
3469 .prot = macho.PROT.READ | macho.PROT.EXEC,
3470 });
3471 self.segment_table_dirty = true;
40373472 }
40383473
40393474 if (self.stub_helper_section_index == null) {
4040 const alignment: u2 = switch (cpu_arch) {
4041 .x86_64 => 0,
4042 .aarch64 => 2,
4043 else => unreachable, // unhandled architecture type
4044 };
4045 const preamble_size: u6 = switch (cpu_arch) {
4046 .x86_64 => 15,
4047 .aarch64 => 6 * @sizeOf(u32),
4048 else => unreachable,
4049 };
4050 const stub_size: u4 = switch (cpu_arch) {
4051 .x86_64 => 10,
4052 .aarch64 => 3 * @sizeOf(u32),
4053 else => unreachable,
4054 };
4055 const needed_size = if (self.mode == .incremental)
4056 stub_size * self.base.options.symbol_count_hint + preamble_size
4057 else
4058 0;
4059 self.stub_helper_section_index = try self.initSection(
4060 "__TEXT",
4061 "__stub_helper",
4062 needed_size,
4063 alignment,
4064 .{
4065 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3475 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
3476 .size = @sizeOf(u32),
3477 .alignment = switch (cpu_arch) {
3478 .x86_64 => 1,
3479 .aarch64 => @sizeOf(u32),
3480 else => unreachable, // unhandled architecture type
40663481 },
4067 );
4068 }
4069
4070 if (self.data_const_segment_cmd_index == null) {
4071 self.data_const_segment_cmd_index = @intCast(u8, self.segments.items.len);
4072 var vmaddr: u64 = 0;
4073 var fileoff: u64 = 0;
4074 var needed_size: u64 = 0;
4075 if (self.mode == .incremental) {
4076 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
4077 vmaddr = base.vmaddr;
4078 fileoff = base.fileoff;
4079 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
4080 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4081 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{
4082 fileoff,
4083 fileoff + needed_size,
4084 });
4085 }
4086 try self.segments.append(gpa, .{
4087 .segname = makeStaticString("__DATA_CONST"),
4088 .vmaddr = vmaddr,
4089 .vmsize = needed_size,
4090 .fileoff = fileoff,
4091 .filesize = needed_size,
4092 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4093 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4094 .cmdsize = @sizeOf(macho.segment_command_64),
3482 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
3483 .prot = macho.PROT.READ | macho.PROT.EXEC,
40953484 });
3485 self.segment_table_dirty = true;
40963486 }
40973487
40983488 if (self.got_section_index == null) {
4099 const needed_size = if (self.mode == .incremental)
4100 @sizeOf(u64) * self.base.options.symbol_count_hint
4101 else
4102 0;
4103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4104 self.got_section_index = try self.initSection(
4105 "__DATA_CONST",
4106 "__got",
4107 needed_size,
4108 alignment,
4109 .{
4110 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
4111 },
4112 );
3489 self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{
3490 .size = @sizeOf(u64) * self.base.options.symbol_count_hint,
3491 .alignment = @alignOf(u64),
3492 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
3493 .prot = macho.PROT.READ | macho.PROT.WRITE,
3494 });
3495 self.segment_table_dirty = true;
41133496 }
41143497
4115 if (self.data_segment_cmd_index == null) {
4116 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);
4117 var vmaddr: u64 = 0;
4118 var fileoff: u64 = 0;
4119 var needed_size: u64 = 0;
4120 if (self.mode == .incremental) {
4121 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
4122 vmaddr = base.vmaddr;
4123 fileoff = base.fileoff;
4124 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
4125 needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
4126 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{
4127 fileoff,
4128 fileoff + needed_size,
4129 });
4130 }
4131 try self.segments.append(gpa, .{
4132 .segname = makeStaticString("__DATA"),
4133 .vmaddr = vmaddr,
4134 .vmsize = needed_size,
4135 .fileoff = fileoff,
4136 .filesize = needed_size,
4137 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
4138 .initprot = macho.PROT.READ | macho.PROT.WRITE,
4139 .cmdsize = @sizeOf(macho.segment_command_64),
3498 if (self.data_const_section_index == null) {
3499 self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{
3500 .size = @sizeOf(u64),
3501 .alignment = @alignOf(u64),
3502 .flags = macho.S_REGULAR,
3503 .prot = macho.PROT.READ | macho.PROT.WRITE,
41403504 });
3505 self.segment_table_dirty = true;
41413506 }
41423507
41433508 if (self.la_symbol_ptr_section_index == null) {
4144 const needed_size = if (self.mode == .incremental)
4145 @sizeOf(u64) * self.base.options.symbol_count_hint
4146 else
4147 0;
4148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4149 self.la_symbol_ptr_section_index = try self.initSection(
4150 "__DATA",
4151 "__la_symbol_ptr",
4152 needed_size,
4153 alignment,
4154 .{
4155 .flags = macho.S_LAZY_SYMBOL_POINTERS,
4156 },
4157 );
3509 self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{
3510 .size = @sizeOf(u64),
3511 .alignment = @alignOf(u64),
3512 .flags = macho.S_LAZY_SYMBOL_POINTERS,
3513 .prot = macho.PROT.READ | macho.PROT.WRITE,
3514 });
3515 self.segment_table_dirty = true;
41583516 }
41593517
41603518 if (self.data_section_index == null) {
4161 const needed_size = if (self.mode == .incremental)
4162 @sizeOf(u64) * self.base.options.symbol_count_hint
4163 else
4164 0;
4165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4166 self.data_section_index = try self.initSection(
4167 "__DATA",
4168 "__data",
4169 needed_size,
4170 alignment,
4171 .{},
4172 );
3519 self.data_section_index = try self.allocateSection("__DATA1", "__data", .{
3520 .size = @sizeOf(u64),
3521 .alignment = @alignOf(u64),
3522 .flags = macho.S_REGULAR,
3523 .prot = macho.PROT.READ | macho.PROT.WRITE,
3524 });
3525 self.segment_table_dirty = true;
41733526 }
41743527
41753528 if (self.linkedit_segment_cmd_index == null) {
41763529 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
4177 var vmaddr: u64 = 0;
4178 var fileoff: u64 = 0;
4179 if (self.mode == .incremental) {
4180 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
4181 vmaddr = base.vmaddr;
4182 fileoff = base.fileoff;
4183 log.debug("found __LINKEDIT segment free space at 0x{x}", .{fileoff});
4184 }
41853530 try self.segments.append(gpa, .{
41863531 .segname = makeStaticString("__LINKEDIT"),
4187 .vmaddr = vmaddr,
4188 .fileoff = fileoff,
41893532 .maxprot = macho.PROT.READ,
41903533 .initprot = macho.PROT.READ,
41913534 .cmdsize = @sizeOf(macho.segment_command_64),
......@@ -4283,7 +3626,19 @@ fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
42833626 return @intCast(u32, sizeofcmds);
42843627}
42853628
4286fn calcMinHeaderPad(self: *MachO) !u64 {
3629pub fn calcPagezeroSize(self: *MachO) u64 {
3630 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;
3631 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);
3632 if (self.base.options.output_mode == .Lib) return 0;
3633 if (aligned_pagezero_vmsize == 0) return 0;
3634 if (aligned_pagezero_vmsize != pagezero_vmsize) {
3635 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});
3636 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});
3637 }
3638 return aligned_pagezero_vmsize;
3639}
3640
3641pub fn calcMinHeaderPad(self: *MachO) !u64 {
42873642 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
42883643 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
42893644
......@@ -4300,444 +3655,118 @@ fn calcMinHeaderPad(self: *MachO) !u64 {
43003655 return offset;
43013656}
43023657
4303fn allocateSegments(self: *MachO) !void {
4304 try self.allocateSegment(self.text_segment_cmd_index, &.{
4305 self.pagezero_segment_cmd_index,
4306 }, try self.calcMinHeaderPad());
4307
4308 if (self.text_segment_cmd_index) |index| blk: {
4309 const indexes = self.getSectionIndexes(index);
4310 if (indexes.start == indexes.end) break :blk;
4311 const seg = self.segments.items[index];
4312
4313 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
4314 var min_alignment: u32 = 0;
4315 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4316 const alignment = try math.powi(u32, 2, header.@"align");
4317 min_alignment = math.max(min_alignment, alignment);
4318 }
4319
4320 assert(min_alignment > 0);
4321 const last_header = self.sections.items(.header)[indexes.end - 1];
4322 const shift: u32 = shift: {
4323 const diff = seg.filesize - last_header.offset - last_header.size;
4324 const factor = @divTrunc(diff, min_alignment);
4325 break :shift @intCast(u32, factor * min_alignment);
4326 };
4327
4328 if (shift > 0) {
4329 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header| {
4330 header.offset += shift;
4331 header.addr += shift;
4332 }
4333 }
4334 }
4335
4336 try self.allocateSegment(self.data_const_segment_cmd_index, &.{
4337 self.text_segment_cmd_index,
4338 self.pagezero_segment_cmd_index,
4339 }, 0);
4340
4341 try self.allocateSegment(self.data_segment_cmd_index, &.{
4342 self.data_const_segment_cmd_index,
4343 self.text_segment_cmd_index,
4344 self.pagezero_segment_cmd_index,
4345 }, 0);
4346
4347 try self.allocateSegment(self.linkedit_segment_cmd_index, &.{
4348 self.data_segment_cmd_index,
4349 self.data_const_segment_cmd_index,
4350 self.text_segment_cmd_index,
4351 self.pagezero_segment_cmd_index,
4352 }, 0);
4353}
4354
4355fn allocateSegment(self: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
4356 const index = maybe_index orelse return;
4357 const seg = &self.segments.items[index];
4358
4359 const base = self.getSegmentAllocBase(indices);
4360 seg.vmaddr = base.vmaddr;
4361 seg.fileoff = base.fileoff;
4362 seg.filesize = init_size;
4363 seg.vmsize = init_size;
4364
4365 // Allocate the sections according to their alignment at the beginning of the segment.
4366 const indexes = self.getSectionIndexes(index);
4367 var start = init_size;
4368 const slice = self.sections.slice();
4369 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
4370 const alignment = try math.powi(u32, 2, header.@"align");
4371 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
4372
4373 header.offset = if (header.isZerofill())
4374 0
4375 else
4376 @intCast(u32, seg.fileoff + start_aligned);
4377 header.addr = seg.vmaddr + start_aligned;
4378
4379 start = start_aligned + header.size;
4380
4381 if (!header.isZerofill()) {
4382 seg.filesize = start;
4383 }
4384 seg.vmsize = start;
4385 }
4386
4387 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4388 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, self.page_size);
4389}
4390
4391const InitSectionOpts = struct {
3658fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
3659 size: u64 = 0,
3660 alignment: u32 = 0,
3661 prot: macho.vm_prot_t = macho.PROT.NONE,
43923662 flags: u32 = macho.S_REGULAR,
4393 reserved1: u32 = 0,
43943663 reserved2: u32 = 0,
4395};
3664}) !u8 {
3665 const gpa = self.base.allocator;
3666 // In incremental context, we create one section per segment pairing. This way,
3667 // we can move the segment in raw file as we please.
3668 const segment_id = @intCast(u8, self.segments.items.len);
3669 const section_id = @intCast(u8, self.sections.slice().len);
3670 const vmaddr = blk: {
3671 const prev_segment = self.segments.items[segment_id - 1];
3672 break :blk mem.alignForwardGeneric(u64, prev_segment.vmaddr + prev_segment.vmsize, self.page_size);
3673 };
3674 // We commit more memory than needed upfront so that we don't have to reallocate too soon.
3675 const vmsize = mem.alignForwardGeneric(u64, opts.size, self.page_size);
3676 const off = self.findFreeSpace(opts.size, self.page_size);
3677
3678 log.debug("found {s},{s} free space 0x{x} to 0x{x} (0x{x} - 0x{x})", .{
3679 segname,
3680 sectname,
3681 off,
3682 off + opts.size,
3683 vmaddr,
3684 vmaddr + vmsize,
3685 });
43963686
4397fn initSection(
4398 self: *MachO,
4399 segname: []const u8,
4400 sectname: []const u8,
4401 size: u64,
4402 alignment: u32,
4403 opts: InitSectionOpts,
4404) !u8 {
4405 const segment_id = self.getSegmentByName(segname).?;
4406 const seg = &self.segments.items[segment_id];
4407 const index = try self.insertSection(segment_id, .{
3687 const seg = try self.segments.addOne(gpa);
3688 seg.* = .{
3689 .segname = makeStaticString(segname),
3690 .vmaddr = vmaddr,
3691 .vmsize = vmsize,
3692 .fileoff = off,
3693 .filesize = vmsize,
3694 .maxprot = opts.prot,
3695 .initprot = opts.prot,
3696 .nsects = 1,
3697 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
3698 };
3699
3700 var section = macho.section_64{
44083701 .sectname = makeStaticString(sectname),
4409 .segname = seg.segname,
3702 .segname = makeStaticString(segname),
3703 .addr = mem.alignForwardGeneric(u64, vmaddr, opts.alignment),
3704 .offset = mem.alignForwardGeneric(u32, @intCast(u32, off), opts.alignment),
3705 .size = opts.size,
3706 .@"align" = math.log2(opts.alignment),
44103707 .flags = opts.flags,
4411 .reserved1 = opts.reserved1,
44123708 .reserved2 = opts.reserved2,
4413 });
4414 seg.cmdsize += @sizeOf(macho.section_64);
4415 seg.nsects += 1;
4416
4417 if (self.mode == .incremental) {
4418 const header = &self.sections.items(.header)[index];
4419 header.size = size;
4420 header.@"align" = alignment;
4421
4422 const prev_end_off = if (index > 0) blk: {
4423 const prev_section = self.sections.get(index - 1);
4424 if (prev_section.segment_index == segment_id) {
4425 const prev_header = prev_section.header;
4426 break :blk prev_header.offset + padToIdeal(prev_header.size);
4427 } else break :blk seg.fileoff;
4428 } else 0;
4429 const alignment_pow_2 = try math.powi(u32, 2, alignment);
4430 // TODO better prealloc for __text section
4431 // const padding: u64 = if (index == 0) try self.calcMinHeaderPad() else 0;
4432 const padding: u64 = if (index == 0) 0x1000 else 0;
4433 const off = mem.alignForwardGeneric(u64, padding + prev_end_off, alignment_pow_2);
4434
4435 if (!header.isZerofill()) {
4436 header.offset = @intCast(u32, off);
4437 }
4438 header.addr = seg.vmaddr + off - seg.fileoff;
4439
4440 // TODO Will this break if we are inserting section that is not the last section
4441 // in a segment?
4442 const max_size = self.allocatedSize(segment_id, off);
4443
4444 if (size > max_size) {
4445 try self.growSection(index, @intCast(u32, size));
4446 }
4447
4448 log.debug("allocating {s},{s} section at 0x{x}", .{ header.segName(), header.sectName(), off });
4449
4450 self.updateSectionOrdinals(index + 1);
4451 }
4452
4453 return index;
4454}
3709 };
3710 assert(!section.isZerofill()); // TODO zerofill sections
44553711
4456fn getSectionPrecedence(header: macho.section_64) u4 {
4457 if (header.isCode()) {
4458 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
4459 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
4460 return 0x2;
4461 }
4462 switch (header.@"type"()) {
4463 macho.S_NON_LAZY_SYMBOL_POINTERS,
4464 macho.S_LAZY_SYMBOL_POINTERS,
4465 => return 0x0,
4466 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
4467 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
4468 macho.S_ZEROFILL => return 0xf,
4469 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
4470 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
4471 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
4472 return 0xf
4473 else
4474 return 0x3,
4475 }
3712 try self.sections.append(gpa, .{
3713 .segment_index = segment_id,
3714 .header = section,
3715 });
3716 return section_id;
44763717}
44773718
4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
4479 const precedence = getSectionPrecedence(header);
4480 const indexes = self.getSectionIndexes(segment_index);
4481 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
4482 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
4483 } else indexes.end;
4484 log.debug("inserting section '{s},{s}' at index {d}", .{
3719fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
3720 const header = &self.sections.items(.header)[sect_id];
3721 const segment = self.getSegmentPtr(sect_id);
3722 const increased_size = padToIdeal(needed_size);
3723 const old_aligned_end = segment.vmaddr + segment.vmsize;
3724 const new_aligned_end = segment.vmaddr + mem.alignForwardGeneric(u64, increased_size, self.page_size);
3725 const diff = new_aligned_end - old_aligned_end;
3726 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{
44853727 header.segName(),
44863728 header.sectName(),
4487 insertion_index,
4488 });
4489 for (&[_]*?u8{
4490 &self.text_section_index,
4491 &self.stubs_section_index,
4492 &self.stub_helper_section_index,
4493 &self.got_section_index,
4494 &self.la_symbol_ptr_section_index,
4495 &self.data_section_index,
4496 }) |maybe_index| {
4497 const index = maybe_index.* orelse continue;
4498 if (insertion_index <= index) maybe_index.* = index + 1;
4499 }
4500 try self.sections.insert(self.base.allocator, insertion_index, .{
4501 .segment_index = segment_index,
4502 .header = header,
3729 diff,
45033730 });
4504 return insertion_index;
4505}
4506
4507fn updateSectionOrdinals(self: *MachO, start: u8) void {
4508 const tracy = trace(@src());
4509 defer tracy.end();
4510
4511 const slice = self.sections.slice();
4512 for (slice.items(.last_atom)[start..]) |last_atom| {
4513 var atom = last_atom orelse continue;
4514
4515 while (true) {
4516 const sym = atom.getSymbolPtr(self);
4517 sym.n_sect = start + 1;
45183731
4519 for (atom.contained.items) |sym_at_off| {
4520 const contained_sym = self.getSymbolPtr(.{
4521 .sym_index = sym_at_off.sym_index,
4522 .file = atom.file,
4523 });
4524 contained_sym.n_sect = start + 1;
3732 // TODO: enforce order by increasing VM addresses in self.sections container.
3733 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
3734 const index = @intCast(u8, sect_id + 1 + next_sect_id);
3735 const maybe_last_atom = &self.sections.items(.last_atom)[index];
3736 const next_segment = self.getSegmentPtr(index);
3737 next_header.addr += diff;
3738 next_segment.vmaddr += diff;
3739
3740 if (maybe_last_atom.*) |last_atom| {
3741 var atom = last_atom;
3742 while (true) {
3743 const sym = atom.getSymbolPtr(self);
3744 sym.n_value += diff;
3745
3746 if (atom.prev) |prev| {
3747 atom = prev;
3748 } else break;
45253749 }
4526
4527 if (atom.prev) |prev| {
4528 atom = prev;
4529 } else break;
4530 }
4531 }
4532}
4533
4534fn shiftLocalsByOffset(self: *MachO, sect_id: u8, offset: i64) !void {
4535 var atom = self.sections.items(.last_atom)[sect_id] orelse return;
4536
4537 while (true) {
4538 const atom_sym = atom.getSymbolPtr(self);
4539 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
4540
4541 for (atom.contained.items) |sym_at_off| {
4542 const contained_sym = self.getSymbolPtr(.{
4543 .sym_index = sym_at_off.sym_index,
4544 .file = atom.file,
4545 });
4546 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
4547 }
4548
4549 if (atom.prev) |prev| {
4550 atom = prev;
4551 } else break;
4552 }
4553}
4554
4555fn growSegment(self: *MachO, segment_index: u8, new_size: u64) !void {
4556 const segment = &self.segments.items[segment_index];
4557 const new_segment_size = mem.alignForwardGeneric(u64, new_size, self.page_size);
4558 assert(new_segment_size > segment.filesize);
4559 const offset_amt = new_segment_size - segment.filesize;
4560 log.debug("growing segment {s} from 0x{x} to 0x{x}", .{
4561 segment.segname,
4562 segment.filesize,
4563 new_segment_size,
4564 });
4565 segment.filesize = new_segment_size;
4566 segment.vmsize = new_segment_size;
4567
4568 log.debug(" (new segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4569 segment.fileoff,
4570 segment.fileoff + segment.filesize,
4571 segment.vmaddr,
4572 segment.vmaddr + segment.vmsize,
4573 });
4574
4575 var next: u8 = segment_index + 1;
4576 while (next < self.linkedit_segment_cmd_index.? + 1) : (next += 1) {
4577 const next_segment = &self.segments.items[next];
4578
4579 try MachO.copyRangeAllOverlappingAlloc(
4580 self.base.allocator,
4581 self.base.file.?,
4582 next_segment.fileoff,
4583 next_segment.fileoff + offset_amt,
4584 math.cast(usize, next_segment.filesize) orelse return error.Overflow,
4585 );
4586
4587 next_segment.fileoff += offset_amt;
4588 next_segment.vmaddr += offset_amt;
4589
4590 log.debug(" (new {s} segment file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4591 next_segment.segname,
4592 next_segment.fileoff,
4593 next_segment.fileoff + next_segment.filesize,
4594 next_segment.vmaddr,
4595 next_segment.vmaddr + next_segment.vmsize,
4596 });
4597
4598 const indexes = self.getSectionIndexes(next);
4599 for (self.sections.items(.header)[indexes.start..indexes.end]) |*header, i| {
4600 header.offset += @intCast(u32, offset_amt);
4601 header.addr += offset_amt;
4602
4603 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4604 header.segName(),
4605 header.sectName(),
4606 header.offset,
4607 header.offset + header.size,
4608 header.addr,
4609 header.addr + header.size,
4610 });
4611
4612 try self.shiftLocalsByOffset(@intCast(u8, i + indexes.start), @intCast(i64, offset_amt));
46133750 }
46143751 }
46153752}
46163753
4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {
4618 const tracy = trace(@src());
4619 defer tracy.end();
4620
4621 const section = self.sections.get(sect_id);
4622 const segment_index = section.segment_index;
4623 const header = section.header;
4624 const segment = self.segments.items[segment_index];
4625
4626 const alignment = try math.powi(u32, 2, header.@"align");
4627 const max_size = self.allocatedSize(segment_index, header.offset);
4628 const ideal_size = padToIdeal(new_size);
4629 const needed_size = mem.alignForwardGeneric(u32, ideal_size, alignment);
4630
4631 if (needed_size > max_size) blk: {
4632 log.debug(" (need to grow! needed 0x{x}, max 0x{x})", .{ needed_size, max_size });
4633
4634 const indexes = self.getSectionIndexes(segment_index);
4635 if (sect_id == indexes.end - 1) {
4636 // Last section, just grow segments
4637 try self.growSegment(segment_index, segment.filesize + needed_size - max_size);
4638 break :blk;
4639 }
4640
4641 // Need to move all sections below in file and address spaces.
4642 const offset_amt = offset: {
4643 const max_alignment = try self.getSectionMaxAlignment(sect_id + 1, indexes.end);
4644 break :offset mem.alignForwardGeneric(u64, needed_size - max_size, max_alignment);
4645 };
4646
4647 // Before we commit to this, check if the segment needs to grow too.
4648 // We assume that each section header is growing linearly with the increasing
4649 // file offset / virtual memory address space.
4650 const last_sect_header = self.sections.items(.header)[indexes.end - 1];
4651 const last_sect_off = last_sect_header.offset + last_sect_header.size;
4652 const seg_off = segment.fileoff + segment.filesize;
4653
4654 if (last_sect_off + offset_amt > seg_off) {
4655 // Need to grow segment first.
4656 const spill_size = (last_sect_off + offset_amt) - seg_off;
4657 try self.growSegment(segment_index, segment.filesize + spill_size);
4658 }
4659
4660 // We have enough space to expand within the segment, so move all sections by
4661 // the required amount and update their header offsets.
4662 const next_sect = self.sections.items(.header)[sect_id + 1];
4663 const total_size = last_sect_off - next_sect.offset;
4664
4665 try MachO.copyRangeAllOverlappingAlloc(
4666 self.base.allocator,
4667 self.base.file.?,
4668 next_sect.offset,
4669 next_sect.offset + offset_amt,
4670 math.cast(usize, total_size) orelse return error.Overflow,
4671 );
4672
4673 for (self.sections.items(.header)[sect_id + 1 .. indexes.end]) |*moved_sect, i| {
4674 moved_sect.offset += @intCast(u32, offset_amt);
4675 moved_sect.addr += offset_amt;
4676
4677 log.debug(" (new {s},{s} file offsets from 0x{x} to 0x{x} (in memory 0x{x} to 0x{x}))", .{
4678 moved_sect.segName(),
4679 moved_sect.sectName(),
4680 moved_sect.offset,
4681 moved_sect.offset + moved_sect.size,
4682 moved_sect.addr,
4683 moved_sect.addr + moved_sect.size,
4684 });
4685
4686 try self.shiftLocalsByOffset(@intCast(u8, sect_id + 1 + i), @intCast(i64, offset_amt));
4687 }
4688 }
4689}
4690
4691fn allocatedSize(self: MachO, segment_id: u8, start: u64) u64 {
4692 const segment = self.segments.items[segment_id];
4693 const indexes = self.getSectionIndexes(segment_id);
4694 assert(start >= segment.fileoff);
4695 var min_pos: u64 = segment.fileoff + segment.filesize;
4696 if (start > min_pos) return 0;
4697 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4698 if (header.offset <= start) continue;
4699 if (header.offset < min_pos) min_pos = header.offset;
4700 }
4701 return min_pos - start;
4702}
4703
4704fn getSectionMaxAlignment(self: *MachO, start: u8, end: u8) !u32 {
4705 var max_alignment: u32 = 1;
4706 const slice = self.sections.slice();
4707 for (slice.items(.header)[start..end]) |header| {
4708 const alignment = try math.powi(u32, 2, header.@"align");
4709 max_alignment = math.max(max_alignment, alignment);
4710 }
4711 return max_alignment;
4712}
4713
4714fn allocateAtomCommon(self: *MachO, atom: *Atom, sect_id: u8) !void {
4715 const sym = atom.getSymbolPtr(self);
4716 if (self.mode == .incremental) {
4717 const size = atom.size;
4718 const alignment = try math.powi(u32, 2, atom.alignment);
4719 const vaddr = try self.allocateAtom(atom, size, alignment, sect_id);
4720 const sym_name = atom.getName(self);
4721 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
4722 sym.n_value = vaddr;
4723 } else try self.addAtomToSection(atom, sect_id);
4724 sym.n_sect = sect_id + 1;
4725}
4726
4727fn allocateAtom(
4728 self: *MachO,
4729 atom: *Atom,
4730 new_atom_size: u64,
4731 alignment: u64,
4732 sect_id: u8,
4733) !u64 {
3754fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
47343755 const tracy = trace(@src());
47353756 defer tracy.end();
47363757
3758 const sect_id = atom.getSymbol(self).n_sect - 1;
3759 const segment = self.getSegmentPtr(sect_id);
47373760 const header = &self.sections.items(.header)[sect_id];
47383761 const free_list = &self.sections.items(.free_list)[sect_id];
47393762 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
4740 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
3763 const requires_padding = blk: {
3764 if (!header.isCode()) break :blk false;
3765 if (header.isSymbolStubs()) break :blk false;
3766 if (mem.eql(u8, "__stub_helper", header.sectName())) break :blk false;
3767 break :blk true;
3768 };
3769 const new_atom_ideal_capacity = if (requires_padding) padToIdeal(new_atom_size) else new_atom_size;
47413770
47423771 // We use these to indicate our intention to update metadata, placing the new atom,
47433772 // and possibly removing a free list node.
......@@ -4757,7 +3786,7 @@ fn allocateAtom(
47573786 // Is it enough that we could fit this new atom?
47583787 const sym = big_atom.getSymbol(self);
47593788 const capacity = big_atom.capacity(self);
4760 const ideal_capacity = if (header.isCode()) padToIdeal(capacity) else capacity;
3789 const ideal_capacity = if (requires_padding) padToIdeal(capacity) else capacity;
47613790 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
47623791 const capacity_end_vaddr = sym.n_value + capacity;
47633792 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
......@@ -4787,29 +3816,63 @@ fn allocateAtom(
47873816 break :blk new_start_vaddr;
47883817 } else if (maybe_last_atom.*) |last| {
47893818 const last_symbol = last.getSymbol(self);
4790 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
3819 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
47913820 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
47923821 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
47933822 atom_placement = last;
47943823 break :blk new_start_vaddr;
47953824 } else {
4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);
3825 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
47973826 }
47983827 };
47993828
48003829 const expand_section = atom_placement == null or atom_placement.?.next == null;
48013830 if (expand_section) {
4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);
4803 try self.growSection(sect_id, needed_size);
4804 maybe_last_atom.* = atom;
3831 const sect_capacity = self.allocatedSize(header.offset);
3832 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
3833 if (needed_size > sect_capacity) {
3834 const new_offset = self.findFreeSpace(needed_size, self.page_size);
3835 const current_size = if (maybe_last_atom.*) |last_atom| blk: {
3836 const sym = last_atom.getSymbol(self);
3837 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
3838 } else 0;
3839
3840 log.debug("moving {s},{s} from 0x{x} to 0x{x}", .{
3841 header.segName(),
3842 header.sectName(),
3843 header.offset,
3844 new_offset,
3845 });
3846
3847 const amt = try self.base.file.?.copyRangeAll(
3848 header.offset,
3849 self.base.file.?,
3850 new_offset,
3851 current_size,
3852 );
3853 if (amt != current_size) return error.InputOutput;
3854 header.offset = @intCast(u32, new_offset);
3855 segment.fileoff = new_offset;
3856 }
3857
3858 const sect_vm_capacity = self.allocatedVirtualSize(segment.vmaddr);
3859 if (needed_size > sect_vm_capacity) {
3860 self.markRelocsDirtyByAddress(segment.vmaddr + needed_size);
3861 try self.moveSectionInVirtualMemory(sect_id, needed_size);
3862 }
3863
48053864 header.size = needed_size;
3865 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3866 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3867 maybe_last_atom.* = atom;
3868
3869 self.segment_table_dirty = true;
48063870 }
3871
48073872 const align_pow = @intCast(u32, math.log2(alignment));
48083873 if (header.@"align" < align_pow) {
48093874 header.@"align" = align_pow;
48103875 }
4811 atom.size = new_atom_size;
4812 atom.alignment = align_pow;
48133876
48143877 if (atom.prev) |prev| {
48153878 prev.next = atom.next;
......@@ -4833,7 +3896,80 @@ fn allocateAtom(
48333896 return vaddr;
48343897}
48353898
4836pub fn addAtomToSection(self: *MachO, atom: *Atom, sect_id: u8) !void {
3899fn getSectionPrecedence(header: macho.section_64) u4 {
3900 if (header.isCode()) {
3901 if (mem.eql(u8, "__text", header.sectName())) return 0x0;
3902 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;
3903 return 0x2;
3904 }
3905 switch (header.@"type"()) {
3906 macho.S_NON_LAZY_SYMBOL_POINTERS,
3907 macho.S_LAZY_SYMBOL_POINTERS,
3908 => return 0x0,
3909 macho.S_MOD_INIT_FUNC_POINTERS => return 0x1,
3910 macho.S_MOD_TERM_FUNC_POINTERS => return 0x2,
3911 macho.S_ZEROFILL => return 0xf,
3912 macho.S_THREAD_LOCAL_REGULAR => return 0xd,
3913 macho.S_THREAD_LOCAL_ZEROFILL => return 0xe,
3914 else => if (mem.eql(u8, "__eh_frame", header.sectName()))
3915 return 0xf
3916 else
3917 return 0x3,
3918 }
3919}
3920
3921const InitSectionOpts = struct {
3922 flags: u32 = macho.S_REGULAR,
3923 reserved1: u32 = 0,
3924 reserved2: u32 = 0,
3925};
3926
3927pub fn initSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: InitSectionOpts) !u8 {
3928 const segment_id = self.getSegmentByName(segname).?;
3929 const seg = &self.segments.items[segment_id];
3930 const index = try self.insertSection(segment_id, .{
3931 .sectname = makeStaticString(sectname),
3932 .segname = seg.segname,
3933 .flags = opts.flags,
3934 .reserved1 = opts.reserved1,
3935 .reserved2 = opts.reserved2,
3936 });
3937 seg.cmdsize += @sizeOf(macho.section_64);
3938 seg.nsects += 1;
3939 return index;
3940}
3941
3942fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {
3943 const precedence = getSectionPrecedence(header);
3944 const indexes = self.getSectionIndexes(segment_index);
3945 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {
3946 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);
3947 } else indexes.end;
3948 log.debug("inserting section '{s},{s}' at index {d}", .{
3949 header.segName(),
3950 header.sectName(),
3951 insertion_index,
3952 });
3953 for (&[_]*?u8{
3954 &self.text_section_index,
3955 &self.stubs_section_index,
3956 &self.stub_helper_section_index,
3957 &self.got_section_index,
3958 &self.la_symbol_ptr_section_index,
3959 &self.data_section_index,
3960 }) |maybe_index| {
3961 const index = maybe_index.* orelse continue;
3962 if (insertion_index <= index) maybe_index.* = index + 1;
3963 }
3964 try self.sections.insert(self.base.allocator, insertion_index, .{
3965 .segment_index = segment_index,
3966 .header = header,
3967 });
3968 return insertion_index;
3969}
3970
3971pub fn addAtomToSection(self: *MachO, atom: *Atom) !void {
3972 const sect_id = atom.getSymbol(self).n_sect - 1;
48373973 var section = self.sections.get(sect_id);
48383974 if (section.header.size > 0) {
48393975 section.last_atom.?.next = atom;
......@@ -4872,203 +4008,209 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
48724008 return global_index;
48734009}
48744010
4875fn getSegmentAllocBase(self: MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
4876 for (indices) |maybe_prev_id| {
4877 const prev_id = maybe_prev_id orelse continue;
4878 const prev = self.segments.items[prev_id];
4879 return .{
4880 .vmaddr = prev.vmaddr + prev.vmsize,
4881 .fileoff = prev.fileoff + prev.filesize,
4882 };
4883 }
4884 return .{ .vmaddr = 0, .fileoff = 0 };
4885}
4886
48874011fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
48884012 for (self.segments.items) |seg, i| {
48894013 const indexes = self.getSectionIndexes(@intCast(u8, i));
4890 var out_seg = seg;
4891 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
4892 out_seg.nsects = 0;
4893
4894 // Update section headers count; any section with size of 0 is excluded
4895 // since it doesn't have any data in the final binary file.
4896 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4897 if (header.size == 0) continue;
4898 out_seg.cmdsize += @sizeOf(macho.section_64);
4899 out_seg.nsects += 1;
4900 }
4901
4902 if (out_seg.nsects == 0 and
4903 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
4904 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
4905
4906 try writer.writeStruct(out_seg);
4014 try writer.writeStruct(seg);
49074015 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4908 if (header.size == 0) continue;
49094016 try writer.writeStruct(header);
49104017 }
4911
49124018 ncmds.* += 1;
49134019 }
49144020}
49154021
49164022fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4917 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4023 const seg = self.getLinkeditSegmentPtr();
49184024 seg.filesize = 0;
49194025 seg.vmsize = 0;
49204026
4027 for (self.segments.items) |segment, id| {
4028 if (self.linkedit_segment_cmd_index.? == @intCast(u8, id)) continue;
4029 if (seg.vmaddr < segment.vmaddr + segment.vmsize) {
4030 seg.vmaddr = mem.alignForwardGeneric(u64, segment.vmaddr + segment.vmsize, self.page_size);
4031 }
4032 if (seg.fileoff < segment.fileoff + segment.filesize) {
4033 seg.fileoff = mem.alignForwardGeneric(u64, segment.fileoff + segment.filesize, self.page_size);
4034 }
4035 }
4036
49214037 try self.writeDyldInfoData(ncmds, lc_writer);
4922 try self.writeFunctionStarts(ncmds, lc_writer);
4923 try self.writeDataInCode(ncmds, lc_writer);
49244038 try self.writeSymtabs(ncmds, lc_writer);
49254039
49264040 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
49274041}
49284042
4929fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4930 const tracy = trace(@src());
4931 defer tracy.end();
4043const AtomLessThanByAddressContext = struct {
4044 macho_file: *MachO,
4045};
4046
4047fn atomLessThanByAddress(ctx: AtomLessThanByAddressContext, lhs: *Atom, rhs: *Atom) bool {
4048 return lhs.getSymbol(ctx.macho_file).n_value < rhs.getSymbol(ctx.macho_file).n_value;
4049}
49324050
4051fn collectRebaseData(self: *MachO, pointers: *std.ArrayList(bind.Pointer)) !void {
49334052 const gpa = self.base.allocator;
49344053
4935 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
4936 defer rebase_pointers.deinit();
4937 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
4938 defer bind_pointers.deinit();
4939 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
4940 defer lazy_bind_pointers.deinit();
4054 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
4055 defer sorted_atoms_by_address.deinit();
4056 try sorted_atoms_by_address.ensureTotalCapacityPrecise(self.rebases.count());
4057
4058 var it = self.rebases.keyIterator();
4059 while (it.next()) |key_ptr| {
4060 sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*);
4061 }
4062
4063 std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{
4064 .macho_file = self,
4065 }, atomLessThanByAddress);
49414066
49424067 const slice = self.sections.slice();
4943 for (slice.items(.last_atom)) |last_atom, sect_id| {
4944 var atom = last_atom orelse continue;
4945 const segment_index = slice.items(.segment_index)[sect_id];
4946 const header = slice.items(.header)[sect_id];
4068 for (sorted_atoms_by_address.items) |atom| {
4069 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
49474070
4948 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
4071 const sym = atom.getSymbol(self);
4072 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
4073 const seg = self.getSegment(sym.n_sect - 1);
49494074
4950 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
4075 const base_offset = sym.n_value - seg.vmaddr;
49514076
4952 const seg = self.segments.items[segment_index];
4077 const rebases = self.rebases.get(atom).?;
4078 try pointers.ensureUnusedCapacity(rebases.items.len);
4079 for (rebases.items) |offset| {
4080 log.debug(" | rebase at {x}", .{base_offset + offset});
49534081
4954 while (true) {
4955 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
4956 const sym = atom.getSymbol(self);
4957 const base_offset = sym.n_value - seg.vmaddr;
4958
4959 for (atom.rebases.items) |offset| {
4960 log.debug(" | rebase at {x}", .{base_offset + offset});
4961 try rebase_pointers.append(.{
4962 .offset = base_offset + offset,
4963 .segment_id = segment_index,
4964 });
4965 }
4082 pointers.appendAssumeCapacity(.{
4083 .offset = base_offset + offset,
4084 .segment_id = segment_index,
4085 });
4086 }
4087 }
4088}
49664089
4967 for (atom.bindings.items) |binding| {
4968 const bind_sym = self.getSymbol(binding.target);
4969 const bind_sym_name = self.getSymbolName(binding.target);
4970 const dylib_ordinal = @divTrunc(
4971 @bitCast(i16, bind_sym.n_desc),
4972 macho.N_SYMBOL_RESOLVER,
4973 );
4974 var flags: u4 = 0;
4975 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4976 binding.offset + base_offset,
4977 bind_sym_name,
4978 dylib_ordinal,
4979 });
4980 if (bind_sym.weakRef()) {
4981 log.debug(" | marking as weak ref ", .{});
4982 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
4983 }
4984 try bind_pointers.append(.{
4985 .offset = binding.offset + base_offset,
4986 .segment_id = segment_index,
4987 .dylib_ordinal = dylib_ordinal,
4988 .name = bind_sym_name,
4989 .bind_flags = flags,
4990 });
4991 }
4090fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bindings: anytype) !void {
4091 const gpa = self.base.allocator;
49924092
4993 for (atom.lazy_bindings.items) |binding| {
4994 const bind_sym = self.getSymbol(binding.target);
4995 const bind_sym_name = self.getSymbolName(binding.target);
4996 const dylib_ordinal = @divTrunc(
4997 @bitCast(i16, bind_sym.n_desc),
4998 macho.N_SYMBOL_RESOLVER,
4999 );
5000 var flags: u4 = 0;
5001 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5002 binding.offset + base_offset,
5003 bind_sym_name,
5004 dylib_ordinal,
5005 });
5006 if (bind_sym.weakRef()) {
5007 log.debug(" | marking as weak ref ", .{});
5008 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5009 }
5010 try lazy_bind_pointers.append(.{
5011 .offset = binding.offset + base_offset,
5012 .segment_id = segment_index,
5013 .dylib_ordinal = dylib_ordinal,
5014 .name = bind_sym_name,
5015 .bind_flags = flags,
5016 });
5017 }
4093 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
4094 defer sorted_atoms_by_address.deinit();
4095 try sorted_atoms_by_address.ensureTotalCapacityPrecise(raw_bindings.count());
50184096
5019 if (atom.prev) |prev| {
5020 atom = prev;
5021 } else break;
5022 }
4097 var it = raw_bindings.keyIterator();
4098 while (it.next()) |key_ptr| {
4099 sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*);
50234100 }
50244101
5025 var trie: Trie = .{};
5026 defer trie.deinit(gpa);
4102 std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{
4103 .macho_file = self,
4104 }, atomLessThanByAddress);
50274105
5028 {
5029 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
5030 log.debug("generating export trie", .{});
5031
5032 const text_segment = self.segments.items[self.text_segment_cmd_index.?];
5033 const base_address = text_segment.vmaddr;
5034
5035 if (self.base.options.output_mode == .Exe) {
5036 for (&[_]SymbolWithLoc{
5037 try self.getEntryPoint(),
5038 self.getGlobal("__mh_execute_header").?,
5039 }) |global| {
5040 const sym = self.getSymbol(global);
5041 const sym_name = self.getSymbolName(global);
5042 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
5043 try trie.put(gpa, .{
5044 .name = sym_name,
5045 .vmaddr_offset = sym.n_value - base_address,
5046 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
5047 });
5048 }
5049 } else {
5050 assert(self.base.options.output_mode == .Lib);
5051 for (self.globals.items) |global| {
5052 const sym = self.getSymbol(global);
5053
5054 if (sym.undf()) continue;
5055 if (!sym.ext()) continue;
5056 if (sym.n_desc == N_DESC_GCED) continue;
5057
5058 const sym_name = self.getSymbolName(global);
5059 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
5060 try trie.put(gpa, .{
5061 .name = sym_name,
5062 .vmaddr_offset = sym.n_value - base_address,
5063 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
5064 });
4106 const slice = self.sections.slice();
4107 for (sorted_atoms_by_address.items) |atom| {
4108 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
4109
4110 const sym = atom.getSymbol(self);
4111 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
4112 const seg = self.getSegment(sym.n_sect - 1);
4113
4114 const base_offset = sym.n_value - seg.vmaddr;
4115
4116 const bindings = raw_bindings.get(atom).?;
4117 try pointers.ensureUnusedCapacity(bindings.items.len);
4118 for (bindings.items) |binding| {
4119 const bind_sym = self.getSymbol(binding.target);
4120 const bind_sym_name = self.getSymbolName(binding.target);
4121 const dylib_ordinal = @divTrunc(
4122 @bitCast(i16, bind_sym.n_desc),
4123 macho.N_SYMBOL_RESOLVER,
4124 );
4125 var flags: u4 = 0;
4126 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
4127 binding.offset + base_offset,
4128 bind_sym_name,
4129 dylib_ordinal,
4130 });
4131 if (bind_sym.weakRef()) {
4132 log.debug(" | marking as weak ref ", .{});
4133 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
50654134 }
4135 pointers.appendAssumeCapacity(.{
4136 .offset = binding.offset + base_offset,
4137 .segment_id = segment_index,
4138 .dylib_ordinal = dylib_ordinal,
4139 .name = bind_sym_name,
4140 .bind_flags = flags,
4141 });
4142 }
4143 }
4144}
4145
4146fn collectExportData(self: *MachO, trie: *Trie) !void {
4147 const gpa = self.base.allocator;
4148
4149 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
4150 log.debug("generating export trie", .{});
4151
4152 const exec_segment = self.segments.items[self.header_segment_cmd_index.?];
4153 const base_address = exec_segment.vmaddr;
4154
4155 if (self.base.options.output_mode == .Exe) {
4156 for (&[_]SymbolWithLoc{
4157 try self.getEntryPoint(),
4158 self.getGlobal("__mh_execute_header").?,
4159 }) |global| {
4160 const sym = self.getSymbol(global);
4161 const sym_name = self.getSymbolName(global);
4162 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
4163 try trie.put(gpa, .{
4164 .name = sym_name,
4165 .vmaddr_offset = sym.n_value - base_address,
4166 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
4167 });
50664168 }
4169 } else {
4170 assert(self.base.options.output_mode == .Lib);
4171 for (self.globals.items) |global| {
4172 const sym = self.getSymbol(global);
4173
4174 if (sym.undf()) continue;
4175 if (!sym.ext()) continue;
4176 if (sym.n_desc == N_DESC_GCED) continue;
50674177
5068 try trie.finalize(gpa);
4178 const sym_name = self.getSymbolName(global);
4179 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
4180 try trie.put(gpa, .{
4181 .name = sym_name,
4182 .vmaddr_offset = sym.n_value - base_address,
4183 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
4184 });
4185 }
50694186 }
50704187
5071 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4188 try trie.finalize(gpa);
4189}
4190
4191fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
4192 const tracy = trace(@src());
4193 defer tracy.end();
4194
4195 const gpa = self.base.allocator;
4196
4197 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
4198 defer rebase_pointers.deinit();
4199 try self.collectRebaseData(&rebase_pointers);
4200
4201 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
4202 defer bind_pointers.deinit();
4203 try self.collectBindData(&bind_pointers, self.bindings);
4204
4205 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
4206 defer lazy_bind_pointers.deinit();
4207 try self.collectBindData(&lazy_bind_pointers, self.lazy_bindings);
4208
4209 var trie: Trie = .{};
4210 defer trie.deinit(gpa);
4211 try self.collectExportData(&trie);
4212
4213 const link_seg = self.getLinkeditSegmentPtr();
50724214 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
50734215 assert(rebase_off == link_seg.fileoff);
50744216 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
......@@ -5150,10 +4292,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
51504292 {
51514293 var stub_atom = last_atom;
51524294 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
5153 const base_addr = blk: {
5154 const seg = self.segments.items[self.data_segment_cmd_index.?];
5155 break :blk seg.vmaddr;
5156 };
4295 const base_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
51574296
51584297 while (true) {
51594298 const laptr_off = blk: {
......@@ -5236,154 +4375,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
52364375 }
52374376}
52384377
5239const asc_u64 = std.sort.asc(u64);
5240
5241fn writeFunctionStarts(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5242 const tracy = trace(@src());
5243 defer tracy.end();
5244
5245 const text_seg_index = self.text_segment_cmd_index orelse return;
5246 const text_sect_index = self.text_section_index orelse return;
5247 const text_seg = self.segments.items[text_seg_index];
5248
5249 const gpa = self.base.allocator;
5250
5251 // We need to sort by address first
5252 var addresses = std.ArrayList(u64).init(gpa);
5253 defer addresses.deinit();
5254 try addresses.ensureTotalCapacityPrecise(self.globals.items.len);
5255
5256 for (self.globals.items) |global| {
5257 const sym = self.getSymbol(global);
5258 if (sym.undf()) continue;
5259 if (sym.n_desc == N_DESC_GCED) continue;
5260 const sect_id = sym.n_sect - 1;
5261 if (sect_id != text_sect_index) continue;
5262
5263 addresses.appendAssumeCapacity(sym.n_value);
5264 }
5265
5266 std.sort.sort(u64, addresses.items, {}, asc_u64);
5267
5268 var offsets = std.ArrayList(u32).init(gpa);
5269 defer offsets.deinit();
5270 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
5271
5272 var last_off: u32 = 0;
5273 for (addresses.items) |addr| {
5274 const offset = @intCast(u32, addr - text_seg.vmaddr);
5275 const diff = offset - last_off;
5276
5277 if (diff == 0) continue;
5278
5279 offsets.appendAssumeCapacity(diff);
5280 last_off = offset;
5281 }
5282
5283 var buffer = std.ArrayList(u8).init(gpa);
5284 defer buffer.deinit();
5285
5286 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
5287 try buffer.ensureTotalCapacity(max_size);
5288
5289 for (offsets.items) |offset| {
5290 try std.leb.writeULEB128(buffer.writer(), offset);
5291 }
5292
5293 const link_seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5294 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
5295 const needed_size = buffer.items.len;
5296 link_seg.filesize = offset + needed_size - link_seg.fileoff;
5297
5298 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5299
5300 try self.base.file.?.pwriteAll(buffer.items, offset);
5301
5302 try lc_writer.writeStruct(macho.linkedit_data_command{
5303 .cmd = .FUNCTION_STARTS,
5304 .cmdsize = @sizeOf(macho.linkedit_data_command),
5305 .dataoff = @intCast(u32, offset),
5306 .datasize = @intCast(u32, needed_size),
5307 });
5308 ncmds.* += 1;
5309}
5310
5311fn filterDataInCode(
5312 dices: []align(1) const macho.data_in_code_entry,
5313 start_addr: u64,
5314 end_addr: u64,
5315) []align(1) const macho.data_in_code_entry {
5316 const Predicate = struct {
5317 addr: u64,
5318
5319 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
5320 return dice.offset >= self.addr;
5321 }
5322 };
5323
5324 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
5325 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
5326
5327 return dices[start..end];
5328}
5329
5330fn writeDataInCode(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5331 const tracy = trace(@src());
5332 defer tracy.end();
5333
5334 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
5335 defer out_dice.deinit();
5336
5337 const text_sect_id = self.text_section_index orelse return;
5338 const text_sect_header = self.sections.items(.header)[text_sect_id];
5339
5340 for (self.objects.items) |object| {
5341 const dice = object.parseDataInCode() orelse continue;
5342 try out_dice.ensureUnusedCapacity(dice.len);
5343
5344 for (object.managed_atoms.items) |atom| {
5345 const sym = atom.getSymbol(self);
5346 if (sym.n_desc == N_DESC_GCED) continue;
5347
5348 const sect_id = sym.n_sect - 1;
5349 if (sect_id != self.text_section_index.?) {
5350 continue;
5351 }
5352
5353 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
5354 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
5355 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
5356 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
5357 return error.Overflow;
5358
5359 for (filtered_dice) |single| {
5360 const offset = single.offset - source_addr + base;
5361 out_dice.appendAssumeCapacity(.{
5362 .offset = offset,
5363 .length = single.length,
5364 .kind = single.kind,
5365 });
5366 }
5367 }
5368 }
5369
5370 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
5371 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5372 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
5373 seg.filesize = offset + needed_size - seg.fileoff;
5374
5375 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
5376
5377 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
5378 try lc_writer.writeStruct(macho.linkedit_data_command{
5379 .cmd = .DATA_IN_CODE,
5380 .cmdsize = @sizeOf(macho.linkedit_data_command),
5381 .dataoff = @intCast(u32, offset),
5382 .datasize = @intCast(u32, needed_size),
5383 });
5384 ncmds.* += 1;
5385}
5386
53874378fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
53884379 var symtab_cmd = macho.symtab_command{
53894380 .cmdsize = @sizeOf(macho.symtab_command),
......@@ -5448,10 +4439,6 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
54484439 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
54494440 try locals.append(out_sym);
54504441 }
5451
5452 if (!self.base.options.strip) {
5453 try self.generateSymbolStabs(object, &locals);
5454 }
54554442 }
54564443
54574444 var exports = std.ArrayList(macho.nlist_64).init(gpa);
......@@ -5487,7 +4474,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
54874474 const nimports = @intCast(u32, imports.items.len);
54884475 const nsyms = nlocals + nexports + nimports;
54894476
5490 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4477 const seg = self.getLinkeditSegmentPtr();
54914478 const offset = mem.alignForwardGeneric(
54924479 u64,
54934480 seg.fileoff + seg.filesize,
......@@ -5518,7 +4505,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
55184505}
55194506
55204507fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
5521 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4508 const seg = self.getLinkeditSegmentPtr();
55224509 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
55234510 const needed_size = self.strtab.buffer.items.len;
55244511 seg.filesize = offset + needed_size - seg.fileoff;
......@@ -5546,7 +4533,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
55464533 const iextdefsym = ctx.nlocalsym;
55474534 const iundefsym = iextdefsym + ctx.nextdefsym;
55484535
5549 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4536 const seg = self.getLinkeditSegmentPtr();
55504537 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
55514538 const needed_size = nindirectsyms * @sizeOf(u32);
55524539 seg.filesize = offset + needed_size - seg.fileoff;
......@@ -5618,7 +4605,7 @@ fn writeCodeSignaturePadding(
56184605 ncmds: *u32,
56194606 lc_writer: anytype,
56204607) !u32 {
5621 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
4608 const seg = self.getLinkeditSegmentPtr();
56224609 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
56234610 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
56244611 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
......@@ -5642,7 +4629,7 @@ fn writeCodeSignaturePadding(
56424629}
56434630
56444631fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {
5645 const seg = self.segments.items[self.text_segment_cmd_index.?];
4632 const seg = self.getSegment(self.text_section_index.?);
56464633
56474634 var buffer = std.ArrayList(u8).init(self.base.allocator);
56484635 defer buffer.deinit();
......@@ -5713,6 +4700,57 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
57134700 std.math.maxInt(@TypeOf(actual_size));
57144701}
57154702
4703fn detectAllocCollision(self: *MachO, start: u64, size: u64) ?u64 {
4704 // TODO: header and load commands have to be part of the __TEXT segment
4705 const header_size = self.segments.items[self.header_segment_cmd_index.?].filesize;
4706 if (start < header_size)
4707 return header_size;
4708
4709 const end = start + padToIdeal(size);
4710
4711 for (self.sections.items(.header)) |header| {
4712 const tight_size = header.size;
4713 const increased_size = padToIdeal(tight_size);
4714 const test_end = header.offset + increased_size;
4715 if (end > header.offset and start < test_end) {
4716 return test_end;
4717 }
4718 }
4719
4720 return null;
4721}
4722
4723fn allocatedSize(self: *MachO, start: u64) u64 {
4724 if (start == 0)
4725 return 0;
4726 var min_pos: u64 = std.math.maxInt(u64);
4727 for (self.sections.items(.header)) |header| {
4728 if (header.offset <= start) continue;
4729 if (header.offset < min_pos) min_pos = header.offset;
4730 }
4731 return min_pos - start;
4732}
4733
4734fn findFreeSpace(self: *MachO, object_size: u64, min_alignment: u32) u64 {
4735 var start: u64 = 0;
4736 while (self.detectAllocCollision(start, object_size)) |item_end| {
4737 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
4738 }
4739 return start;
4740}
4741
4742pub fn allocatedVirtualSize(self: *MachO, start: u64) u64 {
4743 if (start == 0)
4744 return 0;
4745 var min_pos: u64 = std.math.maxInt(u64);
4746 for (self.sections.items(.segment_index)) |seg_id| {
4747 const segment = self.segments.items[seg_id];
4748 if (segment.vmaddr <= start) continue;
4749 if (segment.vmaddr < min_pos) min_pos = segment.vmaddr;
4750 }
4751 return min_pos - start;
4752}
4753
57164754pub fn makeStaticString(bytes: []const u8) [16]u8 {
57174755 var buf = [_]u8{0} ** 16;
57184756 assert(bytes.len <= buf.len);
......@@ -5726,6 +4764,21 @@ fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
57264764 } else return null;
57274765}
57284766
4767pub fn getSegment(self: MachO, sect_id: u8) macho.segment_command_64 {
4768 const index = self.sections.items(.segment_index)[sect_id];
4769 return self.segments.items[index];
4770}
4771
4772pub fn getSegmentPtr(self: *MachO, sect_id: u8) *macho.segment_command_64 {
4773 const index = self.sections.items(.segment_index)[sect_id];
4774 return &self.segments.items[index];
4775}
4776
4777pub fn getLinkeditSegmentPtr(self: *MachO) *macho.segment_command_64 {
4778 const index = self.linkedit_segment_cmd_index.?;
4779 return &self.segments.items[index];
4780}
4781
57294782pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
57304783 // TODO investigate caching with a hashmap
57314784 for (self.sections.items(.header)) |header, i| {
......@@ -5877,161 +4930,6 @@ pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, p
58774930 return i;
58784931}
58794932
5880pub fn generateSymbolStabs(
5881 self: *MachO,
5882 object: Object,
5883 locals: *std.ArrayList(macho.nlist_64),
5884) !void {
5885 assert(!self.base.options.strip);
5886
5887 log.debug("parsing debug info in '{s}'", .{object.name});
5888
5889 const gpa = self.base.allocator;
5890 var debug_info = try object.parseDwarfInfo();
5891 defer debug_info.deinit(gpa);
5892 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
5893
5894 // We assume there is only one CU.
5895 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
5896 error.MissingDebugInfo => {
5897 // TODO audit cases with missing debug info and audit our dwarf.zig module.
5898 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
5899 return;
5900 },
5901 else => |e| return e,
5902 };
5903
5904 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*);
5905 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*);
5906
5907 // Open scope
5908 try locals.ensureUnusedCapacity(3);
5909 locals.appendAssumeCapacity(.{
5910 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
5911 .n_type = macho.N_SO,
5912 .n_sect = 0,
5913 .n_desc = 0,
5914 .n_value = 0,
5915 });
5916 locals.appendAssumeCapacity(.{
5917 .n_strx = try self.strtab.insert(gpa, tu_name),
5918 .n_type = macho.N_SO,
5919 .n_sect = 0,
5920 .n_desc = 0,
5921 .n_value = 0,
5922 });
5923 locals.appendAssumeCapacity(.{
5924 .n_strx = try self.strtab.insert(gpa, object.name),
5925 .n_type = macho.N_OSO,
5926 .n_sect = 0,
5927 .n_desc = 1,
5928 .n_value = object.mtime,
5929 });
5930
5931 var stabs_buf: [4]macho.nlist_64 = undefined;
5932
5933 for (object.managed_atoms.items) |atom| {
5934 const stabs = try self.generateSymbolStabsForSymbol(
5935 atom.getSymbolWithLoc(),
5936 debug_info,
5937 &stabs_buf,
5938 );
5939 try locals.appendSlice(stabs);
5940
5941 for (atom.contained.items) |sym_at_off| {
5942 const sym_loc = SymbolWithLoc{
5943 .sym_index = sym_at_off.sym_index,
5944 .file = atom.file,
5945 };
5946 const contained_stabs = try self.generateSymbolStabsForSymbol(
5947 sym_loc,
5948 debug_info,
5949 &stabs_buf,
5950 );
5951 try locals.appendSlice(contained_stabs);
5952 }
5953 }
5954
5955 // Close scope
5956 try locals.append(.{
5957 .n_strx = 0,
5958 .n_type = macho.N_SO,
5959 .n_sect = 0,
5960 .n_desc = 0,
5961 .n_value = 0,
5962 });
5963}
5964
5965fn generateSymbolStabsForSymbol(
5966 self: *MachO,
5967 sym_loc: SymbolWithLoc,
5968 debug_info: dwarf.DwarfInfo,
5969 buf: *[4]macho.nlist_64,
5970) ![]const macho.nlist_64 {
5971 const gpa = self.base.allocator;
5972 const object = self.objects.items[sym_loc.file.?];
5973 const sym = self.getSymbol(sym_loc);
5974 const sym_name = self.getSymbolName(sym_loc);
5975
5976 if (sym.n_strx == 0) return buf[0..0];
5977 if (sym.n_desc == N_DESC_GCED) return buf[0..0];
5978 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
5979
5980 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
5981 const size: ?u64 = size: {
5982 if (source_sym.tentative()) break :size null;
5983 for (debug_info.func_list.items) |func| {
5984 if (func.pc_range) |range| {
5985 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
5986 break :size range.end - range.start;
5987 }
5988 }
5989 }
5990 break :size null;
5991 };
5992
5993 if (size) |ss| {
5994 buf[0] = .{
5995 .n_strx = 0,
5996 .n_type = macho.N_BNSYM,
5997 .n_sect = sym.n_sect,
5998 .n_desc = 0,
5999 .n_value = sym.n_value,
6000 };
6001 buf[1] = .{
6002 .n_strx = try self.strtab.insert(gpa, sym_name),
6003 .n_type = macho.N_FUN,
6004 .n_sect = sym.n_sect,
6005 .n_desc = 0,
6006 .n_value = sym.n_value,
6007 };
6008 buf[2] = .{
6009 .n_strx = 0,
6010 .n_type = macho.N_FUN,
6011 .n_sect = 0,
6012 .n_desc = 0,
6013 .n_value = ss,
6014 };
6015 buf[3] = .{
6016 .n_strx = 0,
6017 .n_type = macho.N_ENSYM,
6018 .n_sect = sym.n_sect,
6019 .n_desc = 0,
6020 .n_value = ss,
6021 };
6022 return buf;
6023 } else {
6024 buf[0] = .{
6025 .n_strx = try self.strtab.insert(gpa, sym_name),
6026 .n_type = macho.N_STSYM,
6027 .n_sect = sym.n_sect,
6028 .n_desc = 0,
6029 .n_value = sym.n_value,
6030 };
6031 return buf[0..1];
6032 }
6033}
6034
60354933// fn snapshotState(self: *MachO) !void {
60364934// const emit = self.base.options.emit orelse {
60374935// log.debug("no emit directory found; skipping snapshot...", .{});
......@@ -6287,7 +5185,7 @@ fn generateSymbolStabsForSymbol(
62875185// try writer.writeByte(']');
62885186// }
62895187
6290fn logSections(self: *MachO) void {
5188pub fn logSections(self: *MachO) void {
62915189 log.debug("sections:", .{});
62925190 for (self.sections.items(.header)) |header, i| {
62935191 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
......@@ -6325,7 +5223,7 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
63255223 return buf[0..];
63265224}
63275225
6328fn logSymtab(self: *MachO) void {
5226pub fn logSymtab(self: *MachO) void {
63295227 var buf: [9]u8 = undefined;
63305228
63315229 log.debug("symtab:", .{});
......@@ -6336,7 +5234,7 @@ fn logSymtab(self: *MachO) void {
63365234 const def_index = if (sym.undf() and !sym.tentative())
63375235 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
63385236 else
6339 sym.n_sect;
5237 sym.n_sect + 1;
63405238 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
63415239 sym_id,
63425240 object.getString(sym.n_strx),
......@@ -6353,7 +5251,7 @@ fn logSymtab(self: *MachO) void {
63535251 const def_index = if (sym.undf() and !sym.tentative())
63545252 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
63555253 else
6356 sym.n_sect;
5254 sym.n_sect + 1;
63575255 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
63585256 sym_id,
63595257 self.strtab.get(sym.n_strx),
......@@ -6418,7 +5316,7 @@ fn logSymtab(self: *MachO) void {
64185316 }
64195317}
64205318
6421fn logAtoms(self: *MachO) void {
5319pub fn logAtoms(self: *MachO) void {
64225320 log.debug("atoms:", .{});
64235321
64245322 const slice = self.sections.slice();
......@@ -6471,19 +5369,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
64715369 });
64725370 }
64735371}
6474
6475/// Since `os.copy_file_range` cannot be used when copying overlapping ranges within the same file,
6476/// and since `File.copyRangeAll` uses `os.copy_file_range` under-the-hood, we use heap allocated
6477/// buffers on all hosts except Linux (if `copy_file_range` syscall is available).
6478pub fn copyRangeAllOverlappingAlloc(
6479 allocator: Allocator,
6480 file: std.fs.File,
6481 in_offset: u64,
6482 out_offset: u64,
6483 len: usize,
6484) !void {
6485 const buf = try allocator.alloc(u8, len);
6486 defer allocator.free(buf);
6487 _ = try file.preadAll(buf, in_offset);
6488 try file.pwriteAll(buf, out_offset);
6489}
src/link/MachO/Atom.zig+84-5
......@@ -16,6 +16,7 @@ const Arch = std.Target.Cpu.Arch;
1616const Dwarf = @import("../Dwarf.zig");
1717const MachO = @import("../MachO.zig");
1818const Object = @import("Object.zig");
19const RelocationIncr = @import("Relocation.zig"); // temporary name until we clean up object-file relocation scanning
1920const SymbolWithLoc = MachO.SymbolWithLoc;
2021
2122/// Each decl always gets a local symbol with the fully qualified name.
......@@ -65,8 +66,6 @@ prev: ?*Atom,
6566
6667dbg_info_atom: Dwarf.Atom,
6768
68dirty: bool = true,
69
7069pub const Binding = struct {
7170 target: SymbolWithLoc,
7271 offset: u64,
......@@ -196,7 +195,7 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
196195 } else {
197196 // We are the last atom.
198197 // The capacity is limited only by virtual address space.
199 return std.math.maxInt(u64) - self_sym.n_value;
198 return macho_file.allocatedVirtualSize(self_sym.n_value);
200199 }
201200}
202201
......@@ -313,13 +312,13 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info,
313312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
314313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
315314 const sect = object.getSourceSection(sect_id);
316 const match = (try context.macho_file.getOutputSection(sect)) orelse
315 const out_sect_id = (try context.macho_file.getOutputSection(sect)) orelse
317316 unreachable;
318317 const sym_index = @intCast(u32, object.symtab.items.len);
319318 try object.symtab.append(gpa, .{
320319 .n_strx = 0,
321320 .n_type = macho.N_SECT,
322 .n_sect = match + 1,
321 .n_sect = out_sect_id + 1,
323322 .n_desc = 0,
324323 .n_value = sect.addr,
325324 });
......@@ -894,3 +893,83 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {
894893 const group_decode = @truncate(u5, inst[3]);
895894 return ((group_decode >> 2) == 4);
896895}
896
897pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: RelocationIncr) !void {
898 return self.addRelocations(macho_file, 1, .{reloc});
899}
900
901pub fn addRelocations(
902 self: *Atom,
903 macho_file: *MachO,
904 comptime count: comptime_int,
905 relocs: [count]RelocationIncr,
906) !void {
907 const gpa = macho_file.base.allocator;
908 const target = macho_file.base.options.target;
909 const gop = try macho_file.relocs.getOrPut(gpa, self);
910 if (!gop.found_existing) {
911 gop.value_ptr.* = .{};
912 }
913 try gop.value_ptr.ensureUnusedCapacity(gpa, count);
914 for (relocs) |reloc| {
915 log.debug(" (adding reloc of type {s} to target %{d})", .{
916 reloc.fmtType(target),
917 reloc.target.sym_index,
918 });
919 gop.value_ptr.appendAssumeCapacity(reloc);
920 }
921}
922
923pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {
924 const gpa = macho_file.base.allocator;
925 log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index });
926 const gop = try macho_file.rebases.getOrPut(gpa, self);
927 if (!gop.found_existing) {
928 gop.value_ptr.* = .{};
929 }
930 try gop.value_ptr.append(gpa, offset);
931}
932
933pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
934 const gpa = macho_file.base.allocator;
935 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{d})", .{
936 macho_file.getSymbolName(binding.target),
937 binding.offset,
938 self.sym_index,
939 });
940 const gop = try macho_file.bindings.getOrPut(gpa, self);
941 if (!gop.found_existing) {
942 gop.value_ptr.* = .{};
943 }
944 try gop.value_ptr.append(gpa, binding);
945}
946
947pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
948 const gpa = macho_file.base.allocator;
949 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{d})", .{
950 macho_file.getSymbolName(binding.target),
951 binding.offset,
952 self.sym_index,
953 });
954 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);
955 if (!gop.found_existing) {
956 gop.value_ptr.* = .{};
957 }
958 try gop.value_ptr.append(gpa, binding);
959}
960
961pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {
962 const relocs = macho_file.relocs.get(self) orelse return;
963 const source_sym = self.getSymbol(macho_file);
964 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
965 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
966
967 log.debug("relocating '{s}'", .{self.getName(macho_file)});
968
969 for (relocs.items) |*reloc| {
970 if (!reloc.dirty) continue;
971
972 try reloc.resolve(self, macho_file, file_offset);
973 reloc.dirty = false;
974 }
975}
src/link/MachO/DebugSymbols.zig+15-2
......@@ -512,7 +512,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
512512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513513 seg.filesize = aligned_size;
514514
515 try MachO.copyRangeAllOverlappingAlloc(
515 try copyRangeAllOverlappingAlloc(
516516 self.base.base.allocator,
517517 self.file,
518518 dwarf_seg.fileoff,
......@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
571571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572572 seg.filesize = aligned_size;
573573
574 try MachO.copyRangeAllOverlappingAlloc(
574 try copyRangeAllOverlappingAlloc(
575575 self.base.base.allocator,
576576 self.file,
577577 dwarf_seg.fileoff,
......@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601601
602602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
603603}
604
605fn copyRangeAllOverlappingAlloc(
606 allocator: Allocator,
607 file: std.fs.File,
608 in_offset: u64,
609 out_offset: u64,
610 len: usize,
611) !void {
612 const buf = try allocator.alloc(u8, len);
613 defer allocator.free(buf);
614 const amt = try file.preadAll(buf, in_offset);
615 try file.pwriteAll(buf[0..amt], out_offset);
616}
src/link/MachO/Object.zig+27-27
......@@ -220,15 +220,15 @@ fn filterRelocs(
220220
221221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
222222 for (self.sections.items) |sect| {
223 const match = (try macho_file.getOutputSection(sect)) orelse {
223 const sect_id = (try macho_file.getOutputSection(sect)) orelse {
224224 log.debug(" unhandled section", .{});
225225 continue;
226226 };
227 const output = macho_file.sections.items(.header)[match];
227 const output = macho_file.sections.items(.header)[sect_id];
228228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
229229 sect.segName(),
230230 sect.sectName(),
231 match + 1,
231 sect_id + 1,
232232 output.segName(),
233233 output.sectName(),
234234 });
......@@ -236,7 +236,7 @@ pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
236236}
237237
238238/// Splits object into atoms assuming one-shot linking mode.
239pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
239pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) !void {
240240 assert(macho_file.mode == .one_shot);
241241
242242 const tracy = trace(@src());
......@@ -249,7 +249,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
249249 const in_symtab = self.in_symtab orelse {
250250 for (self.sections.items) |sect, id| {
251251 if (sect.isDebug()) continue;
252 const match = (try macho_file.getOutputSection(sect)) orelse {
252 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
253253 log.debug(" unhandled section", .{});
254254 continue;
255255 };
......@@ -261,7 +261,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
261261 try self.symtab.append(gpa, .{
262262 .n_strx = 0,
263263 .n_type = macho.N_SECT,
264 .n_sect = match + 1,
264 .n_sect = out_sect_id + 1,
265265 .n_desc = 0,
266266 .n_value = sect.addr,
267267 });
......@@ -282,10 +282,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
282282 code,
283283 relocs,
284284 &.{},
285 match,
285 out_sect_id,
286286 sect,
287287 );
288 try macho_file.addAtomToSection(atom, match);
288 try macho_file.addAtomToSection(atom);
289289 }
290290 return;
291291 };
......@@ -335,15 +335,15 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
335335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336336
337337 // Get matching segment/section in the final artifact.
338 const match = (try macho_file.getOutputSection(sect)) orelse {
338 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
339339 log.debug(" unhandled section", .{});
340340 continue;
341341 };
342342
343343 log.debug(" output sect({d}, '{s},{s}')", .{
344 match + 1,
345 macho_file.sections.items(.header)[match].segName(),
346 macho_file.sections.items(.header)[match].sectName(),
344 out_sect_id + 1,
345 macho_file.sections.items(.header)[out_sect_id].segName(),
346 macho_file.sections.items(.header)[out_sect_id].sectName(),
347347 });
348348
349349 const cpu_arch = macho_file.base.options.target.cpu.arch;
......@@ -376,7 +376,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
376376 try self.symtab.append(gpa, .{
377377 .n_strx = 0,
378378 .n_type = macho.N_SECT,
379 .n_sect = match + 1,
379 .n_sect = out_sect_id + 1,
380380 .n_desc = 0,
381381 .n_value = sect.addr,
382382 });
......@@ -397,10 +397,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
397397 atom_code,
398398 relocs,
399399 &.{},
400 match,
400 out_sect_id,
401401 sect,
402402 );
403 try macho_file.addAtomToSection(atom, match);
403 try macho_file.addAtomToSection(atom);
404404 }
405405
406406 var next_sym_count: usize = 0;
......@@ -452,7 +452,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
452452 atom_code,
453453 relocs,
454454 sorted_atom_syms.items[1..],
455 match,
455 out_sect_id,
456456 sect,
457457 );
458458
......@@ -465,7 +465,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
465465 try self.symtab.append(gpa, .{
466466 .n_strx = 0,
467467 .n_type = macho.N_SECT,
468 .n_sect = match + 1,
468 .n_sect = out_sect_id + 1,
469469 .n_desc = 0,
470470 .n_value = addr,
471471 });
......@@ -479,7 +479,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
479479 try self.atom_by_index_table.put(gpa, alias, atom);
480480 }
481481
482 try macho_file.addAtomToSection(atom, match);
482 try macho_file.addAtomToSection(atom);
483483 }
484484 } else {
485485 // If there is no symbol to refer to this atom, we create
......@@ -490,7 +490,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
490490 try self.symtab.append(gpa, .{
491491 .n_strx = 0,
492492 .n_type = macho.N_SECT,
493 .n_sect = match + 1,
493 .n_sect = out_sect_id + 1,
494494 .n_desc = 0,
495495 .n_value = sect.addr,
496496 });
......@@ -506,10 +506,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
506506 code,
507507 relocs,
508508 filtered_syms,
509 match,
509 out_sect_id,
510510 sect,
511511 );
512 try macho_file.addAtomToSection(atom, match);
512 try macho_file.addAtomToSection(atom);
513513 }
514514 }
515515}
......@@ -524,21 +524,21 @@ fn createAtomFromSubsection(
524524 code: ?[]const u8,
525525 relocs: []align(1) const macho.relocation_info,
526526 indexes: []const SymbolAtIndex,
527 match: u8,
527 out_sect_id: u8,
528528 sect: macho.section_64,
529529) !*Atom {
530530 const gpa = macho_file.base.allocator;
531531 const sym = self.symtab.items[sym_index];
532532 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
533533 atom.file = object_id;
534 self.symtab.items[sym_index].n_sect = match + 1;
534 self.symtab.items[sym_index].n_sect = out_sect_id + 1;
535535
536536 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
537537 sym_index,
538538 self.getString(sym.n_strx),
539 match + 1,
540 macho_file.sections.items(.header)[match].segName(),
541 macho_file.sections.items(.header)[match].sectName(),
539 out_sect_id + 1,
540 macho_file.sections.items(.header)[out_sect_id].segName(),
541 macho_file.sections.items(.header)[out_sect_id].sectName(),
542542 object_id,
543543 });
544544
......@@ -566,7 +566,7 @@ fn createAtomFromSubsection(
566566 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
567567 for (indexes) |inner_sym_index| {
568568 const inner_sym = &self.symtab.items[inner_sym_index.index];
569 inner_sym.n_sect = match + 1;
569 inner_sym.n_sect = out_sect_id + 1;
570570 atom.contained.appendAssumeCapacity(.{
571571 .sym_index = inner_sym_index.index,
572572 .offset = inner_sym.n_value - sym.n_value,
src/link/MachO/Relocation.zig created+287
......@@ -0,0 +1,287 @@
1const Relocation = @This();
2
3const std = @import("std");
4const aarch64 = @import("../../arch/aarch64/bits.zig");
5const assert = std.debug.assert;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10const meta = std.meta;
11
12const Atom = @import("Atom.zig");
13const MachO = @import("../MachO.zig");
14const SymbolWithLoc = MachO.SymbolWithLoc;
15
16@"type": u4,
17target: SymbolWithLoc,
18offset: u32,
19addend: i64,
20pcrel: bool,
21length: u2,
22dirty: bool = true,
23
24pub fn fmtType(self: Relocation, target: std.Target) []const u8 {
25 switch (target.cpu.arch) {
26 .aarch64 => return @tagName(@intToEnum(macho.reloc_type_arm64, self.@"type")),
27 .x86_64 => return @tagName(@intToEnum(macho.reloc_type_x86_64, self.@"type")),
28 else => unreachable,
29 }
30}
31
32pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
33 switch (macho_file.base.options.target.cpu.arch) {
34 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
35 .ARM64_RELOC_GOT_LOAD_PAGE21,
36 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
37 .ARM64_RELOC_POINTER_TO_GOT,
38 => return macho_file.getGotAtomForSymbol(self.target),
39 else => {},
40 },
41 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
42 .X86_64_RELOC_GOT,
43 .X86_64_RELOC_GOT_LOAD,
44 => return macho_file.getGotAtomForSymbol(self.target),
45 else => {},
46 },
47 else => unreachable,
48 }
49 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
50 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
51 return macho_file.getAtomForSymbol(self.target);
52}
53
54pub fn resolve(self: Relocation, atom: *Atom, macho_file: *MachO, base_offset: u64) !void {
55 const arch = macho_file.base.options.target.cpu.arch;
56 const source_sym = atom.getSymbol(macho_file);
57 const source_addr = source_sym.n_value + self.offset;
58
59 const target_atom = self.getTargetAtom(macho_file) orelse return;
60 const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;
61
62 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
63 source_addr,
64 target_addr,
65 macho_file.getSymbolName(self.target),
66 self.fmtType(macho_file.base.options.target),
67 });
68
69 switch (arch) {
70 .aarch64 => return self.resolveAarch64(macho_file, source_addr, target_addr, base_offset),
71 .x86_64 => return self.resolveX8664(macho_file, source_addr, target_addr, base_offset),
72 else => unreachable,
73 }
74}
75
76fn resolveAarch64(
77 self: Relocation,
78 macho_file: *MachO,
79 source_addr: u64,
80 target_addr: i64,
81 base_offset: u64,
82) !void {
83 const rel_type = @intToEnum(macho.reloc_type_arm64, self.@"type");
84 if (rel_type == .ARM64_RELOC_UNSIGNED) {
85 var buffer: [@sizeOf(u64)]u8 = undefined;
86 const code = blk: {
87 switch (self.length) {
88 2 => {
89 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
90 break :blk buffer[0..4];
91 },
92 3 => {
93 mem.writeIntLittle(u64, &buffer, @bitCast(u64, target_addr));
94 break :blk &buffer;
95 },
96 else => unreachable,
97 }
98 };
99 return macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
100 }
101
102 var buffer: [@sizeOf(u32)]u8 = undefined;
103 const amt = try macho_file.base.file.?.preadAll(&buffer, base_offset + self.offset);
104 if (amt != buffer.len) return error.InputOutput;
105
106 switch (rel_type) {
107 .ARM64_RELOC_BRANCH26 => {
108 const displacement = math.cast(
109 i28,
110 @intCast(i64, target_addr) - @intCast(i64, source_addr),
111 ) orelse unreachable; // TODO codegen should never allow for jump larger than i28 displacement
112 var inst = aarch64.Instruction{
113 .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload(
114 aarch64.Instruction,
115 aarch64.Instruction.unconditional_branch_immediate,
116 ), &buffer),
117 };
118 inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2));
119 mem.writeIntLittle(u32, &buffer, inst.toU32());
120 },
121 .ARM64_RELOC_PAGE21,
122 .ARM64_RELOC_GOT_LOAD_PAGE21,
123 .ARM64_RELOC_TLVP_LOAD_PAGE21,
124 => {
125 const source_page = @intCast(i32, source_addr >> 12);
126 const target_page = @intCast(i32, target_addr >> 12);
127 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
128 var inst = aarch64.Instruction{
129 .pc_relative_address = mem.bytesToValue(meta.TagPayload(
130 aarch64.Instruction,
131 aarch64.Instruction.pc_relative_address,
132 ), &buffer),
133 };
134 inst.pc_relative_address.immhi = @truncate(u19, pages >> 2);
135 inst.pc_relative_address.immlo = @truncate(u2, pages);
136 mem.writeIntLittle(u32, &buffer, inst.toU32());
137 },
138 .ARM64_RELOC_PAGEOFF12,
139 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
140 => {
141 const narrowed = @truncate(u12, @intCast(u64, target_addr));
142 if (isArithmeticOp(&buffer)) {
143 var inst = aarch64.Instruction{
144 .add_subtract_immediate = mem.bytesToValue(meta.TagPayload(
145 aarch64.Instruction,
146 aarch64.Instruction.add_subtract_immediate,
147 ), &buffer),
148 };
149 inst.add_subtract_immediate.imm12 = narrowed;
150 mem.writeIntLittle(u32, &buffer, inst.toU32());
151 } else {
152 var inst = aarch64.Instruction{
153 .load_store_register = mem.bytesToValue(meta.TagPayload(
154 aarch64.Instruction,
155 aarch64.Instruction.load_store_register,
156 ), &buffer),
157 };
158 const offset: u12 = blk: {
159 if (inst.load_store_register.size == 0) {
160 if (inst.load_store_register.v == 1) {
161 // 128-bit SIMD is scaled by 16.
162 break :blk @divExact(narrowed, 16);
163 }
164 // Otherwise, 8-bit SIMD or ldrb.
165 break :blk narrowed;
166 } else {
167 const denom: u4 = math.powi(u4, 2, inst.load_store_register.size) catch unreachable;
168 break :blk @divExact(narrowed, denom);
169 }
170 };
171 inst.load_store_register.offset = offset;
172 mem.writeIntLittle(u32, &buffer, inst.toU32());
173 }
174 },
175 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
176 const RegInfo = struct {
177 rd: u5,
178 rn: u5,
179 size: u2,
180 };
181 const reg_info: RegInfo = blk: {
182 if (isArithmeticOp(&buffer)) {
183 const inst = mem.bytesToValue(meta.TagPayload(
184 aarch64.Instruction,
185 aarch64.Instruction.add_subtract_immediate,
186 ), &buffer);
187 break :blk .{
188 .rd = inst.rd,
189 .rn = inst.rn,
190 .size = inst.sf,
191 };
192 } else {
193 const inst = mem.bytesToValue(meta.TagPayload(
194 aarch64.Instruction,
195 aarch64.Instruction.load_store_register,
196 ), &buffer);
197 break :blk .{
198 .rd = inst.rt,
199 .rn = inst.rn,
200 .size = inst.size,
201 };
202 }
203 };
204 const narrowed = @truncate(u12, @intCast(u64, target_addr));
205 var inst = aarch64.Instruction{
206 .add_subtract_immediate = .{
207 .rd = reg_info.rd,
208 .rn = reg_info.rn,
209 .imm12 = narrowed,
210 .sh = 0,
211 .s = 0,
212 .op = 0,
213 .sf = @truncate(u1, reg_info.size),
214 },
215 };
216 mem.writeIntLittle(u32, &buffer, inst.toU32());
217 },
218 .ARM64_RELOC_POINTER_TO_GOT => {
219 const result = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr));
220 mem.writeIntLittle(i32, &buffer, result);
221 },
222 .ARM64_RELOC_SUBTRACTOR => unreachable,
223 .ARM64_RELOC_ADDEND => unreachable,
224 .ARM64_RELOC_UNSIGNED => unreachable,
225 }
226 try macho_file.base.file.?.pwriteAll(&buffer, base_offset + self.offset);
227}
228
229fn resolveX8664(
230 self: Relocation,
231 macho_file: *MachO,
232 source_addr: u64,
233 target_addr: i64,
234 base_offset: u64,
235) !void {
236 const rel_type = @intToEnum(macho.reloc_type_x86_64, self.@"type");
237 var buffer: [@sizeOf(u64)]u8 = undefined;
238 const code = blk: {
239 switch (rel_type) {
240 .X86_64_RELOC_BRANCH,
241 .X86_64_RELOC_GOT,
242 .X86_64_RELOC_GOT_LOAD,
243 .X86_64_RELOC_TLV,
244 => {
245 const displacement = @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4);
246 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
247 break :blk buffer[0..4];
248 },
249 .X86_64_RELOC_SIGNED,
250 .X86_64_RELOC_SIGNED_1,
251 .X86_64_RELOC_SIGNED_2,
252 .X86_64_RELOC_SIGNED_4,
253 => {
254 const correction: u3 = switch (rel_type) {
255 .X86_64_RELOC_SIGNED => 0,
256 .X86_64_RELOC_SIGNED_1 => 1,
257 .X86_64_RELOC_SIGNED_2 => 2,
258 .X86_64_RELOC_SIGNED_4 => 4,
259 else => unreachable,
260 };
261 const displacement = @intCast(i32, target_addr - @intCast(i64, source_addr + correction + 4));
262 mem.writeIntLittle(u32, buffer[0..4], @bitCast(u32, displacement));
263 break :blk buffer[0..4];
264 },
265 .X86_64_RELOC_UNSIGNED => {
266 switch (self.length) {
267 2 => {
268 mem.writeIntLittle(u32, buffer[0..4], @truncate(u32, @bitCast(u64, target_addr)));
269 break :blk buffer[0..4];
270 },
271 3 => {
272 mem.writeIntLittle(u64, buffer[0..8], @bitCast(u64, target_addr));
273 break :blk &buffer;
274 },
275 else => unreachable,
276 }
277 },
278 .X86_64_RELOC_SUBTRACTOR => unreachable,
279 }
280 };
281 try macho_file.base.file.?.pwriteAll(code, base_offset + self.offset);
282}
283
284inline fn isArithmeticOp(inst: *const [4]u8) bool {
285 const group_decode = @truncate(u5, inst[3]);
286 return ((group_decode >> 2) == 4);
287}
src/link/MachO/dead_strip.zig+3-3
......@@ -233,7 +233,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
233233 if (sym.n_desc != MachO.N_DESC_GCED) continue;
234234
235235 // TODO tombstone
236 const atom = entry.getAtom(macho_file);
236 const atom = entry.getAtom(macho_file).?;
237237 const match = sym.n_sect - 1;
238238 removeAtomFromSection(atom, match, macho_file);
239239 _ = try gc_sections.put(match, {});
......@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
245245 if (sym.n_desc != MachO.N_DESC_GCED) continue;
246246
247247 // TODO tombstone
248 const atom = entry.getAtom(macho_file);
248 const atom = entry.getAtom(macho_file).?;
249249 const match = sym.n_sect - 1;
250250 removeAtomFromSection(atom, match, macho_file);
251251 _ = try gc_sections.put(match, {});
......@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
257257 if (sym.n_desc != MachO.N_DESC_GCED) continue;
258258
259259 // TODO tombstone
260 const atom = entry.getAtom(macho_file);
260 const atom = entry.getAtom(macho_file).?;
261261 const match = sym.n_sect - 1;
262262 removeAtomFromSection(atom, match, macho_file);
263263 _ = try gc_sections.put(match, {});
src/link/MachO/zld.zig created+1953
......@@ -0,0 +1,1953 @@
1const std = @import("std");
2const build_options = @import("build_options");
3const assert = std.debug.assert;
4const dwarf = std.dwarf;
5const fs = std.fs;
6const log = std.log.scoped(.link);
7const macho = std.macho;
8const math = std.math;
9const mem = std.mem;
10
11const aarch64 = @import("../../arch/aarch64/bits.zig");
12const bind = @import("bind.zig");
13const link = @import("../../link.zig");
14const trace = @import("../../tracy.zig").trace;
15
16const Atom = MachO.Atom;
17const Cache = @import("../../Cache.zig");
18const CodeSignature = @import("CodeSignature.zig");
19const Compilation = @import("../../Compilation.zig");
20const Dylib = @import("Dylib.zig");
21const MachO = @import("../MachO.zig");
22const Object = @import("Object.zig");
23const SymbolWithLoc = MachO.SymbolWithLoc;
24const Trie = @import("Trie.zig");
25
26const dead_strip = @import("dead_strip.zig");
27
28pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
29 const tracy = trace(@src());
30 defer tracy.end();
31
32 const gpa = macho_file.base.allocator;
33 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
34 defer arena_allocator.deinit();
35 const arena = arena_allocator.allocator();
36
37 const directory = macho_file.base.options.emit.?.directory; // Just an alias to make it shorter to type.
38 const full_out_path = try directory.join(arena, &[_][]const u8{macho_file.base.options.emit.?.sub_path});
39
40 // If there is no Zig code to compile, then we should skip flushing the output file because it
41 // will not be part of the linker line anyway.
42 const module_obj_path: ?[]const u8 = if (macho_file.base.options.module) |module| blk: {
43 if (macho_file.base.options.use_stage1) {
44 const obj_basename = try std.zig.binNameAlloc(arena, .{
45 .root_name = macho_file.base.options.root_name,
46 .target = macho_file.base.options.target,
47 .output_mode = .Obj,
48 });
49 switch (macho_file.base.options.cache_mode) {
50 .incremental => break :blk try module.zig_cache_artifact_directory.join(
51 arena,
52 &[_][]const u8{obj_basename},
53 ),
54 .whole => break :blk try fs.path.join(arena, &.{
55 fs.path.dirname(full_out_path).?, obj_basename,
56 }),
57 }
58 }
59
60 try macho_file.flushModule(comp, prog_node);
61
62 if (fs.path.dirname(full_out_path)) |dirname| {
63 break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? });
64 } else {
65 break :blk macho_file.base.intermediary_basename.?;
66 }
67 } else null;
68
69 var sub_prog_node = prog_node.start("MachO Flush", 0);
70 sub_prog_node.activate();
71 sub_prog_node.context.refresh();
72 defer sub_prog_node.end();
73
74 const cpu_arch = macho_file.base.options.target.cpu.arch;
75 const os_tag = macho_file.base.options.target.os.tag;
76 const abi = macho_file.base.options.target.abi;
77 const is_lib = macho_file.base.options.output_mode == .Lib;
78 const is_dyn_lib = macho_file.base.options.link_mode == .Dynamic and is_lib;
79 const is_exe_or_dyn_lib = is_dyn_lib or macho_file.base.options.output_mode == .Exe;
80 const stack_size = macho_file.base.options.stack_size_override orelse 0;
81 const is_debug_build = macho_file.base.options.optimize_mode == .Debug;
82 const gc_sections = macho_file.base.options.gc_sections orelse !is_debug_build;
83
84 const id_symlink_basename = "zld.id";
85
86 var man: Cache.Manifest = undefined;
87 defer if (!macho_file.base.options.disable_lld_caching) man.deinit();
88
89 var digest: [Cache.hex_digest_len]u8 = undefined;
90
91 if (!macho_file.base.options.disable_lld_caching) {
92 man = comp.cache_parent.obtain();
93
94 // We are about to obtain this lock, so here we give other processes a chance first.
95 macho_file.base.releaseLock();
96
97 comptime assert(Compilation.link_hash_implementation_version == 7);
98
99 for (macho_file.base.options.objects) |obj| {
100 _ = try man.addFile(obj.path, null);
101 man.hash.add(obj.must_link);
102 }
103 for (comp.c_object_table.keys()) |key| {
104 _ = try man.addFile(key.status.success.object_path, null);
105 }
106 try man.addOptionalFile(module_obj_path);
107 // We can skip hashing libc and libc++ components that we are in charge of building from Zig
108 // installation sources because they are always a product of the compiler version + target information.
109 man.hash.add(stack_size);
110 man.hash.addOptional(macho_file.base.options.pagezero_size);
111 man.hash.addOptional(macho_file.base.options.search_strategy);
112 man.hash.addOptional(macho_file.base.options.headerpad_size);
113 man.hash.add(macho_file.base.options.headerpad_max_install_names);
114 man.hash.add(gc_sections);
115 man.hash.add(macho_file.base.options.dead_strip_dylibs);
116 man.hash.add(macho_file.base.options.strip);
117 man.hash.addListOfBytes(macho_file.base.options.lib_dirs);
118 man.hash.addListOfBytes(macho_file.base.options.framework_dirs);
119 link.hashAddSystemLibs(&man.hash, macho_file.base.options.frameworks);
120 man.hash.addListOfBytes(macho_file.base.options.rpath_list);
121 if (is_dyn_lib) {
122 man.hash.addOptionalBytes(macho_file.base.options.install_name);
123 man.hash.addOptional(macho_file.base.options.version);
124 }
125 link.hashAddSystemLibs(&man.hash, macho_file.base.options.system_libs);
126 man.hash.addOptionalBytes(macho_file.base.options.sysroot);
127 try man.addOptionalFile(macho_file.base.options.entitlements);
128
129 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
130 _ = try man.hit();
131 digest = man.final();
132
133 var prev_digest_buf: [digest.len]u8 = undefined;
134 const prev_digest: []u8 = Cache.readSmallFile(
135 directory.handle,
136 id_symlink_basename,
137 &prev_digest_buf,
138 ) catch |err| blk: {
139 log.debug("MachO Zld new_digest={s} error: {s}", .{
140 std.fmt.fmtSliceHexLower(&digest),
141 @errorName(err),
142 });
143 // Handle this as a cache miss.
144 break :blk prev_digest_buf[0..0];
145 };
146 if (mem.eql(u8, prev_digest, &digest)) {
147 // Hot diggity dog! The output binary is already there.
148 log.debug("MachO Zld digest={s} match - skipping invocation", .{
149 std.fmt.fmtSliceHexLower(&digest),
150 });
151 macho_file.base.lock = man.toOwnedLock();
152 return;
153 }
154 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
155 std.fmt.fmtSliceHexLower(prev_digest),
156 std.fmt.fmtSliceHexLower(&digest),
157 });
158
159 // We are about to change the output file to be different, so we invalidate the build hash now.
160 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
161 error.FileNotFound => {},
162 else => |e| return e,
163 };
164 }
165
166 if (macho_file.base.options.output_mode == .Obj) {
167 // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy
168 // here. TODO: think carefully about how we can avoid this redundant operation when doing
169 // build-obj. See also the corresponding TODO in linkAsArchive.
170 const the_object_path = blk: {
171 if (macho_file.base.options.objects.len != 0) {
172 break :blk macho_file.base.options.objects[0].path;
173 }
174
175 if (comp.c_object_table.count() != 0)
176 break :blk comp.c_object_table.keys()[0].status.success.object_path;
177
178 if (module_obj_path) |p|
179 break :blk p;
180
181 // TODO I think this is unreachable. Audit this situation when solving the above TODO
182 // regarding eliding redundant object -> object transformations.
183 return error.NoObjectsToLink;
184 };
185 // This can happen when using --enable-cache and using the stage1 backend. In this case
186 // we can skip the file copy.
187 if (!mem.eql(u8, the_object_path, full_out_path)) {
188 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
189 }
190 } else {
191 const sub_path = macho_file.base.options.emit.?.sub_path;
192 if (macho_file.base.file == null) {
193 macho_file.base.file = try directory.handle.createFile(sub_path, .{
194 .truncate = true,
195 .read = true,
196 .mode = link.determineMode(macho_file.base.options),
197 });
198 }
199 // Index 0 is always a null symbol.
200 try macho_file.locals.append(gpa, .{
201 .n_strx = 0,
202 .n_type = 0,
203 .n_sect = 0,
204 .n_desc = 0,
205 .n_value = 0,
206 });
207 try macho_file.strtab.buffer.append(gpa, 0);
208 try initSections(macho_file);
209
210 var lib_not_found = false;
211 var framework_not_found = false;
212
213 // Positional arguments to the linker such as object files and static archives.
214 var positionals = std.ArrayList([]const u8).init(arena);
215 try positionals.ensureUnusedCapacity(macho_file.base.options.objects.len);
216
217 var must_link_archives = std.StringArrayHashMap(void).init(arena);
218 try must_link_archives.ensureUnusedCapacity(macho_file.base.options.objects.len);
219
220 for (macho_file.base.options.objects) |obj| {
221 if (must_link_archives.contains(obj.path)) continue;
222 if (obj.must_link) {
223 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
224 } else {
225 _ = positionals.appendAssumeCapacity(obj.path);
226 }
227 }
228
229 for (comp.c_object_table.keys()) |key| {
230 try positionals.append(key.status.success.object_path);
231 }
232
233 if (module_obj_path) |p| {
234 try positionals.append(p);
235 }
236
237 if (comp.compiler_rt_lib) |lib| {
238 try positionals.append(lib.full_object_path);
239 }
240
241 // libc++ dep
242 if (macho_file.base.options.link_libcpp) {
243 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
244 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
245 }
246
247 // Shared and static libraries passed via `-l` flag.
248 var candidate_libs = std.StringArrayHashMap(link.SystemLib).init(arena);
249
250 const system_lib_names = macho_file.base.options.system_libs.keys();
251 for (system_lib_names) |system_lib_name| {
252 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
253 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
254 // case we want to avoid prepending "-l".
255 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
256 try positionals.append(system_lib_name);
257 continue;
258 }
259
260 const system_lib_info = macho_file.base.options.system_libs.get(system_lib_name).?;
261 try candidate_libs.put(system_lib_name, .{
262 .needed = system_lib_info.needed,
263 .weak = system_lib_info.weak,
264 });
265 }
266
267 var lib_dirs = std.ArrayList([]const u8).init(arena);
268 for (macho_file.base.options.lib_dirs) |dir| {
269 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
270 try lib_dirs.append(search_dir);
271 } else {
272 log.warn("directory not found for '-L{s}'", .{dir});
273 }
274 }
275
276 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
277
278 // Assume ld64 default -search_paths_first if no strategy specified.
279 const search_strategy = macho_file.base.options.search_strategy orelse .paths_first;
280 outer: for (candidate_libs.keys()) |lib_name| {
281 switch (search_strategy) {
282 .paths_first => {
283 // Look in each directory for a dylib (stub first), and then for archive
284 for (lib_dirs.items) |dir| {
285 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
286 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
287 try libs.put(full_path, candidate_libs.get(lib_name).?);
288 continue :outer;
289 }
290 }
291 } else {
292 log.warn("library not found for '-l{s}'", .{lib_name});
293 lib_not_found = true;
294 }
295 },
296 .dylibs_first => {
297 // First, look for a dylib in each search dir
298 for (lib_dirs.items) |dir| {
299 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
300 if (try MachO.resolveLib(arena, dir, lib_name, ext)) |full_path| {
301 try libs.put(full_path, candidate_libs.get(lib_name).?);
302 continue :outer;
303 }
304 }
305 } else for (lib_dirs.items) |dir| {
306 if (try MachO.resolveLib(arena, dir, lib_name, ".a")) |full_path| {
307 try libs.put(full_path, candidate_libs.get(lib_name).?);
308 } else {
309 log.warn("library not found for '-l{s}'", .{lib_name});
310 lib_not_found = true;
311 }
312 }
313 },
314 }
315 }
316
317 if (lib_not_found) {
318 log.warn("Library search paths:", .{});
319 for (lib_dirs.items) |dir| {
320 log.warn(" {s}", .{dir});
321 }
322 }
323
324 try macho_file.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
325
326 // frameworks
327 var framework_dirs = std.ArrayList([]const u8).init(arena);
328 for (macho_file.base.options.framework_dirs) |dir| {
329 if (try MachO.resolveSearchDir(arena, dir, macho_file.base.options.sysroot)) |search_dir| {
330 try framework_dirs.append(search_dir);
331 } else {
332 log.warn("directory not found for '-F{s}'", .{dir});
333 }
334 }
335
336 outer: for (macho_file.base.options.frameworks.keys()) |f_name| {
337 for (framework_dirs.items) |dir| {
338 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
339 if (try MachO.resolveFramework(arena, dir, f_name, ext)) |full_path| {
340 const info = macho_file.base.options.frameworks.get(f_name).?;
341 try libs.put(full_path, .{
342 .needed = info.needed,
343 .weak = info.weak,
344 });
345 continue :outer;
346 }
347 }
348 } else {
349 log.warn("framework not found for '-framework {s}'", .{f_name});
350 framework_not_found = true;
351 }
352 }
353
354 if (framework_not_found) {
355 log.warn("Framework search paths:", .{});
356 for (framework_dirs.items) |dir| {
357 log.warn(" {s}", .{dir});
358 }
359 }
360
361 if (macho_file.base.options.verbose_link) {
362 var argv = std.ArrayList([]const u8).init(arena);
363
364 try argv.append("zig");
365 try argv.append("ld");
366
367 if (is_exe_or_dyn_lib) {
368 try argv.append("-dynamic");
369 }
370
371 if (is_dyn_lib) {
372 try argv.append("-dylib");
373
374 if (macho_file.base.options.install_name) |install_name| {
375 try argv.append("-install_name");
376 try argv.append(install_name);
377 }
378 }
379
380 if (macho_file.base.options.sysroot) |syslibroot| {
381 try argv.append("-syslibroot");
382 try argv.append(syslibroot);
383 }
384
385 for (macho_file.base.options.rpath_list) |rpath| {
386 try argv.append("-rpath");
387 try argv.append(rpath);
388 }
389
390 if (macho_file.base.options.pagezero_size) |pagezero_size| {
391 try argv.append("-pagezero_size");
392 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
393 }
394
395 if (macho_file.base.options.search_strategy) |strat| switch (strat) {
396 .paths_first => try argv.append("-search_paths_first"),
397 .dylibs_first => try argv.append("-search_dylibs_first"),
398 };
399
400 if (macho_file.base.options.headerpad_size) |headerpad_size| {
401 try argv.append("-headerpad_size");
402 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
403 }
404
405 if (macho_file.base.options.headerpad_max_install_names) {
406 try argv.append("-headerpad_max_install_names");
407 }
408
409 if (gc_sections) {
410 try argv.append("-dead_strip");
411 }
412
413 if (macho_file.base.options.dead_strip_dylibs) {
414 try argv.append("-dead_strip_dylibs");
415 }
416
417 if (macho_file.base.options.entry) |entry| {
418 try argv.append("-e");
419 try argv.append(entry);
420 }
421
422 for (macho_file.base.options.objects) |obj| {
423 try argv.append(obj.path);
424 }
425
426 for (comp.c_object_table.keys()) |key| {
427 try argv.append(key.status.success.object_path);
428 }
429
430 if (module_obj_path) |p| {
431 try argv.append(p);
432 }
433
434 if (comp.compiler_rt_lib) |lib| {
435 try argv.append(lib.full_object_path);
436 }
437
438 if (macho_file.base.options.link_libcpp) {
439 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
440 try argv.append(comp.libcxx_static_lib.?.full_object_path);
441 }
442
443 try argv.append("-o");
444 try argv.append(full_out_path);
445
446 try argv.append("-lSystem");
447 try argv.append("-lc");
448
449 for (macho_file.base.options.system_libs.keys()) |l_name| {
450 const info = macho_file.base.options.system_libs.get(l_name).?;
451 const arg = if (info.needed)
452 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
453 else if (info.weak)
454 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
455 else
456 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
457 try argv.append(arg);
458 }
459
460 for (macho_file.base.options.lib_dirs) |lib_dir| {
461 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
462 }
463
464 for (macho_file.base.options.frameworks.keys()) |framework| {
465 const info = macho_file.base.options.frameworks.get(framework).?;
466 const arg = if (info.needed)
467 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
468 else if (info.weak)
469 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
470 else
471 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
472 try argv.append(arg);
473 }
474
475 for (macho_file.base.options.framework_dirs) |framework_dir| {
476 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
477 }
478
479 if (is_dyn_lib and (macho_file.base.options.allow_shlib_undefined orelse false)) {
480 try argv.append("-undefined");
481 try argv.append("dynamic_lookup");
482 }
483
484 for (must_link_archives.keys()) |lib| {
485 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
486 }
487
488 Compilation.dump_argv(argv.items);
489 }
490
491 var dependent_libs = std.fifo.LinearFifo(struct {
492 id: Dylib.Id,
493 parent: u16,
494 }, .Dynamic).init(arena);
495
496 try macho_file.parseInputFiles(positionals.items, macho_file.base.options.sysroot, &dependent_libs);
497 try macho_file.parseAndForceLoadStaticArchives(must_link_archives.keys());
498 try macho_file.parseLibs(libs.keys(), libs.values(), macho_file.base.options.sysroot, &dependent_libs);
499 try macho_file.parseDependentLibs(macho_file.base.options.sysroot, &dependent_libs);
500
501 for (macho_file.objects.items) |_, object_id| {
502 try macho_file.resolveSymbolsInObject(@intCast(u16, object_id));
503 }
504
505 try macho_file.resolveSymbolsInArchives();
506 try macho_file.resolveDyldStubBinder();
507 try macho_file.resolveSymbolsInDylibs();
508 try macho_file.createMhExecuteHeaderSymbol();
509 try macho_file.createDsoHandleSymbol();
510 try macho_file.resolveSymbolsAtLoading();
511
512 if (macho_file.unresolved.count() > 0) {
513 return error.UndefinedSymbolReference;
514 }
515 if (lib_not_found) {
516 return error.LibraryNotFound;
517 }
518 if (framework_not_found) {
519 return error.FrameworkNotFound;
520 }
521
522 for (macho_file.objects.items) |*object| {
523 try object.scanInputSections(macho_file);
524 }
525
526 try macho_file.createDyldPrivateAtom();
527 try macho_file.createTentativeDefAtoms();
528 try macho_file.createStubHelperPreambleAtom();
529
530 for (macho_file.objects.items) |*object, object_id| {
531 try object.splitIntoAtoms(macho_file, @intCast(u32, object_id));
532 }
533
534 if (gc_sections) {
535 try dead_strip.gcAtoms(macho_file);
536 }
537
538 try allocateSegments(macho_file);
539 try allocateSymbols(macho_file);
540
541 try macho_file.allocateSpecialSymbols();
542
543 if (build_options.enable_logging or true) {
544 macho_file.logSymtab();
545 macho_file.logSections();
546 macho_file.logAtoms();
547 }
548
549 try writeAtoms(macho_file);
550
551 var lc_buffer = std.ArrayList(u8).init(arena);
552 const lc_writer = lc_buffer.writer();
553 var ncmds: u32 = 0;
554
555 try writeLinkeditSegmentData(macho_file, &ncmds, lc_writer);
556
557 // If the last section of __DATA segment is zerofill section, we need to ensure
558 // that the free space between the end of the last non-zerofill section of __DATA
559 // segment and the beginning of __LINKEDIT segment is zerofilled as the loader will
560 // copy-paste this space into memory for quicker zerofill operation.
561 if (macho_file.data_segment_cmd_index) |data_seg_id| blk: {
562 var physical_zerofill_start: u64 = 0;
563 const section_indexes = macho_file.getSectionIndexes(data_seg_id);
564 for (macho_file.sections.items(.header)[section_indexes.start..section_indexes.end]) |header| {
565 if (header.isZerofill() and header.size > 0) break;
566 physical_zerofill_start = header.offset + header.size;
567 } else break :blk;
568 const linkedit = macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
569 const physical_zerofill_size = math.cast(usize, linkedit.fileoff - physical_zerofill_start) orelse
570 return error.Overflow;
571 if (physical_zerofill_size > 0) {
572 var padding = try macho_file.base.allocator.alloc(u8, physical_zerofill_size);
573 defer macho_file.base.allocator.free(padding);
574 mem.set(u8, padding, 0);
575 try macho_file.base.file.?.pwriteAll(padding, physical_zerofill_start);
576 }
577 }
578
579 try MachO.writeDylinkerLC(&ncmds, lc_writer);
580 try macho_file.writeMainLC(&ncmds, lc_writer);
581 try macho_file.writeDylibIdLC(&ncmds, lc_writer);
582 try macho_file.writeRpathLCs(&ncmds, lc_writer);
583
584 {
585 try lc_writer.writeStruct(macho.source_version_command{
586 .cmdsize = @sizeOf(macho.source_version_command),
587 .version = 0x0,
588 });
589 ncmds += 1;
590 }
591
592 try macho_file.writeBuildVersionLC(&ncmds, lc_writer);
593
594 {
595 var uuid_lc = macho.uuid_command{
596 .cmdsize = @sizeOf(macho.uuid_command),
597 .uuid = undefined,
598 };
599 std.crypto.random.bytes(&uuid_lc.uuid);
600 try lc_writer.writeStruct(uuid_lc);
601 ncmds += 1;
602 }
603
604 try macho_file.writeLoadDylibLCs(&ncmds, lc_writer);
605
606 const requires_codesig = blk: {
607 if (macho_file.base.options.entitlements) |_| break :blk true;
608 if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) break :blk true;
609 break :blk false;
610 };
611 var codesig_offset: ?u32 = null;
612 var codesig: ?CodeSignature = if (requires_codesig) blk: {
613 // Preallocate space for the code signature.
614 // We need to do this at this stage so that we have the load commands with proper values
615 // written out to the file.
616 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
617 // where the code signature goes into.
618 var codesig = CodeSignature.init(macho_file.page_size);
619 codesig.code_directory.ident = macho_file.base.options.emit.?.sub_path;
620 if (macho_file.base.options.entitlements) |path| {
621 try codesig.addEntitlements(arena, path);
622 }
623 codesig_offset = try writeCodeSignaturePadding(macho_file, &codesig, &ncmds, lc_writer);
624 break :blk codesig;
625 } else null;
626
627 var headers_buf = std.ArrayList(u8).init(arena);
628 try writeSegmentHeaders(macho_file, &ncmds, headers_buf.writer());
629
630 try macho_file.base.file.?.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
631 try macho_file.base.file.?.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
632
633 try writeHeader(macho_file, ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
634
635 if (codesig) |*csig| {
636 try writeCodeSignature(macho_file, csig, codesig_offset.?); // code signing always comes last
637 }
638 }
639
640 if (!macho_file.base.options.disable_lld_caching) {
641 // Update the file with the digest. If it fails we can continue; it only
642 // means that the next invocation will have an unnecessary cache miss.
643 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
644 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
645 };
646 // Again failure here only means an unnecessary cache miss.
647 man.writeManifest() catch |err| {
648 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
649 };
650 // We hang on to this lock so that the output file path can be used without
651 // other processes clobbering it.
652 macho_file.base.lock = man.toOwnedLock();
653 }
654}
655
656fn initSections(macho_file: *MachO) !void {
657 const gpa = macho_file.base.allocator;
658 const cpu_arch = macho_file.base.options.target.cpu.arch;
659 const pagezero_vmsize = macho_file.calcPagezeroSize();
660
661 if (macho_file.pagezero_segment_cmd_index == null) {
662 if (pagezero_vmsize > 0) {
663 macho_file.pagezero_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
664 try macho_file.segments.append(gpa, .{
665 .segname = MachO.makeStaticString("__PAGEZERO"),
666 .vmsize = pagezero_vmsize,
667 .cmdsize = @sizeOf(macho.segment_command_64),
668 });
669 }
670 }
671
672 if (macho_file.text_segment_cmd_index == null) {
673 macho_file.text_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
674 try macho_file.segments.append(gpa, .{
675 .segname = MachO.makeStaticString("__TEXT"),
676 .vmaddr = pagezero_vmsize,
677 .vmsize = 0,
678 .filesize = 0,
679 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
680 .initprot = macho.PROT.READ | macho.PROT.EXEC,
681 .cmdsize = @sizeOf(macho.segment_command_64),
682 });
683 }
684
685 if (macho_file.text_section_index == null) {
686 macho_file.text_section_index = try macho_file.initSection("__TEXT", "__text", .{
687 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
688 });
689 }
690
691 if (macho_file.stubs_section_index == null) {
692 const stub_size: u4 = switch (cpu_arch) {
693 .x86_64 => 6,
694 .aarch64 => 3 * @sizeOf(u32),
695 else => unreachable, // unhandled architecture type
696 };
697 macho_file.stubs_section_index = try macho_file.initSection("__TEXT", "__stubs", .{
698 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
699 .reserved2 = stub_size,
700 });
701 }
702
703 if (macho_file.stub_helper_section_index == null) {
704 macho_file.stub_helper_section_index = try macho_file.initSection("__TEXT", "__stub_helper", .{
705 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
706 });
707 }
708
709 if (macho_file.data_const_segment_cmd_index == null) {
710 macho_file.data_const_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
711 try macho_file.segments.append(gpa, .{
712 .segname = MachO.makeStaticString("__DATA_CONST"),
713 .vmaddr = 0,
714 .vmsize = 0,
715 .fileoff = 0,
716 .filesize = 0,
717 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
718 .initprot = macho.PROT.READ | macho.PROT.WRITE,
719 .cmdsize = @sizeOf(macho.segment_command_64),
720 });
721 }
722
723 if (macho_file.got_section_index == null) {
724 macho_file.got_section_index = try macho_file.initSection("__DATA_CONST", "__got", .{
725 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
726 });
727 }
728
729 if (macho_file.data_segment_cmd_index == null) {
730 macho_file.data_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
731 try macho_file.segments.append(gpa, .{
732 .segname = MachO.makeStaticString("__DATA"),
733 .vmaddr = 0,
734 .vmsize = 0,
735 .fileoff = 0,
736 .filesize = 0,
737 .maxprot = macho.PROT.READ | macho.PROT.WRITE,
738 .initprot = macho.PROT.READ | macho.PROT.WRITE,
739 .cmdsize = @sizeOf(macho.segment_command_64),
740 });
741 }
742
743 if (macho_file.la_symbol_ptr_section_index == null) {
744 macho_file.la_symbol_ptr_section_index = try macho_file.initSection("__DATA", "__la_symbol_ptr", .{
745 .flags = macho.S_LAZY_SYMBOL_POINTERS,
746 });
747 }
748
749 if (macho_file.data_section_index == null) {
750 macho_file.data_section_index = try macho_file.initSection("__DATA", "__data", .{});
751 }
752
753 if (macho_file.linkedit_segment_cmd_index == null) {
754 macho_file.linkedit_segment_cmd_index = @intCast(u8, macho_file.segments.items.len);
755 try macho_file.segments.append(gpa, .{
756 .segname = MachO.makeStaticString("__LINKEDIT"),
757 .vmaddr = 0,
758 .fileoff = 0,
759 .maxprot = macho.PROT.READ,
760 .initprot = macho.PROT.READ,
761 .cmdsize = @sizeOf(macho.segment_command_64),
762 });
763 }
764}
765
766fn writeAtoms(macho_file: *MachO) !void {
767 assert(macho_file.mode == .one_shot);
768
769 const gpa = macho_file.base.allocator;
770 const slice = macho_file.sections.slice();
771
772 for (slice.items(.last_atom)) |last_atom, sect_id| {
773 const header = slice.items(.header)[sect_id];
774 if (header.size == 0) continue;
775 var atom = last_atom.?;
776
777 if (header.isZerofill()) continue;
778
779 var buffer = std.ArrayList(u8).init(gpa);
780 defer buffer.deinit();
781 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);
782
783 log.debug("writing atoms in {s},{s}", .{ header.segName(), header.sectName() });
784
785 while (atom.prev) |prev| {
786 atom = prev;
787 }
788
789 while (true) {
790 const this_sym = atom.getSymbol(macho_file);
791 const padding_size: usize = if (atom.next) |next| blk: {
792 const next_sym = next.getSymbol(macho_file);
793 const size = next_sym.n_value - (this_sym.n_value + atom.size);
794 break :blk math.cast(usize, size) orelse return error.Overflow;
795 } else 0;
796
797 log.debug(" (adding ATOM(%{d}, '{s}') from object({?d}) to buffer)", .{
798 atom.sym_index,
799 atom.getName(macho_file),
800 atom.file,
801 });
802 if (padding_size > 0) {
803 log.debug(" (with padding {x})", .{padding_size});
804 }
805
806 try atom.resolveRelocs(macho_file);
807 buffer.appendSliceAssumeCapacity(atom.code.items);
808
809 var i: usize = 0;
810 while (i < padding_size) : (i += 1) {
811 // TODO with NOPs
812 buffer.appendAssumeCapacity(0);
813 }
814
815 if (atom.next) |next| {
816 atom = next;
817 } else {
818 assert(buffer.items.len == header.size);
819 log.debug(" (writing at file offset 0x{x})", .{header.offset});
820 try macho_file.base.file.?.pwriteAll(buffer.items, header.offset);
821 break;
822 }
823 }
824 }
825}
826
827fn allocateSegments(macho_file: *MachO) !void {
828 try allocateSegment(macho_file, macho_file.text_segment_cmd_index, &.{
829 macho_file.pagezero_segment_cmd_index,
830 }, try macho_file.calcMinHeaderPad());
831
832 if (macho_file.text_segment_cmd_index) |index| blk: {
833 const indexes = macho_file.getSectionIndexes(index);
834 if (indexes.start == indexes.end) break :blk;
835 const seg = macho_file.segments.items[index];
836
837 // Shift all sections to the back to minimize jump size between __TEXT and __DATA segments.
838 var min_alignment: u32 = 0;
839 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
840 const alignment = try math.powi(u32, 2, header.@"align");
841 min_alignment = math.max(min_alignment, alignment);
842 }
843
844 assert(min_alignment > 0);
845 const last_header = macho_file.sections.items(.header)[indexes.end - 1];
846 const shift: u32 = shift: {
847 const diff = seg.filesize - last_header.offset - last_header.size;
848 const factor = @divTrunc(diff, min_alignment);
849 break :shift @intCast(u32, factor * min_alignment);
850 };
851
852 if (shift > 0) {
853 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |*header| {
854 header.offset += shift;
855 header.addr += shift;
856 }
857 }
858 }
859
860 try allocateSegment(macho_file, macho_file.data_const_segment_cmd_index, &.{
861 macho_file.text_segment_cmd_index,
862 macho_file.pagezero_segment_cmd_index,
863 }, 0);
864
865 try allocateSegment(macho_file, macho_file.data_segment_cmd_index, &.{
866 macho_file.data_const_segment_cmd_index,
867 macho_file.text_segment_cmd_index,
868 macho_file.pagezero_segment_cmd_index,
869 }, 0);
870
871 try allocateSegment(macho_file, macho_file.linkedit_segment_cmd_index, &.{
872 macho_file.data_segment_cmd_index,
873 macho_file.data_const_segment_cmd_index,
874 macho_file.text_segment_cmd_index,
875 macho_file.pagezero_segment_cmd_index,
876 }, 0);
877}
878
879fn getSegmentAllocBase(macho_file: *MachO, indices: []const ?u8) struct { vmaddr: u64, fileoff: u64 } {
880 for (indices) |maybe_prev_id| {
881 const prev_id = maybe_prev_id orelse continue;
882 const prev = macho_file.segments.items[prev_id];
883 return .{
884 .vmaddr = prev.vmaddr + prev.vmsize,
885 .fileoff = prev.fileoff + prev.filesize,
886 };
887 }
888 return .{ .vmaddr = 0, .fileoff = 0 };
889}
890
891fn allocateSegment(macho_file: *MachO, maybe_index: ?u8, indices: []const ?u8, init_size: u64) !void {
892 const index = maybe_index orelse return;
893 const seg = &macho_file.segments.items[index];
894
895 const base = getSegmentAllocBase(macho_file, indices);
896 seg.vmaddr = base.vmaddr;
897 seg.fileoff = base.fileoff;
898 seg.filesize = init_size;
899 seg.vmsize = init_size;
900
901 // Allocate the sections according to their alignment at the beginning of the segment.
902 const indexes = macho_file.getSectionIndexes(index);
903 var start = init_size;
904 const slice = macho_file.sections.slice();
905 for (slice.items(.header)[indexes.start..indexes.end]) |*header| {
906 const alignment = try math.powi(u32, 2, header.@"align");
907 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
908
909 header.offset = if (header.isZerofill())
910 0
911 else
912 @intCast(u32, seg.fileoff + start_aligned);
913 header.addr = seg.vmaddr + start_aligned;
914
915 start = start_aligned + header.size;
916
917 if (!header.isZerofill()) {
918 seg.filesize = start;
919 }
920 seg.vmsize = start;
921 }
922
923 seg.filesize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
924 seg.vmsize = mem.alignForwardGeneric(u64, seg.vmsize, macho_file.page_size);
925}
926
927fn allocateSymbols(macho_file: *MachO) !void {
928 const slice = macho_file.sections.slice();
929 for (slice.items(.last_atom)) |last_atom, sect_id| {
930 const header = slice.items(.header)[sect_id];
931 var atom = last_atom orelse continue;
932
933 while (atom.prev) |prev| {
934 atom = prev;
935 }
936
937 const n_sect = @intCast(u8, sect_id + 1);
938 var base_vaddr = header.addr;
939
940 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{
941 n_sect,
942 header.segName(),
943 header.sectName(),
944 });
945
946 while (true) {
947 const alignment = try math.powi(u32, 2, atom.alignment);
948 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
949
950 const sym = atom.getSymbolPtr(macho_file);
951 sym.n_value = base_vaddr;
952 sym.n_sect = n_sect;
953
954 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(macho_file), base_vaddr });
955
956 // Update each symbol contained within the atom
957 for (atom.contained.items) |sym_at_off| {
958 const contained_sym = macho_file.getSymbolPtr(.{
959 .sym_index = sym_at_off.sym_index,
960 .file = atom.file,
961 });
962 contained_sym.n_value = base_vaddr + sym_at_off.offset;
963 contained_sym.n_sect = n_sect;
964 }
965
966 base_vaddr += atom.size;
967
968 if (atom.next) |next| {
969 atom = next;
970 } else break;
971 }
972 }
973}
974
975fn writeLinkeditSegmentData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
976 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
977 seg.filesize = 0;
978 seg.vmsize = 0;
979
980 try writeDyldInfoData(macho_file, ncmds, lc_writer);
981 try writeFunctionStarts(macho_file, ncmds, lc_writer);
982 try writeDataInCode(macho_file, ncmds, lc_writer);
983 try writeSymtabs(macho_file, ncmds, lc_writer);
984
985 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
986}
987
988fn writeDyldInfoData(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
989 const tracy = trace(@src());
990 defer tracy.end();
991
992 const gpa = macho_file.base.allocator;
993
994 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
995 defer rebase_pointers.deinit();
996 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
997 defer bind_pointers.deinit();
998 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
999 defer lazy_bind_pointers.deinit();
1000
1001 const slice = macho_file.sections.slice();
1002 for (slice.items(.last_atom)) |last_atom, sect_id| {
1003 var atom = last_atom orelse continue;
1004 const segment_index = slice.items(.segment_index)[sect_id];
1005 const header = slice.items(.header)[sect_id];
1006
1007 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable
1008
1009 log.debug("dyld info for {s},{s}", .{ header.segName(), header.sectName() });
1010
1011 const seg = macho_file.segments.items[segment_index];
1012
1013 while (true) {
1014 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(macho_file) });
1015 const sym = atom.getSymbol(macho_file);
1016 const base_offset = sym.n_value - seg.vmaddr;
1017
1018 for (atom.rebases.items) |offset| {
1019 log.debug(" | rebase at {x}", .{base_offset + offset});
1020 try rebase_pointers.append(.{
1021 .offset = base_offset + offset,
1022 .segment_id = segment_index,
1023 });
1024 }
1025
1026 for (atom.bindings.items) |binding| {
1027 const bind_sym = macho_file.getSymbol(binding.target);
1028 const bind_sym_name = macho_file.getSymbolName(binding.target);
1029 const dylib_ordinal = @divTrunc(
1030 @bitCast(i16, bind_sym.n_desc),
1031 macho.N_SYMBOL_RESOLVER,
1032 );
1033 var flags: u4 = 0;
1034 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
1035 binding.offset + base_offset,
1036 bind_sym_name,
1037 dylib_ordinal,
1038 });
1039 if (bind_sym.weakRef()) {
1040 log.debug(" | marking as weak ref ", .{});
1041 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1042 }
1043 try bind_pointers.append(.{
1044 .offset = binding.offset + base_offset,
1045 .segment_id = segment_index,
1046 .dylib_ordinal = dylib_ordinal,
1047 .name = bind_sym_name,
1048 .bind_flags = flags,
1049 });
1050 }
1051
1052 for (atom.lazy_bindings.items) |binding| {
1053 const bind_sym = macho_file.getSymbol(binding.target);
1054 const bind_sym_name = macho_file.getSymbolName(binding.target);
1055 const dylib_ordinal = @divTrunc(
1056 @bitCast(i16, bind_sym.n_desc),
1057 macho.N_SYMBOL_RESOLVER,
1058 );
1059 var flags: u4 = 0;
1060 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
1061 binding.offset + base_offset,
1062 bind_sym_name,
1063 dylib_ordinal,
1064 });
1065 if (bind_sym.weakRef()) {
1066 log.debug(" | marking as weak ref ", .{});
1067 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
1068 }
1069 try lazy_bind_pointers.append(.{
1070 .offset = binding.offset + base_offset,
1071 .segment_id = segment_index,
1072 .dylib_ordinal = dylib_ordinal,
1073 .name = bind_sym_name,
1074 .bind_flags = flags,
1075 });
1076 }
1077
1078 if (atom.prev) |prev| {
1079 atom = prev;
1080 } else break;
1081 }
1082 }
1083
1084 var trie: Trie = .{};
1085 defer trie.deinit(gpa);
1086
1087 {
1088 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
1089 log.debug("generating export trie", .{});
1090
1091 const text_segment = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1092 const base_address = text_segment.vmaddr;
1093
1094 if (macho_file.base.options.output_mode == .Exe) {
1095 for (&[_]SymbolWithLoc{
1096 try macho_file.getEntryPoint(),
1097 macho_file.getGlobal("__mh_execute_header").?,
1098 }) |global| {
1099 const sym = macho_file.getSymbol(global);
1100 const sym_name = macho_file.getSymbolName(global);
1101 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1102 try trie.put(gpa, .{
1103 .name = sym_name,
1104 .vmaddr_offset = sym.n_value - base_address,
1105 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1106 });
1107 }
1108 } else {
1109 assert(macho_file.base.options.output_mode == .Lib);
1110 for (macho_file.globals.items) |global| {
1111 const sym = macho_file.getSymbol(global);
1112
1113 if (sym.undf()) continue;
1114 if (!sym.ext()) continue;
1115 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1116
1117 const sym_name = macho_file.getSymbolName(global);
1118 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
1119 try trie.put(gpa, .{
1120 .name = sym_name,
1121 .vmaddr_offset = sym.n_value - base_address,
1122 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
1123 });
1124 }
1125 }
1126
1127 try trie.finalize(gpa);
1128 }
1129
1130 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1131 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
1132 assert(rebase_off == link_seg.fileoff);
1133 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
1134 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ rebase_off, rebase_off + rebase_size });
1135
1136 const bind_off = mem.alignForwardGeneric(u64, rebase_off + rebase_size, @alignOf(u64));
1137 const bind_size = try bind.bindInfoSize(bind_pointers.items);
1138 log.debug("writing bind info from 0x{x} to 0x{x}", .{ bind_off, bind_off + bind_size });
1139
1140 const lazy_bind_off = mem.alignForwardGeneric(u64, bind_off + bind_size, @alignOf(u64));
1141 const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items);
1142 log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + lazy_bind_size });
1143
1144 const export_off = mem.alignForwardGeneric(u64, lazy_bind_off + lazy_bind_size, @alignOf(u64));
1145 const export_size = trie.size;
1146 log.debug("writing export trie from 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
1147
1148 const needed_size = export_off + export_size - rebase_off;
1149 link_seg.filesize = needed_size;
1150
1151 var buffer = try gpa.alloc(u8, math.cast(usize, needed_size) orelse return error.Overflow);
1152 defer gpa.free(buffer);
1153 mem.set(u8, buffer, 0);
1154
1155 var stream = std.io.fixedBufferStream(buffer);
1156 const writer = stream.writer();
1157
1158 try bind.writeRebaseInfo(rebase_pointers.items, writer);
1159 try stream.seekTo(bind_off - rebase_off);
1160
1161 try bind.writeBindInfo(bind_pointers.items, writer);
1162 try stream.seekTo(lazy_bind_off - rebase_off);
1163
1164 try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer);
1165 try stream.seekTo(export_off - rebase_off);
1166
1167 _ = try trie.write(writer);
1168
1169 log.debug("writing dyld info from 0x{x} to 0x{x}", .{
1170 rebase_off,
1171 rebase_off + needed_size,
1172 });
1173
1174 try macho_file.base.file.?.pwriteAll(buffer, rebase_off);
1175 const start = math.cast(usize, lazy_bind_off - rebase_off) orelse return error.Overflow;
1176 const end = start + (math.cast(usize, lazy_bind_size) orelse return error.Overflow);
1177 try populateLazyBindOffsetsInStubHelper(macho_file, buffer[start..end]);
1178
1179 try lc_writer.writeStruct(macho.dyld_info_command{
1180 .cmd = .DYLD_INFO_ONLY,
1181 .cmdsize = @sizeOf(macho.dyld_info_command),
1182 .rebase_off = @intCast(u32, rebase_off),
1183 .rebase_size = @intCast(u32, rebase_size),
1184 .bind_off = @intCast(u32, bind_off),
1185 .bind_size = @intCast(u32, bind_size),
1186 .weak_bind_off = 0,
1187 .weak_bind_size = 0,
1188 .lazy_bind_off = @intCast(u32, lazy_bind_off),
1189 .lazy_bind_size = @intCast(u32, lazy_bind_size),
1190 .export_off = @intCast(u32, export_off),
1191 .export_size = @intCast(u32, export_size),
1192 });
1193 ncmds.* += 1;
1194}
1195
1196fn populateLazyBindOffsetsInStubHelper(macho_file: *MachO, buffer: []const u8) !void {
1197 const gpa = macho_file.base.allocator;
1198
1199 const stub_helper_section_index = macho_file.stub_helper_section_index orelse return;
1200 if (macho_file.stub_helper_preamble_atom == null) return;
1201
1202 const section = macho_file.sections.get(stub_helper_section_index);
1203 const last_atom = section.last_atom orelse return;
1204 if (last_atom == macho_file.stub_helper_preamble_atom.?) return; // TODO is this a redundant check?
1205
1206 var table = std.AutoHashMap(i64, *Atom).init(gpa);
1207 defer table.deinit();
1208
1209 {
1210 var stub_atom = last_atom;
1211 var laptr_atom = macho_file.sections.items(.last_atom)[macho_file.la_symbol_ptr_section_index.?].?;
1212 const base_addr = blk: {
1213 const seg = macho_file.segments.items[macho_file.data_segment_cmd_index.?];
1214 break :blk seg.vmaddr;
1215 };
1216
1217 while (true) {
1218 const laptr_off = blk: {
1219 const sym = laptr_atom.getSymbol(macho_file);
1220 break :blk @intCast(i64, sym.n_value - base_addr);
1221 };
1222 try table.putNoClobber(laptr_off, stub_atom);
1223 if (laptr_atom.prev) |prev| {
1224 laptr_atom = prev;
1225 stub_atom = stub_atom.prev.?;
1226 } else break;
1227 }
1228 }
1229
1230 var stream = std.io.fixedBufferStream(buffer);
1231 var reader = stream.reader();
1232 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
1233 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
1234 defer offsets.deinit();
1235 var valid_block = false;
1236
1237 while (true) {
1238 const inst = reader.readByte() catch |err| switch (err) {
1239 error.EndOfStream => break,
1240 };
1241 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
1242
1243 switch (opcode) {
1244 macho.BIND_OPCODE_DO_BIND => {
1245 valid_block = true;
1246 },
1247 macho.BIND_OPCODE_DONE => {
1248 if (valid_block) {
1249 const offset = try stream.getPos();
1250 try offsets.append(.{ .sym_offset = undefined, .offset = @intCast(u32, offset) });
1251 }
1252 valid_block = false;
1253 },
1254 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
1255 var next = try reader.readByte();
1256 while (next != @as(u8, 0)) {
1257 next = try reader.readByte();
1258 }
1259 },
1260 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
1261 var inserted = offsets.pop();
1262 inserted.sym_offset = try std.leb.readILEB128(i64, reader);
1263 try offsets.append(inserted);
1264 },
1265 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
1266 _ = try std.leb.readULEB128(u64, reader);
1267 },
1268 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
1269 _ = try std.leb.readILEB128(i64, reader);
1270 },
1271 else => {},
1272 }
1273 }
1274
1275 const header = macho_file.sections.items(.header)[stub_helper_section_index];
1276 const stub_offset: u4 = switch (macho_file.base.options.target.cpu.arch) {
1277 .x86_64 => 1,
1278 .aarch64 => 2 * @sizeOf(u32),
1279 else => unreachable,
1280 };
1281 var buf: [@sizeOf(u32)]u8 = undefined;
1282 _ = offsets.pop();
1283
1284 while (offsets.popOrNull()) |bind_offset| {
1285 const atom = table.get(bind_offset.sym_offset).?;
1286 const sym = atom.getSymbol(macho_file);
1287 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
1288 mem.writeIntLittle(u32, &buf, bind_offset.offset);
1289 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
1290 bind_offset.offset,
1291 atom.getName(macho_file),
1292 file_offset,
1293 });
1294 try macho_file.base.file.?.pwriteAll(&buf, file_offset);
1295 }
1296}
1297
1298const asc_u64 = std.sort.asc(u64);
1299
1300fn writeFunctionStarts(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1301 const tracy = trace(@src());
1302 defer tracy.end();
1303
1304 const text_seg_index = macho_file.text_segment_cmd_index orelse return;
1305 const text_sect_index = macho_file.text_section_index orelse return;
1306 const text_seg = macho_file.segments.items[text_seg_index];
1307
1308 const gpa = macho_file.base.allocator;
1309
1310 // We need to sort by address first
1311 var addresses = std.ArrayList(u64).init(gpa);
1312 defer addresses.deinit();
1313 try addresses.ensureTotalCapacityPrecise(macho_file.globals.items.len);
1314
1315 for (macho_file.globals.items) |global| {
1316 const sym = macho_file.getSymbol(global);
1317 if (sym.undf()) continue;
1318 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1319 const sect_id = sym.n_sect - 1;
1320 if (sect_id != text_sect_index) continue;
1321
1322 addresses.appendAssumeCapacity(sym.n_value);
1323 }
1324
1325 std.sort.sort(u64, addresses.items, {}, asc_u64);
1326
1327 var offsets = std.ArrayList(u32).init(gpa);
1328 defer offsets.deinit();
1329 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
1330
1331 var last_off: u32 = 0;
1332 for (addresses.items) |addr| {
1333 const offset = @intCast(u32, addr - text_seg.vmaddr);
1334 const diff = offset - last_off;
1335
1336 if (diff == 0) continue;
1337
1338 offsets.appendAssumeCapacity(diff);
1339 last_off = offset;
1340 }
1341
1342 var buffer = std.ArrayList(u8).init(gpa);
1343 defer buffer.deinit();
1344
1345 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
1346 try buffer.ensureTotalCapacity(max_size);
1347
1348 for (offsets.items) |offset| {
1349 try std.leb.writeULEB128(buffer.writer(), offset);
1350 }
1351
1352 const link_seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1353 const offset = mem.alignForwardGeneric(u64, link_seg.fileoff + link_seg.filesize, @alignOf(u64));
1354 const needed_size = buffer.items.len;
1355 link_seg.filesize = offset + needed_size - link_seg.fileoff;
1356
1357 log.debug("writing function starts info from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1358
1359 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1360
1361 try lc_writer.writeStruct(macho.linkedit_data_command{
1362 .cmd = .FUNCTION_STARTS,
1363 .cmdsize = @sizeOf(macho.linkedit_data_command),
1364 .dataoff = @intCast(u32, offset),
1365 .datasize = @intCast(u32, needed_size),
1366 });
1367 ncmds.* += 1;
1368}
1369
1370fn filterDataInCode(
1371 dices: []align(1) const macho.data_in_code_entry,
1372 start_addr: u64,
1373 end_addr: u64,
1374) []align(1) const macho.data_in_code_entry {
1375 const Predicate = struct {
1376 addr: u64,
1377
1378 pub fn predicate(macho_file: @This(), dice: macho.data_in_code_entry) bool {
1379 return dice.offset >= macho_file.addr;
1380 }
1381 };
1382
1383 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
1384 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
1385
1386 return dices[start..end];
1387}
1388
1389fn writeDataInCode(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1390 const tracy = trace(@src());
1391 defer tracy.end();
1392
1393 var out_dice = std.ArrayList(macho.data_in_code_entry).init(macho_file.base.allocator);
1394 defer out_dice.deinit();
1395
1396 const text_sect_id = macho_file.text_section_index orelse return;
1397 const text_sect_header = macho_file.sections.items(.header)[text_sect_id];
1398
1399 for (macho_file.objects.items) |object| {
1400 const dice = object.parseDataInCode() orelse continue;
1401 try out_dice.ensureUnusedCapacity(dice.len);
1402
1403 for (object.managed_atoms.items) |atom| {
1404 const sym = atom.getSymbol(macho_file);
1405 if (sym.n_desc == MachO.N_DESC_GCED) continue;
1406
1407 const sect_id = sym.n_sect - 1;
1408 if (sect_id != macho_file.text_section_index.?) {
1409 continue;
1410 }
1411
1412 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
1413 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
1414 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
1415 const base = math.cast(u32, sym.n_value - text_sect_header.addr + text_sect_header.offset) orelse
1416 return error.Overflow;
1417
1418 for (filtered_dice) |single| {
1419 const offset = single.offset - source_addr + base;
1420 out_dice.appendAssumeCapacity(.{
1421 .offset = offset,
1422 .length = single.length,
1423 .kind = single.kind,
1424 });
1425 }
1426 }
1427 }
1428
1429 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1430 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1431 const needed_size = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
1432 seg.filesize = offset + needed_size - seg.fileoff;
1433
1434 log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1435
1436 try macho_file.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), offset);
1437 try lc_writer.writeStruct(macho.linkedit_data_command{
1438 .cmd = .DATA_IN_CODE,
1439 .cmdsize = @sizeOf(macho.linkedit_data_command),
1440 .dataoff = @intCast(u32, offset),
1441 .datasize = @intCast(u32, needed_size),
1442 });
1443 ncmds.* += 1;
1444}
1445
1446fn writeSymtabs(macho_file: *MachO, ncmds: *u32, lc_writer: anytype) !void {
1447 var symtab_cmd = macho.symtab_command{
1448 .cmdsize = @sizeOf(macho.symtab_command),
1449 .symoff = 0,
1450 .nsyms = 0,
1451 .stroff = 0,
1452 .strsize = 0,
1453 };
1454 var dysymtab_cmd = macho.dysymtab_command{
1455 .cmdsize = @sizeOf(macho.dysymtab_command),
1456 .ilocalsym = 0,
1457 .nlocalsym = 0,
1458 .iextdefsym = 0,
1459 .nextdefsym = 0,
1460 .iundefsym = 0,
1461 .nundefsym = 0,
1462 .tocoff = 0,
1463 .ntoc = 0,
1464 .modtaboff = 0,
1465 .nmodtab = 0,
1466 .extrefsymoff = 0,
1467 .nextrefsyms = 0,
1468 .indirectsymoff = 0,
1469 .nindirectsyms = 0,
1470 .extreloff = 0,
1471 .nextrel = 0,
1472 .locreloff = 0,
1473 .nlocrel = 0,
1474 };
1475 var ctx = try writeSymtab(macho_file, &symtab_cmd);
1476 defer ctx.imports_table.deinit();
1477 try writeDysymtab(macho_file, ctx, &dysymtab_cmd);
1478 try writeStrtab(macho_file, &symtab_cmd);
1479 try lc_writer.writeStruct(symtab_cmd);
1480 try lc_writer.writeStruct(dysymtab_cmd);
1481 ncmds.* += 2;
1482}
1483
1484fn writeSymtab(macho_file: *MachO, lc: *macho.symtab_command) !SymtabCtx {
1485 const gpa = macho_file.base.allocator;
1486
1487 var locals = std.ArrayList(macho.nlist_64).init(gpa);
1488 defer locals.deinit();
1489
1490 for (macho_file.locals.items) |sym, sym_id| {
1491 if (sym.n_strx == 0) continue; // no name, skip
1492 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1493 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
1494 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1495 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1496 try locals.append(sym);
1497 }
1498
1499 for (macho_file.objects.items) |object, object_id| {
1500 for (object.symtab.items) |sym, sym_id| {
1501 if (sym.n_strx == 0) continue; // no name, skip
1502 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1503 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
1504 if (macho_file.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
1505 if (macho_file.getGlobal(macho_file.getSymbolName(sym_loc)) != null) continue; // global symbol is either an export or import, skip
1506 var out_sym = sym;
1507 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(sym_loc));
1508 try locals.append(out_sym);
1509 }
1510
1511 if (!macho_file.base.options.strip) {
1512 try generateSymbolStabs(macho_file, object, &locals);
1513 }
1514 }
1515
1516 var exports = std.ArrayList(macho.nlist_64).init(gpa);
1517 defer exports.deinit();
1518
1519 for (macho_file.globals.items) |global| {
1520 const sym = macho_file.getSymbol(global);
1521 if (sym.undf()) continue; // import, skip
1522 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
1523 var out_sym = sym;
1524 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1525 try exports.append(out_sym);
1526 }
1527
1528 var imports = std.ArrayList(macho.nlist_64).init(gpa);
1529 defer imports.deinit();
1530
1531 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
1532
1533 for (macho_file.globals.items) |global| {
1534 const sym = macho_file.getSymbol(global);
1535 if (sym.n_strx == 0) continue; // no name, skip
1536 if (!sym.undf()) continue; // not an import, skip
1537 const new_index = @intCast(u32, imports.items.len);
1538 var out_sym = sym;
1539 out_sym.n_strx = try macho_file.strtab.insert(gpa, macho_file.getSymbolName(global));
1540 try imports.append(out_sym);
1541 try imports_table.putNoClobber(global, new_index);
1542 }
1543
1544 const nlocals = @intCast(u32, locals.items.len);
1545 const nexports = @intCast(u32, exports.items.len);
1546 const nimports = @intCast(u32, imports.items.len);
1547 const nsyms = nlocals + nexports + nimports;
1548
1549 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1550 const offset = mem.alignForwardGeneric(
1551 u64,
1552 seg.fileoff + seg.filesize,
1553 @alignOf(macho.nlist_64),
1554 );
1555 const needed_size = nsyms * @sizeOf(macho.nlist_64);
1556 seg.filesize = offset + needed_size - seg.fileoff;
1557
1558 var buffer = std.ArrayList(u8).init(gpa);
1559 defer buffer.deinit();
1560 try buffer.ensureTotalCapacityPrecise(needed_size);
1561 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
1562 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
1563 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
1564
1565 log.debug("writing symtab from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1566 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1567
1568 lc.symoff = @intCast(u32, offset);
1569 lc.nsyms = nsyms;
1570
1571 return SymtabCtx{
1572 .nlocalsym = nlocals,
1573 .nextdefsym = nexports,
1574 .nundefsym = nimports,
1575 .imports_table = imports_table,
1576 };
1577}
1578
1579fn writeStrtab(macho_file: *MachO, lc: *macho.symtab_command) !void {
1580 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1581 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1582 const needed_size = macho_file.strtab.buffer.items.len;
1583 seg.filesize = offset + needed_size - seg.fileoff;
1584
1585 log.debug("writing string table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1586
1587 try macho_file.base.file.?.pwriteAll(macho_file.strtab.buffer.items, offset);
1588
1589 lc.stroff = @intCast(u32, offset);
1590 lc.strsize = @intCast(u32, needed_size);
1591}
1592
1593pub fn generateSymbolStabs(
1594 macho_file: *MachO,
1595 object: Object,
1596 locals: *std.ArrayList(macho.nlist_64),
1597) !void {
1598 assert(!macho_file.base.options.strip);
1599
1600 log.debug("parsing debug info in '{s}'", .{object.name});
1601
1602 const gpa = macho_file.base.allocator;
1603 var debug_info = try object.parseDwarfInfo();
1604 defer debug_info.deinit(gpa);
1605 try dwarf.openDwarfDebugInfo(&debug_info, gpa);
1606
1607 // We assume there is only one CU.
1608 const compile_unit = debug_info.findCompileUnit(0x0) catch |err| switch (err) {
1609 error.MissingDebugInfo => {
1610 // TODO audit cases with missing debug info and audit our dwarf.zig module.
1611 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
1612 return;
1613 },
1614 else => |e| return e,
1615 };
1616
1617 const tu_name = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.name, debug_info.debug_str, compile_unit.*);
1618 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info, dwarf.AT.comp_dir, debug_info.debug_str, compile_unit.*);
1619
1620 // Open scope
1621 try locals.ensureUnusedCapacity(3);
1622 locals.appendAssumeCapacity(.{
1623 .n_strx = try macho_file.strtab.insert(gpa, tu_comp_dir),
1624 .n_type = macho.N_SO,
1625 .n_sect = 0,
1626 .n_desc = 0,
1627 .n_value = 0,
1628 });
1629 locals.appendAssumeCapacity(.{
1630 .n_strx = try macho_file.strtab.insert(gpa, tu_name),
1631 .n_type = macho.N_SO,
1632 .n_sect = 0,
1633 .n_desc = 0,
1634 .n_value = 0,
1635 });
1636 locals.appendAssumeCapacity(.{
1637 .n_strx = try macho_file.strtab.insert(gpa, object.name),
1638 .n_type = macho.N_OSO,
1639 .n_sect = 0,
1640 .n_desc = 1,
1641 .n_value = object.mtime,
1642 });
1643
1644 var stabs_buf: [4]macho.nlist_64 = undefined;
1645
1646 for (object.managed_atoms.items) |atom| {
1647 const stabs = try generateSymbolStabsForSymbol(
1648 macho_file,
1649 atom.getSymbolWithLoc(),
1650 debug_info,
1651 &stabs_buf,
1652 );
1653 try locals.appendSlice(stabs);
1654
1655 for (atom.contained.items) |sym_at_off| {
1656 const sym_loc = SymbolWithLoc{
1657 .sym_index = sym_at_off.sym_index,
1658 .file = atom.file,
1659 };
1660 const contained_stabs = try generateSymbolStabsForSymbol(
1661 macho_file,
1662 sym_loc,
1663 debug_info,
1664 &stabs_buf,
1665 );
1666 try locals.appendSlice(contained_stabs);
1667 }
1668 }
1669
1670 // Close scope
1671 try locals.append(.{
1672 .n_strx = 0,
1673 .n_type = macho.N_SO,
1674 .n_sect = 0,
1675 .n_desc = 0,
1676 .n_value = 0,
1677 });
1678}
1679
1680fn generateSymbolStabsForSymbol(
1681 macho_file: *MachO,
1682 sym_loc: SymbolWithLoc,
1683 debug_info: dwarf.DwarfInfo,
1684 buf: *[4]macho.nlist_64,
1685) ![]const macho.nlist_64 {
1686 const gpa = macho_file.base.allocator;
1687 const object = macho_file.objects.items[sym_loc.file.?];
1688 const sym = macho_file.getSymbol(sym_loc);
1689 const sym_name = macho_file.getSymbolName(sym_loc);
1690
1691 if (sym.n_strx == 0) return buf[0..0];
1692 if (sym.n_desc == MachO.N_DESC_GCED) return buf[0..0];
1693 if (macho_file.symbolIsTemp(sym_loc)) return buf[0..0];
1694
1695 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
1696 const size: ?u64 = size: {
1697 if (source_sym.tentative()) break :size null;
1698 for (debug_info.func_list.items) |func| {
1699 if (func.pc_range) |range| {
1700 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
1701 break :size range.end - range.start;
1702 }
1703 }
1704 }
1705 break :size null;
1706 };
1707
1708 if (size) |ss| {
1709 buf[0] = .{
1710 .n_strx = 0,
1711 .n_type = macho.N_BNSYM,
1712 .n_sect = sym.n_sect,
1713 .n_desc = 0,
1714 .n_value = sym.n_value,
1715 };
1716 buf[1] = .{
1717 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1718 .n_type = macho.N_FUN,
1719 .n_sect = sym.n_sect,
1720 .n_desc = 0,
1721 .n_value = sym.n_value,
1722 };
1723 buf[2] = .{
1724 .n_strx = 0,
1725 .n_type = macho.N_FUN,
1726 .n_sect = 0,
1727 .n_desc = 0,
1728 .n_value = ss,
1729 };
1730 buf[3] = .{
1731 .n_strx = 0,
1732 .n_type = macho.N_ENSYM,
1733 .n_sect = sym.n_sect,
1734 .n_desc = 0,
1735 .n_value = ss,
1736 };
1737 return buf;
1738 } else {
1739 buf[0] = .{
1740 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
1741 .n_type = macho.N_STSYM,
1742 .n_sect = sym.n_sect,
1743 .n_desc = 0,
1744 .n_value = sym.n_value,
1745 };
1746 return buf[0..1];
1747 }
1748}
1749
1750const SymtabCtx = struct {
1751 nlocalsym: u32,
1752 nextdefsym: u32,
1753 nundefsym: u32,
1754 imports_table: std.AutoHashMap(SymbolWithLoc, u32),
1755};
1756
1757fn writeDysymtab(macho_file: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !void {
1758 const gpa = macho_file.base.allocator;
1759 const nstubs = @intCast(u32, macho_file.stubs_table.count());
1760 const ngot_entries = @intCast(u32, macho_file.got_entries_table.count());
1761 const nindirectsyms = nstubs * 2 + ngot_entries;
1762 const iextdefsym = ctx.nlocalsym;
1763 const iundefsym = iextdefsym + ctx.nextdefsym;
1764
1765 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1766 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
1767 const needed_size = nindirectsyms * @sizeOf(u32);
1768 seg.filesize = offset + needed_size - seg.fileoff;
1769
1770 log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1771
1772 var buf = std.ArrayList(u8).init(gpa);
1773 defer buf.deinit();
1774 try buf.ensureTotalCapacity(needed_size);
1775 const writer = buf.writer();
1776
1777 if (macho_file.stubs_section_index) |sect_id| {
1778 const stubs = &macho_file.sections.items(.header)[sect_id];
1779 stubs.reserved1 = 0;
1780 for (macho_file.stubs.items) |entry| {
1781 if (entry.sym_index == 0) continue;
1782 const atom_sym = entry.getSymbol(macho_file);
1783 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1784 const target_sym = macho_file.getSymbol(entry.target);
1785 assert(target_sym.undf());
1786 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1787 }
1788 }
1789
1790 if (macho_file.got_section_index) |sect_id| {
1791 const got = &macho_file.sections.items(.header)[sect_id];
1792 got.reserved1 = nstubs;
1793 for (macho_file.got_entries.items) |entry| {
1794 if (entry.sym_index == 0) continue;
1795 const atom_sym = entry.getSymbol(macho_file);
1796 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1797 const target_sym = macho_file.getSymbol(entry.target);
1798 if (target_sym.undf()) {
1799 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1800 } else {
1801 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
1802 }
1803 }
1804 }
1805
1806 if (macho_file.la_symbol_ptr_section_index) |sect_id| {
1807 const la_symbol_ptr = &macho_file.sections.items(.header)[sect_id];
1808 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
1809 for (macho_file.stubs.items) |entry| {
1810 if (entry.sym_index == 0) continue;
1811 const atom_sym = entry.getSymbol(macho_file);
1812 if (atom_sym.n_desc == MachO.N_DESC_GCED) continue;
1813 const target_sym = macho_file.getSymbol(entry.target);
1814 assert(target_sym.undf());
1815 try writer.writeIntLittle(u32, iundefsym + ctx.imports_table.get(entry.target).?);
1816 }
1817 }
1818
1819 assert(buf.items.len == needed_size);
1820 try macho_file.base.file.?.pwriteAll(buf.items, offset);
1821
1822 lc.nlocalsym = ctx.nlocalsym;
1823 lc.iextdefsym = iextdefsym;
1824 lc.nextdefsym = ctx.nextdefsym;
1825 lc.iundefsym = iundefsym;
1826 lc.nundefsym = ctx.nundefsym;
1827 lc.indirectsymoff = @intCast(u32, offset);
1828 lc.nindirectsyms = nindirectsyms;
1829}
1830
1831fn writeCodeSignaturePadding(
1832 macho_file: *MachO,
1833 code_sig: *CodeSignature,
1834 ncmds: *u32,
1835 lc_writer: anytype,
1836) !u32 {
1837 const seg = &macho_file.segments.items[macho_file.linkedit_segment_cmd_index.?];
1838 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
1839 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
1840 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
1841 const needed_size = code_sig.estimateSize(offset);
1842 seg.filesize = offset + needed_size - seg.fileoff;
1843 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, macho_file.page_size);
1844 log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size });
1845 // Pad out the space. We need to do this to calculate valid hashes for everything in the file
1846 // except for code signature data.
1847 try macho_file.base.file.?.pwriteAll(&[_]u8{0}, offset + needed_size - 1);
1848
1849 try lc_writer.writeStruct(macho.linkedit_data_command{
1850 .cmd = .CODE_SIGNATURE,
1851 .cmdsize = @sizeOf(macho.linkedit_data_command),
1852 .dataoff = @intCast(u32, offset),
1853 .datasize = @intCast(u32, needed_size),
1854 });
1855 ncmds.* += 1;
1856
1857 return @intCast(u32, offset);
1858}
1859
1860fn writeCodeSignature(macho_file: *MachO, code_sig: *CodeSignature, offset: u32) !void {
1861 const seg = macho_file.segments.items[macho_file.text_segment_cmd_index.?];
1862
1863 var buffer = std.ArrayList(u8).init(macho_file.base.allocator);
1864 defer buffer.deinit();
1865 try buffer.ensureTotalCapacityPrecise(code_sig.size());
1866 try code_sig.writeAdhocSignature(macho_file.base.allocator, .{
1867 .file = macho_file.base.file.?,
1868 .exec_seg_base = seg.fileoff,
1869 .exec_seg_limit = seg.filesize,
1870 .file_size = offset,
1871 .output_mode = macho_file.base.options.output_mode,
1872 }, buffer.writer());
1873 assert(buffer.items.len == code_sig.size());
1874
1875 log.debug("writing code signature from 0x{x} to 0x{x}", .{
1876 offset,
1877 offset + buffer.items.len,
1878 });
1879
1880 try macho_file.base.file.?.pwriteAll(buffer.items, offset);
1881}
1882
1883fn writeSegmentHeaders(macho_file: *MachO, ncmds: *u32, writer: anytype) !void {
1884 for (macho_file.segments.items) |seg, i| {
1885 const indexes = macho_file.getSectionIndexes(@intCast(u8, i));
1886 var out_seg = seg;
1887 out_seg.cmdsize = @sizeOf(macho.segment_command_64);
1888 out_seg.nsects = 0;
1889
1890 // Update section headers count; any section with size of 0 is excluded
1891 // since it doesn't have any data in the final binary file.
1892 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1893 if (header.size == 0) continue;
1894 out_seg.cmdsize += @sizeOf(macho.section_64);
1895 out_seg.nsects += 1;
1896 }
1897
1898 if (out_seg.nsects == 0 and
1899 (mem.eql(u8, out_seg.segName(), "__DATA_CONST") or
1900 mem.eql(u8, out_seg.segName(), "__DATA"))) continue;
1901
1902 try writer.writeStruct(out_seg);
1903 for (macho_file.sections.items(.header)[indexes.start..indexes.end]) |header| {
1904 if (header.size == 0) continue;
1905 try writer.writeStruct(header);
1906 }
1907
1908 ncmds.* += 1;
1909 }
1910}
1911
1912/// Writes Mach-O file header.
1913fn writeHeader(macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void {
1914 var header: macho.mach_header_64 = .{};
1915 header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL;
1916
1917 switch (macho_file.base.options.target.cpu.arch) {
1918 .aarch64 => {
1919 header.cputype = macho.CPU_TYPE_ARM64;
1920 header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL;
1921 },
1922 .x86_64 => {
1923 header.cputype = macho.CPU_TYPE_X86_64;
1924 header.cpusubtype = macho.CPU_SUBTYPE_X86_64_ALL;
1925 },
1926 else => return error.UnsupportedCpuArchitecture,
1927 }
1928
1929 switch (macho_file.base.options.output_mode) {
1930 .Exe => {
1931 header.filetype = macho.MH_EXECUTE;
1932 },
1933 .Lib => {
1934 // By this point, it can only be a dylib.
1935 header.filetype = macho.MH_DYLIB;
1936 header.flags |= macho.MH_NO_REEXPORTED_DYLIBS;
1937 },
1938 else => unreachable,
1939 }
1940
1941 if (macho_file.getSectionByName("__DATA", "__thread_vars")) |sect_id| {
1942 if (macho_file.sections.items(.header)[sect_id].size > 0) {
1943 header.flags |= macho.MH_HAS_TLV_DESCRIPTORS;
1944 }
1945 }
1946
1947 header.ncmds = ncmds;
1948 header.sizeofcmds = sizeofcmds;
1949
1950 log.debug("writing Mach-O header {}", .{header});
1951
1952 try macho_file.base.file.?.pwriteAll(mem.asBytes(&header), 0);
1953}