authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-23 00:01:09-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-23 00:01:09-07:00
loga8bfddfaeae4f48c044fd134aac1e977e6a161f8
tree4b1b000767ba641f5ca7f7c40aa17e29991e9114
parenta035d75a1750e59e43bb9122f33d8586ed1ee385
parentcf6cfc830db89e0031200d1a16c93eb7801cb911
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12140 from ziglang/macho-gc-sections

macho: add support for `-dead_strip` (GC sections) and simplify symbol resolution

22 files changed, 3507 insertions(+), 2868 deletions(-)

CMakeLists.txt+2
...@@ -757,10 +757,12 @@ set(ZIG_STAGE2_SOURCES...@@ -757,10 +757,12 @@ set(ZIG_STAGE2_SOURCES
757 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"757 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
758 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"758 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
759 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"759 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
760 "${CMAKE_SOURCE_DIR}/src/link/MachO/dead_strip.zig"
760 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"761 "${CMAKE_SOURCE_DIR}/src/link/Plan9.zig"
761 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"762 "${CMAKE_SOURCE_DIR}/src/link/Plan9/aout.zig"
762 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"763 "${CMAKE_SOURCE_DIR}/src/link/Wasm.zig"
763 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"764 "${CMAKE_SOURCE_DIR}/src/link/msdos-stub.bin"
765 "${CMAKE_SOURCE_DIR}/src/link/strtab.zig"
764 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"766 "${CMAKE_SOURCE_DIR}/src/link/tapi.zig"
765 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"767 "${CMAKE_SOURCE_DIR}/src/link/tapi/Tokenizer.zig"
766 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"768 "${CMAKE_SOURCE_DIR}/src/link/tapi/parse.zig"
lib/std/build.zig+7
...@@ -1561,6 +1561,10 @@ pub const LibExeObjStep = struct {...@@ -1561,6 +1561,10 @@ pub const LibExeObjStep = struct {
1561 /// safely garbage-collected during the linking phase.1561 /// safely garbage-collected during the linking phase.
1562 link_function_sections: bool = false,1562 link_function_sections: bool = false,
15631563
1564 /// Remove functions and data that are unreachable by the entry point or
1565 /// exported symbols.
1566 link_gc_sections: ?bool = null,
1567
1564 linker_allow_shlib_undefined: ?bool = null,1568 linker_allow_shlib_undefined: ?bool = null,
15651569
1566 /// Permit read-only relocations in read-only segments. Disallowed by default.1570 /// Permit read-only relocations in read-only segments. Disallowed by default.
...@@ -2705,6 +2709,9 @@ pub const LibExeObjStep = struct {...@@ -2705,6 +2709,9 @@ pub const LibExeObjStep = struct {
2705 if (self.link_function_sections) {2709 if (self.link_function_sections) {
2706 try zig_args.append("-ffunction-sections");2710 try zig_args.append("-ffunction-sections");
2707 }2711 }
2712 if (self.link_gc_sections) |x| {
2713 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
2714 }
2708 if (self.linker_allow_shlib_undefined) |x| {2715 if (self.linker_allow_shlib_undefined) |x| {
2709 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");2716 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
2710 }2717 }
lib/std/build/CheckObjectStep.zig+33-2
...@@ -50,7 +50,7 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe...@@ -50,7 +50,7 @@ pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Targe
50/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively50/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
51/// they could then be added with this simple program `vmaddr entryoff +`.51/// they could then be added with this simple program `vmaddr entryoff +`.
52const Action = struct {52const Action = struct {
53 tag: enum { match, compute_cmp },53 tag: enum { match, not_present, compute_cmp },
54 phrase: []const u8,54 phrase: []const u8,
55 expected: ?ComputeCompareExpected = null,55 expected: ?ComputeCompareExpected = null,
5656
...@@ -63,7 +63,7 @@ const Action = struct {...@@ -63,7 +63,7 @@ const Action = struct {
63 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`63 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
64 /// in that order with other letters in between64 /// in that order with other letters in between
65 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {65 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
66 assert(act.tag == .match);66 assert(act.tag == .match or act.tag == .not_present);
6767
68 var candidate_var: ?struct { name: []const u8, value: u64 } = null;68 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
69 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");69 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
...@@ -202,6 +202,13 @@ const Check = struct {...@@ -202,6 +202,13 @@ const Check = struct {
202 }) catch unreachable;202 }) catch unreachable;
203 }203 }
204204
205 fn notPresent(self: *Check, phrase: []const u8) void {
206 self.actions.append(.{
207 .tag = .not_present,
208 .phrase = self.builder.dupe(phrase),
209 }) catch unreachable;
210 }
211
205 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {212 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
206 self.actions.append(.{213 self.actions.append(.{
207 .tag = .compute_cmp,214 .tag = .compute_cmp,
...@@ -226,6 +233,15 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {...@@ -226,6 +233,15 @@ pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
226 last.match(phrase);233 last.match(phrase);
227}234}
228235
236/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
237/// however ensures there is no matching phrase in the output.
238/// Asserts at least one check already exists.
239pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
240 assert(self.checks.items.len > 0);
241 const last = &self.checks.items[self.checks.items.len - 1];
242 last.notPresent(phrase);
243}
244
229/// Creates a new check checking specifically symbol table parsed and dumped from the object245/// Creates a new check checking specifically symbol table parsed and dumped from the object
230/// file.246/// file.
231/// Issuing this check will force parsing and dumping of the symbol table.247/// Issuing this check will force parsing and dumping of the symbol table.
...@@ -293,6 +309,21 @@ fn make(step: *Step) !void {...@@ -293,6 +309,21 @@ fn make(step: *Step) !void {
293 return error.TestFailed;309 return error.TestFailed;
294 }310 }
295 },311 },
312 .not_present => {
313 while (it.next()) |line| {
314 if (try act.match(line, &vars)) {
315 std.debug.print(
316 \\
317 \\========= Expected not to find: ===================
318 \\{s}
319 \\========= But parsed file does contain it: ========
320 \\{s}
321 \\
322 , .{ act.phrase, output });
323 return error.TestFailed;
324 }
325 }
326 },
296 .compute_cmp => {327 .compute_cmp => {
297 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {328 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
298 error.UnknownVariable => {329 error.UnknownVariable => {
src/arch/aarch64/CodeGen.zig+9-9
...@@ -3174,7 +3174,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3174,7 +3174,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3174 const func = func_payload.data;3174 const func = func_payload.data;
3175 const fn_owner_decl = mod.declPtr(func.owner_decl);3175 const fn_owner_decl = mod.declPtr(func.owner_decl);
3176 try self.genSetReg(Type.initTag(.u64), .x30, .{3176 try self.genSetReg(Type.initTag(.u64), .x30, .{
3177 .got_load = fn_owner_decl.link.macho.local_sym_index,3177 .got_load = fn_owner_decl.link.macho.sym_index,
3178 });3178 });
3179 // blr x303179 // blr x30
3180 _ = try self.addInst(.{3180 _ = try self.addInst(.{
...@@ -3190,14 +3190,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3190,14 +3190,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3190 lib_name,3190 lib_name,
3191 });3191 });
3192 }3192 }
3193 const n_strx = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));3193 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
31943194
3195 _ = try self.addInst(.{3195 _ = try self.addInst(.{
3196 .tag = .call_extern,3196 .tag = .call_extern,
3197 .data = .{3197 .data = .{
3198 .extern_fn = .{3198 .relocation = .{
3199 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,3199 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
3200 .sym_name = n_strx,3200 .sym_index = sym_index,
3201 },3201 },
3202 },3202 },
3203 });3203 });
...@@ -4157,7 +4157,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4157,7 +4157,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4157 .data = .{4157 .data = .{
4158 .payload = try self.addExtra(Mir.LoadMemoryPie{4158 .payload = try self.addExtra(Mir.LoadMemoryPie{
4159 .register = @enumToInt(src_reg),4159 .register = @enumToInt(src_reg),
4160 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,4160 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4161 .sym_index = sym_index,4161 .sym_index = sym_index,
4162 }),4162 }),
4163 },4163 },
...@@ -4270,7 +4270,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4270,7 +4270,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4270 .data = .{4270 .data = .{
4271 .payload = try self.addExtra(Mir.LoadMemoryPie{4271 .payload = try self.addExtra(Mir.LoadMemoryPie{
4272 .register = @enumToInt(reg),4272 .register = @enumToInt(reg),
4273 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,4273 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4274 .sym_index = sym_index,4274 .sym_index = sym_index,
4275 }),4275 }),
4276 },4276 },
...@@ -4578,8 +4578,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -4578,8 +4578,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
4578 } else if (self.bin_file.cast(link.File.MachO)) |_| {4578 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4579 // Because MachO is PIE-always-on, we defer memory address resolution until4579 // Because MachO is PIE-always-on, we defer memory address resolution until
4580 // the linker has enough info to perform relocations.4580 // the linker has enough info to perform relocations.
4581 assert(decl.link.macho.local_sym_index != 0);4581 assert(decl.link.macho.sym_index != 0);
4582 return MCValue{ .got_load = decl.link.macho.local_sym_index };4582 return MCValue{ .got_load = decl.link.macho.sym_index };
4583 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4583 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4584 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;4584 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
4585 return MCValue{ .memory = got_addr };4585 return MCValue{ .memory = got_addr };
src/arch/aarch64/Emit.zig+8-5
...@@ -649,7 +649,7 @@ fn mirDebugEpilogueBegin(self: *Emit) !void {...@@ -649,7 +649,7 @@ fn mirDebugEpilogueBegin(self: *Emit) !void {
649649
650fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {650fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
651 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);651 assert(emit.mir.instructions.items(.tag)[inst] == .call_extern);
652 const extern_fn = emit.mir.instructions.items(.data)[inst].extern_fn;652 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
653653
654 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {654 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
655 const offset = blk: {655 const offset = blk: {
...@@ -659,10 +659,13 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -659,10 +659,13 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
659 break :blk offset;659 break :blk offset;
660 };660 };
661 // Add relocation to the decl.661 // Add relocation to the decl.
662 const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?;662 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
663 try atom.relocs.append(emit.bin_file.allocator, .{663 try atom.relocs.append(emit.bin_file.allocator, .{
664 .offset = offset,664 .offset = offset,
665 .target = .{ .global = extern_fn.sym_name },665 .target = .{
666 .sym_index = relocation.sym_index,
667 .file = null,
668 },
666 .addend = 0,669 .addend = 0,
667 .subtractor = null,670 .subtractor = null,
668 .pcrel = true,671 .pcrel = true,
...@@ -864,7 +867,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -864,7 +867,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
864 // Page reloc for adrp instruction.867 // Page reloc for adrp instruction.
865 try atom.relocs.append(emit.bin_file.allocator, .{868 try atom.relocs.append(emit.bin_file.allocator, .{
866 .offset = offset,869 .offset = offset,
867 .target = .{ .local = data.sym_index },870 .target = .{ .sym_index = data.sym_index, .file = null },
868 .addend = 0,871 .addend = 0,
869 .subtractor = null,872 .subtractor = null,
870 .pcrel = true,873 .pcrel = true,
...@@ -882,7 +885,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -882,7 +885,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882 // Pageoff reloc for adrp instruction.885 // Pageoff reloc for adrp instruction.
883 try atom.relocs.append(emit.bin_file.allocator, .{886 try atom.relocs.append(emit.bin_file.allocator, .{
884 .offset = offset + 4,887 .offset = offset + 4,
885 .target = .{ .local = data.sym_index },888 .target = .{ .sym_index = data.sym_index, .file = null },
886 .addend = 0,889 .addend = 0,
887 .subtractor = null,890 .subtractor = null,
888 .pcrel = false,891 .pcrel = false,
src/arch/aarch64/Mir.zig+5-3
...@@ -225,14 +225,16 @@ pub const Inst = struct {...@@ -225,14 +225,16 @@ pub const Inst = struct {
225 ///225 ///
226 /// Used by e.g. b226 /// Used by e.g. b
227 inst: Index,227 inst: Index,
228 /// An extern function228 /// Relocation for the linker where:
229 /// * `atom_index` is the index of the source
230 /// * `sym_index` is the index of the target
229 ///231 ///
230 /// Used by e.g. call_extern232 /// Used by e.g. call_extern
231 extern_fn: struct {233 relocation: struct {
232 /// Index of the containing atom.234 /// Index of the containing atom.
233 atom_index: u32,235 atom_index: u32,
234 /// Index into the linker's string table.236 /// Index into the linker's string table.
235 sym_name: u32,237 sym_index: u32,
236 },238 },
237 /// A 16-bit immediate value.239 /// A 16-bit immediate value.
238 ///240 ///
src/arch/riscv64/CodeGen.zig+1-1
...@@ -2563,7 +2563,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -2563,7 +2563,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
2563 } else if (self.bin_file.cast(link.File.MachO)) |_| {2563 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2564 // TODO I'm hacking my way through here by repurposing .memory for storing2564 // TODO I'm hacking my way through here by repurposing .memory for storing
2565 // index to the GOT target symbol index.2565 // index to the GOT target symbol index.
2566 return MCValue{ .memory = decl.link.macho.local_sym_index };2566 return MCValue{ .memory = decl.link.macho.sym_index };
2567 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {2567 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2568 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;2568 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2569 return MCValue{ .memory = got_addr };2569 return MCValue{ .memory = got_addr };
src/arch/x86_64/CodeGen.zig+9-9
...@@ -2644,8 +2644,8 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2644,8 +2644,8 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2644 .flags = flags,2644 .flags = flags,
2645 }),2645 }),
2646 .data = .{2646 .data = .{
2647 .load_reloc = .{2647 .relocation = .{
2648 .atom_index = fn_owner_decl.link.macho.local_sym_index,2648 .atom_index = fn_owner_decl.link.macho.sym_index,
2649 .sym_index = sym_index,2649 .sym_index = sym_index,
2650 },2650 },
2651 },2651 },
...@@ -3977,7 +3977,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3977,7 +3977,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3977 const func = func_payload.data;3977 const func = func_payload.data;
3978 const fn_owner_decl = mod.declPtr(func.owner_decl);3978 const fn_owner_decl = mod.declPtr(func.owner_decl);
3979 try self.genSetReg(Type.initTag(.usize), .rax, .{3979 try self.genSetReg(Type.initTag(.usize), .rax, .{
3980 .got_load = fn_owner_decl.link.macho.local_sym_index,3980 .got_load = fn_owner_decl.link.macho.sym_index,
3981 });3981 });
3982 // callq *%rax3982 // callq *%rax
3983 _ = try self.addInst(.{3983 _ = try self.addInst(.{
...@@ -3997,14 +3997,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3997,14 +3997,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3997 lib_name,3997 lib_name,
3998 });3998 });
3999 }3999 }
4000 const n_strx = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));4000 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4001 _ = try self.addInst(.{4001 _ = try self.addInst(.{
4002 .tag = .call_extern,4002 .tag = .call_extern,
4003 .ops = undefined,4003 .ops = undefined,
4004 .data = .{4004 .data = .{
4005 .extern_fn = .{4005 .relocation = .{
4006 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,4006 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4007 .sym_name = n_strx,4007 .sym_index = sym_index,
4008 },4008 },
4009 },4009 },
4010 });4010 });
...@@ -6771,8 +6771,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6771,8 +6771,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6771 } else if (self.bin_file.cast(link.File.MachO)) |_| {6771 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6772 // Because MachO is PIE-always-on, we defer memory address resolution until6772 // Because MachO is PIE-always-on, we defer memory address resolution until
6773 // the linker has enough info to perform relocations.6773 // the linker has enough info to perform relocations.
6774 assert(decl.link.macho.local_sym_index != 0);6774 assert(decl.link.macho.sym_index != 0);
6775 return MCValue{ .got_load = decl.link.macho.local_sym_index };6775 return MCValue{ .got_load = decl.link.macho.sym_index };
6776 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {6776 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6777 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;6777 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
6778 return MCValue{ .memory = got_addr };6778 return MCValue{ .memory = got_addr };
src/arch/x86_64/Emit.zig+10-7
...@@ -982,7 +982,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -982,7 +982,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
982 const tag = emit.mir.instructions.items(.tag)[inst];982 const tag = emit.mir.instructions.items(.tag)[inst];
983 assert(tag == .lea_pie);983 assert(tag == .lea_pie);
984 const ops = emit.mir.instructions.items(.ops)[inst].decode();984 const ops = emit.mir.instructions.items(.ops)[inst].decode();
985 const load_reloc = emit.mir.instructions.items(.data)[inst].load_reloc;985 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
986986
987 // lea reg1, [rip + reloc]987 // lea reg1, [rip + reloc]
988 // RM988 // RM
...@@ -1001,11 +1001,11 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1001,11 +1001,11 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002 else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),1002 else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}),
1003 };1003 };
1004 const atom = macho_file.atom_by_index_table.get(load_reloc.atom_index).?;1004 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
1005 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, load_reloc.sym_index });1005 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, relocation.sym_index });
1006 try atom.relocs.append(emit.bin_file.allocator, .{1006 try atom.relocs.append(emit.bin_file.allocator, .{
1007 .offset = @intCast(u32, end_offset - 4),1007 .offset = @intCast(u32, end_offset - 4),
1008 .target = .{ .local = load_reloc.sym_index },1008 .target = .{ .sym_index = relocation.sym_index, .file = null },
1009 .addend = 0,1009 .addend = 0,
1010 .subtractor = null,1010 .subtractor = null,
1011 .pcrel = true,1011 .pcrel = true,
...@@ -1116,7 +1116,7 @@ fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -1116,7 +1116,7 @@ fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1116fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {1116fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1117 const tag = emit.mir.instructions.items(.tag)[inst];1117 const tag = emit.mir.instructions.items(.tag)[inst];
1118 assert(tag == .call_extern);1118 assert(tag == .call_extern);
1119 const extern_fn = emit.mir.instructions.items(.data)[inst].extern_fn;1119 const relocation = emit.mir.instructions.items(.data)[inst].relocation;
11201120
1121 const offset = blk: {1121 const offset = blk: {
1122 // callq1122 // callq
...@@ -1126,10 +1126,13 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1126,10 +1126,13 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11261126
1127 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1127 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1128 // Add relocation to the decl.1128 // Add relocation to the decl.
1129 const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?;1129 const atom = macho_file.atom_by_index_table.get(relocation.atom_index).?;
1130 try atom.relocs.append(emit.bin_file.allocator, .{1130 try atom.relocs.append(emit.bin_file.allocator, .{
1131 .offset = offset,1131 .offset = offset,
1132 .target = .{ .global = extern_fn.sym_name },1132 .target = .{
1133 .sym_index = relocation.sym_index,
1134 .file = null,
1135 },
1133 .addend = 0,1136 .addend = 0,
1134 .subtractor = null,1137 .subtractor = null,
1135 .pcrel = true,1138 .pcrel = true,
src/arch/x86_64/Mir.zig+6-11
...@@ -181,7 +181,7 @@ pub const Inst = struct {...@@ -181,7 +181,7 @@ pub const Inst = struct {
181 /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation181 /// 0b00 reg1, [rip + reloc] // via GOT emits X86_64_RELOC_GOT relocation
182 /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation182 /// 0b01 reg1, [rip + reloc] // direct load emits X86_64_RELOC_SIGNED relocation
183 /// Notes:183 /// Notes:
184 /// * `Data` contains `load_reloc`184 /// * `Data` contains `relocation`
185 lea_pie,185 lea_pie,
186186
187 /// ops flags: form:187 /// ops flags: form:
...@@ -368,7 +368,7 @@ pub const Inst = struct {...@@ -368,7 +368,7 @@ pub const Inst = struct {
368 /// Pseudo-instructions368 /// Pseudo-instructions
369 /// call extern function369 /// call extern function
370 /// Notes:370 /// Notes:
371 /// * target of the call is stored as `extern_fn` in `Data` union.371 /// * target of the call is stored as `relocation` in `Data` union.
372 call_extern,372 call_extern,
373373
374 /// end of prologue374 /// end of prologue
...@@ -439,15 +439,10 @@ pub const Inst = struct {...@@ -439,15 +439,10 @@ pub const Inst = struct {
439 /// A condition code for use with EFLAGS register.439 /// A condition code for use with EFLAGS register.
440 cc: bits.Condition,440 cc: bits.Condition,
441 },441 },
442 /// An extern function.442 /// Relocation for the linker where:
443 extern_fn: struct {443 /// * `atom_index` is the index of the source
444 /// Index of the containing atom.444 /// * `sym_index` is the index of the target
445 atom_index: u32,445 relocation: struct {
446 /// Index into the linker's string table.
447 sym_name: u32,
448 },
449 /// PIE load relocation.
450 load_reloc: struct {
451 /// Index of the containing atom.446 /// Index of the containing atom.
452 atom_index: u32,447 atom_index: u32,
453 /// Index into the linker's symbol table.448 /// Index into the linker's symbol table.
src/link.zig+1-6
...@@ -544,12 +544,7 @@ pub const File = struct {...@@ -544,12 +544,7 @@ pub const File = struct {
544 switch (base.tag) {544 switch (base.tag) {
545 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),545 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),
546 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index),546 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index),
547 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index) catch |err| switch (err) {547 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index),
548 // remap this error code because we are transitioning away from
549 // `allocateDeclIndexes`.
550 error.Overflow => return error.OutOfMemory,
551 error.OutOfMemory => return error.OutOfMemory,
552 },
553 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),548 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
554 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),549 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
555 .c, .spirv, .nvptx => {},550 .c, .spirv, .nvptx => {},
src/link/MachO.zig+2181-1981
...@@ -4,6 +4,7 @@ const std = @import("std");...@@ -4,6 +4,7 @@ const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const builtin = @import("builtin");5const builtin = @import("builtin");
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const dwarf = std.dwarf;
7const fmt = std.fmt;8const fmt = std.fmt;
8const fs = std.fs;9const fs = std.fs;
9const log = std.log.scoped(.link);10const log = std.log.scoped(.link);
...@@ -15,6 +16,7 @@ const meta = std.meta;...@@ -15,6 +16,7 @@ const meta = std.meta;
15const aarch64 = @import("../arch/aarch64/bits.zig");16const aarch64 = @import("../arch/aarch64/bits.zig");
16const bind = @import("MachO/bind.zig");17const bind = @import("MachO/bind.zig");
17const codegen = @import("../codegen.zig");18const codegen = @import("../codegen.zig");
19const dead_strip = @import("MachO/dead_strip.zig");
18const link = @import("../link.zig");20const link = @import("../link.zig");
19const llvm_backend = @import("../codegen/llvm.zig");21const llvm_backend = @import("../codegen/llvm.zig");
20const target_util = @import("../target.zig");22const target_util = @import("../target.zig");
...@@ -35,8 +37,7 @@ const LibStub = @import("tapi.zig").LibStub;...@@ -35,8 +37,7 @@ const LibStub = @import("tapi.zig").LibStub;
35const Liveness = @import("../Liveness.zig");37const Liveness = @import("../Liveness.zig");
36const LlvmObject = @import("../codegen/llvm.zig").Object;38const LlvmObject = @import("../codegen/llvm.zig").Object;
37const Module = @import("../Module.zig");39const Module = @import("../Module.zig");
38const StringIndexAdapter = std.hash_map.StringIndexAdapter;40const StringTable = @import("strtab.zig").StringTable;
39const StringIndexContext = std.hash_map.StringIndexContext;
40const Trie = @import("MachO/Trie.zig");41const Trie = @import("MachO/Trie.zig");
41const Type = @import("../type.zig").Type;42const Type = @import("../type.zig").Type;
42const TypedValue = @import("../TypedValue.zig");43const TypedValue = @import("../TypedValue.zig");
...@@ -52,6 +53,8 @@ pub const SearchStrategy = enum {...@@ -52,6 +53,8 @@ pub const SearchStrategy = enum {
52 dylibs_first,53 dylibs_first,
53};54};
5455
56pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
57
55const SystemLib = struct {58const SystemLib = struct {
56 needed: bool = false,59 needed: bool = false,
57 weak: bool = false,60 weak: bool = false,
...@@ -69,10 +72,10 @@ d_sym: ?DebugSymbols = null,...@@ -69,10 +72,10 @@ d_sym: ?DebugSymbols = null,
69/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.72/// For x86_64 that's 4KB, whereas for aarch64, that's 16KB.
70page_size: u16,73page_size: u16,
7174
72/// If true, the linker will preallocate several sections and segments before starting the linking75/// Mode of operation: incremental - will preallocate segments/sections and is compatible with
73/// process. This is for example true for stage2 debug builds, however, this is false for stage176/// watch and HCS modes of operation; one_shot - will link relocatables in a traditional, one-shot
74/// and potentially stage2 release builds in the future.77/// fashion (default for LLVM backend).
75needs_prealloc: bool = true,78mode: enum { incremental, one_shot },
7679
77/// The absolute address of the entry point.80/// The absolute address of the entry point.
78entry_addr: ?u64 = null,81entry_addr: ?u64 = null,
...@@ -151,53 +154,48 @@ rustc_section_index: ?u16 = null,...@@ -151,53 +154,48 @@ rustc_section_index: ?u16 = null,
151rustc_section_size: u64 = 0,154rustc_section_size: u64 = 0,
152155
153locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},156locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
154globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},157globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
155undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},158// FIXME Jakub
156symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},159// TODO storing index into globals might be dangerous if we delete a global
157unresolved: std.AutoArrayHashMapUnmanaged(u32, enum {160// while not having everything resolved. Actually, perhaps `unresolved`
158 none,161// should not be stored at the global scope? Is this possible?
159 stub,162// Otherwise, audit if this can be a problem.
160 got,163// An alternative, which I still need to investigate for perf reasons is to
161}) = .{},164// store all global names in an adapted with context strtab.
162tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},165unresolved: std.AutoArrayHashMapUnmanaged(u32, bool) = .{},
163166
164locals_free_list: std.ArrayListUnmanaged(u32) = .{},167locals_free_list: std.ArrayListUnmanaged(u32) = .{},
165globals_free_list: std.ArrayListUnmanaged(u32) = .{},
166168
167dyld_stub_binder_index: ?u32 = null,169dyld_stub_binder_index: ?u32 = null,
168dyld_private_atom: ?*Atom = null,170dyld_private_atom: ?*Atom = null,
169stub_helper_preamble_atom: ?*Atom = null,171stub_helper_preamble_atom: ?*Atom = null,
170172
171mh_execute_header_sym_index: ?u32 = null,173strtab: StringTable(.strtab) = .{},
172dso_handle_sym_index: ?u32 = null,
173
174strtab: std.ArrayListUnmanaged(u8) = .{},
175strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
176174
175// TODO I think synthetic tables are a perfect match for some generic refactoring,
176// and probably reusable between linker backends too.
177tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},177tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
178tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},178tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
179tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{},179tlv_ptr_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
180180
181got_entries: std.ArrayListUnmanaged(Entry) = .{},181got_entries: std.ArrayListUnmanaged(Entry) = .{},
182got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},182got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
183got_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{},183got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
184184
185stubs: std.ArrayListUnmanaged(*Atom) = .{},185stubs: std.ArrayListUnmanaged(Entry) = .{},
186stubs_free_list: std.ArrayListUnmanaged(u32) = .{},186stubs_free_list: std.ArrayListUnmanaged(u32) = .{},
187stubs_table: std.AutoArrayHashMapUnmanaged(u32, u32) = .{},187stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
188188
189error_flags: File.ErrorFlags = File.ErrorFlags{},189error_flags: File.ErrorFlags = File.ErrorFlags{},
190190
191load_commands_dirty: bool = false,191load_commands_dirty: bool = false,
192sections_order_dirty: bool = false,192sections_order_dirty: bool = false,
193has_dices: bool = false,193
194has_stabs: bool = false,
195/// A helper var to indicate if we are at the start of the incremental updates, or194/// A helper var to indicate if we are at the start of the incremental updates, or
196/// already somewhere further along the update-and-run chain.195/// already somewhere further along the update-and-run chain.
197/// TODO once we add opening a prelinked output binary from file, this will become196/// TODO once we add opening a prelinked output binary from file, this will become
198/// obsolete as we will carry on where we left off.197/// obsolete as we will carry on where we left off.
199cold_start: bool = false,198cold_start: bool = true,
200invalidate_relocs: bool = false,
201199
202section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},200section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
203201
...@@ -221,12 +219,10 @@ atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanage...@@ -221,12 +219,10 @@ atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanage
221/// Pointer to the last allocated atom219/// Pointer to the last allocated atom
222atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},220atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
223221
224/// List of atoms that are owned directly by the linker.222/// List of atoms that are either synthetic or map directly to the Zig source program.
225/// Currently these are only atoms that are the result of linking
226/// object files. Atoms which take part in incremental linking are
227/// at present owned by Module.Decl.
228/// TODO consolidate this.
229managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},223managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
224
225/// Table of atoms indexed by the symbol index.
230atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},226atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
231227
232/// Table of unnamed constants associated with a parent `Decl`.228/// Table of unnamed constants associated with a parent `Decl`.
...@@ -257,8 +253,25 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -257,8 +253,25 @@ unnamed_const_atoms: UnnamedConstTable = .{},
257decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},253decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
258254
259const Entry = struct {255const Entry = struct {
260 target: Atom.Relocation.Target,256 target: SymbolWithLoc,
261 atom: *Atom,257 // Index into the synthetic symbol table (i.e., file == null).
258 sym_index: u32,
259
260 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
261 return macho_file.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
262 }
263
264 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
265 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
266 }
267
268 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {
269 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;
270 }
271
272 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
273 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index, .file = null });
274 }
262};275};
263276
264const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));277const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
...@@ -269,15 +282,12 @@ const PendingUpdate = union(enum) {...@@ -269,15 +282,12 @@ const PendingUpdate = union(enum) {
269 add_got_entry: u32,282 add_got_entry: u32,
270};283};
271284
272const SymbolWithLoc = struct {285pub const SymbolWithLoc = struct {
273 // Table where the symbol can be found.286 // Index into the respective symbol table.
274 where: enum {287 sym_index: u32,
275 global,288
276 undef,289 // null means it's a synthetic global.
277 },290 file: ?u32 = null,
278 where_index: u32,
279 local_sym_index: u32 = 0,
280 file: ?u16 = null, // null means Zig module
281};291};
282292
283/// When allocating, the ideal_capacity is calculated by293/// When allocating, the ideal_capacity is calculated by
...@@ -385,7 +395,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -385,7 +395,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
385 .n_desc = 0,395 .n_desc = 0,
386 .n_value = 0,396 .n_value = 0,
387 });397 });
388 try self.strtab.append(allocator, 0);398 try self.strtab.buffer.append(allocator, 0);
389399
390 try self.populateMissingMetadata();400 try self.populateMissingMetadata();
391401
...@@ -406,7 +416,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -406,7 +416,6 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
406 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);416 const requires_adhoc_codesig = cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator);
407 const use_llvm = build_options.have_llvm and options.use_llvm;417 const use_llvm = build_options.have_llvm and options.use_llvm;
408 const use_stage1 = build_options.is_stage1 and options.use_stage1;418 const use_stage1 = build_options.is_stage1 and options.use_stage1;
409 const needs_prealloc = !(use_stage1 or use_llvm or options.cache_mode == .whole);
410419
411 const self = try gpa.create(MachO);420 const self = try gpa.create(MachO);
412 errdefer gpa.destroy(self);421 errdefer gpa.destroy(self);
...@@ -419,14 +428,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {...@@ -419,14 +428,22 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
419 .file = null,428 .file = null,
420 },429 },
421 .page_size = page_size,430 .page_size = page_size,
422 .code_signature = if (requires_adhoc_codesig) CodeSignature.init(page_size) else null,431 .code_signature = if (requires_adhoc_codesig)
423 .needs_prealloc = needs_prealloc,432 CodeSignature.init(page_size)
433 else
434 null,
435 .mode = if (use_stage1 or use_llvm or options.module == null or options.cache_mode == .whole)
436 .one_shot
437 else
438 .incremental,
424 };439 };
425440
426 if (use_llvm and !use_stage1) {441 if (use_llvm and !use_stage1) {
427 self.llvm_object = try LlvmObject.create(gpa, options);442 self.llvm_object = try LlvmObject.create(gpa, options);
428 }443 }
429444
445 log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});
446
430 return self;447 return self;
431}448}
432449
...@@ -448,33 +465,209 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -448,33 +465,209 @@ pub fn flush(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !v
448 return error.TODOImplementWritingStaticLibFiles;465 return error.TODOImplementWritingStaticLibFiles;
449 }466 }
450 }467 }
451 return self.flushModule(comp, prog_node);468
469 switch (self.mode) {
470 .one_shot => return self.linkOneShot(comp, prog_node),
471 .incremental => return self.flushModule(comp, prog_node),
472 }
452}473}
453474
454pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {475pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
455 const tracy = trace(@src());476 const tracy = trace(@src());
456 defer tracy.end();477 defer tracy.end();
457478
458 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;479 if (build_options.have_llvm) {
459
460 if (build_options.have_llvm and !use_stage1) {
461 if (self.llvm_object) |llvm_object| {480 if (self.llvm_object) |llvm_object| {
462 try llvm_object.flushModule(comp, prog_node);481 return try llvm_object.flushModule(comp, prog_node);
463
464 llvm_object.destroy(self.base.allocator);
465 self.llvm_object = null;
466
467 if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) {
468 return;
469 }
470 }482 }
471 }483 }
472484
485 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
486 defer arena_allocator.deinit();
487 const arena = arena_allocator.allocator();
488
473 var sub_prog_node = prog_node.start("MachO Flush", 0);489 var sub_prog_node = prog_node.start("MachO Flush", 0);
474 sub_prog_node.activate();490 sub_prog_node.activate();
475 defer sub_prog_node.end();491 defer sub_prog_node.end();
476492
477 var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);493 const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
494
495 if (self.d_sym) |*d_sym| {
496 try d_sym.dwarf.flushModule(&self.base, module);
497 }
498
499 var libs = std.StringArrayHashMap(SystemLib).init(arena);
500 try self.resolveLibSystem(arena, comp, &.{}, &libs);
501
502 const id_symlink_basename = "zld.id";
503
504 const cache_dir_handle = module.zig_cache_artifact_directory.handle;
505 var man: Cache.Manifest = undefined;
506 defer if (!self.base.options.disable_lld_caching) man.deinit();
507
508 var digest: [Cache.hex_digest_len]u8 = undefined;
509 man = comp.cache_parent.obtain();
510 self.base.releaseLock();
511
512 man.hash.addListOfBytes(libs.keys());
513
514 _ = try man.hit();
515 digest = man.final();
516
517 var prev_digest_buf: [digest.len]u8 = undefined;
518 const prev_digest: []u8 = Cache.readSmallFile(
519 cache_dir_handle,
520 id_symlink_basename,
521 &prev_digest_buf,
522 ) catch |err| blk: {
523 log.debug("MachO Zld new_digest={s} error: {s}", .{
524 std.fmt.fmtSliceHexLower(&digest),
525 @errorName(err),
526 });
527 // Handle this as a cache miss.
528 break :blk prev_digest_buf[0..0];
529 };
530 const cache_miss: bool = cache_miss: {
531 if (mem.eql(u8, prev_digest, &digest)) {
532 log.debug("MachO Zld digest={s} match", .{
533 std.fmt.fmtSliceHexLower(&digest),
534 });
535 if (!self.cold_start) {
536 log.debug(" skipping parsing linker line objects", .{});
537 break :cache_miss false;
538 } else {
539 log.debug(" TODO parse prelinked binary and continue linking where we left off", .{});
540 }
541 }
542 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
543 std.fmt.fmtSliceHexLower(prev_digest),
544 std.fmt.fmtSliceHexLower(&digest),
545 });
546 // We are about to change the output file to be different, so we invalidate the build hash now.
547 cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
548 error.FileNotFound => {},
549 else => |e| return e,
550 };
551 break :cache_miss true;
552 };
553
554 if (cache_miss) {
555 for (self.dylibs.items) |*dylib| {
556 dylib.deinit(self.base.allocator);
557 }
558 self.dylibs.clearRetainingCapacity();
559 self.dylibs_map.clearRetainingCapacity();
560 self.referenced_dylibs.clearRetainingCapacity();
561
562 var dependent_libs = std.fifo.LinearFifo(struct {
563 id: Dylib.Id,
564 parent: u16,
565 }, .Dynamic).init(self.base.allocator);
566 defer dependent_libs.deinit();
567 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
568 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
569 }
570
571 try self.createMhExecuteHeaderSymbol();
572 try self.resolveDyldStubBinder();
573 try self.createDyldPrivateAtom();
574 try self.createStubHelperPreambleAtom();
575 try self.resolveSymbolsInDylibs();
576 try self.addCodeSignatureLC();
577
578 if (self.unresolved.count() > 0) {
579 return error.UndefinedSymbolReference;
580 }
581
582 try self.allocateSpecialSymbols();
583
584 if (build_options.enable_logging) {
585 self.logSymtab();
586 self.logSectionOrdinals();
587 self.logAtoms();
588 }
589
590 try self.writeAtomsIncremental();
591
592 try self.setEntryPoint();
593 try self.updateSectionOrdinals();
594 try self.writeLinkeditSegment();
595
596 if (self.d_sym) |*d_sym| {
597 // Flush debug symbols bundle.
598 try d_sym.flushModule(self.base.allocator, self.base.options);
599 }
600
601 // code signature and entitlements
602 if (self.base.options.entitlements) |path| {
603 if (self.code_signature) |*csig| {
604 try csig.addEntitlements(self.base.allocator, path);
605 csig.code_directory.ident = self.base.options.emit.?.sub_path;
606 } else {
607 var csig = CodeSignature.init(self.page_size);
608 try csig.addEntitlements(self.base.allocator, path);
609 csig.code_directory.ident = self.base.options.emit.?.sub_path;
610 self.code_signature = csig;
611 }
612 }
613
614 if (self.code_signature) |*csig| {
615 csig.clear(self.base.allocator);
616 csig.code_directory.ident = self.base.options.emit.?.sub_path;
617 // Preallocate space for the code signature.
618 // We need to do this at this stage so that we have the load commands with proper values
619 // written out to the file.
620 // The most important here is to have the correct vm and filesize of the __LINKEDIT segment
621 // where the code signature goes into.
622 try self.writeCodeSignaturePadding(csig);
623 }
624
625 try self.writeLoadCommands();
626 try self.writeHeader();
627
628 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
629 log.debug("flushing. no_entry_point_found = true", .{});
630 self.error_flags.no_entry_point_found = true;
631 } else {
632 log.debug("flushing. no_entry_point_found = false", .{});
633 self.error_flags.no_entry_point_found = false;
634 }
635
636 assert(!self.load_commands_dirty);
637
638 if (self.code_signature) |*csig| {
639 try self.writeCodeSignature(csig); // code signing always comes last
640 }
641
642 if (build_options.enable_link_snapshots) {
643 if (self.base.options.enable_link_snapshots)
644 try self.snapshotState();
645 }
646
647 if (cache_miss) {
648 // Update the file with the digest. If it fails we can continue; it only
649 // means that the next invocation will have an unnecessary cache miss.
650 Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| {
651 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
652 };
653 // Again failure here only means an unnecessary cache miss.
654 man.writeManifest() catch |err| {
655 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
656 };
657 // We hang on to this lock so that the output file path can be used without
658 // other processes clobbering it.
659 self.base.lock = man.toOwnedLock();
660 }
661
662 self.cold_start = false;
663}
664
665fn linkOneShot(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) !void {
666 const tracy = trace(@src());
667 defer tracy.end();
668
669 const gpa = self.base.allocator;
670 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
478 defer arena_allocator.deinit();671 defer arena_allocator.deinit();
479 const arena = arena_allocator.allocator();672 const arena = arena_allocator.allocator();
480673
...@@ -484,7 +677,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -484,7 +677,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
484 // If there is no Zig code to compile, then we should skip flushing the output file because it677 // If there is no Zig code to compile, then we should skip flushing the output file because it
485 // will not be part of the linker line anyway.678 // will not be part of the linker line anyway.
486 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {679 const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
487 if (use_stage1) {680 if (self.base.options.use_stage1) {
488 const obj_basename = try std.zig.binNameAlloc(arena, .{681 const obj_basename = try std.zig.binNameAlloc(arena, .{
489 .root_name = self.base.options.root_name,682 .root_name = self.base.options.root_name,
490 .target = self.base.options.target,683 .target = self.base.options.target,
...@@ -501,48 +694,35 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -501,48 +694,35 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
501 }694 }
502 }695 }
503696
504 const obj_basename = self.base.intermediary_basename orelse break :blk null;697 try self.flushModule(comp, prog_node);
505698
506 if (fs.path.dirname(full_out_path)) |dirname| {699 if (fs.path.dirname(full_out_path)) |dirname| {
507 break :blk try fs.path.join(arena, &.{ dirname, obj_basename });700 break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
508 } else {701 } else {
509 break :blk obj_basename;702 break :blk self.base.intermediary_basename.?;
510 }703 }
511 } else null;704 } else null;
512705
513 if (self.d_sym) |*d_sym| {706 var sub_prog_node = prog_node.start("MachO Flush", 0);
514 if (self.base.options.module) |module| {707 sub_prog_node.activate();
515 try d_sym.dwarf.flushModule(&self.base, module);708 sub_prog_node.context.refresh();
516 }709 defer sub_prog_node.end();
517 }
518710
519 const is_lib = self.base.options.output_mode == .Lib;711 const is_lib = self.base.options.output_mode == .Lib;
520 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;712 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
521 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;713 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
522 const stack_size = self.base.options.stack_size_override orelse 0;714 const stack_size = self.base.options.stack_size_override orelse 0;
523 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);715 const is_debug_build = self.base.options.optimize_mode == .Debug;
716 const gc_sections = self.base.options.gc_sections orelse !is_debug_build;
524717
525 const id_symlink_basename = "zld.id";718 const id_symlink_basename = "zld.id";
526 const cache_dir_handle = blk: {
527 if (use_stage1) {
528 break :blk directory.handle;
529 }
530 if (self.base.options.module) |module| {
531 break :blk module.zig_cache_artifact_directory.handle;
532 }
533 break :blk directory.handle;
534 };
535719
536 var man: Cache.Manifest = undefined;720 var man: Cache.Manifest = undefined;
537 defer if (!self.base.options.disable_lld_caching) man.deinit();721 defer if (!self.base.options.disable_lld_caching) man.deinit();
538722
539 var digest: [Cache.hex_digest_len]u8 = undefined;723 var digest: [Cache.hex_digest_len]u8 = undefined;
540 var needs_full_relink = true;
541
542 cache: {
543 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
544 break :cache;
545724
725 if (!self.base.options.disable_lld_caching) {
546 man = comp.cache_parent.obtain();726 man = comp.cache_parent.obtain();
547727
548 // We are about to obtain this lock, so here we give other processes a chance first.728 // We are about to obtain this lock, so here we give other processes a chance first.
...@@ -565,7 +745,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -565,7 +745,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
565 man.hash.addOptional(self.base.options.search_strategy);745 man.hash.addOptional(self.base.options.search_strategy);
566 man.hash.addOptional(self.base.options.headerpad_size);746 man.hash.addOptional(self.base.options.headerpad_size);
567 man.hash.add(self.base.options.headerpad_max_install_names);747 man.hash.add(self.base.options.headerpad_max_install_names);
748 man.hash.add(gc_sections);
568 man.hash.add(self.base.options.dead_strip_dylibs);749 man.hash.add(self.base.options.dead_strip_dylibs);
750 man.hash.add(self.base.options.strip);
569 man.hash.addListOfBytes(self.base.options.lib_dirs);751 man.hash.addListOfBytes(self.base.options.lib_dirs);
570 man.hash.addListOfBytes(self.base.options.framework_dirs);752 man.hash.addListOfBytes(self.base.options.framework_dirs);
571 link.hashAddSystemLibs(&man.hash, self.base.options.frameworks);753 link.hashAddSystemLibs(&man.hash, self.base.options.frameworks);
...@@ -584,7 +766,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -584,7 +766,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
584766
585 var prev_digest_buf: [digest.len]u8 = undefined;767 var prev_digest_buf: [digest.len]u8 = undefined;
586 const prev_digest: []u8 = Cache.readSmallFile(768 const prev_digest: []u8 = Cache.readSmallFile(
587 cache_dir_handle,769 directory.handle,
588 id_symlink_basename,770 id_symlink_basename,
589 &prev_digest_buf,771 &prev_digest_buf,
590 ) catch |err| blk: {772 ) catch |err| blk: {
...@@ -597,23 +779,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -597,23 +779,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
597 };779 };
598 if (mem.eql(u8, prev_digest, &digest)) {780 if (mem.eql(u8, prev_digest, &digest)) {
599 // Hot diggity dog! The output binary is already there.781 // Hot diggity dog! The output binary is already there.
600782 log.debug("MachO Zld digest={s} match - skipping invocation", .{
601 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;783 std.fmt.fmtSliceHexLower(&digest),
602 if (use_llvm or use_stage1) {784 });
603 log.debug("MachO Zld digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});785 self.base.lock = man.toOwnedLock();
604 self.base.lock = man.toOwnedLock();786 return;
605 return;
606 } else {
607 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});
608 if (!self.cold_start) {
609 log.debug(" no need to relink objects", .{});
610 needs_full_relink = false;
611 } else {
612 log.debug(" TODO parse prelinked binary and continue linking where we left off", .{});
613 // TODO until such time however, perform a full relink of objects.
614 needs_full_relink = true;
615 }
616 }
617 }787 }
618 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{788 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
619 std.fmt.fmtSliceHexLower(prev_digest),789 std.fmt.fmtSliceHexLower(prev_digest),
...@@ -621,7 +791,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -621,7 +791,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
621 });791 });
622792
623 // We are about to change the output file to be different, so we invalidate the build hash now.793 // We are about to change the output file to be different, so we invalidate the build hash now.
624 cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) {794 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
625 error.FileNotFound => {},795 error.FileNotFound => {},
626 else => |e| return e,796 else => |e| return e,
627 };797 };
...@@ -652,450 +822,350 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -652,450 +822,350 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
652 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});822 try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
653 }823 }
654 } else {824 } else {
655 if (use_stage1) {825 const sub_path = self.base.options.emit.?.sub_path;
656 const sub_path = self.base.options.emit.?.sub_path;826 self.base.file = try directory.handle.createFile(sub_path, .{
657 self.base.file = try cache_dir_handle.createFile(sub_path, .{827 .truncate = true,
658 .truncate = true,828 .read = true,
659 .read = true,829 .mode = link.determineMode(self.base.options),
660 .mode = link.determineMode(self.base.options),830 });
661 });831 // Index 0 is always a null symbol.
662 // Index 0 is always a null symbol.832 try self.locals.append(gpa, .{
663 try self.locals.append(self.base.allocator, .{833 .n_strx = 0,
664 .n_strx = 0,834 .n_type = 0,
665 .n_type = 0,835 .n_sect = 0,
666 .n_sect = 0,836 .n_desc = 0,
667 .n_desc = 0,837 .n_value = 0,
668 .n_value = 0,838 });
669 });839 try self.strtab.buffer.append(gpa, 0);
670 try self.strtab.append(self.base.allocator, 0);840 try self.populateMissingMetadata();
671 try self.populateMissingMetadata();
672 }
673841
674 var lib_not_found = false;842 var lib_not_found = false;
675 var framework_not_found = false;843 var framework_not_found = false;
676844
677 if (needs_full_relink) {845 // Positional arguments to the linker such as object files and static archives.
678 for (self.objects.items) |*object| {846 var positionals = std.ArrayList([]const u8).init(arena);
679 object.free(self.base.allocator, self);847 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
680 object.deinit(self.base.allocator);
681 }
682 self.objects.clearRetainingCapacity();
683
684 for (self.archives.items) |*archive| {
685 archive.deinit(self.base.allocator);
686 }
687 self.archives.clearRetainingCapacity();
688848
689 for (self.dylibs.items) |*dylib| {849 var must_link_archives = std.StringArrayHashMap(void).init(arena);
690 dylib.deinit(self.base.allocator);850 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
691 }
692 self.dylibs.clearRetainingCapacity();
693 self.dylibs_map.clearRetainingCapacity();
694 self.referenced_dylibs.clearRetainingCapacity();
695
696 {
697 var to_remove = std.ArrayList(u32).init(self.base.allocator);
698 defer to_remove.deinit();
699 var it = self.symbol_resolver.iterator();
700 while (it.next()) |entry| {
701 const key = entry.key_ptr.*;
702 const value = entry.value_ptr.*;
703 if (value.file != null) {
704 try to_remove.append(key);
705 }
706 }
707851
708 for (to_remove.items) |key| {852 for (self.base.options.objects) |obj| {
709 if (self.symbol_resolver.fetchRemove(key)) |entry| {853 if (must_link_archives.contains(obj.path)) continue;
710 const resolv = entry.value;854 if (obj.must_link) {
711 switch (resolv.where) {855 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
712 .global => {856 } else {
713 self.globals_free_list.append(self.base.allocator, resolv.where_index) catch {};857 _ = positionals.appendAssumeCapacity(obj.path);
714 const sym = &self.globals.items[resolv.where_index];
715 sym.n_strx = 0;
716 sym.n_type = 0;
717 sym.n_value = 0;
718 },
719 .undef => {
720 const sym = &self.undefs.items[resolv.where_index];
721 sym.n_strx = 0;
722 sym.n_desc = 0;
723 },
724 }
725 if (self.got_entries_table.get(.{ .global = entry.key })) |i| {
726 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, i)) catch {};
727 self.got_entries.items[i] = .{ .target = .{ .local = 0 }, .atom = undefined };
728 _ = self.got_entries_table.swapRemove(.{ .global = entry.key });
729 }
730 if (self.stubs_table.get(entry.key)) |i| {
731 self.stubs_free_list.append(self.base.allocator, @intCast(u32, i)) catch {};
732 self.stubs.items[i] = undefined;
733 _ = self.stubs_table.swapRemove(entry.key);
734 }
735 }
736 }
737 }858 }
738 // Invalidate all relocs859 }
739 // TODO we only need to invalidate the backlinks to the relinked atoms from
740 // the relocatable object files.
741 self.invalidate_relocs = true;
742
743 // Positional arguments to the linker such as object files and static archives.
744 var positionals = std.ArrayList([]const u8).init(arena);
745 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
746860
747 var must_link_archives = std.StringArrayHashMap(void).init(arena);861 for (comp.c_object_table.keys()) |key| {
748 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);862 try positionals.append(key.status.success.object_path);
863 }
749864
750 for (self.base.options.objects) |obj| {865 if (module_obj_path) |p| {
751 if (must_link_archives.contains(obj.path)) continue;866 try positionals.append(p);
752 if (obj.must_link) {867 }
753 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
754 } else {
755 _ = positionals.appendAssumeCapacity(obj.path);
756 }
757 }
758868
759 for (comp.c_object_table.keys()) |key| {869 if (comp.compiler_rt_lib) |lib| {
760 try positionals.append(key.status.success.object_path);870 try positionals.append(lib.full_object_path);
761 }871 }
762872
763 if (module_obj_path) |p| {873 // libc++ dep
764 try positionals.append(p);874 if (self.base.options.link_libcpp) {
765 }875 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
876 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
877 }
766878
767 if (comp.compiler_rt_lib) |lib| {879 // Shared and static libraries passed via `-l` flag.
768 try positionals.append(lib.full_object_path);880 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
769 }
770881
771 // libc++ dep882 const system_lib_names = self.base.options.system_libs.keys();
772 if (self.base.options.link_libcpp) {883 for (system_lib_names) |system_lib_name| {
773 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);884 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
774 try positionals.append(comp.libcxx_static_lib.?.full_object_path);885 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
886 // case we want to avoid prepending "-l".
887 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
888 try positionals.append(system_lib_name);
889 continue;
775 }890 }
776891
777 // Shared and static libraries passed via `-l` flag.892 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
778 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);893 try candidate_libs.put(system_lib_name, .{
779894 .needed = system_lib_info.needed,
780 const system_lib_names = self.base.options.system_libs.keys();895 .weak = system_lib_info.weak,
781 for (system_lib_names) |system_lib_name| {896 });
782 // By this time, we depend on these libs being dynamically linked libraries and not static libraries897 }
783 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
784 // case we want to avoid prepending "-l".
785 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
786 try positionals.append(system_lib_name);
787 continue;
788 }
789
790 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
791 try candidate_libs.put(system_lib_name, .{
792 .needed = system_lib_info.needed,
793 .weak = system_lib_info.weak,
794 });
795 }
796898
797 var lib_dirs = std.ArrayList([]const u8).init(arena);899 var lib_dirs = std.ArrayList([]const u8).init(arena);
798 for (self.base.options.lib_dirs) |dir| {900 for (self.base.options.lib_dirs) |dir| {
799 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {901 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
800 try lib_dirs.append(search_dir);902 try lib_dirs.append(search_dir);
801 } else {903 } else {
802 log.warn("directory not found for '-L{s}'", .{dir});904 log.warn("directory not found for '-L{s}'", .{dir});
803 }
804 }905 }
906 }
805907
806 var libs = std.StringArrayHashMap(SystemLib).init(arena);908 var libs = std.StringArrayHashMap(SystemLib).init(arena);
807909
808 // Assume ld64 default -search_paths_first if no strategy specified.910 // Assume ld64 default -search_paths_first if no strategy specified.
809 const search_strategy = self.base.options.search_strategy orelse .paths_first;911 const search_strategy = self.base.options.search_strategy orelse .paths_first;
810 outer: for (candidate_libs.keys()) |lib_name| {912 outer: for (candidate_libs.keys()) |lib_name| {
811 switch (search_strategy) {913 switch (search_strategy) {
812 .paths_first => {914 .paths_first => {
813 // Look in each directory for a dylib (stub first), and then for archive915 // Look in each directory for a dylib (stub first), and then for archive
814 for (lib_dirs.items) |dir| {916 for (lib_dirs.items) |dir| {
815 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {917 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
816 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {918 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
817 try libs.put(full_path, candidate_libs.get(lib_name).?);919 try libs.put(full_path, candidate_libs.get(lib_name).?);
818 continue :outer;920 continue :outer;
819 }
820 }921 }
821 } else {
822 log.warn("library not found for '-l{s}'", .{lib_name});
823 lib_not_found = true;
824 }922 }
825 },923 } else {
826 .dylibs_first => {924 log.warn("library not found for '-l{s}'", .{lib_name});
827 // First, look for a dylib in each search dir925 lib_not_found = true;
828 for (lib_dirs.items) |dir| {926 }
829 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {927 },
830 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {928 .dylibs_first => {
831 try libs.put(full_path, candidate_libs.get(lib_name).?);929 // First, look for a dylib in each search dir
832 continue :outer;930 for (lib_dirs.items) |dir| {
833 }931 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
834 }932 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
835 } else for (lib_dirs.items) |dir| {
836 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
837 try libs.put(full_path, candidate_libs.get(lib_name).?);933 try libs.put(full_path, candidate_libs.get(lib_name).?);
838 } else {934 continue :outer;
839 log.warn("library not found for '-l{s}'", .{lib_name});
840 lib_not_found = true;
841 }935 }
842 }936 }
843 },937 } else for (lib_dirs.items) |dir| {
844 }938 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
845 }939 try libs.put(full_path, candidate_libs.get(lib_name).?);
846940 } else {
847 if (lib_not_found) {941 log.warn("library not found for '-l{s}'", .{lib_name});
848 log.warn("Library search paths:", .{});942 lib_not_found = true;
849 for (lib_dirs.items) |dir| {
850 log.warn(" {s}", .{dir});
851 }
852 }
853
854 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
855 var libsystem_available = false;
856 if (self.base.options.sysroot != null) blk: {
857 // Try stub file first. If we hit it, then we're done as the stub file
858 // re-exports every single symbol definition.
859 for (lib_dirs.items) |dir| {
860 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
861 try libs.put(full_path, .{ .needed = true });
862 libsystem_available = true;
863 break :blk;
864 }
865 }
866 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
867 // doesn't export libc.dylib which we'll need to resolve subsequently also.
868 for (lib_dirs.items) |dir| {
869 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
870 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
871 try libs.put(libsystem_path, .{ .needed = true });
872 try libs.put(libc_path, .{ .needed = true });
873 libsystem_available = true;
874 break :blk;
875 }943 }
876 }944 }
877 }945 },
878 }946 }
879 if (!libsystem_available) {947 }
880 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{948
881 self.base.options.target.os.version_range.semver.min.major,949 if (lib_not_found) {
882 });950 log.warn("Library search paths:", .{});
883 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{951 for (lib_dirs.items) |dir| {
884 "libc", "darwin", libsystem_name,952 log.warn(" {s}", .{dir});
885 });
886 try libs.put(full_path, .{ .needed = true });
887 }953 }
954 }
888955
889 // frameworks956 try self.resolveLibSystem(arena, comp, lib_dirs.items, &libs);
890 var framework_dirs = std.ArrayList([]const u8).init(arena);957
891 for (self.base.options.framework_dirs) |dir| {958 // frameworks
892 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {959 var framework_dirs = std.ArrayList([]const u8).init(arena);
893 try framework_dirs.append(search_dir);960 for (self.base.options.framework_dirs) |dir| {
894 } else {961 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
895 log.warn("directory not found for '-F{s}'", .{dir});962 try framework_dirs.append(search_dir);
896 }963 } else {
964 log.warn("directory not found for '-F{s}'", .{dir});
897 }965 }
966 }
898967
899 outer: for (self.base.options.frameworks.keys()) |f_name| {968 outer: for (self.base.options.frameworks.keys()) |f_name| {
900 for (framework_dirs.items) |dir| {969 for (framework_dirs.items) |dir| {
901 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {970 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
902 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {971 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
903 const info = self.base.options.frameworks.get(f_name).?;972 const info = self.base.options.frameworks.get(f_name).?;
904 try libs.put(full_path, .{973 try libs.put(full_path, .{
905 .needed = info.needed,974 .needed = info.needed,
906 .weak = info.weak,975 .weak = info.weak,
907 });976 });
908 continue :outer;977 continue :outer;
909 }
910 }978 }
911 } else {
912 log.warn("framework not found for '-framework {s}'", .{f_name});
913 framework_not_found = true;
914 }979 }
980 } else {
981 log.warn("framework not found for '-framework {s}'", .{f_name});
982 framework_not_found = true;
915 }983 }
984 }
916985
917 if (framework_not_found) {986 if (framework_not_found) {
918 log.warn("Framework search paths:", .{});987 log.warn("Framework search paths:", .{});
919 for (framework_dirs.items) |dir| {988 for (framework_dirs.items) |dir| {
920 log.warn(" {s}", .{dir});989 log.warn(" {s}", .{dir});
921 }
922 }990 }
991 }
923992
924 // rpaths993 // rpaths
925 var rpath_table = std.StringArrayHashMap(void).init(arena);994 var rpath_table = std.StringArrayHashMap(void).init(arena);
926 for (self.base.options.rpath_list) |rpath| {995 for (self.base.options.rpath_list) |rpath| {
927 if (rpath_table.contains(rpath)) continue;996 if (rpath_table.contains(rpath)) continue;
928 const cmdsize = @intCast(u32, mem.alignForwardGeneric(997 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
929 u64,998 u64,
930 @sizeOf(macho.rpath_command) + rpath.len + 1,999 @sizeOf(macho.rpath_command) + rpath.len + 1,
931 @sizeOf(u64),1000 @sizeOf(u64),
932 ));1001 ));
933 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{1002 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
934 .cmdsize = cmdsize,1003 .cmdsize = cmdsize,
935 .path = @sizeOf(macho.rpath_command),1004 .path = @sizeOf(macho.rpath_command),
936 });1005 });
937 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);1006 rpath_cmd.data = try gpa.alloc(u8, cmdsize - rpath_cmd.inner.path);
938 mem.set(u8, rpath_cmd.data, 0);1007 mem.set(u8, rpath_cmd.data, 0);
939 mem.copy(u8, rpath_cmd.data, rpath);1008 mem.copy(u8, rpath_cmd.data, rpath);
940 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });1009 try self.load_commands.append(gpa, .{ .rpath = rpath_cmd });
941 try rpath_table.putNoClobber(rpath, {});1010 try rpath_table.putNoClobber(rpath, {});
942 self.load_commands_dirty = true;1011 self.load_commands_dirty = true;
943 }1012 }
9441013
945 // code signature and entitlements1014 // code signature and entitlements
946 if (self.base.options.entitlements) |path| {1015 if (self.base.options.entitlements) |path| {
947 if (self.code_signature) |*csig| {1016 if (self.code_signature) |*csig| {
948 try csig.addEntitlements(self.base.allocator, path);1017 try csig.addEntitlements(gpa, path);
949 csig.code_directory.ident = self.base.options.emit.?.sub_path;1018 csig.code_directory.ident = self.base.options.emit.?.sub_path;
950 } else {1019 } else {
951 var csig = CodeSignature.init(self.page_size);1020 var csig = CodeSignature.init(self.page_size);
952 try csig.addEntitlements(self.base.allocator, path);1021 try csig.addEntitlements(gpa, path);
953 csig.code_directory.ident = self.base.options.emit.?.sub_path;1022 csig.code_directory.ident = self.base.options.emit.?.sub_path;
954 self.code_signature = csig;1023 self.code_signature = csig;
955 }
956 }1024 }
1025 }
9571026
958 if (self.base.options.verbose_link) {1027 if (self.base.options.verbose_link) {
959 var argv = std.ArrayList([]const u8).init(arena);1028 var argv = std.ArrayList([]const u8).init(arena);
960
961 try argv.append("zig");
962 try argv.append("ld");
963
964 if (is_exe_or_dyn_lib) {
965 try argv.append("-dynamic");
966 }
9671029
968 if (is_dyn_lib) {1030 try argv.append("zig");
969 try argv.append("-dylib");1031 try argv.append("ld");
9701032
971 if (self.base.options.install_name) |install_name| {1033 if (is_exe_or_dyn_lib) {
972 try argv.append("-install_name");1034 try argv.append("-dynamic");
973 try argv.append(install_name);1035 }
974 }
975 }
9761036
977 if (self.base.options.sysroot) |syslibroot| {1037 if (is_dyn_lib) {
978 try argv.append("-syslibroot");1038 try argv.append("-dylib");
979 try argv.append(syslibroot);
980 }
9811039
982 for (rpath_table.keys()) |rpath| {1040 if (self.base.options.install_name) |install_name| {
983 try argv.append("-rpath");1041 try argv.append("-install_name");
984 try argv.append(rpath);1042 try argv.append(install_name);
985 }1043 }
1044 }
9861045
987 if (self.base.options.pagezero_size) |pagezero_size| {1046 if (self.base.options.sysroot) |syslibroot| {
988 try argv.append("-pagezero_size");1047 try argv.append("-syslibroot");
989 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));1048 try argv.append(syslibroot);
990 }1049 }
9911050
992 if (self.base.options.search_strategy) |strat| switch (strat) {1051 for (rpath_table.keys()) |rpath| {
993 .paths_first => try argv.append("-search_paths_first"),1052 try argv.append("-rpath");
994 .dylibs_first => try argv.append("-search_dylibs_first"),1053 try argv.append(rpath);
995 };1054 }
9961055
997 if (self.base.options.headerpad_size) |headerpad_size| {1056 if (self.base.options.pagezero_size) |pagezero_size| {
998 try argv.append("-headerpad_size");1057 try argv.append("-pagezero_size");
999 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));1058 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
1000 }1059 }
10011060
1002 if (self.base.options.headerpad_max_install_names) {1061 if (self.base.options.search_strategy) |strat| switch (strat) {
1003 try argv.append("-headerpad_max_install_names");1062 .paths_first => try argv.append("-search_paths_first"),
1004 }1063 .dylibs_first => try argv.append("-search_dylibs_first"),
1064 };
10051065
1006 if (self.base.options.dead_strip_dylibs) {1066 if (self.base.options.headerpad_size) |headerpad_size| {
1007 try argv.append("-dead_strip_dylibs");1067 try argv.append("-headerpad_size");
1008 }1068 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
1069 }
10091070
1010 if (self.base.options.entry) |entry| {1071 if (self.base.options.headerpad_max_install_names) {
1011 try argv.append("-e");1072 try argv.append("-headerpad_max_install_names");
1012 try argv.append(entry);1073 }
1013 }
10141074
1015 for (self.base.options.objects) |obj| {1075 if (gc_sections) {
1016 try argv.append(obj.path);1076 try argv.append("-dead_strip");
1017 }1077 }
10181078
1019 for (comp.c_object_table.keys()) |key| {1079 if (self.base.options.dead_strip_dylibs) {
1020 try argv.append(key.status.success.object_path);1080 try argv.append("-dead_strip_dylibs");
1021 }1081 }
10221082
1023 if (module_obj_path) |p| {1083 if (self.base.options.entry) |entry| {
1024 try argv.append(p);1084 try argv.append("-e");
1025 }1085 try argv.append(entry);
1086 }
10261087
1027 if (comp.compiler_rt_lib) |lib| {1088 for (self.base.options.objects) |obj| {
1028 try argv.append(lib.full_object_path);1089 try argv.append(obj.path);
1029 }1090 }
10301091
1031 if (self.base.options.link_libcpp) {1092 for (comp.c_object_table.keys()) |key| {
1032 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);1093 try argv.append(key.status.success.object_path);
1033 try argv.append(comp.libcxx_static_lib.?.full_object_path);1094 }
1034 }
10351095
1036 try argv.append("-o");1096 if (module_obj_path) |p| {
1037 try argv.append(full_out_path);1097 try argv.append(p);
1098 }
10381099
1039 try argv.append("-lSystem");1100 if (comp.compiler_rt_lib) |lib| {
1040 try argv.append("-lc");1101 try argv.append(lib.full_object_path);
1102 }
10411103
1042 for (self.base.options.system_libs.keys()) |l_name| {1104 if (self.base.options.link_libcpp) {
1043 const info = self.base.options.system_libs.get(l_name).?;1105 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1044 const arg = if (info.needed)1106 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1045 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})1107 }
1046 else if (info.weak)
1047 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1048 else
1049 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1050 try argv.append(arg);
1051 }
10521108
1053 for (self.base.options.lib_dirs) |lib_dir| {1109 try argv.append("-o");
1054 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));1110 try argv.append(full_out_path);
1055 }1111
1112 try argv.append("-lSystem");
1113 try argv.append("-lc");
1114
1115 for (self.base.options.system_libs.keys()) |l_name| {
1116 const info = self.base.options.system_libs.get(l_name).?;
1117 const arg = if (info.needed)
1118 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
1119 else if (info.weak)
1120 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1121 else
1122 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1123 try argv.append(arg);
1124 }
10561125
1057 for (self.base.options.frameworks.keys()) |framework| {1126 for (self.base.options.lib_dirs) |lib_dir| {
1058 const info = self.base.options.frameworks.get(framework).?;1127 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1059 const arg = if (info.needed)1128 }
1060 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1061 else if (info.weak)
1062 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1063 else
1064 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1065 try argv.append(arg);
1066 }
10671129
1068 for (self.base.options.framework_dirs) |framework_dir| {1130 for (self.base.options.frameworks.keys()) |framework| {
1069 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));1131 const info = self.base.options.frameworks.get(framework).?;
1070 }1132 const arg = if (info.needed)
1133 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1134 else if (info.weak)
1135 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1136 else
1137 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1138 try argv.append(arg);
1139 }
10711140
1072 if (allow_undef) {1141 for (self.base.options.framework_dirs) |framework_dir| {
1073 try argv.append("-undefined");1142 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1074 try argv.append("dynamic_lookup");1143 }
1075 }
10761144
1077 for (must_link_archives.keys()) |lib| {1145 if (is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false)) {
1078 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));1146 try argv.append("-undefined");
1079 }1147 try argv.append("dynamic_lookup");
1148 }
10801149
1081 Compilation.dump_argv(argv.items);1150 for (must_link_archives.keys()) |lib| {
1151 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
1082 }1152 }
10831153
1084 var dependent_libs = std.fifo.LinearFifo(struct {1154 Compilation.dump_argv(argv.items);
1085 id: Dylib.Id,
1086 parent: u16,
1087 }, .Dynamic).init(self.base.allocator);
1088 defer dependent_libs.deinit();
1089 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1090 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1091 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1092 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1093 }1155 }
10941156
1095 try self.createMhExecuteHeaderSymbol();1157 var dependent_libs = std.fifo.LinearFifo(struct {
1158 id: Dylib.Id,
1159 parent: u16,
1160 }, .Dynamic).init(gpa);
1161 defer dependent_libs.deinit();
1162 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1163 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1164 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1165 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1166
1096 for (self.objects.items) |*object, object_id| {1167 for (self.objects.items) |*object, object_id| {
1097 if (object.analyzed) continue;1168 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1098 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1099 }1169 }
11001170
1101 try self.resolveSymbolsInArchives();1171 try self.resolveSymbolsInArchives();
...@@ -1103,46 +1173,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1103,46 +1173,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1103 try self.createDyldPrivateAtom();1173 try self.createDyldPrivateAtom();
1104 try self.createStubHelperPreambleAtom();1174 try self.createStubHelperPreambleAtom();
1105 try self.resolveSymbolsInDylibs();1175 try self.resolveSymbolsInDylibs();
1176 try self.createMhExecuteHeaderSymbol();
1106 try self.createDsoHandleSymbol();1177 try self.createDsoHandleSymbol();
1107 try self.addCodeSignatureLC();1178 try self.addCodeSignatureLC();
1179 try self.resolveSymbolsAtLoading();
11081180
1109 {
1110 var next_sym: usize = 0;
1111 while (next_sym < self.unresolved.count()) {
1112 const sym = &self.undefs.items[self.unresolved.keys()[next_sym]];
1113 const sym_name = self.getString(sym.n_strx);
1114 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
1115
1116 if (sym.discarded()) {
1117 sym.* = .{
1118 .n_strx = 0,
1119 .n_type = macho.N_UNDF,
1120 .n_sect = 0,
1121 .n_desc = 0,
1122 .n_value = 0,
1123 };
1124 _ = self.unresolved.swapRemove(resolv.where_index);
1125 continue;
1126 } else if (allow_undef) {
1127 const n_desc = @bitCast(
1128 u16,
1129 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
1130 );
1131 // TODO allow_shlib_undefined is an ELF flag so figure out macOS specific flags too.
1132 sym.n_type = macho.N_EXT;
1133 sym.n_desc = n_desc;
1134 _ = self.unresolved.swapRemove(resolv.where_index);
1135 continue;
1136 }
1137
1138 log.err("undefined reference to symbol '{s}'", .{sym_name});
1139 if (resolv.file) |file| {
1140 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
1141 }
1142
1143 next_sym += 1;
1144 }
1145 }
1146 if (self.unresolved.count() > 0) {1181 if (self.unresolved.count() > 0) {
1147 return error.UndefinedSymbolReference;1182 return error.UndefinedSymbolReference;
1148 }1183 }
...@@ -1154,46 +1189,42 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1154,46 +1189,42 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1154 }1189 }
11551190
1156 try self.createTentativeDefAtoms();1191 try self.createTentativeDefAtoms();
1157 try self.parseObjectsIntoAtoms();
11581192
1159 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;1193 for (self.objects.items) |*object, object_id| {
1160 if (use_llvm or use_stage1) {1194 try object.splitIntoAtomsOneShot(self, @intCast(u32, object_id));
1161 try self.pruneAndSortSections();
1162 try self.allocateSegments();
1163 try self.allocateLocals();
1164 }1195 }
11651196
1197 if (gc_sections) {
1198 try dead_strip.gcAtoms(self);
1199 }
1200
1201 try self.pruneAndSortSections();
1202 try self.allocateSegments();
1203 try self.allocateSymbols();
1204
1166 try self.allocateSpecialSymbols();1205 try self.allocateSpecialSymbols();
1167 try self.allocateGlobals();
11681206
1169 if (build_options.enable_logging) {1207 if (build_options.enable_logging) {
1170 self.logSymtab();1208 self.logSymtab();
1171 self.logSectionOrdinals();1209 self.logSectionOrdinals();
1210 self.logAtoms();
1172 }1211 }
11731212
1174 if (use_llvm or use_stage1) {1213 try self.writeAtomsOneShot();
1175 try self.writeAllAtoms();
1176 } else {
1177 try self.writeAtoms();
1178 }
11791214
1180 if (self.rustc_section_index) |id| {1215 if (self.rustc_section_index) |id| {
1181 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;1216 const sect = self.getSectionPtr(.{
1182 const sect = &seg.sections.items[id];1217 .seg = self.data_segment_cmd_index.?,
1218 .sect = id,
1219 });
1183 sect.size = self.rustc_section_size;1220 sect.size = self.rustc_section_size;
1184 }1221 }
11851222
1186 try self.setEntryPoint();1223 try self.setEntryPoint();
1187 try self.updateSectionOrdinals();
1188 try self.writeLinkeditSegment();1224 try self.writeLinkeditSegment();
11891225
1190 if (self.d_sym) |*d_sym| {
1191 // Flush debug symbols bundle.
1192 try d_sym.flushModule(self.base.allocator, self.base.options);
1193 }
1194
1195 if (self.code_signature) |*csig| {1226 if (self.code_signature) |*csig| {
1196 csig.clear(self.base.allocator);1227 csig.clear(gpa);
1197 csig.code_directory.ident = self.base.options.emit.?.sub_path;1228 csig.code_directory.ident = self.base.options.emit.?.sub_path;
1198 // Preallocate space for the code signature.1229 // Preallocate space for the code signature.
1199 // We need to do this at this stage so that we have the load commands with proper values1230 // We need to do this at this stage so that we have the load commands with proper values
...@@ -1206,32 +1237,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1206,32 +1237,17 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1206 try self.writeLoadCommands();1237 try self.writeLoadCommands();
1207 try self.writeHeader();1238 try self.writeHeader();
12081239
1209 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
1210 log.debug("flushing. no_entry_point_found = true", .{});
1211 self.error_flags.no_entry_point_found = true;
1212 } else {
1213 log.debug("flushing. no_entry_point_found = false", .{});
1214 self.error_flags.no_entry_point_found = false;
1215 }
1216
1217 assert(!self.load_commands_dirty);1240 assert(!self.load_commands_dirty);
12181241
1219 if (self.code_signature) |*csig| {1242 if (self.code_signature) |*csig| {
1220 try self.writeCodeSignature(csig); // code signing always comes last1243 try self.writeCodeSignature(csig); // code signing always comes last
1221 }1244 }
1222
1223 if (build_options.enable_link_snapshots) {
1224 if (self.base.options.enable_link_snapshots)
1225 try self.snapshotState();
1226 }
1227 }1245 }
12281246
1229 cache: {1247 if (!self.base.options.disable_lld_caching) {
1230 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
1231 break :cache;
1232 // Update the file with the digest. If it fails we can continue; it only1248 // Update the file with the digest. If it fails we can continue; it only
1233 // means that the next invocation will have an unnecessary cache miss.1249 // means that the next invocation will have an unnecessary cache miss.
1234 Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| {1250 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
1235 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});1251 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
1236 };1252 };
1237 // Again failure here only means an unnecessary cache miss.1253 // Again failure here only means an unnecessary cache miss.
...@@ -1242,8 +1258,49 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1242,8 +1258,49 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1242 // other processes clobbering it.1258 // other processes clobbering it.
1243 self.base.lock = man.toOwnedLock();1259 self.base.lock = man.toOwnedLock();
1244 }1260 }
1261}
12451262
1246 self.cold_start = false;1263fn resolveLibSystem(
1264 self: *MachO,
1265 arena: Allocator,
1266 comp: *Compilation,
1267 search_dirs: []const []const u8,
1268 out_libs: anytype,
1269) !void {
1270 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
1271 var libsystem_available = false;
1272 if (self.base.options.sysroot != null) blk: {
1273 // Try stub file first. If we hit it, then we're done as the stub file
1274 // re-exports every single symbol definition.
1275 for (search_dirs) |dir| {
1276 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
1277 try out_libs.put(full_path, .{ .needed = true });
1278 libsystem_available = true;
1279 break :blk;
1280 }
1281 }
1282 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
1283 // doesn't export libc.dylib which we'll need to resolve subsequently also.
1284 for (search_dirs) |dir| {
1285 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
1286 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
1287 try out_libs.put(libsystem_path, .{ .needed = true });
1288 try out_libs.put(libc_path, .{ .needed = true });
1289 libsystem_available = true;
1290 break :blk;
1291 }
1292 }
1293 }
1294 }
1295 if (!libsystem_available) {
1296 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
1297 self.base.options.target.os.version_range.semver.min.major,
1298 });
1299 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
1300 "libc", "darwin", libsystem_name,
1301 });
1302 try out_libs.put(full_path, .{ .needed = true });
1303 }
1247}1304}
12481305
1249fn resolveSearchDir(1306fn resolveSearchDir(
...@@ -1288,6 +1345,16 @@ fn resolveSearchDir(...@@ -1288,6 +1345,16 @@ fn resolveSearchDir(
1288 return null;1345 return null;
1289}1346}
12901347
1348fn resolveSearchDirs(arena: Allocator, dirs: []const []const u8, syslibroot: ?[]const u8, out_dirs: anytype) !void {
1349 for (dirs) |dir| {
1350 if (try resolveSearchDir(arena, dir, syslibroot)) |search_dir| {
1351 try out_dirs.append(search_dir);
1352 } else {
1353 log.warn("directory not found for '-L{s}'", .{dir});
1354 }
1355 }
1356}
1357
1291fn resolveLib(1358fn resolveLib(
1292 arena: Allocator,1359 arena: Allocator,
1293 search_dir: []const u8,1360 search_dir: []const u8,
...@@ -1337,9 +1404,15 @@ fn parseObject(self: *MachO, path: []const u8) !bool {...@@ -1337,9 +1404,15 @@ fn parseObject(self: *MachO, path: []const u8) !bool {
1337 const name = try self.base.allocator.dupe(u8, path);1404 const name = try self.base.allocator.dupe(u8, path);
1338 errdefer self.base.allocator.free(name);1405 errdefer self.base.allocator.free(name);
13391406
1407 const mtime: u64 = mtime: {
1408 const stat = file.stat() catch break :mtime 0;
1409 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));
1410 };
1411
1340 var object = Object{1412 var object = Object{
1341 .name = name,1413 .name = name,
1342 .file = file,1414 .file = file,
1415 .mtime = mtime,
1343 };1416 };
13441417
1345 object.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {1418 object.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {
...@@ -1507,7 +1580,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1507,7 +1580,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1507 .syslibroot = syslibroot,1580 .syslibroot = syslibroot,
1508 })) continue;1581 })) continue;
15091582
1510 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});1583 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
1511 }1584 }
1512}1585}
15131586
...@@ -1522,7 +1595,7 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1522,7 +1595,7 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1522 log.debug("parsing and force loading static archive '{s}'", .{full_path});1595 log.debug("parsing and force loading static archive '{s}'", .{full_path});
15231596
1524 if (try self.parseArchive(full_path, true)) continue;1597 if (try self.parseArchive(full_path, true)) continue;
1525 log.warn("unknown filetype: expected static archive: '{s}'", .{file_name});1598 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
1526 }1599 }
1527}1600}
15281601
...@@ -1543,7 +1616,7 @@ fn parseLibs(...@@ -1543,7 +1616,7 @@ fn parseLibs(
1543 })) continue;1616 })) continue;
1544 if (try self.parseArchive(lib, false)) continue;1617 if (try self.parseArchive(lib, false)) continue;
15451618
1546 log.warn("unknown filetype for a library: '{s}'", .{lib});1619 log.debug("unknown filetype for a library: '{s}'", .{lib});
1547 }1620 }
1548}1621}
15491622
...@@ -1587,7 +1660,7 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1587,7 +1660,7 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1587 });1660 });
1588 if (did_parse_successfully) break;1661 if (did_parse_successfully) break;
1589 } else {1662 } else {
1590 log.warn("unable to resolve dependency {s}", .{dep_id.id.name});1663 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
1591 }1664 }
1592 }1665 }
1593}1666}
...@@ -1595,6 +1668,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1595,6 +1668,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1595pub const MatchingSection = struct {1668pub const MatchingSection = struct {
1596 seg: u16,1669 seg: u16,
1597 sect: u16,1670 sect: u16,
1671
1672 pub fn eql(this: MatchingSection, other: struct {
1673 seg: ?u16,
1674 sect: ?u16,
1675 }) bool {
1676 const seg = other.seg orelse return false;
1677 const sect = other.sect orelse return false;
1678 return this.seg == seg and this.sect == sect;
1679 }
1598};1680};
15991681
1600pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {1682pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
...@@ -2158,33 +2240,31 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -2158,33 +2240,31 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
2158 return res;2240 return res;
2159}2241}
21602242
2161pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*Atom {2243pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
2162 const size_usize = math.cast(usize, size) orelse return error.Overflow;2244 const size_usize = math.cast(usize, size) orelse return error.Overflow;
2163 const atom = try self.base.allocator.create(Atom);2245 const atom = try gpa.create(Atom);
2164 errdefer self.base.allocator.destroy(atom);2246 errdefer gpa.destroy(atom);
2165 atom.* = Atom.empty;2247 atom.* = Atom.empty;
2166 atom.local_sym_index = local_sym_index;2248 atom.sym_index = sym_index;
2167 atom.size = size;2249 atom.size = size;
2168 atom.alignment = alignment;2250 atom.alignment = alignment;
21692251
2170 try atom.code.resize(self.base.allocator, size_usize);2252 try atom.code.resize(gpa, size_usize);
2171 mem.set(u8, atom.code.items, 0);2253 mem.set(u8, atom.code.items, 0);
21722254
2173 try self.managed_atoms.append(self.base.allocator, atom);
2174 return atom;2255 return atom;
2175}2256}
21762257
2177pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {2258pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2178 const seg = self.load_commands.items[match.seg].segment;2259 const sect = self.getSection(match);
2179 const sect = seg.sections.items[match.sect];2260 const sym = atom.getSymbol(self);
2180 const sym = self.locals.items[atom.local_sym_index];
2181 const file_offset = sect.offset + sym.n_value - sect.addr;2261 const file_offset = sect.offset + sym.n_value - sect.addr;
2182 try atom.resolveRelocs(self);2262 try atom.resolveRelocs(self);
2183 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });2263 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
2184 try self.base.file.?.pwriteAll(atom.code.items, file_offset);2264 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2185}2265}
21862266
2187fn allocateLocals(self: *MachO) !void {2267fn allocateSymbols(self: *MachO) !void {
2188 var it = self.atoms.iterator();2268 var it = self.atoms.iterator();
2189 while (it.next()) |entry| {2269 while (it.next()) |entry| {
2190 const match = entry.key_ptr.*;2270 const match = entry.key_ptr.*;
...@@ -2194,37 +2274,25 @@ fn allocateLocals(self: *MachO) !void {...@@ -2194,37 +2274,25 @@ fn allocateLocals(self: *MachO) !void {
2194 atom = prev;2274 atom = prev;
2195 }2275 }
21962276
2197 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2277 const n_sect = self.getSectionOrdinal(match);
2198 const seg = self.load_commands.items[match.seg].segment;2278 const sect = self.getSection(match);
2199 const sect = seg.sections.items[match.sect];
2200 var base_vaddr = sect.addr;2279 var base_vaddr = sect.addr;
22012280
2202 log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() });2281 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ n_sect, sect.segName(), sect.sectName() });
22032282
2204 while (true) {2283 while (true) {
2205 const alignment = try math.powi(u32, 2, atom.alignment);2284 const alignment = try math.powi(u32, 2, atom.alignment);
2206 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);2285 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
22072286
2208 const sym = &self.locals.items[atom.local_sym_index];2287 const sym = atom.getSymbolPtr(self);
2209 sym.n_value = base_vaddr;2288 sym.n_value = base_vaddr;
2210 sym.n_sect = n_sect;2289 sym.n_sect = n_sect;
22112290
2212 log.debug(" {d}: {s} allocated at 0x{x}", .{2291 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
2213 atom.local_sym_index,
2214 self.getString(sym.n_strx),
2215 base_vaddr,
2216 });
2217
2218 // Update each alias (if any)
2219 for (atom.aliases.items) |index| {
2220 const alias_sym = &self.locals.items[index];
2221 alias_sym.n_value = base_vaddr;
2222 alias_sym.n_sect = n_sect;
2223 }
22242292
2225 // Update each symbol contained within the atom2293 // Update each symbol contained within the atom
2226 for (atom.contained.items) |sym_at_off| {2294 for (atom.contained.items) |sym_at_off| {
2227 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];2295 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2228 contained_sym.n_value = base_vaddr + sym_at_off.offset;2296 contained_sym.n_value = base_vaddr + sym_at_off.offset;
2229 contained_sym.n_sect = n_sect;2297 contained_sym.n_sect = n_sect;
2230 }2298 }
...@@ -2242,16 +2310,11 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void...@@ -2242,16 +2310,11 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
2242 var atom = self.atoms.get(match) orelse return;2310 var atom = self.atoms.get(match) orelse return;
22432311
2244 while (true) {2312 while (true) {
2245 const atom_sym = &self.locals.items[atom.local_sym_index];2313 const atom_sym = atom.getSymbolPtr(self);
2246 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);2314 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
22472315
2248 for (atom.aliases.items) |index| {
2249 const alias_sym = &self.locals.items[index];
2250 alias_sym.n_value = @intCast(u64, @intCast(i64, alias_sym.n_value) + offset);
2251 }
2252
2253 for (atom.contained.items) |sym_at_off| {2316 for (atom.contained.items) |sym_at_off| {
2254 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];2317 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2255 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);2318 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2256 }2319 }
22572320
...@@ -2262,53 +2325,33 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void...@@ -2262,53 +2325,33 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
2262}2325}
22632326
2264fn allocateSpecialSymbols(self: *MachO) !void {2327fn allocateSpecialSymbols(self: *MachO) !void {
2265 for (&[_]?u32{2328 for (&[_][]const u8{
2266 self.mh_execute_header_sym_index,2329 "___dso_handle",
2267 self.dso_handle_sym_index,2330 "__mh_execute_header",
2268 }) |maybe_sym_index| {2331 }) |name| {
2269 const sym_index = maybe_sym_index orelse continue;2332 const global = self.globals.get(name) orelse continue;
2270 const sym = &self.locals.items[sym_index];2333 if (global.file != null) continue;
2334 const sym = self.getSymbolPtr(global);
2271 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;2335 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2272 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{2336 sym.n_sect = self.getSectionOrdinal(.{
2273 .seg = self.text_segment_cmd_index.?,2337 .seg = self.text_segment_cmd_index.?,
2274 .sect = 0,2338 .sect = 0,
2275 }).? + 1);2339 });
2276 sym.n_value = seg.inner.vmaddr;2340 sym.n_value = seg.inner.vmaddr;
22772341
2278 log.debug("allocating {s} at the start of {s}", .{2342 log.debug("allocating {s} at the start of {s}", .{
2279 self.getString(sym.n_strx),2343 name,
2280 seg.inner.segName(),2344 seg.inner.segName(),
2281 });2345 });
2282 }2346 }
2283}2347}
22842348
2285fn allocateGlobals(self: *MachO) !void {2349fn writeAtomsOneShot(self: *MachO) !void {
2286 log.debug("allocating global symbols", .{});2350 assert(self.mode == .one_shot);
22872351
2288 var sym_it = self.symbol_resolver.valueIterator();
2289 while (sym_it.next()) |resolv| {
2290 if (resolv.where != .global) continue;
2291
2292 assert(resolv.local_sym_index != 0);
2293 const local_sym = self.locals.items[resolv.local_sym_index];
2294 const sym = &self.globals.items[resolv.where_index];
2295 sym.n_value = local_sym.n_value;
2296 sym.n_sect = local_sym.n_sect;
2297
2298 log.debug(" {d}: {s} allocated at 0x{x}", .{
2299 resolv.where_index,
2300 self.getString(sym.n_strx),
2301 local_sym.n_value,
2302 });
2303 }
2304}
2305
2306fn writeAllAtoms(self: *MachO) !void {
2307 var it = self.atoms.iterator();2352 var it = self.atoms.iterator();
2308 while (it.next()) |entry| {2353 while (it.next()) |entry| {
2309 const match = entry.key_ptr.*;2354 const sect = self.getSection(entry.key_ptr.*);
2310 const seg = self.load_commands.items[match.seg].segment;
2311 const sect = seg.sections.items[match.sect];
2312 var atom: *Atom = entry.value_ptr.*;2355 var atom: *Atom = entry.value_ptr.*;
23132356
2314 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;2357 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
...@@ -2324,20 +2367,28 @@ fn writeAllAtoms(self: *MachO) !void {...@@ -2324,20 +2367,28 @@ fn writeAllAtoms(self: *MachO) !void {
2324 }2367 }
23252368
2326 while (true) {2369 while (true) {
2327 const atom_sym = self.locals.items[atom.local_sym_index];2370 const this_sym = atom.getSymbol(self);
2328 const padding_size: usize = if (atom.next) |next| blk: {2371 const padding_size: usize = if (atom.next) |next| blk: {
2329 const next_sym = self.locals.items[next.local_sym_index];2372 const next_sym = next.getSymbol(self);
2330 const size = next_sym.n_value - (atom_sym.n_value + atom.size);2373 const size = next_sym.n_value - (this_sym.n_value + atom.size);
2331 break :blk math.cast(usize, size) orelse return error.Overflow;2374 break :blk math.cast(usize, size) orelse return error.Overflow;
2332 } else 0;2375 } else 0;
23332376
2334 log.debug(" (adding atom {s} to buffer: {})", .{ self.getString(atom_sym.n_strx), atom_sym });2377 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
2378 atom.sym_index,
2379 atom.getName(self),
2380 atom.file,
2381 });
2382 if (padding_size > 0) {
2383 log.debug(" (with padding {x})", .{padding_size});
2384 }
23352385
2336 try atom.resolveRelocs(self);2386 try atom.resolveRelocs(self);
2337 buffer.appendSliceAssumeCapacity(atom.code.items);2387 buffer.appendSliceAssumeCapacity(atom.code.items);
23382388
2339 var i: usize = 0;2389 var i: usize = 0;
2340 while (i < padding_size) : (i += 1) {2390 while (i < padding_size) : (i += 1) {
2391 // TODO with NOPs
2341 buffer.appendAssumeCapacity(0);2392 buffer.appendAssumeCapacity(0);
2342 }2393 }
23432394
...@@ -2381,12 +2432,13 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty...@@ -2381,12 +2432,13 @@ fn writePadding(self: *MachO, match: MatchingSection, size: usize, writer: anyty
2381 }2432 }
2382}2433}
23832434
2384fn writeAtoms(self: *MachO) !void {2435fn writeAtomsIncremental(self: *MachO) !void {
2436 assert(self.mode == .incremental);
2437
2385 var it = self.atoms.iterator();2438 var it = self.atoms.iterator();
2386 while (it.next()) |entry| {2439 while (it.next()) |entry| {
2387 const match = entry.key_ptr.*;2440 const match = entry.key_ptr.*;
2388 const seg = self.load_commands.items[match.seg].segment;2441 const sect = self.getSection(match);
2389 const sect = seg.sections.items[match.sect];
2390 var atom: *Atom = entry.value_ptr.*;2442 var atom: *Atom = entry.value_ptr.*;
23912443
2392 // TODO handle zerofill in stage22444 // TODO handle zerofill in stage2
...@@ -2395,7 +2447,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -2395,7 +2447,7 @@ fn writeAtoms(self: *MachO) !void {
2395 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });2447 log.debug("writing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
23962448
2397 while (true) {2449 while (true) {
2398 if (atom.dirty or self.invalidate_relocs) {2450 if (atom.dirty) {
2399 try self.writeAtom(atom, match);2451 try self.writeAtom(atom, match);
2400 atom.dirty = false;2452 atom.dirty = false;
2401 }2453 }
...@@ -2407,17 +2459,19 @@ fn writeAtoms(self: *MachO) !void {...@@ -2407,17 +2459,19 @@ fn writeAtoms(self: *MachO) !void {
2407 }2459 }
2408}2460}
24092461
2410pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {2462pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2411 const local_sym_index = @intCast(u32, self.locals.items.len);2463 const gpa = self.base.allocator;
2412 try self.locals.append(self.base.allocator, .{2464 const sym_index = @intCast(u32, self.locals.items.len);
2465 try self.locals.append(gpa, .{
2413 .n_strx = 0,2466 .n_strx = 0,
2414 .n_type = macho.N_SECT,2467 .n_type = macho.N_SECT,
2415 .n_sect = 0,2468 .n_sect = 0,
2416 .n_desc = 0,2469 .n_desc = 0,
2417 .n_value = 0,2470 .n_value = 0,
2418 });2471 });
2419 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2472
2420 try atom.relocs.append(self.base.allocator, .{2473 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2474 try atom.relocs.append(gpa, .{
2421 .offset = 0,2475 .offset = 0,
2422 .target = target,2476 .target = target,
2423 .addend = 0,2477 .addend = 0,
...@@ -2430,35 +2484,60 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {...@@ -2430,35 +2484,60 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2430 else => unreachable,2484 else => unreachable,
2431 },2485 },
2432 });2486 });
2433 switch (target) {2487
2434 .local => {2488 const target_sym = self.getSymbol(target);
2435 try atom.rebases.append(self.base.allocator, 0);2489 if (target_sym.undf()) {
2436 },2490 const global = self.globals.get(self.getSymbolName(target)).?;
2437 .global => |n_strx| {2491 try atom.bindings.append(gpa, .{
2438 try atom.bindings.append(self.base.allocator, .{2492 .target = global,
2439 .n_strx = n_strx,2493 .offset = 0,
2440 .offset = 0,2494 });
2441 });2495 } else {
2442 },2496 try atom.rebases.append(gpa, 0);
2443 }2497 }
2498
2499 try self.managed_atoms.append(gpa, atom);
2500 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2501
2502 try self.allocateAtomCommon(atom, .{
2503 .seg = self.data_const_segment_cmd_index.?,
2504 .sect = self.got_section_index.?,
2505 });
2506
2444 return atom;2507 return atom;
2445}2508}
24462509
2447pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {2510pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2448 const local_sym_index = @intCast(u32, self.locals.items.len);2511 const gpa = self.base.allocator;
2449 try self.locals.append(self.base.allocator, .{2512 const sym_index = @intCast(u32, self.locals.items.len);
2513 try self.locals.append(gpa, .{
2450 .n_strx = 0,2514 .n_strx = 0,
2451 .n_type = macho.N_SECT,2515 .n_type = macho.N_SECT,
2452 .n_sect = 0,2516 .n_sect = 0,
2453 .n_desc = 0,2517 .n_desc = 0,
2454 .n_value = 0,2518 .n_value = 0,
2455 });2519 });
2456 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2520
2457 assert(target == .global);2521 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2458 try atom.bindings.append(self.base.allocator, .{2522 const target_sym = self.getSymbol(target);
2459 .n_strx = target.global,2523 assert(target_sym.undf());
2524
2525 const global = self.globals.get(self.getSymbolName(target)).?;
2526 try atom.bindings.append(gpa, .{
2527 .target = global,
2460 .offset = 0,2528 .offset = 0,
2461 });2529 });
2530
2531 try self.managed_atoms.append(gpa, atom);
2532 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2533
2534 const match = (try self.getMatchingSection(.{
2535 .segname = makeStaticString("__DATA"),
2536 .sectname = makeStaticString("__thread_ptrs"),
2537 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2538 })).?;
2539 try self.allocateAtomCommon(atom, match);
2540
2462 return atom;2541 return atom;
2463}2542}
24642543
...@@ -2466,34 +2545,32 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2466,34 +2545,32 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2466 if (self.dyld_stub_binder_index == null) return;2545 if (self.dyld_stub_binder_index == null) return;
2467 if (self.dyld_private_atom != null) return;2546 if (self.dyld_private_atom != null) return;
24682547
2469 const local_sym_index = @intCast(u32, self.locals.items.len);2548 const gpa = self.base.allocator;
2470 const sym = try self.locals.addOne(self.base.allocator);2549 const sym_index = @intCast(u32, self.locals.items.len);
2471 sym.* = .{2550 try self.locals.append(gpa, .{
2472 .n_strx = 0,2551 .n_strx = 0,
2473 .n_type = macho.N_SECT,2552 .n_type = macho.N_SECT,
2474 .n_sect = 0,2553 .n_sect = 0,
2475 .n_desc = 0,2554 .n_desc = 0,
2476 .n_value = 0,2555 .n_value = 0,
2477 };2556 });
2478 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2557 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2479 self.dyld_private_atom = atom;2558 self.dyld_private_atom = atom;
2480 const match = MatchingSection{2559
2560 try self.allocateAtomCommon(atom, .{
2481 .seg = self.data_segment_cmd_index.?,2561 .seg = self.data_segment_cmd_index.?,
2482 .sect = self.data_section_index.?,2562 .sect = self.data_section_index.?,
2483 };2563 });
2484 if (self.needs_prealloc) {
2485 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
2486 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
2487 sym.n_value = vaddr;
2488 } else try self.addAtomToSection(atom, match);
24892564
2490 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2565 try self.managed_atoms.append(gpa, atom);
2566 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2491}2567}
24922568
2493fn createStubHelperPreambleAtom(self: *MachO) !void {2569fn createStubHelperPreambleAtom(self: *MachO) !void {
2494 if (self.dyld_stub_binder_index == null) return;2570 if (self.dyld_stub_binder_index == null) return;
2495 if (self.stub_helper_preamble_atom != null) return;2571 if (self.stub_helper_preamble_atom != null) return;
24962572
2573 const gpa = self.base.allocator;
2497 const arch = self.base.options.target.cpu.arch;2574 const arch = self.base.options.target.cpu.arch;
2498 const size: u64 = switch (arch) {2575 const size: u64 = switch (arch) {
2499 .x86_64 => 15,2576 .x86_64 => 15,
...@@ -2505,17 +2582,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2505,17 +2582,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2505 .aarch64 => 2,2582 .aarch64 => 2,
2506 else => unreachable,2583 else => unreachable,
2507 };2584 };
2508 const local_sym_index = @intCast(u32, self.locals.items.len);2585 const sym_index = @intCast(u32, self.locals.items.len);
2509 const sym = try self.locals.addOne(self.base.allocator);2586 try self.locals.append(gpa, .{
2510 sym.* = .{
2511 .n_strx = 0,2587 .n_strx = 0,
2512 .n_type = macho.N_SECT,2588 .n_type = macho.N_SECT,
2513 .n_sect = 0,2589 .n_sect = 0,
2514 .n_desc = 0,2590 .n_desc = 0,
2515 .n_value = 0,2591 .n_value = 0,
2516 };2592 });
2517 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);2593 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
2518 const dyld_private_sym_index = self.dyld_private_atom.?.local_sym_index;2594 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
2519 switch (arch) {2595 switch (arch) {
2520 .x86_64 => {2596 .x86_64 => {
2521 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);2597 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
...@@ -2525,7 +2601,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2525,7 +2601,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2525 atom.code.items[2] = 0x1d;2601 atom.code.items[2] = 0x1d;
2526 atom.relocs.appendAssumeCapacity(.{2602 atom.relocs.appendAssumeCapacity(.{
2527 .offset = 3,2603 .offset = 3,
2528 .target = .{ .local = dyld_private_sym_index },2604 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2529 .addend = 0,2605 .addend = 0,
2530 .subtractor = null,2606 .subtractor = null,
2531 .pcrel = true,2607 .pcrel = true,
...@@ -2540,7 +2616,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2540,7 +2616,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2540 atom.code.items[10] = 0x25;2616 atom.code.items[10] = 0x25;
2541 atom.relocs.appendAssumeCapacity(.{2617 atom.relocs.appendAssumeCapacity(.{
2542 .offset = 11,2618 .offset = 11,
2543 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2619 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2544 .addend = 0,2620 .addend = 0,
2545 .subtractor = null,2621 .subtractor = null,
2546 .pcrel = true,2622 .pcrel = true,
...@@ -2554,7 +2630,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2554,7 +2630,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2554 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());2630 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
2555 atom.relocs.appendAssumeCapacity(.{2631 atom.relocs.appendAssumeCapacity(.{
2556 .offset = 0,2632 .offset = 0,
2557 .target = .{ .local = dyld_private_sym_index },2633 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2558 .addend = 0,2634 .addend = 0,
2559 .subtractor = null,2635 .subtractor = null,
2560 .pcrel = true,2636 .pcrel = true,
...@@ -2565,7 +2641,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2565,7 +2641,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2565 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());2641 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
2566 atom.relocs.appendAssumeCapacity(.{2642 atom.relocs.appendAssumeCapacity(.{
2567 .offset = 4,2643 .offset = 4,
2568 .target = .{ .local = dyld_private_sym_index },2644 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2569 .addend = 0,2645 .addend = 0,
2570 .subtractor = null,2646 .subtractor = null,
2571 .pcrel = false,2647 .pcrel = false,
...@@ -2583,7 +2659,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2583,7 +2659,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2583 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2659 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2584 atom.relocs.appendAssumeCapacity(.{2660 atom.relocs.appendAssumeCapacity(.{
2585 .offset = 12,2661 .offset = 12,
2586 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2662 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2587 .addend = 0,2663 .addend = 0,
2588 .subtractor = null,2664 .subtractor = null,
2589 .pcrel = true,2665 .pcrel = true,
...@@ -2598,7 +2674,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2598,7 +2674,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2598 ).toU32());2674 ).toU32());
2599 atom.relocs.appendAssumeCapacity(.{2675 atom.relocs.appendAssumeCapacity(.{
2600 .offset = 16,2676 .offset = 16,
2601 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2677 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2602 .addend = 0,2678 .addend = 0,
2603 .subtractor = null,2679 .subtractor = null,
2604 .pcrel = false,2680 .pcrel = false,
...@@ -2611,22 +2687,18 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2611,22 +2687,18 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2611 else => unreachable,2687 else => unreachable,
2612 }2688 }
2613 self.stub_helper_preamble_atom = atom;2689 self.stub_helper_preamble_atom = atom;
2614 const match = MatchingSection{2690
2691 try self.allocateAtomCommon(atom, .{
2615 .seg = self.text_segment_cmd_index.?,2692 .seg = self.text_segment_cmd_index.?,
2616 .sect = self.stub_helper_section_index.?,2693 .sect = self.stub_helper_section_index.?,
2617 };2694 });
2618
2619 if (self.needs_prealloc) {
2620 const alignment_pow_2 = try math.powi(u32, 2, atom.alignment);
2621 const vaddr = try self.allocateAtom(atom, atom.size, alignment_pow_2, match);
2622 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
2623 sym.n_value = vaddr;
2624 } else try self.addAtomToSection(atom, match);
26252695
2626 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2696 try self.managed_atoms.append(gpa, atom);
2697 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2627}2698}
26282699
2629pub fn createStubHelperAtom(self: *MachO) !*Atom {2700pub fn createStubHelperAtom(self: *MachO) !*Atom {
2701 const gpa = self.base.allocator;
2630 const arch = self.base.options.target.cpu.arch;2702 const arch = self.base.options.target.cpu.arch;
2631 const stub_size: u4 = switch (arch) {2703 const stub_size: u4 = switch (arch) {
2632 .x86_64 => 10,2704 .x86_64 => 10,
...@@ -2638,16 +2710,16 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2638,16 +2710,16 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2638 .aarch64 => 2,2710 .aarch64 => 2,
2639 else => unreachable,2711 else => unreachable,
2640 };2712 };
2641 const local_sym_index = @intCast(u32, self.locals.items.len);2713 const sym_index = @intCast(u32, self.locals.items.len);
2642 try self.locals.append(self.base.allocator, .{2714 try self.locals.append(gpa, .{
2643 .n_strx = 0,2715 .n_strx = 0,
2644 .n_type = macho.N_SECT,2716 .n_type = macho.N_SECT,
2645 .n_sect = 0,2717 .n_sect = 0,
2646 .n_desc = 0,2718 .n_desc = 0,
2647 .n_value = 0,2719 .n_value = 0,
2648 });2720 });
2649 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);2721 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
2650 try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);2722 try atom.relocs.ensureTotalCapacity(gpa, 1);
26512723
2652 switch (arch) {2724 switch (arch) {
2653 .x86_64 => {2725 .x86_64 => {
...@@ -2658,7 +2730,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2658,7 +2730,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2658 atom.code.items[5] = 0xe9;2730 atom.code.items[5] = 0xe9;
2659 atom.relocs.appendAssumeCapacity(.{2731 atom.relocs.appendAssumeCapacity(.{
2660 .offset = 6,2732 .offset = 6,
2661 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },2733 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2662 .addend = 0,2734 .addend = 0,
2663 .subtractor = null,2735 .subtractor = null,
2664 .pcrel = true,2736 .pcrel = true,
...@@ -2680,7 +2752,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2680,7 +2752,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2680 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());2752 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2681 atom.relocs.appendAssumeCapacity(.{2753 atom.relocs.appendAssumeCapacity(.{
2682 .offset = 4,2754 .offset = 4,
2683 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },2755 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2684 .addend = 0,2756 .addend = 0,
2685 .subtractor = null,2757 .subtractor = null,
2686 .pcrel = true,2758 .pcrel = true,
...@@ -2692,22 +2764,31 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2692,22 +2764,31 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2692 else => unreachable,2764 else => unreachable,
2693 }2765 }
26942766
2767 try self.managed_atoms.append(gpa, atom);
2768 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2769
2770 try self.allocateAtomCommon(atom, .{
2771 .seg = self.text_segment_cmd_index.?,
2772 .sect = self.stub_helper_section_index.?,
2773 });
2774
2695 return atom;2775 return atom;
2696}2776}
26972777
2698pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*Atom {2778pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
2699 const local_sym_index = @intCast(u32, self.locals.items.len);2779 const gpa = self.base.allocator;
2700 try self.locals.append(self.base.allocator, .{2780 const sym_index = @intCast(u32, self.locals.items.len);
2781 try self.locals.append(gpa, .{
2701 .n_strx = 0,2782 .n_strx = 0,
2702 .n_type = macho.N_SECT,2783 .n_type = macho.N_SECT,
2703 .n_sect = 0,2784 .n_sect = 0,
2704 .n_desc = 0,2785 .n_desc = 0,
2705 .n_value = 0,2786 .n_value = 0,
2706 });2787 });
2707 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2788 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2708 try atom.relocs.append(self.base.allocator, .{2789 try atom.relocs.append(gpa, .{
2709 .offset = 0,2790 .offset = 0,
2710 .target = .{ .local = stub_sym_index },2791 .target = .{ .sym_index = stub_sym_index, .file = null },
2711 .addend = 0,2792 .addend = 0,
2712 .subtractor = null,2793 .subtractor = null,
2713 .pcrel = false,2794 .pcrel = false,
...@@ -2718,15 +2799,27 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*A...@@ -2718,15 +2799,27 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*A
2718 else => unreachable,2799 else => unreachable,
2719 },2800 },
2720 });2801 });
2721 try atom.rebases.append(self.base.allocator, 0);2802 try atom.rebases.append(gpa, 0);
2722 try atom.lazy_bindings.append(self.base.allocator, .{2803
2723 .n_strx = n_strx,2804 const global = self.globals.get(self.getSymbolName(target)).?;
2805 try atom.lazy_bindings.append(gpa, .{
2806 .target = global,
2724 .offset = 0,2807 .offset = 0,
2725 });2808 });
2809
2810 try self.managed_atoms.append(gpa, atom);
2811 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2812
2813 try self.allocateAtomCommon(atom, .{
2814 .seg = self.data_segment_cmd_index.?,
2815 .sect = self.la_symbol_ptr_section_index.?,
2816 });
2817
2726 return atom;2818 return atom;
2727}2819}
27282820
2729pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {2821pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2822 const gpa = self.base.allocator;
2730 const arch = self.base.options.target.cpu.arch;2823 const arch = self.base.options.target.cpu.arch;
2731 const alignment: u2 = switch (arch) {2824 const alignment: u2 = switch (arch) {
2732 .x86_64 => 0,2825 .x86_64 => 0,
...@@ -2738,23 +2831,23 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2738,23 +2831,23 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2738 .aarch64 => 3 * @sizeOf(u32),2831 .aarch64 => 3 * @sizeOf(u32),
2739 else => unreachable, // unhandled architecture type2832 else => unreachable, // unhandled architecture type
2740 };2833 };
2741 const local_sym_index = @intCast(u32, self.locals.items.len);2834 const sym_index = @intCast(u32, self.locals.items.len);
2742 try self.locals.append(self.base.allocator, .{2835 try self.locals.append(gpa, .{
2743 .n_strx = 0,2836 .n_strx = 0,
2744 .n_type = macho.N_SECT,2837 .n_type = macho.N_SECT,
2745 .n_sect = 0,2838 .n_sect = 0,
2746 .n_desc = 0,2839 .n_desc = 0,
2747 .n_value = 0,2840 .n_value = 0,
2748 });2841 });
2749 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);2842 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
2750 switch (arch) {2843 switch (arch) {
2751 .x86_64 => {2844 .x86_64 => {
2752 // jmp2845 // jmp
2753 atom.code.items[0] = 0xff;2846 atom.code.items[0] = 0xff;
2754 atom.code.items[1] = 0x25;2847 atom.code.items[1] = 0x25;
2755 try atom.relocs.append(self.base.allocator, .{2848 try atom.relocs.append(gpa, .{
2756 .offset = 2,2849 .offset = 2,
2757 .target = .{ .local = laptr_sym_index },2850 .target = .{ .sym_index = laptr_sym_index, .file = null },
2758 .addend = 0,2851 .addend = 0,
2759 .subtractor = null,2852 .subtractor = null,
2760 .pcrel = true,2853 .pcrel = true,
...@@ -2763,12 +2856,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2763,12 +2856,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2763 });2856 });
2764 },2857 },
2765 .aarch64 => {2858 .aarch64 => {
2766 try atom.relocs.ensureTotalCapacity(self.base.allocator, 2);2859 try atom.relocs.ensureTotalCapacity(gpa, 2);
2767 // adrp x16, pages2860 // adrp x16, pages
2768 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2861 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2769 atom.relocs.appendAssumeCapacity(.{2862 atom.relocs.appendAssumeCapacity(.{
2770 .offset = 0,2863 .offset = 0,
2771 .target = .{ .local = laptr_sym_index },2864 .target = .{ .sym_index = laptr_sym_index, .file = null },
2772 .addend = 0,2865 .addend = 0,
2773 .subtractor = null,2866 .subtractor = null,
2774 .pcrel = true,2867 .pcrel = true,
...@@ -2783,7 +2876,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2783,7 +2876,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2783 ).toU32());2876 ).toU32());
2784 atom.relocs.appendAssumeCapacity(.{2877 atom.relocs.appendAssumeCapacity(.{
2785 .offset = 4,2878 .offset = 4,
2786 .target = .{ .local = laptr_sym_index },2879 .target = .{ .sym_index = laptr_sym_index, .file = null },
2787 .addend = 0,2880 .addend = 0,
2788 .subtractor = null,2881 .subtractor = null,
2789 .pcrel = false,2882 .pcrel = false,
...@@ -2795,101 +2888,179 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2795,101 +2888,179 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2795 },2888 },
2796 else => unreachable,2889 else => unreachable,
2797 }2890 }
2891
2892 try self.managed_atoms.append(gpa, atom);
2893 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2894
2895 try self.allocateAtomCommon(atom, .{
2896 .seg = self.text_segment_cmd_index.?,
2897 .sect = self.stubs_section_index.?,
2898 });
2899
2798 return atom;2900 return atom;
2799}2901}
28002902
2801fn createTentativeDefAtoms(self: *MachO) !void {2903fn createTentativeDefAtoms(self: *MachO) !void {
2802 if (self.tentatives.count() == 0) return;2904 const gpa = self.base.allocator;
2803 // Convert any tentative definition into a regular symbol and allocate2905
2804 // text blocks for each tentative definition.2906 for (self.globals.values()) |global| {
2805 while (self.tentatives.popOrNull()) |entry| {2907 const sym = self.getSymbolPtr(global);
2908 if (!sym.tentative()) continue;
2909
2910 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({d})", .{
2911 global.sym_index, self.getSymbolName(global), global.file,
2912 });
2913
2914 // Convert any tentative definition into a regular symbol and allocate
2915 // text blocks for each tentative definition.
2806 const match = MatchingSection{2916 const match = MatchingSection{
2807 .seg = self.data_segment_cmd_index.?,2917 .seg = self.data_segment_cmd_index.?,
2808 .sect = self.bss_section_index.?,2918 .sect = self.bss_section_index.?,
2809 };2919 };
2810 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);2920 _ = try self.section_ordinals.getOrPut(gpa, match);
28112921
2812 const global_sym = &self.globals.items[entry.key];2922 const size = sym.n_value;
2813 const size = global_sym.n_value;2923 const alignment = (sym.n_desc >> 8) & 0x0f;
2814 const alignment = (global_sym.n_desc >> 8) & 0x0f;
28152924
2816 global_sym.n_value = 0;2925 sym.* = .{
2817 global_sym.n_desc = 0;2926 .n_strx = sym.n_strx,
2818 global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2927 .n_type = macho.N_SECT | macho.N_EXT,
28192928 .n_sect = 0,
2820 const local_sym_index = @intCast(u32, self.locals.items.len);
2821 const local_sym = try self.locals.addOne(self.base.allocator);
2822 local_sym.* = .{
2823 .n_strx = global_sym.n_strx,
2824 .n_type = macho.N_SECT,
2825 .n_sect = global_sym.n_sect,
2826 .n_desc = 0,2929 .n_desc = 0,
2827 .n_value = 0,2930 .n_value = 0,
2828 };2931 };
28292932
2830 const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable;2933 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
2831 resolv.local_sym_index = local_sym_index;2934 atom.file = global.file;
28322935
2833 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);2936 try self.allocateAtomCommon(atom, match);
28342937
2835 if (self.needs_prealloc) {2938 if (global.file) |file| {
2836 const alignment_pow_2 = try math.powi(u32, 2, alignment);2939 const object = &self.objects.items[file];
2837 const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match);2940 try object.managed_atoms.append(gpa, atom);
2838 local_sym.n_value = vaddr;2941 try object.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
2839 global_sym.n_value = vaddr;2942 } else {
2840 } else try self.addAtomToSection(atom, match);2943 try self.managed_atoms.append(gpa, atom);
2944 try self.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
2945 }
2841 }2946 }
2842}2947}
28432948
2844fn createDsoHandleSymbol(self: *MachO) !void {2949fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2845 if (self.dso_handle_sym_index != null) return;2950 if (self.base.options.output_mode != .Exe) return;
28462951 if (self.globals.get("__mh_execute_header")) |global| {
2847 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{2952 const sym = self.getSymbol(global);
2848 .bytes = &self.strtab,2953 if (!sym.undf() and !(sym.pext() or sym.weakDef())) return;
2849 }) orelse return;2954 }
2850
2851 const resolv = self.symbol_resolver.getPtr(n_strx) orelse return;
2852 if (resolv.where != .undef) return;
28532955
2854 const undef = &self.undefs.items[resolv.where_index];2956 const gpa = self.base.allocator;
2855 const local_sym_index = @intCast(u32, self.locals.items.len);2957 const n_strx = try self.strtab.insert(gpa, "__mh_execute_header");
2856 var nlist = macho.nlist_64{2958 const sym_index = @intCast(u32, self.locals.items.len);
2857 .n_strx = undef.n_strx,2959 try self.locals.append(gpa, .{
2858 .n_type = macho.N_SECT,2960 .n_strx = n_strx,
2961 .n_type = macho.N_SECT | macho.N_EXT,
2859 .n_sect = 0,2962 .n_sect = 0,
2860 .n_desc = 0,2963 .n_desc = macho.REFERENCED_DYNAMICALLY,
2861 .n_value = 0,2964 .n_value = 0,
2862 };2965 });
2863 try self.locals.append(self.base.allocator, nlist);
2864 const global_sym_index = @intCast(u32, self.globals.items.len);
2865 nlist.n_type |= macho.N_EXT;
2866 nlist.n_desc = macho.N_WEAK_DEF;
2867 try self.globals.append(self.base.allocator, nlist);
2868 self.dso_handle_sym_index = local_sym_index;
28692966
2870 assert(self.unresolved.swapRemove(resolv.where_index));2967 const name = try gpa.dupe(u8, "__mh_execute_header");
2968 const gop = try self.globals.getOrPut(gpa, name);
2969 defer if (gop.found_existing) gpa.free(name);
2970 gop.value_ptr.* = .{
2971 .sym_index = sym_index,
2972 .file = null,
2973 };
2974}
28712975
2872 undef.* = .{2976fn createDsoHandleSymbol(self: *MachO) !void {
2873 .n_strx = 0,2977 const global = self.globals.getPtr("___dso_handle") orelse return;
2874 .n_type = macho.N_UNDF,2978 const sym = self.getSymbolPtr(global.*);
2979 if (!sym.undf()) return;
2980
2981 const gpa = self.base.allocator;
2982 const n_strx = try self.strtab.insert(gpa, "___dso_handle");
2983 const sym_index = @intCast(u32, self.locals.items.len);
2984 try self.locals.append(gpa, .{
2985 .n_strx = n_strx,
2986 .n_type = macho.N_SECT | macho.N_EXT,
2875 .n_sect = 0,2987 .n_sect = 0,
2876 .n_desc = 0,2988 .n_desc = macho.N_WEAK_DEF,
2877 .n_value = 0,2989 .n_value = 0,
2990 });
2991 global.* = .{
2992 .sym_index = sym_index,
2993 .file = null,
2878 };2994 };
2879 resolv.* = .{2995 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));
2880 .where = .global,
2881 .where_index = global_sym_index,
2882 .local_sym_index = local_sym_index,
2883 };
2884}2996}
28852997
2886fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {2998fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
2887 const object = &self.objects.items[object_id];2999 const gpa = self.base.allocator;
3000 const sym = self.getSymbol(current);
3001 const sym_name = self.getSymbolName(current);
3002
3003 const name = try gpa.dupe(u8, sym_name);
3004 const global_index = @intCast(u32, self.globals.values().len);
3005 const gop = try self.globals.getOrPut(gpa, name);
3006 defer if (gop.found_existing) gpa.free(name);
3007
3008 if (!gop.found_existing) {
3009 gop.value_ptr.* = current;
3010 if (sym.undf() and !sym.tentative()) {
3011 try self.unresolved.putNoClobber(gpa, global_index, false);
3012 }
3013 return;
3014 }
3015
3016 const global = gop.value_ptr.*;
3017 const global_sym = self.getSymbol(global);
3018
3019 // Cases to consider: sym vs global_sym
3020 // 1. strong(sym) and strong(global_sym) => error
3021 // 2. strong(sym) and weak(global_sym) => sym
3022 // 3. strong(sym) and tentative(global_sym) => sym
3023 // 4. strong(sym) and undf(global_sym) => sym
3024 // 5. weak(sym) and strong(global_sym) => global_sym
3025 // 6. weak(sym) and tentative(global_sym) => sym
3026 // 7. weak(sym) and undf(global_sym) => sym
3027 // 8. tentative(sym) and strong(global_sym) => global_sym
3028 // 9. tentative(sym) and weak(global_sym) => global_sym
3029 // 10. tentative(sym) and tentative(global_sym) => pick larger
3030 // 11. tentative(sym) and undf(global_sym) => sym
3031 // 12. undf(sym) and * => global_sym
3032 //
3033 // Reduces to:
3034 // 1. strong(sym) and strong(global_sym) => error
3035 // 2. * and strong(global_sym) => global_sym
3036 // 3. weak(sym) and weak(global_sym) => global_sym
3037 // 4. tentative(sym) and tentative(global_sym) => pick larger
3038 // 5. undf(sym) and * => global_sym
3039 // 6. else => sym
3040
3041 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
3042 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
3043 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
3044 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
3045
3046 if (sym_is_strong and global_is_strong) return error.MultipleSymbolDefinitions;
3047 if (global_is_strong) return;
3048 if (sym_is_weak and global_is_weak) return;
3049 if (sym.tentative() and global_sym.tentative()) {
3050 if (global_sym.n_value >= sym.n_value) return;
3051 }
3052 if (sym.undf() and !sym.tentative()) return;
3053
3054 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex(name).?));
3055
3056 gop.value_ptr.* = current;
3057}
28883058
3059fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2889 log.debug("resolving symbols in '{s}'", .{object.name});3060 log.debug("resolving symbols in '{s}'", .{object.name});
28903061
2891 for (object.symtab.items) |sym, id| {3062 for (object.symtab.items) |sym, index| {
2892 const sym_id = @intCast(u32, id);3063 const sym_index = @intCast(u32, index);
2893 const sym_name = object.getString(sym.n_strx);3064 const sym_name = object.getString(sym.n_strx);
28943065
2895 if (sym.stab()) {3066 if (sym.stab()) {
...@@ -2913,170 +3084,27 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2913,170 +3084,27 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2913 return error.UnhandledSymbolType;3084 return error.UnhandledSymbolType;
2914 }3085 }
29153086
2916 if (sym.sect()) {3087 if (sym.sect() and !sym.ext()) {
2917 // Defined symbol regardless of scope lands in the locals symbol table.3088 log.debug("symbol '{s}' local to object {s}; skipping...", .{
2918 const local_sym_index = @intCast(u32, self.locals.items.len);3089 sym_name,
2919 try self.locals.append(self.base.allocator, .{3090 object.name,
2920 .n_strx = if (symbolIsTemp(sym, sym_name)) 0 else try self.makeString(sym_name),
2921 .n_type = macho.N_SECT,
2922 .n_sect = 0,
2923 .n_desc = 0,
2924 .n_value = sym.n_value,
2925 });
2926 try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index);
2927 try object.reverse_symbol_mapping.putNoClobber(self.base.allocator, local_sym_index, sym_id);
2928
2929 // If the symbol's scope is not local aka translation unit, then we need work out
2930 // if we should save the symbol as a global, or potentially flag the error.
2931 if (!sym.ext()) continue;
2932
2933 const n_strx = try self.makeString(sym_name);
2934 const local = self.locals.items[local_sym_index];
2935 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
2936 const global_sym_index = @intCast(u32, self.globals.items.len);
2937 try self.globals.append(self.base.allocator, .{
2938 .n_strx = n_strx,
2939 .n_type = sym.n_type,
2940 .n_sect = 0,
2941 .n_desc = sym.n_desc,
2942 .n_value = sym.n_value,
2943 });
2944 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
2945 .where = .global,
2946 .where_index = global_sym_index,
2947 .local_sym_index = local_sym_index,
2948 .file = object_id,
2949 });
2950 continue;
2951 };
2952
2953 switch (resolv.where) {
2954 .global => {
2955 const global = &self.globals.items[resolv.where_index];
2956
2957 if (global.tentative()) {
2958 assert(self.tentatives.swapRemove(resolv.where_index));
2959 } else if (!(sym.weakDef() or sym.pext()) and !(global.weakDef() or global.pext())) {
2960 log.err("symbol '{s}' defined multiple times", .{sym_name});
2961 if (resolv.file) |file| {
2962 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2963 }
2964 log.err(" next definition in '{s}'", .{object.name});
2965 return error.MultipleSymbolDefinitions;
2966 } else if (sym.weakDef() or sym.pext()) continue; // Current symbol is weak, so skip it.
2967
2968 // Otherwise, update the resolver and the global symbol.
2969 global.n_type = sym.n_type;
2970 resolv.local_sym_index = local_sym_index;
2971 resolv.file = object_id;
2972
2973 continue;
2974 },
2975 .undef => {
2976 const undef = &self.undefs.items[resolv.where_index];
2977 undef.* = .{
2978 .n_strx = 0,
2979 .n_type = macho.N_UNDF,
2980 .n_sect = 0,
2981 .n_desc = 0,
2982 .n_value = 0,
2983 };
2984 assert(self.unresolved.swapRemove(resolv.where_index));
2985 },
2986 }
2987
2988 const global_sym_index = @intCast(u32, self.globals.items.len);
2989 try self.globals.append(self.base.allocator, .{
2990 .n_strx = local.n_strx,
2991 .n_type = sym.n_type,
2992 .n_sect = 0,
2993 .n_desc = sym.n_desc,
2994 .n_value = sym.n_value,
2995 });3091 });
2996 resolv.* = .{3092 continue;
2997 .where = .global,
2998 .where_index = global_sym_index,
2999 .local_sym_index = local_sym_index,
3000 .file = object_id,
3001 };
3002 } else if (sym.tentative()) {
3003 // Symbol is a tentative definition.
3004 const n_strx = try self.makeString(sym_name);
3005 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
3006 const global_sym_index = @intCast(u32, self.globals.items.len);
3007 try self.globals.append(self.base.allocator, .{
3008 .n_strx = try self.makeString(sym_name),
3009 .n_type = sym.n_type,
3010 .n_sect = 0,
3011 .n_desc = sym.n_desc,
3012 .n_value = sym.n_value,
3013 });
3014 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3015 .where = .global,
3016 .where_index = global_sym_index,
3017 .file = object_id,
3018 });
3019 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
3020 continue;
3021 };
3022
3023 switch (resolv.where) {
3024 .global => {
3025 const global = &self.globals.items[resolv.where_index];
3026 if (!global.tentative()) continue;
3027 if (global.n_value >= sym.n_value) continue;
3028
3029 global.n_desc = sym.n_desc;
3030 global.n_value = sym.n_value;
3031 resolv.file = object_id;
3032 },
3033 .undef => {
3034 const undef = &self.undefs.items[resolv.where_index];
3035 const global_sym_index = @intCast(u32, self.globals.items.len);
3036 try self.globals.append(self.base.allocator, .{
3037 .n_strx = undef.n_strx,
3038 .n_type = sym.n_type,
3039 .n_sect = 0,
3040 .n_desc = sym.n_desc,
3041 .n_value = sym.n_value,
3042 });
3043 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
3044 assert(self.unresolved.swapRemove(resolv.where_index));
3045
3046 resolv.* = .{
3047 .where = .global,
3048 .where_index = global_sym_index,
3049 .file = object_id,
3050 };
3051 undef.* = .{
3052 .n_strx = 0,
3053 .n_type = macho.N_UNDF,
3054 .n_sect = 0,
3055 .n_desc = 0,
3056 .n_value = 0,
3057 };
3058 },
3059 }
3060 } else {
3061 // Symbol is undefined.
3062 const n_strx = try self.makeString(sym_name);
3063 if (self.symbol_resolver.contains(n_strx)) continue;
3064
3065 const undef_sym_index = @intCast(u32, self.undefs.items.len);
3066 try self.undefs.append(self.base.allocator, .{
3067 .n_strx = try self.makeString(sym_name),
3068 .n_type = macho.N_UNDF,
3069 .n_sect = 0,
3070 .n_desc = sym.n_desc,
3071 .n_value = 0,
3072 });
3073 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3074 .where = .undef,
3075 .where_index = undef_sym_index,
3076 .file = object_id,
3077 });
3078 try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none);
3079 }3093 }
3094
3095 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = object_id };
3096 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
3097 error.MultipleSymbolDefinitions => {
3098 const global = self.globals.get(sym_name).?;
3099 log.err("symbol '{s}' defined multiple times", .{sym_name});
3100 if (global.file) |file| {
3101 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
3102 }
3103 log.err(" next definition in '{s}'", .{self.objects.items[object_id].name});
3104 return error.MultipleSymbolDefinitions;
3105 },
3106 else => |e| return e,
3107 };
3080 }3108 }
3081}3109}
30823110
...@@ -3085,8 +3113,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3085,8 +3113,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
30853113
3086 var next_sym: usize = 0;3114 var next_sym: usize = 0;
3087 loop: while (next_sym < self.unresolved.count()) {3115 loop: while (next_sym < self.unresolved.count()) {
3088 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];3116 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
3089 const sym_name = self.getString(sym.n_strx);3117 const sym_name = self.getSymbolName(global);
30903118
3091 for (self.archives.items) |archive| {3119 for (self.archives.items) |archive| {
3092 // Check if the entry exists in a static archive.3120 // Check if the entry exists in a static archive.
...@@ -3099,7 +3127,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3099,7 +3127,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
3099 const object_id = @intCast(u16, self.objects.items.len);3127 const object_id = @intCast(u16, self.objects.items.len);
3100 const object = try self.objects.addOne(self.base.allocator);3128 const object = try self.objects.addOne(self.base.allocator);
3101 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);3129 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);
3102 try self.resolveSymbolsInObject(object_id);3130 try self.resolveSymbolsInObject(object, object_id);
31033131
3104 continue :loop;3132 continue :loop;
3105 }3133 }
...@@ -3113,8 +3141,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3113,8 +3141,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31133141
3114 var next_sym: usize = 0;3142 var next_sym: usize = 0;
3115 loop: while (next_sym < self.unresolved.count()) {3143 loop: while (next_sym < self.unresolved.count()) {
3116 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];3144 const global_index = self.unresolved.keys()[next_sym];
3117 const sym_name = self.getString(sym.n_strx);3145 const global = self.globals.values()[global_index];
3146 const sym = self.getSymbolPtr(global);
3147 const sym_name = self.getSymbolName(global);
31183148
3119 for (self.dylibs.items) |dylib, id| {3149 for (self.dylibs.items) |dylib, id| {
3120 if (!dylib.symbols.contains(sym_name)) continue;3150 if (!dylib.symbols.contains(sym_name)) continue;
...@@ -3126,68 +3156,23 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3126,68 +3156,23 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3126 }3156 }
31273157
3128 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;3158 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
3129 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;3159 sym.n_type |= macho.N_EXT;
3130 const undef = &self.undefs.items[resolv.where_index];3160 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
3131 undef.n_type |= macho.N_EXT;
3132 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
31333161
3134 if (dylib.weak) {3162 if (dylib.weak) {
3135 undef.n_desc |= macho.N_WEAK_REF;3163 sym.n_desc |= macho.N_WEAK_REF;
3136 }3164 }
31373165
3138 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {3166 if (self.unresolved.fetchSwapRemove(global_index)) |entry| blk: {
3139 switch (entry.value) {3167 if (!entry.value) break :blk;
3140 .none => {},3168 if (!sym.undf()) break :blk;
3141 .got => return error.TODOGotHint,3169 if (self.stubs_table.contains(global)) break :blk;
3142 .stub => {3170
3143 if (self.stubs_table.contains(sym.n_strx)) break :outer_blk;3171 const stub_index = try self.allocateStubEntry(global);
3144 const stub_helper_atom = blk: {3172 const stub_helper_atom = try self.createStubHelperAtom();
3145 const match = MatchingSection{3173 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
3146 .seg = self.text_segment_cmd_index.?,3174 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
3147 .sect = self.stub_helper_section_index.?,3175 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
3148 };
3149 const atom = try self.createStubHelperAtom();
3150 const atom_sym = &self.locals.items[atom.local_sym_index];
3151 const alignment = try math.powi(u32, 2, atom.alignment);
3152 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3153 atom_sym.n_value = vaddr;
3154 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3155 break :blk atom;
3156 };
3157 const laptr_atom = blk: {
3158 const match = MatchingSection{
3159 .seg = self.data_segment_cmd_index.?,
3160 .sect = self.la_symbol_ptr_section_index.?,
3161 };
3162 const atom = try self.createLazyPointerAtom(
3163 stub_helper_atom.local_sym_index,
3164 sym.n_strx,
3165 );
3166 const atom_sym = &self.locals.items[atom.local_sym_index];
3167 const alignment = try math.powi(u32, 2, atom.alignment);
3168 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3169 atom_sym.n_value = vaddr;
3170 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3171 break :blk atom;
3172 };
3173 const stub_atom = blk: {
3174 const match = MatchingSection{
3175 .seg = self.text_segment_cmd_index.?,
3176 .sect = self.stubs_section_index.?,
3177 };
3178 const atom = try self.createStubAtom(laptr_atom.local_sym_index);
3179 const atom_sym = &self.locals.items[atom.local_sym_index];
3180 const alignment = try math.powi(u32, 2, atom.alignment);
3181 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3182 atom_sym.n_value = vaddr;
3183 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3184 break :blk atom;
3185 };
3186 const stub_index = @intCast(u32, self.stubs.items.len);
3187 try self.stubs.append(self.base.allocator, stub_atom);
3188 try self.stubs_table.putNoClobber(self.base.allocator, sym.n_strx, stub_index);
3189 },
3190 }
3191 }3176 }
31923177
3193 continue :loop;3178 continue :loop;
...@@ -3197,39 +3182,46 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3197,39 +3182,46 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3197 }3182 }
3198}3183}
31993184
3200fn createMhExecuteHeaderSymbol(self: *MachO) !void {3185fn resolveSymbolsAtLoading(self: *MachO) !void {
3201 if (self.base.options.output_mode != .Exe) return;3186 const is_lib = self.base.options.output_mode == .Lib;
3202 if (self.mh_execute_header_sym_index != null) return;3187 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
3188 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
32033189
3204 const n_strx = try self.makeString("__mh_execute_header");3190 var next_sym: usize = 0;
3205 const local_sym_index = @intCast(u32, self.locals.items.len);3191 while (next_sym < self.unresolved.count()) {
3206 var nlist = macho.nlist_64{3192 const global_index = self.unresolved.keys()[next_sym];
3207 .n_strx = n_strx,3193 const global = self.globals.values()[global_index];
3208 .n_type = macho.N_SECT,3194 const sym = self.getSymbolPtr(global);
3209 .n_sect = 0,3195 const sym_name = self.getSymbolName(global);
3210 .n_desc = 0,3196
3211 .n_value = 0,3197 if (sym.discarded()) {
3212 };3198 sym.* = .{
3213 try self.locals.append(self.base.allocator, nlist);3199 .n_strx = 0,
3214 self.mh_execute_header_sym_index = local_sym_index;3200 .n_type = macho.N_UNDF,
32153201 .n_sect = 0,
3216 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {3202 .n_desc = 0,
3217 const global = &self.globals.items[resolv.where_index];3203 .n_value = 0,
3218 if (!(global.weakDef() or !global.pext())) {3204 };
3219 log.err("symbol '__mh_execute_header' defined multiple times", .{});3205 _ = self.unresolved.swapRemove(global_index);
3220 return error.MultipleSymbolDefinitions;3206 continue;
3207 } else if (allow_undef) {
3208 const n_desc = @bitCast(
3209 u16,
3210 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
3211 );
3212 // TODO allow_shlib_undefined is an ELF flag so figure out macOS specific flags too.
3213 sym.n_type = macho.N_EXT;
3214 sym.n_desc = n_desc;
3215 _ = self.unresolved.swapRemove(global_index);
3216 continue;
3221 }3217 }
3222 resolv.local_sym_index = local_sym_index;3218
3223 } else {3219 log.err("undefined reference to symbol '{s}'", .{sym_name});
3224 const global_sym_index = @intCast(u32, self.globals.items.len);3220 if (global.file) |file| {
3225 nlist.n_type |= macho.N_EXT;3221 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
3226 try self.globals.append(self.base.allocator, nlist);3222 }
3227 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{3223
3228 .where = .global,3224 next_sym += 1;
3229 .where_index = global_sym_index,
3230 .local_sym_index = local_sym_index,
3231 .file = null,
3232 });
3233 }3225 }
3234}3226}
32353227
...@@ -3237,21 +3229,20 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3237,21 +3229,20 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3237 if (self.dyld_stub_binder_index != null) return;3229 if (self.dyld_stub_binder_index != null) return;
3238 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports3230 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
32393231
3240 const n_strx = try self.makeString("dyld_stub_binder");3232 const gpa = self.base.allocator;
3241 const sym_index = @intCast(u32, self.undefs.items.len);3233 const n_strx = try self.strtab.insert(gpa, "dyld_stub_binder");
3242 try self.undefs.append(self.base.allocator, .{3234 const sym_index = @intCast(u32, self.locals.items.len);
3235 try self.locals.append(gpa, .{
3243 .n_strx = n_strx,3236 .n_strx = n_strx,
3244 .n_type = macho.N_UNDF,3237 .n_type = macho.N_UNDF,
3245 .n_sect = 0,3238 .n_sect = 0,
3246 .n_desc = 0,3239 .n_desc = 0,
3247 .n_value = 0,3240 .n_value = 0,
3248 });3241 });
3249 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{3242 const sym_name = try gpa.dupe(u8, "dyld_stub_binder");
3250 .where = .undef,3243 const global = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3251 .where_index = sym_index,3244 try self.globals.putNoClobber(gpa, sym_name, global);
3252 });3245 const sym = &self.locals.items[sym_index];
3253 const sym = &self.undefs.items[sym_index];
3254 const sym_name = self.getString(n_strx);
32553246
3256 for (self.dylibs.items) |dylib, id| {3247 for (self.dylibs.items) |dylib, id| {
3257 if (!dylib.symbols.contains(sym_name)) continue;3248 if (!dylib.symbols.contains(sym_name)) continue;
...@@ -3276,205 +3267,9 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3276,205 +3267,9 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3276 }3267 }
32773268
3278 // Add dyld_stub_binder as the final GOT entry.3269 // Add dyld_stub_binder as the final GOT entry.
3279 const target = Atom.Relocation.Target{ .global = n_strx };3270 const got_index = try self.allocateGotEntry(global);
3280 const atom = try self.createGotAtom(target);3271 const got_atom = try self.createGotAtom(global);
3281 const got_index = @intCast(u32, self.got_entries.items.len);3272 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3282 try self.got_entries.append(self.base.allocator, .{ .target = target, .atom = atom });
3283 try self.got_entries_table.putNoClobber(self.base.allocator, target, got_index);
3284 const match = MatchingSection{
3285 .seg = self.data_const_segment_cmd_index.?,
3286 .sect = self.got_section_index.?,
3287 };
3288 const atom_sym = &self.locals.items[atom.local_sym_index];
3289
3290 if (self.needs_prealloc) {
3291 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
3292 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
3293 atom_sym.n_value = vaddr;
3294 } else {
3295 const seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].segment;
3296 const sect = &seg.sections.items[self.got_section_index.?];
3297 sect.size += atom.size;
3298 try self.addAtomToSection(atom, match);
3299 }
3300
3301 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3302}
3303
3304fn parseObjectsIntoAtoms(self: *MachO) !void {
3305 // TODO I need to see if I can simplify this logic, or perhaps split it into two functions:
3306 // one for non-prealloc traditional path, and one for incremental prealloc path.
3307 const tracy = trace(@src());
3308 defer tracy.end();
3309
3310 var parsed_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
3311 defer parsed_atoms.deinit();
3312
3313 var first_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
3314 defer first_atoms.deinit();
3315
3316 var section_metadata = std.AutoHashMap(MatchingSection, struct {
3317 size: u64,
3318 alignment: u32,
3319 }).init(self.base.allocator);
3320 defer section_metadata.deinit();
3321
3322 for (self.objects.items) |*object| {
3323 if (object.analyzed) continue;
3324
3325 try object.parseIntoAtoms(self.base.allocator, self);
3326
3327 var it = object.end_atoms.iterator();
3328 while (it.next()) |entry| {
3329 const match = entry.key_ptr.*;
3330 var atom = entry.value_ptr.*;
3331
3332 while (atom.prev) |prev| {
3333 atom = prev;
3334 }
3335
3336 const first_atom = atom;
3337
3338 const seg = self.load_commands.items[match.seg].segment;
3339 const sect = seg.sections.items[match.sect];
3340 const metadata = try section_metadata.getOrPut(match);
3341 if (!metadata.found_existing) {
3342 metadata.value_ptr.* = .{
3343 .size = sect.size,
3344 .alignment = sect.@"align",
3345 };
3346 }
3347
3348 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
3349
3350 while (true) {
3351 const alignment = try math.powi(u32, 2, atom.alignment);
3352 const curr_size = metadata.value_ptr.size;
3353 const curr_size_aligned = mem.alignForwardGeneric(u64, curr_size, alignment);
3354 metadata.value_ptr.size = curr_size_aligned + atom.size;
3355 metadata.value_ptr.alignment = math.max(metadata.value_ptr.alignment, atom.alignment);
3356
3357 const sym = self.locals.items[atom.local_sym_index];
3358 log.debug(" {s}: n_value=0x{x}, size=0x{x}, alignment=0x{x}", .{
3359 self.getString(sym.n_strx),
3360 sym.n_value,
3361 atom.size,
3362 atom.alignment,
3363 });
3364
3365 if (atom.next) |next| {
3366 atom = next;
3367 } else break;
3368 }
3369
3370 if (parsed_atoms.getPtr(match)) |last| {
3371 last.*.next = first_atom;
3372 first_atom.prev = last.*;
3373 last.* = first_atom;
3374 }
3375 _ = try parsed_atoms.put(match, atom);
3376
3377 if (!first_atoms.contains(match)) {
3378 try first_atoms.putNoClobber(match, first_atom);
3379 }
3380 }
3381
3382 object.analyzed = true;
3383 }
3384
3385 var it = section_metadata.iterator();
3386 while (it.next()) |entry| {
3387 const match = entry.key_ptr.*;
3388 const metadata = entry.value_ptr.*;
3389 const seg = &self.load_commands.items[match.seg].segment;
3390 const sect = &seg.sections.items[match.sect];
3391 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
3392 sect.segName(),
3393 sect.sectName(),
3394 metadata.size,
3395 metadata.alignment,
3396 });
3397
3398 sect.@"align" = math.max(sect.@"align", metadata.alignment);
3399 const needed_size = @intCast(u32, metadata.size);
3400
3401 if (self.needs_prealloc) {
3402 try self.growSection(match, needed_size);
3403 }
3404 sect.size = needed_size;
3405 }
3406
3407 for (&[_]?u16{
3408 self.text_segment_cmd_index,
3409 self.data_const_segment_cmd_index,
3410 self.data_segment_cmd_index,
3411 }) |maybe_seg_id| {
3412 const seg_id = maybe_seg_id orelse continue;
3413 const seg = self.load_commands.items[seg_id].segment;
3414
3415 for (seg.sections.items) |sect, sect_id| {
3416 const match = MatchingSection{
3417 .seg = seg_id,
3418 .sect = @intCast(u16, sect_id),
3419 };
3420 if (!section_metadata.contains(match)) continue;
3421
3422 var base_vaddr = if (self.atoms.get(match)) |last| blk: {
3423 const last_atom_sym = self.locals.items[last.local_sym_index];
3424 break :blk last_atom_sym.n_value + last.size;
3425 } else sect.addr;
3426
3427 if (self.atoms.getPtr(match)) |last| {
3428 const first_atom = first_atoms.get(match).?;
3429 last.*.next = first_atom;
3430 first_atom.prev = last.*;
3431 last.* = first_atom;
3432 }
3433 _ = try self.atoms.put(self.base.allocator, match, parsed_atoms.get(match).?);
3434
3435 if (!self.needs_prealloc) continue;
3436
3437 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3438
3439 var atom = first_atoms.get(match).?;
3440 while (true) {
3441 const alignment = try math.powi(u32, 2, atom.alignment);
3442 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
3443
3444 const sym = &self.locals.items[atom.local_sym_index];
3445 sym.n_value = base_vaddr;
3446 sym.n_sect = n_sect;
3447
3448 log.debug(" {s}: start=0x{x}, end=0x{x}, size=0x{x}, alignment=0x{x}", .{
3449 self.getString(sym.n_strx),
3450 base_vaddr,
3451 base_vaddr + atom.size,
3452 atom.size,
3453 atom.alignment,
3454 });
3455
3456 // Update each alias (if any)
3457 for (atom.aliases.items) |index| {
3458 const alias_sym = &self.locals.items[index];
3459 alias_sym.n_value = base_vaddr;
3460 alias_sym.n_sect = n_sect;
3461 }
3462
3463 // Update each symbol contained within the atom
3464 for (atom.contained.items) |sym_at_off| {
3465 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
3466 contained_sym.n_value = base_vaddr + sym_at_off.offset;
3467 contained_sym.n_sect = n_sect;
3468 }
3469
3470 base_vaddr += atom.size;
3471
3472 if (atom.next) |next| {
3473 atom = next;
3474 } else break;
3475 }
3476 }
3477 }
3478}3273}
34793274
3480fn addLoadDylibLC(self: *MachO, id: u16) !void {3275fn addLoadDylibLC(self: *MachO, id: u16) !void {
...@@ -3511,16 +3306,8 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3511,16 +3306,8 @@ fn setEntryPoint(self: *MachO) !void {
3511 if (self.base.options.output_mode != .Exe) return;3306 if (self.base.options.output_mode != .Exe) return;
35123307
3513 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;3308 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
3514 const entry_name = self.base.options.entry orelse "_main";3309 const global = try self.getEntryPoint();
3515 const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{3310 const sym = self.getSymbol(global);
3516 .bytes = &self.strtab,
3517 }) orelse {
3518 log.err("entrypoint '{s}' not found", .{entry_name});
3519 return error.MissingMainEntrypoint;
3520 };
3521 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
3522 assert(resolv.where == .global);
3523 const sym = self.globals.items[resolv.where_index];
3524 const ec = &self.load_commands.items[self.main_cmd_index.?].main;3311 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
3525 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);3312 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
3526 ec.stacksize = self.base.options.stack_size_override orelse 0;3313 ec.stacksize = self.base.options.stack_size_override orelse 0;
...@@ -3529,76 +3316,77 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3529,76 +3316,77 @@ fn setEntryPoint(self: *MachO) !void {
3529}3316}
35303317
3531pub fn deinit(self: *MachO) void {3318pub fn deinit(self: *MachO) void {
3319 const gpa = self.base.allocator;
3320
3532 if (build_options.have_llvm) {3321 if (build_options.have_llvm) {
3533 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);3322 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
3534 }3323 }
35353324
3536 if (self.d_sym) |*d_sym| {3325 if (self.d_sym) |*d_sym| {
3537 d_sym.deinit(self.base.allocator);3326 d_sym.deinit(gpa);
3538 }3327 }
35393328
3540 self.section_ordinals.deinit(self.base.allocator);3329 self.section_ordinals.deinit(gpa);
3541 self.tlv_ptr_entries.deinit(self.base.allocator);3330 self.tlv_ptr_entries.deinit(gpa);
3542 self.tlv_ptr_entries_free_list.deinit(self.base.allocator);3331 self.tlv_ptr_entries_free_list.deinit(gpa);
3543 self.tlv_ptr_entries_table.deinit(self.base.allocator);3332 self.tlv_ptr_entries_table.deinit(gpa);
3544 self.got_entries.deinit(self.base.allocator);3333 self.got_entries.deinit(gpa);
3545 self.got_entries_free_list.deinit(self.base.allocator);3334 self.got_entries_free_list.deinit(gpa);
3546 self.got_entries_table.deinit(self.base.allocator);3335 self.got_entries_table.deinit(gpa);
3547 self.stubs.deinit(self.base.allocator);3336 self.stubs.deinit(gpa);
3548 self.stubs_free_list.deinit(self.base.allocator);3337 self.stubs_free_list.deinit(gpa);
3549 self.stubs_table.deinit(self.base.allocator);3338 self.stubs_table.deinit(gpa);
3550 self.strtab_dir.deinit(self.base.allocator);3339 self.strtab.deinit(gpa);
3551 self.strtab.deinit(self.base.allocator);3340 self.locals.deinit(gpa);
3552 self.undefs.deinit(self.base.allocator);3341 self.locals_free_list.deinit(gpa);
3553 self.globals.deinit(self.base.allocator);3342 self.unresolved.deinit(gpa);
3554 self.globals_free_list.deinit(self.base.allocator);3343
3555 self.locals.deinit(self.base.allocator);3344 for (self.globals.keys()) |key| {
3556 self.locals_free_list.deinit(self.base.allocator);3345 gpa.free(key);
3557 self.symbol_resolver.deinit(self.base.allocator);3346 }
3558 self.unresolved.deinit(self.base.allocator);3347 self.globals.deinit(gpa);
3559 self.tentatives.deinit(self.base.allocator);
35603348
3561 for (self.objects.items) |*object| {3349 for (self.objects.items) |*object| {
3562 object.deinit(self.base.allocator);3350 object.deinit(gpa);
3563 }3351 }
3564 self.objects.deinit(self.base.allocator);3352 self.objects.deinit(gpa);
35653353
3566 for (self.archives.items) |*archive| {3354 for (self.archives.items) |*archive| {
3567 archive.deinit(self.base.allocator);3355 archive.deinit(gpa);
3568 }3356 }
3569 self.archives.deinit(self.base.allocator);3357 self.archives.deinit(gpa);
35703358
3571 for (self.dylibs.items) |*dylib| {3359 for (self.dylibs.items) |*dylib| {
3572 dylib.deinit(self.base.allocator);3360 dylib.deinit(gpa);
3573 }3361 }
3574 self.dylibs.deinit(self.base.allocator);3362 self.dylibs.deinit(gpa);
3575 self.dylibs_map.deinit(self.base.allocator);3363 self.dylibs_map.deinit(gpa);
3576 self.referenced_dylibs.deinit(self.base.allocator);3364 self.referenced_dylibs.deinit(gpa);
35773365
3578 for (self.load_commands.items) |*lc| {3366 for (self.load_commands.items) |*lc| {
3579 lc.deinit(self.base.allocator);3367 lc.deinit(gpa);
3580 }3368 }
3581 self.load_commands.deinit(self.base.allocator);3369 self.load_commands.deinit(gpa);
35823370
3583 for (self.managed_atoms.items) |atom| {3371 for (self.managed_atoms.items) |atom| {
3584 atom.deinit(self.base.allocator);3372 atom.deinit(gpa);
3585 self.base.allocator.destroy(atom);3373 gpa.destroy(atom);
3586 }3374 }
3587 self.managed_atoms.deinit(self.base.allocator);3375 self.managed_atoms.deinit(gpa);
3588 self.atoms.deinit(self.base.allocator);3376 self.atoms.deinit(gpa);
3589 {3377 {
3590 var it = self.atom_free_lists.valueIterator();3378 var it = self.atom_free_lists.valueIterator();
3591 while (it.next()) |free_list| {3379 while (it.next()) |free_list| {
3592 free_list.deinit(self.base.allocator);3380 free_list.deinit(gpa);
3593 }3381 }
3594 self.atom_free_lists.deinit(self.base.allocator);3382 self.atom_free_lists.deinit(gpa);
3595 }3383 }
3596 if (self.base.options.module) |mod| {3384 if (self.base.options.module) |mod| {
3597 for (self.decls.keys()) |decl_index| {3385 for (self.decls.keys()) |decl_index| {
3598 const decl = mod.declPtr(decl_index);3386 const decl = mod.declPtr(decl_index);
3599 decl.link.macho.deinit(self.base.allocator);3387 decl.link.macho.deinit(gpa);
3600 }3388 }
3601 self.decls.deinit(self.base.allocator);3389 self.decls.deinit(gpa);
3602 } else {3390 } else {
3603 assert(self.decls.count() == 0);3391 assert(self.decls.count() == 0);
3604 }3392 }
...@@ -3606,15 +3394,15 @@ pub fn deinit(self: *MachO) void {...@@ -3606,15 +3394,15 @@ pub fn deinit(self: *MachO) void {
3606 {3394 {
3607 var it = self.unnamed_const_atoms.valueIterator();3395 var it = self.unnamed_const_atoms.valueIterator();
3608 while (it.next()) |atoms| {3396 while (it.next()) |atoms| {
3609 atoms.deinit(self.base.allocator);3397 atoms.deinit(gpa);
3610 }3398 }
3611 self.unnamed_const_atoms.deinit(self.base.allocator);3399 self.unnamed_const_atoms.deinit(gpa);
3612 }3400 }
36133401
3614 self.atom_by_index_table.deinit(self.base.allocator);3402 self.atom_by_index_table.deinit(gpa);
36153403
3616 if (self.code_signature) |*csig| {3404 if (self.code_signature) |*csig| {
3617 csig.deinit(self.base.allocator);3405 csig.deinit(gpa);
3618 }3406 }
3619}3407}
36203408
...@@ -3670,7 +3458,7 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3670,7 +3458,7 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3670 if (atom.prev) |prev| {3458 if (atom.prev) |prev| {
3671 prev.next = atom.next;3459 prev.next = atom.next;
36723460
3673 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {3461 if (!already_have_free_list_node and prev.freeListEligible(self)) {
3674 // The free list is heuristics, it doesn't have to be perfect, so we can ignore3462 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
3675 // the OOM here.3463 // the OOM here.
3676 free_list.append(self.base.allocator, prev) catch {};3464 free_list.append(self.base.allocator, prev) catch {};
...@@ -3700,14 +3488,14 @@ fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSec...@@ -3700,14 +3488,14 @@ fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSec
3700}3488}
37013489
3702fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {3490fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
3703 const sym = self.locals.items[atom.local_sym_index];3491 const sym = atom.getSymbol(self);
3704 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;3492 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3705 const need_realloc = !align_ok or new_atom_size > atom.capacity(self.*);3493 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
3706 if (!need_realloc) return sym.n_value;3494 if (!need_realloc) return sym.n_value;
3707 return self.allocateAtom(atom, new_atom_size, alignment, match);3495 return self.allocateAtom(atom, new_atom_size, alignment, match);
3708}3496}
37093497
3710fn allocateLocalSymbol(self: *MachO) !u32 {3498fn allocateSymbol(self: *MachO) !u32 {
3711 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);3499 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
37123500
3713 const index = blk: {3501 const index = blk: {
...@@ -3733,8 +3521,9 @@ fn allocateLocalSymbol(self: *MachO) !u32 {...@@ -3733,8 +3521,9 @@ fn allocateLocalSymbol(self: *MachO) !u32 {
3733 return index;3521 return index;
3734}3522}
37353523
3736pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {3524pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3737 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);3525 const gpa = self.base.allocator;
3526 try self.got_entries.ensureUnusedCapacity(gpa, 1);
37383527
3739 const index = blk: {3528 const index = blk: {
3740 if (self.got_entries_free_list.popOrNull()) |index| {3529 if (self.got_entries_free_list.popOrNull()) |index| {
...@@ -3748,16 +3537,13 @@ pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {...@@ -3748,16 +3537,13 @@ pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3748 }3537 }
3749 };3538 };
37503539
3751 self.got_entries.items[index] = .{3540 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
3752 .target = target,3541 try self.got_entries_table.putNoClobber(gpa, target, index);
3753 .atom = undefined,
3754 };
3755 try self.got_entries_table.putNoClobber(self.base.allocator, target, index);
37563542
3757 return index;3543 return index;
3758}3544}
37593545
3760pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {3546pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3761 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);3547 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);
37623548
3763 const index = blk: {3549 const index = blk: {
...@@ -3772,13 +3558,13 @@ pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {...@@ -3772,13 +3558,13 @@ pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {
3772 }3558 }
3773 };3559 };
37743560
3775 self.stubs.items[index] = undefined;3561 self.stubs.items[index] = .{ .target = target, .sym_index = 0 };
3776 try self.stubs_table.putNoClobber(self.base.allocator, n_strx, index);3562 try self.stubs_table.putNoClobber(self.base.allocator, target, index);
37773563
3778 return index;3564 return index;
3779}3565}
37803566
3781pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {3567pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3782 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);3568 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);
37833569
3784 const index = blk: {3570 const index = blk: {
...@@ -3793,7 +3579,7 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {...@@ -3793,7 +3579,7 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3793 }3579 }
3794 };3580 };
37953581
3796 self.tlv_ptr_entries.items[index] = .{ .target = target, .atom = undefined };3582 self.tlv_ptr_entries.items[index] = .{ .target = target, .sym_index = 0 };
3797 try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index);3583 try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index);
37983584
3799 return index;3585 return index;
...@@ -3802,16 +3588,11 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {...@@ -3802,16 +3588,11 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3802pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {3588pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
3803 if (self.llvm_object) |_| return;3589 if (self.llvm_object) |_| return;
3804 const decl = self.base.options.module.?.declPtr(decl_index);3590 const decl = self.base.options.module.?.declPtr(decl_index);
3805 if (decl.link.macho.local_sym_index != 0) return;3591 if (decl.link.macho.sym_index != 0) return;
38063592
3807 decl.link.macho.local_sym_index = try self.allocateLocalSymbol();3593 decl.link.macho.sym_index = try self.allocateSymbol();
3808 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho);3594 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
3809 try self.decls.putNoClobber(self.base.allocator, decl_index, null);3595 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
3810
3811 const got_target = .{ .local = decl.link.macho.local_sym_index };
3812 const got_index = try self.allocateGotEntry(got_target);
3813 const got_atom = try self.createGotAtom(got_target);
3814 self.got_entries.items[got_index].atom = got_atom;
3815}3596}
38163597
3817pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {3598pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -3862,14 +3643,14 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -3862,14 +3643,14 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
3862 },3643 },
3863 }3644 }
38643645
3865 const symbol = try self.placeDecl(decl_index, decl.link.macho.code.items.len);3646 const addr = try self.placeDecl(decl_index, decl.link.macho.code.items.len);
38663647
3867 if (decl_state) |*ds| {3648 if (decl_state) |*ds| {
3868 try self.d_sym.?.dwarf.commitDeclState(3649 try self.d_sym.?.dwarf.commitDeclState(
3869 &self.base,3650 &self.base,
3870 module,3651 module,
3871 decl,3652 decl,
3872 symbol.n_value,3653 addr,
3873 decl.link.macho.size,3654 decl.link.macho.size,
3874 ds,3655 ds,
3875 );3656 );
...@@ -3885,8 +3666,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3885,8 +3666,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3885 var code_buffer = std.ArrayList(u8).init(self.base.allocator);3666 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3886 defer code_buffer.deinit();3667 defer code_buffer.deinit();
38873668
3669 const gpa = self.base.allocator;
3888 const module = self.base.options.module.?;3670 const module = self.base.options.module.?;
3889 const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);3671 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
3890 if (!gop.found_existing) {3672 if (!gop.found_existing) {
3891 gop.value_ptr.* = .{};3673 gop.value_ptr.* = .{};
3892 }3674 }
...@@ -3894,25 +3676,32 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3894,25 +3676,32 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
38943676
3895 const decl = module.declPtr(decl_index);3677 const decl = module.declPtr(decl_index);
3896 const decl_name = try decl.getFullyQualifiedName(module);3678 const decl_name = try decl.getFullyQualifiedName(module);
3897 defer self.base.allocator.free(decl_name);3679 defer gpa.free(decl_name);
38983680
3899 const name_str_index = blk: {3681 const name_str_index = blk: {
3900 const index = unnamed_consts.items.len;3682 const index = unnamed_consts.items.len;
3901 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });3683 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
3902 defer self.base.allocator.free(name);3684 defer gpa.free(name);
3903 break :blk try self.makeString(name);3685 break :blk try self.strtab.insert(gpa, name);
3904 };3686 };
3905 const name = self.getString(name_str_index);3687 const name = self.strtab.get(name_str_index);
39063688
3907 log.debug("allocating symbol indexes for {s}", .{name});3689 log.debug("allocating symbol indexes for {s}", .{name});
39083690
3909 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);3691 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3910 const local_sym_index = try self.allocateLocalSymbol();3692 const sym_index = try self.allocateSymbol();
3911 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));3693 const atom = try MachO.createEmptyAtom(
3912 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);3694 gpa,
3695 sym_index,
3696 @sizeOf(u64),
3697 math.log2(required_alignment),
3698 );
3699
3700 try self.managed_atoms.append(gpa, atom);
3701 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
39133702
3914 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{3703 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
3915 .parent_atom_index = local_sym_index,3704 .parent_atom_index = sym_index,
3916 });3705 });
3917 const code = switch (res) {3706 const code = switch (res) {
3918 .externally_managed => |x| x,3707 .externally_managed => |x| x,
...@@ -3926,7 +3715,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3926,7 +3715,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3926 };3715 };
39273716
3928 atom.code.clearRetainingCapacity();3717 atom.code.clearRetainingCapacity();
3929 try atom.code.appendSlice(self.base.allocator, code);3718 try atom.code.appendSlice(gpa, code);
39303719
3931 const match = try self.getMatchingSectionAtom(3720 const match = try self.getMatchingSectionAtom(
3932 atom,3721 atom,
...@@ -3942,18 +3731,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3942,18 +3731,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
39423731
3943 errdefer self.freeAtom(atom, match, true);3732 errdefer self.freeAtom(atom, match, true);
39443733
3945 const symbol = &self.locals.items[atom.local_sym_index];3734 const symbol = atom.getSymbolPtr(self);
3946 symbol.* = .{3735 symbol.* = .{
3947 .n_strx = name_str_index,3736 .n_strx = name_str_index,
3948 .n_type = macho.N_SECT,3737 .n_type = macho.N_SECT,
3949 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,3738 .n_sect = self.getSectionOrdinal(match),
3950 .n_desc = 0,3739 .n_desc = 0,
3951 .n_value = addr,3740 .n_value = addr,
3952 };3741 };
39533742
3954 try unnamed_consts.append(self.base.allocator, atom);3743 try unnamed_consts.append(gpa, atom);
39553744
3956 return atom.local_sym_index;3745 return atom.sym_index;
3957}3746}
39583747
3959pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {3748pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -3995,14 +3784,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3995,14 +3784,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3995 }, &code_buffer, .{3784 }, &code_buffer, .{
3996 .dwarf = ds,3785 .dwarf = ds,
3997 }, .{3786 }, .{
3998 .parent_atom_index = decl.link.macho.local_sym_index,3787 .parent_atom_index = decl.link.macho.sym_index,
3999 })3788 })
4000 else3789 else
4001 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{3790 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
4002 .ty = decl.ty,3791 .ty = decl.ty,
4003 .val = decl_val,3792 .val = decl_val,
4004 }, &code_buffer, .none, .{3793 }, &code_buffer, .none, .{
4005 .parent_atom_index = decl.link.macho.local_sym_index,3794 .parent_atom_index = decl.link.macho.sym_index,
4006 });3795 });
40073796
4008 const code = blk: {3797 const code = blk: {
...@@ -4025,14 +3814,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -4025,14 +3814,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
4025 },3814 },
4026 }3815 }
4027 };3816 };
4028 const symbol = try self.placeDecl(decl_index, code.len);3817 const addr = try self.placeDecl(decl_index, code.len);
40293818
4030 if (decl_state) |*ds| {3819 if (decl_state) |*ds| {
4031 try self.d_sym.?.dwarf.commitDeclState(3820 try self.d_sym.?.dwarf.commitDeclState(
4032 &self.base,3821 &self.base,
4033 module,3822 module,
4034 decl,3823 decl,
4035 symbol.n_value,3824 addr,
4036 decl.link.macho.size,3825 decl.link.macho.size,
4037 ds,3826 ds,
4038 );3827 );
...@@ -4177,8 +3966,7 @@ fn getMatchingSectionAtom(...@@ -4177,8 +3966,7 @@ fn getMatchingSectionAtom(
4177 .@"align" = align_log_2,3966 .@"align" = align_log_2,
4178 })).?;3967 })).?;
4179 };3968 };
4180 const seg = self.load_commands.items[match.seg].segment;3969 const sect = self.getSection(match);
4181 const sect = seg.sections.items[match.sect];
4182 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{3970 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
4183 name,3971 name,
4184 sect.segName(),3972 sect.segName(),
...@@ -4189,12 +3977,11 @@ fn getMatchingSectionAtom(...@@ -4189,12 +3977,11 @@ fn getMatchingSectionAtom(
4189 return match;3977 return match;
4190}3978}
41913979
4192fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*macho.nlist_64 {3980fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !u64 {
4193 const module = self.base.options.module.?;3981 const module = self.base.options.module.?;
4194 const decl = module.declPtr(decl_index);3982 const decl = module.declPtr(decl_index);
4195 const required_alignment = decl.getAlignment(self.base.options.target);3983 const required_alignment = decl.getAlignment(self.base.options.target);
4196 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()3984 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
4197 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
41983985
4199 const sym_name = try decl.getFullyQualifiedName(module);3986 const sym_name = try decl.getFullyQualifiedName(module);
4200 defer self.base.allocator.free(sym_name);3987 defer self.base.allocator.free(sym_name);
...@@ -4212,7 +3999,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4212,7 +3999,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4212 const match = decl_ptr.*.?;3999 const match = decl_ptr.*.?;
42134000
4214 if (decl.link.macho.size != 0) {4001 if (decl.link.macho.size != 0) {
4215 const capacity = decl.link.macho.capacity(self.*);4002 const symbol = decl.link.macho.getSymbolPtr(self);
4003 const capacity = decl.link.macho.capacity(self);
4216 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);4004 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
42174005
4218 if (need_realloc) {4006 if (need_realloc) {
...@@ -4220,18 +4008,24 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4220,18 +4008,24 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4220 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });4008 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, symbol.n_value, vaddr });
4221 log.debug(" (required alignment 0x{x})", .{required_alignment});4009 log.debug(" (required alignment 0x{x})", .{required_alignment});
4222 symbol.n_value = vaddr;4010 symbol.n_value = vaddr;
4011
4012 const got_atom = self.getGotAtomForSymbol(.{
4013 .sym_index = decl.link.macho.sym_index,
4014 .file = null,
4015 }).?;
4016 got_atom.dirty = true;
4223 } else if (code_len < decl.link.macho.size) {4017 } else if (code_len < decl.link.macho.size) {
4224 self.shrinkAtom(&decl.link.macho, code_len, match);4018 self.shrinkAtom(&decl.link.macho, code_len, match);
4225 }4019 }
4226 decl.link.macho.size = code_len;4020 decl.link.macho.size = code_len;
4227 decl.link.macho.dirty = true;4021 decl.link.macho.dirty = true;
42284022
4229 symbol.n_strx = try self.makeString(sym_name);4023 symbol.n_strx = try self.strtab.insert(self.base.allocator, sym_name);
4230 symbol.n_type = macho.N_SECT;4024 symbol.n_type = macho.N_SECT;
4231 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;4025 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
4232 symbol.n_desc = 0;4026 symbol.n_desc = 0;
4233 } else {4027 } else {
4234 const name_str_index = try self.makeString(sym_name);4028 const name_str_index = try self.strtab.insert(self.base.allocator, sym_name);
4235 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);4029 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
42364030
4237 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });4031 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });
...@@ -4239,28 +4033,22 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4239,28 +4033,22 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
42394033
4240 errdefer self.freeAtom(&decl.link.macho, match, false);4034 errdefer self.freeAtom(&decl.link.macho, match, false);
42414035
4036 const symbol = decl.link.macho.getSymbolPtr(self);
4242 symbol.* = .{4037 symbol.* = .{
4243 .n_strx = name_str_index,4038 .n_strx = name_str_index,
4244 .n_type = macho.N_SECT,4039 .n_type = macho.N_SECT,
4245 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,4040 .n_sect = self.getSectionOrdinal(match),
4246 .n_desc = 0,4041 .n_desc = 0,
4247 .n_value = addr,4042 .n_value = addr,
4248 };4043 };
4249 const got_index = self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index }).?;4044
4250 const got_atom = self.got_entries.items[got_index].atom;4045 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
4251 const got_sym = &self.locals.items[got_atom.local_sym_index];4046 const got_index = try self.allocateGotEntry(got_target);
4252 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{4047 const got_atom = try self.createGotAtom(got_target);
4253 .seg = self.data_const_segment_cmd_index.?,4048 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
4254 .sect = self.got_section_index.?,
4255 });
4256 got_sym.n_value = vaddr;
4257 got_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{
4258 .seg = self.data_const_segment_cmd_index.?,
4259 .sect = self.got_section_index.?,
4260 }).? + 1);
4261 }4049 }
42624050
4263 return symbol;4051 return decl.link.macho.getSymbol(self).n_value;
4264}4052}
42654053
4266pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {4054pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
...@@ -4280,19 +4068,23 @@ pub fn updateDeclExports(...@@ -4280,19 +4068,23 @@ pub fn updateDeclExports(
4280 @panic("Attempted to compile for object format that was disabled by build configuration");4068 @panic("Attempted to compile for object format that was disabled by build configuration");
4281 }4069 }
4282 if (build_options.have_llvm) {4070 if (build_options.have_llvm) {
4283 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);4071 if (self.llvm_object) |llvm_object|
4072 return llvm_object.updateDeclExports(module, decl_index, exports);
4284 }4073 }
4285 const tracy = trace(@src());4074 const tracy = trace(@src());
4286 defer tracy.end();4075 defer tracy.end();
42874076
4288 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);4077 const gpa = self.base.allocator;
4078
4289 const decl = module.declPtr(decl_index);4079 const decl = module.declPtr(decl_index);
4290 if (decl.link.macho.local_sym_index == 0) return;4080 if (decl.link.macho.sym_index == 0) return;
4291 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];4081 const decl_sym = decl.link.macho.getSymbol(self);
42924082
4293 for (exports) |exp| {4083 for (exports) |exp| {
4294 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});4084 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
4295 defer self.base.allocator.free(exp_name);4085 defer gpa.free(exp_name);
4086
4087 log.debug("adding new export '{s}'", .{exp_name});
42964088
4297 if (exp.options.section) |section_name| {4089 if (exp.options.section) |section_name| {
4298 if (!mem.eql(u8, section_name, "__text")) {4090 if (!mem.eql(u8, section_name, "__text")) {
...@@ -4300,7 +4092,7 @@ pub fn updateDeclExports(...@@ -4300,7 +4092,7 @@ pub fn updateDeclExports(
4300 module.gpa,4092 module.gpa,
4301 exp,4093 exp,
4302 try Module.ErrorMsg.create(4094 try Module.ErrorMsg.create(
4303 self.base.allocator,4095 gpa,
4304 decl.srcLoc(),4096 decl.srcLoc(),
4305 "Unimplemented: ExportOptions.section",4097 "Unimplemented: ExportOptions.section",
4306 .{},4098 .{},
...@@ -4315,7 +4107,7 @@ pub fn updateDeclExports(...@@ -4315,7 +4107,7 @@ pub fn updateDeclExports(
4315 module.gpa,4107 module.gpa,
4316 exp,4108 exp,
4317 try Module.ErrorMsg.create(4109 try Module.ErrorMsg.create(
4318 self.base.allocator,4110 gpa,
4319 decl.srcLoc(),4111 decl.srcLoc(),
4320 "Unimplemented: GlobalLinkage.LinkOnce",4112 "Unimplemented: GlobalLinkage.LinkOnce",
4321 .{},4113 .{},
...@@ -4324,103 +4116,85 @@ pub fn updateDeclExports(...@@ -4324,103 +4116,85 @@ pub fn updateDeclExports(
4324 continue;4116 continue;
4325 }4117 }
43264118
4327 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;4119 const sym_index = exp.link.macho.sym_index orelse blk: {
4328 const n_strx = try self.makeString(exp_name);4120 const sym_index = try self.allocateSymbol();
4329 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {4121 exp.link.macho.sym_index = sym_index;
4330 switch (resolv.where) {4122 break :blk sym_index;
4331 .global => {4123 };
4332 if (resolv.local_sym_index == decl.link.macho.local_sym_index) continue;4124 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
43334125 const sym = self.getSymbolPtr(sym_loc);
4334 const sym = &self.globals.items[resolv.where_index];4126 sym.* = .{
43354127 .n_strx = try self.strtab.insert(gpa, exp_name),
4336 if (sym.tentative()) {4128 .n_type = macho.N_SECT | macho.N_EXT,
4337 assert(self.tentatives.swapRemove(resolv.where_index));4129 .n_sect = self.getSectionOrdinal(.{
4338 } else if (!is_weak and !(sym.weakDef() or sym.pext())) {4130 .seg = self.text_segment_cmd_index.?,
4339 _ = try module.failed_exports.put(4131 .sect = self.text_section_index.?, // TODO what if we export a variable?
4340 module.gpa,4132 }),
4341 exp,4133 .n_desc = 0,
4342 try Module.ErrorMsg.create(4134 .n_value = decl_sym.n_value,
4343 self.base.allocator,4135 };
4344 decl.srcLoc(),
4345 \\LinkError: symbol '{s}' defined multiple times
4346 \\ first definition in '{s}'
4347 ,
4348 .{ exp_name, self.objects.items[resolv.file.?].name },
4349 ),
4350 );
4351 continue;
4352 } else if (is_weak) continue; // Current symbol is weak, so skip it.
4353
4354 // Otherwise, update the resolver and the global symbol.
4355 sym.n_type = macho.N_SECT | macho.N_EXT;
4356 resolv.local_sym_index = decl.link.macho.local_sym_index;
4357 resolv.file = null;
4358 exp.link.macho.sym_index = resolv.where_index;
4359
4360 continue;
4361 },
4362 .undef => {
4363 assert(self.unresolved.swapRemove(resolv.where_index));
4364 _ = self.symbol_resolver.remove(n_strx);
4365 },
4366 }
4367 }
4368
4369 var n_type: u8 = macho.N_SECT | macho.N_EXT;
4370 var n_desc: u16 = 0;
43714136
4372 switch (exp.options.linkage) {4137 switch (exp.options.linkage) {
4373 .Internal => {4138 .Internal => {
4374 // Symbol should be hidden, or in MachO lingo, private extern.4139 // Symbol should be hidden, or in MachO lingo, private extern.
4375 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.4140 // We should also mark the symbol as Weak: n_desc == N_WEAK_DEF.
4376 // TODO work out when to add N_WEAK_REF.4141 sym.n_type |= macho.N_PEXT;
4377 n_type |= macho.N_PEXT;4142 sym.n_desc |= macho.N_WEAK_DEF;
4378 n_desc |= macho.N_WEAK_DEF;
4379 },4143 },
4380 .Strong => {},4144 .Strong => {},
4381 .Weak => {4145 .Weak => {
4382 // Weak linkage is specified as part of n_desc field.4146 // Weak linkage is specified as part of n_desc field.
4383 // Symbol's n_type is like for a symbol with strong linkage.4147 // Symbol's n_type is like for a symbol with strong linkage.
4384 n_desc |= macho.N_WEAK_DEF;4148 sym.n_desc |= macho.N_WEAK_DEF;
4385 },4149 },
4386 else => unreachable,4150 else => unreachable,
4387 }4151 }
43884152
4389 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {4153 self.resolveGlobalSymbol(sym_loc) catch |err| switch (err) {
4390 const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {4154 error.MultipleSymbolDefinitions => {
4391 _ = self.globals.addOneAssumeCapacity();4155 const global = self.globals.get(exp_name).?;
4392 break :inner @intCast(u32, self.globals.items.len - 1);4156 if (sym_loc.sym_index != global.sym_index and global.file != null) {
4393 };4157 _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
4394 break :blk i;4158 gpa,
4395 };4159 decl.srcLoc(),
4396 const sym = &self.globals.items[global_sym_index];4160 \\LinkError: symbol '{s}' defined multiple times
4397 sym.* = .{4161 \\ first definition in '{s}'
4398 .n_strx = try self.makeString(exp_name),4162 ,
4399 .n_type = n_type,4163 .{ exp_name, self.objects.items[global.file.?].name },
4400 .n_sect = @intCast(u8, self.text_section_index.?) + 1,4164 ));
4401 .n_desc = n_desc,4165 }
4402 .n_value = decl_sym.n_value,4166 },
4167 else => |e| return e,
4403 };4168 };
4404 exp.link.macho.sym_index = global_sym_index;
4405
4406 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4407 .where = .global,
4408 .where_index = global_sym_index,
4409 .local_sym_index = decl.link.macho.local_sym_index,
4410 });
4411 }4169 }
4412}4170}
44134171
4414pub fn deleteExport(self: *MachO, exp: Export) void {4172pub fn deleteExport(self: *MachO, exp: Export) void {
4415 if (self.llvm_object) |_| return;4173 if (self.llvm_object) |_| return;
4416 const sym_index = exp.sym_index orelse return;4174 const sym_index = exp.sym_index orelse return;
4417 self.globals_free_list.append(self.base.allocator, sym_index) catch {};4175
4418 const global = &self.globals.items[sym_index];4176 const gpa = self.base.allocator;
4419 log.debug("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });4177
4420 assert(self.symbol_resolver.remove(global.n_strx));4178 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
4421 global.n_type = 0;4179 const sym = self.getSymbolPtr(sym_loc);
4422 global.n_strx = 0;4180 const sym_name = self.getSymbolName(sym_loc);
4423 global.n_value = 0;4181 log.debug("deleting export '{s}'", .{sym_name});
4182 assert(sym.sect() and sym.ext());
4183 sym.* = .{
4184 .n_strx = 0,
4185 .n_type = 0,
4186 .n_sect = 0,
4187 .n_desc = 0,
4188 .n_value = 0,
4189 };
4190 self.locals_free_list.append(gpa, sym_index) catch {};
4191
4192 if (self.globals.get(sym_name)) |global| blk: {
4193 if (global.sym_index != sym_index) break :blk;
4194 if (global.file != null) break :blk;
4195 const kv = self.globals.fetchSwapRemove(sym_name);
4196 gpa.free(kv.?.key);
4197 }
4424}4198}
44254199
4426fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {4200fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -4430,11 +4204,11 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4430,11 +4204,11 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
4430 .seg = self.text_segment_cmd_index.?,4204 .seg = self.text_segment_cmd_index.?,
4431 .sect = self.text_const_section_index.?,4205 .sect = self.text_const_section_index.?,
4432 }, true);4206 }, true);
4433 self.locals_free_list.append(self.base.allocator, atom.local_sym_index) catch {};4207 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
4434 self.locals.items[atom.local_sym_index].n_type = 0;4208 self.locals.items[atom.sym_index].n_type = 0;
4435 _ = self.atom_by_index_table.remove(atom.local_sym_index);4209 _ = self.atom_by_index_table.remove(atom.sym_index);
4436 log.debug(" adding local symbol index {d} to free list", .{atom.local_sym_index});4210 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
4437 atom.local_sym_index = 0;4211 atom.sym_index = 0;
4438 }4212 }
4439 unnamed_consts.clearAndFree(self.base.allocator);4213 unnamed_consts.clearAndFree(self.base.allocator);
4440}4214}
...@@ -4452,29 +4226,33 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4452,29 +4226,33 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
4452 self.freeUnnamedConsts(decl_index);4226 self.freeUnnamedConsts(decl_index);
4453 }4227 }
4454 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.4228 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
4455 if (decl.link.macho.local_sym_index != 0) {4229 if (decl.link.macho.sym_index != 0) {
4456 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};4230 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};
44574231
4458 // Try freeing GOT atom if this decl had one4232 // Try freeing GOT atom if this decl had one
4459 if (self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index })) |got_index| {4233 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
4234 if (self.got_entries_table.get(got_target)) |got_index| {
4460 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};4235 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
4461 self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };4236 self.got_entries.items[got_index] = .{
4462 _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });4237 .target = .{ .sym_index = 0, .file = null },
4238 .sym_index = 0,
4239 };
4240 _ = self.got_entries_table.remove(got_target);
44634241
4464 if (self.d_sym) |*d_sym| {4242 if (self.d_sym) |*d_sym| {
4465 d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index);4243 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);
4466 }4244 }
44674245
4468 log.debug(" adding GOT index {d} to free list (target local@{d})", .{4246 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
4469 got_index,4247 got_index,
4470 decl.link.macho.local_sym_index,4248 decl.link.macho.sym_index,
4471 });4249 });
4472 }4250 }
44734251
4474 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;4252 self.locals.items[decl.link.macho.sym_index].n_type = 0;
4475 _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index);4253 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);
4476 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.local_sym_index});4254 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});
4477 decl.link.macho.local_sym_index = 0;4255 decl.link.macho.sym_index = 0;
4478 }4256 }
4479 if (self.d_sym) |*d_sym| {4257 if (self.d_sym) |*d_sym| {
4480 d_sym.dwarf.freeDecl(decl);4258 d_sym.dwarf.freeDecl(decl);
...@@ -4486,12 +4264,12 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -4486,12 +4264,12 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
4486 const decl = mod.declPtr(decl_index);4264 const decl = mod.declPtr(decl_index);
44874265
4488 assert(self.llvm_object == null);4266 assert(self.llvm_object == null);
4489 assert(decl.link.macho.local_sym_index != 0);4267 assert(decl.link.macho.sym_index != 0);
44904268
4491 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;4269 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
4492 try atom.relocs.append(self.base.allocator, .{4270 try atom.relocs.append(self.base.allocator, .{
4493 .offset = @intCast(u32, reloc_info.offset),4271 .offset = @intCast(u32, reloc_info.offset),
4494 .target = .{ .local = decl.link.macho.local_sym_index },4272 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
4495 .addend = reloc_info.addend,4273 .addend = reloc_info.addend,
4496 .subtractor = null,4274 .subtractor = null,
4497 .pcrel = false,4275 .pcrel = false,
...@@ -4534,7 +4312,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4534,7 +4312,7 @@ fn populateMissingMetadata(self: *MachO) !void {
45344312
4535 if (self.text_segment_cmd_index == null) {4313 if (self.text_segment_cmd_index == null) {
4536 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);4314 self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4537 const needed_size = if (self.needs_prealloc) blk: {4315 const needed_size = if (self.mode == .incremental) blk: {
4538 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);4316 const headerpad_size = @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size);
4539 const program_code_size_hint = self.base.options.program_code_size_hint;4317 const program_code_size_hint = self.base.options.program_code_size_hint;
4540 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;4318 const got_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
...@@ -4565,7 +4343,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4565,7 +4343,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4565 .aarch64 => 2,4343 .aarch64 => 2,
4566 else => unreachable, // unhandled architecture type4344 else => unreachable, // unhandled architecture type
4567 };4345 };
4568 const needed_size = if (self.needs_prealloc) self.base.options.program_code_size_hint else 0;4346 const needed_size = if (self.mode == .incremental) self.base.options.program_code_size_hint else 0;
4569 self.text_section_index = try self.initSection(4347 self.text_section_index = try self.initSection(
4570 self.text_segment_cmd_index.?,4348 self.text_segment_cmd_index.?,
4571 "__text",4349 "__text",
...@@ -4588,7 +4366,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4588,7 +4366,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4588 .aarch64 => 3 * @sizeOf(u32),4366 .aarch64 => 3 * @sizeOf(u32),
4589 else => unreachable, // unhandled architecture type4367 else => unreachable, // unhandled architecture type
4590 };4368 };
4591 const needed_size = if (self.needs_prealloc) stub_size * self.base.options.symbol_count_hint else 0;4369 const needed_size = if (self.mode == .incremental) stub_size * self.base.options.symbol_count_hint else 0;
4592 self.stubs_section_index = try self.initSection(4370 self.stubs_section_index = try self.initSection(
4593 self.text_segment_cmd_index.?,4371 self.text_segment_cmd_index.?,
4594 "__stubs",4372 "__stubs",
...@@ -4617,7 +4395,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4617,7 +4395,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4617 .aarch64 => 3 * @sizeOf(u32),4395 .aarch64 => 3 * @sizeOf(u32),
4618 else => unreachable,4396 else => unreachable,
4619 };4397 };
4620 const needed_size = if (self.needs_prealloc)4398 const needed_size = if (self.mode == .incremental)
4621 stub_size * self.base.options.symbol_count_hint + preamble_size4399 stub_size * self.base.options.symbol_count_hint + preamble_size
4622 else4400 else
4623 0;4401 0;
...@@ -4637,7 +4415,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4637,7 +4415,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4637 var vmaddr: u64 = 0;4415 var vmaddr: u64 = 0;
4638 var fileoff: u64 = 0;4416 var fileoff: u64 = 0;
4639 var needed_size: u64 = 0;4417 var needed_size: u64 = 0;
4640 if (self.needs_prealloc) {4418 if (self.mode == .incremental) {
4641 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});4419 const base = self.getSegmentAllocBase(&.{self.text_segment_cmd_index.?});
4642 vmaddr = base.vmaddr;4420 vmaddr = base.vmaddr;
4643 fileoff = base.fileoff;4421 fileoff = base.fileoff;
...@@ -4666,7 +4444,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4666,7 +4444,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4666 }4444 }
46674445
4668 if (self.got_section_index == null) {4446 if (self.got_section_index == null) {
4669 const needed_size = if (self.needs_prealloc)4447 const needed_size = if (self.mode == .incremental)
4670 @sizeOf(u64) * self.base.options.symbol_count_hint4448 @sizeOf(u64) * self.base.options.symbol_count_hint
4671 else4449 else
4672 0;4450 0;
...@@ -4687,7 +4465,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4687,7 +4465,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4687 var vmaddr: u64 = 0;4465 var vmaddr: u64 = 0;
4688 var fileoff: u64 = 0;4466 var fileoff: u64 = 0;
4689 var needed_size: u64 = 0;4467 var needed_size: u64 = 0;
4690 if (self.needs_prealloc) {4468 if (self.mode == .incremental) {
4691 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});4469 const base = self.getSegmentAllocBase(&.{self.data_const_segment_cmd_index.?});
4692 vmaddr = base.vmaddr;4470 vmaddr = base.vmaddr;
4693 fileoff = base.fileoff;4471 fileoff = base.fileoff;
...@@ -4716,7 +4494,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4716,7 +4494,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4716 }4494 }
47174495
4718 if (self.la_symbol_ptr_section_index == null) {4496 if (self.la_symbol_ptr_section_index == null) {
4719 const needed_size = if (self.needs_prealloc)4497 const needed_size = if (self.mode == .incremental)
4720 @sizeOf(u64) * self.base.options.symbol_count_hint4498 @sizeOf(u64) * self.base.options.symbol_count_hint
4721 else4499 else
4722 0;4500 0;
...@@ -4733,7 +4511,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4733,7 +4511,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4733 }4511 }
47344512
4735 if (self.data_section_index == null) {4513 if (self.data_section_index == null) {
4736 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;4514 const needed_size = if (self.mode == .incremental)
4515 @sizeOf(u64) * self.base.options.symbol_count_hint
4516 else
4517 0;
4737 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4518 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4738 self.data_section_index = try self.initSection(4519 self.data_section_index = try self.initSection(
4739 self.data_segment_cmd_index.?,4520 self.data_segment_cmd_index.?,
...@@ -4745,7 +4526,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4745,7 +4526,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4745 }4526 }
47464527
4747 if (self.tlv_section_index == null) {4528 if (self.tlv_section_index == null) {
4748 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;4529 const needed_size = if (self.mode == .incremental)
4530 @sizeOf(u64) * self.base.options.symbol_count_hint
4531 else
4532 0;
4749 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4533 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4750 self.tlv_section_index = try self.initSection(4534 self.tlv_section_index = try self.initSection(
4751 self.data_segment_cmd_index.?,4535 self.data_segment_cmd_index.?,
...@@ -4759,7 +4543,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4759,7 +4543,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4759 }4543 }
47604544
4761 if (self.tlv_data_section_index == null) {4545 if (self.tlv_data_section_index == null) {
4762 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;4546 const needed_size = if (self.mode == .incremental)
4547 @sizeOf(u64) * self.base.options.symbol_count_hint
4548 else
4549 0;
4763 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4550 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4764 self.tlv_data_section_index = try self.initSection(4551 self.tlv_data_section_index = try self.initSection(
4765 self.data_segment_cmd_index.?,4552 self.data_segment_cmd_index.?,
...@@ -4773,7 +4560,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4773,7 +4560,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4773 }4560 }
47744561
4775 if (self.tlv_bss_section_index == null) {4562 if (self.tlv_bss_section_index == null) {
4776 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;4563 const needed_size = if (self.mode == .incremental)
4564 @sizeOf(u64) * self.base.options.symbol_count_hint
4565 else
4566 0;
4777 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4567 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4778 self.tlv_bss_section_index = try self.initSection(4568 self.tlv_bss_section_index = try self.initSection(
4779 self.data_segment_cmd_index.?,4569 self.data_segment_cmd_index.?,
...@@ -4787,7 +4577,10 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4787,7 +4577,10 @@ fn populateMissingMetadata(self: *MachO) !void {
4787 }4577 }
47884578
4789 if (self.bss_section_index == null) {4579 if (self.bss_section_index == null) {
4790 const needed_size = if (self.needs_prealloc) @sizeOf(u64) * self.base.options.symbol_count_hint else 0;4580 const needed_size = if (self.mode == .incremental)
4581 @sizeOf(u64) * self.base.options.symbol_count_hint
4582 else
4583 0;
4791 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)4584 const alignment: u16 = 3; // 2^3 = @sizeOf(u64)
4792 self.bss_section_index = try self.initSection(4585 self.bss_section_index = try self.initSection(
4793 self.data_segment_cmd_index.?,4586 self.data_segment_cmd_index.?,
...@@ -4804,7 +4597,7 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -4804,7 +4597,7 @@ fn populateMissingMetadata(self: *MachO) !void {
4804 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);4597 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
4805 var vmaddr: u64 = 0;4598 var vmaddr: u64 = 0;
4806 var fileoff: u64 = 0;4599 var fileoff: u64 = 0;
4807 if (self.needs_prealloc) {4600 if (self.mode == .incremental) {
4808 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});4601 const base = self.getSegmentAllocBase(&.{self.data_segment_cmd_index.?});
4809 vmaddr = base.vmaddr;4602 vmaddr = base.vmaddr;
4810 fileoff = base.fileoff;4603 fileoff = base.fileoff;
...@@ -5028,8 +4821,6 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -5028,8 +4821,6 @@ fn populateMissingMetadata(self: *MachO) !void {
5028 });4821 });
5029 self.load_commands_dirty = true;4822 self.load_commands_dirty = true;
5030 }4823 }
5031
5032 self.cold_start = true;
5033}4824}
50344825
5035fn calcMinHeaderpad(self: *MachO) u64 {4826fn calcMinHeaderpad(self: *MachO) u64 {
...@@ -5130,7 +4921,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_...@@ -5130,7 +4921,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_
51304921
5131 // Allocate the sections according to their alignment at the beginning of the segment.4922 // Allocate the sections according to their alignment at the beginning of the segment.
5132 var start = init_size;4923 var start = init_size;
5133 for (seg.sections.items) |*sect, sect_id| {4924 for (seg.sections.items) |*sect| {
5134 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;4925 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
5135 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4926 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
5136 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;4927 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
...@@ -5138,32 +4929,12 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_...@@ -5138,32 +4929,12 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_
5138 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);4929 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
51394930
5140 // TODO handle zerofill sections in stage24931 // TODO handle zerofill sections in stage2
5141 sect.offset = if (is_zerofill and (use_stage1 or use_llvm)) 0 else @intCast(u32, seg.inner.fileoff + start_aligned);4932 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4933 0
4934 else
4935 @intCast(u32, seg.inner.fileoff + start_aligned);
5142 sect.addr = seg.inner.vmaddr + start_aligned;4936 sect.addr = seg.inner.vmaddr + start_aligned;
51434937
5144 // Recalculate section size given the allocated start address
5145 sect.size = if (self.atoms.get(.{
5146 .seg = index,
5147 .sect = @intCast(u16, sect_id),
5148 })) |last_atom| blk: {
5149 var atom = last_atom;
5150 while (atom.prev) |prev| {
5151 atom = prev;
5152 }
5153
5154 var base_addr = sect.addr;
5155
5156 while (true) {
5157 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5158 base_addr = mem.alignForwardGeneric(u64, base_addr, atom_alignment) + atom.size;
5159 if (atom.next) |next| {
5160 atom = next;
5161 } else break;
5162 }
5163
5164 break :blk base_addr - sect.addr;
5165 } else 0;
5166
5167 start = start_aligned + sect.size;4938 start = start_aligned + sect.size;
51684939
5169 if (!(is_zerofill and (use_stage1 or use_llvm))) {4940 if (!(is_zerofill and (use_stage1 or use_llvm))) {
...@@ -5194,14 +4965,14 @@ fn initSection(...@@ -5194,14 +4965,14 @@ fn initSection(
5194 var sect = macho.section_64{4965 var sect = macho.section_64{
5195 .sectname = makeStaticString(sectname),4966 .sectname = makeStaticString(sectname),
5196 .segname = seg.inner.segname,4967 .segname = seg.inner.segname,
5197 .size = if (self.needs_prealloc) @intCast(u32, size) else 0,4968 .size = if (self.mode == .incremental) @intCast(u32, size) else 0,
5198 .@"align" = alignment,4969 .@"align" = alignment,
5199 .flags = opts.flags,4970 .flags = opts.flags,
5200 .reserved1 = opts.reserved1,4971 .reserved1 = opts.reserved1,
5201 .reserved2 = opts.reserved2,4972 .reserved2 = opts.reserved2,
5202 };4973 };
52034974
5204 if (self.needs_prealloc) {4975 if (self.mode == .incremental) {
5205 const alignment_pow_2 = try math.powi(u32, 2, alignment);4976 const alignment_pow_2 = try math.powi(u32, 2, alignment);
5206 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)4977 const padding: ?u32 = if (segment_id == self.text_segment_cmd_index.?)
5207 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)4978 @maximum(self.base.options.headerpad_size orelse 0, default_headerpad_size)
...@@ -5419,12 +5190,30 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3...@@ -5419,12 +5190,30 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3
5419 return max_alignment;5190 return max_alignment;
5420}5191}
54215192
5422fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {5193fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5194 const sym = atom.getSymbolPtr(self);
5195 if (self.mode == .incremental) {
5196 const size = atom.size;
5197 const alignment = try math.powi(u32, 2, atom.alignment);
5198 const vaddr = try self.allocateAtom(atom, size, alignment, match);
5199 const sym_name = atom.getName(self);
5200 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
5201 sym.n_value = vaddr;
5202 } else try self.addAtomToSection(atom, match);
5203 sym.n_sect = self.getSectionOrdinal(match);
5204}
5205
5206fn allocateAtom(
5207 self: *MachO,
5208 atom: *Atom,
5209 new_atom_size: u64,
5210 alignment: u64,
5211 match: MatchingSection,
5212) !u64 {
5423 const tracy = trace(@src());5213 const tracy = trace(@src());
5424 defer tracy.end();5214 defer tracy.end();
54255215
5426 const seg = &self.load_commands.items[match.seg].segment;5216 const sect = self.getSectionPtr(match);
5427 const sect = &seg.sections.items[match.sect];
5428 var free_list = self.atom_free_lists.get(match).?;5217 var free_list = self.atom_free_lists.get(match).?;
5429 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;5218 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
5430 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;5219 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;
...@@ -5445,8 +5234,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5445,8 +5234,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5445 const big_atom = free_list.items[i];5234 const big_atom = free_list.items[i];
5446 // We now have a pointer to a live atom that has too much capacity.5235 // We now have a pointer to a live atom that has too much capacity.
5447 // Is it enough that we could fit this new atom?5236 // Is it enough that we could fit this new atom?
5448 const sym = self.locals.items[big_atom.local_sym_index];5237 const sym = big_atom.getSymbol(self);
5449 const capacity = big_atom.capacity(self.*);5238 const capacity = big_atom.capacity(self);
5450 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;5239 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
5451 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;5240 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
5452 const capacity_end_vaddr = sym.n_value + capacity;5241 const capacity_end_vaddr = sym.n_value + capacity;
...@@ -5456,7 +5245,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5456,7 +5245,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5456 // Additional bookkeeping here to notice if this free list node5245 // Additional bookkeeping here to notice if this free list node
5457 // should be deleted because the atom that it points to has grown to take up5246 // should be deleted because the atom that it points to has grown to take up
5458 // more of the extra capacity.5247 // more of the extra capacity.
5459 if (!big_atom.freeListEligible(self.*)) {5248 if (!big_atom.freeListEligible(self)) {
5460 _ = free_list.swapRemove(i);5249 _ = free_list.swapRemove(i);
5461 } else {5250 } else {
5462 i += 1;5251 i += 1;
...@@ -5476,7 +5265,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5476,7 +5265,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5476 }5265 }
5477 break :blk new_start_vaddr;5266 break :blk new_start_vaddr;
5478 } else if (self.atoms.get(match)) |last| {5267 } else if (self.atoms.get(match)) |last| {
5479 const last_symbol = self.locals.items[last.local_sym_index];5268 const last_symbol = last.getSymbol(self);
5480 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;5269 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
5481 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;5270 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
5482 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);5271 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
...@@ -5525,7 +5314,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5525,7 +5314,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5525 return vaddr;5314 return vaddr;
5526}5315}
55275316
5528fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {5317pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5529 if (self.atoms.getPtr(match)) |last| {5318 if (self.atoms.getPtr(match)) |last| {
5530 last.*.next = atom;5319 last.*.next = atom;
5531 atom.prev = last.*;5320 atom.prev = last.*;
...@@ -5533,34 +5322,42 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {...@@ -5533,34 +5322,42 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5533 } else {5322 } else {
5534 try self.atoms.putNoClobber(self.base.allocator, match, atom);5323 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5535 }5324 }
5536 const seg = &self.load_commands.items[match.seg].segment;5325 const sect = self.getSectionPtr(match);
5537 const sect = &seg.sections.items[match.sect];5326 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5538 sect.size += atom.size;5327 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5328 const padding = aligned_end_addr - sect.size;
5329 sect.size += padding + atom.size;
5330 sect.@"align" = @maximum(sect.@"align", atom.alignment);
5539}5331}
55405332
5541pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {5333pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5542 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});5334 const gpa = self.base.allocator;
5543 defer self.base.allocator.free(sym_name);5335 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
5544 const n_strx = try self.makeString(sym_name);5336 const global_index = @intCast(u32, self.globals.values().len);
55455337 const gop = try self.globals.getOrPut(gpa, sym_name);
5546 if (!self.symbol_resolver.contains(n_strx)) {5338 defer if (gop.found_existing) gpa.free(sym_name);
5547 log.debug("adding new extern function '{s}'", .{sym_name});5339
5548 const sym_index = @intCast(u32, self.undefs.items.len);5340 if (gop.found_existing) {
5549 try self.undefs.append(self.base.allocator, .{5341 // TODO audit this: can we ever reference anything from outside the Zig module?
5550 .n_strx = n_strx,5342 assert(gop.value_ptr.file == null);
5551 .n_type = macho.N_UNDF,5343 return gop.value_ptr.sym_index;
5552 .n_sect = 0,
5553 .n_desc = 0,
5554 .n_value = 0,
5555 });
5556 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
5557 .where = .undef,
5558 .where_index = sym_index,
5559 });
5560 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
5561 }5344 }
55625345
5563 return n_strx;5346 const sym_index = @intCast(u32, self.locals.items.len);
5347 try self.locals.append(gpa, .{
5348 .n_strx = try self.strtab.insert(gpa, sym_name),
5349 .n_type = macho.N_UNDF,
5350 .n_sect = 0,
5351 .n_desc = 0,
5352 .n_value = 0,
5353 });
5354 gop.value_ptr.* = .{
5355 .sym_index = sym_index,
5356 .file = null,
5357 };
5358 try self.unresolved.putNoClobber(gpa, global_index, true);
5359
5360 return sym_index;
5564}5361}
55655362
5566fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {5363fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
...@@ -5588,7 +5385,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5588,7 +5385,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
55885385
5589 for (indices) |maybe_index| {5386 for (indices) |maybe_index| {
5590 const old_idx = maybe_index.* orelse continue;5387 const old_idx = maybe_index.* orelse continue;
5591 const sect = sections[old_idx];5388 const sect = &sections[old_idx];
5592 if (sect.size == 0) {5389 if (sect.size == 0) {
5593 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });5390 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5594 maybe_index.* = null;5391 maybe_index.* = null;
...@@ -5596,7 +5393,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5596,7 +5393,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
5596 seg.inner.nsects -= 1;5393 seg.inner.nsects -= 1;
5597 } else {5394 } else {
5598 maybe_index.* = @intCast(u16, seg.sections.items.len);5395 maybe_index.* = @intCast(u16, seg.sections.items.len);
5599 seg.sections.appendAssumeCapacity(sect);5396 seg.sections.appendAssumeCapacity(sect.*);
5600 }5397 }
5601 try mapping.putNoClobber(old_idx, maybe_index.*);5398 try mapping.putNoClobber(old_idx, maybe_index.*);
5602 }5399 }
...@@ -5711,7 +5508,11 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5711,7 +5508,11 @@ fn updateSectionOrdinals(self: *MachO) !void {
5711 const tracy = trace(@src());5508 const tracy = trace(@src());
5712 defer tracy.end();5509 defer tracy.end();
57135510
5714 var ordinal_remap = std.AutoHashMap(u8, u8).init(self.base.allocator);5511 log.debug("updating section ordinals", .{});
5512
5513 const gpa = self.base.allocator;
5514
5515 var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5715 defer ordinal_remap.deinit();5516 defer ordinal_remap.deinit();
5716 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};5517 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
57175518
...@@ -5723,27 +5524,40 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5723,27 +5524,40 @@ fn updateSectionOrdinals(self: *MachO) !void {
5723 }) |maybe_index| {5524 }) |maybe_index| {
5724 const index = maybe_index orelse continue;5525 const index = maybe_index orelse continue;
5725 const seg = self.load_commands.items[index].segment;5526 const seg = self.load_commands.items[index].segment;
5726 for (seg.sections.items) |_, sect_id| {5527 for (seg.sections.items) |sect, sect_id| {
5727 const match = MatchingSection{5528 const match = MatchingSection{
5728 .seg = @intCast(u16, index),5529 .seg = @intCast(u16, index),
5729 .sect = @intCast(u16, sect_id),5530 .sect = @intCast(u16, sect_id),
5730 };5531 };
5731 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);5532 const old_ordinal = self.getSectionOrdinal(match);
5732 new_ordinal += 1;5533 new_ordinal += 1;
5534 log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5535 sect.segName(),
5536 sect.sectName(),
5537 old_ordinal,
5538 new_ordinal,
5539 });
5733 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);5540 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5734 try ordinals.putNoClobber(self.base.allocator, match, {});5541 try ordinals.putNoClobber(gpa, match, {});
5735 }5542 }
5736 }5543 }
57375544
5545 // FIXME Jakub
5546 // TODO no need for duping work here; simply walk the atom graph
5738 for (self.locals.items) |*sym| {5547 for (self.locals.items) |*sym| {
5548 if (sym.undf()) continue;
5739 if (sym.n_sect == 0) continue;5549 if (sym.n_sect == 0) continue;
5740 sym.n_sect = ordinal_remap.get(sym.n_sect).?;5550 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5741 }5551 }
5742 for (self.globals.items) |*sym| {5552 for (self.objects.items) |*object| {
5743 sym.n_sect = ordinal_remap.get(sym.n_sect).?;5553 for (object.symtab.items) |*sym| {
5554 if (sym.undf()) continue;
5555 if (sym.n_sect == 0) continue;
5556 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5557 }
5744 }5558 }
57455559
5746 self.section_ordinals.deinit(self.base.allocator);5560 self.section_ordinals.deinit(gpa);
5747 self.section_ordinals = ordinals;5561 self.section_ordinals = ordinals;
5748}5562}
57495563
...@@ -5751,11 +5565,13 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5751,11 +5565,13 @@ fn writeDyldInfoData(self: *MachO) !void {
5751 const tracy = trace(@src());5565 const tracy = trace(@src());
5752 defer tracy.end();5566 defer tracy.end();
57535567
5754 var rebase_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5568 const gpa = self.base.allocator;
5569
5570 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
5755 defer rebase_pointers.deinit();5571 defer rebase_pointers.deinit();
5756 var bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5572 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5757 defer bind_pointers.deinit();5573 defer bind_pointers.deinit();
5758 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5574 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5759 defer lazy_bind_pointers.deinit();5575 defer lazy_bind_pointers.deinit();
57605576
5761 {5577 {
...@@ -5768,13 +5584,17 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5768,13 +5584,17 @@ fn writeDyldInfoData(self: *MachO) !void {
5768 if (match.seg == seg) continue; // __TEXT is non-writable5584 if (match.seg == seg) continue; // __TEXT is non-writable
5769 }5585 }
57705586
5771 const seg = self.load_commands.items[match.seg].segment;5587 const seg = self.getSegment(match);
5588 const sect = self.getSection(match);
5589 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
57725590
5773 while (true) {5591 while (true) {
5774 const sym = self.locals.items[atom.local_sym_index];5592 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
5593 const sym = atom.getSymbol(self);
5775 const base_offset = sym.n_value - seg.inner.vmaddr;5594 const base_offset = sym.n_value - seg.inner.vmaddr;
57765595
5777 for (atom.rebases.items) |offset| {5596 for (atom.rebases.items) |offset| {
5597 log.debug(" | rebase at {x}", .{base_offset + offset});
5778 try rebase_pointers.append(.{5598 try rebase_pointers.append(.{
5779 .offset = base_offset + offset,5599 .offset = base_offset + offset,
5780 .segment_id = match.seg,5600 .segment_id = match.seg,
...@@ -5782,57 +5602,55 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5782,57 +5602,55 @@ fn writeDyldInfoData(self: *MachO) !void {
5782 }5602 }
57835603
5784 for (atom.bindings.items) |binding| {5604 for (atom.bindings.items) |binding| {
5785 const resolv = self.symbol_resolver.get(binding.n_strx).?;5605 const bind_sym = self.getSymbol(binding.target);
5786 switch (resolv.where) {5606 const bind_sym_name = self.getSymbolName(binding.target);
5787 .global => {5607 const dylib_ordinal = @divTrunc(
5788 // Turn into a rebase.5608 @bitCast(i16, bind_sym.n_desc),
5789 try rebase_pointers.append(.{5609 macho.N_SYMBOL_RESOLVER,
5790 .offset = base_offset + binding.offset,5610 );
5791 .segment_id = match.seg,5611 var flags: u4 = 0;
5792 });5612 log.debug(" | bind at {x}, import('{s}') in dylib({d})", .{
5793 },5613 binding.offset + base_offset,
5794 .undef => {5614 bind_sym_name,
5795 const bind_sym = self.undefs.items[resolv.where_index];5615 dylib_ordinal,
5796 var flags: u4 = 0;5616 });
5797 if (bind_sym.weakRef()) {5617 if (bind_sym.weakRef()) {
5798 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);5618 log.debug(" | marking as weak ref ", .{});
5799 }5619 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5800 try bind_pointers.append(.{
5801 .offset = binding.offset + base_offset,
5802 .segment_id = match.seg,
5803 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5804 .name = self.getString(bind_sym.n_strx),
5805 .bind_flags = flags,
5806 });
5807 },
5808 }5620 }
5621 try bind_pointers.append(.{
5622 .offset = binding.offset + base_offset,
5623 .segment_id = match.seg,
5624 .dylib_ordinal = dylib_ordinal,
5625 .name = bind_sym_name,
5626 .bind_flags = flags,
5627 });
5809 }5628 }
58105629
5811 for (atom.lazy_bindings.items) |binding| {5630 for (atom.lazy_bindings.items) |binding| {
5812 const resolv = self.symbol_resolver.get(binding.n_strx).?;5631 const bind_sym = self.getSymbol(binding.target);
5813 switch (resolv.where) {5632 const bind_sym_name = self.getSymbolName(binding.target);
5814 .global => {5633 const dylib_ordinal = @divTrunc(
5815 // Turn into a rebase.5634 @bitCast(i16, bind_sym.n_desc),
5816 try rebase_pointers.append(.{5635 macho.N_SYMBOL_RESOLVER,
5817 .offset = base_offset + binding.offset,5636 );
5818 .segment_id = match.seg,5637 var flags: u4 = 0;
5819 });5638 log.debug(" | lazy bind at {x} import('{s}') ord({d})", .{
5820 },5639 binding.offset + base_offset,
5821 .undef => {5640 bind_sym_name,
5822 const bind_sym = self.undefs.items[resolv.where_index];5641 dylib_ordinal,
5823 var flags: u4 = 0;5642 });
5824 if (bind_sym.weakRef()) {5643 if (bind_sym.weakRef()) {
5825 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);5644 log.debug(" | marking as weak ref ", .{});
5826 }5645 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5827 try lazy_bind_pointers.append(.{
5828 .offset = binding.offset + base_offset,
5829 .segment_id = match.seg,
5830 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5831 .name = self.getString(bind_sym.n_strx),
5832 .bind_flags = flags,
5833 });
5834 },
5835 }5646 }
5647 try lazy_bind_pointers.append(.{
5648 .offset = binding.offset + base_offset,
5649 .segment_id = match.seg,
5650 .dylib_ordinal = dylib_ordinal,
5651 .name = bind_sym_name,
5652 .bind_flags = flags,
5653 });
5836 }5654 }
58375655
5838 if (atom.prev) |prev| {5656 if (atom.prev) |prev| {
...@@ -5843,7 +5661,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5843,7 +5661,7 @@ fn writeDyldInfoData(self: *MachO) !void {
5843 }5661 }
58445662
5845 var trie: Trie = .{};5663 var trie: Trie = .{};
5846 defer trie.deinit(self.base.allocator);5664 defer trie.deinit(gpa);
58475665
5848 {5666 {
5849 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5667 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
...@@ -5852,19 +5670,40 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5852,19 +5670,40 @@ fn writeDyldInfoData(self: *MachO) !void {
5852 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;5670 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
5853 const base_address = text_segment.inner.vmaddr;5671 const base_address = text_segment.inner.vmaddr;
58545672
5855 for (self.globals.items) |sym| {5673 if (self.base.options.output_mode == .Exe) {
5856 if (sym.n_type == 0) continue;5674 for (&[_]SymbolWithLoc{
5857 const sym_name = self.getString(sym.n_strx);5675 try self.getEntryPoint(),
5858 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });5676 self.globals.get("__mh_execute_header").?,
58595677 }) |global| {
5860 try trie.put(self.base.allocator, .{5678 const sym = self.getSymbol(global);
5861 .name = sym_name,5679 const sym_name = self.getSymbolName(global);
5862 .vmaddr_offset = sym.n_value - base_address,5680 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
5863 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,5681 try trie.put(gpa, .{
5864 });5682 .name = sym_name,
5683 .vmaddr_offset = sym.n_value - base_address,
5684 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
5685 });
5686 }
5687 } else {
5688 assert(self.base.options.output_mode == .Lib);
5689 for (self.globals.values()) |global| {
5690 const sym = self.getSymbol(global);
5691
5692 if (sym.undf()) continue;
5693 if (!sym.ext()) continue;
5694 if (sym.n_desc == N_DESC_GCED) continue;
5695
5696 const sym_name = self.getSymbolName(global);
5697 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
5698 try trie.put(gpa, .{
5699 .name = sym_name,
5700 .vmaddr_offset = sym.n_value - base_address,
5701 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
5702 });
5703 }
5865 }5704 }
58665705
5867 try trie.finalize(self.base.allocator);5706 try trie.finalize(gpa);
5868 }5707 }
58695708
5870 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5709 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
...@@ -5909,8 +5748,8 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5909,8 +5748,8 @@ fn writeDyldInfoData(self: *MachO) !void {
5909 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;5748 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
59105749
5911 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;5750 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;
5912 var buffer = try self.base.allocator.alloc(u8, needed_size);5751 var buffer = try gpa.alloc(u8, needed_size);
5913 defer self.base.allocator.free(buffer);5752 defer gpa.free(buffer);
5914 mem.set(u8, buffer, 0);5753 mem.set(u8, buffer, 0);
59155754
5916 var stream = std.io.fixedBufferStream(buffer);5755 var stream = std.io.fixedBufferStream(buffer);
...@@ -5937,10 +5776,12 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5937,10 +5776,12 @@ fn writeDyldInfoData(self: *MachO) !void {
5937 try self.populateLazyBindOffsetsInStubHelper(5776 try self.populateLazyBindOffsetsInStubHelper(
5938 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],5777 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],
5939 );5778 );
5779
5940 self.load_commands_dirty = true;5780 self.load_commands_dirty = true;
5941}5781}
59425782
5943fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {5783fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5784 const gpa = self.base.allocator;
5944 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;5785 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
5945 const stub_helper_section_index = self.stub_helper_section_index orelse return;5786 const stub_helper_section_index = self.stub_helper_section_index orelse return;
5946 const last_atom = self.atoms.get(.{5787 const last_atom = self.atoms.get(.{
...@@ -5950,7 +5791,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5950,7 +5791,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5950 if (self.stub_helper_preamble_atom == null) return;5791 if (self.stub_helper_preamble_atom == null) return;
5951 if (last_atom == self.stub_helper_preamble_atom.?) return;5792 if (last_atom == self.stub_helper_preamble_atom.?) return;
59525793
5953 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);5794 var table = std.AutoHashMap(i64, *Atom).init(gpa);
5954 defer table.deinit();5795 defer table.deinit();
59555796
5956 {5797 {
...@@ -5966,7 +5807,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5966,7 +5807,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
59665807
5967 while (true) {5808 while (true) {
5968 const laptr_off = blk: {5809 const laptr_off = blk: {
5969 const sym = self.locals.items[laptr_atom.local_sym_index];5810 const sym = laptr_atom.getSymbol(self);
5970 break :blk @intCast(i64, sym.n_value - base_addr);5811 break :blk @intCast(i64, sym.n_value - base_addr);
5971 };5812 };
5972 try table.putNoClobber(laptr_off, stub_atom);5813 try table.putNoClobber(laptr_off, stub_atom);
...@@ -5979,7 +5820,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -5979,7 +5820,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
59795820
5980 var stream = std.io.fixedBufferStream(buffer);5821 var stream = std.io.fixedBufferStream(buffer);
5981 var reader = stream.reader();5822 var reader = stream.reader();
5982 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(self.base.allocator);5823 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
5983 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });5824 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
5984 defer offsets.deinit();5825 defer offsets.deinit();
5985 var valid_block = false;5826 var valid_block = false;
...@@ -6022,10 +5863,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6022,10 +5863,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
6022 }5863 }
6023 }5864 }
60245865
6025 const sect = blk: {5866 const sect = self.getSection(.{
6026 const seg = self.load_commands.items[text_segment_cmd_index].segment;5867 .seg = text_segment_cmd_index,
6027 break :blk seg.sections.items[stub_helper_section_index];5868 .sect = stub_helper_section_index,
6028 };5869 });
6029 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {5870 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
6030 .x86_64 => 1,5871 .x86_64 => 1,
6031 .aarch64 => 2 * @sizeOf(u32),5872 .aarch64 => 2 * @sizeOf(u32),
...@@ -6036,79 +5877,63 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6036,79 +5877,63 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
60365877
6037 while (offsets.popOrNull()) |bind_offset| {5878 while (offsets.popOrNull()) |bind_offset| {
6038 const atom = table.get(bind_offset.sym_offset).?;5879 const atom = table.get(bind_offset.sym_offset).?;
6039 const sym = self.locals.items[atom.local_sym_index];5880 const sym = atom.getSymbol(self);
6040 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;5881 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
6041 mem.writeIntLittle(u32, &buf, bind_offset.offset);5882 mem.writeIntLittle(u32, &buf, bind_offset.offset);
6042 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{5883 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
6043 bind_offset.offset,5884 bind_offset.offset,
6044 self.getString(sym.n_strx),5885 atom.getName(self),
6045 file_offset,5886 file_offset,
6046 });5887 });
6047 try self.base.file.?.pwriteAll(&buf, file_offset);5888 try self.base.file.?.pwriteAll(&buf, file_offset);
6048 }5889 }
6049}5890}
60505891
5892const asc_u64 = std.sort.asc(u64);
5893
6051fn writeFunctionStarts(self: *MachO) !void {5894fn writeFunctionStarts(self: *MachO) !void {
6052 var atom = self.atoms.get(.{5895 const text_seg_index = self.text_segment_cmd_index orelse return;
6053 .seg = self.text_segment_cmd_index orelse return,5896 const text_sect_index = self.text_section_index orelse return;
6054 .sect = self.text_section_index orelse return,5897 const text_seg = self.load_commands.items[text_seg_index].segment;
6055 }) orelse return;
60565898
6057 const tracy = trace(@src());5899 const tracy = trace(@src());
6058 defer tracy.end();5900 defer tracy.end();
60595901
6060 while (atom.prev) |prev| {5902 const gpa = self.base.allocator;
6061 atom = prev;
6062 }
6063
6064 var offsets = std.ArrayList(u32).init(self.base.allocator);
6065 defer offsets.deinit();
6066
6067 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6068 var last_off: u32 = 0;
6069
6070 while (true) {
6071 const atom_sym = self.locals.items[atom.local_sym_index];
6072
6073 if (atom_sym.n_strx != 0) blk: {
6074 if (self.symbol_resolver.get(atom_sym.n_strx)) |resolv| {
6075 assert(resolv.where == .global);
6076 if (resolv.local_sym_index != atom.local_sym_index) break :blk;
6077 }
6078
6079 const offset = @intCast(u32, atom_sym.n_value - text_seg.inner.vmaddr);
6080 const diff = offset - last_off;
60815903
6082 if (diff == 0) break :blk;5904 // We need to sort by address first
5905 var addresses = std.ArrayList(u64).init(gpa);
5906 defer addresses.deinit();
5907 try addresses.ensureTotalCapacityPrecise(self.globals.count());
60835908
6084 try offsets.append(diff);5909 for (self.globals.values()) |global| {
6085 last_off = offset;5910 const sym = self.getSymbol(global);
6086 }5911 if (sym.undf()) continue;
5912 if (sym.n_desc == N_DESC_GCED) continue;
5913 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5914 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;
60875915
6088 for (atom.contained.items) |cont| {5916 addresses.appendAssumeCapacity(sym.n_value);
6089 const cont_sym = self.locals.items[cont.local_sym_index];5917 }
60905918
6091 if (cont_sym.n_strx == 0) continue;5919 std.sort.sort(u64, addresses.items, {}, asc_u64);
6092 if (self.symbol_resolver.get(cont_sym.n_strx)) |resolv| {
6093 assert(resolv.where == .global);
6094 if (resolv.local_sym_index != cont.local_sym_index) continue;
6095 }
60965920
6097 const offset = @intCast(u32, cont_sym.n_value - text_seg.inner.vmaddr);5921 var offsets = std.ArrayList(u32).init(gpa);
6098 const diff = offset - last_off;5922 defer offsets.deinit();
5923 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
60995924
6100 if (diff == 0) continue;5925 var last_off: u32 = 0;
5926 for (addresses.items) |addr| {
5927 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);
5928 const diff = offset - last_off;
61015929
6102 try offsets.append(diff);5930 if (diff == 0) continue;
6103 last_off = offset;
6104 }
61055931
6106 if (atom.next) |next| {5932 offsets.appendAssumeCapacity(diff);
6107 atom = next;5933 last_off = offset;
6108 } else break;
6109 }5934 }
61105935
6111 var buffer = std.ArrayList(u8).init(self.base.allocator);5936 var buffer = std.ArrayList(u8).init(gpa);
6112 defer buffer.deinit();5937 defer buffer.deinit();
61135938
6114 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));5939 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
...@@ -6136,53 +5961,72 @@ fn writeFunctionStarts(self: *MachO) !void {...@@ -6136,53 +5961,72 @@ fn writeFunctionStarts(self: *MachO) !void {
6136 self.load_commands_dirty = true;5961 self.load_commands_dirty = true;
6137}5962}
61385963
6139fn writeDices(self: *MachO) !void {5964fn filterDataInCode(
6140 if (!self.has_dices) return;5965 dices: []const macho.data_in_code_entry,
5966 start_addr: u64,
5967 end_addr: u64,
5968) []const macho.data_in_code_entry {
5969 const Predicate = struct {
5970 addr: u64,
5971
5972 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {
5973 return dice.offset >= self.addr;
5974 }
5975 };
5976
5977 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });
5978 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });
5979
5980 return dices[start..end];
5981}
61415982
5983fn writeDataInCode(self: *MachO) !void {
6142 const tracy = trace(@src());5984 const tracy = trace(@src());
6143 defer tracy.end();5985 defer tracy.end();
61445986
6145 var buf = std.ArrayList(u8).init(self.base.allocator);5987 var out_dice = std.ArrayList(macho.data_in_code_entry).init(self.base.allocator);
6146 defer buf.deinit();5988 defer out_dice.deinit();
61475989
6148 var atom: *Atom = self.atoms.get(.{5990 const text_sect = self.getSection(.{
6149 .seg = self.text_segment_cmd_index orelse return,5991 .seg = self.text_segment_cmd_index orelse return,
6150 .sect = self.text_section_index orelse return,5992 .sect = self.text_section_index orelse return,
6151 }) orelse return;5993 });
61525994
6153 while (atom.prev) |prev| {5995 for (self.objects.items) |object| {
6154 atom = prev;5996 const dice = object.parseDataInCode() orelse continue;
6155 }5997 try out_dice.ensureUnusedCapacity(dice.len);
61565998
6157 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5999 for (object.managed_atoms.items) |atom| {
6158 const text_sect = text_seg.sections.items[self.text_section_index.?];6000 const sym = atom.getSymbol(self);
6001 if (sym.n_desc == N_DESC_GCED) continue;
61596002
6160 while (true) {6003 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
6161 if (atom.dices.items.len > 0) {6004 if (match.seg != self.text_segment_cmd_index.? and match.sect != self.text_section_index.?) {
6162 const sym = self.locals.items[atom.local_sym_index];6005 continue;
6163 const base_off = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse return error.Overflow;
6164
6165 try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry));
6166 for (atom.dices.items) |dice| {
6167 const rebased_dice = macho.data_in_code_entry{
6168 .offset = base_off + dice.offset,
6169 .length = dice.length,
6170 .kind = dice.kind,
6171 };
6172 buf.appendSliceAssumeCapacity(mem.asBytes(&rebased_dice));
6173 }6006 }
6174 }
61756007
6176 if (atom.next) |next| {6008 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
6177 atom = next;6009 const source_addr = math.cast(u32, source_sym.n_value) orelse return error.Overflow;
6178 } else break;6010 const filtered_dice = filterDataInCode(dice, source_addr, source_addr + atom.size);
6011 const base = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse
6012 return error.Overflow;
6013
6014 for (filtered_dice) |single| {
6015 const offset = single.offset - source_addr + base;
6016 out_dice.appendAssumeCapacity(.{
6017 .offset = offset,
6018 .length = single.length,
6019 .kind = single.kind,
6020 });
6021 }
6022 }
6179 }6023 }
61806024
6181 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;6025 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6182 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;6026 const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data;
61836027
6184 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));6028 const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6185 const datasize = buf.items.len;6029 const datasize = out_dice.items.len * @sizeOf(macho.data_in_code_entry);
6186 dice_cmd.dataoff = @intCast(u32, dataoff);6030 dice_cmd.dataoff = @intCast(u32, dataoff);
6187 dice_cmd.datasize = @intCast(u32, datasize);6031 dice_cmd.datasize = @intCast(u32, datasize);
6188 seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff;6032 seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff;
...@@ -6192,118 +6036,93 @@ fn writeDices(self: *MachO) !void {...@@ -6192,118 +6036,93 @@ fn writeDices(self: *MachO) !void {
6192 dice_cmd.dataoff + dice_cmd.datasize,6036 dice_cmd.dataoff + dice_cmd.datasize,
6193 });6037 });
61946038
6195 try self.base.file.?.pwriteAll(buf.items, dice_cmd.dataoff);6039 try self.base.file.?.pwriteAll(mem.sliceAsBytes(out_dice.items), dice_cmd.dataoff);
6196 self.load_commands_dirty = true;6040 self.load_commands_dirty = true;
6197}6041}
61986042
6199fn writeSymbolTable(self: *MachO) !void {6043fn writeSymtab(self: *MachO) !void {
6200 const tracy = trace(@src());6044 const tracy = trace(@src());
6201 defer tracy.end();6045 defer tracy.end();
62026046
6047 const gpa = self.base.allocator;
6203 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;6048 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6204 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;6049 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6205 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));6050 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));
6206 symtab.symoff = @intCast(u32, symoff);6051 symtab.symoff = @intCast(u32, symoff);
62076052
6208 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);6053 var locals = std.ArrayList(macho.nlist_64).init(gpa);
6209 defer locals.deinit();6054 defer locals.deinit();
62106055
6211 for (self.locals.items) |sym| {6056 for (self.locals.items) |sym, sym_id| {
6212 if (sym.n_strx == 0) continue;6057 if (sym.n_strx == 0) continue; // no name, skip
6213 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;6058 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6059 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
6060 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
6061 if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
6214 try locals.append(sym);6062 try locals.append(sym);
6215 }6063 }
62166064
6217 // TODO How do we handle null global symbols in incremental context?6065 for (self.objects.items) |object, object_id| {
6218 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);6066 for (object.symtab.items) |sym, sym_id| {
6219 defer undefs.deinit();6067 if (sym.n_strx == 0) continue; // no name, skip
6220 var undefs_table = std.AutoHashMap(u32, u32).init(self.base.allocator);6068 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6221 defer undefs_table.deinit();6069 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
6222 try undefs.ensureTotalCapacity(self.undefs.items.len);6070 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
6223 try undefs_table.ensureTotalCapacity(@intCast(u32, self.undefs.items.len));6071 if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
6072 var out_sym = sym;
6073 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
6074 try locals.append(out_sym);
6075 }
62246076
6225 for (self.undefs.items) |sym, i| {6077 if (!self.base.options.strip) {
6226 if (sym.n_strx == 0) continue;6078 try self.generateSymbolStabs(object, &locals);
6227 const new_index = @intCast(u32, undefs.items.len);6079 }
6228 undefs.appendAssumeCapacity(sym);
6229 undefs_table.putAssumeCapacityNoClobber(@intCast(u32, i), new_index);
6230 }6080 }
62316081
6232 if (self.has_stabs) {6082 var exports = std.ArrayList(macho.nlist_64).init(gpa);
6233 for (self.objects.items) |object| {6083 defer exports.deinit();
6234 if (object.debug_info == null) continue;
62356084
6236 // Open scope6085 for (self.globals.values()) |global| {
6237 try locals.ensureUnusedCapacity(3);6086 const sym = self.getSymbol(global);
6238 locals.appendAssumeCapacity(.{6087 if (sym.undf()) continue; // import, skip
6239 .n_strx = try self.makeString(object.tu_comp_dir.?),6088 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6240 .n_type = macho.N_SO,6089 var out_sym = sym;
6241 .n_sect = 0,6090 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6242 .n_desc = 0,6091 try exports.append(out_sym);
6243 .n_value = 0,6092 }
6244 });
6245 locals.appendAssumeCapacity(.{
6246 .n_strx = try self.makeString(object.tu_name.?),
6247 .n_type = macho.N_SO,
6248 .n_sect = 0,
6249 .n_desc = 0,
6250 .n_value = 0,
6251 });
6252 locals.appendAssumeCapacity(.{
6253 .n_strx = try self.makeString(object.name),
6254 .n_type = macho.N_OSO,
6255 .n_sect = 0,
6256 .n_desc = 1,
6257 .n_value = object.mtime orelse 0,
6258 });
62596093
6260 for (object.contained_atoms.items) |atom| {6094 var imports = std.ArrayList(macho.nlist_64).init(gpa);
6261 if (atom.stab) |stab| {6095 defer imports.deinit();
6262 const nlists = try stab.asNlists(atom.local_sym_index, self);6096 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6263 defer self.base.allocator.free(nlists);6097 defer imports_table.deinit();
6264 try locals.appendSlice(nlists);
6265 } else {
6266 for (atom.contained.items) |sym_at_off| {
6267 const stab = sym_at_off.stab orelse continue;
6268 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
6269 defer self.base.allocator.free(nlists);
6270 try locals.appendSlice(nlists);
6271 }
6272 }
6273 }
62746098
6275 // Close scope6099 for (self.globals.values()) |global| {
6276 try locals.append(.{6100 const sym = self.getSymbol(global);
6277 .n_strx = 0,6101 if (sym.n_strx == 0) continue; // no name, skip
6278 .n_type = macho.N_SO,6102 if (!sym.undf()) continue; // not an import, skip
6279 .n_sect = 0,6103 const new_index = @intCast(u32, imports.items.len);
6280 .n_desc = 0,6104 var out_sym = sym;
6281 .n_value = 0,6105 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6282 });6106 try imports.append(out_sym);
6283 }6107 try imports_table.putNoClobber(global, new_index);
6284 }6108 }
62856109
6286 const nlocals = locals.items.len;6110 const nlocals = locals.items.len;
6287 const nexports = self.globals.items.len;6111 const nexports = exports.items.len;
6288 const nundefs = undefs.items.len;6112 const nimports = imports.items.len;
62896113 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);
6290 const locals_off = symtab.symoff;
6291 const locals_size = nlocals * @sizeOf(macho.nlist_64);
6292 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
6293 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
62946114
6295 const exports_off = locals_off + locals_size;6115 var buffer = std.ArrayList(u8).init(gpa);
6296 const exports_size = nexports * @sizeOf(macho.nlist_64);6116 defer buffer.deinit();
6297 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });6117 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
6298 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.globals.items), exports_off);6118 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
6119 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
6120 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
62996121
6300 const undefs_off = exports_off + exports_size;6122 log.debug("writing symtab from 0x{x} to 0x{x}", .{ symtab.symoff, symtab.symoff + buffer.items.len });
6301 const undefs_size = nundefs * @sizeOf(macho.nlist_64);6123 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);
6302 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
6303 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
63046124
6305 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);6125 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
6306 seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff;
63076126
6308 // Update dynamic symbol table.6127 // Update dynamic symbol table.
6309 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;6128 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
...@@ -6311,7 +6130,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6311,7 +6130,7 @@ fn writeSymbolTable(self: *MachO) !void {
6311 dysymtab.iextdefsym = dysymtab.nlocalsym;6130 dysymtab.iextdefsym = dysymtab.nlocalsym;
6312 dysymtab.nextdefsym = @intCast(u32, nexports);6131 dysymtab.nextdefsym = @intCast(u32, nexports);
6313 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;6132 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6314 dysymtab.nundefsym = @intCast(u32, nundefs);6133 dysymtab.nundefsym = @intCast(u32, nimports);
63156134
6316 const nstubs = @intCast(u32, self.stubs_table.count());6135 const nstubs = @intCast(u32, self.stubs_table.count());
6317 const ngot_entries = @intCast(u32, self.got_entries_table.count());6136 const ngot_entries = @intCast(u32, self.got_entries_table.count());
...@@ -6327,55 +6146,62 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6327,55 +6146,62 @@ fn writeSymbolTable(self: *MachO) !void {
6327 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),6146 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6328 });6147 });
63296148
6330 var buf = std.ArrayList(u8).init(self.base.allocator);6149 var buf = std.ArrayList(u8).init(gpa);
6331 defer buf.deinit();6150 defer buf.deinit();
6332 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));6151 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
6333 const writer = buf.writer();6152 const writer = buf.writer();
63346153
6335 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {6154 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6336 const stubs_section_index = self.stubs_section_index orelse break :blk;6155 const stubs_section_index = self.stubs_section_index orelse break :blk;
6337 const text_segment = &self.load_commands.items[text_segment_cmd_index].segment;6156 const stubs = self.getSectionPtr(.{
6338 const stubs = &text_segment.sections.items[stubs_section_index];6157 .seg = text_segment_cmd_index,
6158 .sect = stubs_section_index,
6159 });
6339 stubs.reserved1 = 0;6160 stubs.reserved1 = 0;
6340 for (self.stubs_table.keys()) |key| {6161 for (self.stubs.items) |entry| {
6341 const resolv = self.symbol_resolver.get(key).?;6162 if (entry.sym_index == 0) continue;
6342 switch (resolv.where) {6163 const atom_sym = entry.getSymbol(self);
6343 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6164 if (atom_sym.n_desc == N_DESC_GCED) continue;
6344 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),6165 const target_sym = self.getSymbol(entry.target);
6345 }6166 assert(target_sym.undf());
6167 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6346 }6168 }
6347 }6169 }
63486170
6349 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {6171 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6350 const got_section_index = self.got_section_index orelse break :blk;6172 const got_section_index = self.got_section_index orelse break :blk;
6351 const data_const_segment = &self.load_commands.items[data_const_segment_cmd_index].segment;6173 const got = self.getSectionPtr(.{
6352 const got = &data_const_segment.sections.items[got_section_index];6174 .seg = data_const_segment_cmd_index,
6175 .sect = got_section_index,
6176 });
6353 got.reserved1 = nstubs;6177 got.reserved1 = nstubs;
6354 for (self.got_entries_table.keys()) |key| {6178 for (self.got_entries.items) |entry| {
6355 switch (key) {6179 if (entry.sym_index == 0) continue;
6356 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6180 const atom_sym = entry.getSymbol(self);
6357 .global => |n_strx| {6181 if (atom_sym.n_desc == N_DESC_GCED) continue;
6358 const resolv = self.symbol_resolver.get(n_strx).?;6182 const target_sym = self.getSymbol(entry.target);
6359 switch (resolv.where) {6183 if (target_sym.undf()) {
6360 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6184 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6361 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),6185 } else {
6362 }6186 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
6363 },
6364 }6187 }
6365 }6188 }
6366 }6189 }
63676190
6368 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {6191 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6369 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;6192 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6370 const data_segment = &self.load_commands.items[data_segment_cmd_index].segment;6193 const la_symbol_ptr = self.getSectionPtr(.{
6371 const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index];6194 .seg = data_segment_cmd_index,
6195 .sect = la_symbol_ptr_section_index,
6196 });
6372 la_symbol_ptr.reserved1 = nstubs + ngot_entries;6197 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6373 for (self.stubs_table.keys()) |key| {6198 for (self.stubs.items) |entry| {
6374 const resolv = self.symbol_resolver.get(key).?;6199 if (entry.sym_index == 0) continue;
6375 switch (resolv.where) {6200 const atom_sym = entry.getSymbol(self);
6376 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6201 if (atom_sym.n_desc == N_DESC_GCED) continue;
6377 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),6202 const target_sym = self.getSymbol(entry.target);
6378 }6203 assert(target_sym.undf());
6204 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6379 }6205 }
6380 }6206 }
63816207
...@@ -6385,21 +6211,22 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6385,21 +6211,22 @@ fn writeSymbolTable(self: *MachO) !void {
6385 self.load_commands_dirty = true;6211 self.load_commands_dirty = true;
6386}6212}
63876213
6388fn writeStringTable(self: *MachO) !void {6214fn writeStrtab(self: *MachO) !void {
6389 const tracy = trace(@src());6215 const tracy = trace(@src());
6390 defer tracy.end();6216 defer tracy.end();
63916217
6392 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;6218 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6393 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;6219 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6394 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));6220 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6395 const strsize = self.strtab.items.len;6221
6222 const strsize = self.strtab.buffer.items.len;
6396 symtab.stroff = @intCast(u32, stroff);6223 symtab.stroff = @intCast(u32, stroff);
6397 symtab.strsize = @intCast(u32, strsize);6224 symtab.strsize = @intCast(u32, strsize);
6398 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;6225 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;
63996226
6400 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });6227 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
64016228
6402 try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);6229 try self.base.file.?.pwriteAll(self.strtab.buffer.items, symtab.stroff);
64036230
6404 self.load_commands_dirty = true;6231 self.load_commands_dirty = true;
6405}6232}
...@@ -6413,9 +6240,9 @@ fn writeLinkeditSegment(self: *MachO) !void {...@@ -6413,9 +6240,9 @@ fn writeLinkeditSegment(self: *MachO) !void {
64136240
6414 try self.writeDyldInfoData();6241 try self.writeDyldInfoData();
6415 try self.writeFunctionStarts();6242 try self.writeFunctionStarts();
6416 try self.writeDices();6243 try self.writeDataInCode();
6417 try self.writeSymbolTable();6244 try self.writeSymtab();
6418 try self.writeStringTable();6245 try self.writeStrtab();
64196246
6420 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);6247 seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size);
6421}6248}
...@@ -6557,43 +6384,114 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {...@@ -6557,43 +6384,114 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
6557 return buf;6384 return buf;
6558}6385}
65596386
6560pub fn makeString(self: *MachO, string: []const u8) !u32 {6387pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {
6561 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{6388 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;
6562 .bytes = &self.strtab,6389}
6563 }, StringIndexContext{
6564 .bytes = &self.strtab,
6565 });
6566 if (gop.found_existing) {
6567 const off = gop.key_ptr.*;
6568 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
6569 return off;
6570 }
6571
6572 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);
6573 const new_off = @intCast(u32, self.strtab.items.len);
65746390
6575 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });6391pub fn getMatchingSectionFromOrdinal(self: *MachO, ord: u8) MatchingSection {
6392 const index = ord - 1;
6393 assert(index < self.section_ordinals.count());
6394 return self.section_ordinals.keys()[index];
6395}
65766396
6577 self.strtab.appendSliceAssumeCapacity(string);6397pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
6578 self.strtab.appendAssumeCapacity(0);6398 assert(match.seg < self.load_commands.items.len);
6399 return &self.load_commands.items[match.seg].segment;
6400}
65796401
6580 gop.key_ptr.* = new_off;6402pub fn getSegment(self: *MachO, match: MatchingSection) macho.SegmentCommand {
6403 return self.getSegmentPtr(match).*;
6404}
65816405
6582 return new_off;6406pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {
6407 const seg = self.getSegmentPtr(match);
6408 assert(match.sect < seg.sections.items.len);
6409 return &seg.sections.items[match.sect];
6583}6410}
65846411
6585pub fn getString(self: MachO, off: u32) []const u8 {6412pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6586 assert(off < self.strtab.items.len);6413 return self.getSectionPtr(match).*;
6587 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);
6588}6414}
65896415
6590pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {6416pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
6417 const sym = self.getSymbol(sym_with_loc);
6591 if (!sym.sect()) return false;6418 if (!sym.sect()) return false;
6592 if (sym.ext()) return false;6419 if (sym.ext()) return false;
6420 const sym_name = self.getSymbolName(sym_with_loc);
6593 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");6421 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
6594}6422}
65956423
6596pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anytype) usize {6424/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
6425pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
6426 if (sym_with_loc.file) |file| {
6427 const object = &self.objects.items[file];
6428 return &object.symtab.items[sym_with_loc.sym_index];
6429 } else {
6430 return &self.locals.items[sym_with_loc.sym_index];
6431 }
6432}
6433
6434/// Returns symbol described by `sym_with_loc` descriptor.
6435pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
6436 return self.getSymbolPtr(sym_with_loc).*;
6437}
6438
6439/// Returns name of the symbol described by `sym_with_loc` descriptor.
6440pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {
6441 if (sym_with_loc.file) |file| {
6442 const object = self.objects.items[file];
6443 const sym = object.symtab.items[sym_with_loc.sym_index];
6444 return object.getString(sym.n_strx);
6445 } else {
6446 const sym = self.locals.items[sym_with_loc.sym_index];
6447 return self.strtab.get(sym.n_strx).?;
6448 }
6449}
6450
6451/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
6452/// Returns null on failure.
6453pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6454 if (sym_with_loc.file) |file| {
6455 const object = self.objects.items[file];
6456 return object.getAtomForSymbol(sym_with_loc.sym_index);
6457 } else {
6458 return self.atom_by_index_table.get(sym_with_loc.sym_index);
6459 }
6460}
6461
6462/// Returns GOT atom that references `sym_with_loc` if one exists.
6463/// Returns null otherwise.
6464pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6465 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
6466 return self.got_entries.items[got_index].getAtom(self);
6467}
6468
6469/// Returns stubs atom that references `sym_with_loc` if one exists.
6470/// Returns null otherwise.
6471pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6472 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
6473 return self.stubs.items[stubs_index].getAtom(self);
6474}
6475
6476/// Returns TLV pointer atom that references `sym_with_loc` if one exists.
6477/// Returns null otherwise.
6478pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6479 const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null;
6480 return self.tlv_ptr_entries.items[tlv_ptr_index].getAtom(self);
6481}
6482
6483/// Returns symbol location corresponding to the set entrypoint.
6484/// Asserts output mode is executable.
6485pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
6486 const entry_name = self.base.options.entry orelse "_main";
6487 const global = self.globals.get(entry_name) orelse {
6488 log.err("entrypoint '{s}' not found", .{entry_name});
6489 return error.MissingMainEntrypoint;
6490 };
6491 return global;
6492}
6493
6494pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
6597 if (!@hasDecl(@TypeOf(predicate), "predicate"))6495 if (!@hasDecl(@TypeOf(predicate), "predicate"))
6598 @compileError("Predicate is required to define fn predicate(@This(), T) bool");6496 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
65996497
...@@ -6606,6 +6504,225 @@ pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anyty...@@ -6606,6 +6504,225 @@ pub fn findFirst(comptime T: type, haystack: []T, start: usize, predicate: anyty
6606 return i;6504 return i;
6607}6505}
66086506
6507const DebugInfo = struct {
6508 inner: dwarf.DwarfInfo,
6509 debug_info: []const u8,
6510 debug_abbrev: []const u8,
6511 debug_str: []const u8,
6512 debug_line: []const u8,
6513 debug_line_str: []const u8,
6514 debug_ranges: []const u8,
6515
6516 pub fn parse(allocator: Allocator, object: Object) !?DebugInfo {
6517 var debug_info = blk: {
6518 const index = object.dwarf_debug_info_index orelse return null;
6519 break :blk try object.getSectionContents(index);
6520 };
6521 var debug_abbrev = blk: {
6522 const index = object.dwarf_debug_abbrev_index orelse return null;
6523 break :blk try object.getSectionContents(index);
6524 };
6525 var debug_str = blk: {
6526 const index = object.dwarf_debug_str_index orelse return null;
6527 break :blk try object.getSectionContents(index);
6528 };
6529 var debug_line = blk: {
6530 const index = object.dwarf_debug_line_index orelse return null;
6531 break :blk try object.getSectionContents(index);
6532 };
6533 var debug_line_str = blk: {
6534 if (object.dwarf_debug_line_str_index) |ind| {
6535 break :blk try object.getSectionContents(ind);
6536 }
6537 break :blk &[0]u8{};
6538 };
6539 var debug_ranges = blk: {
6540 if (object.dwarf_debug_ranges_index) |ind| {
6541 break :blk try object.getSectionContents(ind);
6542 }
6543 break :blk &[0]u8{};
6544 };
6545
6546 var inner: dwarf.DwarfInfo = .{
6547 .endian = .Little,
6548 .debug_info = debug_info,
6549 .debug_abbrev = debug_abbrev,
6550 .debug_str = debug_str,
6551 .debug_line = debug_line,
6552 .debug_line_str = debug_line_str,
6553 .debug_ranges = debug_ranges,
6554 };
6555 try dwarf.openDwarfDebugInfo(&inner, allocator);
6556
6557 return DebugInfo{
6558 .inner = inner,
6559 .debug_info = debug_info,
6560 .debug_abbrev = debug_abbrev,
6561 .debug_str = debug_str,
6562 .debug_line = debug_line,
6563 .debug_line_str = debug_line_str,
6564 .debug_ranges = debug_ranges,
6565 };
6566 }
6567
6568 pub fn deinit(self: *DebugInfo, allocator: Allocator) void {
6569 self.inner.deinit(allocator);
6570 }
6571};
6572
6573pub fn generateSymbolStabs(
6574 self: *MachO,
6575 object: Object,
6576 locals: *std.ArrayList(macho.nlist_64),
6577) !void {
6578 assert(!self.base.options.strip);
6579
6580 const gpa = self.base.allocator;
6581
6582 log.debug("parsing debug info in '{s}'", .{object.name});
6583
6584 var debug_info = (try DebugInfo.parse(gpa, object)) orelse return;
6585
6586 // We assume there is only one CU.
6587 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
6588 error.MissingDebugInfo => {
6589 // TODO audit cases with missing debug info and audit our dwarf.zig module.
6590 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
6591 return;
6592 },
6593 else => |e| return e,
6594 };
6595 const tu_name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);
6596 const tu_comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);
6597
6598 // Open scope
6599 try locals.ensureUnusedCapacity(3);
6600 locals.appendAssumeCapacity(.{
6601 .n_strx = try self.strtab.insert(gpa, tu_comp_dir),
6602 .n_type = macho.N_SO,
6603 .n_sect = 0,
6604 .n_desc = 0,
6605 .n_value = 0,
6606 });
6607 locals.appendAssumeCapacity(.{
6608 .n_strx = try self.strtab.insert(gpa, tu_name),
6609 .n_type = macho.N_SO,
6610 .n_sect = 0,
6611 .n_desc = 0,
6612 .n_value = 0,
6613 });
6614 locals.appendAssumeCapacity(.{
6615 .n_strx = try self.strtab.insert(gpa, object.name),
6616 .n_type = macho.N_OSO,
6617 .n_sect = 0,
6618 .n_desc = 1,
6619 .n_value = object.mtime,
6620 });
6621
6622 var stabs_buf: [4]macho.nlist_64 = undefined;
6623
6624 for (object.managed_atoms.items) |atom| {
6625 const stabs = try self.generateSymbolStabsForSymbol(
6626 atom.getSymbolWithLoc(),
6627 debug_info,
6628 &stabs_buf,
6629 );
6630 try locals.appendSlice(stabs);
6631
6632 for (atom.contained.items) |sym_at_off| {
6633 const sym_loc = SymbolWithLoc{
6634 .sym_index = sym_at_off.sym_index,
6635 .file = atom.file,
6636 };
6637 const contained_stabs = try self.generateSymbolStabsForSymbol(
6638 sym_loc,
6639 debug_info,
6640 &stabs_buf,
6641 );
6642 try locals.appendSlice(contained_stabs);
6643 }
6644 }
6645
6646 // Close scope
6647 try locals.append(.{
6648 .n_strx = 0,
6649 .n_type = macho.N_SO,
6650 .n_sect = 0,
6651 .n_desc = 0,
6652 .n_value = 0,
6653 });
6654}
6655
6656fn generateSymbolStabsForSymbol(
6657 self: *MachO,
6658 sym_loc: SymbolWithLoc,
6659 debug_info: DebugInfo,
6660 buf: *[4]macho.nlist_64,
6661) ![]const macho.nlist_64 {
6662 const gpa = self.base.allocator;
6663 const object = self.objects.items[sym_loc.file.?];
6664 const sym = self.getSymbol(sym_loc);
6665 const sym_name = self.getSymbolName(sym_loc);
6666
6667 if (sym.n_strx == 0) return buf[0..0];
6668 if (sym.n_desc == N_DESC_GCED) return buf[0..0];
6669 if (self.symbolIsTemp(sym_loc)) return buf[0..0];
6670
6671 const source_sym = object.getSourceSymbol(sym_loc.sym_index) orelse return buf[0..0];
6672 const size: ?u64 = size: {
6673 if (source_sym.tentative()) break :size null;
6674 for (debug_info.inner.func_list.items) |func| {
6675 if (func.pc_range) |range| {
6676 if (source_sym.n_value >= range.start and source_sym.n_value < range.end) {
6677 break :size range.end - range.start;
6678 }
6679 }
6680 }
6681 break :size null;
6682 };
6683
6684 if (size) |ss| {
6685 buf[0] = .{
6686 .n_strx = 0,
6687 .n_type = macho.N_BNSYM,
6688 .n_sect = sym.n_sect,
6689 .n_desc = 0,
6690 .n_value = sym.n_value,
6691 };
6692 buf[1] = .{
6693 .n_strx = try self.strtab.insert(gpa, sym_name),
6694 .n_type = macho.N_FUN,
6695 .n_sect = sym.n_sect,
6696 .n_desc = 0,
6697 .n_value = sym.n_value,
6698 };
6699 buf[2] = .{
6700 .n_strx = 0,
6701 .n_type = macho.N_FUN,
6702 .n_sect = 0,
6703 .n_desc = 0,
6704 .n_value = ss,
6705 };
6706 buf[3] = .{
6707 .n_strx = 0,
6708 .n_type = macho.N_ENSYM,
6709 .n_sect = sym.n_sect,
6710 .n_desc = 0,
6711 .n_value = ss,
6712 };
6713 return buf;
6714 } else {
6715 buf[0] = .{
6716 .n_strx = try self.strtab.insert(gpa, sym_name),
6717 .n_type = macho.N_STSYM,
6718 .n_sect = sym.n_sect,
6719 .n_desc = 0,
6720 .n_value = sym.n_value,
6721 };
6722 return buf[0..1];
6723 }
6724}
6725
6609fn snapshotState(self: *MachO) !void {6726fn snapshotState(self: *MachO) !void {
6610 const emit = self.base.options.emit orelse {6727 const emit = self.base.options.emit orelse {
6611 log.debug("no emit directory found; skipping snapshot...", .{});6728 log.debug("no emit directory found; skipping snapshot...", .{});
...@@ -6655,7 +6772,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6655,7 +6772,7 @@ fn snapshotState(self: *MachO) !void {
6655 const arena = arena_allocator.allocator();6772 const arena = arena_allocator.allocator();
66566773
6657 const out_file = try emit.directory.handle.createFile("snapshots.json", .{6774 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6658 .truncate = self.cold_start,6775 .truncate = false,
6659 .read = true,6776 .read = true,
6660 });6777 });
6661 defer out_file.close();6778 defer out_file.close();
...@@ -6675,8 +6792,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6675,8 +6792,7 @@ fn snapshotState(self: *MachO) !void {
6675 var nodes = std.ArrayList(Snapshot.Node).init(arena);6792 var nodes = std.ArrayList(Snapshot.Node).init(arena);
66766793
6677 for (self.section_ordinals.keys()) |key| {6794 for (self.section_ordinals.keys()) |key| {
6678 const seg = self.load_commands.items[key.seg].segment;6795 const sect = self.getSection(key);
6679 const sect = seg.sections.items[key.sect];
6680 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });6796 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6681 try nodes.append(.{6797 try nodes.append(.{
6682 .address = sect.addr,6798 .address = sect.addr,
...@@ -6684,6 +6800,8 @@ fn snapshotState(self: *MachO) !void {...@@ -6684,6 +6800,8 @@ fn snapshotState(self: *MachO) !void {
6684 .payload = .{ .name = sect_name },6800 .payload = .{ .name = sect_name },
6685 });6801 });
66866802
6803 const is_tlv = sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
6804
6687 var atom: *Atom = self.atoms.get(key) orelse {6805 var atom: *Atom = self.atoms.get(key) orelse {
6688 try nodes.append(.{6806 try nodes.append(.{
6689 .address = sect.addr + sect.size,6807 .address = sect.addr + sect.size,
...@@ -6698,103 +6816,63 @@ fn snapshotState(self: *MachO) !void {...@@ -6698,103 +6816,63 @@ fn snapshotState(self: *MachO) !void {
6698 }6816 }
66996817
6700 while (true) {6818 while (true) {
6701 const atom_sym = self.locals.items[atom.local_sym_index];6819 const atom_sym = atom.getSymbol(self);
6702 const should_skip_atom: bool = blk: {
6703 if (self.mh_execute_header_index) |index| {
6704 if (index == atom.local_sym_index) break :blk true;
6705 }
6706 if (mem.eql(u8, self.getString(atom_sym.n_strx), "___dso_handle")) break :blk true;
6707 break :blk false;
6708 };
6709
6710 if (should_skip_atom) {
6711 if (atom.next) |next| {
6712 atom = next;
6713 } else break;
6714 continue;
6715 }
6716
6717 var node = Snapshot.Node{6820 var node = Snapshot.Node{
6718 .address = atom_sym.n_value,6821 .address = atom_sym.n_value,
6719 .tag = .atom_start,6822 .tag = .atom_start,
6720 .payload = .{6823 .payload = .{
6721 .name = self.getString(atom_sym.n_strx),6824 .name = atom.getName(self),
6722 .is_global = self.symbol_resolver.contains(atom_sym.n_strx),6825 .is_global = self.globals.contains(atom.getName(self)),
6723 },6826 },
6724 };6827 };
67256828
6726 var aliases = std.ArrayList([]const u8).init(arena);6829 var aliases = std.ArrayList([]const u8).init(arena);
6727 for (atom.aliases.items) |loc| {6830 for (atom.contained.items) |sym_off| {
6728 try aliases.append(self.getString(self.locals.items[loc].n_strx));6831 if (sym_off.offset == 0) {
6832 try aliases.append(self.getSymbolName(.{
6833 .sym_index = sym_off.sym_index,
6834 .file = atom.file,
6835 }));
6836 }
6729 }6837 }
6730 node.payload.aliases = aliases.toOwnedSlice();6838 node.payload.aliases = aliases.toOwnedSlice();
6731 try nodes.append(node);6839 try nodes.append(node);
67326840
6733 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);6841 var relocs = try std.ArrayList(Snapshot.Node).initCapacity(arena, atom.relocs.items.len);
6734 for (atom.relocs.items) |rel| {6842 for (atom.relocs.items) |rel| {
6735 const arch = self.base.options.target.cpu.arch;
6736 const source_addr = blk: {6843 const source_addr = blk: {
6737 const sym = self.locals.items[atom.local_sym_index];6844 const source_sym = atom.getSymbol(self);
6738 break :blk sym.n_value + rel.offset;6845 break :blk source_sym.n_value + rel.offset;
6739 };6846 };
6740 const target_addr = blk: {6847 const target_addr = blk: {
6741 const is_via_got = got: {6848 const target_atom = rel.getTargetAtom(self) orelse {
6742 switch (arch) {6849 // If there is no atom for target, we still need to check for special, atom-less
6743 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {6850 // symbols such as `___dso_handle`.
6744 .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => true,6851 const target_name = self.getSymbolName(rel.target);
6745 else => false,6852 if (self.globals.contains(target_name)) {
6746 },6853 const atomless_sym = self.getSymbol(rel.target);
6747 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {6854 break :blk atomless_sym.n_value;
6748 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
6749 else => false,
6750 },
6751 else => unreachable,
6752 }6855 }
6856 break :blk 0;
6753 };6857 };
67546858 const target_sym = if (target_atom.isSymbolContained(rel.target, self))
6755 if (is_via_got) {6859 self.getSymbol(rel.target)
6756 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;6860 else
6757 const got_atom = self.got_entries.items[got_index].atom;6861 target_atom.getSymbol(self);
6758 break :blk self.locals.items[got_atom.local_sym_index].n_value;6862 const base_address: u64 = if (is_tlv) base_address: {
6759 }6863 const sect_id: u16 = sect_id: {
67606864 if (self.tlv_data_section_index) |i| {
6761 switch (rel.target) {6865 break :sect_id i;
6762 .local => |sym_index| {6866 } else if (self.tlv_bss_section_index) |i| {
6763 const sym = self.locals.items[sym_index];6867 break :sect_id i;
6764 const is_tlv = is_tlv: {6868 } else unreachable;
6765 const source_sym = self.locals.items[atom.local_sym_index];6869 };
6766 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];6870 break :base_address self.getSection(.{
6767 const match_seg = self.load_commands.items[match.seg].segment;6871 .seg = self.data_segment_cmd_index.?,
6768 const match_sect = match_seg.sections.items[match.sect];6872 .sect = sect_id,
6769 break :is_tlv match_sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;6873 }).addr;
6770 };6874 } else 0;
6771 if (is_tlv) {6875 break :blk target_sym.n_value - base_address;
6772 const match_seg = self.load_commands.items[self.data_segment_cmd_index.?].segment;
6773 const base_address = inner: {
6774 if (self.tlv_data_section_index) |i| {
6775 break :inner match_seg.sections.items[i].addr;
6776 } else if (self.tlv_bss_section_index) |i| {
6777 break :inner match_seg.sections.items[i].addr;
6778 } else unreachable;
6779 };
6780 break :blk sym.n_value - base_address;
6781 }
6782 break :blk sym.n_value;
6783 },
6784 .global => |n_strx| {
6785 const resolv = self.symbol_resolver.get(n_strx).?;
6786 switch (resolv.where) {
6787 .global => break :blk self.globals.items[resolv.where_index].n_value,
6788 .undef => {
6789 if (self.stubs_table.get(n_strx)) |stub_index| {
6790 const stub_atom = self.stubs.items[stub_index];
6791 break :blk self.locals.items[stub_atom.local_sym_index].n_value;
6792 }
6793 break :blk 0;
6794 },
6795 }
6796 },
6797 }
6798 };6876 };
67996877
6800 relocs.appendAssumeCapacity(.{6878 relocs.appendAssumeCapacity(.{
...@@ -6815,15 +6893,18 @@ fn snapshotState(self: *MachO) !void {...@@ -6815,15 +6893,18 @@ fn snapshotState(self: *MachO) !void {
6815 var next_i: usize = 0;6893 var next_i: usize = 0;
6816 var last_rel: usize = 0;6894 var last_rel: usize = 0;
6817 while (next_i < atom.contained.items.len) : (next_i += 1) {6895 while (next_i < atom.contained.items.len) : (next_i += 1) {
6818 const loc = atom.contained.items[next_i];6896 const loc = SymbolWithLoc{
6819 const cont_sym = self.locals.items[loc.local_sym_index];6897 .sym_index = atom.contained.items[next_i].sym_index,
6820 const cont_sym_name = self.getString(cont_sym.n_strx);6898 .file = atom.file,
6899 };
6900 const cont_sym = self.getSymbol(loc);
6901 const cont_sym_name = self.getSymbolName(loc);
6821 var contained_node = Snapshot.Node{6902 var contained_node = Snapshot.Node{
6822 .address = cont_sym.n_value,6903 .address = cont_sym.n_value,
6823 .tag = .atom_start,6904 .tag = .atom_start,
6824 .payload = .{6905 .payload = .{
6825 .name = cont_sym_name,6906 .name = cont_sym_name,
6826 .is_global = self.symbol_resolver.contains(cont_sym.n_strx),6907 .is_global = self.globals.contains(cont_sym_name),
6827 },6908 },
6828 };6909 };
68296910
...@@ -6831,10 +6912,14 @@ fn snapshotState(self: *MachO) !void {...@@ -6831,10 +6912,14 @@ fn snapshotState(self: *MachO) !void {
6831 var inner_aliases = std.ArrayList([]const u8).init(arena);6912 var inner_aliases = std.ArrayList([]const u8).init(arena);
6832 while (true) {6913 while (true) {
6833 if (next_i + 1 >= atom.contained.items.len) break;6914 if (next_i + 1 >= atom.contained.items.len) break;
6834 const next_sym = self.locals.items[atom.contained.items[next_i + 1].local_sym_index];6915 const next_sym_loc = SymbolWithLoc{
6916 .sym_index = atom.contained.items[next_i + 1].sym_index,
6917 .file = atom.file,
6918 };
6919 const next_sym = self.getSymbol(next_sym_loc);
6835 if (next_sym.n_value != cont_sym.n_value) break;6920 if (next_sym.n_value != cont_sym.n_value) break;
6836 const next_sym_name = self.getString(next_sym.n_strx);6921 const next_sym_name = self.getSymbolName(next_sym_loc);
6837 if (self.symbol_resolver.contains(next_sym.n_strx)) {6922 if (self.globals.contains(next_sym_name)) {
6838 try inner_aliases.append(contained_node.payload.name);6923 try inner_aliases.append(contained_node.payload.name);
6839 contained_node.payload.name = next_sym_name;6924 contained_node.payload.name = next_sym_name;
6840 contained_node.payload.is_global = true;6925 contained_node.payload.is_global = true;
...@@ -6843,7 +6928,10 @@ fn snapshotState(self: *MachO) !void {...@@ -6843,7 +6928,10 @@ fn snapshotState(self: *MachO) !void {
6843 }6928 }
68446929
6845 const cont_size = if (next_i + 1 < atom.contained.items.len)6930 const cont_size = if (next_i + 1 < atom.contained.items.len)
6846 self.locals.items[atom.contained.items[next_i + 1].local_sym_index].n_value - cont_sym.n_value6931 self.getSymbol(.{
6932 .sym_index = atom.contained.items[next_i + 1].sym_index,
6933 .file = atom.file,
6934 }).n_value - cont_sym.n_value
6847 else6935 else
6848 atom_sym.n_value + atom.size - cont_sym.n_value;6936 atom_sym.n_value + atom.size - cont_sym.n_value;
68496937
...@@ -6890,69 +6978,181 @@ fn snapshotState(self: *MachO) !void {...@@ -6890,69 +6978,181 @@ fn snapshotState(self: *MachO) !void {
6890 try writer.writeByte(']');6978 try writer.writeByte(']');
6891}6979}
68926980
6893fn logSymtab(self: MachO) void {6981fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
6894 log.debug("locals:", .{});6982 mem.set(u8, buf[0..4], '_');
6895 for (self.locals.items) |sym, id| {6983 mem.set(u8, buf[4..], ' ');
6896 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });6984 if (sym.sect()) {
6985 buf[0] = 's';
6897 }6986 }
68986987 if (sym.ext()) {
6899 log.debug("globals:", .{});6988 if (sym.weakDef() or sym.pext()) {
6900 for (self.globals.items) |sym, id| {6989 buf[1] = 'w';
6901 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });6990 } else {
6991 buf[1] = 'e';
6992 }
6902 }6993 }
69036994 if (sym.tentative()) {
6904 log.debug("undefs:", .{});6995 buf[2] = 't';
6905 for (self.undefs.items) |sym, id| {6996 }
6906 log.debug(" {d}: {s}: in {d}", .{ id, self.getString(sym.n_strx), sym.n_desc });6997 if (sym.undf()) {
6998 buf[3] = 'u';
6907 }6999 }
7000 if (sym.n_desc == N_DESC_GCED) {
7001 mem.copy(u8, buf[5..], "DEAD");
7002 }
7003 return buf[0..];
7004}
69087005
6909 {7006fn logSymtab(self: *MachO) void {
6910 log.debug("resolver:", .{});7007 var buf: [9]u8 = undefined;
6911 var it = self.symbol_resolver.iterator();7008
6912 while (it.next()) |entry| {7009 log.debug("symtab:", .{});
6913 log.debug(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });7010 for (self.objects.items) |object, id| {
7011 log.debug(" object({d}): {s}", .{ id, object.name });
7012 for (object.symtab.items) |sym, sym_id| {
7013 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
7014 const def_index = if (sym.undf() and !sym.tentative())
7015 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
7016 else
7017 sym.n_sect;
7018 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
7019 sym_id,
7020 object.getString(sym.n_strx),
7021 sym.n_value,
7022 where,
7023 def_index,
7024 logSymAttributes(sym, &buf),
7025 });
6914 }7026 }
6915 }7027 }
7028 log.debug(" object(null)", .{});
7029 for (self.locals.items) |sym, sym_id| {
7030 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
7031 const def_index = if (sym.undf() and !sym.tentative())
7032 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
7033 else
7034 sym.n_sect;
7035 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
7036 sym_id,
7037 self.strtab.get(sym.n_strx),
7038 sym.n_value,
7039 where,
7040 def_index,
7041 logSymAttributes(sym, &buf),
7042 });
7043 }
7044
7045 log.debug("globals table:", .{});
7046 for (self.globals.keys()) |name, id| {
7047 const value = self.globals.values()[id];
7048 log.debug(" {s} => %{d} in object({d})", .{ name, value.sym_index, value.file });
7049 }
69167050
6917 log.debug("GOT entries:", .{});7051 log.debug("GOT entries:", .{});
6918 for (self.got_entries_table.values()) |value| {7052 for (self.got_entries.items) |entry, i| {
6919 const key = self.got_entries.items[value].target;7053 const atom_sym = entry.getSymbol(self);
6920 const atom = self.got_entries.items[value].atom;7054 if (atom_sym.n_desc == N_DESC_GCED) continue;
6921 const n_value = self.locals.items[atom.local_sym_index].n_value;7055 const target_sym = self.getSymbol(entry.target);
6922 switch (key) {7056 if (target_sym.undf()) {
6923 .local => |ndx| log.debug(" {d}: @{x}", .{ ndx, n_value }),7057 log.debug(" {d}@{x} => import('{s}')", .{
6924 .global => |n_strx| log.debug(" {s}: @{x}", .{ self.getString(n_strx), n_value }),7058 i,
7059 atom_sym.n_value,
7060 self.getSymbolName(entry.target),
7061 });
7062 } else {
7063 log.debug(" {d}@{x} => local(%{d}) in object({d}) {s}", .{
7064 i,
7065 atom_sym.n_value,
7066 entry.target.sym_index,
7067 entry.target.file,
7068 logSymAttributes(target_sym, &buf),
7069 });
6925 }7070 }
6926 }7071 }
69277072
6928 log.debug("__thread_ptrs entries:", .{});7073 log.debug("__thread_ptrs entries:", .{});
6929 for (self.tlv_ptr_entries_table.values()) |value| {7074 for (self.tlv_ptr_entries.items) |entry, i| {
6930 const key = self.tlv_ptr_entries.items[value].target;7075 const atom_sym = entry.getSymbol(self);
6931 const atom = self.tlv_ptr_entries.items[value].atom;7076 if (atom_sym.n_desc == N_DESC_GCED) continue;
6932 const n_value = self.locals.items[atom.local_sym_index].n_value;7077 const target_sym = self.getSymbol(entry.target);
6933 assert(key == .global);7078 assert(target_sym.undf());
6934 log.debug(" {s}: @{x}", .{ self.getString(key.global), n_value });7079 log.debug(" {d}@{x} => import('{s}')", .{
7080 i,
7081 atom_sym.n_value,
7082 self.getSymbolName(entry.target),
7083 });
6935 }7084 }
69367085
6937 log.debug("stubs:", .{});7086 log.debug("stubs entries:", .{});
6938 for (self.stubs_table.keys()) |key| {7087 for (self.stubs.items) |entry, i| {
6939 const value = self.stubs_table.get(key).?;7088 const target_sym = self.getSymbol(entry.target);
6940 const atom = self.stubs.items[value];7089 const atom_sym = entry.getSymbol(self);
6941 const sym = self.locals.items[atom.local_sym_index];7090 assert(target_sym.undf());
6942 log.debug(" {s}: @{x}", .{ self.getString(key), sym.n_value });7091 log.debug(" {d}@{x} => import('{s}')", .{
7092 i,
7093 atom_sym.n_value,
7094 self.getSymbolName(entry.target),
7095 });
6943 }7096 }
6944}7097}
69457098
6946fn logSectionOrdinals(self: MachO) void {7099fn logSectionOrdinals(self: *MachO) void {
6947 for (self.section_ordinals.keys()) |match, i| {7100 for (self.section_ordinals.keys()) |match, i| {
6948 const seg = self.load_commands.items[match.seg].segment;7101 const sect = self.getSection(match);
6949 const sect = seg.sections.items[match.sect];7102 log.debug("sect({d}, '{s},{s}')", .{ i + 1, sect.segName(), sect.sectName() });
6950 log.debug("ord {d}: {d},{d} => {s},{s}", .{7103 }
6951 i + 1,7104}
6952 match.seg,7105
6953 match.sect,7106fn logAtoms(self: *MachO) void {
6954 sect.segName(),7107 log.debug("atoms:", .{});
6955 sect.sectName(),7108 var it = self.atoms.iterator();
7109 while (it.next()) |entry| {
7110 const match = entry.key_ptr.*;
7111 var atom = entry.value_ptr.*;
7112
7113 while (atom.prev) |prev| {
7114 atom = prev;
7115 }
7116
7117 const sect = self.getSection(match);
7118 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
7119
7120 while (true) {
7121 self.logAtom(atom);
7122 if (atom.next) |next| {
7123 atom = next;
7124 } else break;
7125 }
7126 }
7127}
7128
7129pub fn logAtom(self: *MachO, atom: *const Atom) void {
7130 const sym = atom.getSymbol(self);
7131 const sym_name = atom.getName(self);
7132 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{
7133 atom.sym_index,
7134 sym_name,
7135 sym.n_value,
7136 atom.size,
7137 atom.alignment,
7138 atom.file,
7139 sym.n_sect,
7140 });
7141
7142 for (atom.contained.items) |sym_off| {
7143 const inner_sym = self.getSymbol(.{
7144 .sym_index = sym_off.sym_index,
7145 .file = atom.file,
7146 });
7147 const inner_sym_name = self.getSymbolName(.{
7148 .sym_index = sym_off.sym_index,
7149 .file = atom.file,
7150 });
7151 log.debug(" (%{d}, '{s}') @ {x} ({x})", .{
7152 sym_off.sym_index,
7153 inner_sym_name,
7154 inner_sym.n_value,
7155 sym_off.offset,
6956 });7156 });
6957 }7157 }
6958}7158}
src/link/MachO/Atom.zig+271-397
...@@ -16,7 +16,7 @@ const Arch = std.Target.Cpu.Arch;...@@ -16,7 +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 StringIndexAdapter = std.hash_map.StringIndexAdapter;19const SymbolWithLoc = MachO.SymbolWithLoc;
2020
21/// Each decl always gets a local symbol with the fully qualified name.21/// Each decl always gets a local symbol with the fully qualified name.
22/// The vaddr and size are found here directly.22/// The vaddr and size are found here directly.
...@@ -24,10 +24,10 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;...@@ -24,10 +24,10 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
24/// the symbol references, and adding that to the file offset of the section.24/// the symbol references, and adding that to the file offset of the section.
25/// If this field is 0, it means the codegen size = 0 and there is no symbol or25/// If this field is 0, it means the codegen size = 0 and there is no symbol or
26/// offset table entry.26/// offset table entry.
27local_sym_index: u32,27sym_index: u32,
2828
29/// List of symbol aliases pointing to the same atom via different nlists29/// null means symbol defined by Zig source.
30aliases: std.ArrayListUnmanaged(u32) = .{},30file: ?u32,
3131
32/// List of symbols contained within this atom32/// List of symbols contained within this atom
33contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},33contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
...@@ -48,26 +48,17 @@ alignment: u32,...@@ -48,26 +48,17 @@ alignment: u32,
48relocs: std.ArrayListUnmanaged(Relocation) = .{},48relocs: std.ArrayListUnmanaged(Relocation) = .{},
4949
50/// List of offsets contained within this atom that need rebasing by the dynamic50/// List of offsets contained within this atom that need rebasing by the dynamic
51/// loader in presence of ASLR.51/// loader for example in presence of ASLR.
52rebases: std.ArrayListUnmanaged(u64) = .{},52rebases: std.ArrayListUnmanaged(u64) = .{},
5353
54/// List of offsets contained within this atom that will be dynamically bound54/// List of offsets contained within this atom that will be dynamically bound
55/// by the dynamic loader and contain pointers to resolved (at load time) extern55/// by the dynamic loader and contain pointers to resolved (at load time) extern
56/// symbols (aka proxies aka imports)56/// symbols (aka proxies aka imports).
57bindings: std.ArrayListUnmanaged(Binding) = .{},57bindings: std.ArrayListUnmanaged(Binding) = .{},
5858
59/// List of lazy bindings59/// List of lazy bindings (cf bindings above).
60lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},60lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
6161
62/// List of data-in-code entries. This is currently specific to x86_64 only.
63dices: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
64
65/// Stab entry for this atom. This is currently specific to a binary created
66/// by linking object files in a traditional sense - in incremental sense, we
67/// bypass stabs altogether to produce dSYM bundle directly with fully relocated
68/// DWARF sections.
69stab: ?Stab = null,
70
71/// Points to the previous and next neighbours62/// Points to the previous and next neighbours
72next: ?*Atom,63next: ?*Atom,
73prev: ?*Atom,64prev: ?*Atom,
...@@ -77,107 +68,62 @@ dbg_info_atom: Dwarf.Atom,...@@ -77,107 +68,62 @@ dbg_info_atom: Dwarf.Atom,
77dirty: bool = true,68dirty: bool = true,
7869
79pub const Binding = struct {70pub const Binding = struct {
80 n_strx: u32,71 target: SymbolWithLoc,
81 offset: u64,72 offset: u64,
82};73};
8374
84pub const SymbolAtOffset = struct {75pub const SymbolAtOffset = struct {
85 local_sym_index: u32,76 sym_index: u32,
86 offset: u64,77 offset: u64,
87 stab: ?Stab = null,
88};
89
90pub const Stab = union(enum) {
91 function: u64,
92 static,
93 global,
94
95 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
96 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
97 defer nlists.deinit();
98
99 const sym = macho_file.locals.items[local_sym_index];
100 switch (stab) {
101 .function => |size| {
102 try nlists.ensureUnusedCapacity(4);
103 nlists.appendAssumeCapacity(.{
104 .n_strx = 0,
105 .n_type = macho.N_BNSYM,
106 .n_sect = sym.n_sect,
107 .n_desc = 0,
108 .n_value = sym.n_value,
109 });
110 nlists.appendAssumeCapacity(.{
111 .n_strx = sym.n_strx,
112 .n_type = macho.N_FUN,
113 .n_sect = sym.n_sect,
114 .n_desc = 0,
115 .n_value = sym.n_value,
116 });
117 nlists.appendAssumeCapacity(.{
118 .n_strx = 0,
119 .n_type = macho.N_FUN,
120 .n_sect = 0,
121 .n_desc = 0,
122 .n_value = size,
123 });
124 nlists.appendAssumeCapacity(.{
125 .n_strx = 0,
126 .n_type = macho.N_ENSYM,
127 .n_sect = sym.n_sect,
128 .n_desc = 0,
129 .n_value = size,
130 });
131 },
132 .global => {
133 try nlists.append(.{
134 .n_strx = sym.n_strx,
135 .n_type = macho.N_GSYM,
136 .n_sect = 0,
137 .n_desc = 0,
138 .n_value = 0,
139 });
140 },
141 .static => {
142 try nlists.append(.{
143 .n_strx = sym.n_strx,
144 .n_type = macho.N_STSYM,
145 .n_sect = sym.n_sect,
146 .n_desc = 0,
147 .n_value = sym.n_value,
148 });
149 },
150 }
151
152 return nlists.toOwnedSlice();
153 }
154};78};
15579
156pub const Relocation = struct {80pub const Relocation = struct {
157 pub const Target = union(enum) {
158 local: u32,
159 global: u32,
160 };
161
162 /// Offset within the atom's code buffer.81 /// Offset within the atom's code buffer.
163 /// Note relocation size can be inferred by relocation's kind.82 /// Note relocation size can be inferred by relocation's kind.
164 offset: u32,83 offset: u32,
16584
166 target: Target,85 target: MachO.SymbolWithLoc,
16786
168 addend: i64,87 addend: i64,
16988
170 subtractor: ?u32,89 subtractor: ?MachO.SymbolWithLoc,
17190
172 pcrel: bool,91 pcrel: bool,
17392
174 length: u2,93 length: u2,
17594
176 @"type": u4,95 @"type": u4,
96
97 pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
98 const is_via_got = got: {
99 switch (macho_file.base.options.target.cpu.arch) {
100 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
101 .ARM64_RELOC_GOT_LOAD_PAGE21,
102 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
103 .ARM64_RELOC_POINTER_TO_GOT,
104 => true,
105 else => false,
106 },
107 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
108 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
109 else => false,
110 },
111 else => unreachable,
112 }
113 };
114
115 if (is_via_got) {
116 return macho_file.getGotAtomForSymbol(self.target).?; // panic means fatal error
117 }
118 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
119 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
120 return macho_file.getAtomForSymbol(self.target);
121 }
177};122};
178123
179pub const empty = Atom{124pub const empty = Atom{
180 .local_sym_index = 0,125 .sym_index = 0,
126 .file = null,
181 .size = 0,127 .size = 0,
182 .alignment = 0,128 .alignment = 0,
183 .prev = null,129 .prev = null,
...@@ -186,34 +132,66 @@ pub const empty = Atom{...@@ -186,34 +132,66 @@ pub const empty = Atom{
186};132};
187133
188pub fn deinit(self: *Atom, allocator: Allocator) void {134pub fn deinit(self: *Atom, allocator: Allocator) void {
189 self.dices.deinit(allocator);
190 self.lazy_bindings.deinit(allocator);135 self.lazy_bindings.deinit(allocator);
191 self.bindings.deinit(allocator);136 self.bindings.deinit(allocator);
192 self.rebases.deinit(allocator);137 self.rebases.deinit(allocator);
193 self.relocs.deinit(allocator);138 self.relocs.deinit(allocator);
194 self.contained.deinit(allocator);139 self.contained.deinit(allocator);
195 self.aliases.deinit(allocator);
196 self.code.deinit(allocator);140 self.code.deinit(allocator);
197}141}
198142
199pub fn clearRetainingCapacity(self: *Atom) void {143pub fn clearRetainingCapacity(self: *Atom) void {
200 self.dices.clearRetainingCapacity();
201 self.lazy_bindings.clearRetainingCapacity();144 self.lazy_bindings.clearRetainingCapacity();
202 self.bindings.clearRetainingCapacity();145 self.bindings.clearRetainingCapacity();
203 self.rebases.clearRetainingCapacity();146 self.rebases.clearRetainingCapacity();
204 self.relocs.clearRetainingCapacity();147 self.relocs.clearRetainingCapacity();
205 self.contained.clearRetainingCapacity();148 self.contained.clearRetainingCapacity();
206 self.aliases.clearRetainingCapacity();
207 self.code.clearRetainingCapacity();149 self.code.clearRetainingCapacity();
208}150}
209151
152/// Returns symbol referencing this atom.
153pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
154 return self.getSymbolPtr(macho_file).*;
155}
156
157/// Returns pointer-to-symbol referencing this atom.
158pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
159 return macho_file.getSymbolPtr(.{
160 .sym_index = self.sym_index,
161 .file = self.file,
162 });
163}
164
165pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
166 return .{ .sym_index = self.sym_index, .file = self.file };
167}
168
169/// Returns true if the symbol pointed at with `sym_loc` is contained within this atom.
170/// WARNING this function assumes all atoms have been allocated in the virtual memory.
171/// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will
172/// give bogus results.
173pub fn isSymbolContained(self: Atom, sym_loc: SymbolWithLoc, macho_file: *MachO) bool {
174 const sym = macho_file.getSymbol(sym_loc);
175 if (!sym.sect()) return false;
176 const self_sym = self.getSymbol(macho_file);
177 return sym.n_value >= self_sym.n_value and sym.n_value < self_sym.n_value + self.size;
178}
179
180/// Returns the name of this atom.
181pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
182 return macho_file.getSymbolName(.{
183 .sym_index = self.sym_index,
184 .file = self.file,
185 });
186}
187
210/// Returns how much room there is to grow in virtual address space.188/// Returns how much room there is to grow in virtual address space.
211/// File offset relocation happens transparently, so it is not included in189/// File offset relocation happens transparently, so it is not included in
212/// this calculation.190/// this calculation.
213pub fn capacity(self: Atom, macho_file: MachO) u64 {191pub fn capacity(self: Atom, macho_file: *MachO) u64 {
214 const self_sym = macho_file.locals.items[self.local_sym_index];192 const self_sym = self.getSymbol(macho_file);
215 if (self.next) |next| {193 if (self.next) |next| {
216 const next_sym = macho_file.locals.items[next.local_sym_index];194 const next_sym = next.getSymbol(macho_file);
217 return next_sym.n_value - self_sym.n_value;195 return next_sym.n_value - self_sym.n_value;
218 } else {196 } else {
219 // We are the last atom.197 // We are the last atom.
...@@ -222,11 +200,11 @@ pub fn capacity(self: Atom, macho_file: MachO) u64 {...@@ -222,11 +200,11 @@ pub fn capacity(self: Atom, macho_file: MachO) u64 {
222 }200 }
223}201}
224202
225pub fn freeListEligible(self: Atom, macho_file: MachO) bool {203pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
226 // No need to keep a free list node for the last atom.204 // No need to keep a free list node for the last atom.
227 const next = self.next orelse return false;205 const next = self.next orelse return false;
228 const self_sym = macho_file.locals.items[self.local_sym_index];206 const self_sym = self.getSymbol(macho_file);
229 const next_sym = macho_file.locals.items[next.local_sym_index];207 const next_sym = next.getSymbol(macho_file);
230 const cap = next_sym.n_value - self_sym.n_value;208 const cap = next_sym.n_value - self_sym.n_value;
231 const ideal_cap = MachO.padToIdeal(self.size);209 const ideal_cap = MachO.padToIdeal(self.size);
232 if (cap <= ideal_cap) return false;210 if (cap <= ideal_cap) return false;
...@@ -235,19 +213,20 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {...@@ -235,19 +213,20 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
235}213}
236214
237const RelocContext = struct {215const RelocContext = struct {
238 base_addr: u64 = 0,
239 allocator: Allocator,
240 object: *Object,
241 macho_file: *MachO,216 macho_file: *MachO,
217 base_addr: u64 = 0,
218 base_offset: i32 = 0,
242};219};
243220
244pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocContext) !void {221pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: RelocContext) !void {
245 const tracy = trace(@src());222 const tracy = trace(@src());
246 defer tracy.end();223 defer tracy.end();
247224
225 const gpa = context.macho_file.base.allocator;
226
248 const arch = context.macho_file.base.options.target.cpu.arch;227 const arch = context.macho_file.base.options.target.cpu.arch;
249 var addend: i64 = 0;228 var addend: i64 = 0;
250 var subtractor: ?u32 = null;229 var subtractor: ?SymbolWithLoc = null;
251230
252 for (relocs) |rel, i| {231 for (relocs) |rel, i| {
253 blk: {232 blk: {
...@@ -284,20 +263,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -284,20 +263,16 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
284 }263 }
285264
286 assert(subtractor == null);265 assert(subtractor == null);
287 const sym = context.object.symtab.items[rel.r_symbolnum];266 const sym_loc = MachO.SymbolWithLoc{
267 .sym_index = rel.r_symbolnum,
268 .file = self.file,
269 };
270 const sym = context.macho_file.getSymbol(sym_loc);
288 if (sym.sect() and !sym.ext()) {271 if (sym.sect() and !sym.ext()) {
289 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;272 subtractor = sym_loc;
290 } else {273 } else {
291 const sym_name = context.object.getString(sym.n_strx);274 const sym_name = context.macho_file.getSymbolName(sym_loc);
292 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(275 subtractor = context.macho_file.globals.get(sym_name).?;
293 @as([]const u8, sym_name),
294 StringIndexAdapter{
295 .bytes = &context.macho_file.strtab,
296 },
297 ).?;
298 const resolv = context.macho_file.symbol_resolver.get(n_strx).?;
299 assert(resolv.where == .global);
300 subtractor = resolv.local_sym_index;
301 }276 }
302 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.277 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
303 if (relocs.len <= i + 1) {278 if (relocs.len <= i + 1) {
...@@ -328,45 +303,42 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -328,45 +303,42 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
328 continue;303 continue;
329 }304 }
330305
306 const object = &context.macho_file.objects.items[self.file.?];
331 const target = target: {307 const target = target: {
332 if (rel.r_extern == 0) {308 if (rel.r_extern == 0) {
333 const sect_id = @intCast(u16, rel.r_symbolnum - 1);309 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
334 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {310 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
335 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;311 const sect = object.getSourceSection(sect_id);
336 const sect = seg.sections.items[sect_id];
337 const match = (try context.macho_file.getMatchingSection(sect)) orelse312 const match = (try context.macho_file.getMatchingSection(sect)) orelse
338 unreachable;313 unreachable;
339 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);314 const sym_index = @intCast(u32, object.symtab.items.len);
340 try context.macho_file.locals.append(context.allocator, .{315 try object.symtab.append(gpa, .{
341 .n_strx = 0,316 .n_strx = 0,
342 .n_type = macho.N_SECT,317 .n_type = macho.N_SECT,
343 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),318 .n_sect = context.macho_file.getSectionOrdinal(match),
344 .n_desc = 0,319 .n_desc = 0,
345 .n_value = 0,320 .n_value = sect.addr,
346 });321 });
347 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);322 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
348 break :blk local_sym_index;323 break :blk sym_index;
349 };324 };
350 break :target Relocation.Target{ .local = local_sym_index };325 break :target MachO.SymbolWithLoc{ .sym_index = sym_index, .file = self.file };
351 }326 }
352327
353 const sym = context.object.symtab.items[rel.r_symbolnum];328 const sym_loc = MachO.SymbolWithLoc{
354 const sym_name = context.object.getString(sym.n_strx);329 .sym_index = rel.r_symbolnum,
330 .file = self.file,
331 };
332 const sym = context.macho_file.getSymbol(sym_loc);
355333
356 if (sym.sect() and !sym.ext()) {334 if (sym.sect() and !sym.ext()) {
357 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;335 break :target sym_loc;
358 break :target Relocation.Target{ .local = sym_index };336 } else {
337 const sym_name = context.macho_file.getSymbolName(sym_loc);
338 break :target context.macho_file.globals.get(sym_name).?;
359 }339 }
360
361 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
362 @as([]const u8, sym_name),
363 StringIndexAdapter{
364 .bytes = &context.macho_file.strtab,
365 },
366 ) orelse unreachable;
367 break :target Relocation.Target{ .global = n_strx };
368 };340 };
369 const offset = @intCast(u32, rel.r_address);341 const offset = @intCast(u32, rel.r_address - context.base_offset);
370342
371 switch (arch) {343 switch (arch) {
372 .aarch64 => {344 .aarch64 => {
...@@ -388,8 +360,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -388,8 +360,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
388 else360 else
389 mem.readIntLittle(i32, self.code.items[offset..][0..4]);361 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
390 if (rel.r_extern == 0) {362 if (rel.r_extern == 0) {
391 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;363 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
392 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
393 addend -= @intCast(i64, target_sect_base_addr);364 addend -= @intCast(i64, target_sect_base_addr);
394 }365 }
395 try self.addPtrBindingOrRebase(rel, target, context);366 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -397,9 +368,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -397,9 +368,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
397 .ARM64_RELOC_TLVP_LOAD_PAGE21,368 .ARM64_RELOC_TLVP_LOAD_PAGE21,
398 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,369 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
399 => {370 => {
400 if (target == .global) {371 try addTlvPtrEntry(target, context);
401 try addTlvPtrEntry(target, context);
402 }
403 },372 },
404 else => {},373 else => {},
405 }374 }
...@@ -423,8 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -423,8 +392,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
423 else392 else
424 mem.readIntLittle(i32, self.code.items[offset..][0..4]);393 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
425 if (rel.r_extern == 0) {394 if (rel.r_extern == 0) {
426 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;395 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
427 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
428 addend -= @intCast(i64, target_sect_base_addr);396 addend -= @intCast(i64, target_sect_base_addr);
429 }397 }
430 try self.addPtrBindingOrRebase(rel, target, context);398 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -445,16 +413,15 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -445,16 +413,15 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
445 if (rel.r_extern == 0) {413 if (rel.r_extern == 0) {
446 // Note for the future self: when r_extern == 0, we should subtract correction from the414 // Note for the future self: when r_extern == 0, we should subtract correction from the
447 // addend.415 // addend.
448 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;416 const target_sect_base_addr = object.getSourceSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
449 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;417 // We need to add base_offset, i.e., offset of this atom wrt to the source
418 // section. Otherwise, the addend will over-/under-shoot.
450 addend += @intCast(i64, context.base_addr + offset + 4) -419 addend += @intCast(i64, context.base_addr + offset + 4) -
451 @intCast(i64, target_sect_base_addr);420 @intCast(i64, target_sect_base_addr) + context.base_offset;
452 }421 }
453 },422 },
454 .X86_64_RELOC_TLV => {423 .X86_64_RELOC_TLV => {
455 if (target == .global) {424 try addTlvPtrEntry(target, context);
456 try addTlvPtrEntry(target, context);
457 }
458 },425 },
459 else => {},426 else => {},
460 }427 }
...@@ -462,7 +429,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -462,7 +429,7 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
462 else => unreachable,429 else => unreachable,
463 }430 }
464431
465 try self.relocs.append(context.allocator, .{432 try self.relocs.append(gpa, .{
466 .offset = offset,433 .offset = offset,
467 .target = target,434 .target = target,
468 .addend = addend,435 .addend = addend,
...@@ -480,286 +447,182 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC...@@ -480,286 +447,182 @@ pub fn parseRelocs(self: *Atom, relocs: []macho.relocation_info, context: RelocC
480fn addPtrBindingOrRebase(447fn addPtrBindingOrRebase(
481 self: *Atom,448 self: *Atom,
482 rel: macho.relocation_info,449 rel: macho.relocation_info,
483 target: Relocation.Target,450 target: MachO.SymbolWithLoc,
484 context: RelocContext,451 context: RelocContext,
485) !void {452) !void {
486 switch (target) {453 const gpa = context.macho_file.base.allocator;
487 .global => |n_strx| {454 const sym = context.macho_file.getSymbol(target);
488 try self.bindings.append(context.allocator, .{455 if (sym.undf()) {
489 .n_strx = n_strx,456 try self.bindings.append(gpa, .{
490 .offset = @intCast(u32, rel.r_address),457 .target = target,
491 });458 .offset = @intCast(u32, rel.r_address - context.base_offset),
492 },459 });
493 .local => {460 } else {
494 const source_sym = context.macho_file.locals.items[self.local_sym_index];461 const source_sym = self.getSymbol(context.macho_file);
495 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];462 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
496 const seg = context.macho_file.load_commands.items[match.seg].segment;463 const sect = context.macho_file.getSection(match);
497 const sect = seg.sections.items[match.sect];464 const sect_type = sect.type_();
498 const sect_type = sect.type_();465
499466 const should_rebase = rebase: {
500 const should_rebase = rebase: {467 if (rel.r_length != 3) break :rebase false;
501 if (rel.r_length != 3) break :rebase false;468
502469 // TODO actually, a check similar to what dyld is doing, that is, verifying
503 // TODO actually, a check similar to what dyld is doing, that is, verifying470 // that the segment is writable should be enough here.
504 // that the segment is writable should be enough here.471 const is_right_segment = blk: {
505 const is_right_segment = blk: {472 if (context.macho_file.data_segment_cmd_index) |idx| {
506 if (context.macho_file.data_segment_cmd_index) |idx| {473 if (match.seg == idx) {
507 if (match.seg == idx) {474 break :blk true;
508 break :blk true;
509 }
510 }475 }
511 if (context.macho_file.data_const_segment_cmd_index) |idx| {476 }
512 if (match.seg == idx) {477 if (context.macho_file.data_const_segment_cmd_index) |idx| {
513 break :blk true;478 if (match.seg == idx) {
514 }479 break :blk true;
515 }480 }
516 break :blk false;
517 };
518
519 if (!is_right_segment) break :rebase false;
520 if (sect_type != macho.S_LITERAL_POINTERS and
521 sect_type != macho.S_REGULAR and
522 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
523 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
524 {
525 break :rebase false;
526 }481 }
527482 break :blk false;
528 break :rebase true;
529 };483 };
530484
531 if (should_rebase) {485 if (!is_right_segment) break :rebase false;
532 try self.rebases.append(context.allocator, @intCast(u32, rel.r_address));486 if (sect_type != macho.S_LITERAL_POINTERS and
487 sect_type != macho.S_REGULAR and
488 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
489 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
490 {
491 break :rebase false;
533 }492 }
534 },493
494 break :rebase true;
495 };
496
497 if (should_rebase) {
498 try self.rebases.append(gpa, @intCast(u32, rel.r_address - context.base_offset));
499 }
535 }500 }
536}501}
537502
538fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void {503fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
504 const target_sym = context.macho_file.getSymbol(target);
505 if (!target_sym.undf()) return;
539 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;506 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;
540507
541 const index = try context.macho_file.allocateTlvPtrEntry(target);508 const index = try context.macho_file.allocateTlvPtrEntry(target);
542 const atom = try context.macho_file.createTlvPtrAtom(target);509 const atom = try context.macho_file.createTlvPtrAtom(target);
543 context.macho_file.tlv_ptr_entries.items[index].atom = atom;510 context.macho_file.tlv_ptr_entries.items[index].sym_index = atom.sym_index;
544
545 const match = (try context.macho_file.getMatchingSection(.{
546 .segname = MachO.makeStaticString("__DATA"),
547 .sectname = MachO.makeStaticString("__thread_ptrs"),
548 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
549 })).?;
550 if (!context.object.start_atoms.contains(match)) {
551 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
552 }
553 if (context.object.end_atoms.getPtr(match)) |last| {
554 last.*.next = atom;
555 atom.prev = last.*;
556 last.* = atom;
557 } else {
558 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
559 }
560}511}
561512
562fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {513fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
563 if (context.macho_file.got_entries_table.contains(target)) return;514 if (context.macho_file.got_entries_table.contains(target)) return;
564515
565 const index = try context.macho_file.allocateGotEntry(target);516 const index = try context.macho_file.allocateGotEntry(target);
566 const atom = try context.macho_file.createGotAtom(target);517 const atom = try context.macho_file.createGotAtom(target);
567 context.macho_file.got_entries.items[index].atom = atom;518 context.macho_file.got_entries.items[index].sym_index = atom.sym_index;
568
569 const match = MachO.MatchingSection{
570 .seg = context.macho_file.data_const_segment_cmd_index.?,
571 .sect = context.macho_file.got_section_index.?,
572 };
573 if (!context.object.start_atoms.contains(match)) {
574 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
575 }
576 if (context.object.end_atoms.getPtr(match)) |last| {
577 last.*.next = atom;
578 atom.prev = last.*;
579 last.* = atom;
580 } else {
581 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
582 }
583}519}
584520
585fn addStub(target: Relocation.Target, context: RelocContext) !void {521fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
586 if (target != .global) return;522 const target_sym = context.macho_file.getSymbol(target);
587 if (context.macho_file.stubs_table.contains(target.global)) return;523 if (!target_sym.undf()) return;
588 // If the symbol has been resolved as defined globally elsewhere (in a different translation unit),524 if (context.macho_file.stubs_table.contains(target)) return;
589 // then skip creating stub entry.525
590 // TODO Is this the correct for the incremental?526 const stub_index = try context.macho_file.allocateStubEntry(target);
591 if (context.macho_file.symbol_resolver.get(target.global).?.where == .global) return;527 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
592528 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
593 const stub_index = try context.macho_file.allocateStubEntry(target.global);529 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
594530
595 // TODO clean this up!531 context.macho_file.stubs.items[stub_index].sym_index = stub_atom.sym_index;
596 const stub_helper_atom = atom: {
597 const atom = try context.macho_file.createStubHelperAtom();
598 const match = MachO.MatchingSection{
599 .seg = context.macho_file.text_segment_cmd_index.?,
600 .sect = context.macho_file.stub_helper_section_index.?,
601 };
602 if (!context.object.start_atoms.contains(match)) {
603 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
604 }
605 if (context.object.end_atoms.getPtr(match)) |last| {
606 last.*.next = atom;
607 atom.prev = last.*;
608 last.* = atom;
609 } else {
610 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
611 }
612 break :atom atom;
613 };
614 const laptr_atom = atom: {
615 const atom = try context.macho_file.createLazyPointerAtom(
616 stub_helper_atom.local_sym_index,
617 target.global,
618 );
619 const match = MachO.MatchingSection{
620 .seg = context.macho_file.data_segment_cmd_index.?,
621 .sect = context.macho_file.la_symbol_ptr_section_index.?,
622 };
623 if (!context.object.start_atoms.contains(match)) {
624 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
625 }
626 if (context.object.end_atoms.getPtr(match)) |last| {
627 last.*.next = atom;
628 atom.prev = last.*;
629 last.* = atom;
630 } else {
631 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
632 }
633 break :atom atom;
634 };
635 const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
636 const match = MachO.MatchingSection{
637 .seg = context.macho_file.text_segment_cmd_index.?,
638 .sect = context.macho_file.stubs_section_index.?,
639 };
640 if (!context.object.start_atoms.contains(match)) {
641 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
642 }
643 if (context.object.end_atoms.getPtr(match)) |last| {
644 last.*.next = atom;
645 atom.prev = last.*;
646 last.* = atom;
647 } else {
648 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
649 }
650 context.macho_file.stubs.items[stub_index] = atom;
651}532}
652533
653pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {534pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
654 const tracy = trace(@src());535 const tracy = trace(@src());
655 defer tracy.end();536 defer tracy.end();
656537
538 log.debug("ATOM(%{d}, '{s}')", .{ self.sym_index, self.getName(macho_file) });
539
657 for (self.relocs.items) |rel| {540 for (self.relocs.items) |rel| {
658 log.debug("relocating {}", .{rel});
659 const arch = macho_file.base.options.target.cpu.arch;541 const arch = macho_file.base.options.target.cpu.arch;
542 switch (arch) {
543 .aarch64 => {
544 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
545 @tagName(@intToEnum(macho.reloc_type_arm64, rel.@"type")),
546 rel.offset,
547 rel.target.sym_index,
548 rel.target.file,
549 });
550 },
551 .x86_64 => {
552 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
553 @tagName(@intToEnum(macho.reloc_type_x86_64, rel.@"type")),
554 rel.offset,
555 rel.target.sym_index,
556 rel.target.file,
557 });
558 },
559 else => unreachable,
560 }
561
660 const source_addr = blk: {562 const source_addr = blk: {
661 const sym = macho_file.locals.items[self.local_sym_index];563 const source_sym = self.getSymbol(macho_file);
662 break :blk sym.n_value + rel.offset;564 break :blk source_sym.n_value + rel.offset;
565 };
566 const is_tlv = is_tlv: {
567 const source_sym = self.getSymbol(macho_file);
568 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
569 const sect = macho_file.getSection(match);
570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
663 };571 };
664 var is_via_thread_ptrs: bool = false;
665 const target_addr = blk: {572 const target_addr = blk: {
666 const is_via_got = got: {573 const target_atom = rel.getTargetAtom(macho_file) orelse {
667 switch (arch) {574 // If there is no atom for target, we still need to check for special, atom-less
668 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {575 // symbols such as `___dso_handle`.
669 .ARM64_RELOC_GOT_LOAD_PAGE21,576 const target_name = macho_file.getSymbolName(rel.target);
670 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,577 assert(macho_file.globals.contains(target_name));
671 .ARM64_RELOC_POINTER_TO_GOT,578 const atomless_sym = macho_file.getSymbol(rel.target);
672 => true,579 log.debug(" | atomless target '{s}'", .{target_name});
673 else => false,580 break :blk atomless_sym.n_value;
674 },
675 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
676 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
677 else => false,
678 },
679 else => unreachable,
680 }
681 };581 };
682582 log.debug(" | target ATOM(%{d}, '{s}') in object({d})", .{
683 if (is_via_got) {583 target_atom.sym_index,
684 const got_index = macho_file.got_entries_table.get(rel.target) orelse {584 target_atom.getName(macho_file),
685 log.err("expected GOT entry for symbol", .{});585 target_atom.file,
686 switch (rel.target) {586 });
687 .local => |sym_index| log.err(" local @{d}", .{sym_index}),587 // If `rel.target` is contained within the target atom, pull its address value.
688 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),588 const target_sym = if (target_atom.isSymbolContained(rel.target, macho_file))
589 macho_file.getSymbol(rel.target)
590 else
591 target_atom.getSymbol(macho_file);
592 assert(target_sym.n_desc != MachO.N_DESC_GCED);
593 const base_address: u64 = if (is_tlv) base_address: {
594 // For TLV relocations, the value specified as a relocation is the displacement from the
595 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
596 // defined TLV template init section in the following order:
597 // * wrt to __thread_data if defined, then
598 // * wrt to __thread_bss
599 const sect_id: u16 = sect_id: {
600 if (macho_file.tlv_data_section_index) |i| {
601 break :sect_id i;
602 } else if (macho_file.tlv_bss_section_index) |i| {
603 break :sect_id i;
604 } else {
605 log.err("threadlocal variables present but no initializer sections found", .{});
606 log.err(" __thread_data not found", .{});
607 log.err(" __thread_bss not found", .{});
608 return error.FailedToResolveRelocationTarget;
689 }609 }
690 log.err(" this is an internal linker error", .{});
691 return error.FailedToResolveRelocationTarget;
692 };610 };
693 const atom = macho_file.got_entries.items[got_index].atom;611 break :base_address macho_file.getSection(.{
694 break :blk macho_file.locals.items[atom.local_sym_index].n_value;612 .seg = macho_file.data_segment_cmd_index.?,
695 }613 .sect = sect_id,
696614 }).addr;
697 switch (rel.target) {615 } else 0;
698 .local => |sym_index| {616 break :blk target_sym.n_value - base_address;
699 const sym = macho_file.locals.items[sym_index];
700 const is_tlv = is_tlv: {
701 const source_sym = macho_file.locals.items[self.local_sym_index];
702 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
703 const seg = macho_file.load_commands.items[match.seg].segment;
704 const sect = seg.sections.items[match.sect];
705 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
706 };
707 if (is_tlv) {
708 // For TLV relocations, the value specified as a relocation is the displacement from the
709 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
710 // defined TLV template init section in the following order:
711 // * wrt to __thread_data if defined, then
712 // * wrt to __thread_bss
713 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].segment;
714 const base_address = inner: {
715 if (macho_file.tlv_data_section_index) |i| {
716 break :inner seg.sections.items[i].addr;
717 } else if (macho_file.tlv_bss_section_index) |i| {
718 break :inner seg.sections.items[i].addr;
719 } else {
720 log.err("threadlocal variables present but no initializer sections found", .{});
721 log.err(" __thread_data not found", .{});
722 log.err(" __thread_bss not found", .{});
723 return error.FailedToResolveRelocationTarget;
724 }
725 };
726 break :blk sym.n_value - base_address;
727 }
728 break :blk sym.n_value;
729 },
730 .global => |n_strx| {
731 // TODO Still trying to figure out how to possibly use stubs for local symbol indirection with
732 // branching instructions. If it is not possible, then the best course of action is to
733 // resurrect the former approach of defering creating synthethic atoms in __got and __la_symbol_ptr
734 // sections until we resolve the relocations.
735 const resolv = macho_file.symbol_resolver.get(n_strx).?;
736 switch (resolv.where) {
737 .global => break :blk macho_file.globals.items[resolv.where_index].n_value,
738 .undef => {
739 if (macho_file.stubs_table.get(n_strx)) |stub_index| {
740 const atom = macho_file.stubs.items[stub_index];
741 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
742 } else {
743 if (macho_file.tlv_ptr_entries_table.get(rel.target)) |tlv_ptr_index| {
744 is_via_thread_ptrs = true;
745 const atom = macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
746 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
747 }
748 break :blk 0;
749 }
750 },
751 }
752 },
753 }
754 };617 };
755618
756 log.debug(" | source_addr = 0x{x}", .{source_addr});619 log.debug(" | source_addr = 0x{x}", .{source_addr});
757 log.debug(" | target_addr = 0x{x}", .{target_addr});
758620
759 switch (arch) {621 switch (arch) {
760 .aarch64 => {622 .aarch64 => {
761 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {623 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
762 .ARM64_RELOC_BRANCH26 => {624 .ARM64_RELOC_BRANCH26 => {
625 log.debug(" | target_addr = 0x{x}", .{target_addr});
763 const displacement = math.cast(626 const displacement = math.cast(
764 i28,627 i28,
765 @intCast(i64, target_addr) - @intCast(i64, source_addr),628 @intCast(i64, target_addr) - @intCast(i64, source_addr),
...@@ -788,6 +651,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -788,6 +651,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
788 .ARM64_RELOC_TLVP_LOAD_PAGE21,651 .ARM64_RELOC_TLVP_LOAD_PAGE21,
789 => {652 => {
790 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;653 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
654 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
791 const source_page = @intCast(i32, source_addr >> 12);655 const source_page = @intCast(i32, source_addr >> 12);
792 const target_page = @intCast(i32, actual_target_addr >> 12);656 const target_page = @intCast(i32, actual_target_addr >> 12);
793 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));657 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
...@@ -805,6 +669,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -805,6 +669,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
805 .ARM64_RELOC_PAGEOFF12 => {669 .ARM64_RELOC_PAGEOFF12 => {
806 const code = self.code.items[rel.offset..][0..4];670 const code = self.code.items[rel.offset..][0..4];
807 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;671 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
672 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
808 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));673 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
809 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {674 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {
810 var inst = aarch64.Instruction{675 var inst = aarch64.Instruction{
...@@ -842,6 +707,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -842,6 +707,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
842 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {707 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
843 const code = self.code.items[rel.offset..][0..4];708 const code = self.code.items[rel.offset..][0..4];
844 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;709 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
710 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
845 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));711 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
846 var inst: aarch64.Instruction = .{712 var inst: aarch64.Instruction = .{
847 .load_store_register = mem.bytesToValue(meta.TagPayload(713 .load_store_register = mem.bytesToValue(meta.TagPayload(
...@@ -856,6 +722,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -856,6 +722,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
856 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {722 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
857 const code = self.code.items[rel.offset..][0..4];723 const code = self.code.items[rel.offset..][0..4];
858 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;724 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
725 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
859726
860 const RegInfo = struct {727 const RegInfo = struct {
861 rd: u5,728 rd: u5,
...@@ -886,7 +753,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -886,7 +753,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
886 }753 }
887 };754 };
888 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));755 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
889 var inst = if (is_via_thread_ptrs) blk: {756 var inst = if (macho_file.tlv_ptr_entries_table.contains(rel.target)) blk: {
890 const offset = try math.divExact(u12, narrowed, 8);757 const offset = try math.divExact(u12, narrowed, 8);
891 break :blk aarch64.Instruction{758 break :blk aarch64.Instruction{
892 .load_store_register = .{759 .load_store_register = .{
...@@ -913,18 +780,20 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -913,18 +780,20 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
913 mem.writeIntLittle(u32, code, inst.toU32());780 mem.writeIntLittle(u32, code, inst.toU32());
914 },781 },
915 .ARM64_RELOC_POINTER_TO_GOT => {782 .ARM64_RELOC_POINTER_TO_GOT => {
783 log.debug(" | target_addr = 0x{x}", .{target_addr});
916 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow;784 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow;
917 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));785 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));
918 },786 },
919 .ARM64_RELOC_UNSIGNED => {787 .ARM64_RELOC_UNSIGNED => {
920 const result = blk: {788 const result = blk: {
921 if (rel.subtractor) |subtractor| {789 if (rel.subtractor) |subtractor| {
922 const sym = macho_file.locals.items[subtractor];790 const sym = macho_file.getSymbol(subtractor);
923 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;791 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
924 } else {792 } else {
925 break :blk @intCast(i64, target_addr) + rel.addend;793 break :blk @intCast(i64, target_addr) + rel.addend;
926 }794 }
927 };795 };
796 log.debug(" | target_addr = 0x{x}", .{result});
928797
929 if (rel.length == 3) {798 if (rel.length == 3) {
930 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));799 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
...@@ -943,6 +812,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -943,6 +812,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
943 .x86_64 => {812 .x86_64 => {
944 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {813 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
945 .X86_64_RELOC_BRANCH => {814 .X86_64_RELOC_BRANCH => {
815 log.debug(" | target_addr = 0x{x}", .{target_addr});
946 const displacement = math.cast(816 const displacement = math.cast(
947 i32,817 i32,
948 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,818 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
...@@ -950,6 +820,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -950,6 +820,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
950 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));820 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
951 },821 },
952 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {822 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
823 log.debug(" | target_addr = 0x{x}", .{target_addr});
953 const displacement = math.cast(824 const displacement = math.cast(
954 i32,825 i32,
955 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,826 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
...@@ -957,7 +828,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -957,7 +828,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
957 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));828 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
958 },829 },
959 .X86_64_RELOC_TLV => {830 .X86_64_RELOC_TLV => {
960 if (!is_via_thread_ptrs) {831 log.debug(" | target_addr = 0x{x}", .{target_addr});
832 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {
961 // We need to rewrite the opcode from movq to leaq.833 // We need to rewrite the opcode from movq to leaq.
962 self.code.items[rel.offset - 2] = 0x8d;834 self.code.items[rel.offset - 2] = 0x8d;
963 }835 }
...@@ -980,6 +852,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -980,6 +852,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
980 else => unreachable,852 else => unreachable,
981 };853 };
982 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;854 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
855 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
983 const displacement = math.cast(856 const displacement = math.cast(
984 i32,857 i32,
985 actual_target_addr - @intCast(i64, source_addr + correction + 4),858 actual_target_addr - @intCast(i64, source_addr + correction + 4),
...@@ -989,12 +862,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -989,12 +862,13 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
989 .X86_64_RELOC_UNSIGNED => {862 .X86_64_RELOC_UNSIGNED => {
990 const result = blk: {863 const result = blk: {
991 if (rel.subtractor) |subtractor| {864 if (rel.subtractor) |subtractor| {
992 const sym = macho_file.locals.items[subtractor];865 const sym = macho_file.getSymbol(subtractor);
993 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;866 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
994 } else {867 } else {
995 break :blk @intCast(i64, target_addr) + rel.addend;868 break :blk @intCast(i64, target_addr) + rel.addend;
996 }869 }
997 };870 };
871 log.debug(" | target_addr = 0x{x}", .{result});
998872
999 if (rel.length == 3) {873 if (rel.length == 3) {
1000 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));874 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
src/link/MachO/DebugSymbols.zig+51-14
...@@ -5,7 +5,7 @@ const build_options = @import("build_options");...@@ -5,7 +5,7 @@ const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const fs = std.fs;6const fs = std.fs;
7const link = @import("../../link.zig");7const link = @import("../../link.zig");
8const log = std.log.scoped(.link);8const log = std.log.scoped(.dsym);
9const macho = std.macho;9const macho = std.macho;
10const makeStaticString = MachO.makeStaticString;10const makeStaticString = MachO.makeStaticString;
11const math = std.math;11const math = std.math;
...@@ -17,6 +17,7 @@ const Allocator = mem.Allocator;...@@ -17,6 +17,7 @@ const Allocator = mem.Allocator;
17const Dwarf = @import("../Dwarf.zig");17const Dwarf = @import("../Dwarf.zig");
18const MachO = @import("../MachO.zig");18const MachO = @import("../MachO.zig");
19const Module = @import("../../Module.zig");19const Module = @import("../../Module.zig");
20const StringTable = @import("../strtab.zig").StringTable;
20const TextBlock = MachO.TextBlock;21const TextBlock = MachO.TextBlock;
21const Type = @import("../../type.zig").Type;22const Type = @import("../../type.zig").Type;
2223
...@@ -59,6 +60,8 @@ debug_aranges_section_dirty: bool = false,...@@ -59,6 +60,8 @@ debug_aranges_section_dirty: bool = false,
59debug_info_header_dirty: bool = false,60debug_info_header_dirty: bool = false,
60debug_line_header_dirty: bool = false,61debug_line_header_dirty: bool = false,
6162
63strtab: StringTable(.strtab) = .{},
64
62relocs: std.ArrayListUnmanaged(Reloc) = .{},65relocs: std.ArrayListUnmanaged(Reloc) = .{},
6366
64pub const Reloc = struct {67pub const Reloc = struct {
...@@ -93,6 +96,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void...@@ -93,6 +96,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
93 .strsize = 0,96 .strsize = 0,
94 },97 },
95 });98 });
99 try self.strtab.buffer.append(allocator, 0);
96 self.load_commands_dirty = true;100 self.load_commands_dirty = true;
97 }101 }
98102
...@@ -269,22 +273,36 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -269,22 +273,36 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
269273
270 for (self.relocs.items) |*reloc| {274 for (self.relocs.items) |*reloc| {
271 const sym = switch (reloc.@"type") {275 const sym = switch (reloc.@"type") {
272 .direct_load => self.base.locals.items[reloc.target],276 .direct_load => self.base.getSymbol(.{ .sym_index = reloc.target, .file = null }),
273 .got_load => blk: {277 .got_load => blk: {
274 const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?;278 const got_index = self.base.got_entries_table.get(.{
279 .sym_index = reloc.target,
280 .file = null,
281 }).?;
275 const got_entry = self.base.got_entries.items[got_index];282 const got_entry = self.base.got_entries.items[got_index];
276 break :blk self.base.locals.items[got_entry.atom.local_sym_index];283 break :blk got_entry.getSymbol(self.base);
277 },284 },
278 };285 };
279 if (sym.n_value == reloc.prev_vaddr) continue;286 if (sym.n_value == reloc.prev_vaddr) continue;
280287
288 const sym_name = switch (reloc.@"type") {
289 .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
290 .got_load => blk: {
291 const got_index = self.base.got_entries_table.get(.{
292 .sym_index = reloc.target,
293 .file = null,
294 }).?;
295 const got_entry = self.base.got_entries.items[got_index];
296 break :blk got_entry.getName(self.base);
297 },
298 };
281 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
282 const sect = &seg.sections.items[self.debug_info_section_index.?];300 const sect = &seg.sections.items[self.debug_info_section_index.?];
283 const file_offset = sect.offset + reloc.offset;301 const file_offset = sect.offset + reloc.offset;
284 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{302 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
285 reloc.target,303 reloc.target,
286 sym.n_value,304 sym.n_value,
287 self.base.getString(sym.n_strx),305 sym_name,
288 file_offset,306 file_offset,
289 });307 });
290 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);308 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);
...@@ -367,6 +385,7 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {...@@ -367,6 +385,7 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
367 }385 }
368 self.load_commands.deinit(allocator);386 self.load_commands.deinit(allocator);
369 self.dwarf.deinit();387 self.dwarf.deinit();
388 self.strtab.deinit(allocator);
370 self.relocs.deinit(allocator);389 self.relocs.deinit(allocator);
371}390}
372391
...@@ -582,21 +601,39 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -582,21 +601,39 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
582 const tracy = trace(@src());601 const tracy = trace(@src());
583 defer tracy.end();602 defer tracy.end();
584603
604 const gpa = self.base.base.allocator;
585 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;605 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
586 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;606 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
587 symtab.symoff = @intCast(u32, seg.inner.fileoff);607 symtab.symoff = @intCast(u32, seg.inner.fileoff);
588608
589 var locals = std.ArrayList(macho.nlist_64).init(self.base.base.allocator);609 var locals = std.ArrayList(macho.nlist_64).init(gpa);
590 defer locals.deinit();610 defer locals.deinit();
591611
592 for (self.base.locals.items) |sym| {612 for (self.base.locals.items) |sym, sym_id| {
593 if (sym.n_strx == 0) continue;613 if (sym.n_strx == 0) continue; // no name, skip
594 if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue;614 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
595 try locals.append(sym);615 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
616 if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
617 if (self.base.globals.contains(self.base.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
618 var out_sym = sym;
619 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(sym_loc));
620 try locals.append(out_sym);
621 }
622
623 var exports = std.ArrayList(macho.nlist_64).init(gpa);
624 defer exports.deinit();
625
626 for (self.base.globals.values()) |global| {
627 const sym = self.base.getSymbol(global);
628 if (sym.undf()) continue; // import, skip
629 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
630 var out_sym = sym;
631 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(global));
632 try exports.append(out_sym);
596 }633 }
597634
598 const nlocals = locals.items.len;635 const nlocals = locals.items.len;
599 const nexports = self.base.globals.items.len;636 const nexports = exports.items.len;
600 const locals_off = symtab.symoff;637 const locals_off = symtab.symoff;
601 const locals_size = nlocals * @sizeOf(macho.nlist_64);638 const locals_size = nlocals * @sizeOf(macho.nlist_64);
602 const exports_off = locals_off + locals_size;639 const exports_off = locals_off + locals_size;
...@@ -641,7 +678,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -641,7 +678,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
641 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);678 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
642679
643 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });680 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
644 try self.file.pwriteAll(mem.sliceAsBytes(self.base.globals.items), exports_off);681 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
645682
646 self.load_commands_dirty = true;683 self.load_commands_dirty = true;
647}684}
...@@ -655,7 +692,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -655,7 +692,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
655 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));692 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
656 symtab.stroff = symtab.symoff + symtab_size;693 symtab.stroff = symtab.symoff + symtab_size;
657694
658 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));695 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
659 symtab.strsize = @intCast(u32, needed_size);696 symtab.strsize = @intCast(u32, needed_size);
660697
661 if (symtab_size + needed_size > seg.inner.filesize) {698 if (symtab_size + needed_size > seg.inner.filesize) {
...@@ -692,7 +729,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -692,7 +729,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
692729
693 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });730 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
694731
695 try self.file.pwriteAll(self.base.strtab.items, symtab.stroff);732 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
696733
697 self.load_commands_dirty = true;734 self.load_commands_dirty = true;
698}735}
src/link/MachO/Object.zig+429-422
...@@ -3,7 +3,6 @@ const Object = @This();...@@ -3,7 +3,6 @@ const Object = @This();
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");4const build_options = @import("build_options");
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const dwarf = std.dwarf;
7const fs = std.fs;6const fs = std.fs;
8const io = std.io;7const io = std.io;
9const log = std.log.scoped(.link);8const log = std.log.scoped(.link);
...@@ -16,13 +15,21 @@ const trace = @import("../../tracy.zig").trace;...@@ -16,13 +15,21 @@ const trace = @import("../../tracy.zig").trace;
16const Allocator = mem.Allocator;15const Allocator = mem.Allocator;
17const Atom = @import("Atom.zig");16const Atom = @import("Atom.zig");
18const MachO = @import("../MachO.zig");17const MachO = @import("../MachO.zig");
18const MatchingSection = MachO.MatchingSection;
19const SymbolWithLoc = MachO.SymbolWithLoc;
1920
20file: fs.File,21file: fs.File,
21name: []const u8,22name: []const u8,
23mtime: u64,
24
25/// Data contents of the file. Includes sections, and data of load commands.
26/// Excludes the backing memory for the header and load commands.
27/// Initialized in `parse`.
28contents: []const u8 = undefined,
2229
23file_offset: ?u32 = null,30file_offset: ?u32 = null,
2431
25header: ?macho.mach_header_64 = null,32header: macho.mach_header_64 = undefined,
2633
27load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},34load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
2835
...@@ -42,212 +49,58 @@ dwarf_debug_line_str_index: ?u16 = null,...@@ -42,212 +49,58 @@ dwarf_debug_line_str_index: ?u16 = null,
42dwarf_debug_ranges_index: ?u16 = null,49dwarf_debug_ranges_index: ?u16 = null,
4350
44symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},51symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
45strtab: std.ArrayListUnmanaged(u8) = .{},52strtab: []const u8 = &.{},
46data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},53data_in_code_entries: []const macho.data_in_code_entry = &.{},
47
48// Debug info
49debug_info: ?DebugInfo = null,
50tu_name: ?[]const u8 = null,
51tu_comp_dir: ?[]const u8 = null,
52mtime: ?u64 = null,
53
54contained_atoms: std.ArrayListUnmanaged(*Atom) = .{},
55start_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
56end_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
57sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
5854
59// TODO symbol mapping and its inverse can probably be simple arrays55sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
60// instead of hash maps.
61symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
62reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
63
64analyzed: bool = false,
65
66const DebugInfo = struct {
67 inner: dwarf.DwarfInfo,
68 debug_info: []u8,
69 debug_abbrev: []u8,
70 debug_str: []u8,
71 debug_line: []u8,
72 debug_line_str: []u8,
73 debug_ranges: []u8,
74
75 pub fn parseFromObject(allocator: Allocator, object: *const Object) !?DebugInfo {
76 var debug_info = blk: {
77 const index = object.dwarf_debug_info_index orelse return null;
78 break :blk try object.readSection(allocator, index);
79 };
80 var debug_abbrev = blk: {
81 const index = object.dwarf_debug_abbrev_index orelse return null;
82 break :blk try object.readSection(allocator, index);
83 };
84 var debug_str = blk: {
85 const index = object.dwarf_debug_str_index orelse return null;
86 break :blk try object.readSection(allocator, index);
87 };
88 var debug_line = blk: {
89 const index = object.dwarf_debug_line_index orelse return null;
90 break :blk try object.readSection(allocator, index);
91 };
92 var debug_line_str = blk: {
93 if (object.dwarf_debug_line_str_index) |ind| {
94 break :blk try object.readSection(allocator, ind);
95 }
96 break :blk try allocator.alloc(u8, 0);
97 };
98 var debug_ranges = blk: {
99 if (object.dwarf_debug_ranges_index) |ind| {
100 break :blk try object.readSection(allocator, ind);
101 }
102 break :blk try allocator.alloc(u8, 0);
103 };
10456
105 var inner: dwarf.DwarfInfo = .{57/// List of atoms that map to the symbols parsed from this object file.
106 .endian = .Little,58managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
107 .debug_info = debug_info,
108 .debug_abbrev = debug_abbrev,
109 .debug_str = debug_str,
110 .debug_line = debug_line,
111 .debug_line_str = debug_line_str,
112 .debug_ranges = debug_ranges,
113 };
114 try dwarf.openDwarfDebugInfo(&inner, allocator);
115
116 return DebugInfo{
117 .inner = inner,
118 .debug_info = debug_info,
119 .debug_abbrev = debug_abbrev,
120 .debug_str = debug_str,
121 .debug_line = debug_line,
122 .debug_line_str = debug_line_str,
123 .debug_ranges = debug_ranges,
124 };
125 }
12659
127 pub fn deinit(self: *DebugInfo, allocator: Allocator) void {60/// Table of atoms belonging to this object file indexed by the symbol index.
128 allocator.free(self.debug_info);61atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
129 allocator.free(self.debug_abbrev);
130 allocator.free(self.debug_str);
131 allocator.free(self.debug_line);
132 allocator.free(self.debug_line_str);
133 allocator.free(self.debug_ranges);
134 self.inner.deinit(allocator);
135 }
136};
13762
138pub fn deinit(self: *Object, allocator: Allocator) void {63pub fn deinit(self: *Object, gpa: Allocator) void {
139 for (self.load_commands.items) |*lc| {64 for (self.load_commands.items) |*lc| {
140 lc.deinit(allocator);65 lc.deinit(gpa);
141 }66 }
142 self.load_commands.deinit(allocator);67 self.load_commands.deinit(gpa);
143 self.data_in_code_entries.deinit(allocator);68 gpa.free(self.contents);
144 self.symtab.deinit(allocator);69 self.sections_as_symbols.deinit(gpa);
145 self.strtab.deinit(allocator);70 self.atom_by_index_table.deinit(gpa);
146 self.sections_as_symbols.deinit(allocator);71
147 self.symbol_mapping.deinit(allocator);72 for (self.managed_atoms.items) |atom| {
148 self.reverse_symbol_mapping.deinit(allocator);73 atom.deinit(gpa);
149 allocator.free(self.name);74 gpa.destroy(atom);
150
151 self.contained_atoms.deinit(allocator);
152 self.start_atoms.deinit(allocator);
153 self.end_atoms.deinit(allocator);
154
155 if (self.debug_info) |*db| {
156 db.deinit(allocator);
157 }75 }
76 self.managed_atoms.deinit(gpa);
15877
159 if (self.tu_name) |n| {78 gpa.free(self.name);
160 allocator.free(n);
161 }
162
163 if (self.tu_comp_dir) |n| {
164 allocator.free(n);
165 }
166}
167
168pub fn free(self: *Object, allocator: Allocator, macho_file: *MachO) void {
169 log.debug("freeObject {*}", .{self});
170
171 var it = self.end_atoms.iterator();
172 while (it.next()) |entry| {
173 const match = entry.key_ptr.*;
174 const first_atom = self.start_atoms.get(match).?;
175 const last_atom = entry.value_ptr.*;
176 var atom = first_atom;
177
178 while (true) {
179 if (atom.local_sym_index != 0) {
180 macho_file.locals_free_list.append(allocator, atom.local_sym_index) catch {};
181 const local = &macho_file.locals.items[atom.local_sym_index];
182 local.* = .{
183 .n_strx = 0,
184 .n_type = 0,
185 .n_sect = 0,
186 .n_desc = 0,
187 .n_value = 0,
188 };
189 atom.local_sym_index = 0;
190 }
191 if (atom == last_atom) {
192 break;
193 }
194 if (atom.next) |next| {
195 atom = next;
196 } else break;
197 }
198 }
199
200 self.freeAtoms(macho_file);
201}79}
20280
203fn freeAtoms(self: *Object, macho_file: *MachO) void {81pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {
204 var it = self.end_atoms.iterator();82 const file_stat = try self.file.stat();
205 while (it.next()) |entry| {83 const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
206 const match = entry.key_ptr.*;84 self.contents = try self.file.readToEndAlloc(allocator, file_size);
207 var first_atom: *Atom = self.start_atoms.get(match).?;
208 var last_atom: *Atom = entry.value_ptr.*;
209
210 if (macho_file.atoms.getPtr(match)) |atom_ptr| {
211 if (atom_ptr.* == last_atom) {
212 if (first_atom.prev) |prev| {
213 // TODO shrink the section size here
214 atom_ptr.* = prev;
215 } else {
216 _ = macho_file.atoms.fetchRemove(match);
217 }
218 }
219 }
220
221 if (first_atom.prev) |prev| {
222 prev.next = last_atom.next;
223 } else {
224 first_atom.prev = null;
225 }
22685
227 if (last_atom.next) |next| {86 var stream = std.io.fixedBufferStream(self.contents);
228 next.prev = last_atom.prev;87 const reader = stream.reader();
229 } else {
230 last_atom.next = null;
231 }
232 }
233}
23488
235pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {89 const file_offset = self.file_offset orelse 0;
236 const reader = self.file.reader();90 if (file_offset > 0) {
237 if (self.file_offset) |offset| {91 try reader.context.seekTo(file_offset);
238 try reader.context.seekTo(offset);
239 }92 }
24093
241 const header = try reader.readStruct(macho.mach_header_64);94 self.header = try reader.readStruct(macho.mach_header_64);
242 if (header.filetype != macho.MH_OBJECT) {95 if (self.header.filetype != macho.MH_OBJECT) {
243 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{96 log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
244 macho.MH_OBJECT,97 macho.MH_OBJECT,
245 header.filetype,98 self.header.filetype,
246 });99 });
247 return error.NotObject;100 return error.NotObject;
248 }101 }
249102
250 const this_arch: std.Target.Cpu.Arch = switch (header.cputype) {103 const this_arch: std.Target.Cpu.Arch = switch (self.header.cputype) {
251 macho.CPU_TYPE_ARM64 => .aarch64,104 macho.CPU_TYPE_ARM64 => .aarch64,
252 macho.CPU_TYPE_X86_64 => .x86_64,105 macho.CPU_TYPE_X86_64 => .x86_64,
253 else => |value| {106 else => |value| {
...@@ -260,22 +113,10 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {...@@ -260,22 +113,10 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {
260 return error.MismatchedCpuArchitecture;113 return error.MismatchedCpuArchitecture;
261 }114 }
262115
263 self.header = header;116 try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);
264
265 try self.readLoadCommands(allocator, reader);
266 try self.parseSymtab(allocator);
267 try self.parseDataInCode(allocator);
268 try self.parseDebugInfo(allocator);
269}
270
271pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !void {
272 const header = self.header orelse unreachable; // Unreachable here signifies a fatal unexplored condition.
273 const offset = self.file_offset orelse 0;
274
275 try self.load_commands.ensureUnusedCapacity(allocator, header.ncmds);
276117
277 var i: u16 = 0;118 var i: u16 = 0;
278 while (i < header.ncmds) : (i += 1) {119 while (i < self.header.ncmds) : (i += 1) {
279 var cmd = try macho.LoadCommand.read(allocator, reader);120 var cmd = try macho.LoadCommand.read(allocator, reader);
280 switch (cmd.cmd()) {121 switch (cmd.cmd()) {
281 .SEGMENT_64 => {122 .SEGMENT_64 => {
...@@ -305,18 +146,18 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -305,18 +146,18 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
305 }146 }
306 }147 }
307148
308 sect.offset += offset;149 sect.offset += file_offset;
309 if (sect.reloff > 0) {150 if (sect.reloff > 0) {
310 sect.reloff += offset;151 sect.reloff += file_offset;
311 }152 }
312 }153 }
313154
314 seg.inner.fileoff += offset;155 seg.inner.fileoff += file_offset;
315 },156 },
316 .SYMTAB => {157 .SYMTAB => {
317 self.symtab_cmd_index = i;158 self.symtab_cmd_index = i;
318 cmd.symtab.symoff += offset;159 cmd.symtab.symoff += file_offset;
319 cmd.symtab.stroff += offset;160 cmd.symtab.stroff += file_offset;
320 },161 },
321 .DYSYMTAB => {162 .DYSYMTAB => {
322 self.dysymtab_cmd_index = i;163 self.dysymtab_cmd_index = i;
...@@ -326,7 +167,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -326,7 +167,7 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
326 },167 },
327 .DATA_IN_CODE => {168 .DATA_IN_CODE => {
328 self.data_in_code_cmd_index = i;169 self.data_in_code_cmd_index = i;
329 cmd.linkedit_data.dataoff += offset;170 cmd.linkedit_data.dataoff += file_offset;
330 },171 },
331 else => {172 else => {
332 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});173 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
...@@ -334,21 +175,37 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v...@@ -334,21 +175,37 @@ pub fn readLoadCommands(self: *Object, allocator: Allocator, reader: anytype) !v
334 }175 }
335 self.load_commands.appendAssumeCapacity(cmd);176 self.load_commands.appendAssumeCapacity(cmd);
336 }177 }
178
179 try self.parseSymtab(allocator);
337}180}
338181
339const NlistWithIndex = struct {182const Context = struct {
340 nlist: macho.nlist_64,183 symtab: []const macho.nlist_64,
184 strtab: []const u8,
185};
186
187const SymbolAtIndex = struct {
341 index: u32,188 index: u32,
342189
343 fn lessThan(_: void, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {190 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
344 // We sort by type: defined < undefined, and191 return ctx.symtab[self.index];
345 // afterwards by address in each group. Normally, dysymtab should192 }
346 // be enough to guarantee the sort, but turns out not every compiler193
347 // is kind enough to specify the symbols in the correct order.194 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
348 if (lhs.nlist.sect()) {195 const sym = self.getSymbol(ctx);
349 if (rhs.nlist.sect()) {196 assert(sym.n_strx < ctx.strtab.len);
197 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
198 }
199
200 /// Returns whether lhs is less than rhs by allocated address in object file.
201 /// Undefined symbols are pushed to the back (always evaluate to true).
202 fn lessThan(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
203 const lhs = lhs_index.getSymbol(ctx);
204 const rhs = rhs_index.getSymbol(ctx);
205 if (lhs.sect()) {
206 if (rhs.sect()) {
350 // Same group, sort by address.207 // Same group, sort by address.
351 return lhs.nlist.n_value < rhs.nlist.n_value;208 return lhs.n_value < rhs.n_value;
352 } else {209 } else {
353 return true;210 return true;
354 }211 }
...@@ -357,60 +214,108 @@ const NlistWithIndex = struct {...@@ -357,60 +214,108 @@ const NlistWithIndex = struct {
357 }214 }
358 }215 }
359216
360 fn filterInSection(symbols: []NlistWithIndex, sect: macho.section_64) []NlistWithIndex {217 /// Returns whether lhs is less senior than rhs. The rules are:
361 const Predicate = struct {218 /// 1. ext
362 addr: u64,219 /// 2. weak
363220 /// 3. local
364 pub fn predicate(self: @This(), symbol: NlistWithIndex) bool {221 /// 4. temp (local starting with `l` prefix).
365 return symbol.nlist.n_value >= self.addr;222 fn lessThanBySeniority(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
366 }223 const lhs = lhs_index.getSymbol(ctx);
367 };224 const rhs = rhs_index.getSymbol(ctx);
368225 if (!rhs.ext()) {
369 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{ .addr = sect.addr });226 const lhs_name = lhs_index.getSymbolName(ctx);
370 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{ .addr = sect.addr + sect.size });227 return mem.startsWith(u8, lhs_name, "l") or mem.startsWith(u8, lhs_name, "L");
228 } else if (rhs.pext() or rhs.weakDef()) {
229 return !lhs.ext();
230 } else {
231 return false;
232 }
233 }
371234
372 return symbols[start..end];235 /// Like lessThanBySeniority but negated.
236 fn greaterThanBySeniority(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
237 return !lessThanBySeniority(ctx, lhs_index, rhs_index);
373 }238 }
374};239};
375240
376fn filterDice(dices: []macho.data_in_code_entry, start_addr: u64, end_addr: u64) []macho.data_in_code_entry {241fn filterSymbolsByAddress(
242 indexes: []SymbolAtIndex,
243 start_addr: u64,
244 end_addr: u64,
245 ctx: Context,
246) []SymbolAtIndex {
247 const Predicate = struct {
248 addr: u64,
249 ctx: Context,
250
251 pub fn predicate(pred: @This(), index: SymbolAtIndex) bool {
252 return index.getSymbol(pred.ctx).n_value >= pred.addr;
253 }
254 };
255
256 const start = MachO.findFirst(SymbolAtIndex, indexes, 0, Predicate{
257 .addr = start_addr,
258 .ctx = ctx,
259 });
260 const end = MachO.findFirst(SymbolAtIndex, indexes, start, Predicate{
261 .addr = end_addr,
262 .ctx = ctx,
263 });
264
265 return indexes[start..end];
266}
267
268fn filterRelocs(
269 relocs: []const macho.relocation_info,
270 start_addr: u64,
271 end_addr: u64,
272) []const macho.relocation_info {
377 const Predicate = struct {273 const Predicate = struct {
378 addr: u64,274 addr: u64,
379275
380 pub fn predicate(self: @This(), dice: macho.data_in_code_entry) bool {276 pub fn predicate(self: @This(), rel: macho.relocation_info) bool {
381 return dice.offset >= self.addr;277 return rel.r_address < self.addr;
382 }278 }
383 };279 };
384280
385 const start = MachO.findFirst(macho.data_in_code_entry, dices, 0, Predicate{ .addr = start_addr });281 const start = MachO.findFirst(macho.relocation_info, relocs, 0, Predicate{ .addr = end_addr });
386 const end = MachO.findFirst(macho.data_in_code_entry, dices, start, Predicate{ .addr = end_addr });282 const end = MachO.findFirst(macho.relocation_info, relocs, start, Predicate{ .addr = start_addr });
387283
388 return dices[start..end];284 return relocs[start..end];
389}285}
390286
391pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !void {287/// Splits object into atoms assuming one-shot linking mode.
288pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32) !void {
289 assert(macho_file.mode == .one_shot);
290
392 const tracy = trace(@src());291 const tracy = trace(@src());
393 defer tracy.end();292 defer tracy.end();
394293
294 const gpa = macho_file.base.allocator;
395 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;295 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
396296
397 log.debug("analysing {s}", .{self.name});297 log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
398298
399 // You would expect that the symbol table is at least pre-sorted based on symbol's type:299 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
400 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,300 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
401 // the GO compiler does not necessarily respect that therefore we sort immediately by type301 // the GO compiler does not necessarily respect that therefore we sort immediately by type
402 // and address within.302 // and address within.
403 var sorted_all_nlists = try std.ArrayList(NlistWithIndex).initCapacity(allocator, self.symtab.items.len);303 const context = Context{
404 defer sorted_all_nlists.deinit();304 .symtab = self.getSourceSymtab(),
305 .strtab = self.strtab,
306 };
307 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
308 defer sorted_all_syms.deinit();
405309
406 for (self.symtab.items) |nlist, index| {310 for (context.symtab) |_, index| {
407 sorted_all_nlists.appendAssumeCapacity(.{311 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
408 .nlist = nlist,
409 .index = @intCast(u32, index),
410 });
411 }312 }
412313
413 sort.sort(NlistWithIndex, sorted_all_nlists.items, {}, NlistWithIndex.lessThan);314 // We sort by type: defined < undefined, and
315 // afterwards by address in each group. Normally, dysymtab should
316 // be enough to guarantee the sort, but turns out not every compiler
317 // is kind enough to specify the symbols in the correct order.
318 sort.sort(SymbolAtIndex, sorted_all_syms.items, context, SymbolAtIndex.lessThan);
414319
415 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we320 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
416 // have to infer the start of undef section in the symtab ourselves.321 // have to infer the start of undef section in the symtab ourselves.
...@@ -418,226 +323,328 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -418,226 +323,328 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
418 const dysymtab = self.load_commands.items[cmd_index].dysymtab;323 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
419 break :blk dysymtab.iundefsym;324 break :blk dysymtab.iundefsym;
420 } else blk: {325 } else blk: {
421 var iundefsym: usize = sorted_all_nlists.items.len;326 var iundefsym: usize = sorted_all_syms.items.len;
422 while (iundefsym > 0) : (iundefsym -= 1) {327 while (iundefsym > 0) : (iundefsym -= 1) {
423 const nlist = sorted_all_nlists.items[iundefsym - 1];328 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
424 if (nlist.nlist.sect()) break;329 if (sym.sect()) break;
425 }330 }
426 break :blk iundefsym;331 break :blk iundefsym;
427 };332 };
428333
429 // We only care about defined symbols, so filter every other out.334 // We only care about defined symbols, so filter every other out.
430 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];335 const sorted_syms = sorted_all_syms.items[0..iundefsym];
336 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
431337
432 for (seg.sections.items) |sect, id| {338 for (seg.sections.items) |sect, id| {
433 const sect_id = @intCast(u8, id);339 const sect_id = @intCast(u8, id);
434 log.debug("putting section '{s},{s}' as an Atom", .{ sect.segName(), sect.sectName() });340 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
435341
436 // Get matching segment/section in the final artifact.342 // Get matching segment/section in the final artifact.
437 const match = (try macho_file.getMatchingSection(sect)) orelse {343 const match = (try macho_file.getMatchingSection(sect)) orelse {
438 log.debug("unhandled section", .{});344 log.debug(" unhandled section", .{});
439 continue;345 continue;
440 };346 };
441347
442 // Read section's code348 log.debug(" output sect({d}, '{s},{s}')", .{
443 var code = try allocator.alloc(u8, @intCast(usize, sect.size));349 macho_file.getSectionOrdinal(match),
444 defer allocator.free(code);350 macho_file.getSection(match).segName(),
445 _ = try self.file.preadAll(code, sect.offset);351 macho_file.getSection(match).sectName(),
446352 });
447 // Read section's list of relocations
448 var raw_relocs = try allocator.alloc(u8, sect.nreloc * @sizeOf(macho.relocation_info));
449 defer allocator.free(raw_relocs);
450 _ = try self.file.preadAll(raw_relocs, sect.reloff);
451 const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs);
452
453 // Symbols within this section only.
454 const filtered_nlists = NlistWithIndex.filterInSection(sorted_nlists, sect);
455
456 macho_file.has_dices = macho_file.has_dices or blk: {
457 if (self.text_section_index) |index| {
458 if (index != id) break :blk false;
459 if (self.data_in_code_entries.items.len == 0) break :blk false;
460 break :blk true;
461 }
462 break :blk false;
463 };
464 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
465
466 // Since there is no symbol to refer to this atom, we create
467 // a temp one, unless we already did that when working out the relocations
468 // of other atoms.
469 const atom_local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
470 const atom_local_sym_index = @intCast(u32, macho_file.locals.items.len);
471 try macho_file.locals.append(allocator, .{
472 .n_strx = 0,
473 .n_type = macho.N_SECT,
474 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
475 .n_desc = 0,
476 .n_value = 0,
477 });
478 try self.sections_as_symbols.putNoClobber(allocator, sect_id, atom_local_sym_index);
479 break :blk atom_local_sym_index;
480 };
481 const alignment = try math.powi(u32, 2, sect.@"align");
482 const aligned_size = mem.alignForwardGeneric(u64, sect.size, alignment);
483 const atom = try macho_file.createEmptyAtom(atom_local_sym_index, aligned_size, sect.@"align");
484353
354 const arch = macho_file.base.options.target.cpu.arch;
485 const is_zerofill = blk: {355 const is_zerofill = blk: {
486 const section_type = sect.type_();356 const section_type = sect.type_();
487 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;357 break :blk section_type == macho.S_ZEROFILL or section_type == macho.S_THREAD_LOCAL_ZEROFILL;
488 };358 };
489 if (!is_zerofill) {
490 mem.copy(u8, atom.code.items, code);
491 }
492359
493 // TODO stage2 bug: @alignCast shouldn't be needed360 // Read section's code
494 try atom.parseRelocs(@alignCast(@alignOf(macho.relocation_info), relocs), .{361 const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
495 .base_addr = sect.addr,
496 .allocator = allocator,
497 .object = self,
498 .macho_file = macho_file,
499 });
500362
501 if (macho_file.has_dices) {363 // Read section's list of relocations
502 const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + sect.size);364 const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];
503 try atom.dices.ensureTotalCapacity(allocator, dices.len);365 const relocs = mem.bytesAsSlice(
366 macho.relocation_info,
367 @alignCast(@alignOf(macho.relocation_info), raw_relocs),
368 );
504369
505 for (dices) |dice| {370 // Symbols within this section only.
506 atom.dices.appendAssumeCapacity(.{371 const filtered_syms = filterSymbolsByAddress(
507 .offset = dice.offset - (math.cast(u32, sect.addr) orelse return error.Overflow),372 sorted_syms,
508 .length = dice.length,373 sect.addr,
509 .kind = dice.kind,374 sect.addr + sect.size,
510 });375 context,
376 );
377
378 if (subsections_via_symbols and filtered_syms.len > 0) {
379 // If the first nlist does not match the start of the section,
380 // then we need to encapsulate the memory range [section start, first symbol)
381 // as a temporary symbol and insert the matching Atom.
382 const first_sym = filtered_syms[0].getSymbol(context);
383 if (first_sym.n_value > sect.addr) {
384 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
385 const sym_index = @intCast(u32, self.symtab.items.len);
386 try self.symtab.append(gpa, .{
387 .n_strx = 0,
388 .n_type = macho.N_SECT,
389 .n_sect = macho_file.getSectionOrdinal(match),
390 .n_desc = 0,
391 .n_value = sect.addr,
392 });
393 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
394 break :blk sym_index;
395 };
396 const atom_size = first_sym.n_value - sect.addr;
397 const atom_code: ?[]const u8 = if (code) |cc| blk: {
398 const size = math.cast(usize, atom_size) orelse return error.Overflow;
399 break :blk cc[0..size];
400 } else null;
401 const atom = try self.createAtomFromSubsection(
402 macho_file,
403 object_id,
404 sym_index,
405 atom_size,
406 sect.@"align",
407 atom_code,
408 relocs,
409 &.{},
410 match,
411 sect,
412 );
413 try macho_file.addAtomToSection(atom, match);
511 }414 }
512 }
513415
514 // Since this is atom gets a helper local temporary symbol that didn't exist416 var next_sym_count: usize = 0;
515 // in the object file which encompasses the entire section, we need traverse417 while (next_sym_count < filtered_syms.len) {
516 // the filtered symbols and note which symbol is contained within so that418 const next_sym = filtered_syms[next_sym_count].getSymbol(context);
517 // we can properly allocate addresses down the line.419 const addr = next_sym.n_value;
518 // While we're at it, we need to update segment,section mapping of each symbol too.420 const atom_syms = filterSymbolsByAddress(
519 try atom.contained.ensureTotalCapacity(allocator, filtered_nlists.len);421 filtered_syms[next_sym_count..],
520422 addr,
521 for (filtered_nlists) |nlist_with_index| {423 addr + 1,
522 const nlist = nlist_with_index.nlist;424 context,
523 const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;425 );
524 const local = &macho_file.locals.items[local_sym_index];426 next_sym_count += atom_syms.len;
525 local.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);427
526428 // We want to bubble up the first externally defined symbol here.
527 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {429 assert(atom_syms.len > 0);
528 // TODO there has to be a better to handle this.430 var sorted_atom_syms = std.ArrayList(SymbolAtIndex).init(gpa);
529 for (di.inner.func_list.items) |func| {431 defer sorted_atom_syms.deinit();
530 if (func.pc_range) |range| {432 try sorted_atom_syms.appendSlice(atom_syms);
531 if (nlist.n_value >= range.start and nlist.n_value < range.end) {433 sort.sort(
532 break :blk Atom.Stab{434 SymbolAtIndex,
533 .function = range.end - range.start,435 sorted_atom_syms.items,
534 };436 context,
535 }437 SymbolAtIndex.greaterThanBySeniority,
536 }438 );
439
440 const atom_size = blk: {
441 const end_addr = if (next_sym_count < filtered_syms.len)
442 filtered_syms[next_sym_count].getSymbol(context).n_value
443 else
444 sect.addr + sect.size;
445 break :blk end_addr - addr;
446 };
447 const atom_code: ?[]const u8 = if (code) |cc| blk: {
448 const start = math.cast(usize, addr - sect.addr) orelse return error.Overflow;
449 const size = math.cast(usize, atom_size) orelse return error.Overflow;
450 break :blk cc[start..][0..size];
451 } else null;
452 const atom_align = if (addr > 0)
453 math.min(@ctz(u64, addr), sect.@"align")
454 else
455 sect.@"align";
456 const atom = try self.createAtomFromSubsection(
457 macho_file,
458 object_id,
459 sorted_atom_syms.items[0].index,
460 atom_size,
461 atom_align,
462 atom_code,
463 relocs,
464 sorted_atom_syms.items[1..],
465 match,
466 sect,
467 );
468
469 if (arch == .x86_64 and addr == sect.addr) {
470 // In x86_64 relocs, it can so happen that the compiler refers to the same
471 // atom by both the actual assigned symbol and the start of the section. In this
472 // case, we need to link the two together so add an alias.
473 const alias = self.sections_as_symbols.get(sect_id) orelse blk: {
474 const alias = @intCast(u32, self.symtab.items.len);
475 try self.symtab.append(gpa, .{
476 .n_strx = 0,
477 .n_type = macho.N_SECT,
478 .n_sect = macho_file.getSectionOrdinal(match),
479 .n_desc = 0,
480 .n_value = addr,
481 });
482 try self.sections_as_symbols.putNoClobber(gpa, sect_id, alias);
483 break :blk alias;
484 };
485 try atom.contained.append(gpa, .{
486 .sym_index = alias,
487 .offset = 0,
488 });
489 try self.atom_by_index_table.put(gpa, alias, atom);
537 }490 }
538 // TODO
539 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
540 break :blk .static;
541 } else null;
542
543 atom.contained.appendAssumeCapacity(.{
544 .local_sym_index = local_sym_index,
545 .offset = nlist.n_value - sect.addr,
546 .stab = stab,
547 });
548 }
549491
550 if (!self.start_atoms.contains(match)) {492 try macho_file.addAtomToSection(atom, match);
551 try self.start_atoms.putNoClobber(allocator, match, atom);493 }
552 }
553
554 if (self.end_atoms.getPtr(match)) |last| {
555 last.*.next = atom;
556 atom.prev = last.*;
557 last.* = atom;
558 } else {494 } else {
559 try self.end_atoms.putNoClobber(allocator, match, atom);495 // If there is no symbol to refer to this atom, we create
496 // a temp one, unless we already did that when working out the relocations
497 // of other atoms.
498 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
499 const sym_index = @intCast(u32, self.symtab.items.len);
500 try self.symtab.append(gpa, .{
501 .n_strx = 0,
502 .n_type = macho.N_SECT,
503 .n_sect = macho_file.getSectionOrdinal(match),
504 .n_desc = 0,
505 .n_value = sect.addr,
506 });
507 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
508 break :blk sym_index;
509 };
510 const atom = try self.createAtomFromSubsection(
511 macho_file,
512 object_id,
513 sym_index,
514 sect.size,
515 sect.@"align",
516 code,
517 relocs,
518 filtered_syms,
519 match,
520 sect,
521 );
522 try macho_file.addAtomToSection(atom, match);
560 }523 }
561 try self.contained_atoms.append(allocator, atom);
562 }524 }
563}525}
564526
565fn parseSymtab(self: *Object, allocator: Allocator) !void {527fn createAtomFromSubsection(
566 const index = self.symtab_cmd_index orelse return;528 self: *Object,
567 const symtab_cmd = self.load_commands.items[index].symtab;529 macho_file: *MachO,
568530 object_id: u32,
569 var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);531 sym_index: u32,
570 defer allocator.free(symtab);532 size: u64,
571 _ = try self.file.preadAll(symtab, symtab_cmd.symoff);533 alignment: u32,
572 const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));534 code: ?[]const u8,
573 try self.symtab.appendSlice(allocator, slice);535 relocs: []const macho.relocation_info,
574536 indexes: []const SymbolAtIndex,
575 var strtab = try allocator.alloc(u8, symtab_cmd.strsize);537 match: MatchingSection,
576 defer allocator.free(strtab);538 sect: macho.section_64,
577 _ = try self.file.preadAll(strtab, symtab_cmd.stroff);539) !*Atom {
578 try self.strtab.appendSlice(allocator, strtab);540 const gpa = macho_file.base.allocator;
579}541 const sym = self.symtab.items[sym_index];
580542 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
581pub fn parseDebugInfo(self: *Object, allocator: Allocator) !void {543 atom.file = object_id;
582 log.debug("parsing debug info in '{s}'", .{self.name});544 self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
545
546 log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
547 sym_index,
548 self.getString(sym.n_strx),
549 macho_file.getSectionOrdinal(match),
550 macho_file.getSection(match).segName(),
551 macho_file.getSection(match).sectName(),
552 object_id,
553 });
554
555 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
556 try self.managed_atoms.append(gpa, atom);
557
558 if (code) |cc| {
559 assert(size == cc.len);
560 mem.copy(u8, atom.code.items, cc);
561 }
583562
584 var debug_info = blk: {563 const base_offset = sym.n_value - sect.addr;
585 var di = try DebugInfo.parseFromObject(allocator, self);564 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);
586 break :blk di orelse return;565 try atom.parseRelocs(filtered_relocs, .{
587 };566 .macho_file = macho_file,
567 .base_addr = sect.addr,
568 .base_offset = @intCast(i32, base_offset),
569 });
570
571 // Since this is atom gets a helper local temporary symbol that didn't exist
572 // in the object file which encompasses the entire section, we need traverse
573 // the filtered symbols and note which symbol is contained within so that
574 // we can properly allocate addresses down the line.
575 // While we're at it, we need to update segment,section mapping of each symbol too.
576 try atom.contained.ensureTotalCapacity(gpa, indexes.len);
577 for (indexes) |inner_sym_index| {
578 const inner_sym = &self.symtab.items[inner_sym_index.index];
579 inner_sym.n_sect = macho_file.getSectionOrdinal(match);
580 atom.contained.appendAssumeCapacity(.{
581 .sym_index = inner_sym_index.index,
582 .offset = inner_sym.n_value - sym.n_value,
583 });
588584
589 // We assume there is only one CU.585 try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom);
590 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {586 }
591 error.MissingDebugInfo => {
592 // TODO audit cases with missing debug info and audit our dwarf.zig module.
593 log.debug("invalid or missing debug info in {s}; skipping", .{self.name});
594 return;
595 },
596 else => |e| return e,
597 };
598 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);
599 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);
600587
601 self.debug_info = debug_info;588 return atom;
602 self.tu_name = try allocator.dupe(u8, name);589}
603 self.tu_comp_dir = try allocator.dupe(u8, comp_dir);
604590
605 if (self.mtime == null) {591fn parseSymtab(self: *Object, allocator: Allocator) !void {
606 self.mtime = mtime: {592 const index = self.symtab_cmd_index orelse return;
607 const stat = self.file.stat() catch break :mtime 0;593 const symtab = self.load_commands.items[index].symtab;
608 break :mtime @intCast(u64, @divFloor(stat.mtime, 1_000_000_000));594 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
609 };595 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
610 }
611}596}
612597
613pub fn parseDataInCode(self: *Object, allocator: Allocator) !void {598pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
614 const index = self.data_in_code_cmd_index orelse return;599 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
615 const data_in_code = self.load_commands.items[index].linkedit_data;600 const symtab = self.load_commands.items[index].symtab;
601 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
602 const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
603 return mem.bytesAsSlice(
604 macho.nlist_64,
605 @alignCast(@alignOf(macho.nlist_64), raw_symtab),
606 );
607}
616608
617 var buffer = try allocator.alloc(u8, data_in_code.datasize);609pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
618 defer allocator.free(buffer);610 const symtab = self.getSourceSymtab();
611 if (index >= symtab.len) return null;
612 return symtab[index];
613}
619614
620 _ = try self.file.preadAll(buffer, data_in_code.dataoff);615pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
616 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
617 assert(index < seg.sections.items.len);
618 return seg.sections.items[index];
619}
621620
622 var stream = io.fixedBufferStream(buffer);621pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
623 var reader = stream.reader();622 const index = self.data_in_code_cmd_index orelse return null;
624 while (true) {623 const data_in_code = self.load_commands.items[index].linkedit_data;
625 const dice = reader.readStruct(macho.data_in_code_entry) catch |err| switch (err) {624 const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];
626 error.EndOfStream => break,625 return mem.bytesAsSlice(
627 };626 macho.data_in_code_entry,
628 try self.data_in_code_entries.append(allocator, dice);627 @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),
629 }628 );
630}629}
631630
632fn readSection(self: Object, allocator: Allocator, index: u16) ![]u8 {631pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
633 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;632 const sect = self.getSourceSection(index);
634 const sect = seg.sections.items[index];633 const size = math.cast(usize, sect.size) orelse return error.Overflow;
635 var buffer = try allocator.alloc(u8, @intCast(usize, sect.size));634 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
636 _ = try self.file.preadAll(buffer, sect.offset);635 sect.segName(),
637 return buffer;636 sect.sectName(),
637 sect.offset,
638 sect.offset + sect.size,
639 });
640 return self.contents[sect.offset..][0..size];
638}641}
639642
640pub fn getString(self: Object, off: u32) []const u8 {643pub fn getString(self: Object, off: u32) []const u8 {
641 assert(off < self.strtab.items.len);644 assert(off < self.strtab.len);
642 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);645 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
646}
647
648pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
649 return self.atom_by_index_table.get(sym_index);
643}650}
src/link/MachO/dead_strip.zig created+292
...@@ -0,0 +1,292 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const log = std.log.scoped(.dead_strip);
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7
8const Allocator = mem.Allocator;
9const Atom = @import("Atom.zig");
10const MachO = @import("../MachO.zig");
11const MatchingSection = MachO.MatchingSection;
12
13pub fn gcAtoms(macho_file: *MachO) !void {
14 const gpa = macho_file.base.allocator;
15 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
16 defer arena_allocator.deinit();
17 const arena = arena_allocator.allocator();
18
19 var roots = std.AutoHashMap(*Atom, void).init(arena);
20 try collectRoots(&roots, macho_file);
21
22 var alive = std.AutoHashMap(*Atom, void).init(arena);
23 try mark(roots, &alive, macho_file);
24
25 try prune(arena, alive, macho_file);
26}
27
28fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
29 const sect = macho_file.getSectionPtr(match);
30
31 // If we want to enable GC for incremental codepath, we need to take into
32 // account any padding that might have been left here.
33 sect.size -= atom.size;
34
35 if (atom.prev) |prev| {
36 prev.next = atom.next;
37 }
38 if (atom.next) |next| {
39 next.prev = atom.prev;
40 } else {
41 const last = macho_file.atoms.getPtr(match).?;
42 if (atom.prev) |prev| {
43 last.* = prev;
44 } else {
45 // The section will be GCed in the next step.
46 last.* = undefined;
47 sect.size = 0;
48 }
49 }
50}
51
52fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
53 const output_mode = macho_file.base.options.output_mode;
54
55 switch (output_mode) {
56 .Exe => {
57 // Add entrypoint as GC root
58 const global = try macho_file.getEntryPoint();
59 const atom = macho_file.getAtomForSymbol(global).?; // panic here means fatal error
60 _ = try roots.getOrPut(atom);
61 },
62 else => |other| {
63 assert(other == .Lib);
64 // Add exports as GC roots
65 for (macho_file.globals.values()) |global| {
66 const sym = macho_file.getSymbol(global);
67 if (!sym.sect()) continue;
68 const atom = macho_file.getAtomForSymbol(global) orelse {
69 log.debug("skipping {s}", .{macho_file.getSymbolName(global)});
70 continue;
71 };
72 _ = try roots.getOrPut(atom);
73 log.debug("adding root", .{});
74 macho_file.logAtom(atom);
75 }
76 },
77 }
78
79 // TODO just a temp until we learn how to parse unwind records
80 if (macho_file.globals.get("___gxx_personality_v0")) |global| {
81 if (macho_file.getAtomForSymbol(global)) |atom| {
82 _ = try roots.getOrPut(atom);
83 log.debug("adding root", .{});
84 macho_file.logAtom(atom);
85 }
86 }
87
88 for (macho_file.objects.items) |object| {
89 for (object.managed_atoms.items) |atom| {
90 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
91 if (source_sym.tentative()) continue;
92 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
93 const is_gc_root = blk: {
94 if (source_sect.isDontDeadStrip()) break :blk true;
95 if (mem.eql(u8, "__StaticInit", source_sect.sectName())) break :blk true;
96 switch (source_sect.type_()) {
97 macho.S_MOD_INIT_FUNC_POINTERS,
98 macho.S_MOD_TERM_FUNC_POINTERS,
99 => break :blk true,
100 else => break :blk false,
101 }
102 };
103 if (is_gc_root) {
104 try roots.putNoClobber(atom, {});
105 log.debug("adding root", .{});
106 macho_file.logAtom(atom);
107 }
108 }
109 }
110}
111
112fn markLive(atom: *Atom, alive: *std.AutoHashMap(*Atom, void), macho_file: *MachO) anyerror!void {
113 const gop = try alive.getOrPut(atom);
114 if (gop.found_existing) return;
115
116 log.debug("marking live", .{});
117 macho_file.logAtom(atom);
118
119 for (atom.relocs.items) |rel| {
120 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
121 try markLive(target_atom, alive, macho_file);
122 }
123}
124
125fn refersLive(atom: *Atom, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) bool {
126 for (atom.relocs.items) |rel| {
127 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
128 if (alive.contains(target_atom)) return true;
129 }
130 return false;
131}
132
133fn refersDead(atom: *Atom, macho_file: *MachO) bool {
134 for (atom.relocs.items) |rel| {
135 const target_atom = rel.getTargetAtom(macho_file) orelse continue;
136 const target_sym = target_atom.getSymbol(macho_file);
137 if (target_sym.n_desc == MachO.N_DESC_GCED) return true;
138 }
139 return false;
140}
141
142fn mark(
143 roots: std.AutoHashMap(*Atom, void),
144 alive: *std.AutoHashMap(*Atom, void),
145 macho_file: *MachO,
146) !void {
147 try alive.ensureUnusedCapacity(roots.count());
148
149 var it = roots.keyIterator();
150 while (it.next()) |root| {
151 try markLive(root.*, alive, macho_file);
152 }
153
154 var loop: bool = true;
155 while (loop) {
156 loop = false;
157
158 for (macho_file.objects.items) |object| {
159 for (object.managed_atoms.items) |atom| {
160 if (alive.contains(atom)) continue;
161 const source_sym = object.getSourceSymbol(atom.sym_index) orelse continue;
162 if (source_sym.tentative()) continue;
163 const source_sect = object.getSourceSection(source_sym.n_sect - 1);
164 if (source_sect.isDontDeadStripIfReferencesLive() and refersLive(atom, alive.*, macho_file)) {
165 try markLive(atom, alive, macho_file);
166 loop = true;
167 }
168 }
169 }
170 }
171}
172
173fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
174 // Any section that ends up here will be updated, that is,
175 // its size and alignment recalculated.
176 var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
177 var loop: bool = true;
178 while (loop) {
179 loop = false;
180
181 for (macho_file.objects.items) |object| {
182 for (object.getSourceSymtab()) |_, source_index| {
183 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
184 if (alive.contains(atom)) continue;
185
186 const global = atom.getSymbolWithLoc();
187 const sym = atom.getSymbolPtr(macho_file);
188 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
189
190 if (sym.n_desc == MachO.N_DESC_GCED) continue;
191 if (!sym.ext() and !refersDead(atom, macho_file)) continue;
192
193 macho_file.logAtom(atom);
194 sym.n_desc = MachO.N_DESC_GCED;
195 removeAtomFromSection(atom, match, macho_file);
196 _ = try gc_sections.put(match, {});
197
198 for (atom.contained.items) |sym_off| {
199 const inner = macho_file.getSymbolPtr(.{
200 .sym_index = sym_off.sym_index,
201 .file = atom.file,
202 });
203 inner.n_desc = MachO.N_DESC_GCED;
204 }
205
206 if (macho_file.got_entries_table.contains(global)) {
207 const got_atom = macho_file.getGotAtomForSymbol(global).?;
208 const got_sym = got_atom.getSymbolPtr(macho_file);
209 got_sym.n_desc = MachO.N_DESC_GCED;
210 }
211
212 if (macho_file.stubs_table.contains(global)) {
213 const stubs_atom = macho_file.getStubsAtomForSymbol(global).?;
214 const stubs_sym = stubs_atom.getSymbolPtr(macho_file);
215 stubs_sym.n_desc = MachO.N_DESC_GCED;
216 }
217
218 if (macho_file.tlv_ptr_entries_table.contains(global)) {
219 const tlv_ptr_atom = macho_file.getTlvPtrAtomForSymbol(global).?;
220 const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(macho_file);
221 tlv_ptr_sym.n_desc = MachO.N_DESC_GCED;
222 }
223
224 loop = true;
225 }
226 }
227 }
228
229 for (macho_file.got_entries.items) |entry| {
230 const sym = entry.getSymbol(macho_file);
231 if (sym.n_desc != MachO.N_DESC_GCED) continue;
232
233 // TODO tombstone
234 const atom = entry.getAtom(macho_file);
235 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
236 removeAtomFromSection(atom, match, macho_file);
237 _ = try gc_sections.put(match, {});
238 _ = macho_file.got_entries_table.remove(entry.target);
239 }
240
241 for (macho_file.stubs.items) |entry| {
242 const sym = entry.getSymbol(macho_file);
243 if (sym.n_desc != MachO.N_DESC_GCED) continue;
244
245 // TODO tombstone
246 const atom = entry.getAtom(macho_file);
247 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
248 removeAtomFromSection(atom, match, macho_file);
249 _ = try gc_sections.put(match, {});
250 _ = macho_file.stubs_table.remove(entry.target);
251 }
252
253 for (macho_file.tlv_ptr_entries.items) |entry| {
254 const sym = entry.getSymbol(macho_file);
255 if (sym.n_desc != MachO.N_DESC_GCED) continue;
256
257 // TODO tombstone
258 const atom = entry.getAtom(macho_file);
259 const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
260 removeAtomFromSection(atom, match, macho_file);
261 _ = try gc_sections.put(match, {});
262 _ = macho_file.tlv_ptr_entries_table.remove(entry.target);
263 }
264
265 var gc_sections_it = gc_sections.iterator();
266 while (gc_sections_it.next()) |entry| {
267 const match = entry.key_ptr.*;
268 const sect = macho_file.getSectionPtr(match);
269 if (sect.size == 0) continue; // Pruning happens automatically in next step.
270
271 sect.@"align" = 0;
272 sect.size = 0;
273
274 var atom = macho_file.atoms.get(match).?;
275
276 while (atom.prev) |prev| {
277 atom = prev;
278 }
279
280 while (true) {
281 const atom_alignment = try math.powi(u32, 2, atom.alignment);
282 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
283 const padding = aligned_end_addr - sect.size;
284 sect.size += padding + atom.size;
285 sect.@"align" = @maximum(sect.@"align", atom.alignment);
286
287 if (atom.next) |next| {
288 atom = next;
289 } else break;
290 }
291 }
292}
src/link/strtab.zig created+113
...@@ -0,0 +1,113 @@
1const std = @import("std");
2const mem = std.mem;
3
4const Allocator = mem.Allocator;
5const StringIndexAdapter = std.hash_map.StringIndexAdapter;
6const StringIndexContext = std.hash_map.StringIndexContext;
7
8pub fn StringTable(comptime log_scope: @Type(.EnumLiteral)) type {
9 return struct {
10 const Self = @This();
11
12 const log = std.log.scoped(log_scope);
13
14 buffer: std.ArrayListUnmanaged(u8) = .{},
15 table: std.HashMapUnmanaged(u32, bool, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
16
17 pub fn deinit(self: *Self, gpa: Allocator) void {
18 self.buffer.deinit(gpa);
19 self.table.deinit(gpa);
20 }
21
22 pub fn toOwnedSlice(self: *Self, gpa: Allocator) []const u8 {
23 const result = self.buffer.toOwnedSlice(gpa);
24 self.table.clearRetainingCapacity();
25 return result;
26 }
27
28 pub const PrunedResult = struct {
29 buffer: []const u8,
30 idx_map: std.AutoHashMap(u32, u32),
31 };
32
33 pub fn toPrunedResult(self: *Self, gpa: Allocator) !PrunedResult {
34 var buffer = std.ArrayList(u8).init(gpa);
35 defer buffer.deinit();
36 try buffer.ensureTotalCapacity(self.buffer.items.len);
37 buffer.appendAssumeCapacity(0);
38
39 var idx_map = std.AutoHashMap(u32, u32).init(gpa);
40 errdefer idx_map.deinit();
41 try idx_map.ensureTotalCapacity(self.table.count());
42
43 var it = self.table.iterator();
44 while (it.next()) |entry| {
45 const off = entry.key_ptr.*;
46 const save = entry.value_ptr.*;
47 if (!save) continue;
48 const new_off = @intCast(u32, buffer.items.len);
49 buffer.appendSliceAssumeCapacity(self.getAssumeExists(off));
50 idx_map.putAssumeCapacityNoClobber(off, new_off);
51 }
52
53 self.buffer.clearRetainingCapacity();
54 self.table.clearRetainingCapacity();
55
56 return PrunedResult{
57 .buffer = buffer.toOwnedSlice(),
58 .idx_map = idx_map,
59 };
60 }
61
62 pub fn insert(self: *Self, gpa: Allocator, string: []const u8) !u32 {
63 const gop = try self.table.getOrPutContextAdapted(gpa, @as([]const u8, string), StringIndexAdapter{
64 .bytes = &self.buffer,
65 }, StringIndexContext{
66 .bytes = &self.buffer,
67 });
68 if (gop.found_existing) {
69 const off = gop.key_ptr.*;
70 gop.value_ptr.* = true;
71 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
72 return off;
73 }
74
75 try self.buffer.ensureUnusedCapacity(gpa, string.len + 1);
76 const new_off = @intCast(u32, self.buffer.items.len);
77
78 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });
79
80 self.buffer.appendSliceAssumeCapacity(string);
81 self.buffer.appendAssumeCapacity(0);
82
83 gop.key_ptr.* = new_off;
84 gop.value_ptr.* = true;
85
86 return new_off;
87 }
88
89 pub fn delete(self: *Self, string: []const u8) void {
90 const value_ptr = self.table.getPtrAdapted(@as([]const u8, string), StringIndexAdapter{
91 .bytes = &self.buffer,
92 }) orelse return;
93 value_ptr.* = false;
94 log.debug("marked '{s}' for deletion", .{string});
95 }
96
97 pub fn getOffset(self: *Self, string: []const u8) ?u32 {
98 return self.table.getKeyAdapted(string, StringIndexAdapter{
99 .bytes = &self.buffer,
100 });
101 }
102
103 pub fn get(self: Self, off: u32) ?[]const u8 {
104 log.debug("getting string at 0x{x}", .{off});
105 if (off >= self.buffer.items.len) return null;
106 return mem.sliceTo(@ptrCast([*:0]const u8, self.buffer.items.ptr + off), 0);
107 }
108
109 pub fn getAssumeExists(self: Self, off: u32) []const u8 {
110 return self.get(off) orelse unreachable;
111 }
112 };
113}
src/main.zig+11
...@@ -446,6 +446,8 @@ const usage_build_generic =...@@ -446,6 +446,8 @@ const usage_build_generic =
446 \\ --compress-debug-sections=[e] Debug section compression settings446 \\ --compress-debug-sections=[e] Debug section compression settings
447 \\ none No compression447 \\ none No compression
448 \\ zlib Compression with deflate/inflate448 \\ zlib Compression with deflate/inflate
449 \\ --gc-sections Force removal of functions and data that are unreachable by the entry point or exported symbols
450 \\ --no-gc-sections Don't force removal of unreachable functions and data
449 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker451 \\ --subsystem [subsystem] (Windows) /SUBSYSTEM:<subsystem> to the linker
450 \\ --stack [size] Override default stack size452 \\ --stack [size] Override default stack size
451 \\ --image-base [addr] Set base address for executable image453 \\ --image-base [addr] Set base address for executable image
...@@ -463,6 +465,7 @@ const usage_build_generic =...@@ -463,6 +465,7 @@ const usage_build_generic =
463 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`465 \\ -search_dylibs_first (Darwin) search `libx.dylib` in each dir in library search paths, then `libx.a`
464 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation466 \\ -headerpad [value] (Darwin) set minimum space for future expansion of the load commands in hexadecimal notation
465 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN467 \\ -headerpad_max_install_names (Darwin) set enough space as if all paths were MAXPATHLEN
468 \\ -dead_strip (Darwin) remove functions and data that are unreachable by the entry point or exported symbols
466 \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols469 \\ -dead_strip_dylibs (Darwin) remove dylibs that are unreachable by the entry point or exported symbols
467 \\ --import-memory (WebAssembly) import memory from the environment470 \\ --import-memory (WebAssembly) import memory from the environment
468 \\ --import-table (WebAssembly) import function table from the host environment471 \\ --import-table (WebAssembly) import function table from the host environment
...@@ -969,6 +972,8 @@ fn buildOutputType(...@@ -969,6 +972,8 @@ fn buildOutputType(
969 };972 };
970 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {973 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
971 headerpad_max_install_names = true;974 headerpad_max_install_names = true;
975 } else if (mem.eql(u8, arg, "-dead_strip")) {
976 linker_gc_sections = true;
972 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {977 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
973 dead_strip_dylibs = true;978 dead_strip_dylibs = true;
974 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {979 } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) {
...@@ -1311,6 +1316,10 @@ fn buildOutputType(...@@ -1311,6 +1316,10 @@ fn buildOutputType(
1311 try linker_export_symbol_names.append(arg["--export=".len..]);1316 try linker_export_symbol_names.append(arg["--export=".len..]);
1312 } else if (mem.eql(u8, arg, "-Bsymbolic")) {1317 } else if (mem.eql(u8, arg, "-Bsymbolic")) {
1313 linker_bind_global_refs_locally = true;1318 linker_bind_global_refs_locally = true;
1319 } else if (mem.eql(u8, arg, "--gc-sections")) {
1320 linker_gc_sections = true;
1321 } else if (mem.eql(u8, arg, "--no-gc-sections")) {
1322 linker_gc_sections = false;
1314 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {1323 } else if (mem.eql(u8, arg, "--debug-compile-errors")) {
1315 debug_compile_errors = true;1324 debug_compile_errors = true;
1316 } else if (mem.eql(u8, arg, "--verbose-link")) {1325 } else if (mem.eql(u8, arg, "--verbose-link")) {
...@@ -1764,6 +1773,8 @@ fn buildOutputType(...@@ -1764,6 +1773,8 @@ fn buildOutputType(
1764 };1773 };
1765 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {1774 } else if (mem.eql(u8, arg, "-headerpad_max_install_names")) {
1766 headerpad_max_install_names = true;1775 headerpad_max_install_names = true;
1776 } else if (mem.eql(u8, arg, "-dead_strip")) {
1777 linker_gc_sections = true;
1767 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {1778 } else if (mem.eql(u8, arg, "-dead_strip_dylibs")) {
1768 dead_strip_dylibs = true;1779 dead_strip_dylibs = true;
1769 } else if (mem.eql(u8, arg, "--gc-sections")) {1780 } else if (mem.eql(u8, arg, "--gc-sections")) {
test/cases/recursive_inline_function.0.zig+1-1
...@@ -9,5 +9,5 @@ inline fn fibonacci(n: usize) usize {...@@ -9,5 +9,5 @@ inline fn fibonacci(n: usize) usize {
9}9}
1010
11// run11// run
12// target=x86_64-linux,arm-linux,x86_64-macos,wasm32-wasi12// target=x86_64-linux,arm-linux,wasm32-wasi
13//13//
test/link.zig+4
...@@ -60,6 +60,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -60,6 +60,10 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
60 .build_modes = true,60 .build_modes = true,
61 });61 });
6262
63 cases.addBuildFile("test/link/macho/dead_strip/build.zig", .{
64 .build_modes = false,
65 });
66
63 cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{67 cases.addBuildFile("test/link/macho/dead_strip_dylibs/build.zig", .{
64 .build_modes = true,68 .build_modes = true,
65 .requires_macos_sdk = true,69 .requires_macos_sdk = true,
test/link/macho/dead_strip/build.zig created+49
...@@ -0,0 +1,49 @@
1const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
7
8 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());
10
11 {
12 // Without -dead_strip, we expect `iAmUnused` symbol present
13 const exe = createScenario(b, mode);
14
15 const check = exe.checkObject(.macho);
16 check.checkInSymtab();
17 check.checkNext("{*} (__TEXT,__text) external _iAmUnused");
18
19 test_step.dependOn(&check.step);
20
21 const run_cmd = exe.run();
22 run_cmd.expectStdOutEqual("Hello!\n");
23 test_step.dependOn(&run_cmd.step);
24 }
25
26 {
27 // With -dead_strip, no `iAmUnused` symbol should be present
28 const exe = createScenario(b, mode);
29 exe.link_gc_sections = true;
30
31 const check = exe.checkObject(.macho);
32 check.checkInSymtab();
33 check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");
34
35 test_step.dependOn(&check.step);
36
37 const run_cmd = exe.run();
38 run_cmd.expectStdOutEqual("Hello!\n");
39 test_step.dependOn(&run_cmd.step);
40 }
41}
42
43fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
44 const exe = b.addExecutable("test", null);
45 exe.addCSourceFile("main.c", &[0][]const u8{});
46 exe.setBuildMode(mode);
47 exe.linkLibC();
48 return exe;
49}
test/link/macho/dead_strip/main.c created+14
...@@ -0,0 +1,14 @@
1#include <stdio.h>
2
3void printMe() {
4 printf("Hello!\n");
5}
6
7int main(int argc, char* argv[]) {
8 printMe();
9 return 0;
10}
11
12void iAmUnused() {
13 printf("YOU SHALL NOT PASS!\n");
14}