authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-06 17:11:39+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:20+02:00
log9eb7e5182b963366da9415ff7efe7c0fa5b1ad62
tree8f93fc1156cdc5e9480519c1db0eafbae545fab0
parent843701d0feb683810f6be3cb5d6406eddb5539d0

macho: rework symbol handling to match zld/ELF

Now, each object file will store a mutable table of symbols that it defines. Upon symbol resolution between object files, the symbol will be updated with a globally allocated section ordinal and address in virtual memory. If the object defines a globally available symbol, its location only (comprising of the symbol index and object index) will be stored in the globals map for easy access when relocating, etc. This approach cleans up the symbol management significantly, and matches the status quo used in zld/ELF. Additionally, this makes scoping symbol stabs easier too as they are now naturally contained within each object file.

13 files changed, 2086 insertions(+), 2389 deletions(-)

src/arch/aarch64/CodeGen.zig+8-8
...@@ -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 global_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 .extern_fn = .{
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 .global_index = global_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+4-3
...@@ -660,9 +660,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -660,9 +660,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
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(extern_fn.atom_index).?;
663 const target = macho_file.globals.values()[extern_fn.global_index];
663 try atom.relocs.append(emit.bin_file.allocator, .{664 try atom.relocs.append(emit.bin_file.allocator, .{
664 .offset = offset,665 .offset = offset,
665 .target = .{ .global = extern_fn.sym_name },666 .target = target,
666 .addend = 0,667 .addend = 0,
667 .subtractor = null,668 .subtractor = null,
668 .pcrel = true,669 .pcrel = true,
...@@ -864,7 +865,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -864,7 +865,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
864 // Page reloc for adrp instruction.865 // Page reloc for adrp instruction.
865 try atom.relocs.append(emit.bin_file.allocator, .{866 try atom.relocs.append(emit.bin_file.allocator, .{
866 .offset = offset,867 .offset = offset,
867 .target = .{ .local = data.sym_index },868 .target = .{ .sym_index = data.sym_index, .file = null },
868 .addend = 0,869 .addend = 0,
869 .subtractor = null,870 .subtractor = null,
870 .pcrel = true,871 .pcrel = true,
...@@ -882,7 +883,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -882,7 +883,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882 // Pageoff reloc for adrp instruction.883 // Pageoff reloc for adrp instruction.
883 try atom.relocs.append(emit.bin_file.allocator, .{884 try atom.relocs.append(emit.bin_file.allocator, .{
884 .offset = offset + 4,885 .offset = offset + 4,
885 .target = .{ .local = data.sym_index },886 .target = .{ .sym_index = data.sym_index, .file = null },
886 .addend = 0,887 .addend = 0,
887 .subtractor = null,888 .subtractor = null,
888 .pcrel = false,889 .pcrel = false,
src/arch/aarch64/Mir.zig+1-1
...@@ -232,7 +232,7 @@ pub const Inst = struct {...@@ -232,7 +232,7 @@ pub const Inst = struct {
232 /// Index of the containing atom.232 /// Index of the containing atom.
233 atom_index: u32,233 atom_index: u32,
234 /// Index into the linker's string table.234 /// Index into the linker's string table.
235 sym_name: u32,235 global_index: u32,
236 },236 },
237 /// A 16-bit immediate value.237 /// A 16-bit immediate value.
238 ///238 ///
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+7-7
...@@ -2645,7 +2645,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2645,7 +2645,7 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2645 }),2645 }),
2646 .data = .{2646 .data = .{
2647 .load_reloc = .{2647 .load_reloc = .{
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 global_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 .extern_fn = .{
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 .global_index = global_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+3-2
...@@ -1005,7 +1005,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1005,7 +1005,7 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
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, load_reloc.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 = load_reloc.sym_index, .file = null },
1009 .addend = 0,1009 .addend = 0,
1010 .subtractor = null,1010 .subtractor = null,
1011 .pcrel = true,1011 .pcrel = true,
...@@ -1127,9 +1127,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1127,9 +1127,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
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(extern_fn.atom_index).?;
1130 const target = macho_file.globals.values()[extern_fn.global_index];
1130 try atom.relocs.append(emit.bin_file.allocator, .{1131 try atom.relocs.append(emit.bin_file.allocator, .{
1131 .offset = offset,1132 .offset = offset,
1132 .target = .{ .global = extern_fn.sym_name },1133 .target = target,
1133 .addend = 0,1134 .addend = 0,
1134 .subtractor = null,1135 .subtractor = null,
1135 .pcrel = true,1136 .pcrel = true,
src/arch/x86_64/Mir.zig+2-2
...@@ -443,8 +443,8 @@ pub const Inst = struct {...@@ -443,8 +443,8 @@ pub const Inst = struct {
443 extern_fn: struct {443 extern_fn: struct {
444 /// Index of the containing atom.444 /// Index of the containing atom.
445 atom_index: u32,445 atom_index: u32,
446 /// Index into the linker's string table.446 /// Index into the linker's globals table.
447 sym_name: u32,447 global_index: u32,
448 },448 },
449 /// PIE load relocation.449 /// PIE load relocation.
450 load_reloc: struct {450 load_reloc: struct {
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+1419-1749
...@@ -35,8 +35,7 @@ const LibStub = @import("tapi.zig").LibStub;...@@ -35,8 +35,7 @@ const LibStub = @import("tapi.zig").LibStub;
35const Liveness = @import("../Liveness.zig");35const Liveness = @import("../Liveness.zig");
36const LlvmObject = @import("../codegen/llvm.zig").Object;36const LlvmObject = @import("../codegen/llvm.zig").Object;
37const Module = @import("../Module.zig");37const Module = @import("../Module.zig");
38const StringIndexAdapter = std.hash_map.StringIndexAdapter;38const StringTable = @import("strtab.zig").StringTable;
39const StringIndexContext = std.hash_map.StringIndexContext;
40const Trie = @import("MachO/Trie.zig");39const Trie = @import("MachO/Trie.zig");
41const Type = @import("../type.zig").Type;40const Type = @import("../type.zig").Type;
42const TypedValue = @import("../TypedValue.zig");41const TypedValue = @import("../TypedValue.zig");
...@@ -52,13 +51,13 @@ pub const SearchStrategy = enum {...@@ -52,13 +51,13 @@ pub const SearchStrategy = enum {
52 dylibs_first,51 dylibs_first,
53};52};
5453
54pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
55
55const SystemLib = struct {56const SystemLib = struct {
56 needed: bool = false,57 needed: bool = false,
57 weak: bool = false,58 weak: bool = false,
58};59};
5960
60const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
61
62base: File,61base: File,
6362
64/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.63/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
...@@ -153,40 +152,28 @@ rustc_section_index: ?u16 = null,...@@ -153,40 +152,28 @@ rustc_section_index: ?u16 = null,
153rustc_section_size: u64 = 0,152rustc_section_size: u64 = 0,
154153
155locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},154locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
156globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},155globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
157undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},156unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
158symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
159unresolved: std.AutoArrayHashMapUnmanaged(u32, enum {
160 none,
161 stub,
162 got,
163}) = .{},
164tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
165157
166locals_free_list: std.ArrayListUnmanaged(u32) = .{},158locals_free_list: std.ArrayListUnmanaged(u32) = .{},
167globals_free_list: std.ArrayListUnmanaged(u32) = .{},
168159
169dyld_stub_binder_index: ?u32 = null,160dyld_stub_binder_index: ?u32 = null,
170dyld_private_atom: ?*Atom = null,161dyld_private_atom: ?*Atom = null,
171stub_helper_preamble_atom: ?*Atom = null,162stub_helper_preamble_atom: ?*Atom = null,
172163
173mh_execute_header_sym_index: ?u32 = null,164strtab: StringTable(.link) = .{},
174dso_handle_sym_index: ?u32 = null,
175
176strtab: std.ArrayListUnmanaged(u8) = .{},
177strtab_dir: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
178165
179tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},166tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
180tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},167tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
181tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{},168tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
182169
183got_entries: std.ArrayListUnmanaged(Entry) = .{},170got_entries: std.ArrayListUnmanaged(Entry) = .{},
184got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},171got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
185got_entries_table: std.AutoArrayHashMapUnmanaged(Atom.Relocation.Target, u32) = .{},172got_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
186173
187stubs: std.ArrayListUnmanaged(*Atom) = .{},174stubs: std.ArrayListUnmanaged(Entry) = .{},
188stubs_free_list: std.ArrayListUnmanaged(u32) = .{},175stubs_free_list: std.ArrayListUnmanaged(u32) = .{},
189stubs_table: std.AutoArrayHashMapUnmanaged(u32, u32) = .{},176stubs_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
190177
191error_flags: File.ErrorFlags = File.ErrorFlags{},178error_flags: File.ErrorFlags = File.ErrorFlags{},
192179
...@@ -194,12 +181,6 @@ load_commands_dirty: bool = false,...@@ -194,12 +181,6 @@ load_commands_dirty: bool = false,
194sections_order_dirty: bool = false,181sections_order_dirty: bool = false,
195has_dices: bool = false,182has_dices: bool = false,
196has_stabs: bool = false,183has_stabs: bool = false,
197/// A helper var to indicate if we are at the start of the incremental updates, or
198/// already somewhere further along the update-and-run chain.
199/// TODO once we add opening a prelinked output binary from file, this will become
200/// obsolete as we will carry on where we left off.
201cold_start: bool = false,
202invalidate_relocs: bool = false,
203184
204section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},185section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
205186
...@@ -223,12 +204,10 @@ atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanage...@@ -223,12 +204,10 @@ atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanage
223/// Pointer to the last allocated atom204/// Pointer to the last allocated atom
224atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},205atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
225206
226/// List of atoms that are owned directly by the linker.207/// List of atoms that are either synthetic or map directly to the Zig source program.
227/// Currently these are only atoms that are the result of linking
228/// object files. Atoms which take part in incremental linking are
229/// at present owned by Module.Decl.
230/// TODO consolidate this.
231managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},208managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
209
210/// Table of atoms indexed by the symbol index.
232atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},211atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
233212
234/// Table of unnamed constants associated with a parent `Decl`.213/// Table of unnamed constants associated with a parent `Decl`.
...@@ -259,9 +238,10 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -259,9 +238,10 @@ unnamed_const_atoms: UnnamedConstTable = .{},
259decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},238decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
260239
261gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{},240gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{},
241gc_sections: std.AutoHashMapUnmanaged(MatchingSection, void) = .{},
262242
263const Entry = struct {243const Entry = struct {
264 target: Atom.Relocation.Target,244 target: SymbolWithLoc,
265 atom: *Atom,245 atom: *Atom,
266};246};
267247
...@@ -273,15 +253,12 @@ const PendingUpdate = union(enum) {...@@ -273,15 +253,12 @@ const PendingUpdate = union(enum) {
273 add_got_entry: u32,253 add_got_entry: u32,
274};254};
275255
276const SymbolWithLoc = struct {256pub const SymbolWithLoc = struct {
277 // Table where the symbol can be found.257 // Index into the respective symbol table.
278 where: enum {258 sym_index: u32,
279 global,259
280 undef,260 // null means it's a synthetic global.
281 },261 file: ?u32 = null,
282 where_index: u32,
283 local_sym_index: u32 = 0,
284 file: ?u16 = null, // null means Zig module
285};262};
286263
287/// When allocating, the ideal_capacity is calculated by264/// When allocating, the ideal_capacity is calculated by
...@@ -389,7 +366,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {...@@ -389,7 +366,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
389 .n_desc = 0,366 .n_desc = 0,
390 .n_value = 0,367 .n_value = 0,
391 });368 });
392 try self.strtab.append(allocator, 0);369 try self.strtab.buffer.append(allocator, 0);
393370
394 try self.populateMissingMetadata();371 try self.populateMissingMetadata();
395372
...@@ -524,7 +501,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -524,7 +501,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
524 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;501 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
525 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;502 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
526 const stack_size = self.base.options.stack_size_override orelse 0;503 const stack_size = self.base.options.stack_size_override orelse 0;
527 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
528504
529 const id_symlink_basename = "zld.id";505 const id_symlink_basename = "zld.id";
530 const cache_dir_handle = blk: {506 const cache_dir_handle = blk: {
...@@ -541,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -541,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
541 defer if (!self.base.options.disable_lld_caching) man.deinit();517 defer if (!self.base.options.disable_lld_caching) man.deinit();
542518
543 var digest: [Cache.hex_digest_len]u8 = undefined;519 var digest: [Cache.hex_digest_len]u8 = undefined;
544 var needs_full_relink = true;
545520
546 cache: {521 cache: {
547 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)522 if ((use_stage1 and self.base.options.disable_lld_caching) or self.base.options.cache_mode == .whole)
...@@ -610,14 +585,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -610,14 +585,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
610 return;585 return;
611 } else {586 } else {
612 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});587 log.debug("MachO Zld digest={s} match", .{std.fmt.fmtSliceHexLower(&digest)});
613 if (!self.cold_start) {
614 log.debug(" no need to relink objects", .{});
615 needs_full_relink = false;
616 } else {
617 log.debug(" TODO parse prelinked binary and continue linking where we left off", .{});
618 // TODO until such time however, perform a full relink of objects.
619 needs_full_relink = true;
620 }
621 }588 }
622 }589 }
623 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{590 log.debug("MachO Zld prev_digest={s} new_digest={s}", .{
...@@ -672,441 +639,373 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -672,441 +639,373 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
672 .n_desc = 0,639 .n_desc = 0,
673 .n_value = 0,640 .n_value = 0,
674 });641 });
675 try self.strtab.append(self.base.allocator, 0);642 try self.strtab.buffer.append(self.base.allocator, 0);
676 try self.populateMissingMetadata();643 try self.populateMissingMetadata();
677 }644 }
678645
679 var lib_not_found = false;646 var lib_not_found = false;
680 var framework_not_found = false;647 var framework_not_found = false;
681648
682 if (needs_full_relink) {649 // Positional arguments to the linker such as object files and static archives.
683 for (self.objects.items) |*object| {650 var positionals = std.ArrayList([]const u8).init(arena);
684 object.free(self.base.allocator, self);651 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
685 object.deinit(self.base.allocator);
686 }
687 self.objects.clearRetainingCapacity();
688652
689 for (self.archives.items) |*archive| {653 var must_link_archives = std.StringArrayHashMap(void).init(arena);
690 archive.deinit(self.base.allocator);654 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
691 }
692 self.archives.clearRetainingCapacity();
693655
694 for (self.dylibs.items) |*dylib| {656 for (self.base.options.objects) |obj| {
695 dylib.deinit(self.base.allocator);657 if (must_link_archives.contains(obj.path)) continue;
696 }658 if (obj.must_link) {
697 self.dylibs.clearRetainingCapacity();659 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
698 self.dylibs_map.clearRetainingCapacity();660 } else {
699 self.referenced_dylibs.clearRetainingCapacity();661 _ = positionals.appendAssumeCapacity(obj.path);
700
701 {
702 var to_remove = std.ArrayList(u32).init(self.base.allocator);
703 defer to_remove.deinit();
704 var it = self.symbol_resolver.iterator();
705 while (it.next()) |entry| {
706 const key = entry.key_ptr.*;
707 const value = entry.value_ptr.*;
708 if (value.file != null) {
709 try to_remove.append(key);
710 }
711 }
712
713 for (to_remove.items) |key| {
714 if (self.symbol_resolver.fetchRemove(key)) |entry| {
715 const resolv = entry.value;
716 switch (resolv.where) {
717 .global => {
718 self.globals_free_list.append(self.base.allocator, resolv.where_index) catch {};
719 const sym = &self.globals.items[resolv.where_index];
720 sym.n_strx = 0;
721 sym.n_type = 0;
722 sym.n_value = 0;
723 },
724 .undef => {
725 const sym = &self.undefs.items[resolv.where_index];
726 sym.n_strx = 0;
727 sym.n_desc = 0;
728 },
729 }
730 if (self.got_entries_table.get(.{ .global = entry.key })) |i| {
731 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, i)) catch {};
732 self.got_entries.items[i] = .{ .target = .{ .local = 0 }, .atom = undefined };
733 _ = self.got_entries_table.swapRemove(.{ .global = entry.key });
734 }
735 if (self.stubs_table.get(entry.key)) |i| {
736 self.stubs_free_list.append(self.base.allocator, @intCast(u32, i)) catch {};
737 self.stubs.items[i] = undefined;
738 _ = self.stubs_table.swapRemove(entry.key);
739 }
740 }
741 }
742 }662 }
743 // Invalidate all relocs663 }
744 // TODO we only need to invalidate the backlinks to the relinked atoms from
745 // the relocatable object files.
746 self.invalidate_relocs = true;
747
748 // Positional arguments to the linker such as object files and static archives.
749 var positionals = std.ArrayList([]const u8).init(arena);
750 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
751664
752 var must_link_archives = std.StringArrayHashMap(void).init(arena);665 for (comp.c_object_table.keys()) |key| {
753 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);666 try positionals.append(key.status.success.object_path);
667 }
754668
755 for (self.base.options.objects) |obj| {669 if (module_obj_path) |p| {
756 if (must_link_archives.contains(obj.path)) continue;670 try positionals.append(p);
757 if (obj.must_link) {671 }
758 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
759 } else {
760 _ = positionals.appendAssumeCapacity(obj.path);
761 }
762 }
763672
764 for (comp.c_object_table.keys()) |key| {673 if (comp.compiler_rt_lib) |lib| {
765 try positionals.append(key.status.success.object_path);674 try positionals.append(lib.full_object_path);
766 }675 }
767676
768 if (module_obj_path) |p| {677 // libc++ dep
769 try positionals.append(p);678 if (self.base.options.link_libcpp) {
770 }679 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
680 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
681 }
771682
772 if (comp.compiler_rt_lib) |lib| {683 // Shared and static libraries passed via `-l` flag.
773 try positionals.append(lib.full_object_path);684 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
774 }
775685
776 // libc++ dep686 const system_lib_names = self.base.options.system_libs.keys();
777 if (self.base.options.link_libcpp) {687 for (system_lib_names) |system_lib_name| {
778 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);688 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
779 try positionals.append(comp.libcxx_static_lib.?.full_object_path);689 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
690 // case we want to avoid prepending "-l".
691 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
692 try positionals.append(system_lib_name);
693 continue;
780 }694 }
781695
782 // Shared and static libraries passed via `-l` flag.696 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
783 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);697 try candidate_libs.put(system_lib_name, .{
784698 .needed = system_lib_info.needed,
785 const system_lib_names = self.base.options.system_libs.keys();699 .weak = system_lib_info.weak,
786 for (system_lib_names) |system_lib_name| {700 });
787 // By this time, we depend on these libs being dynamically linked libraries and not static libraries701 }
788 // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
789 // case we want to avoid prepending "-l".
790 if (Compilation.classifyFileExt(system_lib_name) == .shared_library) {
791 try positionals.append(system_lib_name);
792 continue;
793 }
794
795 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
796 try candidate_libs.put(system_lib_name, .{
797 .needed = system_lib_info.needed,
798 .weak = system_lib_info.weak,
799 });
800 }
801702
802 var lib_dirs = std.ArrayList([]const u8).init(arena);703 var lib_dirs = std.ArrayList([]const u8).init(arena);
803 for (self.base.options.lib_dirs) |dir| {704 for (self.base.options.lib_dirs) |dir| {
804 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {705 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
805 try lib_dirs.append(search_dir);706 try lib_dirs.append(search_dir);
806 } else {707 } else {
807 log.warn("directory not found for '-L{s}'", .{dir});708 log.warn("directory not found for '-L{s}'", .{dir});
808 }
809 }709 }
710 }
810711
811 var libs = std.StringArrayHashMap(SystemLib).init(arena);712 var libs = std.StringArrayHashMap(SystemLib).init(arena);
812713
813 // Assume ld64 default -search_paths_first if no strategy specified.714 // Assume ld64 default -search_paths_first if no strategy specified.
814 const search_strategy = self.base.options.search_strategy orelse .paths_first;715 const search_strategy = self.base.options.search_strategy orelse .paths_first;
815 outer: for (candidate_libs.keys()) |lib_name| {716 outer: for (candidate_libs.keys()) |lib_name| {
816 switch (search_strategy) {717 switch (search_strategy) {
817 .paths_first => {718 .paths_first => {
818 // Look in each directory for a dylib (stub first), and then for archive719 // Look in each directory for a dylib (stub first), and then for archive
819 for (lib_dirs.items) |dir| {720 for (lib_dirs.items) |dir| {
820 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {721 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
821 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {722 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
822 try libs.put(full_path, candidate_libs.get(lib_name).?);723 try libs.put(full_path, candidate_libs.get(lib_name).?);
823 continue :outer;724 continue :outer;
824 }
825 }725 }
826 } else {
827 log.warn("library not found for '-l{s}'", .{lib_name});
828 lib_not_found = true;
829 }726 }
830 },727 } else {
831 .dylibs_first => {728 log.warn("library not found for '-l{s}'", .{lib_name});
832 // First, look for a dylib in each search dir729 lib_not_found = true;
833 for (lib_dirs.items) |dir| {730 }
834 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {731 },
835 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {732 .dylibs_first => {
836 try libs.put(full_path, candidate_libs.get(lib_name).?);733 // First, look for a dylib in each search dir
837 continue :outer;734 for (lib_dirs.items) |dir| {
838 }735 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
839 }736 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
840 } else for (lib_dirs.items) |dir| {
841 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
842 try libs.put(full_path, candidate_libs.get(lib_name).?);737 try libs.put(full_path, candidate_libs.get(lib_name).?);
843 } else {738 continue :outer;
844 log.warn("library not found for '-l{s}'", .{lib_name});
845 lib_not_found = true;
846 }739 }
847 }740 }
848 },741 } else for (lib_dirs.items) |dir| {
849 }742 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
743 try libs.put(full_path, candidate_libs.get(lib_name).?);
744 } else {
745 log.warn("library not found for '-l{s}'", .{lib_name});
746 lib_not_found = true;
747 }
748 }
749 },
850 }750 }
751 }
851752
852 if (lib_not_found) {753 if (lib_not_found) {
853 log.warn("Library search paths:", .{});754 log.warn("Library search paths:", .{});
854 for (lib_dirs.items) |dir| {755 for (lib_dirs.items) |dir| {
855 log.warn(" {s}", .{dir});756 log.warn(" {s}", .{dir});
856 }
857 }757 }
758 }
858759
859 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.760 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
860 var libsystem_available = false;761 var libsystem_available = false;
861 if (self.base.options.sysroot != null) blk: {762 if (self.base.options.sysroot != null) blk: {
862 // Try stub file first. If we hit it, then we're done as the stub file763 // Try stub file first. If we hit it, then we're done as the stub file
863 // re-exports every single symbol definition.764 // re-exports every single symbol definition.
864 for (lib_dirs.items) |dir| {765 for (lib_dirs.items) |dir| {
865 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {766 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
866 try libs.put(full_path, .{ .needed = true });767 try libs.put(full_path, .{ .needed = true });
768 libsystem_available = true;
769 break :blk;
770 }
771 }
772 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
773 // doesn't export libc.dylib which we'll need to resolve subsequently also.
774 for (lib_dirs.items) |dir| {
775 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
776 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
777 try libs.put(libsystem_path, .{ .needed = true });
778 try libs.put(libc_path, .{ .needed = true });
867 libsystem_available = true;779 libsystem_available = true;
868 break :blk;780 break :blk;
869 }781 }
870 }782 }
871 // If we didn't hit the stub file, try .dylib next. However, libSystem.dylib
872 // doesn't export libc.dylib which we'll need to resolve subsequently also.
873 for (lib_dirs.items) |dir| {
874 if (try resolveLib(arena, dir, "System", ".dylib")) |libsystem_path| {
875 if (try resolveLib(arena, dir, "c", ".dylib")) |libc_path| {
876 try libs.put(libsystem_path, .{ .needed = true });
877 try libs.put(libc_path, .{ .needed = true });
878 libsystem_available = true;
879 break :blk;
880 }
881 }
882 }
883 }
884 if (!libsystem_available) {
885 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
886 self.base.options.target.os.version_range.semver.min.major,
887 });
888 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
889 "libc", "darwin", libsystem_name,
890 });
891 try libs.put(full_path, .{ .needed = true });
892 }783 }
784 }
785 if (!libsystem_available) {
786 const libsystem_name = try std.fmt.allocPrint(arena, "libSystem.{d}.tbd", .{
787 self.base.options.target.os.version_range.semver.min.major,
788 });
789 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
790 "libc", "darwin", libsystem_name,
791 });
792 try libs.put(full_path, .{ .needed = true });
793 }
893794
894 // frameworks795 // frameworks
895 var framework_dirs = std.ArrayList([]const u8).init(arena);796 var framework_dirs = std.ArrayList([]const u8).init(arena);
896 for (self.base.options.framework_dirs) |dir| {797 for (self.base.options.framework_dirs) |dir| {
897 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {798 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
898 try framework_dirs.append(search_dir);799 try framework_dirs.append(search_dir);
899 } else {800 } else {
900 log.warn("directory not found for '-F{s}'", .{dir});801 log.warn("directory not found for '-F{s}'", .{dir});
901 }
902 }802 }
803 }
903804
904 outer: for (self.base.options.frameworks.keys()) |f_name| {805 outer: for (self.base.options.frameworks.keys()) |f_name| {
905 for (framework_dirs.items) |dir| {806 for (framework_dirs.items) |dir| {
906 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {807 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
907 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {808 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
908 const info = self.base.options.frameworks.get(f_name).?;809 const info = self.base.options.frameworks.get(f_name).?;
909 try libs.put(full_path, .{810 try libs.put(full_path, .{
910 .needed = info.needed,811 .needed = info.needed,
911 .weak = info.weak,812 .weak = info.weak,
912 });813 });
913 continue :outer;814 continue :outer;
914 }
915 }815 }
916 } else {
917 log.warn("framework not found for '-framework {s}'", .{f_name});
918 framework_not_found = true;
919 }816 }
817 } else {
818 log.warn("framework not found for '-framework {s}'", .{f_name});
819 framework_not_found = true;
920 }820 }
821 }
921822
922 if (framework_not_found) {823 if (framework_not_found) {
923 log.warn("Framework search paths:", .{});824 log.warn("Framework search paths:", .{});
924 for (framework_dirs.items) |dir| {825 for (framework_dirs.items) |dir| {
925 log.warn(" {s}", .{dir});826 log.warn(" {s}", .{dir});
926 }
927 }827 }
828 }
928829
929 // rpaths830 // rpaths
930 var rpath_table = std.StringArrayHashMap(void).init(arena);831 var rpath_table = std.StringArrayHashMap(void).init(arena);
931 for (self.base.options.rpath_list) |rpath| {832 for (self.base.options.rpath_list) |rpath| {
932 if (rpath_table.contains(rpath)) continue;833 if (rpath_table.contains(rpath)) continue;
933 const cmdsize = @intCast(u32, mem.alignForwardGeneric(834 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
934 u64,835 u64,
935 @sizeOf(macho.rpath_command) + rpath.len + 1,836 @sizeOf(macho.rpath_command) + rpath.len + 1,
936 @sizeOf(u64),837 @sizeOf(u64),
937 ));838 ));
938 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{839 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
939 .cmdsize = cmdsize,840 .cmdsize = cmdsize,
940 .path = @sizeOf(macho.rpath_command),841 .path = @sizeOf(macho.rpath_command),
941 });842 });
942 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);843 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
943 mem.set(u8, rpath_cmd.data, 0);844 mem.set(u8, rpath_cmd.data, 0);
944 mem.copy(u8, rpath_cmd.data, rpath);845 mem.copy(u8, rpath_cmd.data, rpath);
945 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });846 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });
946 try rpath_table.putNoClobber(rpath, {});847 try rpath_table.putNoClobber(rpath, {});
947 self.load_commands_dirty = true;848 self.load_commands_dirty = true;
948 }849 }
949850
950 // code signature and entitlements851 // code signature and entitlements
951 if (self.base.options.entitlements) |path| {852 if (self.base.options.entitlements) |path| {
952 if (self.code_signature) |*csig| {853 if (self.code_signature) |*csig| {
953 try csig.addEntitlements(self.base.allocator, path);854 try csig.addEntitlements(self.base.allocator, path);
954 csig.code_directory.ident = self.base.options.emit.?.sub_path;855 csig.code_directory.ident = self.base.options.emit.?.sub_path;
955 } else {856 } else {
956 var csig = CodeSignature.init(self.page_size);857 var csig = CodeSignature.init(self.page_size);
957 try csig.addEntitlements(self.base.allocator, path);858 try csig.addEntitlements(self.base.allocator, path);
958 csig.code_directory.ident = self.base.options.emit.?.sub_path;859 csig.code_directory.ident = self.base.options.emit.?.sub_path;
959 self.code_signature = csig;860 self.code_signature = csig;
960 }
961 }861 }
862 }
962863
963 if (self.base.options.verbose_link) {864 if (self.base.options.verbose_link) {
964 var argv = std.ArrayList([]const u8).init(arena);865 var argv = std.ArrayList([]const u8).init(arena);
965
966 try argv.append("zig");
967 try argv.append("ld");
968
969 if (is_exe_or_dyn_lib) {
970 try argv.append("-dynamic");
971 }
972
973 if (is_dyn_lib) {
974 try argv.append("-dylib");
975866
976 if (self.base.options.install_name) |install_name| {867 try argv.append("zig");
977 try argv.append("-install_name");868 try argv.append("ld");
978 try argv.append(install_name);
979 }
980 }
981869
982 if (self.base.options.sysroot) |syslibroot| {870 if (is_exe_or_dyn_lib) {
983 try argv.append("-syslibroot");871 try argv.append("-dynamic");
984 try argv.append(syslibroot);872 }
985 }
986873
987 for (rpath_table.keys()) |rpath| {874 if (is_dyn_lib) {
988 try argv.append("-rpath");875 try argv.append("-dylib");
989 try argv.append(rpath);
990 }
991876
992 if (self.base.options.pagezero_size) |pagezero_size| {877 if (self.base.options.install_name) |install_name| {
993 try argv.append("-pagezero_size");878 try argv.append("-install_name");
994 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));879 try argv.append(install_name);
995 }880 }
881 }
996882
997 if (self.base.options.search_strategy) |strat| switch (strat) {883 if (self.base.options.sysroot) |syslibroot| {
998 .paths_first => try argv.append("-search_paths_first"),884 try argv.append("-syslibroot");
999 .dylibs_first => try argv.append("-search_dylibs_first"),885 try argv.append(syslibroot);
1000 };886 }
1001887
1002 if (self.base.options.headerpad_size) |headerpad_size| {888 for (rpath_table.keys()) |rpath| {
1003 try argv.append("-headerpad_size");889 try argv.append("-rpath");
1004 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));890 try argv.append(rpath);
1005 }891 }
1006892
1007 if (self.base.options.headerpad_max_install_names) {893 if (self.base.options.pagezero_size) |pagezero_size| {
1008 try argv.append("-headerpad_max_install_names");894 try argv.append("-pagezero_size");
1009 }895 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
896 }
1010897
1011 if (self.base.options.gc_sections) |is_set| {898 if (self.base.options.search_strategy) |strat| switch (strat) {
1012 if (is_set) {899 .paths_first => try argv.append("-search_paths_first"),
1013 try argv.append("-dead_strip");900 .dylibs_first => try argv.append("-search_dylibs_first"),
1014 }901 };
1015 }
1016902
1017 if (self.base.options.dead_strip_dylibs) {903 if (self.base.options.headerpad_size) |headerpad_size| {
1018 try argv.append("-dead_strip_dylibs");904 try argv.append("-headerpad_size");
1019 }905 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
906 }
1020907
1021 if (self.base.options.entry) |entry| {908 if (self.base.options.headerpad_max_install_names) {
1022 try argv.append("-e");909 try argv.append("-headerpad_max_install_names");
1023 try argv.append(entry);910 }
1024 }
1025911
1026 for (self.base.options.objects) |obj| {912 if (self.base.options.gc_sections) |is_set| {
1027 try argv.append(obj.path);913 if (is_set) {
914 try argv.append("-dead_strip");
1028 }915 }
916 }
1029917
1030 for (comp.c_object_table.keys()) |key| {918 if (self.base.options.dead_strip_dylibs) {
1031 try argv.append(key.status.success.object_path);919 try argv.append("-dead_strip_dylibs");
1032 }920 }
1033921
1034 if (module_obj_path) |p| {922 if (self.base.options.entry) |entry| {
1035 try argv.append(p);923 try argv.append("-e");
1036 }924 try argv.append(entry);
925 }
1037926
1038 if (comp.compiler_rt_lib) |lib| {927 for (self.base.options.objects) |obj| {
1039 try argv.append(lib.full_object_path);928 try argv.append(obj.path);
1040 }929 }
1041930
1042 if (self.base.options.link_libcpp) {931 for (comp.c_object_table.keys()) |key| {
1043 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);932 try argv.append(key.status.success.object_path);
1044 try argv.append(comp.libcxx_static_lib.?.full_object_path);933 }
1045 }
1046934
1047 try argv.append("-o");935 if (module_obj_path) |p| {
1048 try argv.append(full_out_path);936 try argv.append(p);
937 }
1049938
1050 try argv.append("-lSystem");939 if (comp.compiler_rt_lib) |lib| {
1051 try argv.append("-lc");940 try argv.append(lib.full_object_path);
941 }
1052942
1053 for (self.base.options.system_libs.keys()) |l_name| {943 if (self.base.options.link_libcpp) {
1054 const info = self.base.options.system_libs.get(l_name).?;944 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1055 const arg = if (info.needed)945 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1056 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})946 }
1057 else if (info.weak)
1058 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
1059 else
1060 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
1061 try argv.append(arg);
1062 }
1063947
1064 for (self.base.options.lib_dirs) |lib_dir| {948 try argv.append("-o");
1065 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));949 try argv.append(full_out_path);
1066 }950
951 try argv.append("-lSystem");
952 try argv.append("-lc");
953
954 for (self.base.options.system_libs.keys()) |l_name| {
955 const info = self.base.options.system_libs.get(l_name).?;
956 const arg = if (info.needed)
957 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
958 else if (info.weak)
959 try std.fmt.allocPrint(arena, "-weak-l{s}", .{l_name})
960 else
961 try std.fmt.allocPrint(arena, "-l{s}", .{l_name});
962 try argv.append(arg);
963 }
1067964
1068 for (self.base.options.frameworks.keys()) |framework| {965 for (self.base.options.lib_dirs) |lib_dir| {
1069 const info = self.base.options.frameworks.get(framework).?;966 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1070 const arg = if (info.needed)967 }
1071 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
1072 else if (info.weak)
1073 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
1074 else
1075 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
1076 try argv.append(arg);
1077 }
1078968
1079 for (self.base.options.framework_dirs) |framework_dir| {969 for (self.base.options.frameworks.keys()) |framework| {
1080 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));970 const info = self.base.options.frameworks.get(framework).?;
1081 }971 const arg = if (info.needed)
972 try std.fmt.allocPrint(arena, "-needed_framework {s}", .{framework})
973 else if (info.weak)
974 try std.fmt.allocPrint(arena, "-weak_framework {s}", .{framework})
975 else
976 try std.fmt.allocPrint(arena, "-framework {s}", .{framework});
977 try argv.append(arg);
978 }
1082979
1083 if (allow_undef) {980 for (self.base.options.framework_dirs) |framework_dir| {
1084 try argv.append("-undefined");981 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1085 try argv.append("dynamic_lookup");982 }
1086 }
1087983
1088 for (must_link_archives.keys()) |lib| {984 if (is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false)) {
1089 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));985 try argv.append("-undefined");
1090 }986 try argv.append("dynamic_lookup");
987 }
1091988
1092 Compilation.dump_argv(argv.items);989 for (must_link_archives.keys()) |lib| {
990 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
1093 }991 }
1094992
1095 var dependent_libs = std.fifo.LinearFifo(struct {993 Compilation.dump_argv(argv.items);
1096 id: Dylib.Id,
1097 parent: u16,
1098 }, .Dynamic).init(self.base.allocator);
1099 defer dependent_libs.deinit();
1100 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1101 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1102 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1103 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1104 }994 }
1105995
996 var dependent_libs = std.fifo.LinearFifo(struct {
997 id: Dylib.Id,
998 parent: u16,
999 }, .Dynamic).init(self.base.allocator);
1000 defer dependent_libs.deinit();
1001 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
1002 try self.parseAndForceLoadStaticArchives(must_link_archives.keys());
1003 try self.parseLibs(libs.keys(), libs.values(), self.base.options.sysroot, &dependent_libs);
1004 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
1005
1106 try self.createMhExecuteHeaderSymbol();1006 try self.createMhExecuteHeaderSymbol();
1107 for (self.objects.items) |*object, object_id| {1007 for (self.objects.items) |*object, object_id| {
1108 if (object.analyzed) continue;1008 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
1109 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1110 }1009 }
11111010
1112 try self.resolveSymbolsInArchives();1011 try self.resolveSymbolsInArchives();
...@@ -1116,44 +1015,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1116,44 +1015,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1116 try self.resolveSymbolsInDylibs();1015 try self.resolveSymbolsInDylibs();
1117 try self.createDsoHandleSymbol();1016 try self.createDsoHandleSymbol();
1118 try self.addCodeSignatureLC();1017 try self.addCodeSignatureLC();
1018 try self.resolveSymbolsAtLoading();
11191019
1120 {
1121 var next_sym: usize = 0;
1122 while (next_sym < self.unresolved.count()) {
1123 const sym = &self.undefs.items[self.unresolved.keys()[next_sym]];
1124 const sym_name = self.getString(sym.n_strx);
1125 const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable;
1126
1127 if (sym.discarded()) {
1128 sym.* = .{
1129 .n_strx = 0,
1130 .n_type = macho.N_UNDF,
1131 .n_sect = 0,
1132 .n_desc = 0,
1133 .n_value = 0,
1134 };
1135 _ = self.unresolved.swapRemove(resolv.where_index);
1136 continue;
1137 } else if (allow_undef) {
1138 const n_desc = @bitCast(
1139 u16,
1140 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
1141 );
1142 // TODO allow_shlib_undefined is an ELF flag so figure out macOS specific flags too.
1143 sym.n_type = macho.N_EXT;
1144 sym.n_desc = n_desc;
1145 _ = self.unresolved.swapRemove(resolv.where_index);
1146 continue;
1147 }
1148
1149 log.err("undefined reference to symbol '{s}'", .{sym_name});
1150 if (resolv.file) |file| {
1151 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
1152 }
1153
1154 next_sym += 1;
1155 }
1156 }
1157 if (self.unresolved.count() > 0) {1020 if (self.unresolved.count() > 0) {
1158 return error.UndefinedSymbolReference;1021 return error.UndefinedSymbolReference;
1159 }1022 }
...@@ -1165,35 +1028,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1165,35 +1028,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1165 }1028 }
11661029
1167 try self.createTentativeDefAtoms();1030 try self.createTentativeDefAtoms();
1168 try self.parseObjectsIntoAtoms();
11691031
1170 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;1032 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
1171 if (use_llvm or use_stage1) {1033 if (use_llvm or use_stage1) {
1172 self.logAtoms();1034 for (self.objects.items) |*object, object_id| {
1035 try object.splitIntoAtomsWhole(self, @intCast(u32, object_id));
1036 }
1037
1173 try self.gcAtoms();1038 try self.gcAtoms();
1174 try self.pruneAndSortSections();1039 try self.pruneAndSortSections();
1175 try self.allocateSegments();1040 try self.allocateSegments();
1176 try self.allocateLocals();1041 try self.allocateSymbols();
1042 } else {
1043 // TODO incremental mode: parsing objects into atoms
1177 }1044 }
11781045
1179 try self.allocateSpecialSymbols();1046 try self.allocateSpecialSymbols();
1180 try self.allocateGlobals();
11811047
1182 if (build_options.enable_logging or true) {1048 if (build_options.enable_logging) {
1183 self.logSymtab();1049 self.logSymtab();
1184 self.logSectionOrdinals();1050 self.logSectionOrdinals();
1185 self.logAtoms();1051 self.logAtoms();
1186 }1052 }
11871053
1188 if (use_llvm or use_stage1) {1054 if (use_llvm or use_stage1) {
1189 try self.writeAllAtoms();1055 try self.writeAtomsWhole();
1190 } else {1056 } else {
1191 try self.writeAtoms();1057 // try self.writeAtoms();
1192 }1058 }
11931059
1194 if (self.rustc_section_index) |id| {1060 if (self.rustc_section_index) |id| {
1195 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;1061 const sect = self.getSectionPtr(.{
1196 const sect = &seg.sections.items[id];1062 .seg = self.data_segment_cmd_index.?,
1063 .sect = id,
1064 });
1197 sect.size = self.rustc_section_size;1065 sect.size = self.rustc_section_size;
1198 }1066 }
11991067
...@@ -1234,10 +1102,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1234,10 +1102,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1234 try self.writeCodeSignature(csig); // code signing always comes last1102 try self.writeCodeSignature(csig); // code signing always comes last
1235 }1103 }
12361104
1237 if (build_options.enable_link_snapshots) {1105 // if (build_options.enable_link_snapshots) {
1238 if (self.base.options.enable_link_snapshots)1106 // if (self.base.options.enable_link_snapshots)
1239 try self.snapshotState();1107 // try self.snapshotState();
1240 }1108 // }
1241 }1109 }
12421110
1243 cache: {1111 cache: {
...@@ -1256,8 +1124,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -1256,8 +1124,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
1256 // other processes clobbering it.1124 // other processes clobbering it.
1257 self.base.lock = man.toOwnedLock();1125 self.base.lock = man.toOwnedLock();
1258 }1126 }
1259
1260 self.cold_start = false;
1261}1127}
12621128
1263fn resolveSearchDir(1129fn resolveSearchDir(
...@@ -1521,7 +1387,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1521,7 +1387,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1521 .syslibroot = syslibroot,1387 .syslibroot = syslibroot,
1522 })) continue;1388 })) continue;
15231389
1524 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});1390 log.debug("unknown filetype for positional input file: '{s}'", .{file_name});
1525 }1391 }
1526}1392}
15271393
...@@ -1536,7 +1402,7 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi...@@ -1536,7 +1402,7 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
1536 log.debug("parsing and force loading static archive '{s}'", .{full_path});1402 log.debug("parsing and force loading static archive '{s}'", .{full_path});
15371403
1538 if (try self.parseArchive(full_path, true)) continue;1404 if (try self.parseArchive(full_path, true)) continue;
1539 log.warn("unknown filetype: expected static archive: '{s}'", .{file_name});1405 log.debug("unknown filetype: expected static archive: '{s}'", .{file_name});
1540 }1406 }
1541}1407}
15421408
...@@ -1557,7 +1423,7 @@ fn parseLibs(...@@ -1557,7 +1423,7 @@ fn parseLibs(
1557 })) continue;1423 })) continue;
1558 if (try self.parseArchive(lib, false)) continue;1424 if (try self.parseArchive(lib, false)) continue;
15591425
1560 log.warn("unknown filetype for a library: '{s}'", .{lib});1426 log.debug("unknown filetype for a library: '{s}'", .{lib});
1561 }1427 }
1562}1428}
15631429
...@@ -1601,7 +1467,7 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1601,7 +1467,7 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1601 });1467 });
1602 if (did_parse_successfully) break;1468 if (did_parse_successfully) break;
1603 } else {1469 } else {
1604 log.warn("unable to resolve dependency {s}", .{dep_id.id.name});1470 log.debug("unable to resolve dependency {s}", .{dep_id.id.name});
1605 }1471 }
1606 }1472 }
1607}1473}
...@@ -2172,34 +2038,31 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio...@@ -2172,34 +2038,31 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
2172 return res;2038 return res;
2173}2039}
21742040
2175pub fn createEmptyAtom(self: *MachO, local_sym_index: u32, size: u64, alignment: u32) !*Atom {2041pub fn createEmptyAtom(gpa: Allocator, sym_index: u32, size: u64, alignment: u32) !*Atom {
2176 const size_usize = math.cast(usize, size) orelse return error.Overflow;2042 const size_usize = math.cast(usize, size) orelse return error.Overflow;
2177 const atom = try self.base.allocator.create(Atom);2043 const atom = try gpa.create(Atom);
2178 errdefer self.base.allocator.destroy(atom);2044 errdefer gpa.destroy(atom);
2179 atom.* = Atom.empty;2045 atom.* = Atom.empty;
2180 atom.local_sym_index = local_sym_index;2046 atom.sym_index = sym_index;
2181 atom.size = size;2047 atom.size = size;
2182 atom.alignment = alignment;2048 atom.alignment = alignment;
21832049
2184 try atom.code.resize(self.base.allocator, size_usize);2050 try atom.code.resize(gpa, size_usize);
2185 mem.set(u8, atom.code.items, 0);2051 mem.set(u8, atom.code.items, 0);
21862052
2187 try self.atom_by_index_table.putNoClobber(self.base.allocator, local_sym_index, atom);
2188 try self.managed_atoms.append(self.base.allocator, atom);
2189 return atom;2053 return atom;
2190}2054}
21912055
2192pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {2056pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2193 const seg = self.load_commands.items[match.seg].segment;2057 const sect = self.getSection(match);
2194 const sect = seg.sections.items[match.sect];2058 const sym = atom.getSymbol(self);
2195 const sym = self.locals.items[atom.local_sym_index];
2196 const file_offset = sect.offset + sym.n_value - sect.addr;2059 const file_offset = sect.offset + sym.n_value - sect.addr;
2197 try atom.resolveRelocs(self);2060 try atom.resolveRelocs(self);
2198 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ self.getString(sym.n_strx), file_offset });2061 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
2199 try self.base.file.?.pwriteAll(atom.code.items, file_offset);2062 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
2200}2063}
22012064
2202fn allocateLocals(self: *MachO) !void {2065fn allocateSymbols(self: *MachO) !void {
2203 var it = self.atoms.iterator();2066 var it = self.atoms.iterator();
2204 while (it.next()) |entry| {2067 while (it.next()) |entry| {
2205 const match = entry.key_ptr.*;2068 const match = entry.key_ptr.*;
...@@ -2209,30 +2072,25 @@ fn allocateLocals(self: *MachO) !void {...@@ -2209,30 +2072,25 @@ fn allocateLocals(self: *MachO) !void {
2209 atom = prev;2072 atom = prev;
2210 }2073 }
22112074
2212 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2075 const n_sect = self.getSectionOrdinal(match);
2213 const seg = self.load_commands.items[match.seg].segment;2076 const sect = self.getSection(match);
2214 const sect = seg.sections.items[match.sect];
2215 var base_vaddr = sect.addr;2077 var base_vaddr = sect.addr;
22162078
2217 log.debug("allocating local symbols in {s},{s}", .{ sect.segName(), sect.sectName() });2079 log.debug("allocating local symbols in sect({d}, '{s},{s}')", .{ n_sect, sect.segName(), sect.sectName() });
22182080
2219 while (true) {2081 while (true) {
2220 const alignment = try math.powi(u32, 2, atom.alignment);2082 const alignment = try math.powi(u32, 2, atom.alignment);
2221 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);2083 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
22222084
2223 const sym = &self.locals.items[atom.local_sym_index];2085 const sym = atom.getSymbolPtr(self);
2224 sym.n_value = base_vaddr;2086 sym.n_value = base_vaddr;
2225 sym.n_sect = n_sect;2087 sym.n_sect = n_sect;
22262088
2227 log.debug(" {d}: {s} allocated at 0x{x}", .{2089 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
2228 atom.local_sym_index,
2229 self.getString(sym.n_strx),
2230 base_vaddr,
2231 });
22322090
2233 // Update each symbol contained within the atom2091 // Update each symbol contained within the atom
2234 for (atom.contained.items) |sym_at_off| {2092 for (atom.contained.items) |sym_at_off| {
2235 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];2093 const contained_sym = self.getSymbolPtr(.{ .sym_index = sym_at_off.sym_index, .file = atom.file });
2236 contained_sym.n_value = base_vaddr + sym_at_off.offset;2094 contained_sym.n_value = base_vaddr + sym_at_off.offset;
2237 contained_sym.n_sect = n_sect;2095 contained_sym.n_sect = n_sect;
2238 }2096 }
...@@ -2250,11 +2108,11 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void...@@ -2250,11 +2108,11 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
2250 var atom = self.atoms.get(match) orelse return;2108 var atom = self.atoms.get(match) orelse return;
22512109
2252 while (true) {2110 while (true) {
2253 const atom_sym = &self.locals.items[atom.local_sym_index];2111 const atom_sym = &self.locals.items[atom.sym_index];
2254 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);2112 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
22552113
2256 for (atom.contained.items) |sym_at_off| {2114 for (atom.contained.items) |sym_at_off| {
2257 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];2115 const contained_sym = &self.locals.items[sym_at_off.sym_index];
2258 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);2116 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
2259 }2117 }
22602118
...@@ -2265,53 +2123,30 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void...@@ -2265,53 +2123,30 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
2265}2123}
22662124
2267fn allocateSpecialSymbols(self: *MachO) !void {2125fn allocateSpecialSymbols(self: *MachO) !void {
2268 for (&[_]?u32{2126 for (&[_][]const u8{
2269 self.mh_execute_header_sym_index,2127 "___dso_handle",
2270 self.dso_handle_sym_index,2128 "__mh_execute_header",
2271 }) |maybe_sym_index| {2129 }) |name| {
2272 const sym_index = maybe_sym_index orelse continue;2130 const global = self.globals.get(name) orelse continue;
2273 const sym = &self.locals.items[sym_index];2131 const sym = self.getSymbolPtr(global);
2274 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;2132 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
2275 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{2133 sym.n_sect = self.getSectionOrdinal(.{
2276 .seg = self.text_segment_cmd_index.?,2134 .seg = self.text_segment_cmd_index.?,
2277 .sect = 0,2135 .sect = 0,
2278 }).? + 1);2136 });
2279 sym.n_value = seg.inner.vmaddr;2137 sym.n_value = seg.inner.vmaddr;
22802138
2281 log.debug("allocating {s} at the start of {s}", .{2139 log.debug("allocating {s} at the start of {s}", .{
2282 self.getString(sym.n_strx),2140 name,
2283 seg.inner.segName(),2141 seg.inner.segName(),
2284 });2142 });
2285 }2143 }
2286}2144}
22872145
2288fn allocateGlobals(self: *MachO) !void {2146fn writeAtomsWhole(self: *MachO) !void {
2289 log.debug("allocating global symbols", .{});
2290
2291 var sym_it = self.symbol_resolver.valueIterator();
2292 while (sym_it.next()) |resolv| {
2293 if (resolv.where != .global) continue;
2294
2295 assert(resolv.local_sym_index != 0);
2296 const local_sym = self.locals.items[resolv.local_sym_index];
2297 const sym = &self.globals.items[resolv.where_index];
2298 sym.n_value = local_sym.n_value;
2299 sym.n_sect = local_sym.n_sect;
2300
2301 log.debug(" {d}: {s} allocated at 0x{x}", .{
2302 resolv.where_index,
2303 self.getString(sym.n_strx),
2304 local_sym.n_value,
2305 });
2306 }
2307}
2308
2309fn writeAllAtoms(self: *MachO) !void {
2310 var it = self.atoms.iterator();2147 var it = self.atoms.iterator();
2311 while (it.next()) |entry| {2148 while (it.next()) |entry| {
2312 const match = entry.key_ptr.*;2149 const sect = self.getSection(entry.key_ptr.*);
2313 const seg = self.load_commands.items[match.seg].segment;
2314 const sect = seg.sections.items[match.sect];
2315 var atom: *Atom = entry.value_ptr.*;2150 var atom: *Atom = entry.value_ptr.*;
23162151
2317 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;2152 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
...@@ -2327,20 +2162,28 @@ fn writeAllAtoms(self: *MachO) !void {...@@ -2327,20 +2162,28 @@ fn writeAllAtoms(self: *MachO) !void {
2327 }2162 }
23282163
2329 while (true) {2164 while (true) {
2330 const atom_sym = self.locals.items[atom.local_sym_index];2165 const this_sym = atom.getSymbol(self);
2331 const padding_size: usize = if (atom.next) |next| blk: {2166 const padding_size: usize = if (atom.next) |next| blk: {
2332 const next_sym = self.locals.items[next.local_sym_index];2167 const next_sym = next.getSymbol(self);
2333 const size = next_sym.n_value - (atom_sym.n_value + atom.size);2168 const size = next_sym.n_value - (this_sym.n_value + atom.size);
2334 break :blk math.cast(usize, size) orelse return error.Overflow;2169 break :blk math.cast(usize, size) orelse return error.Overflow;
2335 } else 0;2170 } else 0;
23362171
2337 log.debug(" (adding atom {s} to buffer: {})", .{ self.getString(atom_sym.n_strx), atom_sym });2172 log.debug(" (adding ATOM(%{d}, '{s}') from object({d}) to buffer)", .{
2173 atom.sym_index,
2174 atom.getName(self),
2175 atom.file,
2176 });
2177 if (padding_size > 0) {
2178 log.debug(" (with padding {x})", .{padding_size});
2179 }
23382180
2339 try atom.resolveRelocs(self);2181 try atom.resolveRelocs(self);
2340 buffer.appendSliceAssumeCapacity(atom.code.items);2182 buffer.appendSliceAssumeCapacity(atom.code.items);
23412183
2342 var i: usize = 0;2184 var i: usize = 0;
2343 while (i < padding_size) : (i += 1) {2185 while (i < padding_size) : (i += 1) {
2186 // TODO with NOPs
2344 buffer.appendAssumeCapacity(0);2187 buffer.appendAssumeCapacity(0);
2345 }2188 }
23462189
...@@ -2388,8 +2231,7 @@ fn writeAtoms(self: *MachO) !void {...@@ -2388,8 +2231,7 @@ fn writeAtoms(self: *MachO) !void {
2388 var it = self.atoms.iterator();2231 var it = self.atoms.iterator();
2389 while (it.next()) |entry| {2232 while (it.next()) |entry| {
2390 const match = entry.key_ptr.*;2233 const match = entry.key_ptr.*;
2391 const seg = self.load_commands.items[match.seg].segment;2234 const sect = self.getSection(match);
2392 const sect = seg.sections.items[match.sect];
2393 var atom: *Atom = entry.value_ptr.*;2235 var atom: *Atom = entry.value_ptr.*;
23942236
2395 // TODO handle zerofill in stage22237 // TODO handle zerofill in stage2
...@@ -2410,17 +2252,19 @@ fn writeAtoms(self: *MachO) !void {...@@ -2410,17 +2252,19 @@ fn writeAtoms(self: *MachO) !void {
2410 }2252 }
2411}2253}
24122254
2413pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {2255pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2414 const local_sym_index = @intCast(u32, self.locals.items.len);2256 const gpa = self.base.allocator;
2415 try self.locals.append(self.base.allocator, .{2257 const sym_index = @intCast(u32, self.locals.items.len);
2258 try self.locals.append(gpa, .{
2416 .n_strx = 0,2259 .n_strx = 0,
2417 .n_type = macho.N_SECT,2260 .n_type = macho.N_SECT,
2418 .n_sect = 0,2261 .n_sect = 0,
2419 .n_desc = 0,2262 .n_desc = 0,
2420 .n_value = 0,2263 .n_value = 0,
2421 });2264 });
2422 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2265
2423 try atom.relocs.append(self.base.allocator, .{2266 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2267 try atom.relocs.append(gpa, .{
2424 .offset = 0,2268 .offset = 0,
2425 .target = target,2269 .target = target,
2426 .addend = 0,2270 .addend = 0,
...@@ -2433,35 +2277,59 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {...@@ -2433,35 +2277,59 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2433 else => unreachable,2277 else => unreachable,
2434 },2278 },
2435 });2279 });
2436 switch (target) {2280
2437 .local => {2281 const target_sym = self.getSymbol(target);
2438 try atom.rebases.append(self.base.allocator, 0);2282 if (target_sym.undf()) {
2439 },2283 const global_index = @intCast(u32, self.globals.getIndex(self.getSymbolName(target)).?);
2440 .global => |n_strx| {2284 try atom.bindings.append(gpa, .{
2441 try atom.bindings.append(self.base.allocator, .{2285 .global_index = global_index,
2442 .n_strx = n_strx,2286 .offset = 0,
2443 .offset = 0,2287 });
2444 });2288 } else {
2445 },2289 try atom.rebases.append(gpa, 0);
2446 }2290 }
2291
2292 try self.managed_atoms.append(gpa, atom);
2293 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2294
2295 try self.allocateAtomCommon(atom, .{
2296 .seg = self.data_const_segment_cmd_index.?,
2297 .sect = self.got_section_index.?,
2298 });
2299
2447 return atom;2300 return atom;
2448}2301}
24492302
2450pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {2303pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2451 const local_sym_index = @intCast(u32, self.locals.items.len);2304 const gpa = self.base.allocator;
2452 try self.locals.append(self.base.allocator, .{2305 const sym_index = @intCast(u32, self.locals.items.len);
2306 try self.locals.append(gpa, .{
2453 .n_strx = 0,2307 .n_strx = 0,
2454 .n_type = macho.N_SECT,2308 .n_type = macho.N_SECT,
2455 .n_sect = 0,2309 .n_sect = 0,
2456 .n_desc = 0,2310 .n_desc = 0,
2457 .n_value = 0,2311 .n_value = 0,
2458 });2312 });
2459 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2313
2460 assert(target == .global);2314 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2461 try atom.bindings.append(self.base.allocator, .{2315 const target_sym = self.getSymbol(target);
2462 .n_strx = target.global,2316 assert(target_sym.undf());
2317 const global_index = @intCast(u32, self.globals.getIndex(self.getSymbolName(target)).?);
2318 try atom.bindings.append(gpa, .{
2319 .global_index = global_index,
2463 .offset = 0,2320 .offset = 0,
2464 });2321 });
2322
2323 try self.managed_atoms.append(gpa, atom);
2324 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2325
2326 const match = (try self.getMatchingSection(.{
2327 .segname = makeStaticString("__DATA"),
2328 .sectname = makeStaticString("__thread_ptrs"),
2329 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
2330 })).?;
2331 try self.allocateAtomCommon(atom, match);
2332
2465 return atom;2333 return atom;
2466}2334}
24672335
...@@ -2469,34 +2337,32 @@ fn createDyldPrivateAtom(self: *MachO) !void {...@@ -2469,34 +2337,32 @@ fn createDyldPrivateAtom(self: *MachO) !void {
2469 if (self.dyld_stub_binder_index == null) return;2337 if (self.dyld_stub_binder_index == null) return;
2470 if (self.dyld_private_atom != null) return;2338 if (self.dyld_private_atom != null) return;
24712339
2472 const local_sym_index = @intCast(u32, self.locals.items.len);2340 const gpa = self.base.allocator;
2473 const sym = try self.locals.addOne(self.base.allocator);2341 const sym_index = @intCast(u32, self.locals.items.len);
2474 sym.* = .{2342 try self.locals.append(gpa, .{
2475 .n_strx = 0,2343 .n_strx = 0,
2476 .n_type = macho.N_SECT,2344 .n_type = macho.N_SECT,
2477 .n_sect = 0,2345 .n_sect = 0,
2478 .n_desc = 0,2346 .n_desc = 0,
2479 .n_value = 0,2347 .n_value = 0,
2480 };2348 });
2481 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2349 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2482 self.dyld_private_atom = atom;2350 self.dyld_private_atom = atom;
2483 const match = MatchingSection{2351
2352 try self.allocateAtomCommon(atom, .{
2484 .seg = self.data_segment_cmd_index.?,2353 .seg = self.data_segment_cmd_index.?,
2485 .sect = self.data_section_index.?,2354 .sect = self.data_section_index.?,
2486 };2355 });
2487 if (self.needs_prealloc) {
2488 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
2489 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
2490 sym.n_value = vaddr;
2491 } else try self.addAtomToSection(atom, match);
24922356
2493 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2357 try self.managed_atoms.append(gpa, atom);
2358 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2494}2359}
24952360
2496fn createStubHelperPreambleAtom(self: *MachO) !void {2361fn createStubHelperPreambleAtom(self: *MachO) !void {
2497 if (self.dyld_stub_binder_index == null) return;2362 if (self.dyld_stub_binder_index == null) return;
2498 if (self.stub_helper_preamble_atom != null) return;2363 if (self.stub_helper_preamble_atom != null) return;
24992364
2365 const gpa = self.base.allocator;
2500 const arch = self.base.options.target.cpu.arch;2366 const arch = self.base.options.target.cpu.arch;
2501 const size: u64 = switch (arch) {2367 const size: u64 = switch (arch) {
2502 .x86_64 => 15,2368 .x86_64 => 15,
...@@ -2508,17 +2374,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2508,17 +2374,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2508 .aarch64 => 2,2374 .aarch64 => 2,
2509 else => unreachable,2375 else => unreachable,
2510 };2376 };
2511 const local_sym_index = @intCast(u32, self.locals.items.len);2377 const sym_index = @intCast(u32, self.locals.items.len);
2512 const sym = try self.locals.addOne(self.base.allocator);2378 try self.locals.append(gpa, .{
2513 sym.* = .{
2514 .n_strx = 0,2379 .n_strx = 0,
2515 .n_type = macho.N_SECT,2380 .n_type = macho.N_SECT,
2516 .n_sect = 0,2381 .n_sect = 0,
2517 .n_desc = 0,2382 .n_desc = 0,
2518 .n_value = 0,2383 .n_value = 0,
2519 };2384 });
2520 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);2385 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
2521 const dyld_private_sym_index = self.dyld_private_atom.?.local_sym_index;2386 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
2522 switch (arch) {2387 switch (arch) {
2523 .x86_64 => {2388 .x86_64 => {
2524 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);2389 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
...@@ -2528,7 +2393,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2528,7 +2393,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2528 atom.code.items[2] = 0x1d;2393 atom.code.items[2] = 0x1d;
2529 atom.relocs.appendAssumeCapacity(.{2394 atom.relocs.appendAssumeCapacity(.{
2530 .offset = 3,2395 .offset = 3,
2531 .target = .{ .local = dyld_private_sym_index },2396 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2532 .addend = 0,2397 .addend = 0,
2533 .subtractor = null,2398 .subtractor = null,
2534 .pcrel = true,2399 .pcrel = true,
...@@ -2543,7 +2408,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2543,7 +2408,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2543 atom.code.items[10] = 0x25;2408 atom.code.items[10] = 0x25;
2544 atom.relocs.appendAssumeCapacity(.{2409 atom.relocs.appendAssumeCapacity(.{
2545 .offset = 11,2410 .offset = 11,
2546 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2411 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2547 .addend = 0,2412 .addend = 0,
2548 .subtractor = null,2413 .subtractor = null,
2549 .pcrel = true,2414 .pcrel = true,
...@@ -2557,7 +2422,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2557,7 +2422,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2557 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());2422 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
2558 atom.relocs.appendAssumeCapacity(.{2423 atom.relocs.appendAssumeCapacity(.{
2559 .offset = 0,2424 .offset = 0,
2560 .target = .{ .local = dyld_private_sym_index },2425 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2561 .addend = 0,2426 .addend = 0,
2562 .subtractor = null,2427 .subtractor = null,
2563 .pcrel = true,2428 .pcrel = true,
...@@ -2568,7 +2433,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2568,7 +2433,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2568 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());2433 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
2569 atom.relocs.appendAssumeCapacity(.{2434 atom.relocs.appendAssumeCapacity(.{
2570 .offset = 4,2435 .offset = 4,
2571 .target = .{ .local = dyld_private_sym_index },2436 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
2572 .addend = 0,2437 .addend = 0,
2573 .subtractor = null,2438 .subtractor = null,
2574 .pcrel = false,2439 .pcrel = false,
...@@ -2586,7 +2451,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2586,7 +2451,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2586 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2451 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2587 atom.relocs.appendAssumeCapacity(.{2452 atom.relocs.appendAssumeCapacity(.{
2588 .offset = 12,2453 .offset = 12,
2589 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2454 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2590 .addend = 0,2455 .addend = 0,
2591 .subtractor = null,2456 .subtractor = null,
2592 .pcrel = true,2457 .pcrel = true,
...@@ -2601,7 +2466,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2601,7 +2466,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2601 ).toU32());2466 ).toU32());
2602 atom.relocs.appendAssumeCapacity(.{2467 atom.relocs.appendAssumeCapacity(.{
2603 .offset = 16,2468 .offset = 16,
2604 .target = .{ .global = self.undefs.items[self.dyld_stub_binder_index.?].n_strx },2469 .target = .{ .sym_index = self.dyld_stub_binder_index.?, .file = null },
2605 .addend = 0,2470 .addend = 0,
2606 .subtractor = null,2471 .subtractor = null,
2607 .pcrel = false,2472 .pcrel = false,
...@@ -2614,22 +2479,18 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -2614,22 +2479,18 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
2614 else => unreachable,2479 else => unreachable,
2615 }2480 }
2616 self.stub_helper_preamble_atom = atom;2481 self.stub_helper_preamble_atom = atom;
2617 const match = MatchingSection{2482
2483 try self.allocateAtomCommon(atom, .{
2618 .seg = self.text_segment_cmd_index.?,2484 .seg = self.text_segment_cmd_index.?,
2619 .sect = self.stub_helper_section_index.?,2485 .sect = self.stub_helper_section_index.?,
2620 };2486 });
2621
2622 if (self.needs_prealloc) {
2623 const alignment_pow_2 = try math.powi(u32, 2, atom.alignment);
2624 const vaddr = try self.allocateAtom(atom, atom.size, alignment_pow_2, match);
2625 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
2626 sym.n_value = vaddr;
2627 } else try self.addAtomToSection(atom, match);
26282487
2629 sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);2488 try self.managed_atoms.append(gpa, atom);
2489 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2630}2490}
26312491
2632pub fn createStubHelperAtom(self: *MachO) !*Atom {2492pub fn createStubHelperAtom(self: *MachO) !*Atom {
2493 const gpa = self.base.allocator;
2633 const arch = self.base.options.target.cpu.arch;2494 const arch = self.base.options.target.cpu.arch;
2634 const stub_size: u4 = switch (arch) {2495 const stub_size: u4 = switch (arch) {
2635 .x86_64 => 10,2496 .x86_64 => 10,
...@@ -2641,16 +2502,16 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2641,16 +2502,16 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2641 .aarch64 => 2,2502 .aarch64 => 2,
2642 else => unreachable,2503 else => unreachable,
2643 };2504 };
2644 const local_sym_index = @intCast(u32, self.locals.items.len);2505 const sym_index = @intCast(u32, self.locals.items.len);
2645 try self.locals.append(self.base.allocator, .{2506 try self.locals.append(gpa, .{
2646 .n_strx = 0,2507 .n_strx = 0,
2647 .n_type = macho.N_SECT,2508 .n_type = macho.N_SECT,
2648 .n_sect = 0,2509 .n_sect = 0,
2649 .n_desc = 0,2510 .n_desc = 0,
2650 .n_value = 0,2511 .n_value = 0,
2651 });2512 });
2652 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);2513 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
2653 try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);2514 try atom.relocs.ensureTotalCapacity(gpa, 1);
26542515
2655 switch (arch) {2516 switch (arch) {
2656 .x86_64 => {2517 .x86_64 => {
...@@ -2661,7 +2522,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2661,7 +2522,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2661 atom.code.items[5] = 0xe9;2522 atom.code.items[5] = 0xe9;
2662 atom.relocs.appendAssumeCapacity(.{2523 atom.relocs.appendAssumeCapacity(.{
2663 .offset = 6,2524 .offset = 6,
2664 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },2525 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2665 .addend = 0,2526 .addend = 0,
2666 .subtractor = null,2527 .subtractor = null,
2667 .pcrel = true,2528 .pcrel = true,
...@@ -2683,7 +2544,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2683,7 +2544,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2683 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());2544 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
2684 atom.relocs.appendAssumeCapacity(.{2545 atom.relocs.appendAssumeCapacity(.{
2685 .offset = 4,2546 .offset = 4,
2686 .target = .{ .local = self.stub_helper_preamble_atom.?.local_sym_index },2547 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },
2687 .addend = 0,2548 .addend = 0,
2688 .subtractor = null,2549 .subtractor = null,
2689 .pcrel = true,2550 .pcrel = true,
...@@ -2695,22 +2556,32 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -2695,22 +2556,32 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
2695 else => unreachable,2556 else => unreachable,
2696 }2557 }
26972558
2559 try self.managed_atoms.append(gpa, atom);
2560 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2561
2562 try self.allocateAtomCommon(atom, .{
2563 .seg = self.text_segment_cmd_index.?,
2564 .sect = self.stub_helper_section_index.?,
2565 });
2566
2698 return atom;2567 return atom;
2699}2568}
27002569
2701pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*Atom {2570pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
2702 const local_sym_index = @intCast(u32, self.locals.items.len);2571 const gpa = self.base.allocator;
2703 try self.locals.append(self.base.allocator, .{2572 const sym_index = @intCast(u32, self.locals.items.len);
2573 const global_index = @intCast(u32, self.globals.getIndex(self.getSymbolName(target)).?);
2574 try self.locals.append(gpa, .{
2704 .n_strx = 0,2575 .n_strx = 0,
2705 .n_type = macho.N_SECT,2576 .n_type = macho.N_SECT,
2706 .n_sect = 0,2577 .n_sect = 0,
2707 .n_desc = 0,2578 .n_desc = 0,
2708 .n_value = 0,2579 .n_value = 0,
2709 });2580 });
2710 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);2581 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2711 try atom.relocs.append(self.base.allocator, .{2582 try atom.relocs.append(gpa, .{
2712 .offset = 0,2583 .offset = 0,
2713 .target = .{ .local = stub_sym_index },2584 .target = .{ .sym_index = stub_sym_index, .file = null },
2714 .addend = 0,2585 .addend = 0,
2715 .subtractor = null,2586 .subtractor = null,
2716 .pcrel = false,2587 .pcrel = false,
...@@ -2721,15 +2592,25 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*A...@@ -2721,15 +2592,25 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*A
2721 else => unreachable,2592 else => unreachable,
2722 },2593 },
2723 });2594 });
2724 try atom.rebases.append(self.base.allocator, 0);2595 try atom.rebases.append(gpa, 0);
2725 try atom.lazy_bindings.append(self.base.allocator, .{2596 try atom.lazy_bindings.append(gpa, .{
2726 .n_strx = n_strx,2597 .global_index = global_index,
2727 .offset = 0,2598 .offset = 0,
2728 });2599 });
2600
2601 try self.managed_atoms.append(gpa, atom);
2602 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2603
2604 try self.allocateAtomCommon(atom, .{
2605 .seg = self.data_segment_cmd_index.?,
2606 .sect = self.la_symbol_ptr_section_index.?,
2607 });
2608
2729 return atom;2609 return atom;
2730}2610}
27312611
2732pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {2612pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2613 const gpa = self.base.allocator;
2733 const arch = self.base.options.target.cpu.arch;2614 const arch = self.base.options.target.cpu.arch;
2734 const alignment: u2 = switch (arch) {2615 const alignment: u2 = switch (arch) {
2735 .x86_64 => 0,2616 .x86_64 => 0,
...@@ -2741,23 +2622,23 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2741,23 +2622,23 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2741 .aarch64 => 3 * @sizeOf(u32),2622 .aarch64 => 3 * @sizeOf(u32),
2742 else => unreachable, // unhandled architecture type2623 else => unreachable, // unhandled architecture type
2743 };2624 };
2744 const local_sym_index = @intCast(u32, self.locals.items.len);2625 const sym_index = @intCast(u32, self.locals.items.len);
2745 try self.locals.append(self.base.allocator, .{2626 try self.locals.append(gpa, .{
2746 .n_strx = 0,2627 .n_strx = 0,
2747 .n_type = macho.N_SECT,2628 .n_type = macho.N_SECT,
2748 .n_sect = 0,2629 .n_sect = 0,
2749 .n_desc = 0,2630 .n_desc = 0,
2750 .n_value = 0,2631 .n_value = 0,
2751 });2632 });
2752 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);2633 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
2753 switch (arch) {2634 switch (arch) {
2754 .x86_64 => {2635 .x86_64 => {
2755 // jmp2636 // jmp
2756 atom.code.items[0] = 0xff;2637 atom.code.items[0] = 0xff;
2757 atom.code.items[1] = 0x25;2638 atom.code.items[1] = 0x25;
2758 try atom.relocs.append(self.base.allocator, .{2639 try atom.relocs.append(gpa, .{
2759 .offset = 2,2640 .offset = 2,
2760 .target = .{ .local = laptr_sym_index },2641 .target = .{ .sym_index = laptr_sym_index, .file = null },
2761 .addend = 0,2642 .addend = 0,
2762 .subtractor = null,2643 .subtractor = null,
2763 .pcrel = true,2644 .pcrel = true,
...@@ -2766,12 +2647,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2766,12 +2647,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2766 });2647 });
2767 },2648 },
2768 .aarch64 => {2649 .aarch64 => {
2769 try atom.relocs.ensureTotalCapacity(self.base.allocator, 2);2650 try atom.relocs.ensureTotalCapacity(gpa, 2);
2770 // adrp x16, pages2651 // adrp x16, pages
2771 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());2652 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
2772 atom.relocs.appendAssumeCapacity(.{2653 atom.relocs.appendAssumeCapacity(.{
2773 .offset = 0,2654 .offset = 0,
2774 .target = .{ .local = laptr_sym_index },2655 .target = .{ .sym_index = laptr_sym_index, .file = null },
2775 .addend = 0,2656 .addend = 0,
2776 .subtractor = null,2657 .subtractor = null,
2777 .pcrel = true,2658 .pcrel = true,
...@@ -2786,7 +2667,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2786,7 +2667,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2786 ).toU32());2667 ).toU32());
2787 atom.relocs.appendAssumeCapacity(.{2668 atom.relocs.appendAssumeCapacity(.{
2788 .offset = 4,2669 .offset = 4,
2789 .target = .{ .local = laptr_sym_index },2670 .target = .{ .sym_index = laptr_sym_index, .file = null },
2790 .addend = 0,2671 .addend = 0,
2791 .subtractor = null,2672 .subtractor = null,
2792 .pcrel = false,2673 .pcrel = false,
...@@ -2798,101 +2679,121 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -2798,101 +2679,121 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2798 },2679 },
2799 else => unreachable,2680 else => unreachable,
2800 }2681 }
2682
2683 try self.managed_atoms.append(gpa, atom);
2684 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
2685
2686 try self.allocateAtomCommon(atom, .{
2687 .seg = self.text_segment_cmd_index.?,
2688 .sect = self.stubs_section_index.?,
2689 });
2690
2801 return atom;2691 return atom;
2802}2692}
28032693
2804fn createTentativeDefAtoms(self: *MachO) !void {2694fn createTentativeDefAtoms(self: *MachO) !void {
2805 if (self.tentatives.count() == 0) return;2695 const gpa = self.base.allocator;
2806 // Convert any tentative definition into a regular symbol and allocate2696
2807 // text blocks for each tentative definition.2697 for (self.globals.values()) |global| {
2808 while (self.tentatives.popOrNull()) |entry| {2698 const sym = self.getSymbolPtr(global);
2699 if (!sym.tentative()) continue;
2700
2701 log.debug("creating tentative definition for ATOM(%{d}, '{s}') in object({d})", .{
2702 global.sym_index, self.getSymbolName(global), global.file,
2703 });
2704
2705 // Convert any tentative definition into a regular symbol and allocate
2706 // text blocks for each tentative definition.
2809 const match = MatchingSection{2707 const match = MatchingSection{
2810 .seg = self.data_segment_cmd_index.?,2708 .seg = self.data_segment_cmd_index.?,
2811 .sect = self.bss_section_index.?,2709 .sect = self.bss_section_index.?,
2812 };2710 };
2813 _ = try self.section_ordinals.getOrPut(self.base.allocator, match);2711 _ = try self.section_ordinals.getOrPut(gpa, match);
2814
2815 const global_sym = &self.globals.items[entry.key];
2816 const size = global_sym.n_value;
2817 const alignment = (global_sym.n_desc >> 8) & 0x0f;
28182712
2819 global_sym.n_value = 0;2713 const size = sym.n_value;
2820 global_sym.n_desc = 0;2714 const alignment = (sym.n_desc >> 8) & 0x0f;
2821 global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
28222715
2823 const local_sym_index = @intCast(u32, self.locals.items.len);2716 sym.* = .{
2824 const local_sym = try self.locals.addOne(self.base.allocator);2717 .n_strx = sym.n_strx,
2825 local_sym.* = .{2718 .n_type = macho.N_SECT | macho.N_EXT,
2826 .n_strx = global_sym.n_strx,2719 .n_sect = 0,
2827 .n_type = macho.N_SECT,
2828 .n_sect = global_sym.n_sect,
2829 .n_desc = 0,2720 .n_desc = 0,
2830 .n_value = 0,2721 .n_value = 0,
2831 };2722 };
28322723
2833 const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable;2724 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
2834 resolv.local_sym_index = local_sym_index;2725 atom.file = global.file;
28352726
2836 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);2727 try self.allocateAtomCommon(atom, match);
28372728
2838 if (self.needs_prealloc) {2729 if (global.file) |file| {
2839 const alignment_pow_2 = try math.powi(u32, 2, alignment);2730 const object = &self.objects.items[file];
2840 const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match);
2841 local_sym.n_value = vaddr;
2842 global_sym.n_value = vaddr;
2843 } else try self.addAtomToSection(atom, match);
2844 }
2845}
28462731
2847fn createDsoHandleSymbol(self: *MachO) !void {2732 try atom.contained.append(gpa, .{
2848 if (self.dso_handle_sym_index != null) return;2733 .sym_index = global.sym_index,
2734 .offset = 0,
2735 .stab = if (object.debug_info) |_| .static else null,
2736 });
28492737
2850 const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "___dso_handle"), StringIndexAdapter{2738 try object.managed_atoms.append(gpa, atom);
2851 .bytes = &self.strtab,2739 try object.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
2852 }) orelse return;2740 } else {
2741 try self.managed_atoms.append(gpa, atom);
2742 try self.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
2743 }
2744 }
2745}
28532746
2854 const resolv = self.symbol_resolver.getPtr(n_strx) orelse return;2747fn createMhExecuteHeaderSymbol(self: *MachO) !void {
2855 if (resolv.where != .undef) return;2748 if (self.base.options.output_mode != .Exe) return;
2749 if (self.globals.contains("__mh_execute_header")) return;
28562750
2857 const undef = &self.undefs.items[resolv.where_index];2751 const gpa = self.base.allocator;
2858 const local_sym_index = @intCast(u32, self.locals.items.len);2752 const name = try gpa.dupe(u8, "__mh_execute_header");
2859 var nlist = macho.nlist_64{2753 const n_strx = try self.strtab.insert(gpa, name);
2860 .n_strx = undef.n_strx,2754 const sym_index = @intCast(u32, self.locals.items.len);
2861 .n_type = macho.N_SECT,2755 try self.locals.append(gpa, .{
2756 .n_strx = n_strx,
2757 .n_type = macho.N_SECT | macho.N_EXT,
2862 .n_sect = 0,2758 .n_sect = 0,
2863 .n_desc = 0,2759 .n_desc = 0,
2864 .n_value = 0,2760 .n_value = 0,
2865 };2761 });
2866 try self.locals.append(self.base.allocator, nlist);2762 try self.globals.putNoClobber(gpa, name, .{
2867 const global_sym_index = @intCast(u32, self.globals.items.len);2763 .sym_index = sym_index,
2868 nlist.n_type |= macho.N_EXT;2764 .file = null,
2869 nlist.n_desc = macho.N_WEAK_DEF;2765 });
2870 try self.globals.append(self.base.allocator, nlist);2766}
2871 self.dso_handle_sym_index = local_sym_index;
2872
2873 assert(self.unresolved.swapRemove(resolv.where_index));
28742767
2875 undef.* = .{2768fn createDsoHandleSymbol(self: *MachO) !void {
2876 .n_strx = 0,2769 const global = self.globals.getPtr("___dso_handle") orelse return;
2877 .n_type = macho.N_UNDF,2770 const sym = self.getSymbolPtr(global.*);
2771 if (!sym.undf()) return;
2772
2773 const gpa = self.base.allocator;
2774 const n_strx = try self.strtab.insert(gpa, "___dso_handle");
2775 const sym_index = @intCast(u32, self.locals.items.len);
2776 try self.locals.append(gpa, .{
2777 .n_strx = n_strx,
2778 .n_type = macho.N_SECT | macho.N_EXT,
2878 .n_sect = 0,2779 .n_sect = 0,
2879 .n_desc = 0,2780 .n_desc = macho.N_WEAK_DEF,
2880 .n_value = 0,2781 .n_value = 0,
2782 });
2783 global.* = .{
2784 .sym_index = sym_index,
2785 .file = null,
2881 };2786 };
2882 resolv.* = .{2787 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));
2883 .where = .global,
2884 .where_index = global_sym_index,
2885 .local_sym_index = local_sym_index,
2886 };
2887}2788}
28882789
2889fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {2790fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2890 const object = &self.objects.items[object_id];2791 const gpa = self.base.allocator;
28912792
2892 log.debug("resolving symbols in '{s}'", .{object.name});2793 log.debug("resolving symbols in '{s}'", .{object.name});
28932794
2894 for (object.symtab) |sym, id| {2795 for (object.symtab.items) |sym, index| {
2895 const sym_id = @intCast(u32, id);2796 const sym_index = @intCast(u32, index);
2896 const sym_name = object.getString(sym.n_strx);2797 const sym_name = object.getString(sym.n_strx);
28972798
2898 if (sym.stab()) {2799 if (sym.stab()) {
...@@ -2916,170 +2817,81 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {...@@ -2916,170 +2817,81 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2916 return error.UnhandledSymbolType;2817 return error.UnhandledSymbolType;
2917 }2818 }
29182819
2919 if (sym.sect()) {2820 if (sym.sect() and !sym.ext()) {
2920 // Defined symbol regardless of scope lands in the locals symbol table.2821 log.debug("symbol '{s}' local to object {s}; skipping...", .{
2921 const local_sym_index = @intCast(u32, self.locals.items.len);2822 sym_name,
2922 try self.locals.append(self.base.allocator, .{2823 object.name,
2923 .n_strx = if (symbolIsTemp(sym, sym_name)) 0 else try self.makeString(sym_name),
2924 .n_type = macho.N_SECT,
2925 .n_sect = 0,
2926 .n_desc = 0,
2927 .n_value = sym.n_value,
2928 });2824 });
2929 try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index);2825 continue;
2930 try object.reverse_symbol_mapping.putNoClobber(self.base.allocator, local_sym_index, sym_id);2826 }
2931
2932 // If the symbol's scope is not local aka translation unit, then we need work out
2933 // if we should save the symbol as a global, or potentially flag the error.
2934 if (!sym.ext()) continue;
2935
2936 const n_strx = try self.makeString(sym_name);
2937 const local = self.locals.items[local_sym_index];
2938 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
2939 const global_sym_index = @intCast(u32, self.globals.items.len);
2940 try self.globals.append(self.base.allocator, .{
2941 .n_strx = n_strx,
2942 .n_type = sym.n_type,
2943 .n_sect = 0,
2944 .n_desc = sym.n_desc,
2945 .n_value = sym.n_value,
2946 });
2947 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
2948 .where = .global,
2949 .where_index = global_sym_index,
2950 .local_sym_index = local_sym_index,
2951 .file = object_id,
2952 });
2953 continue;
2954 };
2955
2956 switch (resolv.where) {
2957 .global => {
2958 const global = &self.globals.items[resolv.where_index];
2959
2960 if (global.tentative()) {
2961 assert(self.tentatives.swapRemove(resolv.where_index));
2962 } else if (!(sym.weakDef() or sym.pext()) and !(global.weakDef() or global.pext())) {
2963 log.err("symbol '{s}' defined multiple times", .{sym_name});
2964 if (resolv.file) |file| {
2965 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2966 }
2967 log.err(" next definition in '{s}'", .{object.name});
2968 return error.MultipleSymbolDefinitions;
2969 } else if (sym.weakDef() or sym.pext()) continue; // Current symbol is weak, so skip it.
2970
2971 // Otherwise, update the resolver and the global symbol.
2972 global.n_type = sym.n_type;
2973 resolv.local_sym_index = local_sym_index;
2974 resolv.file = object_id;
29752827
2976 continue;2828 const name = try gpa.dupe(u8, sym_name);
2977 },2829 const global_index = @intCast(u32, self.globals.values().len);
2978 .undef => {2830 const gop = try self.globals.getOrPut(gpa, name);
2979 const undef = &self.undefs.items[resolv.where_index];2831 defer if (gop.found_existing) gpa.free(name);
2980 undef.* = .{
2981 .n_strx = 0,
2982 .n_type = macho.N_UNDF,
2983 .n_sect = 0,
2984 .n_desc = 0,
2985 .n_value = 0,
2986 };
2987 assert(self.unresolved.swapRemove(resolv.where_index));
2988 },
2989 }
29902832
2991 const global_sym_index = @intCast(u32, self.globals.items.len);2833 if (!gop.found_existing) {
2992 try self.globals.append(self.base.allocator, .{2834 gop.value_ptr.* = .{
2993 .n_strx = local.n_strx,2835 .sym_index = sym_index,
2994 .n_type = sym.n_type,
2995 .n_sect = 0,
2996 .n_desc = sym.n_desc,
2997 .n_value = sym.n_value,
2998 });
2999 resolv.* = .{
3000 .where = .global,
3001 .where_index = global_sym_index,
3002 .local_sym_index = local_sym_index,
3003 .file = object_id,2836 .file = object_id,
3004 };2837 };
3005 } else if (sym.tentative()) {2838 if (sym.undf() and !sym.tentative()) {
3006 // Symbol is a tentative definition.2839 try self.unresolved.putNoClobber(gpa, global_index, {});
3007 const n_strx = try self.makeString(sym_name);
3008 const resolv = self.symbol_resolver.getPtr(n_strx) orelse {
3009 const global_sym_index = @intCast(u32, self.globals.items.len);
3010 try self.globals.append(self.base.allocator, .{
3011 .n_strx = try self.makeString(sym_name),
3012 .n_type = sym.n_type,
3013 .n_sect = 0,
3014 .n_desc = sym.n_desc,
3015 .n_value = sym.n_value,
3016 });
3017 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3018 .where = .global,
3019 .where_index = global_sym_index,
3020 .file = object_id,
3021 });
3022 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
3023 continue;
3024 };
3025
3026 switch (resolv.where) {
3027 .global => {
3028 const global = &self.globals.items[resolv.where_index];
3029 if (!global.tentative()) continue;
3030 if (global.n_value >= sym.n_value) continue;
3031
3032 global.n_desc = sym.n_desc;
3033 global.n_value = sym.n_value;
3034 resolv.file = object_id;
3035 },
3036 .undef => {
3037 const undef = &self.undefs.items[resolv.where_index];
3038 const global_sym_index = @intCast(u32, self.globals.items.len);
3039 try self.globals.append(self.base.allocator, .{
3040 .n_strx = undef.n_strx,
3041 .n_type = sym.n_type,
3042 .n_sect = 0,
3043 .n_desc = sym.n_desc,
3044 .n_value = sym.n_value,
3045 });
3046 _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index);
3047 assert(self.unresolved.swapRemove(resolv.where_index));
3048
3049 resolv.* = .{
3050 .where = .global,
3051 .where_index = global_sym_index,
3052 .file = object_id,
3053 };
3054 undef.* = .{
3055 .n_strx = 0,
3056 .n_type = macho.N_UNDF,
3057 .n_sect = 0,
3058 .n_desc = 0,
3059 .n_value = 0,
3060 };
3061 },
3062 }2840 }
3063 } else {2841 continue;
3064 // Symbol is undefined.2842 }
3065 const n_strx = try self.makeString(sym_name);
3066 if (self.symbol_resolver.contains(n_strx)) continue;
30672843
3068 const undef_sym_index = @intCast(u32, self.undefs.items.len);2844 const global = gop.value_ptr.*;
3069 try self.undefs.append(self.base.allocator, .{2845 const global_sym = self.getSymbol(global);
3070 .n_strx = try self.makeString(sym_name),2846
3071 .n_type = macho.N_UNDF,2847 // Cases to consider: sym vs global_sym
3072 .n_sect = 0,2848 // 1. strong(sym) and strong(global_sym) => error
3073 .n_desc = sym.n_desc,2849 // 2. strong(sym) and weak(global_sym) => sym
3074 .n_value = 0,2850 // 3. strong(sym) and tentative(global_sym) => sym
3075 });2851 // 4. strong(sym) and undf(global_sym) => sym
3076 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{2852 // 5. weak(sym) and strong(global_sym) => global_sym
3077 .where = .undef,2853 // 6. weak(sym) and tentative(global_sym) => sym
3078 .where_index = undef_sym_index,2854 // 7. weak(sym) and undf(global_sym) => sym
3079 .file = object_id,2855 // 8. tentative(sym) and strong(global_sym) => global_sym
3080 });2856 // 9. tentative(sym) and weak(global_sym) => global_sym
3081 try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none);2857 // 10. tentative(sym) and tentative(global_sym) => pick larger
2858 // 11. tentative(sym) and undf(global_sym) => sym
2859 // 12. undf(sym) and * => global_sym
2860 //
2861 // Reduces to:
2862 // 1. strong(sym) and strong(global_sym) => error
2863 // 2. * and strong(global_sym) => global_sym
2864 // 3. weak(sym) and weak(global_sym) => global_sym
2865 // 4. tentative(sym) and tentative(global_sym) => pick larger
2866 // 5. undf(sym) and * => global_sym
2867 // 6. else => sym
2868
2869 const sym_is_strong = sym.sect() and !(sym.weakDef() or sym.pext());
2870 const global_is_strong = global_sym.sect() and !(global_sym.weakDef() or global_sym.pext());
2871 const sym_is_weak = sym.sect() and (sym.weakDef() or sym.pext());
2872 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
2873
2874 if (sym_is_strong and global_is_strong) {
2875 log.err("symbol '{s}' defined multiple times", .{sym_name});
2876 if (global.file) |file| {
2877 log.err(" first definition in '{s}'", .{self.objects.items[file].name});
2878 }
2879 log.err(" next definition in '{s}'", .{object.name});
2880 return error.MultipleSymbolDefinitions;
2881 }
2882 if (global_is_strong) continue;
2883 if (sym_is_weak and global_is_weak) continue;
2884 if (sym.tentative() and global_sym.tentative()) {
2885 if (global_sym.n_value >= sym.n_value) continue;
3082 }2886 }
2887 if (sym.undf() and !sym.tentative()) continue;
2888
2889 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex(name).?));
2890
2891 gop.value_ptr.* = .{
2892 .sym_index = sym_index,
2893 .file = object_id,
2894 };
3083 }2895 }
3084}2896}
30852897
...@@ -3088,8 +2900,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3088,8 +2900,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
30882900
3089 var next_sym: usize = 0;2901 var next_sym: usize = 0;
3090 loop: while (next_sym < self.unresolved.count()) {2902 loop: while (next_sym < self.unresolved.count()) {
3091 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];2903 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
3092 const sym_name = self.getString(sym.n_strx);2904 const sym_name = self.getSymbolName(global);
30932905
3094 for (self.archives.items) |archive| {2906 for (self.archives.items) |archive| {
3095 // Check if the entry exists in a static archive.2907 // Check if the entry exists in a static archive.
...@@ -3102,7 +2914,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {...@@ -3102,7 +2914,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
3102 const object_id = @intCast(u16, self.objects.items.len);2914 const object_id = @intCast(u16, self.objects.items.len);
3103 const object = try self.objects.addOne(self.base.allocator);2915 const object = try self.objects.addOne(self.base.allocator);
3104 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);2916 object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]);
3105 try self.resolveSymbolsInObject(object_id);2917 try self.resolveSymbolsInObject(object, object_id);
31062918
3107 continue :loop;2919 continue :loop;
3108 }2920 }
...@@ -3116,8 +2928,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3116,8 +2928,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31162928
3117 var next_sym: usize = 0;2929 var next_sym: usize = 0;
3118 loop: while (next_sym < self.unresolved.count()) {2930 loop: while (next_sym < self.unresolved.count()) {
3119 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];2931 const global_index = self.unresolved.keys()[next_sym];
3120 const sym_name = self.getString(sym.n_strx);2932 const global = self.globals.values()[global_index];
2933 const sym = self.getSymbolPtr(global);
2934 const sym_name = self.getSymbolName(global);
31212935
3122 for (self.dylibs.items) |dylib, id| {2936 for (self.dylibs.items) |dylib, id| {
3123 if (!dylib.symbols.contains(sym_name)) continue;2937 if (!dylib.symbols.contains(sym_name)) continue;
...@@ -3129,69 +2943,14 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3129,69 +2943,14 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3129 }2943 }
31302944
3131 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;2945 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
3132 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;2946 sym.n_type |= macho.N_EXT;
3133 const undef = &self.undefs.items[resolv.where_index];2947 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
3134 undef.n_type |= macho.N_EXT;
3135 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
31362948
3137 if (dylib.weak) {2949 if (dylib.weak) {
3138 undef.n_desc |= macho.N_WEAK_REF;2950 sym.n_desc |= macho.N_WEAK_REF;
3139 }2951 }
31402952
3141 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {2953 assert(self.unresolved.swapRemove(global_index));
3142 switch (entry.value) {
3143 .none => {},
3144 .got => return error.TODOGotHint,
3145 .stub => {
3146 if (self.stubs_table.contains(sym.n_strx)) break :outer_blk;
3147 const stub_helper_atom = blk: {
3148 const match = MatchingSection{
3149 .seg = self.text_segment_cmd_index.?,
3150 .sect = self.stub_helper_section_index.?,
3151 };
3152 const atom = try self.createStubHelperAtom();
3153 const atom_sym = &self.locals.items[atom.local_sym_index];
3154 const alignment = try math.powi(u32, 2, atom.alignment);
3155 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3156 atom_sym.n_value = vaddr;
3157 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3158 break :blk atom;
3159 };
3160 const laptr_atom = blk: {
3161 const match = MatchingSection{
3162 .seg = self.data_segment_cmd_index.?,
3163 .sect = self.la_symbol_ptr_section_index.?,
3164 };
3165 const atom = try self.createLazyPointerAtom(
3166 stub_helper_atom.local_sym_index,
3167 sym.n_strx,
3168 );
3169 const atom_sym = &self.locals.items[atom.local_sym_index];
3170 const alignment = try math.powi(u32, 2, atom.alignment);
3171 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3172 atom_sym.n_value = vaddr;
3173 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3174 break :blk atom;
3175 };
3176 const stub_atom = blk: {
3177 const match = MatchingSection{
3178 .seg = self.text_segment_cmd_index.?,
3179 .sect = self.stubs_section_index.?,
3180 };
3181 const atom = try self.createStubAtom(laptr_atom.local_sym_index);
3182 const atom_sym = &self.locals.items[atom.local_sym_index];
3183 const alignment = try math.powi(u32, 2, atom.alignment);
3184 const vaddr = try self.allocateAtom(atom, atom.size, alignment, match);
3185 atom_sym.n_value = vaddr;
3186 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3187 break :blk atom;
3188 };
3189 const stub_index = @intCast(u32, self.stubs.items.len);
3190 try self.stubs.append(self.base.allocator, stub_atom);
3191 try self.stubs_table.putNoClobber(self.base.allocator, sym.n_strx, stub_index);
3192 },
3193 }
3194 }
31952954
3196 continue :loop;2955 continue :loop;
3197 }2956 }
...@@ -3200,39 +2959,46 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3200,39 +2959,46 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3200 }2959 }
3201}2960}
32022961
3203fn createMhExecuteHeaderSymbol(self: *MachO) !void {2962fn resolveSymbolsAtLoading(self: *MachO) !void {
3204 if (self.base.options.output_mode != .Exe) return;2963 const is_lib = self.base.options.output_mode == .Lib;
3205 if (self.mh_execute_header_sym_index != null) return;2964 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
2965 const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false);
32062966
3207 const n_strx = try self.makeString("__mh_execute_header");2967 var next_sym: usize = 0;
3208 const local_sym_index = @intCast(u32, self.locals.items.len);2968 while (next_sym < self.unresolved.count()) {
3209 var nlist = macho.nlist_64{2969 const global_index = self.unresolved.keys()[next_sym];
3210 .n_strx = n_strx,2970 const global = self.globals.values()[global_index];
3211 .n_type = macho.N_SECT,2971 const sym = self.getSymbolPtr(global);
3212 .n_sect = 0,2972 const sym_name = self.getSymbolName(global);
3213 .n_desc = 0,2973
3214 .n_value = 0,2974 if (sym.discarded()) {
3215 };2975 sym.* = .{
3216 try self.locals.append(self.base.allocator, nlist);2976 .n_strx = 0,
3217 self.mh_execute_header_sym_index = local_sym_index;2977 .n_type = macho.N_UNDF,
2978 .n_sect = 0,
2979 .n_desc = 0,
2980 .n_value = 0,
2981 };
2982 _ = self.unresolved.swapRemove(global_index);
2983 continue;
2984 } else if (allow_undef) {
2985 const n_desc = @bitCast(
2986 u16,
2987 macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP * @intCast(i16, macho.N_SYMBOL_RESOLVER),
2988 );
2989 // TODO allow_shlib_undefined is an ELF flag so figure out macOS specific flags too.
2990 sym.n_type = macho.N_EXT;
2991 sym.n_desc = n_desc;
2992 _ = self.unresolved.swapRemove(global_index);
2993 continue;
2994 }
32182995
3219 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {2996 log.err("undefined reference to symbol '{s}'", .{sym_name});
3220 const global = &self.globals.items[resolv.where_index];2997 if (global.file) |file| {
3221 if (!(global.weakDef() or !global.pext())) {2998 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
3222 log.err("symbol '__mh_execute_header' defined multiple times", .{});
3223 return error.MultipleSymbolDefinitions;
3224 }2999 }
3225 resolv.local_sym_index = local_sym_index;3000
3226 } else {3001 next_sym += 1;
3227 const global_sym_index = @intCast(u32, self.globals.items.len);
3228 nlist.n_type |= macho.N_EXT;
3229 try self.globals.append(self.base.allocator, nlist);
3230 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3231 .where = .global,
3232 .where_index = global_sym_index,
3233 .local_sym_index = local_sym_index,
3234 .file = null,
3235 });
3236 }3002 }
3237}3003}
32383004
...@@ -3240,21 +3006,20 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3240,21 +3006,20 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3240 if (self.dyld_stub_binder_index != null) return;3006 if (self.dyld_stub_binder_index != null) return;
3241 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports3007 if (self.unresolved.count() == 0) return; // no need for a stub binder if we don't have any imports
32423008
3243 const n_strx = try self.makeString("dyld_stub_binder");3009 const gpa = self.base.allocator;
3244 const sym_index = @intCast(u32, self.undefs.items.len);3010 const n_strx = try self.strtab.insert(gpa, "dyld_stub_binder");
3245 try self.undefs.append(self.base.allocator, .{3011 const sym_index = @intCast(u32, self.locals.items.len);
3012 try self.locals.append(gpa, .{
3246 .n_strx = n_strx,3013 .n_strx = n_strx,
3247 .n_type = macho.N_UNDF,3014 .n_type = macho.N_UNDF,
3248 .n_sect = 0,3015 .n_sect = 0,
3249 .n_desc = 0,3016 .n_desc = 0,
3250 .n_value = 0,3017 .n_value = 0,
3251 });3018 });
3252 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{3019 const sym_name = try gpa.dupe(u8, "dyld_stub_binder");
3253 .where = .undef,3020 const global = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3254 .where_index = sym_index,3021 try self.globals.putNoClobber(gpa, sym_name, global);
3255 });3022 const sym = &self.locals.items[sym_index];
3256 const sym = &self.undefs.items[sym_index];
3257 const sym_name = self.getString(n_strx);
32583023
3259 for (self.dylibs.items) |dylib, id| {3024 for (self.dylibs.items) |dylib, id| {
3260 if (!dylib.symbols.contains(sym_name)) continue;3025 if (!dylib.symbols.contains(sym_name)) continue;
...@@ -3275,197 +3040,13 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3275,197 +3040,13 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32753040
3276 if (self.dyld_stub_binder_index == null) {3041 if (self.dyld_stub_binder_index == null) {
3277 log.err("undefined reference to symbol '{s}'", .{sym_name});3042 log.err("undefined reference to symbol '{s}'", .{sym_name});
3278 return error.UndefinedSymbolReference;3043 return error.UndefinedSymbolReference;
3279 }
3280
3281 // Add dyld_stub_binder as the final GOT entry.
3282 const target = Atom.Relocation.Target{ .global = n_strx };
3283 const atom = try self.createGotAtom(target);
3284 const got_index = @intCast(u32, self.got_entries.items.len);
3285 try self.got_entries.append(self.base.allocator, .{ .target = target, .atom = atom });
3286 try self.got_entries_table.putNoClobber(self.base.allocator, target, got_index);
3287 const match = MatchingSection{
3288 .seg = self.data_const_segment_cmd_index.?,
3289 .sect = self.got_section_index.?,
3290 };
3291 const atom_sym = &self.locals.items[atom.local_sym_index];
3292
3293 if (self.needs_prealloc) {
3294 const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match);
3295 log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr });
3296 atom_sym.n_value = vaddr;
3297 } else try self.addAtomToSection(atom, match);
3298
3299 atom_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3300}
3301
3302fn parseObjectsIntoAtoms(self: *MachO) !void {
3303 // TODO I need to see if I can simplify this logic, or perhaps split it into two functions:
3304 // one for non-prealloc traditional path, and one for incremental prealloc path.
3305 const tracy = trace(@src());
3306 defer tracy.end();
3307
3308 var parsed_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
3309 defer parsed_atoms.deinit();
3310
3311 var first_atoms = std.AutoArrayHashMap(MatchingSection, *Atom).init(self.base.allocator);
3312 defer first_atoms.deinit();
3313
3314 var section_metadata = std.AutoHashMap(MatchingSection, struct {
3315 size: u64,
3316 alignment: u32,
3317 }).init(self.base.allocator);
3318 defer section_metadata.deinit();
3319
3320 for (self.objects.items) |*object| {
3321 if (object.analyzed) continue;
3322
3323 try object.parseIntoAtoms(self.base.allocator, self);
3324
3325 var it = object.end_atoms.iterator();
3326 while (it.next()) |entry| {
3327 const match = entry.key_ptr.*;
3328 var atom = entry.value_ptr.*;
3329
3330 while (atom.prev) |prev| {
3331 atom = prev;
3332 }
3333
3334 const first_atom = atom;
3335
3336 const seg = self.load_commands.items[match.seg].segment;
3337 const sect = seg.sections.items[match.sect];
3338 const metadata = try section_metadata.getOrPut(match);
3339 if (!metadata.found_existing) {
3340 metadata.value_ptr.* = .{
3341 .size = sect.size,
3342 .alignment = sect.@"align",
3343 };
3344 }
3345
3346 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
3347
3348 while (true) {
3349 const alignment = try math.powi(u32, 2, atom.alignment);
3350 const curr_size = metadata.value_ptr.size;
3351 const curr_size_aligned = mem.alignForwardGeneric(u64, curr_size, alignment);
3352 metadata.value_ptr.size = curr_size_aligned + atom.size;
3353 metadata.value_ptr.alignment = math.max(metadata.value_ptr.alignment, atom.alignment);
3354
3355 const sym = self.locals.items[atom.local_sym_index];
3356 log.debug(" {s}: n_value=0x{x}, size=0x{x}, alignment=0x{x}", .{
3357 self.getString(sym.n_strx),
3358 sym.n_value,
3359 atom.size,
3360 atom.alignment,
3361 });
3362
3363 if (atom.next) |next| {
3364 atom = next;
3365 } else break;
3366 }
3367
3368 if (parsed_atoms.getPtr(match)) |last| {
3369 last.*.next = first_atom;
3370 first_atom.prev = last.*;
3371 last.* = first_atom;
3372 }
3373 _ = try parsed_atoms.put(match, atom);
3374
3375 if (!first_atoms.contains(match)) {
3376 try first_atoms.putNoClobber(match, first_atom);
3377 }
3378 }
3379
3380 object.analyzed = true;
3381 }
3382
3383 var it = section_metadata.iterator();
3384 while (it.next()) |entry| {
3385 const match = entry.key_ptr.*;
3386 const metadata = entry.value_ptr.*;
3387 const seg = &self.load_commands.items[match.seg].segment;
3388 const sect = &seg.sections.items[match.sect];
3389 log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{
3390 sect.segName(),
3391 sect.sectName(),
3392 metadata.size,
3393 metadata.alignment,
3394 });
3395
3396 sect.@"align" = math.max(sect.@"align", metadata.alignment);
3397 const needed_size = @intCast(u32, metadata.size);
3398
3399 if (self.needs_prealloc) {
3400 try self.growSection(match, needed_size);
3401 }
3402 sect.size = needed_size;
3403 }
3404
3405 for (&[_]?u16{
3406 self.text_segment_cmd_index,
3407 self.data_const_segment_cmd_index,
3408 self.data_segment_cmd_index,
3409 }) |maybe_seg_id| {
3410 const seg_id = maybe_seg_id orelse continue;
3411 const seg = self.load_commands.items[seg_id].segment;
3412
3413 for (seg.sections.items) |sect, sect_id| {
3414 const match = MatchingSection{
3415 .seg = seg_id,
3416 .sect = @intCast(u16, sect_id),
3417 };
3418 if (!section_metadata.contains(match)) continue;
3419
3420 var base_vaddr = if (self.atoms.get(match)) |last| blk: {
3421 const last_atom_sym = self.locals.items[last.local_sym_index];
3422 break :blk last_atom_sym.n_value + last.size;
3423 } else sect.addr;
3424
3425 if (self.atoms.getPtr(match)) |last| {
3426 const first_atom = first_atoms.get(match).?;
3427 last.*.next = first_atom;
3428 first_atom.prev = last.*;
3429 last.* = first_atom;
3430 }
3431 _ = try self.atoms.put(self.base.allocator, match, parsed_atoms.get(match).?);
3432
3433 if (!self.needs_prealloc) continue;
3434
3435 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
3436
3437 var atom = first_atoms.get(match).?;
3438 while (true) {
3439 const alignment = try math.powi(u32, 2, atom.alignment);
3440 base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment);
3441
3442 const sym = &self.locals.items[atom.local_sym_index];
3443 sym.n_value = base_vaddr;
3444 sym.n_sect = n_sect;
3445
3446 log.debug(" {s}: start=0x{x}, end=0x{x}, size=0x{x}, alignment=0x{x}", .{
3447 self.getString(sym.n_strx),
3448 base_vaddr,
3449 base_vaddr + atom.size,
3450 atom.size,
3451 atom.alignment,
3452 });
3453
3454 // Update each symbol contained within the atom
3455 for (atom.contained.items) |sym_at_off| {
3456 const contained_sym = &self.locals.items[sym_at_off.local_sym_index];
3457 contained_sym.n_value = base_vaddr + sym_at_off.offset;
3458 contained_sym.n_sect = n_sect;
3459 }
3460
3461 base_vaddr += atom.size;
3462
3463 if (atom.next) |next| {
3464 atom = next;
3465 } else break;
3466 }
3467 }
3468 }3044 }
3045
3046 // Add dyld_stub_binder as the final GOT entry.
3047 const got_index = try self.allocateGotEntry(global);
3048 const got_atom = try self.createGotAtom(global);
3049 self.got_entries.items[got_index].atom = got_atom;
3469}3050}
34703051
3471fn addLoadDylibLC(self: *MachO, id: u16) !void {3052fn addLoadDylibLC(self: *MachO, id: u16) !void {
...@@ -3503,15 +3084,11 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3503,15 +3084,11 @@ fn setEntryPoint(self: *MachO) !void {
35033084
3504 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;3085 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
3505 const entry_name = self.base.options.entry orelse "_main";3086 const entry_name = self.base.options.entry orelse "_main";
3506 const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{3087 const global = self.globals.get(entry_name) orelse {
3507 .bytes = &self.strtab,
3508 }) orelse {
3509 log.err("entrypoint '{s}' not found", .{entry_name});3088 log.err("entrypoint '{s}' not found", .{entry_name});
3510 return error.MissingMainEntrypoint;3089 return error.MissingMainEntrypoint;
3511 };3090 };
3512 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;3091 const sym = self.getSymbol(global);
3513 assert(resolv.where == .global);
3514 const sym = self.globals.items[resolv.where_index];
3515 const ec = &self.load_commands.items[self.main_cmd_index.?].main;3092 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
3516 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);3093 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
3517 ec.stacksize = self.base.options.stack_size_override orelse 0;3094 ec.stacksize = self.base.options.stack_size_override orelse 0;
...@@ -3538,17 +3115,13 @@ pub fn deinit(self: *MachO) void {...@@ -3538,17 +3115,13 @@ pub fn deinit(self: *MachO) void {
3538 self.stubs.deinit(self.base.allocator);3115 self.stubs.deinit(self.base.allocator);
3539 self.stubs_free_list.deinit(self.base.allocator);3116 self.stubs_free_list.deinit(self.base.allocator);
3540 self.stubs_table.deinit(self.base.allocator);3117 self.stubs_table.deinit(self.base.allocator);
3541 self.strtab_dir.deinit(self.base.allocator);
3542 self.strtab.deinit(self.base.allocator);3118 self.strtab.deinit(self.base.allocator);
3543 self.undefs.deinit(self.base.allocator);
3544 self.globals.deinit(self.base.allocator);3119 self.globals.deinit(self.base.allocator);
3545 self.globals_free_list.deinit(self.base.allocator);
3546 self.locals.deinit(self.base.allocator);3120 self.locals.deinit(self.base.allocator);
3547 self.locals_free_list.deinit(self.base.allocator);3121 self.locals_free_list.deinit(self.base.allocator);
3548 self.symbol_resolver.deinit(self.base.allocator);
3549 self.unresolved.deinit(self.base.allocator);3122 self.unresolved.deinit(self.base.allocator);
3550 self.tentatives.deinit(self.base.allocator);
3551 self.gc_roots.deinit(self.base.allocator);3123 self.gc_roots.deinit(self.base.allocator);
3124 self.gc_sections.deinit(self.base.allocator);
35523125
3553 for (self.objects.items) |*object| {3126 for (self.objects.items) |*object| {
3554 object.deinit(self.base.allocator);3127 object.deinit(self.base.allocator);
...@@ -3662,7 +3235,7 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)...@@ -3662,7 +3235,7 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
3662 if (atom.prev) |prev| {3235 if (atom.prev) |prev| {
3663 prev.next = atom.next;3236 prev.next = atom.next;
36643237
3665 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {3238 if (!already_have_free_list_node and prev.freeListEligible(self)) {
3666 // The free list is heuristics, it doesn't have to be perfect, so we can ignore3239 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
3667 // the OOM here.3240 // the OOM here.
3668 free_list.append(self.base.allocator, prev) catch {};3241 free_list.append(self.base.allocator, prev) catch {};
...@@ -3692,9 +3265,9 @@ fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSec...@@ -3692,9 +3265,9 @@ fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSec
3692}3265}
36933266
3694fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {3267fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {
3695 const sym = self.locals.items[atom.local_sym_index];3268 const sym = self.locals.items[atom.sym_index];
3696 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;3269 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
3697 const need_realloc = !align_ok or new_atom_size > atom.capacity(self.*);3270 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
3698 if (!need_realloc) return sym.n_value;3271 if (!need_realloc) return sym.n_value;
3699 return self.allocateAtom(atom, new_atom_size, alignment, match);3272 return self.allocateAtom(atom, new_atom_size, alignment, match);
3700}3273}
...@@ -3725,7 +3298,7 @@ fn allocateLocalSymbol(self: *MachO) !u32 {...@@ -3725,7 +3298,7 @@ fn allocateLocalSymbol(self: *MachO) !u32 {
3725 return index;3298 return index;
3726}3299}
37273300
3728pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {3301pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3729 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);3302 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);
37303303
3731 const index = blk: {3304 const index = blk: {
...@@ -3740,16 +3313,13 @@ pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {...@@ -3740,16 +3313,13 @@ pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3740 }3313 }
3741 };3314 };
37423315
3743 self.got_entries.items[index] = .{3316 self.got_entries.items[index] = .{ .target = target, .atom = undefined };
3744 .target = target,
3745 .atom = undefined,
3746 };
3747 try self.got_entries_table.putNoClobber(self.base.allocator, target, index);3317 try self.got_entries_table.putNoClobber(self.base.allocator, target, index);
37483318
3749 return index;3319 return index;
3750}3320}
37513321
3752pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {3322pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3753 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);3323 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);
37543324
3755 const index = blk: {3325 const index = blk: {
...@@ -3764,13 +3334,13 @@ pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {...@@ -3764,13 +3334,13 @@ pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {
3764 }3334 }
3765 };3335 };
37663336
3767 self.stubs.items[index] = undefined;3337 self.stubs.items[index] = .{ .target = target, .atom = undefined };
3768 try self.stubs_table.putNoClobber(self.base.allocator, n_strx, index);3338 try self.stubs_table.putNoClobber(self.base.allocator, target, index);
37693339
3770 return index;3340 return index;
3771}3341}
37723342
3773pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {3343pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3774 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);3344 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);
37753345
3776 const index = blk: {3346 const index = blk: {
...@@ -3794,16 +3364,14 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {...@@ -3794,16 +3364,14 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3794pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {3364pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
3795 if (self.llvm_object) |_| return;3365 if (self.llvm_object) |_| return;
3796 const decl = self.base.options.module.?.declPtr(decl_index);3366 const decl = self.base.options.module.?.declPtr(decl_index);
3797 if (decl.link.macho.local_sym_index != 0) return;3367 if (decl.link.macho.sym_index != 0) return;
37983368
3799 decl.link.macho.local_sym_index = try self.allocateLocalSymbol();3369 decl.link.macho.sym_index = try self.allocateLocalSymbol();
3800 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho);3370 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
3801 try self.decls.putNoClobber(self.base.allocator, decl_index, null);3371 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
38023372
3803 const got_target = .{ .local = decl.link.macho.local_sym_index };3373 const got_target = .{ .sym_index = decl.link.macho.sym_index, .file = null };
3804 const got_index = try self.allocateGotEntry(got_target);3374 _ = try self.allocateGotEntry(got_target);
3805 const got_atom = try self.createGotAtom(got_target);
3806 self.got_entries.items[got_index].atom = got_atom;
3807}3375}
38083376
3809pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {3377pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -3877,8 +3445,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3877,8 +3445,9 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3877 var code_buffer = std.ArrayList(u8).init(self.base.allocator);3445 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
3878 defer code_buffer.deinit();3446 defer code_buffer.deinit();
38793447
3448 const gpa = self.base.allocator;
3880 const module = self.base.options.module.?;3449 const module = self.base.options.module.?;
3881 const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);3450 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
3882 if (!gop.found_existing) {3451 if (!gop.found_existing) {
3883 gop.value_ptr.* = .{};3452 gop.value_ptr.* = .{};
3884 }3453 }
...@@ -3886,24 +3455,32 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3886,24 +3455,32 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
38863455
3887 const decl = module.declPtr(decl_index);3456 const decl = module.declPtr(decl_index);
3888 const decl_name = try decl.getFullyQualifiedName(module);3457 const decl_name = try decl.getFullyQualifiedName(module);
3889 defer self.base.allocator.free(decl_name);3458 defer gpa.free(decl_name);
38903459
3891 const name_str_index = blk: {3460 const name_str_index = blk: {
3892 const index = unnamed_consts.items.len;3461 const index = unnamed_consts.items.len;
3893 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });3462 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
3894 defer self.base.allocator.free(name);3463 defer gpa.free(name);
3895 break :blk try self.makeString(name);3464 break :blk try self.strtab.insert(gpa, name);
3896 };3465 };
3897 const name = self.getString(name_str_index);3466 const name = self.strtab.get(name_str_index);
38983467
3899 log.debug("allocating symbol indexes for {s}", .{name});3468 log.debug("allocating symbol indexes for {s}", .{name});
39003469
3901 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);3470 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3902 const local_sym_index = try self.allocateLocalSymbol();3471 const sym_index = try self.allocateLocalSymbol();
3903 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));3472 const atom = try MachO.createEmptyAtom(
3473 gpa,
3474 sym_index,
3475 @sizeOf(u64),
3476 math.log2(required_alignment),
3477 );
3478
3479 try self.managed_atoms.append(gpa, atom);
3480 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
39043481
3905 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{3482 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
3906 .parent_atom_index = local_sym_index,3483 .parent_atom_index = sym_index,
3907 });3484 });
3908 const code = switch (res) {3485 const code = switch (res) {
3909 .externally_managed => |x| x,3486 .externally_managed => |x| x,
...@@ -3917,7 +3494,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3917,7 +3494,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
3917 };3494 };
39183495
3919 atom.code.clearRetainingCapacity();3496 atom.code.clearRetainingCapacity();
3920 try atom.code.appendSlice(self.base.allocator, code);3497 try atom.code.appendSlice(gpa, code);
39213498
3922 const match = try self.getMatchingSectionAtom(3499 const match = try self.getMatchingSectionAtom(
3923 atom,3500 atom,
...@@ -3933,18 +3510,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -3933,18 +3510,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
39333510
3934 errdefer self.freeAtom(atom, match, true);3511 errdefer self.freeAtom(atom, match, true);
39353512
3936 const symbol = &self.locals.items[atom.local_sym_index];3513 const symbol = &self.locals.items[atom.sym_index];
3937 symbol.* = .{3514 symbol.* = .{
3938 .n_strx = name_str_index,3515 .n_strx = name_str_index,
3939 .n_type = macho.N_SECT,3516 .n_type = macho.N_SECT,
3940 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,3517 .n_sect = self.getSectionOrdinal(match),
3941 .n_desc = 0,3518 .n_desc = 0,
3942 .n_value = addr,3519 .n_value = addr,
3943 };3520 };
39443521
3945 try unnamed_consts.append(self.base.allocator, atom);3522 try unnamed_consts.append(gpa, atom);
39463523
3947 return atom.local_sym_index;3524 return atom.sym_index;
3948}3525}
39493526
3950pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {3527pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -3986,14 +3563,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -3986,14 +3563,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
3986 }, &code_buffer, .{3563 }, &code_buffer, .{
3987 .dwarf = ds,3564 .dwarf = ds,
3988 }, .{3565 }, .{
3989 .parent_atom_index = decl.link.macho.local_sym_index,3566 .parent_atom_index = decl.link.macho.sym_index,
3990 })3567 })
3991 else3568 else
3992 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{3569 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
3993 .ty = decl.ty,3570 .ty = decl.ty,
3994 .val = decl_val,3571 .val = decl_val,
3995 }, &code_buffer, .none, .{3572 }, &code_buffer, .none, .{
3996 .parent_atom_index = decl.link.macho.local_sym_index,3573 .parent_atom_index = decl.link.macho.sym_index,
3997 });3574 });
39983575
3999 const code = blk: {3576 const code = blk: {
...@@ -4168,8 +3745,7 @@ fn getMatchingSectionAtom(...@@ -4168,8 +3745,7 @@ fn getMatchingSectionAtom(
4168 .@"align" = align_log_2,3745 .@"align" = align_log_2,
4169 })).?;3746 })).?;
4170 };3747 };
4171 const seg = self.load_commands.items[match.seg].segment;3748 const sect = self.getSection(match);
4172 const sect = seg.sections.items[match.sect];
4173 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{3749 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
4174 name,3750 name,
4175 sect.segName(),3751 sect.segName(),
...@@ -4184,8 +3760,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4184,8 +3760,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4184 const module = self.base.options.module.?;3760 const module = self.base.options.module.?;
4185 const decl = module.declPtr(decl_index);3761 const decl = module.declPtr(decl_index);
4186 const required_alignment = decl.getAlignment(self.base.options.target);3762 const required_alignment = decl.getAlignment(self.base.options.target);
4187 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()3763 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
4188 const symbol = &self.locals.items[decl.link.macho.local_sym_index];3764 const symbol = &self.locals.items[decl.link.macho.sym_index];
41893765
4190 const sym_name = try decl.getFullyQualifiedName(module);3766 const sym_name = try decl.getFullyQualifiedName(module);
4191 defer self.base.allocator.free(sym_name);3767 defer self.base.allocator.free(sym_name);
...@@ -4203,7 +3779,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4203,7 +3779,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4203 const match = decl_ptr.*.?;3779 const match = decl_ptr.*.?;
42043780
4205 if (decl.link.macho.size != 0) {3781 if (decl.link.macho.size != 0) {
4206 const capacity = decl.link.macho.capacity(self.*);3782 const capacity = decl.link.macho.capacity(self);
4207 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);3783 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
42083784
4209 if (need_realloc) {3785 if (need_realloc) {
...@@ -4217,12 +3793,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4217,12 +3793,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4217 decl.link.macho.size = code_len;3793 decl.link.macho.size = code_len;
4218 decl.link.macho.dirty = true;3794 decl.link.macho.dirty = true;
42193795
4220 symbol.n_strx = try self.makeString(sym_name);3796 symbol.n_strx = try self.strtab.insert(self.base.allocator, sym_name);
4221 symbol.n_type = macho.N_SECT;3797 symbol.n_type = macho.N_SECT;
4222 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;3798 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
4223 symbol.n_desc = 0;3799 symbol.n_desc = 0;
4224 } else {3800 } else {
4225 const name_str_index = try self.makeString(sym_name);3801 const name_str_index = try self.strtab.insert(self.base.allocator, sym_name);
4226 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);3802 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
42273803
4228 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });3804 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, addr });
...@@ -4233,22 +3809,18 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4233,22 +3809,18 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4233 symbol.* = .{3809 symbol.* = .{
4234 .n_strx = name_str_index,3810 .n_strx = name_str_index,
4235 .n_type = macho.N_SECT,3811 .n_type = macho.N_SECT,
4236 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,3812 .n_sect = self.getSectionOrdinal(match),
4237 .n_desc = 0,3813 .n_desc = 0,
4238 .n_value = addr,3814 .n_value = addr,
4239 };3815 };
4240 const got_index = self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index }).?;3816
4241 const got_atom = self.got_entries.items[got_index].atom;3817 const got_target = SymbolWithLoc{
4242 const got_sym = &self.locals.items[got_atom.local_sym_index];3818 .sym_index = decl.link.macho.sym_index,
4243 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{3819 .file = null,
4244 .seg = self.data_const_segment_cmd_index.?,3820 };
4245 .sect = self.got_section_index.?,3821 const got_index = self.got_entries_table.get(got_target).?;
4246 });3822 const got_atom = try self.createGotAtom(got_target);
4247 got_sym.n_value = vaddr;3823 self.got_entries.items[got_index].atom = got_atom;
4248 got_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(.{
4249 .seg = self.data_const_segment_cmd_index.?,
4250 .sect = self.got_section_index.?,
4251 }).? + 1);
4252 }3824 }
42533825
4254 return symbol;3826 return symbol;
...@@ -4278,8 +3850,8 @@ pub fn updateDeclExports(...@@ -4278,8 +3850,8 @@ pub fn updateDeclExports(
42783850
4279 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);3851 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);
4280 const decl = module.declPtr(decl_index);3852 const decl = module.declPtr(decl_index);
4281 if (decl.link.macho.local_sym_index == 0) return;3853 if (decl.link.macho.sym_index == 0) return;
4282 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];3854 const decl_sym = &self.locals.items[decl.link.macho.sym_index];
42833855
4284 for (exports) |exp| {3856 for (exports) |exp| {
4285 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});3857 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
...@@ -4316,46 +3888,47 @@ pub fn updateDeclExports(...@@ -4316,46 +3888,47 @@ pub fn updateDeclExports(
4316 }3888 }
43173889
4318 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;3890 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
4319 const n_strx = try self.makeString(exp_name);3891 _ = is_weak;
4320 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {3892 const n_strx = try self.strtab.insert(self.base.allocator, exp_name);
4321 switch (resolv.where) {3893 // if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
4322 .global => {3894 // switch (resolv.where) {
4323 if (resolv.local_sym_index == decl.link.macho.local_sym_index) continue;3895 // .global => {
43243896 // if (resolv.sym_index == decl.link.macho.sym_index) continue;
4325 const sym = &self.globals.items[resolv.where_index];3897
43263898 // const sym = &self.globals.items[resolv.where_index];
4327 if (sym.tentative()) {3899
4328 assert(self.tentatives.swapRemove(resolv.where_index));3900 // if (sym.tentative()) {
4329 } else if (!is_weak and !(sym.weakDef() or sym.pext())) {3901 // assert(self.tentatives.swapRemove(resolv.where_index));
4330 _ = try module.failed_exports.put(3902 // } else if (!is_weak and !(sym.weakDef() or sym.pext())) {
4331 module.gpa,3903 // _ = try module.failed_exports.put(
4332 exp,3904 // module.gpa,
4333 try Module.ErrorMsg.create(3905 // exp,
4334 self.base.allocator,3906 // try Module.ErrorMsg.create(
4335 decl.srcLoc(),3907 // self.base.allocator,
4336 \\LinkError: symbol '{s}' defined multiple times3908 // decl.srcLoc(),
4337 \\ first definition in '{s}'3909 // \\LinkError: symbol '{s}' defined multiple times
4338 ,3910 // \\ first definition in '{s}'
4339 .{ exp_name, self.objects.items[resolv.file.?].name },3911 // ,
4340 ),3912 // .{ exp_name, self.objects.items[resolv.file.?].name },
4341 );3913 // ),
4342 continue;3914 // );
4343 } else if (is_weak) continue; // Current symbol is weak, so skip it.3915 // continue;
43443916 // } else if (is_weak) continue; // Current symbol is weak, so skip it.
4345 // Otherwise, update the resolver and the global symbol.3917
4346 sym.n_type = macho.N_SECT | macho.N_EXT;3918 // // Otherwise, update the resolver and the global symbol.
4347 resolv.local_sym_index = decl.link.macho.local_sym_index;3919 // sym.n_type = macho.N_SECT | macho.N_EXT;
4348 resolv.file = null;3920 // resolv.sym_index = decl.link.macho.sym_index;
4349 exp.link.macho.sym_index = resolv.where_index;3921 // resolv.file = null;
43503922 // exp.link.macho.sym_index = resolv.where_index;
4351 continue;3923
4352 },3924 // continue;
4353 .undef => {3925 // },
4354 assert(self.unresolved.swapRemove(resolv.where_index));3926 // .undef => {
4355 _ = self.symbol_resolver.remove(n_strx);3927 // assert(self.unresolved.swapRemove(resolv.where_index));
4356 },3928 // _ = self.symbol_resolver.remove(n_strx);
4357 }3929 // },
4358 }3930 // }
3931 // }
43593932
4360 var n_type: u8 = macho.N_SECT | macho.N_EXT;3933 var n_type: u8 = macho.N_SECT | macho.N_EXT;
4361 var n_desc: u16 = 0;3934 var n_desc: u16 = 0;
...@@ -4377,41 +3950,44 @@ pub fn updateDeclExports(...@@ -4377,41 +3950,44 @@ pub fn updateDeclExports(
4377 else => unreachable,3950 else => unreachable,
4378 }3951 }
43793952
4380 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {3953 const global_sym_index: u32 = 0;
4381 const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {3954 // const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
4382 _ = self.globals.addOneAssumeCapacity();3955 // const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
4383 break :inner @intCast(u32, self.globals.items.len - 1);3956 // _ = self.globals.addOneAssumeCapacity();
4384 };3957 // break :inner @intCast(u32, self.globals.items.len - 1);
4385 break :blk i;3958 // };
4386 };3959 // break :blk i;
4387 const sym = &self.globals.items[global_sym_index];3960 // };
3961 const sym = &self.locals.items[global_sym_index];
4388 sym.* = .{3962 sym.* = .{
4389 .n_strx = try self.makeString(exp_name),3963 .n_strx = try self.strtab.insert(self.base.allocator, exp_name),
4390 .n_type = n_type,3964 .n_type = n_type,
4391 .n_sect = @intCast(u8, self.text_section_index.?) + 1,3965 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
4392 .n_desc = n_desc,3966 .n_desc = n_desc,
4393 .n_value = decl_sym.n_value,3967 .n_value = decl_sym.n_value,
4394 };3968 };
4395 exp.link.macho.sym_index = global_sym_index;3969 exp.link.macho.sym_index = global_sym_index;
3970 _ = n_strx;
43963971
4397 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{3972 // try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4398 .where = .global,3973 // .where = .global,
4399 .where_index = global_sym_index,3974 // .where_index = global_sym_index,
4400 .local_sym_index = decl.link.macho.local_sym_index,3975 // .sym_index = decl.link.macho.sym_index,
4401 });3976 // });
4402 }3977 }
4403}3978}
44043979
4405pub fn deleteExport(self: *MachO, exp: Export) void {3980pub fn deleteExport(self: *MachO, exp: Export) void {
4406 if (self.llvm_object) |_| return;3981 if (self.llvm_object) |_| return;
4407 const sym_index = exp.sym_index orelse return;3982 const sym_index = exp.sym_index orelse return;
4408 self.globals_free_list.append(self.base.allocator, sym_index) catch {};3983 _ = sym_index;
4409 const global = &self.globals.items[sym_index];3984 // self.globals_free_list.append(self.base.allocator, sym_index) catch {};
4410 log.debug("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });3985 // const global = &self.globals.items[sym_index];
4411 assert(self.symbol_resolver.remove(global.n_strx));3986 // log.warn("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });
4412 global.n_type = 0;3987 // assert(self.symbol_resolver.remove(global.n_strx));
4413 global.n_strx = 0;3988 // global.n_type = 0;
4414 global.n_value = 0;3989 // global.n_strx = 0;
3990 // global.n_value = 0;
4415}3991}
44163992
4417fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {3993fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -4421,11 +3997,11 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4421,11 +3997,11 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
4421 .seg = self.text_segment_cmd_index.?,3997 .seg = self.text_segment_cmd_index.?,
4422 .sect = self.text_const_section_index.?,3998 .sect = self.text_const_section_index.?,
4423 }, true);3999 }, true);
4424 self.locals_free_list.append(self.base.allocator, atom.local_sym_index) catch {};4000 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
4425 self.locals.items[atom.local_sym_index].n_type = 0;4001 self.locals.items[atom.sym_index].n_type = 0;
4426 _ = self.atom_by_index_table.remove(atom.local_sym_index);4002 _ = self.atom_by_index_table.remove(atom.sym_index);
4427 log.debug(" adding local symbol index {d} to free list", .{atom.local_sym_index});4003 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
4428 atom.local_sym_index = 0;4004 atom.sym_index = 0;
4429 }4005 }
4430 unnamed_consts.clearAndFree(self.base.allocator);4006 unnamed_consts.clearAndFree(self.base.allocator);
4431}4007}
...@@ -4443,29 +4019,30 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4443,29 +4019,30 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
4443 self.freeUnnamedConsts(decl_index);4019 self.freeUnnamedConsts(decl_index);
4444 }4020 }
4445 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.4021 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
4446 if (decl.link.macho.local_sym_index != 0) {4022 if (decl.link.macho.sym_index != 0) {
4447 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};4023 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};
44484024
4449 // Try freeing GOT atom if this decl had one4025 // Try freeing GOT atom if this decl had one
4450 if (self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index })) |got_index| {4026 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
4027 if (self.got_entries_table.get(got_target)) |got_index| {
4451 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};4028 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
4452 self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };4029 self.got_entries.items[got_index] = .{ .target = .{ .sym_index = 0, .file = null }, .atom = undefined };
4453 _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });4030 _ = self.got_entries_table.swapRemove(got_target);
44544031
4455 if (self.d_sym) |*d_sym| {4032 if (self.d_sym) |*d_sym| {
4456 d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index);4033 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);
4457 }4034 }
44584035
4459 log.debug(" adding GOT index {d} to free list (target local@{d})", .{4036 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
4460 got_index,4037 got_index,
4461 decl.link.macho.local_sym_index,4038 decl.link.macho.sym_index,
4462 });4039 });
4463 }4040 }
44644041
4465 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;4042 self.locals.items[decl.link.macho.sym_index].n_type = 0;
4466 _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index);4043 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);
4467 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.local_sym_index});4044 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});
4468 decl.link.macho.local_sym_index = 0;4045 decl.link.macho.sym_index = 0;
4469 }4046 }
4470 if (self.d_sym) |*d_sym| {4047 if (self.d_sym) |*d_sym| {
4471 d_sym.dwarf.freeDecl(decl);4048 d_sym.dwarf.freeDecl(decl);
...@@ -4477,12 +4054,12 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -4477,12 +4054,12 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
4477 const decl = mod.declPtr(decl_index);4054 const decl = mod.declPtr(decl_index);
44784055
4479 assert(self.llvm_object == null);4056 assert(self.llvm_object == null);
4480 assert(decl.link.macho.local_sym_index != 0);4057 assert(decl.link.macho.sym_index != 0);
44814058
4482 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;4059 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
4483 try atom.relocs.append(self.base.allocator, .{4060 try atom.relocs.append(self.base.allocator, .{
4484 .offset = @intCast(u32, reloc_info.offset),4061 .offset = @intCast(u32, reloc_info.offset),
4485 .target = .{ .local = decl.link.macho.local_sym_index },4062 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
4486 .addend = reloc_info.addend,4063 .addend = reloc_info.addend,
4487 .subtractor = null,4064 .subtractor = null,
4488 .pcrel = false,4065 .pcrel = false,
...@@ -5019,8 +4596,6 @@ fn populateMissingMetadata(self: *MachO) !void {...@@ -5019,8 +4596,6 @@ fn populateMissingMetadata(self: *MachO) !void {
5019 });4596 });
5020 self.load_commands_dirty = true;4597 self.load_commands_dirty = true;
5021 }4598 }
5022
5023 self.cold_start = true;
5024}4599}
50254600
5026fn calcMinHeaderpad(self: *MachO) u64 {4601fn calcMinHeaderpad(self: *MachO) u64 {
...@@ -5121,7 +4696,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_...@@ -5121,7 +4696,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_
51214696
5122 // Allocate the sections according to their alignment at the beginning of the segment.4697 // Allocate the sections according to their alignment at the beginning of the segment.
5123 var start = init_size;4698 var start = init_size;
5124 for (seg.sections.items) |*sect, sect_id| {4699 for (seg.sections.items) |*sect| {
5125 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;4700 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
5126 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;4701 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
5127 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;4702 const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1;
...@@ -5129,32 +4704,12 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_...@@ -5129,32 +4704,12 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_
5129 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);4704 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
51304705
5131 // TODO handle zerofill sections in stage24706 // TODO handle zerofill sections in stage2
5132 sect.offset = if (is_zerofill and (use_stage1 or use_llvm)) 0 else @intCast(u32, seg.inner.fileoff + start_aligned);4707 sect.offset = if (is_zerofill and (use_stage1 or use_llvm))
4708 0
4709 else
4710 @intCast(u32, seg.inner.fileoff + start_aligned);
5133 sect.addr = seg.inner.vmaddr + start_aligned;4711 sect.addr = seg.inner.vmaddr + start_aligned;
51344712
5135 // Recalculate section size given the allocated start address
5136 sect.size = if (self.atoms.get(.{
5137 .seg = index,
5138 .sect = @intCast(u16, sect_id),
5139 })) |last_atom| blk: {
5140 var atom = last_atom;
5141 while (atom.prev) |prev| {
5142 atom = prev;
5143 }
5144
5145 var base_addr = sect.addr;
5146
5147 while (true) {
5148 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5149 base_addr = mem.alignForwardGeneric(u64, base_addr, atom_alignment) + atom.size;
5150 if (atom.next) |next| {
5151 atom = next;
5152 } else break;
5153 }
5154
5155 break :blk base_addr - sect.addr;
5156 } else 0;
5157
5158 start = start_aligned + sect.size;4713 start = start_aligned + sect.size;
51594714
5160 if (!(is_zerofill and (use_stage1 or use_llvm))) {4715 if (!(is_zerofill and (use_stage1 or use_llvm))) {
...@@ -5410,12 +4965,30 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3...@@ -5410,12 +4965,30 @@ fn getSectionMaxAlignment(self: *MachO, segment_id: u16, start_sect_id: u16) !u3
5410 return max_alignment;4965 return max_alignment;
5411}4966}
54124967
5413fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, match: MatchingSection) !u64 {4968fn allocateAtomCommon(self: *MachO, atom: *Atom, match: MatchingSection) !void {
4969 const sym = atom.getSymbolPtr(self);
4970 if (self.needs_prealloc) {
4971 const size = atom.size;
4972 const alignment = try math.powi(u32, 2, atom.alignment);
4973 const vaddr = try self.allocateAtom(atom, size, alignment, match);
4974 const sym_name = atom.getName(self);
4975 log.debug("allocated {s} atom at 0x{x}", .{ sym_name, vaddr });
4976 sym.n_value = vaddr;
4977 } else try self.addAtomToSection(atom, match);
4978 sym.n_sect = self.getSectionOrdinal(match);
4979}
4980
4981fn allocateAtom(
4982 self: *MachO,
4983 atom: *Atom,
4984 new_atom_size: u64,
4985 alignment: u64,
4986 match: MatchingSection,
4987) !u64 {
5414 const tracy = trace(@src());4988 const tracy = trace(@src());
5415 defer tracy.end();4989 defer tracy.end();
54164990
5417 const seg = &self.load_commands.items[match.seg].segment;4991 const sect = self.getSectionPtr(match);
5418 const sect = &seg.sections.items[match.sect];
5419 var free_list = self.atom_free_lists.get(match).?;4992 var free_list = self.atom_free_lists.get(match).?;
5420 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;4993 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
5421 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;4994 const new_atom_ideal_capacity = if (needs_padding) padToIdeal(new_atom_size) else new_atom_size;
...@@ -5436,8 +5009,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5436,8 +5009,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5436 const big_atom = free_list.items[i];5009 const big_atom = free_list.items[i];
5437 // We now have a pointer to a live atom that has too much capacity.5010 // We now have a pointer to a live atom that has too much capacity.
5438 // Is it enough that we could fit this new atom?5011 // Is it enough that we could fit this new atom?
5439 const sym = self.locals.items[big_atom.local_sym_index];5012 const sym = self.locals.items[big_atom.sym_index];
5440 const capacity = big_atom.capacity(self.*);5013 const capacity = big_atom.capacity(self);
5441 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;5014 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
5442 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;5015 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
5443 const capacity_end_vaddr = sym.n_value + capacity;5016 const capacity_end_vaddr = sym.n_value + capacity;
...@@ -5447,7 +5020,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5447,7 +5020,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5447 // Additional bookkeeping here to notice if this free list node5020 // Additional bookkeeping here to notice if this free list node
5448 // should be deleted because the atom that it points to has grown to take up5021 // should be deleted because the atom that it points to has grown to take up
5449 // more of the extra capacity.5022 // more of the extra capacity.
5450 if (!big_atom.freeListEligible(self.*)) {5023 if (!big_atom.freeListEligible(self)) {
5451 _ = free_list.swapRemove(i);5024 _ = free_list.swapRemove(i);
5452 } else {5025 } else {
5453 i += 1;5026 i += 1;
...@@ -5467,7 +5040,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5467,7 +5040,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5467 }5040 }
5468 break :blk new_start_vaddr;5041 break :blk new_start_vaddr;
5469 } else if (self.atoms.get(match)) |last| {5042 } else if (self.atoms.get(match)) |last| {
5470 const last_symbol = self.locals.items[last.local_sym_index];5043 const last_symbol = self.locals.items[last.sym_index];
5471 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;5044 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
5472 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;5045 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
5473 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);5046 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
...@@ -5516,7 +5089,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m...@@ -5516,7 +5089,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
5516 return vaddr;5089 return vaddr;
5517}5090}
55185091
5519fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {5092pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5520 if (self.atoms.getPtr(match)) |last| {5093 if (self.atoms.getPtr(match)) |last| {
5521 last.*.next = atom;5094 last.*.next = atom;
5522 atom.prev = last.*;5095 atom.prev = last.*;
...@@ -5524,34 +5097,38 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {...@@ -5524,34 +5097,38 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5524 } else {5097 } else {
5525 try self.atoms.putNoClobber(self.base.allocator, match, atom);5098 try self.atoms.putNoClobber(self.base.allocator, match, atom);
5526 }5099 }
5527 const seg = &self.load_commands.items[match.seg].segment;5100 const sect = self.getSectionPtr(match);
5528 const sect = &seg.sections.items[match.sect];5101 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5529 sect.size += atom.size;5102 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5103 const padding = aligned_end_addr - sect.size;
5104 sect.size += padding + atom.size;
5105 sect.@"align" = @maximum(sect.@"align", atom.alignment);
5530}5106}
55315107
5532pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {5108pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5533 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});5109 const gpa = self.base.allocator;
5534 defer self.base.allocator.free(sym_name);5110 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
5535 const n_strx = try self.makeString(sym_name);5111 defer gpa.free(sym_name);
55365112
5537 if (!self.symbol_resolver.contains(n_strx)) {5113 if (self.globals.getIndex(sym_name)) |global_index| {
5538 log.debug("adding new extern function '{s}'", .{sym_name});5114 return @intCast(u32, global_index);
5539 const sym_index = @intCast(u32, self.undefs.items.len);
5540 try self.undefs.append(self.base.allocator, .{
5541 .n_strx = n_strx,
5542 .n_type = macho.N_UNDF,
5543 .n_sect = 0,
5544 .n_desc = 0,
5545 .n_value = 0,
5546 });
5547 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
5548 .where = .undef,
5549 .where_index = sym_index,
5550 });
5551 try self.unresolved.putNoClobber(self.base.allocator, sym_index, .stub);
5552 }5115 }
55535116
5554 return n_strx;5117 const n_strx = try self.strtab.insert(gpa, sym_name);
5118 const sym_index = @intCast(u32, self.locals.items.len);
5119 try self.locals.append(gpa, .{
5120 .n_strx = n_strx,
5121 .n_type = macho.N_UNDF,
5122 .n_sect = 0,
5123 .n_desc = 0,
5124 .n_value = 0,
5125 });
5126 try self.globals.putNoClobber(gpa, sym_name, .{
5127 .sym_index = sym_index,
5128 .file = null,
5129 });
5130 const global_index = self.globals.getIndex(sym_name).?;
5131 return @intCast(u32, global_index);
5555}5132}
55565133
5557fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {5134fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
...@@ -5579,15 +5156,44 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5579,15 +5156,44 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
55795156
5580 for (indices) |maybe_index| {5157 for (indices) |maybe_index| {
5581 const old_idx = maybe_index.* orelse continue;5158 const old_idx = maybe_index.* orelse continue;
5582 const sect = sections[old_idx];5159 const sect = &sections[old_idx];
5160
5161 // Recalculate section alignment and size if required.
5162 const match = MatchingSection{
5163 .seg = seg_id,
5164 .sect = old_idx,
5165 };
5166 if (self.gc_sections.get(match)) |_| blk: {
5167 sect.@"align" = 0;
5168 sect.size = 0;
5169
5170 var atom = self.atoms.get(match) orelse break :blk;
5171
5172 while (atom.prev) |prev| {
5173 atom = prev;
5174 }
5175
5176 while (true) {
5177 const atom_alignment = try math.powi(u32, 2, atom.alignment);
5178 const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
5179 const padding = aligned_end_addr - sect.size;
5180 sect.size += padding + atom.size;
5181 sect.@"align" = @maximum(sect.@"align", atom.alignment);
5182
5183 if (atom.next) |next| {
5184 atom = next;
5185 } else break;
5186 }
5187 }
5188
5583 if (sect.size == 0) {5189 if (sect.size == 0) {
5584 log.warn("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });5190 log.debug("pruning section {s},{s}", .{ sect.segName(), sect.sectName() });
5585 maybe_index.* = null;5191 maybe_index.* = null;
5586 seg.inner.cmdsize -= @sizeOf(macho.section_64);5192 seg.inner.cmdsize -= @sizeOf(macho.section_64);
5587 seg.inner.nsects -= 1;5193 seg.inner.nsects -= 1;
5588 } else {5194 } else {
5589 maybe_index.* = @intCast(u16, seg.sections.items.len);5195 maybe_index.* = @intCast(u16, seg.sections.items.len);
5590 seg.sections.appendAssumeCapacity(sect);5196 seg.sections.appendAssumeCapacity(sect.*);
5591 }5197 }
5592 try mapping.putNoClobber(old_idx, maybe_index.*);5198 try mapping.putNoClobber(old_idx, maybe_index.*);
5593 }5199 }
...@@ -5614,7 +5220,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*...@@ -5614,7 +5220,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
56145220
5615 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {5221 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
5616 // Segment has now become empty, so mark it as such5222 // Segment has now become empty, so mark it as such
5617 log.warn("marking segment {s} as dead", .{seg.inner.segName()});5223 log.debug("marking segment {s} as dead", .{seg.inner.segName()});
5618 seg.inner.cmd = @intToEnum(macho.LC, 0);5224 seg.inner.cmd = @intToEnum(macho.LC, 0);
5619 maybe_seg_id.* = null;5225 maybe_seg_id.* = null;
5620 }5226 }
...@@ -5697,36 +5303,22 @@ fn pruneAndSortSections(self: *MachO) !void {...@@ -5697,36 +5303,22 @@ fn pruneAndSortSections(self: *MachO) !void {
5697}5303}
56985304
5699fn gcAtoms(self: *MachO) !void {5305fn gcAtoms(self: *MachO) !void {
5700 const dead_strip = self.base.options.gc_sections orelse false;5306 const dead_strip = self.base.options.gc_sections orelse return;
5701 if (!dead_strip) return;5307 if (!dead_strip) return;
57025308
5309 const gpa = self.base.allocator;
5310
5703 // Add all exports as GC roots5311 // Add all exports as GC roots
5704 for (self.globals.items) |sym| {5312 for (self.globals.values()) |global| {
5705 if (sym.n_type == 0) continue;5313 const sym = self.getSymbol(global);
5706 const resolv = self.symbol_resolver.get(sym.n_strx).?;5314 if (!sym.sect()) continue;
5707 assert(resolv.where == .global);5315 const gc_root = self.getAtomForSymbol(global) orelse {
5708 const gc_root = self.atom_by_index_table.get(resolv.local_sym_index) orelse {5316 log.debug("skipping {s}", .{self.getSymbolName(global)});
5709 log.warn("skipping {s}", .{self.getString(sym.n_strx)});
5710 continue;5317 continue;
5711 };5318 };
5712 _ = try self.gc_roots.getOrPut(self.base.allocator, gc_root);5319 _ = try self.gc_roots.getOrPut(gpa, gc_root);
5713 }5320 }
57145321
5715 // if (self.tlv_ptrs_section_index) |sect| {
5716 // var atom = self.atoms.get(.{
5717 // .seg = self.data_segment_cmd_index.?,
5718 // .sect = sect,
5719 // }).?;
5720
5721 // while (true) {
5722 // _ = try self.gc_roots.getOrPut(self.base.allocator, atom);
5723
5724 // if (atom.prev) |prev| {
5725 // atom = prev;
5726 // } else break;
5727 // }
5728 // }
5729
5730 // Add any atom targeting an import as GC root5322 // Add any atom targeting an import as GC root
5731 var atoms_it = self.atoms.iterator();5323 var atoms_it = self.atoms.iterator();
5732 while (atoms_it.next()) |entry| {5324 while (atoms_it.next()) |entry| {
...@@ -5734,19 +5326,13 @@ fn gcAtoms(self: *MachO) !void {...@@ -5734,19 +5326,13 @@ fn gcAtoms(self: *MachO) !void {
57345326
5735 while (true) {5327 while (true) {
5736 for (atom.relocs.items) |rel| {5328 for (atom.relocs.items) |rel| {
5737 if ((try Atom.getTargetAtom(rel, self)) == null) switch (rel.target) {5329 if ((try rel.getTargetAtom(self)) == null) {
5738 .local => {},5330 const target_sym = self.getSymbol(rel.target);
5739 .global => |n_strx| {5331 if (target_sym.undf()) {
5740 const resolv = self.symbol_resolver.get(n_strx).?;5332 _ = try self.gc_roots.getOrPut(gpa, atom);
5741 switch (resolv.where) {5333 break;
5742 .global => {},5334 }
5743 .undef => {5335 }
5744 _ = try self.gc_roots.getOrPut(self.base.allocator, atom);
5745 break;
5746 },
5747 }
5748 },
5749 };
5750 }5336 }
57515337
5752 if (atom.prev) |prev| {5338 if (atom.prev) |prev| {
...@@ -5755,15 +5341,15 @@ fn gcAtoms(self: *MachO) !void {...@@ -5755,15 +5341,15 @@ fn gcAtoms(self: *MachO) !void {
5755 }5341 }
5756 }5342 }
57575343
5758 var stack = std.ArrayList(*Atom).init(self.base.allocator);5344 var stack = std.ArrayList(*Atom).init(gpa);
5759 defer stack.deinit();5345 defer stack.deinit();
5760 try stack.ensureUnusedCapacity(self.gc_roots.count());5346 try stack.ensureUnusedCapacity(self.gc_roots.count());
57615347
5762 var retained = std.AutoHashMap(*Atom, void).init(self.base.allocator);5348 var retained = std.AutoHashMap(*Atom, void).init(gpa);
5763 defer retained.deinit();5349 defer retained.deinit();
5764 try retained.ensureUnusedCapacity(self.gc_roots.count());5350 try retained.ensureUnusedCapacity(self.gc_roots.count());
57655351
5766 log.warn("GC roots:", .{});5352 log.debug("GC roots:", .{});
5767 var gc_roots_it = self.gc_roots.keyIterator();5353 var gc_roots_it = self.gc_roots.keyIterator();
5768 while (gc_roots_it.next()) |gc_root| {5354 while (gc_roots_it.next()) |gc_root| {
5769 self.logAtom(gc_root.*);5355 self.logAtom(gc_root.*);
...@@ -5772,15 +5358,15 @@ fn gcAtoms(self: *MachO) !void {...@@ -5772,15 +5358,15 @@ fn gcAtoms(self: *MachO) !void {
5772 retained.putAssumeCapacityNoClobber(gc_root.*, {});5358 retained.putAssumeCapacityNoClobber(gc_root.*, {});
5773 }5359 }
57745360
5775 log.warn("walking tree...", .{});5361 log.debug("walking tree...", .{});
5776 while (stack.popOrNull()) |source_atom| {5362 while (stack.popOrNull()) |source_atom| {
5777 for (source_atom.relocs.items) |rel| {5363 for (source_atom.relocs.items) |rel| {
5778 if (try Atom.getTargetAtom(rel, self)) |target_atom| {5364 if (try rel.getTargetAtom(self)) |target_atom| {
5779 const gop = try retained.getOrPut(target_atom);5365 const gop = try retained.getOrPut(target_atom);
5780 if (!gop.found_existing) {5366 if (!gop.found_existing) {
5781 log.warn(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{5367 log.debug(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{
5782 source_atom.local_sym_index,5368 source_atom.sym_index,
5783 target_atom.local_sym_index,5369 target_atom.sym_index,
5784 });5370 });
5785 try stack.append(target_atom);5371 try stack.append(target_atom);
5786 }5372 }
...@@ -5808,58 +5394,38 @@ fn gcAtoms(self: *MachO) !void {...@@ -5808,58 +5394,38 @@ fn gcAtoms(self: *MachO) !void {
5808 }5394 }
5809 }5395 }
58105396
5811 const seg = &self.load_commands.items[match.seg].segment;5397 const sect = self.getSectionPtr(match);
5812 const sect = &seg.sections.items[match.sect];
5813 var atom = entry.value_ptr.*;5398 var atom = entry.value_ptr.*;
58145399
5815 log.warn("GCing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });5400 log.debug("GCing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });
58165401
5817 while (true) {5402 while (true) {
5818 const orig_prev = atom.prev;5403 const orig_prev = atom.prev;
58195404
5820 if (!retained.contains(atom)) {5405 if (!retained.contains(atom)) {
5821 // Dead atom; remove.5406 // Dead atom; remove.
5822 log.warn(" DEAD ATOM(%{d})", .{atom.local_sym_index});5407 log.debug(" DEAD ATOM(%{d})", .{atom.sym_index});
58235408
5824 const sym = &self.locals.items[atom.local_sym_index];5409 const sym = atom.getSymbolPtr(self);
5825 sym.n_desc = N_DESC_GCED;5410 sym.n_desc = N_DESC_GCED;
58265411
5827 if (self.symbol_resolver.getPtr(sym.n_strx)) |resolv| {5412 // TODO add full bookkeeping here
5828 if (resolv.local_sym_index == atom.local_sym_index) {5413 const global = SymbolWithLoc{ .sym_index = atom.sym_index, .file = atom.file };
5829 const global = &self.globals.items[resolv.where_index];5414 _ = self.got_entries_table.swapRemove(global);
5830 global.n_desc = N_DESC_GCED;5415 _ = self.stubs_table.swapRemove(global);
5831 }5416 _ = self.tlv_ptr_entries_table.swapRemove(global);
5832 }
5833
5834 for (self.got_entries.items) |got_entry| {
5835 if (got_entry.atom == atom) {
5836 _ = self.got_entries_table.swapRemove(got_entry.target);
5837 break;
5838 }
5839 }
5840
5841 for (self.stubs.items) |stub, i| {
5842 if (stub == atom) {
5843 _ = self.stubs_table.swapRemove(@intCast(u32, i));
5844 break;
5845 }
5846 }
58475417
5848 for (atom.contained.items) |sym_off| {5418 for (atom.contained.items) |sym_off| {
5849 const inner = &self.locals.items[sym_off.local_sym_index];5419 const inner = self.getSymbolPtr(.{
5420 .sym_index = sym_off.sym_index,
5421 .file = atom.file,
5422 });
5850 inner.n_desc = N_DESC_GCED;5423 inner.n_desc = N_DESC_GCED;
5851
5852 if (self.symbol_resolver.getPtr(inner.n_strx)) |resolv| {
5853 if (resolv.local_sym_index == atom.local_sym_index) {
5854 const global = &self.globals.items[resolv.where_index];
5855 global.n_desc = N_DESC_GCED;
5856 }
5857 }
5858 }5424 }
58595425 // If we want to enable GC for incremental codepath, we need to take into
5860 log.warn(" BEFORE size = {x}", .{sect.size});5426 // account any padding that might have been left here.
5861 sect.size -= atom.size;5427 sect.size -= atom.size;
5862 log.warn(" AFTER size = {x}", .{sect.size});5428
5863 if (atom.prev) |prev| {5429 if (atom.prev) |prev| {
5864 prev.next = atom.next;5430 prev.next = atom.next;
5865 }5431 }
...@@ -5870,6 +5436,8 @@ fn gcAtoms(self: *MachO) !void {...@@ -5870,6 +5436,8 @@ fn gcAtoms(self: *MachO) !void {
5870 // The section will be GCed in the next step.5436 // The section will be GCed in the next step.
5871 entry.value_ptr.* = if (atom.prev) |prev| prev else undefined;5437 entry.value_ptr.* = if (atom.prev) |prev| prev else undefined;
5872 }5438 }
5439
5440 _ = try self.gc_sections.getOrPut(gpa, match);
5873 }5441 }
58745442
5875 if (orig_prev) |prev| {5443 if (orig_prev) |prev| {
...@@ -5885,7 +5453,11 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5885,7 +5453,11 @@ fn updateSectionOrdinals(self: *MachO) !void {
5885 const tracy = trace(@src());5453 const tracy = trace(@src());
5886 defer tracy.end();5454 defer tracy.end();
58875455
5888 var ordinal_remap = std.AutoHashMap(u8, u8).init(self.base.allocator);5456 log.debug("updating section ordinals", .{});
5457
5458 const gpa = self.base.allocator;
5459
5460 var ordinal_remap = std.AutoHashMap(u8, u8).init(gpa);
5889 defer ordinal_remap.deinit();5461 defer ordinal_remap.deinit();
5890 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};5462 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
58915463
...@@ -5897,27 +5469,38 @@ fn updateSectionOrdinals(self: *MachO) !void {...@@ -5897,27 +5469,38 @@ fn updateSectionOrdinals(self: *MachO) !void {
5897 }) |maybe_index| {5469 }) |maybe_index| {
5898 const index = maybe_index orelse continue;5470 const index = maybe_index orelse continue;
5899 const seg = self.load_commands.items[index].segment;5471 const seg = self.load_commands.items[index].segment;
5900 for (seg.sections.items) |_, sect_id| {5472 for (seg.sections.items) |sect, sect_id| {
5901 const match = MatchingSection{5473 const match = MatchingSection{
5902 .seg = @intCast(u16, index),5474 .seg = @intCast(u16, index),
5903 .sect = @intCast(u16, sect_id),5475 .sect = @intCast(u16, sect_id),
5904 };5476 };
5905 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);5477 const old_ordinal = self.getSectionOrdinal(match);
5906 new_ordinal += 1;5478 new_ordinal += 1;
5479 log.debug("'{s},{s}': sect({d}, '_,_') => sect({d}, '_,_')", .{
5480 sect.segName(),
5481 sect.sectName(),
5482 old_ordinal,
5483 new_ordinal,
5484 });
5907 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);5485 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5908 try ordinals.putNoClobber(self.base.allocator, match, {});5486 try ordinals.putNoClobber(gpa, match, {});
5909 }5487 }
5910 }5488 }
59115489
5912 for (self.locals.items) |*sym| {5490 for (self.locals.items) |*sym| {
5491 if (sym.undf()) continue;
5913 if (sym.n_sect == 0) continue;5492 if (sym.n_sect == 0) continue;
5914 sym.n_sect = ordinal_remap.get(sym.n_sect).?;5493 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5915 }5494 }
5916 for (self.globals.items) |*sym| {5495 for (self.objects.items) |*object| {
5917 sym.n_sect = ordinal_remap.get(sym.n_sect).?;5496 for (object.symtab.items) |*sym| {
5497 if (sym.undf()) continue;
5498 if (sym.n_sect == 0) continue;
5499 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5500 }
5918 }5501 }
59195502
5920 self.section_ordinals.deinit(self.base.allocator);5503 self.section_ordinals.deinit(gpa);
5921 self.section_ordinals = ordinals;5504 self.section_ordinals = ordinals;
5922}5505}
59235506
...@@ -5925,11 +5508,13 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5925,11 +5508,13 @@ fn writeDyldInfoData(self: *MachO) !void {
5925 const tracy = trace(@src());5508 const tracy = trace(@src());
5926 defer tracy.end();5509 defer tracy.end();
59275510
5928 var rebase_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5511 const gpa = self.base.allocator;
5512
5513 var rebase_pointers = std.ArrayList(bind.Pointer).init(gpa);
5929 defer rebase_pointers.deinit();5514 defer rebase_pointers.deinit();
5930 var bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5515 var bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5931 defer bind_pointers.deinit();5516 defer bind_pointers.deinit();
5932 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator);5517 var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(gpa);
5933 defer lazy_bind_pointers.deinit();5518 defer lazy_bind_pointers.deinit();
59345519
5935 {5520 {
...@@ -5942,13 +5527,13 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5942,13 +5527,13 @@ fn writeDyldInfoData(self: *MachO) !void {
5942 if (match.seg == seg) continue; // __TEXT is non-writable5527 if (match.seg == seg) continue; // __TEXT is non-writable
5943 }5528 }
59445529
5945 const seg = self.load_commands.items[match.seg].segment;5530 const seg = self.getSegment(match);
5946 const sect = seg.sections.items[match.sect];5531 const sect = self.getSection(match);
5947 log.warn("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });5532 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
59485533
5949 while (true) {5534 while (true) {
5950 log.warn(" ATOM %{d}", .{atom.local_sym_index});5535 log.debug(" ATOM %{d}", .{atom.sym_index});
5951 const sym = self.locals.items[atom.local_sym_index];5536 const sym = atom.getSymbol(self);
5952 const base_offset = sym.n_value - seg.inner.vmaddr;5537 const base_offset = sym.n_value - seg.inner.vmaddr;
59535538
5954 for (atom.rebases.items) |offset| {5539 for (atom.rebases.items) |offset| {
...@@ -5959,57 +5544,35 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5959,57 +5544,35 @@ fn writeDyldInfoData(self: *MachO) !void {
5959 }5544 }
59605545
5961 for (atom.bindings.items) |binding| {5546 for (atom.bindings.items) |binding| {
5962 const resolv = self.symbol_resolver.get(binding.n_strx).?;5547 const global = self.globals.values()[binding.global_index];
5963 switch (resolv.where) {5548 const bind_sym = self.getSymbol(global);
5964 .global => {5549 var flags: u4 = 0;
5965 // Turn into a rebase.5550 if (bind_sym.weakRef()) {
5966 try rebase_pointers.append(.{5551 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5967 .offset = base_offset + binding.offset,
5968 .segment_id = match.seg,
5969 });
5970 },
5971 .undef => {
5972 const bind_sym = self.undefs.items[resolv.where_index];
5973 var flags: u4 = 0;
5974 if (bind_sym.weakRef()) {
5975 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5976 }
5977 try bind_pointers.append(.{
5978 .offset = binding.offset + base_offset,
5979 .segment_id = match.seg,
5980 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5981 .name = self.getString(bind_sym.n_strx),
5982 .bind_flags = flags,
5983 });
5984 },
5985 }5552 }
5553 try bind_pointers.append(.{
5554 .offset = binding.offset + base_offset,
5555 .segment_id = match.seg,
5556 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5557 .name = self.getSymbolName(global),
5558 .bind_flags = flags,
5559 });
5986 }5560 }
59875561
5988 for (atom.lazy_bindings.items) |binding| {5562 for (atom.lazy_bindings.items) |binding| {
5989 const resolv = self.symbol_resolver.get(binding.n_strx).?;5563 const global = self.globals.values()[binding.global_index];
5990 switch (resolv.where) {5564 const bind_sym = self.getSymbol(global);
5991 .global => {5565 var flags: u4 = 0;
5992 // Turn into a rebase.5566 if (bind_sym.weakRef()) {
5993 try rebase_pointers.append(.{5567 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
5994 .offset = base_offset + binding.offset,
5995 .segment_id = match.seg,
5996 });
5997 },
5998 .undef => {
5999 const bind_sym = self.undefs.items[resolv.where_index];
6000 var flags: u4 = 0;
6001 if (bind_sym.weakRef()) {
6002 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
6003 }
6004 try lazy_bind_pointers.append(.{
6005 .offset = binding.offset + base_offset,
6006 .segment_id = match.seg,
6007 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
6008 .name = self.getString(bind_sym.n_strx),
6009 .bind_flags = flags,
6010 });
6011 },
6012 }5568 }
5569 try lazy_bind_pointers.append(.{
5570 .offset = binding.offset + base_offset,
5571 .segment_id = match.seg,
5572 .dylib_ordinal = @divTrunc(@bitCast(i16, bind_sym.n_desc), macho.N_SYMBOL_RESOLVER),
5573 .name = self.getSymbolName(global),
5574 .bind_flags = flags,
5575 });
6013 }5576 }
60145577
6015 if (atom.prev) |prev| {5578 if (atom.prev) |prev| {
...@@ -6020,7 +5583,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -6020,7 +5583,7 @@ fn writeDyldInfoData(self: *MachO) !void {
6020 }5583 }
60215584
6022 var trie: Trie = .{};5585 var trie: Trie = .{};
6023 defer trie.deinit(self.base.allocator);5586 defer trie.deinit(gpa);
60245587
6025 {5588 {
6026 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.5589 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
...@@ -6029,19 +5592,22 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -6029,19 +5592,22 @@ fn writeDyldInfoData(self: *MachO) !void {
6029 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;5592 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6030 const base_address = text_segment.inner.vmaddr;5593 const base_address = text_segment.inner.vmaddr;
60315594
6032 for (self.globals.items) |sym| {5595 for (self.globals.values()) |global| {
6033 if (sym.n_type == 0) continue;5596 const sym = self.getSymbol(global);
6034 const sym_name = self.getString(sym.n_strx);5597 if (sym.undf()) continue;
5598 if (!sym.ext()) continue;
5599 if (sym.n_desc == N_DESC_GCED) continue;
5600 const sym_name = self.getSymbolName(global);
6035 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });5601 log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value });
60365602
6037 try trie.put(self.base.allocator, .{5603 try trie.put(gpa, .{
6038 .name = sym_name,5604 .name = sym_name,
6039 .vmaddr_offset = sym.n_value - base_address,5605 .vmaddr_offset = sym.n_value - base_address,
6040 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,5606 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
6041 });5607 });
6042 }5608 }
60435609
6044 try trie.finalize(self.base.allocator);5610 try trie.finalize(gpa);
6045 }5611 }
60465612
6047 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5613 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
...@@ -6086,8 +5652,8 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -6086,8 +5652,8 @@ fn writeDyldInfoData(self: *MachO) !void {
6086 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;5652 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
60875653
6088 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;5654 const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off;
6089 var buffer = try self.base.allocator.alloc(u8, needed_size);5655 var buffer = try gpa.alloc(u8, needed_size);
6090 defer self.base.allocator.free(buffer);5656 defer gpa.free(buffer);
6091 mem.set(u8, buffer, 0);5657 mem.set(u8, buffer, 0);
60925658
6093 var stream = std.io.fixedBufferStream(buffer);5659 var stream = std.io.fixedBufferStream(buffer);
...@@ -6114,10 +5680,12 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -6114,10 +5680,12 @@ fn writeDyldInfoData(self: *MachO) !void {
6114 try self.populateLazyBindOffsetsInStubHelper(5680 try self.populateLazyBindOffsetsInStubHelper(
6115 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],5681 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],
6116 );5682 );
5683
6117 self.load_commands_dirty = true;5684 self.load_commands_dirty = true;
6118}5685}
61195686
6120fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {5687fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5688 const gpa = self.base.allocator;
6121 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;5689 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
6122 const stub_helper_section_index = self.stub_helper_section_index orelse return;5690 const stub_helper_section_index = self.stub_helper_section_index orelse return;
6123 const last_atom = self.atoms.get(.{5691 const last_atom = self.atoms.get(.{
...@@ -6127,7 +5695,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6127,7 +5695,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
6127 if (self.stub_helper_preamble_atom == null) return;5695 if (self.stub_helper_preamble_atom == null) return;
6128 if (last_atom == self.stub_helper_preamble_atom.?) return;5696 if (last_atom == self.stub_helper_preamble_atom.?) return;
61295697
6130 var table = std.AutoHashMap(i64, *Atom).init(self.base.allocator);5698 var table = std.AutoHashMap(i64, *Atom).init(gpa);
6131 defer table.deinit();5699 defer table.deinit();
61325700
6133 {5701 {
...@@ -6143,7 +5711,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6143,7 +5711,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61435711
6144 while (true) {5712 while (true) {
6145 const laptr_off = blk: {5713 const laptr_off = blk: {
6146 const sym = self.locals.items[laptr_atom.local_sym_index];5714 const sym = laptr_atom.getSymbol(self);
6147 break :blk @intCast(i64, sym.n_value - base_addr);5715 break :blk @intCast(i64, sym.n_value - base_addr);
6148 };5716 };
6149 try table.putNoClobber(laptr_off, stub_atom);5717 try table.putNoClobber(laptr_off, stub_atom);
...@@ -6156,7 +5724,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6156,7 +5724,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61565724
6157 var stream = std.io.fixedBufferStream(buffer);5725 var stream = std.io.fixedBufferStream(buffer);
6158 var reader = stream.reader();5726 var reader = stream.reader();
6159 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(self.base.allocator);5727 var offsets = std.ArrayList(struct { sym_offset: i64, offset: u32 }).init(gpa);
6160 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });5728 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
6161 defer offsets.deinit();5729 defer offsets.deinit();
6162 var valid_block = false;5730 var valid_block = false;
...@@ -6199,10 +5767,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6199,10 +5767,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
6199 }5767 }
6200 }5768 }
62015769
6202 const sect = blk: {5770 const sect = self.getSection(.{
6203 const seg = self.load_commands.items[text_segment_cmd_index].segment;5771 .seg = text_segment_cmd_index,
6204 break :blk seg.sections.items[stub_helper_section_index];5772 .sect = stub_helper_section_index,
6205 };5773 });
6206 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {5774 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
6207 .x86_64 => 1,5775 .x86_64 => 1,
6208 .aarch64 => 2 * @sizeOf(u32),5776 .aarch64 => 2 * @sizeOf(u32),
...@@ -6213,79 +5781,63 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {...@@ -6213,79 +5781,63 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
62135781
6214 while (offsets.popOrNull()) |bind_offset| {5782 while (offsets.popOrNull()) |bind_offset| {
6215 const atom = table.get(bind_offset.sym_offset).?;5783 const atom = table.get(bind_offset.sym_offset).?;
6216 const sym = self.locals.items[atom.local_sym_index];5784 const sym = atom.getSymbol(self);
6217 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;5785 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
6218 mem.writeIntLittle(u32, &buf, bind_offset.offset);5786 mem.writeIntLittle(u32, &buf, bind_offset.offset);
6219 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{5787 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
6220 bind_offset.offset,5788 bind_offset.offset,
6221 self.getString(sym.n_strx),5789 atom.getName(self),
6222 file_offset,5790 file_offset,
6223 });5791 });
6224 try self.base.file.?.pwriteAll(&buf, file_offset);5792 try self.base.file.?.pwriteAll(&buf, file_offset);
6225 }5793 }
6226}5794}
62275795
5796const asc_u64 = std.sort.asc(u64);
5797
6228fn writeFunctionStarts(self: *MachO) !void {5798fn writeFunctionStarts(self: *MachO) !void {
6229 var atom = self.atoms.get(.{5799 const text_seg_index = self.text_segment_cmd_index orelse return;
6230 .seg = self.text_segment_cmd_index orelse return,5800 const text_sect_index = self.text_section_index orelse return;
6231 .sect = self.text_section_index orelse return,5801 const text_seg = self.load_commands.items[text_seg_index].segment;
6232 }) orelse return;
62335802
6234 const tracy = trace(@src());5803 const tracy = trace(@src());
6235 defer tracy.end();5804 defer tracy.end();
62365805
6237 while (atom.prev) |prev| {5806 const gpa = self.base.allocator;
6238 atom = prev;
6239 }
6240
6241 var offsets = std.ArrayList(u32).init(self.base.allocator);
6242 defer offsets.deinit();
6243
6244 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6245 var last_off: u32 = 0;
6246
6247 while (true) {
6248 const atom_sym = self.locals.items[atom.local_sym_index];
6249
6250 if (atom_sym.n_strx != 0) blk: {
6251 if (self.symbol_resolver.get(atom_sym.n_strx)) |resolv| {
6252 assert(resolv.where == .global);
6253 if (resolv.local_sym_index != atom.local_sym_index) break :blk;
6254 }
6255
6256 const offset = @intCast(u32, atom_sym.n_value - text_seg.inner.vmaddr);
6257 const diff = offset - last_off;
62585807
6259 if (diff == 0) break :blk;5808 // We need to sort by address first
5809 var addresses = std.ArrayList(u64).init(gpa);
5810 defer addresses.deinit();
5811 try addresses.ensureTotalCapacityPrecise(self.globals.count());
62605812
6261 try offsets.append(diff);5813 for (self.globals.values()) |global| {
6262 last_off = offset;5814 const sym = self.getSymbol(global);
6263 }5815 if (sym.undf()) continue;
5816 if (sym.n_desc == N_DESC_GCED) continue;
5817 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5818 if (match.seg != text_seg_index or match.sect != text_sect_index) continue;
62645819
6265 for (atom.contained.items) |cont| {5820 addresses.appendAssumeCapacity(sym.n_value);
6266 const cont_sym = self.locals.items[cont.local_sym_index];5821 }
62675822
6268 if (cont_sym.n_strx == 0) continue;5823 std.sort.sort(u64, addresses.items, {}, asc_u64);
6269 if (self.symbol_resolver.get(cont_sym.n_strx)) |resolv| {
6270 assert(resolv.where == .global);
6271 if (resolv.local_sym_index != cont.local_sym_index) continue;
6272 }
62735824
6274 const offset = @intCast(u32, cont_sym.n_value - text_seg.inner.vmaddr);5825 var offsets = std.ArrayList(u32).init(gpa);
6275 const diff = offset - last_off;5826 defer offsets.deinit();
5827 try offsets.ensureTotalCapacityPrecise(addresses.items.len);
62765828
6277 if (diff == 0) continue;5829 var last_off: u32 = 0;
5830 for (addresses.items) |addr| {
5831 const offset = @intCast(u32, addr - text_seg.inner.vmaddr);
5832 const diff = offset - last_off;
62785833
6279 try offsets.append(diff);5834 if (diff == 0) continue;
6280 last_off = offset;
6281 }
62825835
6283 if (atom.next) |next| {5836 offsets.appendAssumeCapacity(diff);
6284 atom = next;5837 last_off = offset;
6285 } else break;
6286 }5838 }
62875839
6288 var buffer = std.ArrayList(u8).init(self.base.allocator);5840 var buffer = std.ArrayList(u8).init(gpa);
6289 defer buffer.deinit();5841 defer buffer.deinit();
62905842
6291 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));5843 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
...@@ -6331,12 +5883,14 @@ fn writeDices(self: *MachO) !void {...@@ -6331,12 +5883,14 @@ fn writeDices(self: *MachO) !void {
6331 atom = prev;5883 atom = prev;
6332 }5884 }
63335885
6334 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;5886 const text_sect = self.getSection(.{
6335 const text_sect = text_seg.sections.items[self.text_section_index.?];5887 .seg = self.text_segment_cmd_index.?,
5888 .sect = self.text_section_index.?,
5889 });
63365890
6337 while (true) {5891 while (true) {
6338 if (atom.dices.items.len > 0) {5892 if (atom.dices.items.len > 0) {
6339 const sym = self.locals.items[atom.local_sym_index];5893 const sym = atom.getSymbol(self);
6340 const base_off = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse return error.Overflow;5894 const base_off = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse return error.Overflow;
63415895
6342 try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry));5896 try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry));
...@@ -6377,113 +5931,139 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6377,113 +5931,139 @@ fn writeSymbolTable(self: *MachO) !void {
6377 const tracy = trace(@src());5931 const tracy = trace(@src());
6378 defer tracy.end();5932 defer tracy.end();
63795933
5934 const gpa = self.base.allocator;
6380 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;5935 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6381 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;5936 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6382 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));5937 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));
6383 symtab.symoff = @intCast(u32, symoff);5938 symtab.symoff = @intCast(u32, symoff);
63845939
6385 var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator);5940 var locals = std.ArrayList(macho.nlist_64).init(gpa);
6386 defer locals.deinit();5941 defer locals.deinit();
63875942
6388 for (self.locals.items) |sym| {5943 for (self.locals.items) |sym, sym_id| {
6389 if (sym.n_strx == 0) continue;5944 if (sym.n_strx == 0) continue; // no name, skip
6390 if (sym.n_desc == N_DESC_GCED) continue;5945 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6391 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;5946 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
5947 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
5948 if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
6392 try locals.append(sym);5949 try locals.append(sym);
6393 }5950 }
63945951
6395 var globals = std.ArrayList(macho.nlist_64).init(self.base.allocator);5952 for (self.objects.items) |object, object_id| {
6396 defer globals.deinit();5953 if (self.has_stabs) {
63975954 if (object.debug_info) |_| {
6398 for (self.globals.items) |sym| {5955 // Open scope
6399 if (sym.n_desc == N_DESC_GCED) continue;5956 try locals.ensureUnusedCapacity(3);
6400 try globals.append(sym);5957 locals.appendAssumeCapacity(.{
6401 }5958 .n_strx = try self.strtab.insert(gpa, object.tu_comp_dir.?),
5959 .n_type = macho.N_SO,
5960 .n_sect = 0,
5961 .n_desc = 0,
5962 .n_value = 0,
5963 });
5964 locals.appendAssumeCapacity(.{
5965 .n_strx = try self.strtab.insert(gpa, object.tu_name.?),
5966 .n_type = macho.N_SO,
5967 .n_sect = 0,
5968 .n_desc = 0,
5969 .n_value = 0,
5970 });
5971 locals.appendAssumeCapacity(.{
5972 .n_strx = try self.strtab.insert(gpa, object.name),
5973 .n_type = macho.N_OSO,
5974 .n_sect = 0,
5975 .n_desc = 1,
5976 .n_value = object.mtime orelse 0,
5977 });
64025978
6403 // TODO How do we handle null global symbols in incremental context?5979 for (object.managed_atoms.items) |atom| {
6404 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);5980 for (atom.contained.items) |sym_at_off| {
6405 defer undefs.deinit();5981 const stab = sym_at_off.stab orelse continue;
6406 var undefs_table = std.AutoHashMap(u32, u32).init(self.base.allocator);5982 const sym_loc = SymbolWithLoc{
6407 defer undefs_table.deinit();5983 .sym_index = sym_at_off.sym_index,
6408 try undefs.ensureTotalCapacity(self.undefs.items.len);5984 .file = atom.file,
6409 try undefs_table.ensureTotalCapacity(@intCast(u32, self.undefs.items.len));5985 };
5986 const sym = self.getSymbol(sym_loc);
5987 if (sym.n_strx == 0) continue;
5988 if (sym.n_desc == N_DESC_GCED) continue;
5989 if (self.symbolIsTemp(sym_loc)) continue;
5990
5991 const nlists = try stab.asNlists(.{
5992 .sym_index = sym_at_off.sym_index,
5993 .file = atom.file,
5994 }, self);
5995 defer gpa.free(nlists);
5996
5997 try locals.appendSlice(nlists);
5998 }
5999 }
64106000
6411 for (self.undefs.items) |sym, i| {6001 // Close scope
6412 if (sym.n_strx == 0) continue;6002 try locals.append(.{
6413 const new_index = @intCast(u32, undefs.items.len);6003 .n_strx = 0,
6414 undefs.appendAssumeCapacity(sym);6004 .n_type = macho.N_SO,
6415 undefs_table.putAssumeCapacityNoClobber(@intCast(u32, i), new_index);6005 .n_sect = 0,
6006 .n_desc = 0,
6007 .n_value = 0,
6008 });
6009 }
6010 }
6011 for (object.symtab.items) |sym, sym_id| {
6012 if (sym.n_strx == 0) continue; // no name, skip
6013 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6014 const sym_loc = SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = @intCast(u32, object_id) };
6015 if (self.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
6016 if (self.globals.contains(self.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
6017 var out_sym = sym;
6018 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(sym_loc));
6019 try locals.append(out_sym);
6020 }
6416 }6021 }
64176022
6418 if (self.has_stabs) {6023 var exports = std.ArrayList(macho.nlist_64).init(gpa);
6419 for (self.objects.items) |object| {6024 defer exports.deinit();
6420 if (object.debug_info == null) continue;
64216025
6422 // Open scope6026 for (self.globals.values()) |global| {
6423 try locals.ensureUnusedCapacity(3);6027 const sym = self.getSymbol(global);
6424 locals.appendAssumeCapacity(.{6028 if (sym.undf()) continue; // import, skip
6425 .n_strx = try self.makeString(object.tu_comp_dir.?),6029 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6426 .n_type = macho.N_SO,6030 var out_sym = sym;
6427 .n_sect = 0,6031 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6428 .n_desc = 0,6032 try exports.append(out_sym);
6429 .n_value = 0,6033 }
6430 });
6431 locals.appendAssumeCapacity(.{
6432 .n_strx = try self.makeString(object.tu_name.?),
6433 .n_type = macho.N_SO,
6434 .n_sect = 0,
6435 .n_desc = 0,
6436 .n_value = 0,
6437 });
6438 locals.appendAssumeCapacity(.{
6439 .n_strx = try self.makeString(object.name),
6440 .n_type = macho.N_OSO,
6441 .n_sect = 0,
6442 .n_desc = 1,
6443 .n_value = object.mtime orelse 0,
6444 });
64456034
6446 for (object.contained_atoms.items) |atom| {6035 var imports = std.ArrayList(macho.nlist_64).init(gpa);
6447 for (atom.contained.items) |sym_at_off| {6036 defer imports.deinit();
6448 const stab = sym_at_off.stab orelse continue;6037 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6449 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);6038 defer imports_table.deinit();
6450 defer self.base.allocator.free(nlists);
6451 try locals.appendSlice(nlists);
6452 }
6453 }
64546039
6455 // Close scope6040 for (self.globals.values()) |global| {
6456 try locals.append(.{6041 const sym = self.getSymbol(global);
6457 .n_strx = 0,6042 if (sym.n_strx == 0) continue; // no name, skip
6458 .n_type = macho.N_SO,6043 if (!sym.undf()) continue; // not an import, skip
6459 .n_sect = 0,6044 const new_index = @intCast(u32, imports.items.len);
6460 .n_desc = 0,6045 var out_sym = sym;
6461 .n_value = 0,6046 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6462 });6047 try imports.append(out_sym);
6463 }6048 try imports_table.putNoClobber(global, new_index);
6464 }6049 }
64656050
6466 const nlocals = locals.items.len;6051 const nlocals = locals.items.len;
6467 const nexports = globals.items.len;6052 const nexports = exports.items.len;
6468 const nundefs = undefs.items.len;6053 const nimports = imports.items.len;
6054 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);
64696055
6470 const locals_off = symtab.symoff;6056 var buffer = std.ArrayList(u8).init(gpa);
6471 const locals_size = nlocals * @sizeOf(macho.nlist_64);6057 defer buffer.deinit();
6472 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });6058 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
6473 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);6059 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
64746060 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
6475 const exports_off = locals_off + locals_size;6061 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
6476 const exports_size = nexports * @sizeOf(macho.nlist_64);
6477 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
6478 try self.base.file.?.pwriteAll(mem.sliceAsBytes(globals.items), exports_off);
64796062
6480 const undefs_off = exports_off + exports_size;6063 log.debug("writing symtab from 0x{x} to 0x{x}", .{ symtab.symoff, symtab.symoff + buffer.items.len });
6481 const undefs_size = nundefs * @sizeOf(macho.nlist_64);6064 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);
6482 log.debug("writing undefined symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
6483 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
64846065
6485 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);6066 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
6486 seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff;
64876067
6488 // Update dynamic symbol table.6068 // Update dynamic symbol table.
6489 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;6069 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
...@@ -6491,7 +6071,7 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6491,7 +6071,7 @@ fn writeSymbolTable(self: *MachO) !void {
6491 dysymtab.iextdefsym = dysymtab.nlocalsym;6071 dysymtab.iextdefsym = dysymtab.nlocalsym;
6492 dysymtab.nextdefsym = @intCast(u32, nexports);6072 dysymtab.nextdefsym = @intCast(u32, nexports);
6493 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;6073 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6494 dysymtab.nundefsym = @intCast(u32, nundefs);6074 dysymtab.nundefsym = @intCast(u32, nimports);
64956075
6496 const nstubs = @intCast(u32, self.stubs_table.count());6076 const nstubs = @intCast(u32, self.stubs_table.count());
6497 const ngot_entries = @intCast(u32, self.got_entries_table.count());6077 const ngot_entries = @intCast(u32, self.got_entries_table.count());
...@@ -6507,55 +6087,53 @@ fn writeSymbolTable(self: *MachO) !void {...@@ -6507,55 +6087,53 @@ fn writeSymbolTable(self: *MachO) !void {
6507 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),6087 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
6508 });6088 });
65096089
6510 var buf = std.ArrayList(u8).init(self.base.allocator);6090 var buf = std.ArrayList(u8).init(gpa);
6511 defer buf.deinit();6091 defer buf.deinit();
6512 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));6092 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
6513 const writer = buf.writer();6093 const writer = buf.writer();
65146094
6515 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {6095 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
6516 const stubs_section_index = self.stubs_section_index orelse break :blk;6096 const stubs_section_index = self.stubs_section_index orelse break :blk;
6517 const text_segment = &self.load_commands.items[text_segment_cmd_index].segment;6097 const stubs = self.getSectionPtr(.{
6518 const stubs = &text_segment.sections.items[stubs_section_index];6098 .seg = text_segment_cmd_index,
6099 .sect = stubs_section_index,
6100 });
6519 stubs.reserved1 = 0;6101 stubs.reserved1 = 0;
6520 for (self.stubs_table.keys()) |key| {6102 for (self.stubs_table.keys()) |target| {
6521 const resolv = self.symbol_resolver.get(key).?;6103 const sym = self.getSymbol(target);
6522 switch (resolv.where) {6104 assert(sym.undf());
6523 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6105 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
6524 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6525 }
6526 }6106 }
6527 }6107 }
65286108
6529 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {6109 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
6530 const got_section_index = self.got_section_index orelse break :blk;6110 const got_section_index = self.got_section_index orelse break :blk;
6531 const data_const_segment = &self.load_commands.items[data_const_segment_cmd_index].segment;6111 const got = self.getSectionPtr(.{
6532 const got = &data_const_segment.sections.items[got_section_index];6112 .seg = data_const_segment_cmd_index,
6113 .sect = got_section_index,
6114 });
6533 got.reserved1 = nstubs;6115 got.reserved1 = nstubs;
6534 for (self.got_entries_table.keys()) |key| {6116 for (self.got_entries_table.keys()) |target| {
6535 switch (key) {6117 const sym = self.getSymbol(target);
6536 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6118 if (sym.undf()) {
6537 .global => |n_strx| {6119 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
6538 const resolv = self.symbol_resolver.get(n_strx).?;6120 } else {
6539 switch (resolv.where) {6121 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
6540 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6541 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6542 }
6543 },
6544 }6122 }
6545 }6123 }
6546 }6124 }
65476125
6548 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {6126 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
6549 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;6127 const la_symbol_ptr_section_index = self.la_symbol_ptr_section_index orelse break :blk;
6550 const data_segment = &self.load_commands.items[data_segment_cmd_index].segment;6128 const la_symbol_ptr = self.getSectionPtr(.{
6551 const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index];6129 .seg = data_segment_cmd_index,
6130 .sect = la_symbol_ptr_section_index,
6131 });
6552 la_symbol_ptr.reserved1 = nstubs + ngot_entries;6132 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6553 for (self.stubs_table.keys()) |key| {6133 for (self.stubs_table.keys()) |target| {
6554 const resolv = self.symbol_resolver.get(key).?;6134 const sym = self.getSymbol(target);
6555 switch (resolv.where) {6135 assert(sym.undf());
6556 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),6136 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
6557 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6558 }
6559 }6137 }
6560 }6138 }
65616139
...@@ -6572,14 +6150,15 @@ fn writeStringTable(self: *MachO) !void {...@@ -6572,14 +6150,15 @@ fn writeStringTable(self: *MachO) !void {
6572 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;6150 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
6573 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;6151 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
6574 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));6152 const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64));
6575 const strsize = self.strtab.items.len;6153
6154 const strsize = self.strtab.buffer.items.len;
6576 symtab.stroff = @intCast(u32, stroff);6155 symtab.stroff = @intCast(u32, stroff);
6577 symtab.strsize = @intCast(u32, strsize);6156 symtab.strsize = @intCast(u32, strsize);
6578 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;6157 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;
65796158
6580 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });6159 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
65816160
6582 try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff);6161 try self.base.file.?.pwriteAll(self.strtab.buffer.items, symtab.stroff);
65836162
6584 self.load_commands_dirty = true;6163 self.load_commands_dirty = true;
6585}6164}
...@@ -6737,42 +6316,81 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {...@@ -6737,42 +6316,81 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
6737 return buf;6316 return buf;
6738}6317}
67396318
6740pub fn makeString(self: *MachO, string: []const u8) !u32 {6319pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {
6741 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{6320 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;
6742 .bytes = &self.strtab,6321}
6743 }, StringIndexContext{
6744 .bytes = &self.strtab,
6745 });
6746 if (gop.found_existing) {
6747 const off = gop.key_ptr.*;
6748 log.debug("reusing string '{s}' at offset 0x{x}", .{ string, off });
6749 return off;
6750 }
6751
6752 try self.strtab.ensureUnusedCapacity(self.base.allocator, string.len + 1);
6753 const new_off = @intCast(u32, self.strtab.items.len);
67546322
6755 log.debug("writing new string '{s}' at offset 0x{x}", .{ string, new_off });6323pub fn getMatchingSectionFromOrdinal(self: *MachO, ord: u8) MatchingSection {
6324 const index = ord - 1;
6325 assert(index < self.section_ordinals.count());
6326 return self.section_ordinals.keys()[index];
6327}
67566328
6757 self.strtab.appendSliceAssumeCapacity(string);6329pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
6758 self.strtab.appendAssumeCapacity(0);6330 assert(match.seg < self.load_commands.items.len);
6331 return &self.load_commands.items[match.seg].segment;
6332}
67596333
6760 gop.key_ptr.* = new_off;6334pub fn getSegment(self: *MachO, match: MatchingSection) macho.SegmentCommand {
6335 return self.getSegmentPtr(match).*;
6336}
67616337
6762 return new_off;6338pub fn getSectionPtr(self: *MachO, match: MatchingSection) *macho.section_64 {
6339 const seg = self.getSegmentPtr(match);
6340 assert(match.sect < seg.sections.items.len);
6341 return &seg.sections.items[match.sect];
6763}6342}
67646343
6765pub fn getString(self: MachO, off: u32) []const u8 {6344pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6766 assert(off < self.strtab.items.len);6345 return self.getSectionPtr(match).*;
6767 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);
6768}6346}
67696347
6770pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool {6348pub fn symbolIsTemp(self: *MachO, sym_with_loc: SymbolWithLoc) bool {
6349 const sym = self.getSymbol(sym_with_loc);
6771 if (!sym.sect()) return false;6350 if (!sym.sect()) return false;
6772 if (sym.ext()) return false;6351 if (sym.ext()) return false;
6352 const sym_name = self.getSymbolName(sym_with_loc);
6773 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");6353 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
6774}6354}
67756355
6356/// Returns pointer-to-symbol described by `sym_with_loc` descriptor.
6357pub fn getSymbolPtr(self: *MachO, sym_with_loc: SymbolWithLoc) *macho.nlist_64 {
6358 if (sym_with_loc.file) |file| {
6359 const object = &self.objects.items[file];
6360 return &object.symtab.items[sym_with_loc.sym_index];
6361 } else {
6362 return &self.locals.items[sym_with_loc.sym_index];
6363 }
6364}
6365
6366/// Returns symbol described by `sym_with_loc` descriptor.
6367pub fn getSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) macho.nlist_64 {
6368 return self.getSymbolPtr(sym_with_loc).*;
6369}
6370
6371/// Returns name of the symbol described by `sym_with_loc` descriptor.
6372pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {
6373 if (sym_with_loc.file) |file| {
6374 const object = self.objects.items[file];
6375 const sym = object.symtab.items[sym_with_loc.sym_index];
6376 return object.getString(sym.n_strx);
6377 } else {
6378 const sym = self.locals.items[sym_with_loc.sym_index];
6379 return self.strtab.get(sym.n_strx).?;
6380 }
6381}
6382
6383/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
6384/// Returns null on failure.
6385pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6386 if (sym_with_loc.file) |file| {
6387 const object = self.objects.items[file];
6388 return object.atom_by_index_table.get(sym_with_loc.sym_index);
6389 } else {
6390 return self.atom_by_index_table.get(sym_with_loc.sym_index);
6391 }
6392}
6393
6776pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {6394pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
6777 if (!@hasDecl(@TypeOf(predicate), "predicate"))6395 if (!@hasDecl(@TypeOf(predicate), "predicate"))
6778 @compileError("Predicate is required to define fn predicate(@This(), T) bool");6396 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
...@@ -6835,7 +6453,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6835,7 +6453,7 @@ fn snapshotState(self: *MachO) !void {
6835 const arena = arena_allocator.allocator();6453 const arena = arena_allocator.allocator();
68366454
6837 const out_file = try emit.directory.handle.createFile("snapshots.json", .{6455 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6838 .truncate = self.cold_start,6456 .truncate = false,
6839 .read = true,6457 .read = true,
6840 });6458 });
6841 defer out_file.close();6459 defer out_file.close();
...@@ -6855,8 +6473,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6855,8 +6473,7 @@ fn snapshotState(self: *MachO) !void {
6855 var nodes = std.ArrayList(Snapshot.Node).init(arena);6473 var nodes = std.ArrayList(Snapshot.Node).init(arena);
68566474
6857 for (self.section_ordinals.keys()) |key| {6475 for (self.section_ordinals.keys()) |key| {
6858 const seg = self.load_commands.items[key.seg].segment;6476 const sect = self.getSection(key);
6859 const sect = seg.sections.items[key.sect];
6860 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });6477 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
6861 try nodes.append(.{6478 try nodes.append(.{
6862 .address = sect.addr,6479 .address = sect.addr,
...@@ -6878,10 +6495,10 @@ fn snapshotState(self: *MachO) !void {...@@ -6878,10 +6495,10 @@ fn snapshotState(self: *MachO) !void {
6878 }6495 }
68796496
6880 while (true) {6497 while (true) {
6881 const atom_sym = self.locals.items[atom.local_sym_index];6498 const atom_sym = self.locals.items[atom.sym_index];
6882 const should_skip_atom: bool = blk: {6499 const should_skip_atom: bool = blk: {
6883 if (self.mh_execute_header_index) |index| {6500 if (self.mh_execute_header_index) |index| {
6884 if (index == atom.local_sym_index) break :blk true;6501 if (index == atom.sym_index) break :blk true;
6885 }6502 }
6886 if (mem.eql(u8, self.getString(atom_sym.n_strx), "___dso_handle")) break :blk true;6503 if (mem.eql(u8, self.getString(atom_sym.n_strx), "___dso_handle")) break :blk true;
6887 break :blk false;6504 break :blk false;
...@@ -6906,7 +6523,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6906,7 +6523,7 @@ fn snapshotState(self: *MachO) !void {
6906 var aliases = std.ArrayList([]const u8).init(arena);6523 var aliases = std.ArrayList([]const u8).init(arena);
6907 for (atom.contained.items) |sym_off| {6524 for (atom.contained.items) |sym_off| {
6908 if (sym_off.offset == 0) {6525 if (sym_off.offset == 0) {
6909 try aliases.append(self.getString(self.locals.items[sym_off.local_sym_index].n_strx));6526 try aliases.append(self.getString(self.locals.items[sym_off.sym_index].n_strx));
6910 }6527 }
6911 }6528 }
6912 node.payload.aliases = aliases.toOwnedSlice();6529 node.payload.aliases = aliases.toOwnedSlice();
...@@ -6916,7 +6533,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6916,7 +6533,7 @@ fn snapshotState(self: *MachO) !void {
6916 for (atom.relocs.items) |rel| {6533 for (atom.relocs.items) |rel| {
6917 const arch = self.base.options.target.cpu.arch;6534 const arch = self.base.options.target.cpu.arch;
6918 const source_addr = blk: {6535 const source_addr = blk: {
6919 const sym = self.locals.items[atom.local_sym_index];6536 const sym = self.locals.items[atom.sym_index];
6920 break :blk sym.n_value + rel.offset;6537 break :blk sym.n_value + rel.offset;
6921 };6538 };
6922 const target_addr = blk: {6539 const target_addr = blk: {
...@@ -6937,14 +6554,14 @@ fn snapshotState(self: *MachO) !void {...@@ -6937,14 +6554,14 @@ fn snapshotState(self: *MachO) !void {
6937 if (is_via_got) {6554 if (is_via_got) {
6938 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;6555 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;
6939 const got_atom = self.got_entries.items[got_index].atom;6556 const got_atom = self.got_entries.items[got_index].atom;
6940 break :blk self.locals.items[got_atom.local_sym_index].n_value;6557 break :blk self.locals.items[got_atom.sym_index].n_value;
6941 }6558 }
69426559
6943 switch (rel.target) {6560 switch (rel.target) {
6944 .local => |sym_index| {6561 .local => |sym_index| {
6945 const sym = self.locals.items[sym_index];6562 const sym = self.locals.items[sym_index];
6946 const is_tlv = is_tlv: {6563 const is_tlv = is_tlv: {
6947 const source_sym = self.locals.items[atom.local_sym_index];6564 const source_sym = self.locals.items[atom.sym_index];
6948 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];6565 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
6949 const match_seg = self.load_commands.items[match.seg].segment;6566 const match_seg = self.load_commands.items[match.seg].segment;
6950 const match_sect = match_seg.sections.items[match.sect];6567 const match_sect = match_seg.sections.items[match.sect];
...@@ -6970,7 +6587,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6970,7 +6587,7 @@ fn snapshotState(self: *MachO) !void {
6970 .undef => {6587 .undef => {
6971 if (self.stubs_table.get(n_strx)) |stub_index| {6588 if (self.stubs_table.get(n_strx)) |stub_index| {
6972 const stub_atom = self.stubs.items[stub_index];6589 const stub_atom = self.stubs.items[stub_index];
6973 break :blk self.locals.items[stub_atom.local_sym_index].n_value;6590 break :blk self.locals.items[stub_atom.sym_index].n_value;
6974 }6591 }
6975 break :blk 0;6592 break :blk 0;
6976 },6593 },
...@@ -6998,7 +6615,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6998,7 +6615,7 @@ fn snapshotState(self: *MachO) !void {
6998 var last_rel: usize = 0;6615 var last_rel: usize = 0;
6999 while (next_i < atom.contained.items.len) : (next_i += 1) {6616 while (next_i < atom.contained.items.len) : (next_i += 1) {
7000 const loc = atom.contained.items[next_i];6617 const loc = atom.contained.items[next_i];
7001 const cont_sym = self.locals.items[loc.local_sym_index];6618 const cont_sym = self.locals.items[loc.sym_index];
7002 const cont_sym_name = self.getString(cont_sym.n_strx);6619 const cont_sym_name = self.getString(cont_sym.n_strx);
7003 var contained_node = Snapshot.Node{6620 var contained_node = Snapshot.Node{
7004 .address = cont_sym.n_value,6621 .address = cont_sym.n_value,
...@@ -7013,7 +6630,7 @@ fn snapshotState(self: *MachO) !void {...@@ -7013,7 +6630,7 @@ fn snapshotState(self: *MachO) !void {
7013 var inner_aliases = std.ArrayList([]const u8).init(arena);6630 var inner_aliases = std.ArrayList([]const u8).init(arena);
7014 while (true) {6631 while (true) {
7015 if (next_i + 1 >= atom.contained.items.len) break;6632 if (next_i + 1 >= atom.contained.items.len) break;
7016 const next_sym = self.locals.items[atom.contained.items[next_i + 1].local_sym_index];6633 const next_sym = self.locals.items[atom.contained.items[next_i + 1].sym_index];
7017 if (next_sym.n_value != cont_sym.n_value) break;6634 if (next_sym.n_value != cont_sym.n_value) break;
7018 const next_sym_name = self.getString(next_sym.n_strx);6635 const next_sym_name = self.getString(next_sym.n_strx);
7019 if (self.symbol_resolver.contains(next_sym.n_strx)) {6636 if (self.symbol_resolver.contains(next_sym.n_strx)) {
...@@ -7025,7 +6642,7 @@ fn snapshotState(self: *MachO) !void {...@@ -7025,7 +6642,7 @@ fn snapshotState(self: *MachO) !void {
7025 }6642 }
70266643
7027 const cont_size = if (next_i + 1 < atom.contained.items.len)6644 const cont_size = if (next_i + 1 < atom.contained.items.len)
7028 self.locals.items[atom.contained.items[next_i + 1].local_sym_index].n_value - cont_sym.n_value6645 self.locals.items[atom.contained.items[next_i + 1].sym_index].n_value - cont_sym.n_value
7029 else6646 else
7030 atom_sym.n_value + atom.size - cont_sym.n_value;6647 atom_sym.n_value + atom.size - cont_sym.n_value;
70316648
...@@ -7072,75 +6689,117 @@ fn snapshotState(self: *MachO) !void {...@@ -7072,75 +6689,117 @@ fn snapshotState(self: *MachO) !void {
7072 try writer.writeByte(']');6689 try writer.writeByte(']');
7073}6690}
70746691
7075fn logSymtab(self: MachO) void {6692pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
7076 log.warn("locals:", .{});6693 mem.set(u8, buf, '_');
7077 for (self.locals.items) |sym, id| {6694 if (sym.sect()) {
7078 log.warn(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });6695 buf[0] = 's';
7079 }6696 }
70806697 if (sym.ext()) {
7081 log.warn("globals:", .{});6698 buf[1] = 'e';
7082 for (self.globals.items) |sym, id| {
7083 log.warn(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
7084 }6699 }
70856700 if (sym.tentative()) {
7086 log.warn("undefs:", .{});6701 buf[2] = 't';
7087 for (self.undefs.items) |sym, id| {
7088 log.warn(" {d}: {s}: in {d}", .{ id, self.getString(sym.n_strx), sym.n_desc });
7089 }6702 }
6703 if (sym.undf()) {
6704 buf[3] = 'u';
6705 }
6706 return buf[0..];
6707}
70906708
7091 {6709fn logSymtab(self: *MachO) void {
7092 log.warn("resolver:", .{});6710 var buf: [4]u8 = undefined;
7093 var it = self.symbol_resolver.iterator();6711
7094 while (it.next()) |entry| {6712 log.debug("symtab:", .{});
7095 log.warn(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });6713 for (self.objects.items) |object, id| {
6714 log.debug(" object({d}): {s}", .{ id, object.name });
6715 for (object.symtab.items) |sym, sym_id| {
6716 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
6717 const def_index = if (sym.undf() and !sym.tentative())
6718 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
6719 else
6720 sym.n_sect;
6721 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
6722 sym_id,
6723 object.getString(sym.n_strx),
6724 sym.n_value,
6725 where,
6726 def_index,
6727 logSymAttributes(sym, &buf),
6728 });
7096 }6729 }
7097 }6730 }
6731 log.debug(" object(null)", .{});
6732 for (self.locals.items) |sym, sym_id| {
6733 const where = if (sym.undf() and !sym.tentative()) "ord" else "sect";
6734 const def_index = if (sym.undf() and !sym.tentative())
6735 @divTrunc(sym.n_desc, macho.N_SYMBOL_RESOLVER)
6736 else
6737 sym.n_sect;
6738 log.debug(" %{d}: {s} @{x} in {s}({d}), {s}", .{
6739 sym_id,
6740 self.strtab.get(sym.n_strx),
6741 sym.n_value,
6742 where,
6743 def_index,
6744 logSymAttributes(sym, &buf),
6745 });
6746 }
6747
6748 log.debug("globals table:", .{});
6749 for (self.globals.keys()) |name, id| {
6750 const value = self.globals.values()[id];
6751 log.debug(" {s} => %{d} in object({d})", .{ name, value.sym_index, value.file });
6752 }
70986753
7099 log.warn("GOT entries:", .{});6754 log.debug("GOT entries:", .{});
7100 for (self.got_entries_table.values()) |value| {6755 for (self.got_entries_table.values()) |value| {
7101 const key = self.got_entries.items[value].target;6756 const target = self.got_entries.items[value].target;
6757 const target_sym = self.getSymbol(target);
7102 const atom = self.got_entries.items[value].atom;6758 const atom = self.got_entries.items[value].atom;
7103 const n_value = self.locals.items[atom.local_sym_index].n_value;6759 const atom_sym = atom.getSymbol(self);
7104 switch (key) {6760
7105 .local => |ndx| log.warn(" {d}: @{x}", .{ ndx, n_value }),6761 if (target_sym.undf()) {
7106 .global => |n_strx| log.warn(" {s}: @{x}", .{ self.getString(n_strx), n_value }),6762 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });
6763 } else {
6764 log.debug(" {d}@{x} => local(%{d}) in object({d})", .{
6765 value,
6766 atom_sym.n_value,
6767 target.sym_index,
6768 target.file,
6769 });
7107 }6770 }
7108 }6771 }
71096772
7110 log.warn("__thread_ptrs entries:", .{});6773 log.debug("__thread_ptrs entries:", .{});
7111 for (self.tlv_ptr_entries_table.values()) |value| {6774 for (self.tlv_ptr_entries_table.values()) |value| {
7112 const key = self.tlv_ptr_entries.items[value].target;6775 const target = self.tlv_ptr_entries.items[value].target;
6776 const target_sym = self.getSymbol(target);
7113 const atom = self.tlv_ptr_entries.items[value].atom;6777 const atom = self.tlv_ptr_entries.items[value].atom;
7114 const n_value = self.locals.items[atom.local_sym_index].n_value;6778 const atom_sym = atom.getSymbol(self);
7115 assert(key == .global);6779 assert(target_sym.undf());
7116 log.warn(" {s}: @{x}", .{ self.getString(key.global), n_value });6780 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });
7117 }6781 }
71186782
7119 log.warn("stubs:", .{});6783 log.debug("stubs entries:", .{});
7120 for (self.stubs_table.keys()) |key| {6784 for (self.stubs_table.values()) |value| {
7121 const value = self.stubs_table.get(key).?;6785 const target = self.stubs.items[value].target;
7122 const atom = self.stubs.items[value];6786 const target_sym = self.getSymbol(target);
7123 const sym = self.locals.items[atom.local_sym_index];6787 const atom = self.stubs.items[value].atom;
7124 log.warn(" {s}: @{x}", .{ self.getString(key), sym.n_value });6788 const atom_sym = atom.getSymbol(self);
6789 assert(target_sym.undf());
6790 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });
7125 }6791 }
7126}6792}
71276793
7128fn logSectionOrdinals(self: MachO) void {6794fn logSectionOrdinals(self: *MachO) void {
7129 for (self.section_ordinals.keys()) |match, i| {6795 for (self.section_ordinals.keys()) |match, i| {
7130 const seg = self.load_commands.items[match.seg].segment;6796 const sect = self.getSection(match);
7131 const sect = seg.sections.items[match.sect];6797 log.debug("sect({d}, '{s},{s}')", .{ i + 1, sect.segName(), sect.sectName() });
7132 log.debug("ord {d}: {d},{d} => {s},{s}", .{
7133 i + 1,
7134 match.seg,
7135 match.sect,
7136 sect.segName(),
7137 sect.sectName(),
7138 });
7139 }6798 }
7140}6799}
71416800
7142fn logAtoms(self: MachO) void {6801fn logAtoms(self: *MachO) void {
7143 log.warn("atoms:", .{});6802 log.debug("atoms:", .{});
7144 var it = self.atoms.iterator();6803 var it = self.atoms.iterator();
7145 while (it.next()) |entry| {6804 while (it.next()) |entry| {
7146 const match = entry.key_ptr.*;6805 const match = entry.key_ptr.*;
...@@ -7150,9 +6809,8 @@ fn logAtoms(self: MachO) void {...@@ -7150,9 +6809,8 @@ fn logAtoms(self: MachO) void {
7150 atom = prev;6809 atom = prev;
7151 }6810 }
71526811
7153 const seg = self.load_commands.items[match.seg].segment;6812 const sect = self.getSection(match);
7154 const sect = seg.sections.items[match.sect];6813 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
7155 log.warn("{s},{s}", .{ sect.segName(), sect.sectName() });
71566814
7157 while (true) {6815 while (true) {
7158 self.logAtom(atom);6816 self.logAtom(atom);
...@@ -7164,16 +6822,28 @@ fn logAtoms(self: MachO) void {...@@ -7164,16 +6822,28 @@ fn logAtoms(self: MachO) void {
7164 }6822 }
7165}6823}
71666824
7167fn logAtom(self: MachO, atom: *const Atom) void {6825pub fn logAtom(self: *MachO, atom: *const Atom) void {
7168 const sym = self.locals.items[atom.local_sym_index];6826 const sym = atom.getSymbol(self);
7169 log.warn(" ATOM(%{d}) @ {x}", .{ atom.local_sym_index, sym.n_value });6827 const sym_name = atom.getName(self);
6828 log.debug(" ATOM(%{d}, '{s}') @ {x} in object({d})", .{
6829 atom.sym_index,
6830 sym_name,
6831 sym.n_value,
6832 atom.file,
6833 });
71706834
7171 for (atom.contained.items) |sym_off| {6835 for (atom.contained.items) |sym_off| {
7172 const inner_sym = self.locals.items[sym_off.local_sym_index];6836 const inner_sym = self.getSymbol(.{
7173 log.warn(" %{d} ('{s}') @ {x}", .{6837 .sym_index = sym_off.sym_index,
7174 sym_off.local_sym_index,6838 .file = atom.file,
7175 self.getString(inner_sym.n_strx),6839 });
6840 const inner_sym_name = self.getSymbolName(.{ .sym_index = sym_off.sym_index, .file = atom.file });
6841 log.debug(" (%{d}, '{s}') @ {x} ({x}) in object({d})", .{
6842 sym_off.sym_index,
6843 inner_sym_name,
7176 inner_sym.n_value,6844 inner_sym.n_value,
6845 sym_off.offset,
6846 atom.file,
7177 });6847 });
7178 }6848 }
7179}6849}
src/link/MachO/Atom.zig+272-369
...@@ -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,7 +24,10 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;...@@ -24,7 +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,
28
29/// null means symbol defined by Zig source.
30file: ?u32,
2831
29/// List of symbols contained within this atom32/// List of symbols contained within this atom
30contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},33contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
...@@ -45,15 +48,15 @@ alignment: u32,...@@ -45,15 +48,15 @@ alignment: u32,
45relocs: std.ArrayListUnmanaged(Relocation) = .{},48relocs: std.ArrayListUnmanaged(Relocation) = .{},
4649
47/// 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
48/// loader in presence of ASLR.51/// loader for example in presence of ASLR.
49rebases: std.ArrayListUnmanaged(u64) = .{},52rebases: std.ArrayListUnmanaged(u64) = .{},
5053
51/// List of offsets contained within this atom that will be dynamically bound54/// List of offsets contained within this atom that will be dynamically bound
52/// 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
53/// symbols (aka proxies aka imports)56/// symbols (aka proxies aka imports).
54bindings: std.ArrayListUnmanaged(Binding) = .{},57bindings: std.ArrayListUnmanaged(Binding) = .{},
5558
56/// List of lazy bindings59/// List of lazy bindings (cf bindings above).
57lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},60lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
5861
59/// List of data-in-code entries. This is currently specific to x86_64 only.62/// List of data-in-code entries. This is currently specific to x86_64 only.
...@@ -68,12 +71,12 @@ dbg_info_atom: Dwarf.Atom,...@@ -68,12 +71,12 @@ dbg_info_atom: Dwarf.Atom,
68dirty: bool = true,71dirty: bool = true,
6972
70pub const Binding = struct {73pub const Binding = struct {
71 n_strx: u32,74 global_index: u32,
72 offset: u64,75 offset: u64,
73};76};
7477
75pub const SymbolAtOffset = struct {78pub const SymbolAtOffset = struct {
76 local_sym_index: u32,79 sym_index: u32,
77 offset: u64,80 offset: u64,
78 stab: ?Stab = null,81 stab: ?Stab = null,
79};82};
...@@ -83,11 +86,14 @@ pub const Stab = union(enum) {...@@ -83,11 +86,14 @@ pub const Stab = union(enum) {
83 static,86 static,
84 global,87 global,
8588
86 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {89 pub fn asNlists(stab: Stab, sym_loc: SymbolWithLoc, macho_file: *MachO) ![]macho.nlist_64 {
87 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);90 const gpa = macho_file.base.allocator;
91
92 var nlists = std.ArrayList(macho.nlist_64).init(gpa);
88 defer nlists.deinit();93 defer nlists.deinit();
8994
90 const sym = macho_file.locals.items[local_sym_index];95 const sym = macho_file.getSymbol(sym_loc);
96 const sym_name = macho_file.getSymbolName(sym_loc);
91 switch (stab) {97 switch (stab) {
92 .function => |size| {98 .function => |size| {
93 try nlists.ensureUnusedCapacity(4);99 try nlists.ensureUnusedCapacity(4);
...@@ -99,7 +105,7 @@ pub const Stab = union(enum) {...@@ -99,7 +105,7 @@ pub const Stab = union(enum) {
99 .n_value = sym.n_value,105 .n_value = sym.n_value,
100 });106 });
101 nlists.appendAssumeCapacity(.{107 nlists.appendAssumeCapacity(.{
102 .n_strx = sym.n_strx,108 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
103 .n_type = macho.N_FUN,109 .n_type = macho.N_FUN,
104 .n_sect = sym.n_sect,110 .n_sect = sym.n_sect,
105 .n_desc = 0,111 .n_desc = 0,
...@@ -122,7 +128,7 @@ pub const Stab = union(enum) {...@@ -122,7 +128,7 @@ pub const Stab = union(enum) {
122 },128 },
123 .global => {129 .global => {
124 try nlists.append(.{130 try nlists.append(.{
125 .n_strx = sym.n_strx,131 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
126 .n_type = macho.N_GSYM,132 .n_type = macho.N_GSYM,
127 .n_sect = 0,133 .n_sect = 0,
128 .n_desc = 0,134 .n_desc = 0,
...@@ -131,7 +137,7 @@ pub const Stab = union(enum) {...@@ -131,7 +137,7 @@ pub const Stab = union(enum) {
131 },137 },
132 .static => {138 .static => {
133 try nlists.append(.{139 try nlists.append(.{
134 .n_strx = sym.n_strx,140 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
135 .n_type = macho.N_STSYM,141 .n_type = macho.N_STSYM,
136 .n_sect = sym.n_sect,142 .n_sect = sym.n_sect,
137 .n_desc = 0,143 .n_desc = 0,
...@@ -145,30 +151,66 @@ pub const Stab = union(enum) {...@@ -145,30 +151,66 @@ pub const Stab = union(enum) {
145};151};
146152
147pub const Relocation = struct {153pub const Relocation = struct {
148 pub const Target = union(enum) {
149 local: u32,
150 global: u32,
151 };
152
153 /// Offset within the atom's code buffer.154 /// Offset within the atom's code buffer.
154 /// Note relocation size can be inferred by relocation's kind.155 /// Note relocation size can be inferred by relocation's kind.
155 offset: u32,156 offset: u32,
156157
157 target: Target,158 target: MachO.SymbolWithLoc,
158159
159 addend: i64,160 addend: i64,
160161
161 subtractor: ?u32,162 subtractor: ?MachO.SymbolWithLoc,
162163
163 pcrel: bool,164 pcrel: bool,
164165
165 length: u2,166 length: u2,
166167
167 @"type": u4,168 @"type": u4,
169
170 pub fn getTargetAtom(self: Relocation, macho_file: *MachO) !?*Atom {
171 const is_via_got = got: {
172 switch (macho_file.base.options.target.cpu.arch) {
173 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
174 .ARM64_RELOC_GOT_LOAD_PAGE21,
175 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
176 .ARM64_RELOC_POINTER_TO_GOT,
177 => true,
178 else => false,
179 },
180 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, self.@"type")) {
181 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
182 else => false,
183 },
184 else => unreachable,
185 }
186 };
187
188 const target_sym = macho_file.getSymbol(self.target);
189 if (is_via_got) {
190 const got_index = macho_file.got_entries_table.get(self.target) orelse {
191 log.err("expected GOT entry for symbol", .{});
192 if (target_sym.undf()) {
193 log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)});
194 } else {
195 log.err(" local(%{d}) in object({d})", .{ self.target.sym_index, self.target.file });
196 }
197 log.err(" this is an internal linker error", .{});
198 return error.FailedToResolveRelocationTarget;
199 };
200 return macho_file.got_entries.items[got_index].atom;
201 }
202
203 if (macho_file.stubs_table.get(self.target)) |stub_index| {
204 return macho_file.stubs.items[stub_index].atom;
205 } else if (macho_file.tlv_ptr_entries_table.get(self.target)) |tlv_ptr_index| {
206 return macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
207 } else return macho_file.getAtomForSymbol(self.target);
208 }
168};209};
169210
170pub const empty = Atom{211pub const empty = Atom{
171 .local_sym_index = 0,212 .sym_index = 0,
213 .file = null,
172 .size = 0,214 .size = 0,
173 .alignment = 0,215 .alignment = 0,
174 .prev = null,216 .prev = null,
...@@ -196,13 +238,45 @@ pub fn clearRetainingCapacity(self: *Atom) void {...@@ -196,13 +238,45 @@ pub fn clearRetainingCapacity(self: *Atom) void {
196 self.code.clearRetainingCapacity();238 self.code.clearRetainingCapacity();
197}239}
198240
241/// Returns symbol referencing this atom.
242pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
243 return self.getSymbolPtr(macho_file).*;
244}
245
246/// Returns pointer-to-symbol referencing this atom.
247pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
248 return macho_file.getSymbolPtr(.{
249 .sym_index = self.sym_index,
250 .file = self.file,
251 });
252}
253
254/// Returns true if the symbol pointed at with `sym_loc` is contained within this atom.
255/// WARNING this function assumes all atoms have been allocated in the virtual memory.
256/// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will
257/// give bogus results.
258pub fn isSymbolContained(self: Atom, sym_loc: SymbolWithLoc, macho_file: *MachO) bool {
259 const sym = macho_file.getSymbol(sym_loc);
260 if (!sym.sect()) return false;
261 const self_sym = self.getSymbol(macho_file);
262 return sym.n_value >= self_sym.n_value and sym.n_value < self_sym.n_value + self.size;
263}
264
265/// Returns the name of this atom.
266pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
267 return macho_file.getSymbolName(.{
268 .sym_index = self.sym_index,
269 .file = self.file,
270 });
271}
272
199/// Returns how much room there is to grow in virtual address space.273/// Returns how much room there is to grow in virtual address space.
200/// File offset relocation happens transparently, so it is not included in274/// File offset relocation happens transparently, so it is not included in
201/// this calculation.275/// this calculation.
202pub fn capacity(self: Atom, macho_file: MachO) u64 {276pub fn capacity(self: Atom, macho_file: *MachO) u64 {
203 const self_sym = macho_file.locals.items[self.local_sym_index];277 const self_sym = self.getSymbol(macho_file);
204 if (self.next) |next| {278 if (self.next) |next| {
205 const next_sym = macho_file.locals.items[next.local_sym_index];279 const next_sym = next.getSymbol(macho_file);
206 return next_sym.n_value - self_sym.n_value;280 return next_sym.n_value - self_sym.n_value;
207 } else {281 } else {
208 // We are the last atom.282 // We are the last atom.
...@@ -211,11 +285,11 @@ pub fn capacity(self: Atom, macho_file: MachO) u64 {...@@ -211,11 +285,11 @@ pub fn capacity(self: Atom, macho_file: MachO) u64 {
211 }285 }
212}286}
213287
214pub fn freeListEligible(self: Atom, macho_file: MachO) bool {288pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
215 // No need to keep a free list node for the last atom.289 // No need to keep a free list node for the last atom.
216 const next = self.next orelse return false;290 const next = self.next orelse return false;
217 const self_sym = macho_file.locals.items[self.local_sym_index];291 const self_sym = self.getSymbol(macho_file);
218 const next_sym = macho_file.locals.items[next.local_sym_index];292 const next_sym = next.getSymbol(macho_file);
219 const cap = next_sym.n_value - self_sym.n_value;293 const cap = next_sym.n_value - self_sym.n_value;
220 const ideal_cap = MachO.padToIdeal(self.size);294 const ideal_cap = MachO.padToIdeal(self.size);
221 if (cap <= ideal_cap) return false;295 if (cap <= ideal_cap) return false;
...@@ -224,20 +298,20 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {...@@ -224,20 +298,20 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
224}298}
225299
226const RelocContext = struct {300const RelocContext = struct {
301 macho_file: *MachO,
227 base_addr: u64 = 0,302 base_addr: u64 = 0,
228 base_offset: i32 = 0,303 base_offset: i32 = 0,
229 allocator: Allocator,
230 object: *Object,
231 macho_file: *MachO,
232};304};
233305
234pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: RelocContext) !void {306pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: RelocContext) !void {
235 const tracy = trace(@src());307 const tracy = trace(@src());
236 defer tracy.end();308 defer tracy.end();
237309
310 const gpa = context.macho_file.base.allocator;
311
238 const arch = context.macho_file.base.options.target.cpu.arch;312 const arch = context.macho_file.base.options.target.cpu.arch;
239 var addend: i64 = 0;313 var addend: i64 = 0;
240 var subtractor: ?u32 = null;314 var subtractor: ?SymbolWithLoc = null;
241315
242 for (relocs) |rel, i| {316 for (relocs) |rel, i| {
243 blk: {317 blk: {
...@@ -274,20 +348,16 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -274,20 +348,16 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
274 }348 }
275349
276 assert(subtractor == null);350 assert(subtractor == null);
277 const sym = context.object.symtab[rel.r_symbolnum];351 const sym_loc = MachO.SymbolWithLoc{
352 .sym_index = rel.r_symbolnum,
353 .file = self.file,
354 };
355 const sym = context.macho_file.getSymbol(sym_loc);
278 if (sym.sect() and !sym.ext()) {356 if (sym.sect() and !sym.ext()) {
279 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;357 subtractor = sym_loc;
280 } else {358 } else {
281 const sym_name = context.object.getString(sym.n_strx);359 const sym_name = context.macho_file.getSymbolName(sym_loc);
282 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(360 subtractor = context.macho_file.globals.get(sym_name).?;
283 @as([]const u8, sym_name),
284 StringIndexAdapter{
285 .bytes = &context.macho_file.strtab,
286 },
287 ).?;
288 const resolv = context.macho_file.symbol_resolver.get(n_strx).?;
289 assert(resolv.where == .global);
290 subtractor = resolv.local_sym_index;
291 }361 }
292 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.362 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
293 if (relocs.len <= i + 1) {363 if (relocs.len <= i + 1) {
...@@ -318,43 +388,40 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -318,43 +388,40 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
318 continue;388 continue;
319 }389 }
320390
391 const object = &context.macho_file.objects.items[self.file.?];
321 const target = target: {392 const target = target: {
322 if (rel.r_extern == 0) {393 if (rel.r_extern == 0) {
323 const sect_id = @intCast(u16, rel.r_symbolnum - 1);394 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
324 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {395 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
325 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;396 const sect = object.getSection(sect_id);
326 const sect = seg.sections.items[sect_id];
327 const match = (try context.macho_file.getMatchingSection(sect)) orelse397 const match = (try context.macho_file.getMatchingSection(sect)) orelse
328 unreachable;398 unreachable;
329 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);399 const sym_index = @intCast(u32, object.symtab.items.len);
330 try context.macho_file.locals.append(context.allocator, .{400 try object.symtab.append(gpa, .{
331 .n_strx = 0,401 .n_strx = 0,
332 .n_type = macho.N_SECT,402 .n_type = macho.N_SECT,
333 .n_sect = @intCast(u8, context.macho_file.section_ordinals.getIndex(match).? + 1),403 .n_sect = context.macho_file.getSectionOrdinal(match),
334 .n_desc = 0,404 .n_desc = 0,
335 .n_value = 0,405 .n_value = 0,
336 });406 });
337 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);407 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
338 break :blk local_sym_index;408 break :blk sym_index;
339 };409 };
340 break :target Relocation.Target{ .local = local_sym_index };410 break :target MachO.SymbolWithLoc{ .sym_index = sym_index, .file = self.file };
341 }411 }
342412
343 const sym = context.object.symtab[rel.r_symbolnum];413 const sym_loc = MachO.SymbolWithLoc{
344 const sym_name = context.object.getString(sym.n_strx);414 .sym_index = rel.r_symbolnum,
415 .file = self.file,
416 };
417 const sym = context.macho_file.getSymbol(sym_loc);
345418
346 if (sym.sect() and !sym.ext()) {419 if (sym.sect() and !sym.ext()) {
347 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;420 break :target sym_loc;
348 break :target Relocation.Target{ .local = sym_index };421 } else {
422 const sym_name = context.macho_file.getSymbolName(sym_loc);
423 break :target context.macho_file.globals.get(sym_name).?;
349 }424 }
350
351 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
352 @as([]const u8, sym_name),
353 StringIndexAdapter{
354 .bytes = &context.macho_file.strtab,
355 },
356 ) orelse unreachable;
357 break :target Relocation.Target{ .global = n_strx };
358 };425 };
359 const offset = @intCast(u32, rel.r_address - context.base_offset);426 const offset = @intCast(u32, rel.r_address - context.base_offset);
360427
...@@ -378,8 +445,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -378,8 +445,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
378 else445 else
379 mem.readIntLittle(i32, self.code.items[offset..][0..4]);446 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
380 if (rel.r_extern == 0) {447 if (rel.r_extern == 0) {
381 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;448 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
382 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
383 addend -= @intCast(i64, target_sect_base_addr);449 addend -= @intCast(i64, target_sect_base_addr);
384 }450 }
385 try self.addPtrBindingOrRebase(rel, target, context);451 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -387,9 +453,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -387,9 +453,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
387 .ARM64_RELOC_TLVP_LOAD_PAGE21,453 .ARM64_RELOC_TLVP_LOAD_PAGE21,
388 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,454 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
389 => {455 => {
390 if (target == .global) {456 try addTlvPtrEntry(target, context);
391 try addTlvPtrEntry(target, context);
392 }
393 },457 },
394 else => {},458 else => {},
395 }459 }
...@@ -413,8 +477,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -413,8 +477,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
413 else477 else
414 mem.readIntLittle(i32, self.code.items[offset..][0..4]);478 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
415 if (rel.r_extern == 0) {479 if (rel.r_extern == 0) {
416 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;480 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
417 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
418 addend -= @intCast(i64, target_sect_base_addr);481 addend -= @intCast(i64, target_sect_base_addr);
419 }482 }
420 try self.addPtrBindingOrRebase(rel, target, context);483 try self.addPtrBindingOrRebase(rel, target, context);
...@@ -435,16 +498,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -435,16 +498,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
435 if (rel.r_extern == 0) {498 if (rel.r_extern == 0) {
436 // Note for the future self: when r_extern == 0, we should subtract correction from the499 // Note for the future self: when r_extern == 0, we should subtract correction from the
437 // addend.500 // addend.
438 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;501 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
439 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
440 addend += @intCast(i64, context.base_addr + offset + 4) -502 addend += @intCast(i64, context.base_addr + offset + 4) -
441 @intCast(i64, target_sect_base_addr);503 @intCast(i64, target_sect_base_addr);
442 }504 }
443 },505 },
444 .X86_64_RELOC_TLV => {506 .X86_64_RELOC_TLV => {
445 if (target == .global) {507 try addTlvPtrEntry(target, context);
446 try addTlvPtrEntry(target, context);
447 }
448 },508 },
449 else => {},509 else => {},
450 }510 }
...@@ -452,7 +512,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -452,7 +512,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
452 else => unreachable,512 else => unreachable,
453 }513 }
454514
455 try self.relocs.append(context.allocator, .{515 try self.relocs.append(gpa, .{
456 .offset = offset,516 .offset = offset,
457 .target = target,517 .target = target,
458 .addend = addend,518 .addend = addend,
...@@ -470,338 +530,181 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:...@@ -470,338 +530,181 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
470fn addPtrBindingOrRebase(530fn addPtrBindingOrRebase(
471 self: *Atom,531 self: *Atom,
472 rel: macho.relocation_info,532 rel: macho.relocation_info,
473 target: Relocation.Target,533 target: MachO.SymbolWithLoc,
474 context: RelocContext,534 context: RelocContext,
475) !void {535) !void {
476 switch (target) {536 const gpa = context.macho_file.base.allocator;
477 .global => |n_strx| {537 const sym = context.macho_file.getSymbol(target);
478 try self.bindings.append(context.allocator, .{538 if (sym.undf()) {
479 .n_strx = n_strx,539 const sym_name = context.macho_file.getSymbolName(target);
480 .offset = @intCast(u32, rel.r_address - context.base_offset),540 const global_index = @intCast(u32, context.macho_file.globals.getIndex(sym_name).?);
481 });541 try self.bindings.append(gpa, .{
482 },542 .global_index = global_index,
483 .local => {543 .offset = @intCast(u32, rel.r_address - context.base_offset),
484 const source_sym = context.macho_file.locals.items[self.local_sym_index];544 });
485 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];545 } else {
486 const seg = context.macho_file.load_commands.items[match.seg].segment;546 const source_sym = self.getSymbol(context.macho_file);
487 const sect = seg.sections.items[match.sect];547 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
488 const sect_type = sect.type_();548 const sect = context.macho_file.getSection(match);
489549 const sect_type = sect.type_();
490 const should_rebase = rebase: {550
491 if (rel.r_length != 3) break :rebase false;551 const should_rebase = rebase: {
492552 if (rel.r_length != 3) break :rebase false;
493 // TODO actually, a check similar to what dyld is doing, that is, verifying553
494 // that the segment is writable should be enough here.554 // TODO actually, a check similar to what dyld is doing, that is, verifying
495 const is_right_segment = blk: {555 // that the segment is writable should be enough here.
496 if (context.macho_file.data_segment_cmd_index) |idx| {556 const is_right_segment = blk: {
497 if (match.seg == idx) {557 if (context.macho_file.data_segment_cmd_index) |idx| {
498 break :blk true;558 if (match.seg == idx) {
499 }559 break :blk true;
500 }560 }
501 if (context.macho_file.data_const_segment_cmd_index) |idx| {561 }
502 if (match.seg == idx) {562 if (context.macho_file.data_const_segment_cmd_index) |idx| {
503 break :blk true;563 if (match.seg == idx) {
504 }564 break :blk true;
505 }565 }
506 break :blk false;
507 };
508
509 if (!is_right_segment) break :rebase false;
510 if (sect_type != macho.S_LITERAL_POINTERS and
511 sect_type != macho.S_REGULAR and
512 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
513 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
514 {
515 break :rebase false;
516 }566 }
517567 break :blk false;
518 break :rebase true;
519 };568 };
520569
521 if (should_rebase) {570 if (!is_right_segment) break :rebase false;
522 try self.rebases.append(571 if (sect_type != macho.S_LITERAL_POINTERS and
523 context.allocator,572 sect_type != macho.S_REGULAR and
524 @intCast(u32, rel.r_address - context.base_offset),573 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
525 );574 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
575 {
576 break :rebase false;
526 }577 }
527 },578
579 break :rebase true;
580 };
581
582 if (should_rebase) {
583 try self.rebases.append(gpa, @intCast(u32, rel.r_address - context.base_offset));
584 }
528 }585 }
529}586}
530587
531fn addTlvPtrEntry(target: Relocation.Target, context: RelocContext) !void {588fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
589 const target_sym = context.macho_file.getSymbol(target);
590 if (!target_sym.undf()) return;
532 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;591 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;
533592
534 const index = try context.macho_file.allocateTlvPtrEntry(target);593 const index = try context.macho_file.allocateTlvPtrEntry(target);
535 const atom = try context.macho_file.createTlvPtrAtom(target);594 const atom = try context.macho_file.createTlvPtrAtom(target);
536 context.macho_file.tlv_ptr_entries.items[index].atom = atom;595 context.macho_file.tlv_ptr_entries.items[index].atom = atom;
537
538 const match = (try context.macho_file.getMatchingSection(.{
539 .segname = MachO.makeStaticString("__DATA"),
540 .sectname = MachO.makeStaticString("__thread_ptrs"),
541 .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS,
542 })).?;
543 if (!context.object.start_atoms.contains(match)) {
544 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
545 }
546 if (context.object.end_atoms.getPtr(match)) |last| {
547 last.*.next = atom;
548 atom.prev = last.*;
549 last.* = atom;
550 } else {
551 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
552 }
553}596}
554597
555fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {598fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
556 if (context.macho_file.got_entries_table.contains(target)) return;599 if (context.macho_file.got_entries_table.contains(target)) return;
557600
558 const index = try context.macho_file.allocateGotEntry(target);601 const index = try context.macho_file.allocateGotEntry(target);
559 const atom = try context.macho_file.createGotAtom(target);602 const atom = try context.macho_file.createGotAtom(target);
560 context.macho_file.got_entries.items[index].atom = atom;603 context.macho_file.got_entries.items[index].atom = atom;
561
562 const match = MachO.MatchingSection{
563 .seg = context.macho_file.data_const_segment_cmd_index.?,
564 .sect = context.macho_file.got_section_index.?,
565 };
566 if (!context.object.start_atoms.contains(match)) {
567 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
568 }
569 if (context.object.end_atoms.getPtr(match)) |last| {
570 last.*.next = atom;
571 atom.prev = last.*;
572 last.* = atom;
573 } else {
574 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
575 }
576}604}
577605
578fn addStub(target: Relocation.Target, context: RelocContext) !void {606fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
579 if (target != .global) return;607 const target_sym = context.macho_file.getSymbol(target);
580 if (context.macho_file.stubs_table.contains(target.global)) return;608 if (!target_sym.undf()) return;
581 // If the symbol has been resolved as defined globally elsewhere (in a different translation unit),609 if (context.macho_file.stubs_table.contains(target)) return;
582 // then skip creating stub entry.
583 // TODO Is this the correct for the incremental?
584 if (context.macho_file.symbol_resolver.get(target.global).?.where == .global) return;
585
586 const stub_index = try context.macho_file.allocateStubEntry(target.global);
587
588 // TODO clean this up!
589 const stub_helper_atom = atom: {
590 const atom = try context.macho_file.createStubHelperAtom();
591 const match = MachO.MatchingSection{
592 .seg = context.macho_file.text_segment_cmd_index.?,
593 .sect = context.macho_file.stub_helper_section_index.?,
594 };
595 if (!context.object.start_atoms.contains(match)) {
596 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
597 }
598 if (context.object.end_atoms.getPtr(match)) |last| {
599 last.*.next = atom;
600 atom.prev = last.*;
601 last.* = atom;
602 } else {
603 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
604 }
605 break :atom atom;
606 };
607 const laptr_atom = atom: {
608 const atom = try context.macho_file.createLazyPointerAtom(
609 stub_helper_atom.local_sym_index,
610 target.global,
611 );
612 const match = MachO.MatchingSection{
613 .seg = context.macho_file.data_segment_cmd_index.?,
614 .sect = context.macho_file.la_symbol_ptr_section_index.?,
615 };
616 if (!context.object.start_atoms.contains(match)) {
617 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
618 }
619 if (context.object.end_atoms.getPtr(match)) |last| {
620 last.*.next = atom;
621 atom.prev = last.*;
622 last.* = atom;
623 } else {
624 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
625 }
626 break :atom atom;
627 };
628 const atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index);
629 const match = MachO.MatchingSection{
630 .seg = context.macho_file.text_segment_cmd_index.?,
631 .sect = context.macho_file.stubs_section_index.?,
632 };
633 if (!context.object.start_atoms.contains(match)) {
634 try context.object.start_atoms.putNoClobber(context.allocator, match, atom);
635 }
636 if (context.object.end_atoms.getPtr(match)) |last| {
637 last.*.next = atom;
638 atom.prev = last.*;
639 last.* = atom;
640 } else {
641 try context.object.end_atoms.putNoClobber(context.allocator, match, atom);
642 }
643 context.macho_file.stubs.items[stub_index] = atom;
644}
645610
646pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) !?*Atom {611 const stub_index = try context.macho_file.allocateStubEntry(target);
647 const is_via_got = got: {612 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
648 switch (macho_file.base.options.target.cpu.arch) {613 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
649 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {614 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
650 .ARM64_RELOC_GOT_LOAD_PAGE21,
651 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
652 .ARM64_RELOC_POINTER_TO_GOT,
653 => true,
654 else => false,
655 },
656 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
657 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
658 else => false,
659 },
660 else => unreachable,
661 }
662 };
663
664 if (is_via_got) {
665 const got_index = macho_file.got_entries_table.get(rel.target) orelse {
666 log.err("expected GOT entry for symbol", .{});
667 switch (rel.target) {
668 .local => |sym_index| log.err(" local @{d}", .{sym_index}),
669 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),
670 }
671 log.err(" this is an internal linker error", .{});
672 return error.FailedToResolveRelocationTarget;
673 };
674 return macho_file.got_entries.items[got_index].atom;
675 }
676615
677 switch (rel.target) {616 context.macho_file.stubs.items[stub_index].atom = stub_atom;
678 .local => |sym_index| {
679 return macho_file.atom_by_index_table.get(sym_index);
680 },
681 .global => |n_strx| {
682 const resolv = macho_file.symbol_resolver.get(n_strx).?;
683 switch (resolv.where) {
684 .global => return macho_file.atom_by_index_table.get(resolv.local_sym_index),
685 .undef => {
686 if (macho_file.stubs_table.get(n_strx)) |stub_index| {
687 return macho_file.stubs.items[stub_index];
688 } else {
689 if (macho_file.tlv_ptr_entries_table.get(rel.target)) |tlv_ptr_index| {
690 return macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
691 }
692 return null;
693 }
694 },
695 }
696 },
697 }
698}617}
699618
700pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {619pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
701 const tracy = trace(@src());620 const tracy = trace(@src());
702 defer tracy.end();621 defer tracy.end();
703622
623 log.debug("ATOM(%{d}, '{s}')", .{ self.sym_index, self.getName(macho_file) });
624
704 for (self.relocs.items) |rel| {625 for (self.relocs.items) |rel| {
705 log.debug("relocating {}", .{rel});
706 const arch = macho_file.base.options.target.cpu.arch;626 const arch = macho_file.base.options.target.cpu.arch;
627 switch (arch) {
628 .aarch64 => {
629 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
630 @tagName(@intToEnum(macho.reloc_type_arm64, rel.@"type")),
631 rel.offset,
632 rel.target.sym_index,
633 rel.target.file,
634 });
635 },
636 .x86_64 => {
637 log.debug(" RELA({s}) @ {x} => %{d} in object({d})", .{
638 @tagName(@intToEnum(macho.reloc_type_x86_64, rel.@"type")),
639 rel.offset,
640 rel.target.sym_index,
641 rel.target.file,
642 });
643 },
644 else => unreachable,
645 }
646
707 const source_addr = blk: {647 const source_addr = blk: {
708 const sym = macho_file.locals.items[self.local_sym_index];648 const source_sym = self.getSymbol(macho_file);
709 break :blk sym.n_value + rel.offset;649 break :blk source_sym.n_value + rel.offset;
650 };
651 const is_tlv = is_tlv: {
652 const source_sym = self.getSymbol(macho_file);
653 const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
654 const sect = macho_file.getSection(match);
655 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
710 };656 };
711 var is_via_thread_ptrs: bool = false;
712 const target_addr = blk: {657 const target_addr = blk: {
713 const is_via_got = got: {658 const target_atom = (try rel.getTargetAtom(macho_file)) orelse {
714 switch (arch) {659 // If there is no atom for target, we still need to check for special, atom-less
715 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {660 // symbols such as `___dso_handle`.
716 .ARM64_RELOC_GOT_LOAD_PAGE21,661 const target_name = macho_file.getSymbolName(rel.target);
717 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,662 if (macho_file.globals.contains(target_name)) {
718 .ARM64_RELOC_POINTER_TO_GOT,663 const atomless_sym = macho_file.getSymbol(rel.target);
719 => true,664 log.debug(" | atomless target '{s}'", .{target_name});
720 else => false,665 break :blk atomless_sym.n_value;
721 },
722 .x86_64 => break :got switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
723 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => true,
724 else => false,
725 },
726 else => unreachable,
727 }666 }
667 log.debug(" | undef target '{s}'", .{target_name});
668 break :blk 0;
728 };669 };
729670 log.debug(" | target ATOM(%{d}, '{s}') in object({d})", .{
730 if (is_via_got) {671 target_atom.sym_index,
731 const got_index = macho_file.got_entries_table.get(rel.target) orelse {672 target_atom.getName(macho_file),
732 log.err("expected GOT entry for symbol", .{});673 target_atom.file,
733 switch (rel.target) {674 });
734 .local => |sym_index| log.err(" local @{d}", .{sym_index}),675 // If `rel.target` is contained within the target atom, pull its address value.
735 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),676 const target_sym = if (target_atom.isSymbolContained(rel.target, macho_file))
677 macho_file.getSymbol(rel.target)
678 else
679 target_atom.getSymbol(macho_file);
680 const base_address: u64 = if (is_tlv) base_address: {
681 // For TLV relocations, the value specified as a relocation is the displacement from the
682 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
683 // defined TLV template init section in the following order:
684 // * wrt to __thread_data if defined, then
685 // * wrt to __thread_bss
686 const sect_id: u16 = sect_id: {
687 if (macho_file.tlv_data_section_index) |i| {
688 break :sect_id i;
689 } else if (macho_file.tlv_bss_section_index) |i| {
690 break :sect_id i;
691 } else {
692 log.err("threadlocal variables present but no initializer sections found", .{});
693 log.err(" __thread_data not found", .{});
694 log.err(" __thread_bss not found", .{});
695 return error.FailedToResolveRelocationTarget;
736 }696 }
737 log.err(" this is an internal linker error", .{});
738 return error.FailedToResolveRelocationTarget;
739 };697 };
740 const atom = macho_file.got_entries.items[got_index].atom;698 break :base_address macho_file.getSection(.{
741 break :blk macho_file.locals.items[atom.local_sym_index].n_value;699 .seg = macho_file.data_segment_cmd_index.?,
742 }700 .sect = sect_id,
743701 }).addr;
744 switch (rel.target) {702 } else 0;
745 .local => |sym_index| {703 break :blk target_sym.n_value - base_address;
746 const sym = macho_file.locals.items[sym_index];
747 const is_tlv = is_tlv: {
748 const source_sym = macho_file.locals.items[self.local_sym_index];
749 const match = macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
750 const seg = macho_file.load_commands.items[match.seg].segment;
751 const sect = seg.sections.items[match.sect];
752 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
753 };
754 if (is_tlv) {
755 // For TLV relocations, the value specified as a relocation is the displacement from the
756 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
757 // defined TLV template init section in the following order:
758 // * wrt to __thread_data if defined, then
759 // * wrt to __thread_bss
760 const seg = macho_file.load_commands.items[macho_file.data_segment_cmd_index.?].segment;
761 const base_address = inner: {
762 if (macho_file.tlv_data_section_index) |i| {
763 break :inner seg.sections.items[i].addr;
764 } else if (macho_file.tlv_bss_section_index) |i| {
765 break :inner seg.sections.items[i].addr;
766 } else {
767 log.err("threadlocal variables present but no initializer sections found", .{});
768 log.err(" __thread_data not found", .{});
769 log.err(" __thread_bss not found", .{});
770 return error.FailedToResolveRelocationTarget;
771 }
772 };
773 break :blk sym.n_value - base_address;
774 }
775 break :blk sym.n_value;
776 },
777 .global => |n_strx| {
778 // TODO Still trying to figure out how to possibly use stubs for local symbol indirection with
779 // branching instructions. If it is not possible, then the best course of action is to
780 // resurrect the former approach of defering creating synthethic atoms in __got and __la_symbol_ptr
781 // sections until we resolve the relocations.
782 const resolv = macho_file.symbol_resolver.get(n_strx).?;
783 switch (resolv.where) {
784 .global => break :blk macho_file.globals.items[resolv.where_index].n_value,
785 .undef => {
786 if (macho_file.stubs_table.get(n_strx)) |stub_index| {
787 const atom = macho_file.stubs.items[stub_index];
788 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
789 } else {
790 if (macho_file.tlv_ptr_entries_table.get(rel.target)) |tlv_ptr_index| {
791 is_via_thread_ptrs = true;
792 const atom = macho_file.tlv_ptr_entries.items[tlv_ptr_index].atom;
793 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
794 }
795 break :blk 0;
796 }
797 },
798 }
799 },
800 }
801 };704 };
802705
803 log.debug(" | source_addr = 0x{x}", .{source_addr});706 log.debug(" | source_addr = 0x{x}", .{source_addr});
804 log.debug(" | target_addr = 0x{x}", .{target_addr});707 log.debug(" | target_addr = 0x{x}", .{target_addr});
805708
806 switch (arch) {709 switch (arch) {
807 .aarch64 => {710 .aarch64 => {
...@@ -933,7 +836,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -933,7 +836,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
933 }836 }
934 };837 };
935 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));838 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
936 var inst = if (is_via_thread_ptrs) blk: {839 var inst = if (macho_file.tlv_ptr_entries_table.contains(rel.target)) blk: {
937 const offset = try math.divExact(u12, narrowed, 8);840 const offset = try math.divExact(u12, narrowed, 8);
938 break :blk aarch64.Instruction{841 break :blk aarch64.Instruction{
939 .load_store_register = .{842 .load_store_register = .{
...@@ -966,7 +869,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -966,7 +869,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
966 .ARM64_RELOC_UNSIGNED => {869 .ARM64_RELOC_UNSIGNED => {
967 const result = blk: {870 const result = blk: {
968 if (rel.subtractor) |subtractor| {871 if (rel.subtractor) |subtractor| {
969 const sym = macho_file.locals.items[subtractor];872 const sym = macho_file.getSymbol(subtractor);
970 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;873 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
971 } else {874 } else {
972 break :blk @intCast(i64, target_addr) + rel.addend;875 break :blk @intCast(i64, target_addr) + rel.addend;
...@@ -1004,7 +907,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -1004,7 +907,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1004 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));907 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
1005 },908 },
1006 .X86_64_RELOC_TLV => {909 .X86_64_RELOC_TLV => {
1007 if (!is_via_thread_ptrs) {910 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {
1008 // We need to rewrite the opcode from movq to leaq.911 // We need to rewrite the opcode from movq to leaq.
1009 self.code.items[rel.offset - 2] = 0x8d;912 self.code.items[rel.offset - 2] = 0x8d;
1010 }913 }
...@@ -1036,7 +939,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -1036,7 +939,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1036 .X86_64_RELOC_UNSIGNED => {939 .X86_64_RELOC_UNSIGNED => {
1037 const result = blk: {940 const result = blk: {
1038 if (rel.subtractor) |subtractor| {941 if (rel.subtractor) |subtractor| {
1039 const sym = macho_file.locals.items[subtractor];942 const sym = macho_file.getSymbol(subtractor);
1040 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;943 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
1041 } else {944 } else {
1042 break :blk @intCast(i64, target_addr) + rel.addend;945 break :blk @intCast(i64, target_addr) + rel.addend;
src/link/MachO/DebugSymbols.zig+45-14
...@@ -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(.link) = .{},
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,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -269,22 +273,30 @@ 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(.{ .sym_index = reloc.target, .file = null }).?;
275 const got_entry = self.base.got_entries.items[got_index];279 const got_atom = self.base.got_entries.items[got_index].atom;
276 break :blk self.base.locals.items[got_entry.atom.local_sym_index];280 break :blk got_atom.getSymbol(self.base);
277 },281 },
278 };282 };
279 if (sym.n_value == reloc.prev_vaddr) continue;283 if (sym.n_value == reloc.prev_vaddr) continue;
280284
285 const sym_name = switch (reloc.@"type") {
286 .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
287 .got_load => blk: {
288 const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?;
289 const got_atom = self.base.got_entries.items[got_index].atom;
290 break :blk got_atom.getName(self.base);
291 },
292 };
281 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;293 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
282 const sect = &seg.sections.items[self.debug_info_section_index.?];294 const sect = &seg.sections.items[self.debug_info_section_index.?];
283 const file_offset = sect.offset + reloc.offset;295 const file_offset = sect.offset + reloc.offset;
284 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{296 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
285 reloc.target,297 reloc.target,
286 sym.n_value,298 sym.n_value,
287 self.base.getString(sym.n_strx),299 sym_name,
288 file_offset,300 file_offset,
289 });301 });
290 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);302 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);
...@@ -367,6 +379,7 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {...@@ -367,6 +379,7 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
367 }379 }
368 self.load_commands.deinit(allocator);380 self.load_commands.deinit(allocator);
369 self.dwarf.deinit();381 self.dwarf.deinit();
382 self.strtab.deinit(allocator);
370 self.relocs.deinit(allocator);383 self.relocs.deinit(allocator);
371}384}
372385
...@@ -582,21 +595,39 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -582,21 +595,39 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
582 const tracy = trace(@src());595 const tracy = trace(@src());
583 defer tracy.end();596 defer tracy.end();
584597
598 const gpa = self.base.base.allocator;
585 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;599 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
586 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;600 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
587 symtab.symoff = @intCast(u32, seg.inner.fileoff);601 symtab.symoff = @intCast(u32, seg.inner.fileoff);
588602
589 var locals = std.ArrayList(macho.nlist_64).init(self.base.base.allocator);603 var locals = std.ArrayList(macho.nlist_64).init(gpa);
590 defer locals.deinit();604 defer locals.deinit();
591605
592 for (self.base.locals.items) |sym| {606 for (self.base.locals.items) |sym, sym_id| {
593 if (sym.n_strx == 0) continue;607 if (sym.n_strx == 0) continue; // no name, skip
594 if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue;608 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
595 try locals.append(sym);609 const sym_loc = MachO.SymbolWithLoc{ .sym_index = @intCast(u32, sym_id), .file = null };
610 if (self.base.symbolIsTemp(sym_loc)) continue; // local temp symbol, skip
611 if (self.base.globals.contains(self.base.getSymbolName(sym_loc))) continue; // global symbol is either an export or import, skip
612 var out_sym = sym;
613 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(sym_loc));
614 try locals.append(out_sym);
615 }
616
617 var exports = std.ArrayList(macho.nlist_64).init(gpa);
618 defer exports.deinit();
619
620 for (self.base.globals.values()) |global| {
621 const sym = self.base.getSymbol(global);
622 if (sym.undf()) continue; // import, skip
623 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
624 var out_sym = sym;
625 out_sym.n_strx = try self.strtab.insert(gpa, self.base.getSymbolName(global));
626 try exports.append(out_sym);
596 }627 }
597628
598 const nlocals = locals.items.len;629 const nlocals = locals.items.len;
599 const nexports = self.base.globals.items.len;630 const nexports = exports.items.len;
600 const locals_off = symtab.symoff;631 const locals_off = symtab.symoff;
601 const locals_size = nlocals * @sizeOf(macho.nlist_64);632 const locals_size = nlocals * @sizeOf(macho.nlist_64);
602 const exports_off = locals_off + locals_size;633 const exports_off = locals_off + locals_size;
...@@ -641,7 +672,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void {...@@ -641,7 +672,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
641 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);672 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
642673
643 log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });674 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);675 try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
645676
646 self.load_commands_dirty = true;677 self.load_commands_dirty = true;
647}678}
...@@ -655,7 +686,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -655,7 +686,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
655 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));686 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
656 symtab.stroff = symtab.symoff + symtab_size;687 symtab.stroff = symtab.symoff + symtab_size;
657688
658 const needed_size = mem.alignForwardGeneric(u64, self.base.strtab.items.len, @alignOf(u64));689 const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
659 symtab.strsize = @intCast(u32, needed_size);690 symtab.strsize = @intCast(u32, needed_size);
660691
661 if (symtab_size + needed_size > seg.inner.filesize) {692 if (symtab_size + needed_size > seg.inner.filesize) {
...@@ -692,7 +723,7 @@ fn writeStringTable(self: *DebugSymbols) !void {...@@ -692,7 +723,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
692723
693 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });724 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
694725
695 try self.file.pwriteAll(self.base.strtab.items, symtab.stroff);726 try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
696727
697 self.load_commands_dirty = true;728 self.load_commands_dirty = true;
698}729}
src/link/MachO/Object.zig+210-227
...@@ -47,7 +47,7 @@ dwarf_debug_line_index: ?u16 = null,...@@ -47,7 +47,7 @@ dwarf_debug_line_index: ?u16 = null,
47dwarf_debug_line_str_index: ?u16 = null,47dwarf_debug_line_str_index: ?u16 = null,
48dwarf_debug_ranges_index: ?u16 = null,48dwarf_debug_ranges_index: ?u16 = null,
4949
50symtab: []const macho.nlist_64 = &.{},50symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
51strtab: []const u8 = &.{},51strtab: []const u8 = &.{},
52data_in_code_entries: []const macho.data_in_code_entry = &.{},52data_in_code_entries: []const macho.data_in_code_entry = &.{},
5353
...@@ -57,17 +57,13 @@ tu_name: ?[]const u8 = null,...@@ -57,17 +57,13 @@ tu_name: ?[]const u8 = null,
57tu_comp_dir: ?[]const u8 = null,57tu_comp_dir: ?[]const u8 = null,
58mtime: ?u64 = null,58mtime: ?u64 = null,
5959
60contained_atoms: std.ArrayListUnmanaged(*Atom) = .{},
61start_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
62end_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
63sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},60sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
6461
65// TODO symbol mapping and its inverse can probably be simple arrays62/// List of atoms that map to the symbols parsed from this object file.
66// instead of hash maps.63managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
67symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
68reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
6964
70analyzed: bool = false,65/// Table of atoms belonging to this object file indexed by the symbol index.
66atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
7167
72const DebugInfo = struct {68const DebugInfo = struct {
73 inner: dwarf.DwarfInfo,69 inner: dwarf.DwarfInfo,
...@@ -135,97 +131,25 @@ const DebugInfo = struct {...@@ -135,97 +131,25 @@ const DebugInfo = struct {
135 }131 }
136};132};
137133
138pub fn deinit(self: *Object, allocator: Allocator) void {134pub fn deinit(self: *Object, gpa: Allocator) void {
139 for (self.load_commands.items) |*lc| {135 for (self.load_commands.items) |*lc| {
140 lc.deinit(allocator);136 lc.deinit(gpa);
141 }137 }
142 self.load_commands.deinit(allocator);138 self.load_commands.deinit(gpa);
143 allocator.free(self.contents);139 gpa.free(self.contents);
144 self.sections_as_symbols.deinit(allocator);140 self.sections_as_symbols.deinit(gpa);
145 self.symbol_mapping.deinit(allocator);141 self.atom_by_index_table.deinit(gpa);
146 self.reverse_symbol_mapping.deinit(allocator);142
147 allocator.free(self.name);143 for (self.managed_atoms.items) |atom| {
148144 atom.deinit(gpa);
149 self.contained_atoms.deinit(allocator);145 gpa.destroy(atom);
150 self.start_atoms.deinit(allocator);
151 self.end_atoms.deinit(allocator);
152
153 if (self.debug_info) |*db| {
154 db.deinit(allocator);
155 }
156}
157
158pub fn free(self: *Object, allocator: Allocator, macho_file: *MachO) void {
159 log.debug("freeObject {*}", .{self});
160
161 var it = self.end_atoms.iterator();
162 while (it.next()) |entry| {
163 const match = entry.key_ptr.*;
164 const first_atom = self.start_atoms.get(match).?;
165 const last_atom = entry.value_ptr.*;
166 var atom = first_atom;
167
168 while (true) {
169 if (atom.local_sym_index != 0) {
170 macho_file.locals_free_list.append(allocator, atom.local_sym_index) catch {};
171 const local = &macho_file.locals.items[atom.local_sym_index];
172 local.* = .{
173 .n_strx = 0,
174 .n_type = 0,
175 .n_sect = 0,
176 .n_desc = 0,
177 .n_value = 0,
178 };
179 _ = macho_file.atom_by_index_table.remove(atom.local_sym_index);
180 _ = macho_file.gc_roots.remove(atom);
181
182 for (atom.contained.items) |sym_off| {
183 _ = macho_file.atom_by_index_table.remove(sym_off.local_sym_index);
184 }
185
186 atom.local_sym_index = 0;
187 }
188 if (atom == last_atom) {
189 break;
190 }
191 if (atom.next) |next| {
192 atom = next;
193 } else break;
194 }
195 }146 }
147 self.managed_atoms.deinit(gpa);
196148
197 self.freeAtoms(macho_file);149 gpa.free(self.name);
198}
199
200fn freeAtoms(self: *Object, macho_file: *MachO) void {
201 var it = self.end_atoms.iterator();
202 while (it.next()) |entry| {
203 const match = entry.key_ptr.*;
204 var first_atom: *Atom = self.start_atoms.get(match).?;
205 var last_atom: *Atom = entry.value_ptr.*;
206
207 if (macho_file.atoms.getPtr(match)) |atom_ptr| {
208 if (atom_ptr.* == last_atom) {
209 if (first_atom.prev) |prev| {
210 // TODO shrink the section size here
211 atom_ptr.* = prev;
212 } else {
213 _ = macho_file.atoms.fetchRemove(match);
214 }
215 }
216 }
217150
218 if (first_atom.prev) |prev| {151 if (self.debug_info) |*db| {
219 prev.next = last_atom.next;152 db.deinit(gpa);
220 } else {
221 first_atom.prev = null;
222 }
223
224 if (last_atom.next) |next| {
225 next.prev = last_atom.prev;
226 } else {
227 last_atom.next = null;
228 }
229 }153 }
230}154}
231155
...@@ -327,24 +251,40 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {...@@ -327,24 +251,40 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {
327 self.load_commands.appendAssumeCapacity(cmd);251 self.load_commands.appendAssumeCapacity(cmd);
328 }252 }
329253
330 self.parseSymtab();254 try self.parseSymtab(allocator);
331 self.parseDataInCode();255 self.parseDataInCode();
332 try self.parseDebugInfo(allocator);256 try self.parseDebugInfo(allocator);
333}257}
334258
335const NlistWithIndex = struct {259const Context = struct {
336 nlist: macho.nlist_64,260 symtab: []const macho.nlist_64,
261 strtab: []const u8,
262};
263
264const SymbolAtIndex = struct {
337 index: u32,265 index: u32,
338266
339 fn lessThan(_: void, lhs: NlistWithIndex, rhs: NlistWithIndex) bool {267 fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
268 return ctx.symtab[self.index];
269 }
270
271 fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
272 const sym = self.getSymbol(ctx);
273 if (sym.n_strx == 0) return "";
274 return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
275 }
276
277 fn lessThan(ctx: Context, lhs_index: SymbolAtIndex, rhs_index: SymbolAtIndex) bool {
340 // We sort by type: defined < undefined, and278 // We sort by type: defined < undefined, and
341 // afterwards by address in each group. Normally, dysymtab should279 // afterwards by address in each group. Normally, dysymtab should
342 // be enough to guarantee the sort, but turns out not every compiler280 // be enough to guarantee the sort, but turns out not every compiler
343 // is kind enough to specify the symbols in the correct order.281 // is kind enough to specify the symbols in the correct order.
344 if (lhs.nlist.sect()) {282 const lhs = lhs_index.getSymbol(ctx);
345 if (rhs.nlist.sect()) {283 const rhs = rhs_index.getSymbol(ctx);
284 if (lhs.sect()) {
285 if (rhs.sect()) {
346 // Same group, sort by address.286 // Same group, sort by address.
347 return lhs.nlist.n_value < rhs.nlist.n_value;287 return lhs.n_value < rhs.n_value;
348 } else {288 } else {
349 return true;289 return true;
350 }290 }
...@@ -352,26 +292,34 @@ const NlistWithIndex = struct {...@@ -352,26 +292,34 @@ const NlistWithIndex = struct {
352 return false;292 return false;
353 }293 }
354 }294 }
295};
355296
356 fn filterByAddress(symbols: []NlistWithIndex, start_addr: u64, end_addr: u64) []NlistWithIndex {297fn filterSymbolsByAddress(
357 const Predicate = struct {298 indexes: []SymbolAtIndex,
358 addr: u64,299 start_addr: u64,
300 end_addr: u64,
301 ctx: Context,
302) []SymbolAtIndex {
303 const Predicate = struct {
304 addr: u64,
305 ctx: Context,
359306
360 pub fn predicate(self: @This(), symbol: NlistWithIndex) bool {307 pub fn predicate(pred: @This(), index: SymbolAtIndex) bool {
361 return symbol.nlist.n_value >= self.addr;308 return index.getSymbol(pred.ctx).n_value >= pred.addr;
362 }309 }
363 };310 };
364311
365 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{312 const start = MachO.findFirst(SymbolAtIndex, indexes, 0, Predicate{
366 .addr = start_addr,313 .addr = start_addr,
367 });314 .ctx = ctx,
368 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{315 });
369 .addr = end_addr,316 const end = MachO.findFirst(SymbolAtIndex, indexes, start, Predicate{
370 });317 .addr = end_addr,
318 .ctx = ctx,
319 });
371320
372 return symbols[start..end];321 return indexes[start..end];
373 }322}
374};
375323
376fn filterRelocs(324fn filterRelocs(
377 relocs: []const macho.relocation_info,325 relocs: []const macho.relocation_info,
...@@ -411,29 +359,32 @@ fn filterDice(...@@ -411,29 +359,32 @@ fn filterDice(
411 return dices[start..end];359 return dices[start..end];
412}360}
413361
414pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !void {362/// Splits object into atoms assuming whole cache mode aka traditional linking mode.
363pub fn splitIntoAtomsWhole(self: *Object, macho_file: *MachO, object_id: u32) !void {
415 const tracy = trace(@src());364 const tracy = trace(@src());
416 defer tracy.end();365 defer tracy.end();
417366
367 const gpa = macho_file.base.allocator;
418 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;368 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
419369
420 log.debug("analysing {s}", .{self.name});370 log.debug("splitting object({d}, {s}) into atoms: whole cache mode", .{ object_id, self.name });
421371
422 // You would expect that the symbol table is at least pre-sorted based on symbol's type:372 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
423 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,373 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
424 // the GO compiler does not necessarily respect that therefore we sort immediately by type374 // the GO compiler does not necessarily respect that therefore we sort immediately by type
425 // and address within.375 // and address within.
426 var sorted_all_nlists = try std.ArrayList(NlistWithIndex).initCapacity(allocator, self.symtab.len);376 const context = Context{
427 defer sorted_all_nlists.deinit();377 .symtab = self.getSourceSymtab(),
378 .strtab = self.strtab,
379 };
380 var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
381 defer sorted_all_syms.deinit();
428382
429 for (self.symtab) |nlist, index| {383 for (context.symtab) |_, index| {
430 sorted_all_nlists.appendAssumeCapacity(.{384 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
431 .nlist = nlist,
432 .index = @intCast(u32, index),
433 });
434 }385 }
435386
436 sort.sort(NlistWithIndex, sorted_all_nlists.items, {}, NlistWithIndex.lessThan);387 sort.sort(SymbolAtIndex, sorted_all_syms.items, context, SymbolAtIndex.lessThan);
437388
438 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we389 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
439 // have to infer the start of undef section in the symtab ourselves.390 // have to infer the start of undef section in the symtab ourselves.
...@@ -441,30 +392,36 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -441,30 +392,36 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
441 const dysymtab = self.load_commands.items[cmd_index].dysymtab;392 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
442 break :blk dysymtab.iundefsym;393 break :blk dysymtab.iundefsym;
443 } else blk: {394 } else blk: {
444 var iundefsym: usize = sorted_all_nlists.items.len;395 var iundefsym: usize = sorted_all_syms.items.len;
445 while (iundefsym > 0) : (iundefsym -= 1) {396 while (iundefsym > 0) : (iundefsym -= 1) {
446 const nlist = sorted_all_nlists.items[iundefsym - 1];397 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
447 if (nlist.nlist.sect()) break;398 if (sym.sect()) break;
448 }399 }
449 break :blk iundefsym;400 break :blk iundefsym;
450 };401 };
451402
452 // We only care about defined symbols, so filter every other out.403 // We only care about defined symbols, so filter every other out.
453 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];404 const sorted_syms = sorted_all_syms.items[0..iundefsym];
454
455 const dead_strip = macho_file.base.options.gc_sections orelse false;405 const dead_strip = macho_file.base.options.gc_sections orelse false;
456 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and406 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and
457 (macho_file.base.options.optimize_mode != .Debug or dead_strip);407 (macho_file.base.options.optimize_mode != .Debug or dead_strip);
408 // const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
458409
459 for (seg.sections.items) |sect, id| {410 for (seg.sections.items) |sect, id| {
460 const sect_id = @intCast(u8, id);411 const sect_id = @intCast(u8, id);
461 log.debug("parsing section '{s},{s}' into Atoms", .{ sect.segName(), sect.sectName() });412 log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
462413
463 // Get matching segment/section in the final artifact.414 // Get matching segment/section in the final artifact.
464 const match = (try macho_file.getMatchingSection(sect)) orelse {415 const match = (try macho_file.getMatchingSection(sect)) orelse {
465 log.debug("unhandled section", .{});416 log.debug(" unhandled section", .{});
466 continue;417 continue;
467 };418 };
419 const target_sect = macho_file.getSection(match);
420 log.debug(" output sect({d}, '{s},{s}')", .{
421 macho_file.getSectionOrdinal(match),
422 target_sect.segName(),
423 target_sect.sectName(),
424 });
468425
469 const is_zerofill = blk: {426 const is_zerofill = blk: {
470 const section_type = sect.type_();427 const section_type = sect.type_();
...@@ -482,10 +439,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -482,10 +439,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
482 );439 );
483440
484 // Symbols within this section only.441 // Symbols within this section only.
485 const filtered_nlists = NlistWithIndex.filterByAddress(442 const filtered_syms = filterSymbolsByAddress(
486 sorted_nlists,443 sorted_syms,
487 sect.addr,444 sect.addr,
488 sect.addr + sect.size,445 sect.addr + sect.size,
446 context,
489 );447 );
490448
491 macho_file.has_dices = macho_file.has_dices or blk: {449 macho_file.has_dices = macho_file.has_dices or blk: {
...@@ -498,32 +456,33 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -498,32 +456,33 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
498 };456 };
499 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;457 macho_file.has_stabs = macho_file.has_stabs or self.debug_info != null;
500458
501 if (subsections_via_symbols and filtered_nlists.len > 0) {459 if (subsections_via_symbols and filtered_syms.len > 0) {
502 // If the first nlist does not match the start of the section,460 // If the first nlist does not match the start of the section,
503 // then we need to encapsulate the memory range [section start, first symbol)461 // then we need to encapsulate the memory range [section start, first symbol)
504 // as a temporary symbol and insert the matching Atom.462 // as a temporary symbol and insert the matching Atom.
505 const first_nlist = filtered_nlists[0].nlist;463 const first_sym = filtered_syms[0].getSymbol(context);
506 if (first_nlist.n_value > sect.addr) {464 if (first_sym.n_value > sect.addr) {
507 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {465 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
508 const local_sym_index = @intCast(u32, macho_file.locals.items.len);466 const sym_index = @intCast(u32, self.symtab.items.len);
509 try macho_file.locals.append(allocator, .{467 try self.symtab.append(gpa, .{
510 .n_strx = 0,468 .n_strx = 0,
511 .n_type = macho.N_SECT,469 .n_type = macho.N_SECT,
512 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),470 .n_sect = macho_file.getSectionOrdinal(match),
513 .n_desc = 0,471 .n_desc = 0,
514 .n_value = sect.addr,472 .n_value = sect.addr,
515 });473 });
516 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);474 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
517 break :blk local_sym_index;475 break :blk sym_index;
518 };476 };
519 const atom_size = first_nlist.n_value - sect.addr;477 const atom_size = first_sym.n_value - sect.addr;
520 const atom_code: ?[]const u8 = if (code) |cc|478 const atom_code: ?[]const u8 = if (code) |cc|
521 cc[0..atom_size]479 cc[0..atom_size]
522 else480 else
523 null;481 null;
524 try self.parseIntoAtom(482 const atom = try self.createAtomFromSubsection(
525 allocator,483 macho_file,
526 local_sym_index,484 object_id,
485 sym_index,
527 atom_size,486 atom_size,
528 sect.@"align",487 sect.@"align",
529 atom_code,488 atom_code,
...@@ -531,33 +490,27 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -531,33 +490,27 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
531 &.{},490 &.{},
532 match,491 match,
533 sect,492 sect,
534 macho_file,
535 );493 );
494 try macho_file.addAtomToSection(atom, match);
536 }495 }
537496
538 var next_nlist_count: usize = 0;497 var next_sym_count: usize = 0;
539 while (next_nlist_count < filtered_nlists.len) {498 while (next_sym_count < filtered_syms.len) {
540 const next_nlist = filtered_nlists[next_nlist_count];499 const next_sym = filtered_syms[next_sym_count].getSymbol(context);
541 const addr = next_nlist.nlist.n_value;500 const addr = next_sym.n_value;
542 const atom_nlists = NlistWithIndex.filterByAddress(501 const atom_syms = filterSymbolsByAddress(
543 filtered_nlists[next_nlist_count..],502 filtered_syms[next_sym_count..],
544 addr,503 addr,
545 addr + 1,504 addr + 1,
505 context,
546 );506 );
547 next_nlist_count += atom_nlists.len;507 next_sym_count += atom_syms.len;
548
549 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
550 try macho_file.locals.append(allocator, .{
551 .n_strx = 0,
552 .n_type = macho.N_SECT,
553 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
554 .n_desc = 0,
555 .n_value = addr,
556 });
557508
509 assert(atom_syms.len > 0);
510 const sym_index = atom_syms[0].index;
558 const atom_size = blk: {511 const atom_size = blk: {
559 const end_addr = if (next_nlist_count < filtered_nlists.len)512 const end_addr = if (next_sym_count < filtered_syms.len)
560 filtered_nlists[next_nlist_count].nlist.n_value513 filtered_syms[next_sym_count].getSymbol(context).n_value
561 else514 else
562 sect.addr + sect.size;515 sect.addr + sect.size;
563 break :blk end_addr - addr;516 break :blk end_addr - addr;
...@@ -570,86 +523,91 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !...@@ -570,86 +523,91 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
570 math.min(@ctz(u64, addr), sect.@"align")523 math.min(@ctz(u64, addr), sect.@"align")
571 else524 else
572 sect.@"align";525 sect.@"align";
573 try self.parseIntoAtom(526 const atom = try self.createAtomFromSubsection(
574 allocator,527 macho_file,
575 local_sym_index,528 object_id,
529 sym_index,
576 atom_size,530 atom_size,
577 atom_align,531 atom_align,
578 atom_code,532 atom_code,
579 relocs,533 relocs,
580 atom_nlists,534 atom_syms[1..],
581 match,535 match,
582 sect,536 sect,
583 macho_file,
584 );537 );
538 try macho_file.addAtomToSection(atom, match);
585 }539 }
586 } else {540 } else {
587 // If there is no symbol to refer to this atom, we create541 // If there is no symbol to refer to this atom, we create
588 // a temp one, unless we already did that when working out the relocations542 // a temp one, unless we already did that when working out the relocations
589 // of other atoms.543 // of other atoms.
590 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {544 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
591 const local_sym_index = @intCast(u32, macho_file.locals.items.len);545 const sym_index = @intCast(u32, self.symtab.items.len);
592 try macho_file.locals.append(allocator, .{546 try self.symtab.append(gpa, .{
593 .n_strx = 0,547 .n_strx = 0,
594 .n_type = macho.N_SECT,548 .n_type = macho.N_SECT,
595 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),549 .n_sect = macho_file.getSectionOrdinal(match),
596 .n_desc = 0,550 .n_desc = 0,
597 .n_value = sect.addr,551 .n_value = sect.addr,
598 });552 });
599 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);553 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
600 break :blk local_sym_index;554 break :blk sym_index;
601 };555 };
602 try self.parseIntoAtom(556 const atom = try self.createAtomFromSubsection(
603 allocator,557 macho_file,
604 local_sym_index,558 object_id,
559 sym_index,
605 sect.size,560 sect.size,
606 sect.@"align",561 sect.@"align",
607 code,562 code,
608 relocs,563 relocs,
609 filtered_nlists,564 filtered_syms,
610 match,565 match,
611 sect,566 sect,
612 macho_file,
613 );567 );
568 try macho_file.addAtomToSection(atom, match);
614 }569 }
615 }570 }
616}571}
617572
618fn parseIntoAtom(573fn createAtomFromSubsection(
619 self: *Object,574 self: *Object,
620 allocator: Allocator,575 macho_file: *MachO,
621 local_sym_index: u32,576 object_id: u32,
577 sym_index: u32,
622 size: u64,578 size: u64,
623 alignment: u32,579 alignment: u32,
624 code: ?[]const u8,580 code: ?[]const u8,
625 relocs: []const macho.relocation_info,581 relocs: []const macho.relocation_info,
626 nlists: []const NlistWithIndex,582 indexes: []const SymbolAtIndex,
627 match: MatchingSection,583 match: MatchingSection,
628 sect: macho.section_64,584 sect: macho.section_64,
629 macho_file: *MachO,585) !*Atom {
630) !void {586 const gpa = macho_file.base.allocator;
631 const sym = macho_file.locals.items[local_sym_index];587 const sym = &self.symtab.items[sym_index];
632 const align_pow_2 = try math.powi(u32, 2, alignment);588 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
633 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);589 atom.file = object_id;
634 const atom = try macho_file.createEmptyAtom(local_sym_index, aligned_size, alignment);590 sym.n_sect = macho_file.getSectionOrdinal(match);
591
592 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
593 try self.managed_atoms.append(gpa, atom);
635594
636 if (code) |cc| {595 if (code) |cc| {
596 assert(size == cc.len);
637 mem.copy(u8, atom.code.items, cc);597 mem.copy(u8, atom.code.items, cc);
638 }598 }
639599
640 const base_offset = sym.n_value - sect.addr;600 const base_offset = sym.n_value - sect.addr;
641 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);601 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);
642 try atom.parseRelocs(filtered_relocs, .{602 try atom.parseRelocs(filtered_relocs, .{
603 .macho_file = macho_file,
643 .base_addr = sect.addr,604 .base_addr = sect.addr,
644 .base_offset = @intCast(i32, base_offset),605 .base_offset = @intCast(i32, base_offset),
645 .allocator = allocator,
646 .object = self,
647 .macho_file = macho_file,
648 });606 });
649607
650 if (macho_file.has_dices) {608 if (macho_file.has_dices) {
651 const dices = filterDice(self.data_in_code_entries, sym.n_value, sym.n_value + size);609 const dices = filterDice(self.data_in_code_entries, sym.n_value, sym.n_value + size);
652 try atom.dices.ensureTotalCapacity(allocator, dices.len);610 try atom.dices.ensureTotalCapacity(gpa, dices.len);
653611
654 for (dices) |dice| {612 for (dices) |dice| {
655 atom.dices.appendAssumeCapacity(.{613 atom.dices.appendAssumeCapacity(.{
...@@ -665,19 +623,41 @@ fn parseIntoAtom(...@@ -665,19 +623,41 @@ fn parseIntoAtom(
665 // the filtered symbols and note which symbol is contained within so that623 // the filtered symbols and note which symbol is contained within so that
666 // we can properly allocate addresses down the line.624 // we can properly allocate addresses down the line.
667 // While we're at it, we need to update segment,section mapping of each symbol too.625 // While we're at it, we need to update segment,section mapping of each symbol too.
668 try atom.contained.ensureTotalCapacity(allocator, nlists.len);626 try atom.contained.ensureTotalCapacity(gpa, indexes.len + 1);
627
628 {
629 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
630 // TODO there has to be a better to handle this.
631 for (di.inner.func_list.items) |func| {
632 if (func.pc_range) |range| {
633 if (sym.n_value >= range.start and sym.n_value < range.end) {
634 break :blk Atom.Stab{
635 .function = range.end - range.start,
636 };
637 }
638 }
639 }
640 // TODO
641 // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global;
642 break :blk .static;
643 } else null;
669644
670 for (nlists) |nlist_with_index| {645 atom.contained.appendAssumeCapacity(.{
671 const nlist = nlist_with_index.nlist;646 .sym_index = sym_index,
672 const sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;647 .offset = 0,
673 const this_sym = &macho_file.locals.items[sym_index];648 .stab = stab,
674 this_sym.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);649 });
650 }
651
652 for (indexes) |inner_sym_index| {
653 const inner_sym = &self.symtab.items[inner_sym_index.index];
654 inner_sym.n_sect = macho_file.getSectionOrdinal(match);
675655
676 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {656 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
677 // TODO there has to be a better to handle this.657 // TODO there has to be a better to handle this.
678 for (di.inner.func_list.items) |func| {658 for (di.inner.func_list.items) |func| {
679 if (func.pc_range) |range| {659 if (func.pc_range) |range| {
680 if (nlist.n_value >= range.start and nlist.n_value < range.end) {660 if (inner_sym.n_value >= range.start and inner_sym.n_value < range.end) {
681 break :blk Atom.Stab{661 break :blk Atom.Stab{
682 .function = range.end - range.start,662 .function = range.end - range.start,
683 };663 };
...@@ -690,12 +670,12 @@ fn parseIntoAtom(...@@ -690,12 +670,12 @@ fn parseIntoAtom(
690 } else null;670 } else null;
691671
692 atom.contained.appendAssumeCapacity(.{672 atom.contained.appendAssumeCapacity(.{
693 .local_sym_index = sym_index,673 .sym_index = inner_sym_index.index,
694 .offset = nlist.n_value - sym.n_value,674 .offset = inner_sym.n_value - sym.n_value,
695 .stab = stab,675 .stab = stab,
696 });676 });
697677
698 try macho_file.atom_by_index_table.putNoClobber(allocator, sym_index, atom);678 try self.atom_by_index_table.putNoClobber(gpa, inner_sym_index.index, atom);
699 }679 }
700680
701 const is_gc_root = blk: {681 const is_gc_root = blk: {
...@@ -714,30 +694,28 @@ fn parseIntoAtom(...@@ -714,30 +694,28 @@ fn parseIntoAtom(
714 }694 }
715 };695 };
716 if (is_gc_root) {696 if (is_gc_root) {
717 try macho_file.gc_roots.putNoClobber(allocator, atom, {});697 try macho_file.gc_roots.putNoClobber(gpa, atom, {});
718 }698 }
719699
720 if (!self.start_atoms.contains(match)) {700 return atom;
721 try self.start_atoms.putNoClobber(allocator, match, atom);
722 }
723
724 if (self.end_atoms.getPtr(match)) |last| {
725 last.*.next = atom;
726 atom.prev = last.*;
727 last.* = atom;
728 } else {
729 try self.end_atoms.putNoClobber(allocator, match, atom);
730 }
731 try self.contained_atoms.append(allocator, atom);
732}701}
733702
734fn parseSymtab(self: *Object) void {703fn parseSymtab(self: *Object, allocator: Allocator) !void {
735 const index = self.symtab_cmd_index orelse return;704 const index = self.symtab_cmd_index orelse return;
736 const symtab = self.load_commands.items[index].symtab;705 const symtab = self.load_commands.items[index].symtab;
706 try self.symtab.appendSlice(allocator, self.getSourceSymtab());
707 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
708}
709
710fn getSourceSymtab(self: *Object) []const macho.nlist_64 {
711 const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
712 const symtab = self.load_commands.items[index].symtab;
737 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;713 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
738 const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];714 const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
739 self.symtab = mem.bytesAsSlice(macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), raw_symtab));715 return mem.bytesAsSlice(
740 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];716 macho.nlist_64,
717 @alignCast(@alignOf(macho.nlist_64), raw_symtab),
718 );
741}719}
742720
743fn parseDebugInfo(self: *Object, allocator: Allocator) !void {721fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
...@@ -783,8 +761,7 @@ fn parseDataInCode(self: *Object) void {...@@ -783,8 +761,7 @@ fn parseDataInCode(self: *Object) void {
783}761}
784762
785fn getSectionContents(self: Object, sect_id: u16) []const u8 {763fn getSectionContents(self: Object, sect_id: u16) []const u8 {
786 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;764 const sect = self.getSection(sect_id);
787 const sect = seg.sections.items[sect_id];
788 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{765 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
789 sect.segName(),766 sect.segName(),
790 sect.sectName(),767 sect.sectName(),
...@@ -798,3 +775,9 @@ pub fn getString(self: Object, off: u32) []const u8 {...@@ -798,3 +775,9 @@ pub fn getString(self: Object, off: u32) []const u8 {
798 assert(off < self.strtab.len);775 assert(off < self.strtab.len);
799 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);776 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
800}777}
778
779pub fn getSection(self: Object, n_sect: u16) macho.section_64 {
780 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
781 assert(n_sect < seg.sections.items.len);
782 return seg.sections.items[n_sect];
783}
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}