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 {...@@ -798,6 +798,11 @@ pub const section_64 = extern struct {
798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;798 return tt == S_ZEROFILL or tt == S_GB_ZEROFILL or tt == S_THREAD_LOCAL_ZEROFILL;
799 }799 }
800800
801 pub fn isSymbolStubs(sect: section_64) bool {
802 const tt = sect.@"type"();
803 return tt == S_SYMBOL_STUBS;
804 }
805
801 pub fn isDebug(sect: section_64) bool {806 pub fn isDebug(sect: section_64) bool {
802 return sect.attrs() & S_ATTR_DEBUG != 0;807 return sect.attrs() & S_ATTR_DEBUG != 0;
803 }808 }
src/arch/aarch64/Emit.zig+12-15
...@@ -680,16 +680,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -680,16 +680,15 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
680 break :blk offset;680 break :blk offset;
681 };681 };
682 // Add relocation to the decl.682 // 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 }).?;
684 const target = macho_file.getGlobalByIndex(relocation.sym_index);684 const target = macho_file.getGlobalByIndex(relocation.sym_index);
685 try atom.relocs.append(emit.bin_file.allocator, .{685 try atom.addRelocation(macho_file, .{
686 .offset = offset,686 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
687 .target = target,687 .target = target,
688 .offset = offset,
688 .addend = 0,689 .addend = 0,
689 .subtractor = null,
690 .pcrel = true,690 .pcrel = true,
691 .length = 2,691 .length = 2,
692 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
693 });692 });
694 } else {693 } else {
695 return emit.fail("Implement call_extern for linking backends != MachO", .{});694 return emit.fail("Implement call_extern for linking backends != MachO", .{});
...@@ -872,8 +871,8 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -872,8 +871,8 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
872 Instruction.LoadStoreOffset.imm(0),871 Instruction.LoadStoreOffset.imm(0),
873 ));872 ));
874 },873 },
875 .load_memory_ptr_got,
876 .load_memory_ptr_direct,874 .load_memory_ptr_direct,
875 .load_memory_ptr_got,
877 => {876 => {
878 // add reg, reg, offset877 // add reg, reg, offset
879 try emit.writeInstruction(Instruction.add(reg, reg, 0, false));878 try emit.writeInstruction(Instruction.add(reg, reg, 0, false));
...@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -882,13 +881,13 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882 }881 }
883882
884 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {883 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
885 const atom = macho_file.atom_by_index_table.get(data.atom_index).?;884 const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
886 // Page reloc for adrp instruction.885 // TODO this causes segfault in stage1
887 try atom.relocs.append(emit.bin_file.allocator, .{886 // try atom.addRelocations(macho_file, 2, .{
888 .offset = offset,887 try atom.addRelocation(macho_file, .{
889 .target = .{ .sym_index = data.sym_index, .file = null },888 .target = .{ .sym_index = data.sym_index, .file = null },
889 .offset = offset,
890 .addend = 0,890 .addend = 0,
891 .subtractor = null,
892 .pcrel = true,891 .pcrel = true,
893 .length = 2,892 .length = 2,
894 .@"type" = switch (tag) {893 .@"type" = switch (tag) {
...@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -901,12 +900,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
901 else => unreachable,900 else => unreachable,
902 },901 },
903 });902 });
904 // Pageoff reloc for adrp instruction.903 try atom.addRelocation(macho_file, .{
905 try atom.relocs.append(emit.bin_file.allocator, .{
906 .offset = offset + 4,
907 .target = .{ .sym_index = data.sym_index, .file = null },904 .target = .{ .sym_index = data.sym_index, .file = null },
905 .offset = offset + 4,
908 .addend = 0,906 .addend = 0,
909 .subtractor = null,
910 .pcrel = false,907 .pcrel = false,
911 .length = 2,908 .length = 2,
912 .@"type" = switch (tag) {909 .@"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 {...@@ -996,7 +996,6 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
996 );996 );
997997
998 const end_offset = emit.code.items.len;998 const end_offset = emit.code.items.len;
999 const gpa = emit.bin_file.allocator;
1000999
1001 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1000 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1002 const reloc_type = switch (ops.flags) {1001 const reloc_type = switch (ops.flags) {
...@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1004,19 +1003,17 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1004 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1003 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1005 else => unreachable,1004 else => unreachable,
1006 };1005 };
1007 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;1006 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1008 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });1007 try atom.addRelocation(macho_file, .{
1009 try atom.relocs.append(gpa, .{1008 .@"type" = reloc_type,
1010 .offset = @intCast(u32, end_offset - 4),
1011 .target = .{ .sym_index = relocation.sym_index, .file = null },1009 .target = .{ .sym_index = relocation.sym_index, .file = null },
1010 .offset = @intCast(u32, end_offset - 4),
1012 .addend = 0,1011 .addend = 0,
1013 .subtractor = null,
1014 .pcrel = true,1012 .pcrel = true,
1015 .length = 2,1013 .length = 2,
1016 .@"type" = reloc_type,
1017 });1014 });
1018 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1015 } 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 }).?;
1020 try atom.addRelocation(coff_file, .{1017 try atom.addRelocation(coff_file, .{
1021 .@"type" = switch (ops.flags) {1018 .@"type" = switch (ops.flags) {
1022 0b00 => .got,1019 0b00 => .got,
...@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1145,20 +1142,19 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11451142
1146 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1143 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1147 // Add relocation to the decl.1144 // 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 }).?;
1149 const target = macho_file.getGlobalByIndex(relocation.sym_index);1146 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1150 try atom.relocs.append(emit.bin_file.allocator, .{1147 try atom.addRelocation(macho_file, .{
1151 .offset = offset,1148 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1152 .target = target,1149 .target = target,
1150 .offset = offset,
1153 .addend = 0,1151 .addend = 0,
1154 .subtractor = null,
1155 .pcrel = true,1152 .pcrel = true,
1156 .length = 2,1153 .length = 2,
1157 .@"type" = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1158 });1154 });
1159 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1155 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1160 // Add relocation to the decl.1156 // 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 }).?;
1162 const target = coff_file.getGlobalByIndex(relocation.sym_index);1158 const target = coff_file.getGlobalByIndex(relocation.sym_index);
1163 try atom.addRelocation(coff_file, .{1159 try atom.addRelocation(coff_file, .{
1164 .@"type" = .direct,1160 .@"type" = .direct,
src/link/Coff.zig+2-1
...@@ -1135,6 +1135,7 @@ fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {...@@ -1135,6 +1135,7 @@ fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
1135 }1135 }
11361136
1137 switch (zig_ty) {1137 switch (zig_ty) {
1138 // TODO: what if this is a function pointer?
1138 .Fn => break :blk self.text_section_index.?,1139 .Fn => break :blk self.text_section_index.?,
1139 else => {1140 else => {
1140 if (val.castTag(.variable)) |_| {1141 if (val.castTag(.variable)) |_| {
...@@ -1527,7 +1528,7 @@ pub fn getDeclVAddr(...@@ -1527,7 +1528,7 @@ pub fn getDeclVAddr(
1527 assert(self.llvm_object == null);1528 assert(self.llvm_object == null);
1528 assert(decl.link.coff.sym_index != 0);1529 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 }).?;
1531 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };1532 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
1532 try atom.addRelocation(self, .{1533 try atom.addRelocation(self, .{
1533 .@"type" = .direct,1534 .@"type" = .direct,
src/link/Dwarf.zig+15-2
...@@ -948,7 +948,7 @@ pub fn commitDeclState(...@@ -948,7 +948,7 @@ pub fn commitDeclState(
948 new_offset,948 new_offset,
949 });949 });
950950
951 try File.MachO.copyRangeAllOverlappingAlloc(951 try copyRangeAllOverlappingAlloc(
952 gpa,952 gpa,
953 d_sym.file,953 d_sym.file,
954 debug_line_sect.offset,954 debug_line_sect.offset,
...@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co...@@ -1247,7 +1247,7 @@ fn writeDeclDebugInfo(self: *Dwarf, file: *File, atom: *Atom, dbg_info_buf: []co
1247 new_offset,1247 new_offset,
1248 });1248 });
12491249
1250 try File.MachO.copyRangeAllOverlappingAlloc(1250 try copyRangeAllOverlappingAlloc(
1251 gpa,1251 gpa,
1252 d_sym.file,1252 d_sym.file,
1253 debug_info_sect.offset,1253 debug_info_sect.offset,
...@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(...@@ -2338,3 +2338,16 @@ fn addDbgInfoErrorSet(
2338 // DW.AT.enumeration_type delimit children2338 // DW.AT.enumeration_type delimit children
2339 try dbg_info_buffer.append(0);2339 try dbg_info_buffer.append(0);
2340}2340}
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 {...@@ -2320,6 +2320,7 @@ fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {
2320 }2320 }
23212321
2322 switch (zig_ty) {2322 switch (zig_ty) {
2323 // TODO: what if this is a function pointer?
2323 .Fn => break :blk self.phdr_load_re_index.?,2324 .Fn => break :blk self.phdr_load_re_index.?,
2324 else => {2325 else => {
2325 if (val.castTag(.variable)) |_| {2326 if (val.castTag(.variable)) |_| {
src/link/MachO.zig+1485-2603
...@@ -22,6 +22,7 @@ const link = @import("../link.zig");...@@ -22,6 +22,7 @@ const link = @import("../link.zig");
22const llvm_backend = @import("../codegen/llvm.zig");22const llvm_backend = @import("../codegen/llvm.zig");
23const target_util = @import("../target.zig");23const target_util = @import("../target.zig");
24const trace = @import("../tracy.zig").trace;24const trace = @import("../tracy.zig").trace;
25const zld = @import("MachO/zld.zig");
2526
26const Air = @import("../Air.zig");27const Air = @import("../Air.zig");
27const Allocator = mem.Allocator;28const Allocator = mem.Allocator;
...@@ -38,6 +39,7 @@ const LibStub = @import("tapi.zig").LibStub;...@@ -38,6 +39,7 @@ const LibStub = @import("tapi.zig").LibStub;
38const Liveness = @import("../Liveness.zig");39const Liveness = @import("../Liveness.zig");
39const LlvmObject = @import("../codegen/llvm.zig").Object;40const LlvmObject = @import("../codegen/llvm.zig").Object;
40const Module = @import("../Module.zig");41const Module = @import("../Module.zig");
42const Relocation = @import("MachO/Relocation.zig");
41const StringTable = @import("strtab.zig").StringTable;43const StringTable = @import("strtab.zig").StringTable;
42const Trie = @import("MachO/Trie.zig");44const Trie = @import("MachO/Trie.zig");
43const Type = @import("../type.zig").Type;45const Type = @import("../type.zig").Type;
...@@ -55,11 +57,6 @@ pub const SearchStrategy = enum {...@@ -55,11 +57,6 @@ pub const SearchStrategy = enum {
5557
56pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));58pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
5759
58const SystemLib = struct {
59 needed: bool = false,
60 weak: bool = false,
61};
62
63const Section = struct {60const Section = struct {
64 header: macho.section_64,61 header: macho.section_64,
65 segment_index: u8,62 segment_index: u8,
...@@ -118,6 +115,7 @@ segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},...@@ -118,6 +115,7 @@ segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
118sections: std.MultiArrayList(Section) = .{},115sections: std.MultiArrayList(Section) = .{},
119116
120pagezero_segment_cmd_index: ?u8 = null,117pagezero_segment_cmd_index: ?u8 = null,
118header_segment_cmd_index: ?u8 = null,
121text_segment_cmd_index: ?u8 = null,119text_segment_cmd_index: ?u8 = null,
122data_const_segment_cmd_index: ?u8 = null,120data_const_segment_cmd_index: ?u8 = null,
123data_segment_cmd_index: ?u8 = null,121data_segment_cmd_index: ?u8 = null,
...@@ -127,6 +125,7 @@ text_section_index: ?u8 = null,...@@ -127,6 +125,7 @@ text_section_index: ?u8 = null,
127stubs_section_index: ?u8 = null,125stubs_section_index: ?u8 = null,
128stub_helper_section_index: ?u8 = null,126stub_helper_section_index: ?u8 = null,
129got_section_index: ?u8 = null,127got_section_index: ?u8 = null,
128data_const_section_index: ?u8 = null,
130la_symbol_ptr_section_index: ?u8 = null,129la_symbol_ptr_section_index: ?u8 = null,
131data_section_index: ?u8 = null,130data_section_index: ?u8 = null,
132131
...@@ -160,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},...@@ -160,6 +159,8 @@ stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
160159
161error_flags: File.ErrorFlags = File.ErrorFlags{},160error_flags: File.ErrorFlags = File.ErrorFlags{},
162161
162segment_table_dirty: bool = false,
163
163/// A helper var to indicate if we are at the start of the incremental updates, or164/// A helper var to indicate if we are at the start of the incremental updates, or
164/// already somewhere further along the update-and-run chain.165/// already somewhere further along the update-and-run chain.
165/// TODO once we add opening a prelinked output binary from file, this will become166/// 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) = .{},...@@ -193,6 +194,26 @@ atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
193/// with `Decl` `main`, and lives as long as that `Decl`.194/// with `Decl` `main`, and lives as long as that `Decl`.
194unnamed_const_atoms: UnnamedConstTable = .{},195unnamed_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
196/// Table of Decls that are currently alive.217/// Table of Decls that are currently alive.
197/// We store them here so that we can properly dispose of any allocated218/// We store them here so that we can properly dispose of any allocated
198/// memory within the atom in the incremental linker.219/// memory within the atom in the incremental linker.
...@@ -212,8 +233,8 @@ const Entry = struct {...@@ -212,8 +233,8 @@ const Entry = struct {
212 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });233 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
213 }234 }
214235
215 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {236 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {
216 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;237 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });
217 }238 }
218239
219 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {240 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
...@@ -221,7 +242,10 @@ const Entry = struct {...@@ -221,7 +242,10 @@ const Entry = struct {
221 }242 }
222};243};
223244
245const BindingTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));
224const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));246const 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
226const PendingUpdate = union(enum) {250const PendingUpdate = union(enum) {
227 resolve_undef: u32,251 resolve_undef: u32,
...@@ -235,11 +259,21 @@ pub const SymbolWithLoc = struct {...@@ -235,11 +259,21 @@ pub const SymbolWithLoc = struct {
235259
236 // null means it's a synthetic global.260 // null means it's a synthetic global.
237 file: ?u32 = null,261 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 }
238};272};
239273
240/// When allocating, the ideal_capacity is calculated by274/// When allocating, the ideal_capacity is calculated by
241/// actual_capacity + (actual_capacity / ideal_factor)275/// actual_capacity + (actual_capacity / ideal_factor)
242const ideal_factor = 4;276const ideal_factor = 3;
243277
244/// Default path to dyld278/// Default path to dyld
245const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";279const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
...@@ -301,13 +335,9 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -301,13 +335,9 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
301 errdefer file.close();335 errdefer file.close();
302 self.base.file = file;336 self.base.file = file;
303337
304 if (!options.strip and options.module != null) blk: {338 if (self.mode == .one_shot) return self;
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;
310339
340 if (!options.strip and options.module != null) {
311 // Create dSYM bundle.341 // Create dSYM bundle.
312 const dir = options.module.?.zig_cache_artifact_directory;342 const dir = options.module.?.zig_cache_artifact_directory;
313 log.debug("creating {s}.dSYM bundle in {?s}", .{ emit.sub_path, dir.path });343 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...@@ -405,7 +435,7 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
405 }435 }
406436
407 switch (self.mode) {437 switch (self.mode) {
408 .one_shot => return self.linkOneShot(comp, prog_node),438 .one_shot => return zld.linkWithZld(self, comp, prog_node),
409 .incremental => return self.flushModule(comp, prog_node),439 .incremental => return self.flushModule(comp, prog_node),
410 }440 }
411}441}
...@@ -434,10 +464,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -434,10 +464,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
434 try d_sym.dwarf.flushModule(&self.base, module);464 try d_sym.dwarf.flushModule(&self.base, module);
435 }465 }
436466
437 var libs = std.StringArrayHashMap(SystemLib).init(arena);467 var libs = std.StringArrayHashMap(link.SystemLib).init(arena);
438 try self.resolveLibSystem(arena, comp, &.{}, &libs);468 try self.resolveLibSystem(arena, comp, &.{}, &libs);
439469
440 const id_symlink_basename = "zld.id";470 const id_symlink_basename = "link.id";
441471
442 const cache_dir_handle = module.zig_cache_artifact_directory.handle;472 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
443 var man: Cache.Manifest = undefined;473 var man: Cache.Manifest = undefined;
...@@ -518,14 +548,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -518,14 +548,19 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
518548
519 try self.allocateSpecialSymbols();549 try self.allocateSpecialSymbols();
520550
551 {
552 var it = self.relocs.keyIterator();
553 while (it.next()) |atom| {
554 try atom.*.resolveRelocations(self);
555 }
556 }
557
521 if (build_options.enable_logging) {558 if (build_options.enable_logging) {
522 self.logSymtab();559 self.logSymtab();
523 self.logSections();560 self.logSections();
524 self.logAtoms();561 self.logAtoms();
525 }562 }
526563
527 try self.writeAtomsIncremental();
528
529 var lc_buffer = std.ArrayList(u8).init(arena);564 var lc_buffer = std.ArrayList(u8).init(arena);
530 const lc_writer = lc_buffer.writer();565 const lc_writer = lc_buffer.writer();
531 var ncmds: u32 = 0;566 var ncmds: u32 = 0;
...@@ -624,635 +659,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -624,635 +659,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
624 self.cold_start = false;659 self.cold_start = false;
625}660}
626661
627fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {662pub fn resolveLibSystem(
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(
1256 self: *MachO,663 self: *MachO,
1257 arena: Allocator,664 arena: Allocator,
1258 comp: *Compilation,665 comp: *Compilation,
...@@ -1295,7 +702,7 @@ fn resolveLibSystem(...@@ -1295,7 +702,7 @@ fn resolveLibSystem(
1295 }702 }
1296}703}
1297704
1298fn resolveSearchDir(705pub fn resolveSearchDir(
1299 arena: Allocator,706 arena: Allocator,
1300 dir: []const u8,707 dir: []const u8,
1301 syslibroot: ?[]const u8,708 syslibroot: ?[]const u8,
...@@ -1337,17 +744,7 @@ fn resolveSearchDir(...@@ -1337,17 +744,7 @@ fn resolveSearchDir(
1337 return null;744 return null;
1338}745}
1339746
1340fn resolveSearchDirs(arena: Allocator, dirs: []const []const u8, syslibroot: ?[]const u8, out_dirs: anytype) !void {747pub fn resolveLib(
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(
1351 arena: Allocator,748 arena: Allocator,
1352 search_dir: []const u8,749 search_dir: []const u8,
1353 name: []const u8,750 name: []const u8,
...@@ -1366,7 +763,7 @@ fn resolveLib(...@@ -1366,7 +763,7 @@ fn resolveLib(
1366 return full_path;763 return full_path;
1367}764}
1368765
1369fn resolveFramework(766pub fn resolveFramework(
1370 arena: Allocator,767 arena: Allocator,
1371 search_dir: []const u8,768 search_dir: []const u8,
1372 name: []const u8,769 name: []const u8,
...@@ -1576,7 +973,7 @@ pub fn parseDylib(...@@ -1576,7 +973,7 @@ pub fn parseDylib(
1576 return true;973 return true;
1577}974}
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 {
1580 for (files) |file_name| {977 for (files) |file_name| {
1581 const full_path = full_path: {978 const full_path = full_path: {
1582 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;979 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1594,7 +991,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1594,7 +991,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1594 }991 }
1595}992}
1596993
1597fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {994pub fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !void {
1598 for (files) |file_name| {995 for (files) |file_name| {
1599 const full_path = full_path: {996 const full_path = full_path: {
1600 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;997 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1607,10 +1004,10 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1607,10 +1004,10 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1607 }1004 }
1608}1005}
16091006
1610fn parseLibs(1007pub fn parseLibs(
1611 self: *MachO,1008 self: *MachO,
1612 lib_names: []const []const u8,1009 lib_names: []const []const u8,
1613 lib_infos: []const SystemLib,1010 lib_infos: []const link.SystemLib,
1614 syslibroot: ?[]const u8,1011 syslibroot: ?[]const u8,
1615 dependent_libs: anytype,1012 dependent_libs: anytype,
1616) !void {1013) !void {
...@@ -1628,7 +1025,7 @@ fn parseLibs(...@@ -1628,7 +1025,7 @@ fn parseLibs(
1628 }1025 }
1629}1026}
16301027
1631fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {1028pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1632 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:1029 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
1633 // 1) anything on the linker line is parsed first1030 // 1) anything on the linker line is parsed first
1634 // 2) afterwards, we parse dependents of the included dylibs1031 // 2) afterwards, we parse dependents of the included dylibs
...@@ -1673,168 +1070,6 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1673,168 +1070,6 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1673 }1070 }
1674}1071}
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
1838pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {1073pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
1839 const size_usize = math.cast(usize, size) orelse return error.Overflow;1074 const size_usize = math.cast(usize, size) orelse return error.Overflow;
1840 const atom = try gpa.create(Atom);1075 const atom = try gpa.create(Atom);
...@@ -1850,64 +1085,51 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32...@@ -1850,64 +1085,51 @@ pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32
1850 return atom;1085 return atom;
1851}1086}
18521087
1853pub fn writeAtom(self: *MachO, atom: *Atom, sect_id: u8) !void {1088pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void {
1854 const section = self.sections.get(sect_id);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
1855 const sym = atom.getSymbol(self);1096 const sym = atom.getSymbol(self);
1097 const section = self.sections.get(sym.n_sect - 1);
1856 const file_offset = section.header.offset + sym.n_value - section.header.addr;1098 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1857 try atom.resolveRelocs(self);
1858 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });1099 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);
1860}1102}
18611103
1862fn allocateSymbols(self: *MachO) !void {1104fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {
1863 const slice = self.sections.slice();1105 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1864 for (slice.items(.last_atom)) |last_atom, sect_id| {1106 try self.writeAtom(atom, &buffer);
1865 const header = slice.items(.header)[sect_id];1107}
1866 var atom = last_atom orelse continue;
18671108
1868 while (atom.prev) |prev| {1109fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1869 atom = prev;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;
1870 }1116 }
1117 }
1118}
18711119
1872 const n_sect = @intCast(u8, sect_id + 1);1120fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1873 var base_vaddr = header.addr;1121 var it = self.relocs.valueIterator();
18741122 while (it.next()) |relocs| {
1875 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{1123 for (relocs.items) |*reloc| {
1876 n_sect,1124 const target_atom = reloc.getTargetAtom(self) orelse continue;
1877 header.segName(),1125 const target_sym = target_atom.getSymbol(self);
1878 header.sectName(),1126 if (target_sym.n_value < addr) continue;
1879 });1127 reloc.dirty = true;
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;
1906 }1128 }
1907 }1129 }
1908}1130}
19091131
1910fn allocateSpecialSymbols(self: *MachO) !void {1132pub fn allocateSpecialSymbols(self: *MachO) !void {
1911 for (&[_][]const u8{1133 for (&[_][]const u8{
1912 "___dso_handle",1134 "___dso_handle",
1913 "__mh_execute_header",1135 "__mh_execute_header",
...@@ -1915,7 +1137,10 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1915,7 +1137,10 @@ fn allocateSpecialSymbols(self: *MachO) !void {
1915 const global = self.getGlobal(name) orelse continue;1137 const global = self.getGlobal(name) orelse continue;
1916 if (global.file != null) continue;1138 if (global.file != null) continue;
1917 const sym = self.getSymbolPtr(global);1139 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 };
1919 sym.n_sect = 1;1144 sym.n_sect = 1;
1920 sym.n_value = seg.vmaddr;1145 sym.n_value = seg.vmaddr;
19211146
...@@ -1926,214 +1151,133 @@ fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1926,214 +1151,133 @@ fn allocateSpecialSymbols(self: *MachO) !void {
1926 }1151 }
1927}1152}
19281153
1929fn writeAtomsOneShot(self: *MachO) !void {1154pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1930 assert(self.mode == .one_shot);
1931
1932 const gpa = self.base.allocator;1155 const gpa = self.base.allocator;
1933 const slice = self.sections.slice();
19341156
1935 for (slice.items(.last_atom)) |last_atom, sect_id| {1157 const sym_index = try self.allocateSymbol();
1936 const header = slice.items(.header)[sect_id];1158 const atom = switch (self.mode) {
1937 if (header.size == 0) continue;1159 .incremental => blk: {
1938 var atom = last_atom.?;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);1174 const sym = atom.getSymbolPtr(self);
1943 defer buffer.deinit();1175 sym.n_type = macho.N_SECT;
1944 try buffer.ensureTotalCapacity(math.cast(usize, header.size) orelse return error.Overflow);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| {1181 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
1949 atom = prev;
1950 }
19511182
1952 while (true) {1183 try atom.addRelocation(self, .{
1953 const this_sym = atom.getSymbol(self);1184 .@"type" = switch (self.base.options.target.cpu.arch) {
1954 const padding_size: usize = if (atom.next) |next| blk: {1185 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1955 const next_sym = next.getSymbol(self);1186 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1956 const size = next_sym.n_value - (this_sym.n_value + atom.size);1187 else => unreachable,
1957 break :blk math.cast(usize, size) orelse return error.Overflow;1188 },
1958 } else 0;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)", .{1196 const target_sym = self.getSymbol(target);
1961 atom.sym_index,1197 if (target_sym.undf()) {
1962 atom.getName(self),1198 try atom.addBinding(self, .{
1963 atom.file,1199 .target = self.getGlobal(self.getSymbolName(target)).?,
1200 .offset = 0,
1964 });1201 });
1965 if (padding_size > 0) {1202 } else {
1966 log.debug(" (with padding {x})", .{padding_size});1203 try atom.addRebase(self, 0);
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 }
1986 }1204 }
1987 }1205 } else {
1988}1206 try atom.relocs.append(gpa, .{
19891207 .offset = 0,
1990fn writePadding(self: *MachO, sect_id: u8, size: usize, writer: anytype) !void {1208 .target = target,
1991 const header = self.sections.items(.header)[sect_id];1209 .addend = 0,
1992 const min_alignment: u3 = if (!header.isCode())1210 .subtractor = null,
1993 11211 .pcrel = false,
1994 else switch (self.base.options.target.cpu.arch) {1212 .length = 3,
1995 .aarch64 => @sizeOf(u32),1213 .@"type" = switch (self.base.options.target.cpu.arch) {
1996 .x86_64 => @as(u3, 1),1214 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1997 else => unreachable,1215 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
1998 };1216 else => unreachable,
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);
2012 },1217 },
2013 else => unreachable,1218 });
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 }
20361219
2037 if (atom.prev) |prev| {1220 const target_sym = self.getSymbol(target);
2038 atom = prev;1221 if (target_sym.undf()) {
2039 } else break;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);
2040 }1229 }
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);1231 try self.addAtomToSection(atom);
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);
2074 }1232 }
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
2110 return atom;1234 return atom;
2111}1235}
21121236
2113fn createDyldPrivateAtom(self: *MachO) !void {1237pub fn createDyldPrivateAtom(self: *MachO) !void {
2114 if (self.dyld_stub_binder_index == null) return;1238 if (self.dyld_stub_binder_index == null) return;
2115 if (self.dyld_private_atom != null) return;1239 if (self.dyld_private_atom != null) return;
21161240
2117 const gpa = self.base.allocator;1241 const gpa = self.base.allocator;
1242
2118 const sym_index = try self.allocateSymbol();1243 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
2120 const sym = atom.getSymbolPtr(self);1257 const sym = atom.getSymbolPtr(self);
2121 sym.n_type = macho.N_SECT;1258 sym.n_type = macho.N_SECT;
1259 sym.n_sect = self.data_section_index.? + 1;
2122 self.dyld_private_atom = atom;1260 self.dyld_private_atom = atom;
21231261
2124 try self.allocateAtomCommon(atom, self.data_section_index.?);
2125
2126 try self.managed_atoms.append(gpa, atom);1262 try self.managed_atoms.append(gpa, atom);
2127 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1263 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 }
2128}1272}
21291273
2130fn createStubHelperPreambleAtom(self: *MachO) !void {1274pub fn createStubHelperPreambleAtom(self: *MachO) !void {
2131 if (self.dyld_stub_binder_index == null) return;1275 if (self.dyld_stub_binder_index == null) return;
2132 if (self.stub_helper_preamble_atom != null) return;1276 if (self.stub_helper_preamble_atom != null) return;
21331277
2134 const gpa = self.base.allocator;1278 const gpa = self.base.allocator;
2135 const arch = self.base.options.target.cpu.arch;1279 const arch = self.base.options.target.cpu.arch;
2136 const size: u64 = switch (arch) {1280 const size: u5 = switch (arch) {
2137 .x86_64 => 15,1281 .x86_64 => 15,
2138 .aarch64 => 6 * @sizeOf(u32),1282 .aarch64 => 6 * @sizeOf(u32),
2139 else => unreachable,1283 else => unreachable,
...@@ -2144,117 +1288,200 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2144,117 +1288,200 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2144 else => unreachable,1288 else => unreachable,
2145 };1289 };
2146 const sym_index = try self.allocateSymbol();1290 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
2148 const sym = atom.getSymbolPtr(self);1308 const sym = atom.getSymbolPtr(self);
2149 sym.n_type = macho.N_SECT;1309 sym.n_type = macho.N_SECT;
1310 sym.n_sect = self.stub_helper_section_index.? + 1;
21501311
2151 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;1312 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
2152 switch (arch) {1318 switch (arch) {
2153 .x86_64 => {1319 .x86_64 => {
2154 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
2155 // lea %r11, [rip + disp]1320 // lea %r11, [rip + disp]
2156 atom.code.items[0] = 0x4c;1321 code[0] = 0x4c;
2157 atom.code.items[1] = 0x8d;1322 code[1] = 0x8d;
2158 atom.code.items[2] = 0x1d;1323 code[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 });
2168 // push %r111324 // push %r11
2169 atom.code.items[7] = 0x41;1325 code[7] = 0x41;
2170 atom.code.items[8] = 0x53;1326 code[8] = 0x53;
2171 // jmp [rip + disp]1327 // jmp [rip + disp]
2172 atom.code.items[9] = 0xff;1328 code[9] = 0xff;
2173 atom.code.items[10] = 0x25;1329 code[10] = 0x25;
2174 atom.relocs.appendAssumeCapacity(.{1330
2175 .offset = 11,1331 if (self.mode == .incremental) {
2176 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },1332 try atom.addRelocations(self, 2, .{ .{
2177 .addend = 0,1333 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
2178 .subtractor = null,1334 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2179 .pcrel = true,1335 .offset = 3,
2180 .length = 2,1336 .addend = 0,
2181 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_GOT),1337 .pcrel = true,
2182 });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 }
2183 },1368 },
1369
2184 .aarch64 => {1370 .aarch64 => {
2185 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 4);
2186 // adrp x17, 01371 // adrp x17, 0
2187 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());1372 mem.writeIntLittle(u32, code[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 });
2197 // add x17, x17, 01373 // add x17, x17, 0
2198 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());1374 mem.writeIntLittle(u32, code[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 });
2208 // stp x16, x17, [sp, #-16]!1375 // 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(
2210 .x16,1377 .x16,
2211 .x17,1378 .x17,
2212 aarch64.Register.sp,1379 aarch64.Register.sp,
2213 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),1380 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2214 ).toU32());1381 ).toU32());
2215 // adrp x16, 01382 // adrp x16, 0
2216 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());1383 mem.writeIntLittle(u32, code[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 });
2226 // ldr x16, [x16, 0]1384 // 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(
2228 .x16,1386 .x16,
2229 .x16,1387 .x16,
2230 aarch64.Instruction.LoadStoreOffset.imm(0),1388 aarch64.Instruction.LoadStoreOffset.imm(0),
2231 ).toU32());1389 ).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 });
2241 // br x161390 // 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 }
2243 },1462 },
1463
2244 else => unreachable,1464 else => unreachable,
2245 }1465 }
2246 self.stub_helper_preamble_atom = atom;1466 self.stub_helper_preamble_atom = atom;
22471467
2248 try self.allocateAtomCommon(atom, self.stub_helper_section_index.?);
2249
2250 try self.managed_atoms.append(gpa, atom);1468 try self.managed_atoms.append(gpa, atom);
2251 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1469 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 }
2252}1479}
22531480
2254pub fn createStubHelperAtom(self: *MachO) !*Atom {1481pub fn createStubHelperAtom(self: *MachO) !*Atom {
2255 const gpa = self.base.allocator;1482 const gpa = self.base.allocator;
2256 const arch = self.base.options.target.cpu.arch;1483 const arch = self.base.options.target.cpu.arch;
2257 const stub_size: u4 = switch (arch) {1484 const size: u4 = switch (arch) {
2258 .x86_64 => 10,1485 .x86_64 => 10,
2259 .aarch64 => 3 * @sizeOf(u32),1486 .aarch64 => 3 * @sizeOf(u32),
2260 else => unreachable,1487 else => unreachable,
...@@ -2265,51 +1492,96 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2265,51 +1492,96 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2265 else => unreachable,1492 else => unreachable,
2266 };1493 };
2267 const sym_index = try self.allocateSymbol();1494 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
2269 const sym = atom.getSymbolPtr(self);1512 const sym = atom.getSymbolPtr(self);
2270 sym.n_type = macho.N_SECT;1513 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
2274 switch (arch) {1520 switch (arch) {
2275 .x86_64 => {1521 .x86_64 => {
2276 // pushq1522 // pushq
2277 atom.code.items[0] = 0x68;1523 code[0] = 0x68;
2278 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.1524 // Next 4 bytes 1..4 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2279 // jmpq1525 // jmpq
2280 atom.code.items[5] = 0xe9;1526 code[5] = 0xe9;
2281 atom.relocs.appendAssumeCapacity(.{1527
2282 .offset = 6,1528 if (self.mode == .incremental) {
2283 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },1529 try atom.addRelocation(self, .{
2284 .addend = 0,1530 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2285 .subtractor = null,1531 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2286 .pcrel = true,1532 .offset = 6,
2287 .length = 2,1533 .addend = 0,
2288 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1534 .pcrel = true,
2289 });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 }
2290 },1549 },
2291 .aarch64 => {1550 .aarch64 => {
2292 const literal = blk: {1551 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);
2294 break :blk math.cast(u18, div_res) orelse return error.Overflow;1553 break :blk math.cast(u18, div_res) orelse return error.Overflow;
2295 };1554 };
2296 // ldr w16, literal1555 // 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(
2298 .w16,1557 .w16,
2299 literal,1558 literal,
2300 ).toU32());1559 ).toU32());
2301 // b disp1560 // b disp
2302 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());1561 mem.writeIntLittle(u32, code[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 });
2312 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.1562 // 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 }
2313 },1585 },
2314 else => unreachable,1586 else => unreachable,
2315 }1587 }
...@@ -2317,7 +1589,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2317,7 +1589,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2317 try self.managed_atoms.append(gpa, atom);1589 try self.managed_atoms.append(gpa, atom);
2318 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1590 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
2322 return atom;1601 return atom;
2323}1602}
...@@ -2325,35 +1604,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2325,35 +1604,73 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2325pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {1604pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
2326 const gpa = self.base.allocator;1605 const gpa = self.base.allocator;
2327 const sym_index = try self.allocateSymbol();1606 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
2329 const sym = atom.getSymbolPtr(self);1620 const sym = atom.getSymbolPtr(self);
2330 sym.n_type = macho.N_SECT;1621 sym.n_type = macho.N_SECT;
1622 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
23311623
2332 try atom.relocs.append(gpa, .{1624 if (self.mode == .incremental) {
2333 .offset = 0,1625 try atom.addRelocation(self, .{
2334 .target = .{ .sym_index = stub_sym_index, .file = null },1626 .@"type" = switch (self.base.options.target.cpu.arch) {
2335 .addend = 0,1627 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2336 .subtractor = null,1628 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2337 .pcrel = false,1629 else => unreachable,
2338 .length = 3,1630 },
2339 .@"type" = switch (self.base.options.target.cpu.arch) {1631 .target = .{ .sym_index = stub_sym_index, .file = null },
2340 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1632 .offset = 0,
2341 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),1633 .addend = 0,
2342 else => unreachable,1634 .pcrel = false,
2343 },1635 .length = 3,
2344 });1636 });
2345 try atom.rebases.append(gpa, 0);1637 try atom.addRebase(self, 0);
23461638 try atom.addLazyBinding(self, .{
2347 const global = self.getGlobal(self.getSymbolName(target)).?;1639 .target = self.getGlobal(self.getSymbolName(target)).?,
2348 try atom.lazy_bindings.append(gpa, .{1640 .offset = 0,
2349 .target = global,1641 });
2350 .offset = 0,1642 } else {
2351 });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
2353 try self.managed_atoms.append(gpa, atom);1664 try self.managed_atoms.append(gpa, atom);
2354 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1665 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
2358 return atom;1675 return atom;
2359}1676}
...@@ -2366,61 +1683,117 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2366,61 +1683,117 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2366 .aarch64 => 2,1683 .aarch64 => 2,
2367 else => unreachable, // unhandled architecture type1684 else => unreachable, // unhandled architecture type
2368 };1685 };
2369 const stub_size: u4 = switch (arch) {1686 const size: u4 = switch (arch) {
2370 .x86_64 => 6,1687 .x86_64 => 6,
2371 .aarch64 => 3 * @sizeOf(u32),1688 .aarch64 => 3 * @sizeOf(u32),
2372 else => unreachable, // unhandled architecture type1689 else => unreachable, // unhandled architecture type
2373 };1690 };
2374 const sym_index = try self.allocateSymbol();1691 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
2376 const sym = atom.getSymbolPtr(self);1710 const sym = atom.getSymbolPtr(self);
2377 sym.n_type = macho.N_SECT;1711 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
2379 switch (arch) {1718 switch (arch) {
2380 .x86_64 => {1719 .x86_64 => {
2381 // jmp1720 // jmp
2382 atom.code.items[0] = 0xff;1721 code[0] = 0xff;
2383 atom.code.items[1] = 0x25;1722 code[1] = 0x25;
2384 try atom.relocs.append(gpa, .{1723
2385 .offset = 2,1724 if (self.mode == .incremental) {
2386 .target = .{ .sym_index = laptr_sym_index, .file = null },1725 try atom.addRelocation(self, .{
2387 .addend = 0,1726 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
2388 .subtractor = null,1727 .target = .{ .sym_index = laptr_sym_index, .file = null },
2389 .pcrel = true,1728 .offset = 2,
2390 .length = 2,1729 .addend = 0,
2391 .@"type" = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1730 .pcrel = true,
2392 });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 }
2393 },1744 },
2394 .aarch64 => {1745 .aarch64 => {
2395 try atom.relocs.ensureTotalCapacity(gpa, 2);
2396 // adrp x16, pages1746 // adrp x16, pages
2397 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());1747 mem.writeIntLittle(u32, code[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 });
2407 // ldr x16, x16, offset1748 // 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(
2409 .x16,1750 .x16,
2410 .x16,1751 .x16,
2411 aarch64.Instruction.LoadStoreOffset.imm(0),1752 aarch64.Instruction.LoadStoreOffset.imm(0),
2412 ).toU32());1753 ).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 });
2422 // br x161754 // 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 }
2424 },1797 },
2425 else => unreachable,1798 else => unreachable,
2426 }1799 }
...@@ -2428,12 +1801,53 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2428,12 +1801,53 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2428 try self.managed_atoms.append(gpa, atom);1801 try self.managed_atoms.append(gpa, atom);
2429 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);1802 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
2433 return atom;1846 return atom;
2434}1847}
24351848
2436fn createTentativeDefAtoms(self: *MachO) !void {1849pub fn createTentativeDefAtoms(self: *MachO) !void {
1850 assert(self.mode == .one_shot);
2437 const gpa = self.base.allocator;1851 const gpa = self.base.allocator;
24381852
2439 for (self.globals.items) |global| {1853 for (self.globals.items) |global| {
...@@ -2448,16 +1862,15 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2448,16 +1862,15 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2448 // text blocks for each tentative definition.1862 // text blocks for each tentative definition.
2449 const size = sym.n_value;1863 const size = sym.n_value;
2450 const alignment = (sym.n_desc >> 8) & 0x0f;1864 const alignment = (sym.n_desc >> 8) & 0x0f;
2451 const n_sect = (try self.getOutputSection(.{1865 const sect_id = (try self.getOutputSection(.{
2452 .segname = makeStaticString("__DATA"),1866 .segname = makeStaticString("__DATA"),
2453 .sectname = makeStaticString("__bss"),1867 .sectname = makeStaticString("__bss"),
2454 .flags = macho.S_ZEROFILL,1868 .flags = macho.S_ZEROFILL,
2455 })).?;1869 })).?;
2456
2457 sym.* = .{1870 sym.* = .{
2458 .n_strx = sym.n_strx,1871 .n_strx = sym.n_strx,
2459 .n_type = macho.N_SECT | macho.N_EXT,1872 .n_type = macho.N_SECT | macho.N_EXT,
2460 .n_sect = n_sect,1873 .n_sect = sect_id + 1,
2461 .n_desc = 0,1874 .n_desc = 0,
2462 .n_value = 0,1875 .n_value = 0,
2463 };1876 };
...@@ -2465,7 +1878,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2465,7 +1878,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2465 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);1878 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
2466 atom.file = global.file;1879 atom.file = global.file;
24671880
2468 try self.allocateAtomCommon(atom, n_sect);1881 try self.addAtomToSection(atom);
24691882
2470 if (global.file) |file| {1883 if (global.file) |file| {
2471 const object = &self.objects.items[file];1884 const object = &self.objects.items[file];
...@@ -2478,7 +1891,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {...@@ -2478,7 +1891,7 @@ fn createTentativeDefAtoms(self: *MachO) !void {
2478 }1891 }
2479}1892}
24801893
2481fn createMhExecuteHeaderSymbol(self: *MachO) !void {1894pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2482 if (self.base.options.output_mode != .Exe) return;1895 if (self.base.options.output_mode != .Exe) return;
2483 if (self.getGlobal("__mh_execute_header")) |global| {1896 if (self.getGlobal("__mh_execute_header")) |global| {
2484 const sym = self.getSymbol(global);1897 const sym = self.getSymbol(global);
...@@ -2501,7 +1914,7 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {...@@ -2501,7 +1914,7 @@ fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2501 gop.value_ptr.* = sym_loc;1914 gop.value_ptr.* = sym_loc;
2502}1915}
25031916
2504fn createDsoHandleSymbol(self: *MachO) !void {1917pub fn createDsoHandleSymbol(self: *MachO) !void {
2505 const global = self.getGlobalPtr("___dso_handle") orelse return;1918 const global = self.getGlobalPtr("___dso_handle") orelse return;
2506 if (!self.getSymbol(global.*).undf()) return;1919 if (!self.getSymbol(global.*).undf()) return;
25071920
...@@ -2576,7 +1989,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -2576,7 +1989,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
2576 gop.value_ptr.* = current;1989 gop.value_ptr.* = current;
2577}1990}
25781991
2579fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {1992pub fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2580 const object = &self.objects.items[object_id];1993 const object = &self.objects.items[object_id];
2581 log.debug("resolving symbols in '{s}'", .{object.name});1994 log.debug("resolving symbols in '{s}'", .{object.name});
25821995
...@@ -2629,7 +2042,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2629,7 +2042,7 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2629 }2042 }
2630}2043}
26312044
2632fn resolveSymbolsInArchives(self: *MachO) !void {2045pub fn resolveSymbolsInArchives(self: *MachO) !void {
2633 if (self.archives.items.len == 0) return;2046 if (self.archives.items.len == 0) return;
26342047
2635 const gpa = self.base.allocator;2048 const gpa = self.base.allocator;
...@@ -2660,7 +2073,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -2660,7 +2073,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
2660 }2073 }
2661}2074}
26622075
2663fn resolveSymbolsInDylibs(self: *MachO) !void {2076pub fn resolveSymbolsInDylibs(self: *MachO) !void {
2664 if (self.dylibs.items.len == 0) return;2077 if (self.dylibs.items.len == 0) return;
26652078
2666 const gpa = self.base.allocator;2079 const gpa = self.base.allocator;
...@@ -2697,6 +2110,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2697,6 +2110,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2697 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);2110 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
2698 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);2111 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
2699 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;2112 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
2113 self.markRelocsDirtyByTarget(global);
2700 }2114 }
27012115
2702 continue :loop;2116 continue :loop;
...@@ -2706,7 +2120,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -2706,7 +2120,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
2706 }2120 }
2707}2121}
27082122
2709fn resolveSymbolsAtLoading(self: *MachO) !void {2123pub fn resolveSymbolsAtLoading(self: *MachO) !void {
2710 const is_lib = self.base.options.output_mode == .Lib;2124 const is_lib = self.base.options.output_mode == .Lib;
2711 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;2125 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
2712 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);2126 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
...@@ -2749,7 +2163,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {...@@ -2749,7 +2163,7 @@ fn resolveSymbolsAtLoading(self: *MachO) !void {
2749 }2163 }
2750}2164}
27512165
2752fn resolveDyldStubBinder(self: *MachO) !void {2166pub fn resolveDyldStubBinder(self: *MachO) !void {
2753 if (self.dyld_stub_binder_index != null) return;2167 if (self.dyld_stub_binder_index != null) return;
2754 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports2168 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 {...@@ -2794,9 +2208,13 @@ fn resolveDyldStubBinder(self: *MachO) !void {
2794 const got_index = try self.allocateGotEntry(global);2208 const got_index = try self.allocateGotEntry(global);
2795 const got_atom = try self.createGotAtom(global);2209 const got_atom = try self.createGotAtom(global);
2796 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2210 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 }
2797}2215}
27982216
2799fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {2217pub fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2800 const name_len = mem.sliceTo(default_dyld_path, 0).len;2218 const name_len = mem.sliceTo(default_dyld_path, 0).len;
2801 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2219 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2802 u64,2220 u64,
...@@ -2816,9 +2234,13 @@ fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {...@@ -2816,9 +2234,13 @@ fn writeDylinkerLC(ncmds: *u32, lc_writer: anytype) !void {
2816 ncmds.* += 1;2234 ncmds.* += 1;
2817}2235}
28182236
2819fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2237pub fn writeMainLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2820 if (self.base.options.output_mode != .Exe) return;2238 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];
2822 const global = try self.getEntryPoint();2244 const global = try self.getEntryPoint();
2823 const sym = self.getSymbol(global);2245 const sym = self.getSymbol(global);
2824 try lc_writer.writeStruct(macho.entry_point_command{2246 try lc_writer.writeStruct(macho.entry_point_command{
...@@ -2838,7 +2260,7 @@ const WriteDylibLCCtx = struct {...@@ -2838,7 +2260,7 @@ const WriteDylibLCCtx = struct {
2838 compatibility_version: u32 = 0x10000,2260 compatibility_version: u32 = 0x10000,
2839};2261};
28402262
2841fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {2263pub fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2842 const name_len = ctx.name.len + 1;2264 const name_len = ctx.name.len + 1;
2843 const cmdsize = @intCast(u32, mem.alignForwardGeneric(2265 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
2844 u64,2266 u64,
...@@ -2864,7 +2286,7 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {...@@ -2864,7 +2286,7 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, ncmds: *u32, lc_writer: anytype) !void {
2864 ncmds.* += 1;2286 ncmds.* += 1;
2865}2287}
28662288
2867fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2289pub fn writeDylibIdLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2868 if (self.base.options.output_mode != .Lib) return;2290 if (self.base.options.output_mode != .Lib) return;
2869 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;2291 const install_name = self.base.options.install_name orelse self.base.options.emit.?.sub_path;
2870 const curr = self.base.options.version orelse std.builtin.Version{2292 const curr = self.base.options.version orelse std.builtin.Version{
...@@ -2910,7 +2332,7 @@ const RpathIterator = struct {...@@ -2910,7 +2332,7 @@ const RpathIterator = struct {
2910 }2332 }
2911};2333};
29122334
2913fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2335pub fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2914 const gpa = self.base.allocator;2336 const gpa = self.base.allocator;
29152337
2916 var it = RpathIterator.init(gpa, self.base.options.rpath_list);2338 var it = RpathIterator.init(gpa, self.base.options.rpath_list);
...@@ -2937,7 +2359,7 @@ fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {...@@ -2937,7 +2359,7 @@ fn writeRpathLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2937 }2359 }
2938}2360}
29392361
2940fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2362pub fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2941 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);2363 const cmdsize = @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version);
2942 const platform_version = blk: {2364 const platform_version = blk: {
2943 const ver = self.base.options.target.os.version_range.semver.min;2365 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 {...@@ -2970,7 +2392,7 @@ fn writeBuildVersionLC(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2970 ncmds.* += 1;2392 ncmds.* += 1;
2971}2393}
29722394
2973fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {2395pub fn writeLoadDylibLCs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
2974 for (self.referenced_dylibs.keys()) |id| {2396 for (self.referenced_dylibs.keys()) |id| {
2975 const dylib = self.dylibs.items[id];2397 const dylib = self.dylibs.items[id];
2976 const dylib_id = dylib.id orelse unreachable;2398 const dylib_id = dylib.id orelse unreachable;
...@@ -3069,14 +2491,49 @@ pub fn deinit(self: *MachO) void {...@@ -3069,14 +2491,49 @@ pub fn deinit(self: *MachO) void {
3069 }2491 }
30702492
3071 self.atom_by_index_table.deinit(gpa);2493 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 }
3072}2526}
30732527
3074fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {2528fn freeAtom(self: *MachO, atom: *Atom, owns_atom: bool) void {
3075 log.debug("freeAtom {*}", .{atom});2529 log.debug("freeAtom {*}", .{atom});
3076 if (!owns_atom) {2530 if (!owns_atom) {
3077 atom.deinit(self.base.allocator);2531 atom.deinit(self.base.allocator);
3078 }2532 }
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;
3080 const free_list = &self.sections.items(.free_list)[sect_id];2537 const free_list = &self.sections.items(.free_list)[sect_id];
3081 var already_have_free_list_node = false;2538 var already_have_free_list_node = false;
3082 {2539 {
...@@ -3129,21 +2586,20 @@ fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {...@@ -3129,21 +2586,20 @@ fn freeAtom(self: *MachO, atom: *Atom, sect_id: u8, owns_atom: bool) void {
3129 }2586 }
3130}2587}
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 {
3133 _ = self;2590 _ = self;
3134 _ = atom;2591 _ = atom;
3135 _ = new_block_size;2592 _ = new_block_size;
3136 _ = sect_id;
3137 // TODO check the new capacity, and if it crosses the size threshold into a big enough2593 // TODO check the new capacity, and if it crosses the size threshold into a big enough
3138 // capacity, insert a free list node for it.2594 // capacity, insert a free list node for it.
3139}2595}
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 {
3142 const sym = atom.getSymbol(self);2598 const sym = atom.getSymbol(self);
3143 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;2599 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3144 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);2600 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
3145 if (!need_realloc) return sym.n_value;2601 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);
3147}2603}
31482604
3149fn allocateSymbol(self: *MachO) !u32 {2605fn allocateSymbol(self: *MachO) !u32 {
...@@ -3282,13 +2738,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3282,13 +2738,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3282 const decl_index = func.owner_decl;2738 const decl_index = func.owner_decl;
3283 const decl = module.declPtr(decl_index);2739 const decl = module.declPtr(decl_index);
3284 self.freeUnnamedConsts(decl_index);2740 self.freeUnnamedConsts(decl_index);
32852741 self.freeRelocationsForAtom(&decl.link.macho);
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();
32922742
3293 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2743 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3294 defer code_buffer.deinit();2744 defer code_buffer.deinit();
...@@ -3306,18 +2756,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3306,18 +2756,16 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3306 else2756 else
3307 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);2757 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
33082758
3309 switch (res) {2759 const code = switch (res) {
3310 .appended => {2760 .appended => code_buffer.items,
3311 try decl.link.macho.code.appendSlice(self.base.allocator, code_buffer.items);
3312 },
3313 .fail => |em| {2761 .fail => |em| {
3314 decl.analysis = .codegen_failure;2762 decl.analysis = .codegen_failure;
3315 try module.failed_decls.put(module.gpa, decl_index, em);2763 try module.failed_decls.put(module.gpa, decl_index, em);
3316 return;2764 return;
3317 },2765 },
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
3322 if (decl_state) |*ds| {2770 if (decl_state) |*ds| {
3323 try self.d_sym.?.dwarf.commitDeclState(2771 try self.d_sym.?.dwarf.commitDeclState(
...@@ -3363,20 +2811,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3363,20 +2811,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
33632811
3364 log.debug("allocating symbol indexes for {?s}", .{name});2812 log.debug("allocating symbol indexes for {?s}", .{name});
33652813
3366 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2814 const atom = try gpa.create(Atom);
3367 const sym_index = try self.allocateSymbol();2815 errdefer gpa.destroy(atom);
3368 const atom = try MachO.createEmptyAtom(2816 atom.* = Atom.empty;
3369 gpa,2817
3370 sym_index,2818 atom.sym_index = try self.allocateSymbol();
3371 @sizeOf(u64),
3372 math.log2(required_alignment),
3373 );
33742819
3375 try self.managed_atoms.append(gpa, atom);2820 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
3378 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{2823 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,
3380 });2825 });
3381 const code = switch (res) {2826 const code = switch (res) {
3382 .externally_managed => |x| x,2827 .externally_managed => |x| x,
...@@ -3389,33 +2834,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3389,33 +2834,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3389 },2834 },
3390 };2835 };
33912836
3392 atom.code.clearRetainingCapacity();2837 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3393 try atom.code.appendSlice(gpa, code);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(2850 try unnamed_consts.append(gpa, atom);
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);
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 });
3405 log.debug(" (required alignment 0x{x})", .{required_alignment});2853 log.debug(" (required alignment 0x{x})", .{required_alignment});
34062854
3407 errdefer self.freeAtom(atom, sect_id, true);2855 try self.writeAtom(atom, code);
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);
34192856
3420 return atom.sym_index;2857 return atom.sym_index;
3421}2858}
...@@ -3442,6 +2879,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3442,6 +2879,8 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3442 }2879 }
3443 }2880 }
34442881
2882 self.freeRelocationsForAtom(&decl.link.macho);
2883
3445 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2884 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3446 defer code_buffer.deinit();2885 defer code_buffer.deinit();
34472886
...@@ -3469,27 +2908,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3469,27 +2908,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3469 .parent_atom_index = decl.link.macho.sym_index,2908 .parent_atom_index = decl.link.macho.sym_index,
3470 });2909 });
34712910
3472 const code = blk: {2911 const code = switch (res) {
3473 switch (res) {2912 .externally_managed => |x| x,
3474 .externally_managed => |x| break :blk x,2913 .appended => code_buffer.items,
3475 .appended => {2914 .fail => |em| {
3476 // TODO clearing the code and relocs buffer should probably be orchestrated2915 decl.analysis = .codegen_failure;
3477 // in a different, smarter, more automatic way somewhere else, in a more centralised2916 try module.failed_decls.put(module.gpa, decl_index, em);
3478 // way than this.2917 return;
3479 // If we don't clear the buffers here, we are up for some nasty surprises when2918 },
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 }
3491 };2919 };
3492 const addr = try self.placeDecl(decl_index, code.len);2920 const addr = try self.updateDeclCode(decl_index, code);
34932921
3494 if (decl_state) |*ds| {2922 if (decl_state) |*ds| {
3495 try self.d_sym.?.dwarf.commitDeclState(2923 try self.d_sym.?.dwarf.commitDeclState(
...@@ -3508,82 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3508,82 +2936,16 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3508 try self.updateDeclExports(module, decl_index, decl_exports);2936 try self.updateDeclExports(module, decl_index, decl_exports);
3509}2937}
35102938
3511/// Checks if the value, or any of its embedded values stores a pointer, and thus requires2939fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
3512/// a rebase opcode for the dynamic linker.2940 const ty = decl.ty;
3513fn needsPointerRebase(ty: Type, val: Value, mod: *Module) bool {2941 const val = decl.val;
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);
3575 const zig_ty = ty.zigTypeTag();2942 const zig_ty = ty.zigTypeTag();
3576 const mode = self.base.options.optimize_mode;2943 const mode = self.base.options.optimize_mode;
3577 const sect_id: u8 = blk: {2944 const sect_id: u8 = blk: {
3578 // TODO finish and audit this function2945 // TODO finish and audit this function
3579 if (val.isUndefDeep()) {2946 if (val.isUndefDeep()) {
3580 if (mode == .ReleaseFast or mode == .ReleaseSmall) {2947 if (mode == .ReleaseFast or mode == .ReleaseSmall) {
3581 break :blk (try self.getOutputSection(.{2948 @panic("TODO __DATA,__bss");
3582 .segname = makeStaticString("__DATA"),
3583 .sectname = makeStaticString("__bss"),
3584 .size = code.len,
3585 .@"align" = align_log_2,
3586 })).?;
3587 } else {2949 } else {
3588 break :blk self.data_section_index.?;2950 break :blk self.data_section_index.?;
3589 }2951 }
...@@ -3593,129 +2955,207 @@ fn getOutputSectionAtom(...@@ -3593,129 +2955,207 @@ fn getOutputSectionAtom(
3593 break :blk self.data_section_index.?;2955 break :blk self.data_section_index.?;
3594 }2956 }
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
3605 switch (zig_ty) {2958 switch (zig_ty) {
3606 .Fn => {2959 // TODO: what if this is a function pointer?
3607 break :blk self.text_section_index.?;2960 .Fn => break :blk self.text_section_index.?,
3608 },2961 else => {
3609 .Array => {2962 if (val.castTag(.variable)) |_| {
3610 if (val.tag() == .bytes) {2963 break :blk self.data_section_index.?;
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 }
3626 }2964 }
2965 break :blk self.data_const_section_index.?;
3627 },2966 },
3628 else => {},
3629 }2967 }
3630 break :blk (try self.getOutputSection(.{
3631 .segname = makeStaticString("__TEXT"),
3632 .sectname = makeStaticString("__const"),
3633 .size = code.len,
3634 .@"align" = align_log_2,
3635 })).?;
3636 };2968 };
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 });
3644 return sect_id;2969 return sect_id;
3645}2970}
36462971
3647fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {2972pub fn getOutputSection(self: *MachO, sect: macho.section_64) !?u8 {
3648 const module = self.base.options.module.?;2973 const segname = sect.segName();
3649 const decl = module.declPtr(decl_index);2974 const sectname = sect.sectName();
3650 const required_alignment = decl.getAlignment(self.base.options.target);2975 const sect_id: ?u8 = blk: {
3651 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()2976 if (mem.eql(u8, "__LLVM", segname)) {
36522977 log.debug("TODO LLVM section: type 0x{x}, name '{s},{s}'", .{
3653 const sym_name = try decl.getFullyQualifiedName(module);2978 sect.flags, segname, sectname,
3654 defer self.base.allocator.free(sym_name);2979 });
36552980 break :blk null;
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);
3686 }2981 }
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 });2983 if (sect.isCode()) {
3699 log.debug(" (required alignment 0x{x})", .{required_alignment});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);3004 switch (sect.@"type"()) {
3704 symbol.* = .{3005 macho.S_4BYTE_LITERALS,
3705 .n_strx = name_str_index,3006 macho.S_8BYTE_LITERALS,
3706 .n_type = macho.N_SECT,3007 macho.S_16BYTE_LITERALS,
3707 .n_sect = match + 1,3008 => {
3708 .n_desc = 0,3009 if (self.getSectionByName("__TEXT", "__const")) |sect_id| break :blk sect_id;
3709 .n_value = addr,3010 break :blk try self.initSection("__TEXT", "__const", .{});
3710 };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 };
3713 const got_index = try self.allocateGotEntry(got_target);3149 const got_index = try self.allocateGotEntry(got_target);
3714 const got_atom = try self.createGotAtom(got_target);3150 const got_atom = try self.createGotAtom(got_target);
3715 self.got_entries.items[got_index].sym_index = got_atom.sym_index;3151 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3152 try self.writePtrWidthAtom(got_atom);
3716 }3153 }
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;
3719}3159}
37203160
3721pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {3161pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
...@@ -3863,20 +3303,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -3863,20 +3303,25 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
3863 }3303 }
3864}3304}
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
3866fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {3313fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
3314 const gpa = self.base.allocator;
3867 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;3315 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
3868 for (unnamed_consts.items) |atom| {3316 for (unnamed_consts.items) |atom| {
3869 // TODO3317 self.freeAtom(atom, true);
3870 // const sect_id = atom.getSymbol(self).n_sect;3318 self.locals_free_list.append(gpa, atom.sym_index) catch {};
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 {};
3874 self.locals.items[atom.sym_index].n_type = 0;3319 self.locals.items[atom.sym_index].n_type = 0;
3875 _ = self.atom_by_index_table.remove(atom.sym_index);3320 _ = self.atom_by_index_table.remove(atom.sym_index);
3876 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});3321 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
3877 atom.sym_index = 0;3322 atom.sym_index = 0;
3878 }3323 }
3879 unnamed_consts.clearAndFree(self.base.allocator);3324 unnamed_consts.clearAndFree(gpa);
3880}3325}
38813326
3882pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {3327pub 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 {...@@ -3885,20 +3330,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
3885 }3330 }
3886 const mod = self.base.options.module.?;3331 const mod = self.base.options.module.?;
3887 const decl = mod.declPtr(decl_index);3332 const decl = mod.declPtr(decl_index);
3333
3888 log.debug("freeDecl {*}", .{decl});3334 log.debug("freeDecl {*}", .{decl});
3335
3889 const kv = self.decls.fetchSwapRemove(decl_index);3336 const kv = self.decls.fetchSwapRemove(decl_index);
3890 if (kv.?.value) |match| {3337 if (kv.?.value) |_| {
3891 self.freeAtom(&decl.link.macho, match, false);3338 self.freeAtom(&decl.link.macho, false);
3892 self.freeUnnamedConsts(decl_index);3339 self.freeUnnamedConsts(decl_index);
3893 }3340 }
3341
3894 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.3342 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
3895 if (decl.link.macho.sym_index != 0) {3343 const gpa = self.base.allocator;
3896 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};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
3898 // Try freeing GOT atom if this decl had one3348 // 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 };
3900 if (self.got_entries_table.get(got_target)) |got_index| {3350 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 {};
3902 self.got_entries.items[got_index] = .{3352 self.got_entries.items[got_index] = .{
3903 .target = .{ .sym_index = 0, .file = null },3353 .target = .{ .sym_index = 0, .file = null },
3904 .sym_index = 0,3354 .sym_index = 0,
...@@ -3906,20 +3356,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -3906,20 +3356,18 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
3906 _ = self.got_entries_table.remove(got_target);3356 _ = self.got_entries_table.remove(got_target);
39073357
3908 if (self.d_sym) |*d_sym| {3358 if (self.d_sym) |*d_sym| {
3909 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);3359 d_sym.swapRemoveRelocs(sym_index);
3910 }3360 }
39113361
3912 log.debug(" adding GOT index {d} to free list (target local@{d})", .{3362 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
3913 got_index,
3914 decl.link.macho.sym_index,
3915 });
3916 }3363 }
39173364
3918 self.locals.items[decl.link.macho.sym_index].n_type = 0;3365 self.locals.items[sym_index].n_type = 0;
3919 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);3366 _ = self.atom_by_index_table.remove(sym_index);
3920 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});3367 log.debug(" adding local symbol index {d} to free list", .{sym_index});
3921 decl.link.macho.sym_index = 0;3368 decl.link.macho.sym_index = 0;
3922 }3369 }
3370
3923 if (self.d_sym) |*d_sym| {3371 if (self.d_sym) |*d_sym| {
3924 d_sym.dwarf.freeDecl(decl);3372 d_sym.dwarf.freeDecl(decl);
3925 }3373 }
...@@ -3932,260 +3380,155 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -3932,260 +3380,155 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
3932 assert(self.llvm_object == null);3380 assert(self.llvm_object == null);
3933 assert(decl.link.macho.sym_index != 0);3381 assert(decl.link.macho.sym_index != 0);
39343382
3935 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;3383 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
3936 try atom.relocs.append(self.base.allocator, .{3384 try atom.addRelocation(self, .{
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,
3943 .@"type" = switch (self.base.options.target.cpu.arch) {3385 .@"type" = switch (self.base.options.target.cpu.arch) {
3944 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),3386 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
3945 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),3387 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
3946 else => unreachable,3388 else => unreachable,
3947 },3389 },
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,
3948 });3395 });
3949 try atom.rebases.append(self.base.allocator, reloc_info.offset);3396 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
39503397
3951 return 0;3398 return 0;
3952}3399}
39533400
3954fn populateMissingMetadata(self: *MachO) !void {3401pub fn populateMissingMetadata(self: *MachO) !void {
3402 assert(self.mode == .incremental);
3403
3955 const gpa = self.base.allocator;3404 const gpa = self.base.allocator;
3956 const cpu_arch = self.base.options.target.cpu.arch;3405 const cpu_arch = self.base.options.target.cpu.arch;
3957 const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize;3406 const pagezero_vmsize = self.calcPagezeroSize();
3958 const aligned_pagezero_vmsize = mem.alignBackwardGeneric(u64, pagezero_vmsize, self.page_size);3407
39593408 if (self.pagezero_segment_cmd_index == null) {
3960 if (self.pagezero_segment_cmd_index == null) blk: {3409 if (pagezero_vmsize > 0) {
3961 if (self.base.options.output_mode == .Lib) break :blk;3410 self.pagezero_segment_cmd_index = @intCast(u8, self.segments.items.len);
3962 if (aligned_pagezero_vmsize == 0) break :blk;3411 try self.segments.append(gpa, .{
3963 if (aligned_pagezero_vmsize != pagezero_vmsize) {3412 .segname = makeStaticString("__PAGEZERO"),
3964 log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize});3413 .vmsize = pagezero_vmsize,
3965 log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize});3414 .cmdsize = @sizeOf(macho.segment_command_64),
3415 });
3966 }3416 }
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 });
3973 }3417 }
39743418
3975 if (self.text_segment_cmd_index == null) {3419 if (self.header_segment_cmd_index == null) {
3976 self.text_segment_cmd_index = @intCast(u8, self.segments.items.len);3420 // The first __TEXT segment is immovable and covers MachO header and load commands.
3977 const needed_size = if (self.mode == .incremental) blk: {3421 self.header_segment_cmd_index = @intCast(u8, self.segments.items.len);
3978 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);3422 const ideal_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;3423 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);
3980 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;3424
3981 const ideal_size = headerpad_size + program_code_size_hint + got_size_hint;3425 log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size });
3982 const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.page_size);3426
3983 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
3984 break :blk needed_size;
3985 } else 0;
3986 try self.segments.append(gpa, .{3427 try self.segments.append(gpa, .{
3987 .segname = makeStaticString("__TEXT"),3428 .segname = makeStaticString("__TEXT"),
3988 .vmaddr = aligned_pagezero_vmsize,3429 .vmaddr = pagezero_vmsize,
3989 .vmsize = needed_size,3430 .vmsize = needed_size,
3990 .filesize = needed_size,3431 .filesize = needed_size,
3991 .maxprot = macho.PROT.READ | macho.PROT.EXEC,3432 .maxprot = macho.PROT.READ | macho.PROT.EXEC,
3992 .initprot = macho.PROT.READ | macho.PROT.EXEC,3433 .initprot = macho.PROT.READ | macho.PROT.EXEC,
3993 .cmdsize = @sizeOf(macho.segment_command_64),3434 .cmdsize = @sizeOf(macho.segment_command_64),
3994 });3435 });
3436 self.segment_table_dirty = true;
3995 }3437 }
39963438
3997 if (self.text_section_index == null) {3439 if (self.text_section_index == null) {
3998 const alignment: u2 = switch (cpu_arch) {3440 // Sadly, segments need unique string identfiers for some reason.
3999 .x86_64 => 0,3441 self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{
4000 .aarch64 => 2,3442 .size = self.base.options.program_code_size_hint,
4001 else => unreachable, // unhandled architecture type3443 .alignment = switch (cpu_arch) {
4002 };3444 .x86_64 => 1,
4003 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;3445 .aarch64 => @sizeOf(u32),
4004 self.text_section_index = try self.initSection(3446 else => unreachable, // unhandled architecture type
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,
4011 },3447 },
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;
4013 }3452 }
40143453
4015 if (self.stubs_section_index == null) {3454 if (self.stubs_section_index == null) {
4016 const alignment: u2 = switch (cpu_arch) {3455 const stub_size: u32 = 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) {
4022 .x86_64 => 6,3456 .x86_64 => 6,
4023 .aarch64 => 3 * @sizeOf(u32),3457 .aarch64 => 3 * @sizeOf(u32),
4024 else => unreachable, // unhandled architecture type3458 else => unreachable, // unhandled architecture type
4025 };3459 };
4026 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;3460 self.stubs_section_index = try self.allocateSection("__TEXT2", "__stubs", .{
4027 self.stubs_section_index = try self.initSection(3461 .size = stub_size,
4028 "__TEXT",3462 .alignment = switch (cpu_arch) {
4029 "__stubs",3463 .x86_64 => 1,
4030 needed_size,3464 .aarch64 => @sizeOf(u32),
4031 alignment,3465 else => unreachable, // unhandled architecture type
4032 .{
4033 .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
4034 .reserved2 = stub_size,
4035 },3466 },
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;
4037 }3472 }
40383473
4039 if (self.stub_helper_section_index == null) {3474 if (self.stub_helper_section_index == null) {
4040 const alignment: u2 = switch (cpu_arch) {3475 self.stub_helper_section_index = try self.allocateSection("__TEXT3", "__stub_helper", .{
4041 .x86_64 => 0,3476 .size = @sizeOf(u32),
4042 .aarch64 => 2,3477 .alignment = switch (cpu_arch) {
4043 else => unreachable, // unhandled architecture type3478 .x86_64 => 1,
4044 };3479 .aarch64 => @sizeOf(u32),
4045 const preamble_size: u6 = switch (cpu_arch) {3480 else => unreachable, // unhandled architecture type
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,
4066 },3481 },
4067 );3482 .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
4068 }3483 .prot = macho.PROT.READ | macho.PROT.EXEC,
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),
4095 });3484 });
3485 self.segment_table_dirty = true;
4096 }3486 }
40973487
4098 if (self.got_section_index == null) {3488 if (self.got_section_index == null) {
4099 const needed_size = if (self.mode == .incremental)3489 self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{
4100 @sizeOf(u64) * self.base.options.symbol_count_hint3490 .size = @sizeOf(u64) * self.base.options.symbol_count_hint,
4101 else3491 .alignment = @alignOf(u64),
4102 0;3492 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
4103 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3493 .prot = macho.PROT.READ | macho.PROT.WRITE,
4104 self.got_section_index = try self.initSection(3494 });
4105 "__DATA_CONST",3495 self.segment_table_dirty = true;
4106 "__got",
4107 needed_size,
4108 alignment,
4109 .{
4110 .flags = macho.S_NON_LAZY_SYMBOL_POINTERS,
4111 },
4112 );
4113 }3496 }
41143497
4115 if (self.data_segment_cmd_index == null) {3498 if (self.data_const_section_index == null) {
4116 self.data_segment_cmd_index = @intCast(u8, self.segments.items.len);3499 self.data_const_section_index = try self.allocateSection("__DATA_CONST1", "__const", .{
4117 var vmaddr: u64 = 0;3500 .size = @sizeOf(u64),
4118 var fileoff: u64 = 0;3501 .alignment = @alignOf(u64),
4119 var needed_size: u64 = 0;3502 .flags = macho.S_REGULAR,
4120 if (self.mode == .incremental) {3503 .prot = macho.PROT.READ | macho.PROT.WRITE,
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),
4140 });3504 });
3505 self.segment_table_dirty = true;
4141 }3506 }
41423507
4143 if (self.la_symbol_ptr_section_index == null) {3508 if (self.la_symbol_ptr_section_index == null) {
4144 const needed_size = if (self.mode == .incremental)3509 self.la_symbol_ptr_section_index = try self.allocateSection("__DATA", "__la_symbol_ptr", .{
4145 @sizeOf(u64) * self.base.options.symbol_count_hint3510 .size = @sizeOf(u64),
4146 else3511 .alignment = @alignOf(u64),
4147 0;3512 .flags = macho.S_LAZY_SYMBOL_POINTERS,
4148 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3513 .prot = macho.PROT.READ | macho.PROT.WRITE,
4149 self.la_symbol_ptr_section_index = try self.initSection(3514 });
4150 "__DATA",3515 self.segment_table_dirty = true;
4151 "__la_symbol_ptr",
4152 needed_size,
4153 alignment,
4154 .{
4155 .flags = macho.S_LAZY_SYMBOL_POINTERS,
4156 },
4157 );
4158 }3516 }
41593517
4160 if (self.data_section_index == null) {3518 if (self.data_section_index == null) {
4161 const needed_size = if (self.mode == .incremental)3519 self.data_section_index = try self.allocateSection("__DATA1", "__data", .{
4162 @sizeOf(u64) * self.base.options.symbol_count_hint3520 .size = @sizeOf(u64),
4163 else3521 .alignment = @alignOf(u64),
4164 0;3522 .flags = macho.S_REGULAR,
4165 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)3523 .prot = macho.PROT.READ | macho.PROT.WRITE,
4166 self.data_section_index = try self.initSection(3524 });
4167 "__DATA",3525 self.segment_table_dirty = true;
4168 "__data",
4169 needed_size,
4170 alignment,
4171 .{},
4172 );
4173 }3526 }
41743527
4175 if (self.linkedit_segment_cmd_index == null) {3528 if (self.linkedit_segment_cmd_index == null) {
4176 self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);3529 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 }
4185 try self.segments.append(gpa, .{3530 try self.segments.append(gpa, .{
4186 .segname = makeStaticString("__LINKEDIT"),3531 .segname = makeStaticString("__LINKEDIT"),
4187 .vmaddr = vmaddr,
4188 .fileoff = fileoff,
4189 .maxprot = macho.PROT.READ,3532 .maxprot = macho.PROT.READ,
4190 .initprot = macho.PROT.READ,3533 .initprot = macho.PROT.READ,
4191 .cmdsize = @sizeOf(macho.segment_command_64),3534 .cmdsize = @sizeOf(macho.segment_command_64),
...@@ -4283,7 +3626,19 @@ fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {...@@ -4283,7 +3626,19 @@ fn calcLCsSize(self: *MachO, assume_max_path_len: bool) !u32 {
4283 return @intCast(u32, sizeofcmds);3626 return @intCast(u32, sizeofcmds);
4284}3627}
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 {
4287 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);3642 var padding: u32 = (try self.calcLCsSize(false)) + (self.base.options.headerpad_size orelse 0);
4288 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});3643 log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)});
42893644
...@@ -4300,444 +3655,118 @@ fn calcMinHeaderPad(self: *MachO) !u64 {...@@ -4300,444 +3655,118 @@ fn calcMinHeaderPad(self: *MachO) !u64 {
4300 return offset;3655 return offset;
4301}3656}
43023657
4303fn allocateSegments(self: *MachO) !void {3658fn allocateSection(self: *MachO, segname: []const u8, sectname: []const u8, opts: struct {
4304 try self.allocateSegment(self.text_segment_cmd_index, &.{3659 size: u64 = 0,
4305 self.pagezero_segment_cmd_index,3660 alignment: u32 = 0,
4306 }, try self.calcMinHeaderPad());3661 prot: macho.vm_prot_t = macho.PROT.NONE,
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 {
4392 flags: u32 = macho.S_REGULAR,3662 flags: u32 = macho.S_REGULAR,
4393 reserved1: u32 = 0,
4394 reserved2: u32 = 0,3663 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(3687 const seg = try self.segments.addOne(gpa);
4398 self: *MachO,3688 seg.* = .{
4399 segname: []const u8,3689 .segname = makeStaticString(segname),
4400 sectname: []const u8,3690 .vmaddr = vmaddr,
4401 size: u64,3691 .vmsize = vmsize,
4402 alignment: u32,3692 .fileoff = off,
4403 opts: InitSectionOpts,3693 .filesize = vmsize,
4404) !u8 {3694 .maxprot = opts.prot,
4405 const segment_id = self.getSegmentByName(segname).?;3695 .initprot = opts.prot,
4406 const seg = &self.segments.items[segment_id];3696 .nsects = 1,
4407 const index = try self.insertSection(segment_id, .{3697 .cmdsize = @sizeOf(macho.segment_command_64) + @sizeOf(macho.section_64),
3698 };
3699
3700 var section = macho.section_64{
4408 .sectname = makeStaticString(sectname),3701 .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),
4410 .flags = opts.flags,3707 .flags = opts.flags,
4411 .reserved1 = opts.reserved1,
4412 .reserved2 = opts.reserved2,3708 .reserved2 = opts.reserved2,
4413 });3709 };
4414 seg.cmdsize += @sizeOf(macho.section_64);3710 assert(!section.isZerofill()); // TODO zerofill sections
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}
44553711
4456fn getSectionPrecedence(header: macho.section_64) u4 {3712 try self.sections.append(gpa, .{
4457 if (header.isCode()) {3713 .segment_index = segment_id,
4458 if (mem.eql(u8, "__text", header.sectName())) return 0x0;3714 .header = section,
4459 if (header.@"type"() == macho.S_SYMBOL_STUBS) return 0x1;3715 });
4460 return 0x2;3716 return section_id;
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 }
4476}3717}
44773718
4478fn insertSection(self: *MachO, segment_index: u8, header: macho.section_64) !u8 {3719fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void {
4479 const precedence = getSectionPrecedence(header);3720 const header = &self.sections.items(.header)[sect_id];
4480 const indexes = self.getSectionIndexes(segment_index);3721 const segment = self.getSegmentPtr(sect_id);
4481 const insertion_index = for (self.sections.items(.header)[indexes.start..indexes.end]) |hdr, i| {3722 const increased_size = padToIdeal(needed_size);
4482 if (getSectionPrecedence(hdr) > precedence) break @intCast(u8, i + indexes.start);3723 const old_aligned_end = segment.vmaddr + segment.vmsize;
4483 } else indexes.end;3724 const new_aligned_end = segment.vmaddr + mem.alignForwardGeneric(u64, increased_size, self.page_size);
4484 log.debug("inserting section '{s},{s}' at index {d}", .{3725 const diff = new_aligned_end - old_aligned_end;
3726 log.debug("shifting every segment after {s},{s} in virtual memory by {x}", .{
4485 header.segName(),3727 header.segName(),
4486 header.sectName(),3728 header.sectName(),
4487 insertion_index,3729 diff,
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,
4503 });3730 });
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| {3732 // TODO: enforce order by increasing VM addresses in self.sections container.
4520 const contained_sym = self.getSymbolPtr(.{3733 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
4521 .sym_index = sym_at_off.sym_index,3734 const index = @intCast(u8, sect_id + 1 + next_sect_id);
4522 .file = atom.file,3735 const maybe_last_atom = &self.sections.items(.last_atom)[index];
4523 });3736 const next_segment = self.getSegmentPtr(index);
4524 contained_sym.n_sect = start + 1;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;
4525 }3749 }
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));
4613 }3750 }
4614 }3751 }
4615}3752}
46163753
4617fn growSection(self: *MachO, sect_id: u8, new_size: u32) !void {3754fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
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 {
4734 const tracy = trace(@src());3755 const tracy = trace(@src());
4735 defer tracy.end();3756 defer tracy.end();
47363757
3758 const sect_id = atom.getSymbol(self).n_sect - 1;
3759 const segment = self.getSegmentPtr(sect_id);
4737 const header = &self.sections.items(.header)[sect_id];3760 const header = &self.sections.items(.header)[sect_id];
4738 const free_list = &self.sections.items(.free_list)[sect_id];3761 const free_list = &self.sections.items(.free_list)[sect_id];
4739 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];3762 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
4742 // We use these to indicate our intention to update metadata, placing the new atom,3771 // We use these to indicate our intention to update metadata, placing the new atom,
4743 // and possibly removing a free list node.3772 // and possibly removing a free list node.
...@@ -4757,7 +3786,7 @@ fn allocateAtom(...@@ -4757,7 +3786,7 @@ fn allocateAtom(
4757 // Is it enough that we could fit this new atom?3786 // Is it enough that we could fit this new atom?
4758 const sym = big_atom.getSymbol(self);3787 const sym = big_atom.getSymbol(self);
4759 const capacity = big_atom.capacity(self);3788 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;
4761 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;3790 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
4762 const capacity_end_vaddr = sym.n_value + capacity;3791 const capacity_end_vaddr = sym.n_value + capacity;
4763 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;3792 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
...@@ -4787,29 +3816,63 @@ fn allocateAtom(...@@ -4787,29 +3816,63 @@ fn allocateAtom(
4787 break :blk new_start_vaddr;3816 break :blk new_start_vaddr;
4788 } else if (maybe_last_atom.*) |last| {3817 } else if (maybe_last_atom.*) |last| {
4789 const last_symbol = last.getSymbol(self);3818 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;
4791 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;3820 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
4792 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);3821 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
4793 atom_placement = last;3822 atom_placement = last;
4794 break :blk new_start_vaddr;3823 break :blk new_start_vaddr;
4795 } else {3824 } else {
4796 break :blk mem.alignForwardGeneric(u64, header.addr, alignment);3825 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
4797 }3826 }
4798 };3827 };
47993828
4800 const expand_section = atom_placement == null or atom_placement.?.next == null;3829 const expand_section = atom_placement == null or atom_placement.?.next == null;
4801 if (expand_section) {3830 if (expand_section) {
4802 const needed_size = @intCast(u32, (vaddr + new_atom_size) - header.addr);3831 const sect_capacity = self.allocatedSize(header.offset);
4803 try self.growSection(sect_id, needed_size);3832 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
4804 maybe_last_atom.* = atom;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
4805 header.size = needed_size;3864 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;
4806 }3870 }
3871
4807 const align_pow = @intCast(u32, math.log2(alignment));3872 const align_pow = @intCast(u32, math.log2(alignment));
4808 if (header.@"align" < align_pow) {3873 if (header.@"align" < align_pow) {
4809 header.@"align" = align_pow;3874 header.@"align" = align_pow;
4810 }3875 }
4811 atom.size = new_atom_size;
4812 atom.alignment = align_pow;
48133876
4814 if (atom.prev) |prev| {3877 if (atom.prev) |prev| {
4815 prev.next = atom.next;3878 prev.next = atom.next;
...@@ -4833,7 +3896,80 @@ fn allocateAtom(...@@ -4833,7 +3896,80 @@ fn allocateAtom(
4833 return vaddr;3896 return vaddr;
4834}3897}
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;
4837 var section = self.sections.get(sect_id);3973 var section = self.sections.get(sect_id);
4838 if (section.header.size > 0) {3974 if (section.header.size > 0) {
4839 section.last_atom.?.next = atom;3975 section.last_atom.?.next = atom;
...@@ -4872,203 +4008,209 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {...@@ -4872,203 +4008,209 @@ pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
4872 return global_index;4008 return global_index;
4873}4009}
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
4887fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {4011fn writeSegmentHeaders(self: *MachO, ncmds: *u32, writer: anytype) !void {
4888 for (self.segments.items) |seg, i| {4012 for (self.segments.items) |seg, i| {
4889 const indexes = self.getSectionIndexes(@intCast(u8, i));4013 const indexes = self.getSectionIndexes(@intCast(u8, i));
4890 var out_seg = seg;4014 try writer.writeStruct(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);
4907 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {4015 for (self.sections.items(.header)[indexes.start..indexes.end]) |header| {
4908 if (header.size == 0) continue;
4909 try writer.writeStruct(header);4016 try writer.writeStruct(header);
4910 }4017 }
4911
4912 ncmds.* += 1;4018 ncmds.* += 1;
4913 }4019 }
4914}4020}
49154021
4916fn writeLinkeditSegmentData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4022fn 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();
4918 seg.filesize = 0;4024 seg.filesize = 0;
4919 seg.vmsize = 0;4025 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
4921 try self.writeDyldInfoData(ncmds, lc_writer);4037 try self.writeDyldInfoData(ncmds, lc_writer);
4922 try self.writeFunctionStarts(ncmds, lc_writer);
4923 try self.writeDataInCode(ncmds, lc_writer);
4924 try self.writeSymtabs(ncmds, lc_writer);4038 try self.writeSymtabs(ncmds, lc_writer);
49254039
4926 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);4040 seg.vmsize = mem.alignForwardGeneric(u64, seg.filesize, self.page_size);
4927}4041}
49284042
4929fn writeDyldInfoData(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4043const AtomLessThanByAddressContext = struct {
4930 const tracy = trace(@src());4044 macho_file: *MachO,
4931 defer tracy.end();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 {
4933 const gpa = self.base.allocator;4052 const gpa = self.base.allocator;
49344053
4935 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);4054 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
4936 defer rebase_pointers.deinit();4055 defer sorted_atoms_by_address.deinit();
4937 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);4056 try sorted_atoms_by_address.ensureTotalCapacityPrecise(self.rebases.count());
4938 defer bind_pointers.deinit();4057
4939 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);4058 var it = self.rebases.keyIterator();
4940 defer lazy_bind_pointers.deinit();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
4942 const slice = self.sections.slice();4067 const slice = self.sections.slice();
4943 for (slice.items(.last_atom)) |last_atom, sect_id| {4068 for (sorted_atoms_by_address.items) |atom| {
4944 var atom = last_atom orelse continue;4069 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
4945 const segment_index = slice.items(.segment_index)[sect_id];
4946 const header = slice.items(.header)[sect_id];
49474070
4948 if (mem.eql(u8, header.segName(), "__TEXT")) continue; // __TEXT is non-writable4071 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) {4082 pointers.appendAssumeCapacity(.{
4955 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });4083 .offset = base_offset + offset,
4956 const sym = atom.getSymbol(self);4084 .segment_id = segment_index,
4957 const base_offset = sym.n_value - seg.vmaddr;4085 });
49584086 }
4959 for (atom.rebases.items) |offset| {4087 }
4960 log.debug(" | rebase at {x}", .{base_offset + offset});4088}
4961 try rebase_pointers.append(.{
4962 .offset = base_offset + offset,
4963 .segment_id = segment_index,
4964 });
4965 }
49664089
4967 for (atom.bindings.items) |binding| {4090fn collectBindData(self: *MachO, pointers: *std.ArrayList(bind.Pointer), raw_bindings: anytype) !void {
4968 const bind_sym = self.getSymbol(binding.target);4091 const gpa = self.base.allocator;
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 }
49924092
4993 for (atom.lazy_bindings.items) |binding| {4093 var sorted_atoms_by_address = std.ArrayList(*Atom).init(gpa);
4994 const bind_sym = self.getSymbol(binding.target);4094 defer sorted_atoms_by_address.deinit();
4995 const bind_sym_name = self.getSymbolName(binding.target);4095 try sorted_atoms_by_address.ensureTotalCapacityPrecise(raw_bindings.count());
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 }
50184096
5019 if (atom.prev) |prev| {4097 var it = raw_bindings.keyIterator();
5020 atom = prev;4098 while (it.next()) |key_ptr| {
5021 } else break;4099 sorted_atoms_by_address.appendAssumeCapacity(key_ptr.*);
5022 }
5023 }4100 }
50244101
5025 var trie: Trie = .{};4102 std.sort.sort(*Atom, sorted_atoms_by_address.items, AtomLessThanByAddressContext{
5026 defer trie.deinit(gpa);4103 .macho_file = self,
4104 }, atomLessThanByAddress);
50274105
5028 {4106 const slice = self.sections.slice();
5029 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.4107 for (sorted_atoms_by_address.items) |atom| {
5030 log.debug("generating export trie", .{});4108 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
50314109
5032 const text_segment = self.segments.items[self.text_segment_cmd_index.?];4110 const sym = atom.getSymbol(self);
5033 const base_address = text_segment.vmaddr;4111 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
50344112 const seg = self.getSegment(sym.n_sect - 1);
5035 if (self.base.options.output_mode == .Exe) {4113
5036 for (&[_]SymbolWithLoc{4114 const base_offset = sym.n_value - seg.vmaddr;
5037 try self.getEntryPoint(),4115
5038 self.getGlobal("__mh_execute_header").?,4116 const bindings = raw_bindings.get(atom).?;
5039 }) |global| {4117 try pointers.ensureUnusedCapacity(bindings.items.len);
5040 const sym = self.getSymbol(global);4118 for (bindings.items) |binding| {
5041 const sym_name = self.getSymbolName(global);4119 const bind_sym = self.getSymbol(binding.target);
5042 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });4120 const bind_sym_name = self.getSymbolName(binding.target);
5043 try trie.put(gpa, .{4121 const dylib_ordinal = @divTrunc(
5044 .name = sym_name,4122 @bitCast(i16, bind_sym.n_desc),
5045 .vmaddr_offset = sym.n_value - base_address,4123 macho.N_SYMBOL_RESOLVER,
5046 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,4124 );
5047 });4125 var flags: u4 = 0;
5048 }4126 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5049 } else {4127 binding.offset + base_offset,
5050 assert(self.base.options.output_mode == .Lib);4128 bind_sym_name,
5051 for (self.globals.items) |global| {4129 dylib_ordinal,
5052 const sym = self.getSymbol(global);4130 });
50534131 if (bind_sym.weakRef()) {
5054 if (sym.undf()) continue;4132 log.debug(" | marking as weak ref ", .{});
5055 if (!sym.ext()) continue;4133 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
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 });
5065 }4134 }
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 });
5066 }4168 }
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 }
5069 }4186 }
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();
5072 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));4214 const rebase_off = mem.alignForwardGeneric(u64, link_seg.fileoff, @alignOf(u64));
5073 assert(rebase_off == link_seg.fileoff);4215 assert(rebase_off == link_seg.fileoff);
5074 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);4216 const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items);
...@@ -5150,10 +4292,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5150,10 +4292,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5150 {4292 {
5151 var stub_atom = last_atom;4293 var stub_atom = last_atom;
5152 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;4294 var laptr_atom = self.sections.items(.last_atom)[self.la_symbol_ptr_section_index.?].?;
5153 const base_addr = blk: {4295 const base_addr = self.getSegment(self.la_symbol_ptr_section_index.?).vmaddr;
5154 const seg = self.segments.items[self.data_segment_cmd_index.?];
5155 break :blk seg.vmaddr;
5156 };
51574296
5158 while (true) {4297 while (true) {
5159 const laptr_off = blk: {4298 const laptr_off = blk: {
...@@ -5236,154 +4375,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5236,154 +4375,6 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5236 }4375 }
5237}4376}
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
5387fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {4378fn writeSymtabs(self: *MachO, ncmds: *u32, lc_writer: anytype) !void {
5388 var symtab_cmd = macho.symtab_command{4379 var symtab_cmd = macho.symtab_command{
5389 .cmdsize = @sizeOf(macho.symtab_command),4380 .cmdsize = @sizeOf(macho.symtab_command),
...@@ -5448,10 +4439,6 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -5448,10 +4439,6 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
5448 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));4439 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
5449 try locals.append(out_sym);4440 try locals.append(out_sym);
5450 }4441 }
5451
5452 if (!self.base.options.strip) {
5453 try self.generateSymbolStabs(object, &locals);
5454 }
5455 }4442 }
54564443
5457 var exports = std.ArrayList(macho.nlist_64).init(gpa);4444 var exports = std.ArrayList(macho.nlist_64).init(gpa);
...@@ -5487,7 +4474,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -5487,7 +4474,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
5487 const nimports = @intCast(u32, imports.items.len);4474 const nimports = @intCast(u32, imports.items.len);
5488 const nsyms = nlocals + nexports + nimports;4475 const nsyms = nlocals + nexports + nimports;
54894476
5490 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4477 const seg = self.getLinkeditSegmentPtr();
5491 const offset = mem.alignForwardGeneric(4478 const offset = mem.alignForwardGeneric(
5492 u64,4479 u64,
5493 seg.fileoff + seg.filesize,4480 seg.fileoff + seg.filesize,
...@@ -5518,7 +4505,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {...@@ -5518,7 +4505,7 @@ fn writeSymtab(self: *MachO, lc: *macho.symtab_command) !SymtabCtx {
5518}4505}
55194506
5520fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {4507fn writeStrtab(self: *MachO, lc: *macho.symtab_command) !void {
5521 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4508 const seg = self.getLinkeditSegmentPtr();
5522 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));4509 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5523 const needed_size = self.strtab.buffer.items.len;4510 const needed_size = self.strtab.buffer.items.len;
5524 seg.filesize = offset + needed_size - seg.fileoff;4511 seg.filesize = offset + needed_size - seg.fileoff;
...@@ -5546,7 +4533,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi...@@ -5546,7 +4533,7 @@ fn writeDysymtab(self: *MachO, ctx: SymtabCtx, lc: *macho.dysymtab_command) !voi
5546 const iextdefsym = ctx.nlocalsym;4533 const iextdefsym = ctx.nlocalsym;
5547 const iundefsym = iextdefsym + ctx.nextdefsym;4534 const iundefsym = iextdefsym + ctx.nextdefsym;
55484535
5549 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4536 const seg = self.getLinkeditSegmentPtr();
5550 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));4537 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, @alignOf(u64));
5551 const needed_size = nindirectsyms * @sizeOf(u32);4538 const needed_size = nindirectsyms * @sizeOf(u32);
5552 seg.filesize = offset + needed_size - seg.fileoff;4539 seg.filesize = offset + needed_size - seg.fileoff;
...@@ -5618,7 +4605,7 @@ fn writeCodeSignaturePadding(...@@ -5618,7 +4605,7 @@ fn writeCodeSignaturePadding(
5618 ncmds: *u32,4605 ncmds: *u32,
5619 lc_writer: anytype,4606 lc_writer: anytype,
5620) !u32 {4607) !u32 {
5621 const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];4608 const seg = self.getLinkeditSegmentPtr();
5622 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file4609 // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file
5623 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L2714610 // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271
5624 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);4611 const offset = mem.alignForwardGeneric(u64, seg.fileoff + seg.filesize, 16);
...@@ -5642,7 +4629,7 @@ fn writeCodeSignaturePadding(...@@ -5642,7 +4629,7 @@ fn writeCodeSignaturePadding(
5642}4629}
56434630
5644fn writeCodeSignature(self: *MachO, code_sig: *CodeSignature, offset: u32) !void {4631fn 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
5647 var buffer = std.ArrayList(u8).init(self.base.allocator);4634 var buffer = std.ArrayList(u8).init(self.base.allocator);
5648 defer buffer.deinit();4635 defer buffer.deinit();
...@@ -5713,6 +4700,57 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {...@@ -5713,6 +4700,57 @@ pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
5713 std.math.maxInt(@TypeOf(actual_size));4700 std.math.maxInt(@TypeOf(actual_size));
5714}4701}
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
5716pub fn makeStaticString(bytes: []const u8) [16]u8 {4754pub fn makeStaticString(bytes: []const u8) [16]u8 {
5717 var buf = [_]u8{0} ** 16;4755 var buf = [_]u8{0} ** 16;
5718 assert(bytes.len <= buf.len);4756 assert(bytes.len <= buf.len);
...@@ -5726,6 +4764,21 @@ fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {...@@ -5726,6 +4764,21 @@ fn getSegmentByName(self: MachO, segname: []const u8) ?u8 {
5726 } else return null;4764 } else return null;
5727}4765}
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
5729pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {4782pub fn getSectionByName(self: MachO, segname: []const u8, sectname: []const u8) ?u8 {
5730 // TODO investigate caching with a hashmap4783 // TODO investigate caching with a hashmap
5731 for (self.sections.items(.header)) |header, i| {4784 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...@@ -5877,161 +4930,6 @@ pub fn findFirst(comptime T: type, haystack: []align(1) const T, start: usize, p
5877 return i;4930 return i;
5878}4931}
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
6035// fn snapshotState(self: *MachO) !void {4933// fn snapshotState(self: *MachO) !void {
6036// const emit = self.base.options.emit orelse {4934// const emit = self.base.options.emit orelse {
6037// log.debug("no emit directory found; skipping snapshot...", .{});4935// log.debug("no emit directory found; skipping snapshot...", .{});
...@@ -6287,7 +5185,7 @@ fn generateSymbolStabsForSymbol(...@@ -6287,7 +5185,7 @@ fn generateSymbolStabsForSymbol(
6287// try writer.writeByte(']');5185// try writer.writeByte(']');
6288// }5186// }
62895187
6290fn logSections(self: *MachO) void {5188pub fn logSections(self: *MachO) void {
6291 log.debug("sections:", .{});5189 log.debug("sections:", .{});
6292 for (self.sections.items(.header)) |header, i| {5190 for (self.sections.items(.header)) |header, i| {
6293 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{5191 log.debug(" sect({d}): {s},{s} @{x}, sizeof({x})", .{
...@@ -6325,7 +5223,7 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {...@@ -6325,7 +5223,7 @@ fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
6325 return buf[0..];5223 return buf[0..];
6326}5224}
63275225
6328fn logSymtab(self: *MachO) void {5226pub fn logSymtab(self: *MachO) void {
6329 var buf: [9]u8 = undefined;5227 var buf: [9]u8 = undefined;
63305228
6331 log.debug("symtab:", .{});5229 log.debug("symtab:", .{});
...@@ -6336,7 +5234,7 @@ fn logSymtab(self: *MachO) void {...@@ -6336,7 +5234,7 @@ fn logSymtab(self: *MachO) void {
6336 const def_index = if (sym.undf() and !sym.tentative())5234 const def_index = if (sym.undf() and !sym.tentative())
6337 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)5235 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
6338 else5236 else
6339 sym.n_sect;5237 sym.n_sect + 1;
6340 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{5238 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
6341 sym_id,5239 sym_id,
6342 object.getString(sym.n_strx),5240 object.getString(sym.n_strx),
...@@ -6353,7 +5251,7 @@ fn logSymtab(self: *MachO) void {...@@ -6353,7 +5251,7 @@ fn logSymtab(self: *MachO) void {
6353 const def_index = if (sym.undf() and !sym.tentative())5251 const def_index = if (sym.undf() and !sym.tentative())
6354 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)5252 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
6355 else5253 else
6356 sym.n_sect;5254 sym.n_sect + 1;
6357 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{5255 log.debug(" %{d}: {?s} @{x} in {s}({d}), {s}", .{
6358 sym_id,5256 sym_id,
6359 self.strtab.get(sym.n_strx),5257 self.strtab.get(sym.n_strx),
...@@ -6418,7 +5316,7 @@ fn logSymtab(self: *MachO) void {...@@ -6418,7 +5316,7 @@ fn logSymtab(self: *MachO) void {
6418 }5316 }
6419}5317}
64205318
6421fn logAtoms(self: *MachO) void {5319pub fn logAtoms(self: *MachO) void {
6422 log.debug("atoms:", .{});5320 log.debug("atoms:", .{});
64235321
6424 const slice = self.sections.slice();5322 const slice = self.sections.slice();
...@@ -6471,19 +5369,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {...@@ -6471,19 +5369,3 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
6471 });5369 });
6472 }5370 }
6473}5371}
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;...@@ -16,6 +16,7 @@ const Arch = std.Target.Cpu.Arch;
16const Dwarf = @import("../Dwarf.zig");16const Dwarf = @import("../Dwarf.zig");
17const MachO = @import("../MachO.zig");17const MachO = @import("../MachO.zig");
18const Object = @import("Object.zig");18const Object = @import("Object.zig");
19const RelocationIncr = @import("Relocation.zig"); // temporary name until we clean up object-file relocation scanning
19const SymbolWithLoc = MachO.SymbolWithLoc;20const SymbolWithLoc = MachO.SymbolWithLoc;
2021
21/// Each decl always gets a local symbol with the fully qualified name.22/// Each decl always gets a local symbol with the fully qualified name.
...@@ -65,8 +66,6 @@ prev: ?*Atom,...@@ -65,8 +66,6 @@ prev: ?*Atom,
6566
66dbg_info_atom: Dwarf.Atom,67dbg_info_atom: Dwarf.Atom,
6768
68dirty: bool = true,
69
70pub const Binding = struct {69pub const Binding = struct {
71 target: SymbolWithLoc,70 target: SymbolWithLoc,
72 offset: u64,71 offset: u64,
...@@ -196,7 +195,7 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {...@@ -196,7 +195,7 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
196 } else {195 } else {
197 // We are the last atom.196 // We are the last atom.
198 // The capacity is limited only by virtual address space.197 // 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);
200 }199 }
201}200}
202201
...@@ -313,13 +312,13 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info,...@@ -313,13 +312,13 @@ pub fn parseRelocs(self: *Atom, relocs: []align(1) const macho.relocation_info,
313 const sect_id = @intCast(u16, rel.r_symbolnum - 1);312 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
314 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {313 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
315 const sect = object.getSourceSection(sect_id);314 const sect = object.getSourceSection(sect_id);
316 const match = (try context.macho_file.getOutputSection(sect)) orelse315 const out_sect_id = (try context.macho_file.getOutputSection(sect)) orelse
317 unreachable;316 unreachable;
318 const sym_index = @intCast(u32, object.symtab.items.len);317 const sym_index = @intCast(u32, object.symtab.items.len);
319 try object.symtab.append(gpa, .{318 try object.symtab.append(gpa, .{
320 .n_strx = 0,319 .n_strx = 0,
321 .n_type = macho.N_SECT,320 .n_type = macho.N_SECT,
322 .n_sect = match + 1,321 .n_sect = out_sect_id + 1,
323 .n_desc = 0,322 .n_desc = 0,
324 .n_value = sect.addr,323 .n_value = sect.addr,
325 });324 });
...@@ -894,3 +893,83 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {...@@ -894,3 +893,83 @@ inline fn isArithmeticOp(inst: *const [4]u8) bool {
894 const group_decode = @truncate(u5, inst[3]);893 const group_decode = @truncate(u5, inst[3]);
895 return ((group_decode >> 2) == 4);894 return ((group_decode >> 2) == 4);
896}895}
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 {...@@ -512,7 +512,7 @@ fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];512 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
513 seg.filesize = aligned_size;513 seg.filesize = aligned_size;
514514
515 try MachO.copyRangeAllOverlappingAlloc(515 try copyRangeAllOverlappingAlloc(
516 self.base.base.allocator,516 self.base.base.allocator,
517 self.file,517 self.file,
518 dwarf_seg.fileoff,518 dwarf_seg.fileoff,
...@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -571,7 +571,7 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];571 const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
572 seg.filesize = aligned_size;572 seg.filesize = aligned_size;
573573
574 try MachO.copyRangeAllOverlappingAlloc(574 try copyRangeAllOverlappingAlloc(
575 self.base.base.allocator,575 self.base.base.allocator,
576 self.file,576 self.file,
577 dwarf_seg.fileoff,577 dwarf_seg.fileoff,
...@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {...@@ -601,3 +601,16 @@ fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
601601
602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);602 try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
603}603}
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(...@@ -220,15 +220,15 @@ fn filterRelocs(
220220
221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {221pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
222 for (self.sections.items) |sect| {222 for (self.sections.items) |sect| {
223 const match = (try macho_file.getOutputSection(sect)) orelse {223 const sect_id = (try macho_file.getOutputSection(sect)) orelse {
224 log.debug(" unhandled section", .{});224 log.debug(" unhandled section", .{});
225 continue;225 continue;
226 };226 };
227 const output = macho_file.sections.items(.header)[match];227 const output = macho_file.sections.items(.header)[sect_id];
228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{228 log.debug("mapping '{s},{s}' into output sect({d}, '{s},{s}')", .{
229 sect.segName(),229 sect.segName(),
230 sect.sectName(),230 sect.sectName(),
231 match + 1,231 sect_id + 1,
232 output.segName(),232 output.segName(),
233 output.sectName(),233 output.sectName(),
234 });234 });
...@@ -236,7 +236,7 @@ pub fn scanInputSections(self: Object, macho_file: *MachO) !void {...@@ -236,7 +236,7 @@ pub fn scanInputSections(self: Object, macho_file: *MachO) !void {
236}236}
237237
238/// Splits object into atoms assuming one-shot linking mode.238/// 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 {
240 assert(macho_file.mode == .one_shot);240 assert(macho_file.mode == .one_shot);
241241
242 const tracy = trace(@src());242 const tracy = trace(@src());
...@@ -249,7 +249,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -249,7 +249,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
249 const in_symtab = self.in_symtab orelse {249 const in_symtab = self.in_symtab orelse {
250 for (self.sections.items) |sect, id| {250 for (self.sections.items) |sect, id| {
251 if (sect.isDebug()) continue;251 if (sect.isDebug()) continue;
252 const match = (try macho_file.getOutputSection(sect)) orelse {252 const out_sect_id = (try macho_file.getOutputSection(sect)) orelse {
253 log.debug(" unhandled section", .{});253 log.debug(" unhandled section", .{});
254 continue;254 continue;
255 };255 };
...@@ -261,7 +261,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -261,7 +261,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
261 try self.symtab.append(gpa, .{261 try self.symtab.append(gpa, .{
262 .n_strx = 0,262 .n_strx = 0,
263 .n_type = macho.N_SECT,263 .n_type = macho.N_SECT,
264 .n_sect = match + 1,264 .n_sect = out_sect_id + 1,
265 .n_desc = 0,265 .n_desc = 0,
266 .n_value = sect.addr,266 .n_value = sect.addr,
267 });267 });
...@@ -282,10 +282,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -282,10 +282,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
282 code,282 code,
283 relocs,283 relocs,
284 &.{},284 &.{},
285 match,285 out_sect_id,
286 sect,286 sect,
287 );287 );
288 try macho_file.addAtomToSection(atom, match);288 try macho_file.addAtomToSection(atom);
289 }289 }
290 return;290 return;
291 };291 };
...@@ -335,15 +335,15 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -335,15 +335,15 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });335 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
336336
337 // Get matching segment/section in the final artifact.337 // 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 {
339 log.debug(" unhandled section", .{});339 log.debug(" unhandled section", .{});
340 continue;340 continue;
341 };341 };
342342
343 log.debug(" output sect({d}, '{s},{s}')", .{343 log.debug(" output sect({d}, '{s},{s}')", .{
344 match + 1,344 out_sect_id + 1,
345 macho_file.sections.items(.header)[match].segName(),345 macho_file.sections.items(.header)[out_sect_id].segName(),
346 macho_file.sections.items(.header)[match].sectName(),346 macho_file.sections.items(.header)[out_sect_id].sectName(),
347 });347 });
348348
349 const cpu_arch = macho_file.base.options.target.cpu.arch;349 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)...@@ -376,7 +376,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
376 try self.symtab.append(gpa, .{376 try self.symtab.append(gpa, .{
377 .n_strx = 0,377 .n_strx = 0,
378 .n_type = macho.N_SECT,378 .n_type = macho.N_SECT,
379 .n_sect = match + 1,379 .n_sect = out_sect_id + 1,
380 .n_desc = 0,380 .n_desc = 0,
381 .n_value = sect.addr,381 .n_value = sect.addr,
382 });382 });
...@@ -397,10 +397,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -397,10 +397,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
397 atom_code,397 atom_code,
398 relocs,398 relocs,
399 &.{},399 &.{},
400 match,400 out_sect_id,
401 sect,401 sect,
402 );402 );
403 try macho_file.addAtomToSection(atom, match);403 try macho_file.addAtomToSection(atom);
404 }404 }
405405
406 var next_sym_count: usize = 0;406 var next_sym_count: usize = 0;
...@@ -452,7 +452,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -452,7 +452,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
452 atom_code,452 atom_code,
453 relocs,453 relocs,
454 sorted_atom_syms.items[1..],454 sorted_atom_syms.items[1..],
455 match,455 out_sect_id,
456 sect,456 sect,
457 );457 );
458458
...@@ -465,7 +465,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -465,7 +465,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
465 try self.symtab.append(gpa, .{465 try self.symtab.append(gpa, .{
466 .n_strx = 0,466 .n_strx = 0,
467 .n_type = macho.N_SECT,467 .n_type = macho.N_SECT,
468 .n_sect = match + 1,468 .n_sect = out_sect_id + 1,
469 .n_desc = 0,469 .n_desc = 0,
470 .n_value = addr,470 .n_value = addr,
471 });471 });
...@@ -479,7 +479,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -479,7 +479,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
479 try self.atom_by_index_table.put(gpa, alias, atom);479 try self.atom_by_index_table.put(gpa, alias, atom);
480 }480 }
481481
482 try macho_file.addAtomToSection(atom, match);482 try macho_file.addAtomToSection(atom);
483 }483 }
484 } else {484 } else {
485 // If there is no symbol to refer to this atom, we create485 // 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)...@@ -490,7 +490,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
490 try self.symtab.append(gpa, .{490 try self.symtab.append(gpa, .{
491 .n_strx = 0,491 .n_strx = 0,
492 .n_type = macho.N_SECT,492 .n_type = macho.N_SECT,
493 .n_sect = match + 1,493 .n_sect = out_sect_id + 1,
494 .n_desc = 0,494 .n_desc = 0,
495 .n_value = sect.addr,495 .n_value = sect.addr,
496 });496 });
...@@ -506,10 +506,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)...@@ -506,10 +506,10 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
506 code,506 code,
507 relocs,507 relocs,
508 filtered_syms,508 filtered_syms,
509 match,509 out_sect_id,
510 sect,510 sect,
511 );511 );
512 try macho_file.addAtomToSection(atom, match);512 try macho_file.addAtomToSection(atom);
513 }513 }
514 }514 }
515}515}
...@@ -524,21 +524,21 @@ fn createAtomFromSubsection(...@@ -524,21 +524,21 @@ fn createAtomFromSubsection(
524 code: ?[]const u8,524 code: ?[]const u8,
525 relocs: []align(1) const macho.relocation_info,525 relocs: []align(1) const macho.relocation_info,
526 indexes: []const SymbolAtIndex,526 indexes: []const SymbolAtIndex,
527 match: u8,527 out_sect_id: u8,
528 sect: macho.section_64,528 sect: macho.section_64,
529) !*Atom {529) !*Atom {
530 const gpa = macho_file.base.allocator;530 const gpa = macho_file.base.allocator;
531 const sym = self.symtab.items[sym_index];531 const sym = self.symtab.items[sym_index];
532 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);532 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
533 atom.file = object_id;533 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
536 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{536 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
537 sym_index,537 sym_index,
538 self.getString(sym.n_strx),538 self.getString(sym.n_strx),
539 match + 1,539 out_sect_id + 1,
540 macho_file.sections.items(.header)[match].segName(),540 macho_file.sections.items(.header)[out_sect_id].segName(),
541 macho_file.sections.items(.header)[match].sectName(),541 macho_file.sections.items(.header)[out_sect_id].sectName(),
542 object_id,542 object_id,
543 });543 });
544544
...@@ -566,7 +566,7 @@ fn createAtomFromSubsection(...@@ -566,7 +566,7 @@ fn createAtomFromSubsection(
566 try atom.contained.ensureTotalCapacity(gpa, indexes.len);566 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
567 for (indexes) |inner_sym_index| {567 for (indexes) |inner_sym_index| {
568 const inner_sym = &self.symtab.items[inner_sym_index.index];568 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;
570 atom.contained.appendAssumeCapacity(.{570 atom.contained.appendAssumeCapacity(.{
571 .sym_index = inner_sym_index.index,571 .sym_index = inner_sym_index.index,
572 .offset = inner_sym.n_value - sym.n_value,572 .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...@@ -233,7 +233,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
233 if (sym.n_desc != MachO.N_DESC_GCED) continue;233 if (sym.n_desc != MachO.N_DESC_GCED) continue;
234234
235 // TODO tombstone235 // TODO tombstone
236 const atom = entry.getAtom(macho_file);236 const atom = entry.getAtom(macho_file).?;
237 const match = sym.n_sect - 1;237 const match = sym.n_sect - 1;
238 removeAtomFromSection(atom, match, macho_file);238 removeAtomFromSection(atom, match, macho_file);
239 _ = try gc_sections.put(match, {});239 _ = try gc_sections.put(match, {});
...@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -245,7 +245,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
245 if (sym.n_desc != MachO.N_DESC_GCED) continue;245 if (sym.n_desc != MachO.N_DESC_GCED) continue;
246246
247 // TODO tombstone247 // TODO tombstone
248 const atom = entry.getAtom(macho_file);248 const atom = entry.getAtom(macho_file).?;
249 const match = sym.n_sect - 1;249 const match = sym.n_sect - 1;
250 removeAtomFromSection(atom, match, macho_file);250 removeAtomFromSection(atom, match, macho_file);
251 _ = try gc_sections.put(match, {});251 _ = try gc_sections.put(match, {});
...@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac...@@ -257,7 +257,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
257 if (sym.n_desc != MachO.N_DESC_GCED) continue;257 if (sym.n_desc != MachO.N_DESC_GCED) continue;
258258
259 // TODO tombstone259 // TODO tombstone
260 const atom = entry.getAtom(macho_file);260 const atom = entry.getAtom(macho_file).?;
261 const match = sym.n_sect - 1;261 const match = sym.n_sect - 1;
262 removeAtomFromSection(atom, match, macho_file);262 removeAtomFromSection(atom, match, macho_file);
263 _ = try gc_sections.put(match, {});263 _ = 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}