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.
31743174 const func = func_payload.data;
31753175 const fn_owner_decl = mod.declPtr(func.owner_decl);
31763176 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,
31783178 });
31793179 // blr x30
31803180 _ = try self.addInst(.{
......@@ -3190,14 +3190,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
31903190 lib_name,
31913191 });
31923192 }
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
31953195 _ = try self.addInst(.{
31963196 .tag = .call_extern,
31973197 .data = .{
31983198 .extern_fn = .{
3199 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
3200 .sym_name = n_strx,
3199 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
3200 .global_index = global_index,
32013201 },
32023202 },
32033203 });
......@@ -4157,7 +4157,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
41574157 .data = .{
41584158 .payload = try self.addExtra(Mir.LoadMemoryPie{
41594159 .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,
41614161 .sym_index = sym_index,
41624162 }),
41634163 },
......@@ -4270,7 +4270,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
42704270 .data = .{
42714271 .payload = try self.addExtra(Mir.LoadMemoryPie{
42724272 .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,
42744274 .sym_index = sym_index,
42754275 }),
42764276 },
......@@ -4578,8 +4578,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
45784578 } else if (self.bin_file.cast(link.File.MachO)) |_| {
45794579 // Because MachO is PIE-always-on, we defer memory address resolution until
45804580 // the linker has enough info to perform relocations.
4581 assert(decl.link.macho.local_sym_index != 0);
4582 return MCValue{ .got_load = decl.link.macho.local_sym_index };
4581 assert(decl.link.macho.sym_index != 0);
4582 return MCValue{ .got_load = decl.link.macho.sym_index };
45834583 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
45844584 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
45854585 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 {
660660 };
661661 // Add relocation to the decl.
662662 const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?;
663 const target = macho_file.globals.values()[extern_fn.global_index];
663664 try atom.relocs.append(emit.bin_file.allocator, .{
664665 .offset = offset,
665 .target = .{ .global = extern_fn.sym_name },
666 .target = target,
666667 .addend = 0,
667668 .subtractor = null,
668669 .pcrel = true,
......@@ -864,7 +865,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
864865 // Page reloc for adrp instruction.
865866 try atom.relocs.append(emit.bin_file.allocator, .{
866867 .offset = offset,
867 .target = .{ .local = data.sym_index },
868 .target = .{ .sym_index = data.sym_index, .file = null },
868869 .addend = 0,
869870 .subtractor = null,
870871 .pcrel = true,
......@@ -882,7 +883,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
882883 // Pageoff reloc for adrp instruction.
883884 try atom.relocs.append(emit.bin_file.allocator, .{
884885 .offset = offset + 4,
885 .target = .{ .local = data.sym_index },
886 .target = .{ .sym_index = data.sym_index, .file = null },
886887 .addend = 0,
887888 .subtractor = null,
888889 .pcrel = false,
src/arch/aarch64/Mir.zig+1-1
......@@ -232,7 +232,7 @@ pub const Inst = struct {
232232 /// Index of the containing atom.
233233 atom_index: u32,
234234 /// Index into the linker's string table.
235 sym_name: u32,
235 global_index: u32,
236236 },
237237 /// A 16-bit immediate value.
238238 ///
src/arch/riscv64/CodeGen.zig+1-1
......@@ -2563,7 +2563,7 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25632563 } else if (self.bin_file.cast(link.File.MachO)) |_| {
25642564 // TODO I'm hacking my way through here by repurposing .memory for storing
25652565 // 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 };
25672567 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
25682568 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
25692569 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
26452645 }),
26462646 .data = .{
26472647 .load_reloc = .{
2648 .atom_index = fn_owner_decl.link.macho.local_sym_index,
2648 .atom_index = fn_owner_decl.link.macho.sym_index,
26492649 .sym_index = sym_index,
26502650 },
26512651 },
......@@ -3977,7 +3977,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39773977 const func = func_payload.data;
39783978 const fn_owner_decl = mod.declPtr(func.owner_decl);
39793979 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,
39813981 });
39823982 // callq *%rax
39833983 _ = try self.addInst(.{
......@@ -3997,14 +3997,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
39973997 lib_name,
39983998 });
39993999 }
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));
40014001 _ = try self.addInst(.{
40024002 .tag = .call_extern,
40034003 .ops = undefined,
40044004 .data = .{
40054005 .extern_fn = .{
4006 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index,
4007 .sym_name = n_strx,
4006 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4007 .global_index = global_index,
40084008 },
40094009 },
40104010 });
......@@ -6771,8 +6771,8 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
67716771 } else if (self.bin_file.cast(link.File.MachO)) |_| {
67726772 // Because MachO is PIE-always-on, we defer memory address resolution until
67736773 // the linker has enough info to perform relocations.
6774 assert(decl.link.macho.local_sym_index != 0);
6775 return MCValue{ .got_load = decl.link.macho.local_sym_index };
6774 assert(decl.link.macho.sym_index != 0);
6775 return MCValue{ .got_load = decl.link.macho.sym_index };
67766776 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
67776777 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
67786778 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 {
10051005 log.debug("adding reloc of type {} to local @{d}", .{ reloc_type, load_reloc.sym_index });
10061006 try atom.relocs.append(emit.bin_file.allocator, .{
10071007 .offset = @intCast(u32, end_offset - 4),
1008 .target = .{ .local = load_reloc.sym_index },
1008 .target = .{ .sym_index = load_reloc.sym_index, .file = null },
10091009 .addend = 0,
10101010 .subtractor = null,
10111011 .pcrel = true,
......@@ -1127,9 +1127,10 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11271127 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
11281128 // Add relocation to the decl.
11291129 const atom = macho_file.atom_by_index_table.get(extern_fn.atom_index).?;
1130 const target = macho_file.globals.values()[extern_fn.global_index];
11301131 try atom.relocs.append(emit.bin_file.allocator, .{
11311132 .offset = offset,
1132 .target = .{ .global = extern_fn.sym_name },
1133 .target = target,
11331134 .addend = 0,
11341135 .subtractor = null,
11351136 .pcrel = true,
src/arch/x86_64/Mir.zig+2-2
......@@ -443,8 +443,8 @@ pub const Inst = struct {
443443 extern_fn: struct {
444444 /// Index of the containing atom.
445445 atom_index: u32,
446 /// Index into the linker's string table.
447 sym_name: u32,
446 /// Index into the linker's globals table.
447 global_index: u32,
448448 },
449449 /// PIE load relocation.
450450 load_reloc: struct {
src/link.zig+1-6
......@@ -544,12 +544,7 @@ pub const File = struct {
544544 switch (base.tag) {
545545 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),
546546 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index),
547 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index) catch |err| switch (err) {
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 },
547 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index),
553548 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
554549 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
555550 .c, .spirv, .nvptx => {},
src/link/MachO.zig+1419-1749
......@@ -35,8 +35,7 @@ const LibStub = @import("tapi.zig").LibStub;
3535const Liveness = @import("../Liveness.zig");
3636const LlvmObject = @import("../codegen/llvm.zig").Object;
3737const Module = @import("../Module.zig");
38const StringIndexAdapter = std.hash_map.StringIndexAdapter;
39const StringIndexContext = std.hash_map.StringIndexContext;
38const StringTable = @import("strtab.zig").StringTable;
4039const Trie = @import("MachO/Trie.zig");
4140const Type = @import("../type.zig").Type;
4241const TypedValue = @import("../TypedValue.zig");
......@@ -52,13 +51,13 @@ pub const SearchStrategy = enum {
5251 dylibs_first,
5352};
5453
54pub const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
55
5556const SystemLib = struct {
5657 needed: bool = false,
5758 weak: bool = false,
5859};
5960
60const N_DESC_GCED: u16 = @bitCast(u16, @as(i16, -1));
61
6261base: File,
6362
6463/// 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,
153152rustc_section_size: u64 = 0,
154153
155154locals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
156globals: std.ArrayListUnmanaged(macho.nlist_64) = .{},
157undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{},
158symbol_resolver: std.AutoHashMapUnmanaged(u32, SymbolWithLoc) = .{},
159unresolved: std.AutoArrayHashMapUnmanaged(u32, enum {
160 none,
161 stub,
162 got,
163}) = .{},
164tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
155globals: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{},
156unresolved: std.AutoArrayHashMapUnmanaged(u32, void) = .{},
165157
166158locals_free_list: std.ArrayListUnmanaged(u32) = .{},
167globals_free_list: std.ArrayListUnmanaged(u32) = .{},
168159
169160dyld_stub_binder_index: ?u32 = null,
170161dyld_private_atom: ?*Atom = null,
171162stub_helper_preamble_atom: ?*Atom = null,
172163
173mh_execute_header_sym_index: ?u32 = null,
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) = .{},
164strtab: StringTable(.link) = .{},
178165
179166tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
180167tlv_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
183170got_entries: std.ArrayListUnmanaged(Entry) = .{},
184171got_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) = .{},
188175stubs_free_list: std.ArrayListUnmanaged(u32) = .{},
189stubs_table: std.AutoArrayHashMapUnmanaged(u32, u32) = .{},
176stubs_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},
190177
191178error_flags: File.ErrorFlags = File.ErrorFlags{},
192179
......@@ -194,12 +181,6 @@ load_commands_dirty: bool = false,
194181sections_order_dirty: bool = false,
195182has_dices: bool = false,
196183has_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
204185section_ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{},
205186
......@@ -223,12 +204,10 @@ atom_free_lists: std.AutoHashMapUnmanaged(MatchingSection, std.ArrayListUnmanage
223204/// Pointer to the last allocated atom
224205atoms: std.AutoHashMapUnmanaged(MatchingSection, *Atom) = .{},
225206
226/// List of atoms that are owned directly by the linker.
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.
207/// List of atoms that are either synthetic or map directly to the Zig source program.
231208managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
209
210/// Table of atoms indexed by the symbol index.
232211atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
233212
234213/// Table of unnamed constants associated with a parent `Decl`.
......@@ -259,9 +238,10 @@ unnamed_const_atoms: UnnamedConstTable = .{},
259238decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
260239
261240gc_roots: std.AutoHashMapUnmanaged(*Atom, void) = .{},
241gc_sections: std.AutoHashMapUnmanaged(MatchingSection, void) = .{},
262242
263243const Entry = struct {
264 target: Atom.Relocation.Target,
244 target: SymbolWithLoc,
265245 atom: *Atom,
266246};
267247
......@@ -273,15 +253,12 @@ const PendingUpdate = union(enum) {
273253 add_got_entry: u32,
274254};
275255
276const SymbolWithLoc = struct {
277 // Table where the symbol can be found.
278 where: enum {
279 global,
280 undef,
281 },
282 where_index: u32,
283 local_sym_index: u32 = 0,
284 file: ?u16 = null, // null means Zig module
256pub const SymbolWithLoc = struct {
257 // Index into the respective symbol table.
258 sym_index: u32,
259
260 // null means it's a synthetic global.
261 file: ?u32 = null,
285262};
286263
287264/// When allocating, the ideal_capacity is calculated by
......@@ -389,7 +366,7 @@ pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
389366 .n_desc = 0,
390367 .n_value = 0,
391368 });
392 try self.strtab.append(allocator, 0);
369 try self.strtab.buffer.append(allocator, 0);
393370
394371 try self.populateMissingMetadata();
395372
......@@ -524,7 +501,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
524501 const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
525502 const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
526503 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
529505 const id_symlink_basename = "zld.id";
530506 const cache_dir_handle = blk: {
......@@ -541,7 +517,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
541517 defer if (!self.base.options.disable_lld_caching) man.deinit();
542518
543519 var digest: [Cache.hex_digest_len]u8 = undefined;
544 var needs_full_relink = true;
545520
546521 cache: {
547522 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
610585 return;
611586 } else {
612587 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 }
621588 }
622589 }
623590 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
672639 .n_desc = 0,
673640 .n_value = 0,
674641 });
675 try self.strtab.append(self.base.allocator, 0);
642 try self.strtab.buffer.append(self.base.allocator, 0);
676643 try self.populateMissingMetadata();
677644 }
678645
679646 var lib_not_found = false;
680647 var framework_not_found = false;
681648
682 if (needs_full_relink) {
683 for (self.objects.items) |*object| {
684 object.free(self.base.allocator, self);
685 object.deinit(self.base.allocator);
686 }
687 self.objects.clearRetainingCapacity();
649 // Positional arguments to the linker such as object files and static archives.
650 var positionals = std.ArrayList([]const u8).init(arena);
651 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
688652
689 for (self.archives.items) |*archive| {
690 archive.deinit(self.base.allocator);
691 }
692 self.archives.clearRetainingCapacity();
653 var must_link_archives = std.StringArrayHashMap(void).init(arena);
654 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
693655
694 for (self.dylibs.items) |*dylib| {
695 dylib.deinit(self.base.allocator);
696 }
697 self.dylibs.clearRetainingCapacity();
698 self.dylibs_map.clearRetainingCapacity();
699 self.referenced_dylibs.clearRetainingCapacity();
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 }
656 for (self.base.options.objects) |obj| {
657 if (must_link_archives.contains(obj.path)) continue;
658 if (obj.must_link) {
659 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
660 } else {
661 _ = positionals.appendAssumeCapacity(obj.path);
742662 }
743 // Invalidate all relocs
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);
663 }
751664
752 var must_link_archives = std.StringArrayHashMap(void).init(arena);
753 try must_link_archives.ensureUnusedCapacity(self.base.options.objects.len);
665 for (comp.c_object_table.keys()) |key| {
666 try positionals.append(key.status.success.object_path);
667 }
754668
755 for (self.base.options.objects) |obj| {
756 if (must_link_archives.contains(obj.path)) continue;
757 if (obj.must_link) {
758 _ = must_link_archives.getOrPutAssumeCapacity(obj.path);
759 } else {
760 _ = positionals.appendAssumeCapacity(obj.path);
761 }
762 }
669 if (module_obj_path) |p| {
670 try positionals.append(p);
671 }
763672
764 for (comp.c_object_table.keys()) |key| {
765 try positionals.append(key.status.success.object_path);
766 }
673 if (comp.compiler_rt_lib) |lib| {
674 try positionals.append(lib.full_object_path);
675 }
767676
768 if (module_obj_path) |p| {
769 try positionals.append(p);
770 }
677 // libc++ dep
678 if (self.base.options.link_libcpp) {
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| {
773 try positionals.append(lib.full_object_path);
774 }
683 // Shared and static libraries passed via `-l` flag.
684 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
775685
776 // libc++ dep
777 if (self.base.options.link_libcpp) {
778 try positionals.append(comp.libcxxabi_static_lib.?.full_object_path);
779 try positionals.append(comp.libcxx_static_lib.?.full_object_path);
686 const system_lib_names = self.base.options.system_libs.keys();
687 for (system_lib_names) |system_lib_name| {
688 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
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;
780694 }
781695
782 // Shared and static libraries passed via `-l` flag.
783 var candidate_libs = std.StringArrayHashMap(SystemLib).init(arena);
784
785 const system_lib_names = self.base.options.system_libs.keys();
786 for (system_lib_names) |system_lib_name| {
787 // By this time, we depend on these libs being dynamically linked libraries and not static libraries
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 }
696 const system_lib_info = self.base.options.system_libs.get(system_lib_name).?;
697 try candidate_libs.put(system_lib_name, .{
698 .needed = system_lib_info.needed,
699 .weak = system_lib_info.weak,
700 });
701 }
801702
802 var lib_dirs = std.ArrayList([]const u8).init(arena);
803 for (self.base.options.lib_dirs) |dir| {
804 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
805 try lib_dirs.append(search_dir);
806 } else {
807 log.warn("directory not found for '-L{s}'", .{dir});
808 }
703 var lib_dirs = std.ArrayList([]const u8).init(arena);
704 for (self.base.options.lib_dirs) |dir| {
705 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
706 try lib_dirs.append(search_dir);
707 } else {
708 log.warn("directory not found for '-L{s}'", .{dir});
809709 }
710 }
810711
811 var libs = std.StringArrayHashMap(SystemLib).init(arena);
812
813 // Assume ld64 default -search_paths_first if no strategy specified.
814 const search_strategy = self.base.options.search_strategy orelse .paths_first;
815 outer: for (candidate_libs.keys()) |lib_name| {
816 switch (search_strategy) {
817 .paths_first => {
818 // Look in each directory for a dylib (stub first), and then for archive
819 for (lib_dirs.items) |dir| {
820 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
821 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
822 try libs.put(full_path, candidate_libs.get(lib_name).?);
823 continue :outer;
824 }
712 var libs = std.StringArrayHashMap(SystemLib).init(arena);
713
714 // Assume ld64 default -search_paths_first if no strategy specified.
715 const search_strategy = self.base.options.search_strategy orelse .paths_first;
716 outer: for (candidate_libs.keys()) |lib_name| {
717 switch (search_strategy) {
718 .paths_first => {
719 // Look in each directory for a dylib (stub first), and then for archive
720 for (lib_dirs.items) |dir| {
721 for (&[_][]const u8{ ".tbd", ".dylib", ".a" }) |ext| {
722 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
723 try libs.put(full_path, candidate_libs.get(lib_name).?);
724 continue :outer;
825725 }
826 } else {
827 log.warn("library not found for '-l{s}'", .{lib_name});
828 lib_not_found = true;
829726 }
830 },
831 .dylibs_first => {
832 // First, look for a dylib in each search dir
833 for (lib_dirs.items) |dir| {
834 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
835 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
836 try libs.put(full_path, candidate_libs.get(lib_name).?);
837 continue :outer;
838 }
839 }
840 } else for (lib_dirs.items) |dir| {
841 if (try resolveLib(arena, dir, lib_name, ".a")) |full_path| {
727 } else {
728 log.warn("library not found for '-l{s}'", .{lib_name});
729 lib_not_found = true;
730 }
731 },
732 .dylibs_first => {
733 // First, look for a dylib in each search dir
734 for (lib_dirs.items) |dir| {
735 for (&[_][]const u8{ ".tbd", ".dylib" }) |ext| {
736 if (try resolveLib(arena, dir, lib_name, ext)) |full_path| {
842737 try libs.put(full_path, candidate_libs.get(lib_name).?);
843 } else {
844 log.warn("library not found for '-l{s}'", .{lib_name});
845 lib_not_found = true;
738 continue :outer;
846739 }
847740 }
848 },
849 }
741 } else for (lib_dirs.items) |dir| {
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 },
850750 }
751 }
851752
852 if (lib_not_found) {
853 log.warn("Library search paths:", .{});
854 for (lib_dirs.items) |dir| {
855 log.warn(" {s}", .{dir});
856 }
753 if (lib_not_found) {
754 log.warn("Library search paths:", .{});
755 for (lib_dirs.items) |dir| {
756 log.warn(" {s}", .{dir});
857757 }
758 }
858759
859 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
860 var libsystem_available = false;
861 if (self.base.options.sysroot != null) blk: {
862 // Try stub file first. If we hit it, then we're done as the stub file
863 // re-exports every single symbol definition.
864 for (lib_dirs.items) |dir| {
865 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
866 try libs.put(full_path, .{ .needed = true });
760 // If we were given the sysroot, try to look there first for libSystem.B.{dylib, tbd}.
761 var libsystem_available = false;
762 if (self.base.options.sysroot != null) blk: {
763 // Try stub file first. If we hit it, then we're done as the stub file
764 // re-exports every single symbol definition.
765 for (lib_dirs.items) |dir| {
766 if (try resolveLib(arena, dir, "System", ".tbd")) |full_path| {
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 });
867779 libsystem_available = true;
868780 break :blk;
869781 }
870782 }
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 });
892783 }
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 // frameworks
895 var framework_dirs = std.ArrayList([]const u8).init(arena);
896 for (self.base.options.framework_dirs) |dir| {
897 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
898 try framework_dirs.append(search_dir);
899 } else {
900 log.warn("directory not found for '-F{s}'", .{dir});
901 }
795 // frameworks
796 var framework_dirs = std.ArrayList([]const u8).init(arena);
797 for (self.base.options.framework_dirs) |dir| {
798 if (try resolveSearchDir(arena, dir, self.base.options.sysroot)) |search_dir| {
799 try framework_dirs.append(search_dir);
800 } else {
801 log.warn("directory not found for '-F{s}'", .{dir});
902802 }
803 }
903804
904 outer: for (self.base.options.frameworks.keys()) |f_name| {
905 for (framework_dirs.items) |dir| {
906 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
907 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
908 const info = self.base.options.frameworks.get(f_name).?;
909 try libs.put(full_path, .{
910 .needed = info.needed,
911 .weak = info.weak,
912 });
913 continue :outer;
914 }
805 outer: for (self.base.options.frameworks.keys()) |f_name| {
806 for (framework_dirs.items) |dir| {
807 for (&[_][]const u8{ ".tbd", ".dylib", "" }) |ext| {
808 if (try resolveFramework(arena, dir, f_name, ext)) |full_path| {
809 const info = self.base.options.frameworks.get(f_name).?;
810 try libs.put(full_path, .{
811 .needed = info.needed,
812 .weak = info.weak,
813 });
814 continue :outer;
915815 }
916 } else {
917 log.warn("framework not found for '-framework {s}'", .{f_name});
918 framework_not_found = true;
919816 }
817 } else {
818 log.warn("framework not found for '-framework {s}'", .{f_name});
819 framework_not_found = true;
920820 }
821 }
921822
922 if (framework_not_found) {
923 log.warn("Framework search paths:", .{});
924 for (framework_dirs.items) |dir| {
925 log.warn(" {s}", .{dir});
926 }
823 if (framework_not_found) {
824 log.warn("Framework search paths:", .{});
825 for (framework_dirs.items) |dir| {
826 log.warn(" {s}", .{dir});
927827 }
828 }
928829
929 // rpaths
930 var rpath_table = std.StringArrayHashMap(void).init(arena);
931 for (self.base.options.rpath_list) |rpath| {
932 if (rpath_table.contains(rpath)) continue;
933 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
934 u64,
935 @sizeOf(macho.rpath_command) + rpath.len + 1,
936 @sizeOf(u64),
937 ));
938 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
939 .cmdsize = cmdsize,
940 .path = @sizeOf(macho.rpath_command),
941 });
942 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
943 mem.set(u8, rpath_cmd.data, 0);
944 mem.copy(u8, rpath_cmd.data, rpath);
945 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });
946 try rpath_table.putNoClobber(rpath, {});
947 self.load_commands_dirty = true;
948 }
830 // rpaths
831 var rpath_table = std.StringArrayHashMap(void).init(arena);
832 for (self.base.options.rpath_list) |rpath| {
833 if (rpath_table.contains(rpath)) continue;
834 const cmdsize = @intCast(u32, mem.alignForwardGeneric(
835 u64,
836 @sizeOf(macho.rpath_command) + rpath.len + 1,
837 @sizeOf(u64),
838 ));
839 var rpath_cmd = macho.emptyGenericCommandWithData(macho.rpath_command{
840 .cmdsize = cmdsize,
841 .path = @sizeOf(macho.rpath_command),
842 });
843 rpath_cmd.data = try self.base.allocator.alloc(u8, cmdsize - rpath_cmd.inner.path);
844 mem.set(u8, rpath_cmd.data, 0);
845 mem.copy(u8, rpath_cmd.data, rpath);
846 try self.load_commands.append(self.base.allocator, .{ .rpath = rpath_cmd });
847 try rpath_table.putNoClobber(rpath, {});
848 self.load_commands_dirty = true;
849 }
949850
950 // code signature and entitlements
951 if (self.base.options.entitlements) |path| {
952 if (self.code_signature) |*csig| {
953 try csig.addEntitlements(self.base.allocator, path);
954 csig.code_directory.ident = self.base.options.emit.?.sub_path;
955 } else {
956 var csig = CodeSignature.init(self.page_size);
957 try csig.addEntitlements(self.base.allocator, path);
958 csig.code_directory.ident = self.base.options.emit.?.sub_path;
959 self.code_signature = csig;
960 }
851 // code signature and entitlements
852 if (self.base.options.entitlements) |path| {
853 if (self.code_signature) |*csig| {
854 try csig.addEntitlements(self.base.allocator, path);
855 csig.code_directory.ident = self.base.options.emit.?.sub_path;
856 } else {
857 var csig = CodeSignature.init(self.page_size);
858 try csig.addEntitlements(self.base.allocator, path);
859 csig.code_directory.ident = self.base.options.emit.?.sub_path;
860 self.code_signature = csig;
961861 }
862 }
962863
963 if (self.base.options.verbose_link) {
964 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");
864 if (self.base.options.verbose_link) {
865 var argv = std.ArrayList([]const u8).init(arena);
975866
976 if (self.base.options.install_name) |install_name| {
977 try argv.append("-install_name");
978 try argv.append(install_name);
979 }
980 }
867 try argv.append("zig");
868 try argv.append("ld");
981869
982 if (self.base.options.sysroot) |syslibroot| {
983 try argv.append("-syslibroot");
984 try argv.append(syslibroot);
985 }
870 if (is_exe_or_dyn_lib) {
871 try argv.append("-dynamic");
872 }
986873
987 for (rpath_table.keys()) |rpath| {
988 try argv.append("-rpath");
989 try argv.append(rpath);
990 }
874 if (is_dyn_lib) {
875 try argv.append("-dylib");
991876
992 if (self.base.options.pagezero_size) |pagezero_size| {
993 try argv.append("-pagezero_size");
994 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
877 if (self.base.options.install_name) |install_name| {
878 try argv.append("-install_name");
879 try argv.append(install_name);
995880 }
881 }
996882
997 if (self.base.options.search_strategy) |strat| switch (strat) {
998 .paths_first => try argv.append("-search_paths_first"),
999 .dylibs_first => try argv.append("-search_dylibs_first"),
1000 };
883 if (self.base.options.sysroot) |syslibroot| {
884 try argv.append("-syslibroot");
885 try argv.append(syslibroot);
886 }
1001887
1002 if (self.base.options.headerpad_size) |headerpad_size| {
1003 try argv.append("-headerpad_size");
1004 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
1005 }
888 for (rpath_table.keys()) |rpath| {
889 try argv.append("-rpath");
890 try argv.append(rpath);
891 }
1006892
1007 if (self.base.options.headerpad_max_install_names) {
1008 try argv.append("-headerpad_max_install_names");
1009 }
893 if (self.base.options.pagezero_size) |pagezero_size| {
894 try argv.append("-pagezero_size");
895 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size}));
896 }
1010897
1011 if (self.base.options.gc_sections) |is_set| {
1012 if (is_set) {
1013 try argv.append("-dead_strip");
1014 }
1015 }
898 if (self.base.options.search_strategy) |strat| switch (strat) {
899 .paths_first => try argv.append("-search_paths_first"),
900 .dylibs_first => try argv.append("-search_dylibs_first"),
901 };
1016902
1017 if (self.base.options.dead_strip_dylibs) {
1018 try argv.append("-dead_strip_dylibs");
1019 }
903 if (self.base.options.headerpad_size) |headerpad_size| {
904 try argv.append("-headerpad_size");
905 try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size}));
906 }
1020907
1021 if (self.base.options.entry) |entry| {
1022 try argv.append("-e");
1023 try argv.append(entry);
1024 }
908 if (self.base.options.headerpad_max_install_names) {
909 try argv.append("-headerpad_max_install_names");
910 }
1025911
1026 for (self.base.options.objects) |obj| {
1027 try argv.append(obj.path);
912 if (self.base.options.gc_sections) |is_set| {
913 if (is_set) {
914 try argv.append("-dead_strip");
1028915 }
916 }
1029917
1030 for (comp.c_object_table.keys()) |key| {
1031 try argv.append(key.status.success.object_path);
1032 }
918 if (self.base.options.dead_strip_dylibs) {
919 try argv.append("-dead_strip_dylibs");
920 }
1033921
1034 if (module_obj_path) |p| {
1035 try argv.append(p);
1036 }
922 if (self.base.options.entry) |entry| {
923 try argv.append("-e");
924 try argv.append(entry);
925 }
1037926
1038 if (comp.compiler_rt_lib) |lib| {
1039 try argv.append(lib.full_object_path);
1040 }
927 for (self.base.options.objects) |obj| {
928 try argv.append(obj.path);
929 }
1041930
1042 if (self.base.options.link_libcpp) {
1043 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1044 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1045 }
931 for (comp.c_object_table.keys()) |key| {
932 try argv.append(key.status.success.object_path);
933 }
1046934
1047 try argv.append("-o");
1048 try argv.append(full_out_path);
935 if (module_obj_path) |p| {
936 try argv.append(p);
937 }
1049938
1050 try argv.append("-lSystem");
1051 try argv.append("-lc");
939 if (comp.compiler_rt_lib) |lib| {
940 try argv.append(lib.full_object_path);
941 }
1052942
1053 for (self.base.options.system_libs.keys()) |l_name| {
1054 const info = self.base.options.system_libs.get(l_name).?;
1055 const arg = if (info.needed)
1056 try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name})
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 }
943 if (self.base.options.link_libcpp) {
944 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
945 try argv.append(comp.libcxx_static_lib.?.full_object_path);
946 }
1063947
1064 for (self.base.options.lib_dirs) |lib_dir| {
1065 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
1066 }
948 try argv.append("-o");
949 try argv.append(full_out_path);
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| {
1069 const info = self.base.options.frameworks.get(framework).?;
1070 const arg = if (info.needed)
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 }
965 for (self.base.options.lib_dirs) |lib_dir| {
966 try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir}));
967 }
1078968
1079 for (self.base.options.framework_dirs) |framework_dir| {
1080 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
1081 }
969 for (self.base.options.frameworks.keys()) |framework| {
970 const info = self.base.options.frameworks.get(framework).?;
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) {
1084 try argv.append("-undefined");
1085 try argv.append("dynamic_lookup");
1086 }
980 for (self.base.options.framework_dirs) |framework_dir| {
981 try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir}));
982 }
1087983
1088 for (must_link_archives.keys()) |lib| {
1089 try argv.append(try std.fmt.allocPrint(arena, "-force_load {s}", .{lib}));
1090 }
984 if (is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false)) {
985 try argv.append("-undefined");
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}));
1093991 }
1094992
1095 var dependent_libs = std.fifo.LinearFifo(struct {
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);
993 Compilation.dump_argv(argv.items);
1104994 }
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
11061006 try self.createMhExecuteHeaderSymbol();
11071007 for (self.objects.items) |*object, object_id| {
1108 if (object.analyzed) continue;
1109 try self.resolveSymbolsInObject(@intCast(u16, object_id));
1008 try self.resolveSymbolsInObject(object, @intCast(u16, object_id));
11101009 }
11111010
11121011 try self.resolveSymbolsInArchives();
......@@ -1116,44 +1015,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11161015 try self.resolveSymbolsInDylibs();
11171016 try self.createDsoHandleSymbol();
11181017 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 }
11571020 if (self.unresolved.count() > 0) {
11581021 return error.UndefinedSymbolReference;
11591022 }
......@@ -1165,35 +1028,40 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
11651028 }
11661029
11671030 try self.createTentativeDefAtoms();
1168 try self.parseObjectsIntoAtoms();
11691031
11701032 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
11711033 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
11731038 try self.gcAtoms();
11741039 try self.pruneAndSortSections();
11751040 try self.allocateSegments();
1176 try self.allocateLocals();
1041 try self.allocateSymbols();
1042 } else {
1043 // TODO incremental mode: parsing objects into atoms
11771044 }
11781045
11791046 try self.allocateSpecialSymbols();
1180 try self.allocateGlobals();
11811047
1182 if (build_options.enable_logging or true) {
1048 if (build_options.enable_logging) {
11831049 self.logSymtab();
11841050 self.logSectionOrdinals();
11851051 self.logAtoms();
11861052 }
11871053
11881054 if (use_llvm or use_stage1) {
1189 try self.writeAllAtoms();
1055 try self.writeAtomsWhole();
11901056 } else {
1191 try self.writeAtoms();
1057 // try self.writeAtoms();
11921058 }
11931059
11941060 if (self.rustc_section_index) |id| {
1195 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].segment;
1196 const sect = &seg.sections.items[id];
1061 const sect = self.getSectionPtr(.{
1062 .seg = self.data_segment_cmd_index.?,
1063 .sect = id,
1064 });
11971065 sect.size = self.rustc_section_size;
11981066 }
11991067
......@@ -1234,10 +1102,10 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
12341102 try self.writeCodeSignature(csig); // code signing always comes last
12351103 }
12361104
1237 if (build_options.enable_link_snapshots) {
1238 if (self.base.options.enable_link_snapshots)
1239 try self.snapshotState();
1240 }
1105 // if (build_options.enable_link_snapshots) {
1106 // if (self.base.options.enable_link_snapshots)
1107 // try self.snapshotState();
1108 // }
12411109 }
12421110
12431111 cache: {
......@@ -1256,8 +1124,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
12561124 // other processes clobbering it.
12571125 self.base.lock = man.toOwnedLock();
12581126 }
1259
1260 self.cold_start = false;
12611127}
12621128
12631129fn resolveSearchDir(
......@@ -1521,7 +1387,7 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
15211387 .syslibroot = syslibroot,
15221388 })) 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});
15251391 }
15261392}
15271393
......@@ -1536,7 +1402,7 @@ fn parseAndForceLoadStaticArchives(self: *MachO, files: []const []const u8) !voi
15361402 log.debug("parsing and force loading static archive '{s}'", .{full_path});
15371403
15381404 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});
15401406 }
15411407}
15421408
......@@ -1557,7 +1423,7 @@ fn parseLibs(
15571423 })) continue;
15581424 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});
15611427 }
15621428}
15631429
......@@ -1601,7 +1467,7 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
16011467 });
16021468 if (did_parse_successfully) break;
16031469 } 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});
16051471 }
16061472 }
16071473}
......@@ -2172,34 +2038,31 @@ pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSectio
21722038 return res;
21732039}
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 {
21762042 const size_usize = math.cast(usize, size) orelse return error.Overflow;
2177 const atom = try self.base.allocator.create(Atom);
2178 errdefer self.base.allocator.destroy(atom);
2043 const atom = try gpa.create(Atom);
2044 errdefer gpa.destroy(atom);
21792045 atom.* = Atom.empty;
2180 atom.local_sym_index = local_sym_index;
2046 atom.sym_index = sym_index;
21812047 atom.size = size;
21822048 atom.alignment = alignment;
21832049
2184 try atom.code.resize(self.base.allocator, size_usize);
2050 try atom.code.resize(gpa, size_usize);
21852051 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);
21892053 return atom;
21902054}
21912055
21922056pub fn writeAtom(self: *MachO, atom: *Atom, match: MatchingSection) !void {
2193 const seg = self.load_commands.items[match.seg].segment;
2194 const sect = seg.sections.items[match.sect];
2195 const sym = self.locals.items[atom.local_sym_index];
2057 const sect = self.getSection(match);
2058 const sym = atom.getSymbol(self);
21962059 const file_offset = sect.offset + sym.n_value - sect.addr;
21972060 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 });
21992062 try self.base.file.?.pwriteAll(atom.code.items, file_offset);
22002063}
22012064
2202fn allocateLocals(self: *MachO) !void {
2065fn allocateSymbols(self: *MachO) !void {
22032066 var it = self.atoms.iterator();
22042067 while (it.next()) |entry| {
22052068 const match = entry.key_ptr.*;
......@@ -2209,30 +2072,25 @@ fn allocateLocals(self: *MachO) !void {
22092072 atom = prev;
22102073 }
22112074
2212 const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2213 const seg = self.load_commands.items[match.seg].segment;
2214 const sect = seg.sections.items[match.sect];
2075 const n_sect = self.getSectionOrdinal(match);
2076 const sect = self.getSection(match);
22152077 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
22192081 while (true) {
22202082 const alignment = try math.powi(u32, 2, atom.alignment);
22212083 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);
22242086 sym.n_value = base_vaddr;
22252087 sym.n_sect = n_sect;
22262088
2227 log.debug(" {d}: {s} allocated at 0x{x}", .{
2228 atom.local_sym_index,
2229 self.getString(sym.n_strx),
2230 base_vaddr,
2231 });
2089 log.debug(" ATOM(%{d}, '{s}') @{x}", .{ atom.sym_index, atom.getName(self), base_vaddr });
22322090
22332091 // Update each symbol contained within the atom
22342092 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 });
22362094 contained_sym.n_value = base_vaddr + sym_at_off.offset;
22372095 contained_sym.n_sect = n_sect;
22382096 }
......@@ -2250,11 +2108,11 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
22502108 var atom = self.atoms.get(match) orelse return;
22512109
22522110 while (true) {
2253 const atom_sym = &self.locals.items[atom.local_sym_index];
2111 const atom_sym = &self.locals.items[atom.sym_index];
22542112 atom_sym.n_value = @intCast(u64, @intCast(i64, atom_sym.n_value) + offset);
22552113
22562114 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];
22582116 contained_sym.n_value = @intCast(u64, @intCast(i64, contained_sym.n_value) + offset);
22592117 }
22602118
......@@ -2265,53 +2123,30 @@ fn shiftLocalsByOffset(self: *MachO, match: MatchingSection, offset: i64) !void
22652123}
22662124
22672125fn allocateSpecialSymbols(self: *MachO) !void {
2268 for (&[_]?u32{
2269 self.mh_execute_header_sym_index,
2270 self.dso_handle_sym_index,
2271 }) |maybe_sym_index| {
2272 const sym_index = maybe_sym_index orelse continue;
2273 const sym = &self.locals.items[sym_index];
2126 for (&[_][]const u8{
2127 "___dso_handle",
2128 "__mh_execute_header",
2129 }) |name| {
2130 const global = self.globals.get(name) orelse continue;
2131 const sym = self.getSymbolPtr(global);
22742132 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(.{
22762134 .seg = self.text_segment_cmd_index.?,
22772135 .sect = 0,
2278 }).? + 1);
2136 });
22792137 sym.n_value = seg.inner.vmaddr;
22802138
22812139 log.debug("allocating {s} at the start of {s}", .{
2282 self.getString(sym.n_strx),
2140 name,
22832141 seg.inner.segName(),
22842142 });
22852143 }
22862144}
22872145
2288fn allocateGlobals(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 {
2146fn writeAtomsWhole(self: *MachO) !void {
23102147 var it = self.atoms.iterator();
23112148 while (it.next()) |entry| {
2312 const match = entry.key_ptr.*;
2313 const seg = self.load_commands.items[match.seg].segment;
2314 const sect = seg.sections.items[match.sect];
2149 const sect = self.getSection(entry.key_ptr.*);
23152150 var atom: *Atom = entry.value_ptr.*;
23162151
23172152 if (sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL) continue;
......@@ -2327,20 +2162,28 @@ fn writeAllAtoms(self: *MachO) !void {
23272162 }
23282163
23292164 while (true) {
2330 const atom_sym = self.locals.items[atom.local_sym_index];
2165 const this_sym = atom.getSymbol(self);
23312166 const padding_size: usize = if (atom.next) |next| blk: {
2332 const next_sym = self.locals.items[next.local_sym_index];
2333 const size = next_sym.n_value - (atom_sym.n_value + atom.size);
2167 const next_sym = next.getSymbol(self);
2168 const size = next_sym.n_value - (this_sym.n_value + atom.size);
23342169 break :blk math.cast(usize, size) orelse return error.Overflow;
23352170 } 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
23392181 try atom.resolveRelocs(self);
23402182 buffer.appendSliceAssumeCapacity(atom.code.items);
23412183
23422184 var i: usize = 0;
23432185 while (i < padding_size) : (i += 1) {
2186 // TODO with NOPs
23442187 buffer.appendAssumeCapacity(0);
23452188 }
23462189
......@@ -2388,8 +2231,7 @@ fn writeAtoms(self: *MachO) !void {
23882231 var it = self.atoms.iterator();
23892232 while (it.next()) |entry| {
23902233 const match = entry.key_ptr.*;
2391 const seg = self.load_commands.items[match.seg].segment;
2392 const sect = seg.sections.items[match.sect];
2234 const sect = self.getSection(match);
23932235 var atom: *Atom = entry.value_ptr.*;
23942236
23952237 // TODO handle zerofill in stage2
......@@ -2410,17 +2252,19 @@ fn writeAtoms(self: *MachO) !void {
24102252 }
24112253}
24122254
2413pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2414 const local_sym_index = @intCast(u32, self.locals.items.len);
2415 try self.locals.append(self.base.allocator, .{
2255pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2256 const gpa = self.base.allocator;
2257 const sym_index = @intCast(u32, self.locals.items.len);
2258 try self.locals.append(gpa, .{
24162259 .n_strx = 0,
24172260 .n_type = macho.N_SECT,
24182261 .n_sect = 0,
24192262 .n_desc = 0,
24202263 .n_value = 0,
24212264 });
2422 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2423 try atom.relocs.append(self.base.allocator, .{
2265
2266 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2267 try atom.relocs.append(gpa, .{
24242268 .offset = 0,
24252269 .target = target,
24262270 .addend = 0,
......@@ -2433,35 +2277,59 @@ pub fn createGotAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
24332277 else => unreachable,
24342278 },
24352279 });
2436 switch (target) {
2437 .local => {
2438 try atom.rebases.append(self.base.allocator, 0);
2439 },
2440 .global => |n_strx| {
2441 try atom.bindings.append(self.base.allocator, .{
2442 .n_strx = n_strx,
2443 .offset = 0,
2444 });
2445 },
2280
2281 const target_sym = self.getSymbol(target);
2282 if (target_sym.undf()) {
2283 const global_index = @intCast(u32, self.globals.getIndex(self.getSymbolName(target)).?);
2284 try atom.bindings.append(gpa, .{
2285 .global_index = global_index,
2286 .offset = 0,
2287 });
2288 } else {
2289 try atom.rebases.append(gpa, 0);
24462290 }
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
24472300 return atom;
24482301}
24492302
2450pub fn createTlvPtrAtom(self: *MachO, target: Atom.Relocation.Target) !*Atom {
2451 const local_sym_index = @intCast(u32, self.locals.items.len);
2452 try self.locals.append(self.base.allocator, .{
2303pub fn createTlvPtrAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
2304 const gpa = self.base.allocator;
2305 const sym_index = @intCast(u32, self.locals.items.len);
2306 try self.locals.append(gpa, .{
24532307 .n_strx = 0,
24542308 .n_type = macho.N_SECT,
24552309 .n_sect = 0,
24562310 .n_desc = 0,
24572311 .n_value = 0,
24582312 });
2459 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2460 assert(target == .global);
2461 try atom.bindings.append(self.base.allocator, .{
2462 .n_strx = target.global,
2313
2314 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2315 const target_sym = self.getSymbol(target);
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,
24632320 .offset = 0,
24642321 });
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
24652333 return atom;
24662334}
24672335
......@@ -2469,34 +2337,32 @@ fn createDyldPrivateAtom(self: *MachO) !void {
24692337 if (self.dyld_stub_binder_index == null) return;
24702338 if (self.dyld_private_atom != null) return;
24712339
2472 const local_sym_index = @intCast(u32, self.locals.items.len);
2473 const sym = try self.locals.addOne(self.base.allocator);
2474 sym.* = .{
2340 const gpa = self.base.allocator;
2341 const sym_index = @intCast(u32, self.locals.items.len);
2342 try self.locals.append(gpa, .{
24752343 .n_strx = 0,
24762344 .n_type = macho.N_SECT,
24772345 .n_sect = 0,
24782346 .n_desc = 0,
24792347 .n_value = 0,
2480 };
2481 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2348 });
2349 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
24822350 self.dyld_private_atom = atom;
2483 const match = MatchingSection{
2351
2352 try self.allocateAtomCommon(atom, .{
24842353 .seg = self.data_segment_cmd_index.?,
24852354 .sect = self.data_section_index.?,
2486 };
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);
2355 });
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);
24942359}
24952360
24962361fn createStubHelperPreambleAtom(self: *MachO) !void {
24972362 if (self.dyld_stub_binder_index == null) return;
24982363 if (self.stub_helper_preamble_atom != null) return;
24992364
2365 const gpa = self.base.allocator;
25002366 const arch = self.base.options.target.cpu.arch;
25012367 const size: u64 = switch (arch) {
25022368 .x86_64 => 15,
......@@ -2508,17 +2374,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25082374 .aarch64 => 2,
25092375 else => unreachable,
25102376 };
2511 const local_sym_index = @intCast(u32, self.locals.items.len);
2512 const sym = try self.locals.addOne(self.base.allocator);
2513 sym.* = .{
2377 const sym_index = @intCast(u32, self.locals.items.len);
2378 try self.locals.append(gpa, .{
25142379 .n_strx = 0,
25152380 .n_type = macho.N_SECT,
25162381 .n_sect = 0,
25172382 .n_desc = 0,
25182383 .n_value = 0,
2519 };
2520 const atom = try self.createEmptyAtom(local_sym_index, size, alignment);
2521 const dyld_private_sym_index = self.dyld_private_atom.?.local_sym_index;
2384 });
2385 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
2386 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;
25222387 switch (arch) {
25232388 .x86_64 => {
25242389 try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2);
......@@ -2528,7 +2393,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25282393 atom.code.items[2] = 0x1d;
25292394 atom.relocs.appendAssumeCapacity(.{
25302395 .offset = 3,
2531 .target = .{ .local = dyld_private_sym_index },
2396 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
25322397 .addend = 0,
25332398 .subtractor = null,
25342399 .pcrel = true,
......@@ -2543,7 +2408,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25432408 atom.code.items[10] = 0x25;
25442409 atom.relocs.appendAssumeCapacity(.{
25452410 .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 },
25472412 .addend = 0,
25482413 .subtractor = null,
25492414 .pcrel = true,
......@@ -2557,7 +2422,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25572422 mem.writeIntLittle(u32, atom.code.items[0..][0..4], aarch64.Instruction.adrp(.x17, 0).toU32());
25582423 atom.relocs.appendAssumeCapacity(.{
25592424 .offset = 0,
2560 .target = .{ .local = dyld_private_sym_index },
2425 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
25612426 .addend = 0,
25622427 .subtractor = null,
25632428 .pcrel = true,
......@@ -2568,7 +2433,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25682433 mem.writeIntLittle(u32, atom.code.items[4..][0..4], aarch64.Instruction.add(.x17, .x17, 0, false).toU32());
25692434 atom.relocs.appendAssumeCapacity(.{
25702435 .offset = 4,
2571 .target = .{ .local = dyld_private_sym_index },
2436 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
25722437 .addend = 0,
25732438 .subtractor = null,
25742439 .pcrel = false,
......@@ -2586,7 +2451,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
25862451 mem.writeIntLittle(u32, atom.code.items[12..][0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
25872452 atom.relocs.appendAssumeCapacity(.{
25882453 .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 },
25902455 .addend = 0,
25912456 .subtractor = null,
25922457 .pcrel = true,
......@@ -2601,7 +2466,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
26012466 ).toU32());
26022467 atom.relocs.appendAssumeCapacity(.{
26032468 .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 },
26052470 .addend = 0,
26062471 .subtractor = null,
26072472 .pcrel = false,
......@@ -2614,22 +2479,18 @@ fn createStubHelperPreambleAtom(self: *MachO) !void {
26142479 else => unreachable,
26152480 }
26162481 self.stub_helper_preamble_atom = atom;
2617 const match = MatchingSection{
2482
2483 try self.allocateAtomCommon(atom, .{
26182484 .seg = self.text_segment_cmd_index.?,
26192485 .sect = self.stub_helper_section_index.?,
2620 };
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);
2486 });
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);
26302490}
26312491
26322492pub fn createStubHelperAtom(self: *MachO) !*Atom {
2493 const gpa = self.base.allocator;
26332494 const arch = self.base.options.target.cpu.arch;
26342495 const stub_size: u4 = switch (arch) {
26352496 .x86_64 => 10,
......@@ -2641,16 +2502,16 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
26412502 .aarch64 => 2,
26422503 else => unreachable,
26432504 };
2644 const local_sym_index = @intCast(u32, self.locals.items.len);
2645 try self.locals.append(self.base.allocator, .{
2505 const sym_index = @intCast(u32, self.locals.items.len);
2506 try self.locals.append(gpa, .{
26462507 .n_strx = 0,
26472508 .n_type = macho.N_SECT,
26482509 .n_sect = 0,
26492510 .n_desc = 0,
26502511 .n_value = 0,
26512512 });
2652 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);
2653 try atom.relocs.ensureTotalCapacity(self.base.allocator, 1);
2513 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
2514 try atom.relocs.ensureTotalCapacity(gpa, 1);
26542515
26552516 switch (arch) {
26562517 .x86_64 => {
......@@ -2661,7 +2522,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
26612522 atom.code.items[5] = 0xe9;
26622523 atom.relocs.appendAssumeCapacity(.{
26632524 .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 },
26652526 .addend = 0,
26662527 .subtractor = null,
26672528 .pcrel = true,
......@@ -2683,7 +2544,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
26832544 mem.writeIntLittle(u32, atom.code.items[4..8], aarch64.Instruction.b(0).toU32());
26842545 atom.relocs.appendAssumeCapacity(.{
26852546 .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 },
26872548 .addend = 0,
26882549 .subtractor = null,
26892550 .pcrel = true,
......@@ -2695,22 +2556,32 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
26952556 else => unreachable,
26962557 }
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
26982567 return atom;
26992568}
27002569
2701pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*Atom {
2702 const local_sym_index = @intCast(u32, self.locals.items.len);
2703 try self.locals.append(self.base.allocator, .{
2570pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
2571 const gpa = 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, .{
27042575 .n_strx = 0,
27052576 .n_type = macho.N_SECT,
27062577 .n_sect = 0,
27072578 .n_desc = 0,
27082579 .n_value = 0,
27092580 });
2710 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3);
2711 try atom.relocs.append(self.base.allocator, .{
2581 const atom = try MachO.createEmptyAtom(gpa, sym_index, @sizeOf(u64), 3);
2582 try atom.relocs.append(gpa, .{
27122583 .offset = 0,
2713 .target = .{ .local = stub_sym_index },
2584 .target = .{ .sym_index = stub_sym_index, .file = null },
27142585 .addend = 0,
27152586 .subtractor = null,
27162587 .pcrel = false,
......@@ -2721,15 +2592,25 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, n_strx: u32) !*A
27212592 else => unreachable,
27222593 },
27232594 });
2724 try atom.rebases.append(self.base.allocator, 0);
2725 try atom.lazy_bindings.append(self.base.allocator, .{
2726 .n_strx = n_strx,
2595 try atom.rebases.append(gpa, 0);
2596 try atom.lazy_bindings.append(gpa, .{
2597 .global_index = global_index,
27272598 .offset = 0,
27282599 });
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
27292609 return atom;
27302610}
27312611
27322612pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
2613 const gpa = self.base.allocator;
27332614 const arch = self.base.options.target.cpu.arch;
27342615 const alignment: u2 = switch (arch) {
27352616 .x86_64 => 0,
......@@ -2741,23 +2622,23 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
27412622 .aarch64 => 3 * @sizeOf(u32),
27422623 else => unreachable, // unhandled architecture type
27432624 };
2744 const local_sym_index = @intCast(u32, self.locals.items.len);
2745 try self.locals.append(self.base.allocator, .{
2625 const sym_index = @intCast(u32, self.locals.items.len);
2626 try self.locals.append(gpa, .{
27462627 .n_strx = 0,
27472628 .n_type = macho.N_SECT,
27482629 .n_sect = 0,
27492630 .n_desc = 0,
27502631 .n_value = 0,
27512632 });
2752 const atom = try self.createEmptyAtom(local_sym_index, stub_size, alignment);
2633 const atom = try MachO.createEmptyAtom(gpa, sym_index, stub_size, alignment);
27532634 switch (arch) {
27542635 .x86_64 => {
27552636 // jmp
27562637 atom.code.items[0] = 0xff;
27572638 atom.code.items[1] = 0x25;
2758 try atom.relocs.append(self.base.allocator, .{
2639 try atom.relocs.append(gpa, .{
27592640 .offset = 2,
2760 .target = .{ .local = laptr_sym_index },
2641 .target = .{ .sym_index = laptr_sym_index, .file = null },
27612642 .addend = 0,
27622643 .subtractor = null,
27632644 .pcrel = true,
......@@ -2766,12 +2647,12 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
27662647 });
27672648 },
27682649 .aarch64 => {
2769 try atom.relocs.ensureTotalCapacity(self.base.allocator, 2);
2650 try atom.relocs.ensureTotalCapacity(gpa, 2);
27702651 // adrp x16, pages
27712652 mem.writeIntLittle(u32, atom.code.items[0..4], aarch64.Instruction.adrp(.x16, 0).toU32());
27722653 atom.relocs.appendAssumeCapacity(.{
27732654 .offset = 0,
2774 .target = .{ .local = laptr_sym_index },
2655 .target = .{ .sym_index = laptr_sym_index, .file = null },
27752656 .addend = 0,
27762657 .subtractor = null,
27772658 .pcrel = true,
......@@ -2786,7 +2667,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
27862667 ).toU32());
27872668 atom.relocs.appendAssumeCapacity(.{
27882669 .offset = 4,
2789 .target = .{ .local = laptr_sym_index },
2670 .target = .{ .sym_index = laptr_sym_index, .file = null },
27902671 .addend = 0,
27912672 .subtractor = null,
27922673 .pcrel = false,
......@@ -2798,101 +2679,121 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
27982679 },
27992680 else => unreachable,
28002681 }
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
28012691 return atom;
28022692}
28032693
28042694fn createTentativeDefAtoms(self: *MachO) !void {
2805 if (self.tentatives.count() == 0) return;
2806 // Convert any tentative definition into a regular symbol and allocate
2807 // text blocks for each tentative definition.
2808 while (self.tentatives.popOrNull()) |entry| {
2695 const gpa = self.base.allocator;
2696
2697 for (self.globals.values()) |global| {
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.
28092707 const match = MatchingSection{
28102708 .seg = self.data_segment_cmd_index.?,
28112709 .sect = self.bss_section_index.?,
28122710 };
2813 _ = try self.section_ordinals.getOrPut(self.base.allocator, 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;
2711 _ = try self.section_ordinals.getOrPut(gpa, match);
28182712
2819 global_sym.n_value = 0;
2820 global_sym.n_desc = 0;
2821 global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
2713 const size = sym.n_value;
2714 const alignment = (sym.n_desc >> 8) & 0x0f;
28222715
2823 const local_sym_index = @intCast(u32, self.locals.items.len);
2824 const local_sym = try self.locals.addOne(self.base.allocator);
2825 local_sym.* = .{
2826 .n_strx = global_sym.n_strx,
2827 .n_type = macho.N_SECT,
2828 .n_sect = global_sym.n_sect,
2716 sym.* = .{
2717 .n_strx = sym.n_strx,
2718 .n_type = macho.N_SECT | macho.N_EXT,
2719 .n_sect = 0,
28292720 .n_desc = 0,
28302721 .n_value = 0,
28312722 };
28322723
2833 const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable;
2834 resolv.local_sym_index = local_sym_index;
2724 const atom = try MachO.createEmptyAtom(gpa, global.sym_index, size, alignment);
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) {
2839 const alignment_pow_2 = try math.powi(u32, 2, alignment);
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}
2729 if (global.file) |file| {
2730 const object = &self.objects.items[file];
28462731
2847fn createDsoHandleSymbol(self: *MachO) !void {
2848 if (self.dso_handle_sym_index != null) return;
2732 try atom.contained.append(gpa, .{
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{
2851 .bytes = &self.strtab,
2852 }) orelse return;
2738 try object.managed_atoms.append(gpa, atom);
2739 try object.atom_by_index_table.putNoClobber(gpa, global.sym_index, atom);
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;
2855 if (resolv.where != .undef) return;
2747fn createMhExecuteHeaderSymbol(self: *MachO) !void {
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];
2858 const local_sym_index = @intCast(u32, self.locals.items.len);
2859 var nlist = macho.nlist_64{
2860 .n_strx = undef.n_strx,
2861 .n_type = macho.N_SECT,
2751 const gpa = self.base.allocator;
2752 const name = try gpa.dupe(u8, "__mh_execute_header");
2753 const n_strx = try self.strtab.insert(gpa, name);
2754 const sym_index = @intCast(u32, self.locals.items.len);
2755 try self.locals.append(gpa, .{
2756 .n_strx = n_strx,
2757 .n_type = macho.N_SECT | macho.N_EXT,
28622758 .n_sect = 0,
28632759 .n_desc = 0,
28642760 .n_value = 0,
2865 };
2866 try self.locals.append(self.base.allocator, nlist);
2867 const global_sym_index = @intCast(u32, self.globals.items.len);
2868 nlist.n_type |= macho.N_EXT;
2869 nlist.n_desc = macho.N_WEAK_DEF;
2870 try self.globals.append(self.base.allocator, nlist);
2871 self.dso_handle_sym_index = local_sym_index;
2872
2873 assert(self.unresolved.swapRemove(resolv.where_index));
2761 });
2762 try self.globals.putNoClobber(gpa, name, .{
2763 .sym_index = sym_index,
2764 .file = null,
2765 });
2766}
28742767
2875 undef.* = .{
2876 .n_strx = 0,
2877 .n_type = macho.N_UNDF,
2768fn createDsoHandleSymbol(self: *MachO) !void {
2769 const global = self.globals.getPtr("___dso_handle") orelse return;
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,
28782779 .n_sect = 0,
2879 .n_desc = 0,
2780 .n_desc = macho.N_WEAK_DEF,
28802781 .n_value = 0,
2782 });
2783 global.* = .{
2784 .sym_index = sym_index,
2785 .file = null,
28812786 };
2882 resolv.* = .{
2883 .where = .global,
2884 .where_index = global_sym_index,
2885 .local_sym_index = local_sym_index,
2886 };
2787 _ = self.unresolved.swapRemove(@intCast(u32, self.globals.getIndex("___dso_handle").?));
28872788}
28882789
2889fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
2890 const object = &self.objects.items[object_id];
2790fn resolveSymbolsInObject(self: *MachO, object: *Object, object_id: u16) !void {
2791 const gpa = self.base.allocator;
28912792
28922793 log.debug("resolving symbols in '{s}'", .{object.name});
28932794
2894 for (object.symtab) |sym, id| {
2895 const sym_id = @intCast(u32, id);
2795 for (object.symtab.items) |sym, index| {
2796 const sym_index = @intCast(u32, index);
28962797 const sym_name = object.getString(sym.n_strx);
28972798
28982799 if (sym.stab()) {
......@@ -2916,170 +2817,81 @@ fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void {
29162817 return error.UnhandledSymbolType;
29172818 }
29182819
2919 if (sym.sect()) {
2920 // Defined symbol regardless of scope lands in the locals symbol table.
2921 const local_sym_index = @intCast(u32, self.locals.items.len);
2922 try self.locals.append(self.base.allocator, .{
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,
2820 if (sym.sect() and !sym.ext()) {
2821 log.debug("symbol '{s}' local to object {s}; skipping...", .{
2822 sym_name,
2823 object.name,
29282824 });
2929 try object.symbol_mapping.putNoClobber(self.base.allocator, sym_id, local_sym_index);
2930 try object.reverse_symbol_mapping.putNoClobber(self.base.allocator, local_sym_index, sym_id);
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;
2825 continue;
2826 }
29752827
2976 continue;
2977 },
2978 .undef => {
2979 const undef = &self.undefs.items[resolv.where_index];
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 }
2828 const name = try gpa.dupe(u8, sym_name);
2829 const global_index = @intCast(u32, self.globals.values().len);
2830 const gop = try self.globals.getOrPut(gpa, name);
2831 defer if (gop.found_existing) gpa.free(name);
29902832
2991 const global_sym_index = @intCast(u32, self.globals.items.len);
2992 try self.globals.append(self.base.allocator, .{
2993 .n_strx = local.n_strx,
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,
2833 if (!gop.found_existing) {
2834 gop.value_ptr.* = .{
2835 .sym_index = sym_index,
30032836 .file = object_id,
30042837 };
3005 } else if (sym.tentative()) {
3006 // Symbol is a tentative definition.
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 },
2838 if (sym.undf() and !sym.tentative()) {
2839 try self.unresolved.putNoClobber(gpa, global_index, {});
30622840 }
3063 } else {
3064 // Symbol is undefined.
3065 const n_strx = try self.makeString(sym_name);
3066 if (self.symbol_resolver.contains(n_strx)) continue;
2841 continue;
2842 }
30672843
3068 const undef_sym_index = @intCast(u32, self.undefs.items.len);
3069 try self.undefs.append(self.base.allocator, .{
3070 .n_strx = try self.makeString(sym_name),
3071 .n_type = macho.N_UNDF,
3072 .n_sect = 0,
3073 .n_desc = sym.n_desc,
3074 .n_value = 0,
3075 });
3076 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3077 .where = .undef,
3078 .where_index = undef_sym_index,
3079 .file = object_id,
3080 });
3081 try self.unresolved.putNoClobber(self.base.allocator, undef_sym_index, .none);
2844 const global = gop.value_ptr.*;
2845 const global_sym = self.getSymbol(global);
2846
2847 // Cases to consider: sym vs global_sym
2848 // 1. strong(sym) and strong(global_sym) => error
2849 // 2. strong(sym) and weak(global_sym) => sym
2850 // 3. strong(sym) and tentative(global_sym) => sym
2851 // 4. strong(sym) and undf(global_sym) => sym
2852 // 5. weak(sym) and strong(global_sym) => global_sym
2853 // 6. weak(sym) and tentative(global_sym) => sym
2854 // 7. weak(sym) and undf(global_sym) => sym
2855 // 8. tentative(sym) and strong(global_sym) => global_sym
2856 // 9. tentative(sym) and weak(global_sym) => global_sym
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;
30822886 }
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 };
30832895 }
30842896}
30852897
......@@ -3088,8 +2900,8 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
30882900
30892901 var next_sym: usize = 0;
30902902 loop: while (next_sym < self.unresolved.count()) {
3091 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
3092 const sym_name = self.getString(sym.n_strx);
2903 const global = self.globals.values()[self.unresolved.keys()[next_sym]];
2904 const sym_name = self.getSymbolName(global);
30932905
30942906 for (self.archives.items) |archive| {
30952907 // Check if the entry exists in a static archive.
......@@ -3102,7 +2914,7 @@ fn resolveSymbolsInArchives(self: *MachO) !void {
31022914 const object_id = @intCast(u16, self.objects.items.len);
31032915 const object = try self.objects.addOne(self.base.allocator);
31042916 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
31072919 continue :loop;
31082920 }
......@@ -3116,8 +2928,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31162928
31172929 var next_sym: usize = 0;
31182930 loop: while (next_sym < self.unresolved.count()) {
3119 const sym = self.undefs.items[self.unresolved.keys()[next_sym]];
3120 const sym_name = self.getString(sym.n_strx);
2931 const global_index = self.unresolved.keys()[next_sym];
2932 const global = self.globals.values()[global_index];
2933 const sym = self.getSymbolPtr(global);
2934 const sym_name = self.getSymbolName(global);
31212935
31222936 for (self.dylibs.items) |dylib, id| {
31232937 if (!dylib.symbols.contains(sym_name)) continue;
......@@ -3129,69 +2943,14 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
31292943 }
31302944
31312945 const ordinal = self.referenced_dylibs.getIndex(dylib_id) orelse unreachable;
3132 const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable;
3133 const undef = &self.undefs.items[resolv.where_index];
3134 undef.n_type |= macho.N_EXT;
3135 undef.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
2946 sym.n_type |= macho.N_EXT;
2947 sym.n_desc = @intCast(u16, ordinal + 1) * macho.N_SYMBOL_RESOLVER;
31362948
31372949 if (dylib.weak) {
3138 undef.n_desc |= macho.N_WEAK_REF;
2950 sym.n_desc |= macho.N_WEAK_REF;
31392951 }
31402952
3141 if (self.unresolved.fetchSwapRemove(resolv.where_index)) |entry| outer_blk: {
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 }
2953 assert(self.unresolved.swapRemove(global_index));
31952954
31962955 continue :loop;
31972956 }
......@@ -3200,39 +2959,46 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
32002959 }
32012960}
32022961
3203fn createMhExecuteHeaderSymbol(self: *MachO) !void {
3204 if (self.base.options.output_mode != .Exe) return;
3205 if (self.mh_execute_header_sym_index != null) return;
2962fn resolveSymbolsAtLoading(self: *MachO) !void {
2963 const is_lib = self.base.options.output_mode == .Lib;
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");
3208 const local_sym_index = @intCast(u32, self.locals.items.len);
3209 var nlist = macho.nlist_64{
3210 .n_strx = n_strx,
3211 .n_type = macho.N_SECT,
3212 .n_sect = 0,
3213 .n_desc = 0,
3214 .n_value = 0,
3215 };
3216 try self.locals.append(self.base.allocator, nlist);
3217 self.mh_execute_header_sym_index = local_sym_index;
2967 var next_sym: usize = 0;
2968 while (next_sym < self.unresolved.count()) {
2969 const global_index = self.unresolved.keys()[next_sym];
2970 const global = self.globals.values()[global_index];
2971 const sym = self.getSymbolPtr(global);
2972 const sym_name = self.getSymbolName(global);
2973
2974 if (sym.discarded()) {
2975 sym.* = .{
2976 .n_strx = 0,
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| {
3220 const global = &self.globals.items[resolv.where_index];
3221 if (!(global.weakDef() or !global.pext())) {
3222 log.err("symbol '__mh_execute_header' defined multiple times", .{});
3223 return error.MultipleSymbolDefinitions;
2996 log.err("undefined reference to symbol '{s}'", .{sym_name});
2997 if (global.file) |file| {
2998 log.err(" first referenced in '{s}'", .{self.objects.items[file].name});
32242999 }
3225 resolv.local_sym_index = local_sym_index;
3226 } else {
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 });
3000
3001 next_sym += 1;
32363002 }
32373003}
32383004
......@@ -3240,21 +3006,20 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32403006 if (self.dyld_stub_binder_index != null) return;
32413007 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");
3244 const sym_index = @intCast(u32, self.undefs.items.len);
3245 try self.undefs.append(self.base.allocator, .{
3009 const gpa = self.base.allocator;
3010 const n_strx = try self.strtab.insert(gpa, "dyld_stub_binder");
3011 const sym_index = @intCast(u32, self.locals.items.len);
3012 try self.locals.append(gpa, .{
32463013 .n_strx = n_strx,
32473014 .n_type = macho.N_UNDF,
32483015 .n_sect = 0,
32493016 .n_desc = 0,
32503017 .n_value = 0,
32513018 });
3252 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3253 .where = .undef,
3254 .where_index = sym_index,
3255 });
3256 const sym = &self.undefs.items[sym_index];
3257 const sym_name = self.getString(n_strx);
3019 const sym_name = try gpa.dupe(u8, "dyld_stub_binder");
3020 const global = SymbolWithLoc{ .sym_index = sym_index, .file = null };
3021 try self.globals.putNoClobber(gpa, sym_name, global);
3022 const sym = &self.locals.items[sym_index];
32583023
32593024 for (self.dylibs.items) |dylib, id| {
32603025 if (!dylib.symbols.contains(sym_name)) continue;
......@@ -3275,197 +3040,13 @@ fn resolveDyldStubBinder(self: *MachO) !void {
32753040
32763041 if (self.dyld_stub_binder_index == null) {
32773042 log.err("undefined reference to symbol '{s}'", .{sym_name});
3278 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 }
3043 return error.UndefinedSymbolReference;
34683044 }
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;
34693050}
34703051
34713052fn addLoadDylibLC(self: *MachO, id: u16) !void {
......@@ -3503,15 +3084,11 @@ fn setEntryPoint(self: *MachO) !void {
35033084
35043085 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
35053086 const entry_name = self.base.options.entry orelse "_main";
3506 const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{
3507 .bytes = &self.strtab,
3508 }) orelse {
3087 const global = self.globals.get(entry_name) orelse {
35093088 log.err("entrypoint '{s}' not found", .{entry_name});
35103089 return error.MissingMainEntrypoint;
35113090 };
3512 const resolv = self.symbol_resolver.get(n_strx) orelse unreachable;
3513 assert(resolv.where == .global);
3514 const sym = self.globals.items[resolv.where_index];
3091 const sym = self.getSymbol(global);
35153092 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
35163093 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
35173094 ec.stacksize = self.base.options.stack_size_override orelse 0;
......@@ -3538,17 +3115,13 @@ pub fn deinit(self: *MachO) void {
35383115 self.stubs.deinit(self.base.allocator);
35393116 self.stubs_free_list.deinit(self.base.allocator);
35403117 self.stubs_table.deinit(self.base.allocator);
3541 self.strtab_dir.deinit(self.base.allocator);
35423118 self.strtab.deinit(self.base.allocator);
3543 self.undefs.deinit(self.base.allocator);
35443119 self.globals.deinit(self.base.allocator);
3545 self.globals_free_list.deinit(self.base.allocator);
35463120 self.locals.deinit(self.base.allocator);
35473121 self.locals_free_list.deinit(self.base.allocator);
3548 self.symbol_resolver.deinit(self.base.allocator);
35493122 self.unresolved.deinit(self.base.allocator);
3550 self.tentatives.deinit(self.base.allocator);
35513123 self.gc_roots.deinit(self.base.allocator);
3124 self.gc_sections.deinit(self.base.allocator);
35523125
35533126 for (self.objects.items) |*object| {
35543127 object.deinit(self.base.allocator);
......@@ -3662,7 +3235,7 @@ fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool)
36623235 if (atom.prev) |prev| {
36633236 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)) {
36663239 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
36673240 // the OOM here.
36683241 free_list.append(self.base.allocator, prev) catch {};
......@@ -3692,9 +3265,9 @@ fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64, match: MatchingSec
36923265}
36933266
36943267fn 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];
36963269 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);
36983271 if (!need_realloc) return sym.n_value;
36993272 return self.allocateAtom(atom, new_atom_size, alignment, match);
37003273}
......@@ -3725,7 +3298,7 @@ fn allocateLocalSymbol(self: *MachO) !u32 {
37253298 return index;
37263299}
37273300
3728pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3301pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
37293302 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);
37303303
37313304 const index = blk: {
......@@ -3740,16 +3313,13 @@ pub fn allocateGotEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
37403313 }
37413314 };
37423315
3743 self.got_entries.items[index] = .{
3744 .target = target,
3745 .atom = undefined,
3746 };
3316 self.got_entries.items[index] = .{ .target = target, .atom = undefined };
37473317 try self.got_entries_table.putNoClobber(self.base.allocator, target, index);
37483318
37493319 return index;
37503320}
37513321
3752pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {
3322pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
37533323 try self.stubs.ensureUnusedCapacity(self.base.allocator, 1);
37543324
37553325 const index = blk: {
......@@ -3764,13 +3334,13 @@ pub fn allocateStubEntry(self: *MachO, n_strx: u32) !u32 {
37643334 }
37653335 };
37663336
3767 self.stubs.items[index] = undefined;
3768 try self.stubs_table.putNoClobber(self.base.allocator, n_strx, index);
3337 self.stubs.items[index] = .{ .target = target, .atom = undefined };
3338 try self.stubs_table.putNoClobber(self.base.allocator, target, index);
37693339
37703340 return index;
37713341}
37723342
3773pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
3343pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {
37743344 try self.tlv_ptr_entries.ensureUnusedCapacity(self.base.allocator, 1);
37753345
37763346 const index = blk: {
......@@ -3794,16 +3364,14 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: Atom.Relocation.Target) !u32 {
37943364pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
37953365 if (self.llvm_object) |_| return;
37963366 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();
3800 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.local_sym_index, &decl.link.macho);
3369 decl.link.macho.sym_index = try self.allocateLocalSymbol();
3370 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
38013371 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
38023372
3803 const got_target = .{ .local = decl.link.macho.local_sym_index };
3804 const got_index = try self.allocateGotEntry(got_target);
3805 const got_atom = try self.createGotAtom(got_target);
3806 self.got_entries.items[got_index].atom = got_atom;
3373 const got_target = .{ .sym_index = decl.link.macho.sym_index, .file = null };
3374 _ = try self.allocateGotEntry(got_target);
38073375}
38083376
38093377pub 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
38773445 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
38783446 defer code_buffer.deinit();
38793447
3448 const gpa = self.base.allocator;
38803449 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);
38823451 if (!gop.found_existing) {
38833452 gop.value_ptr.* = .{};
38843453 }
......@@ -3886,24 +3455,32 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
38863455
38873456 const decl = module.declPtr(decl_index);
38883457 const decl_name = try decl.getFullyQualifiedName(module);
3889 defer self.base.allocator.free(decl_name);
3458 defer gpa.free(decl_name);
38903459
38913460 const name_str_index = blk: {
38923461 const index = unnamed_consts.items.len;
3893 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });
3894 defer self.base.allocator.free(name);
3895 break :blk try self.makeString(name);
3462 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
3463 defer gpa.free(name);
3464 break :blk try self.strtab.insert(gpa, name);
38963465 };
3897 const name = self.getString(name_str_index);
3466 const name = self.strtab.get(name_str_index);
38983467
38993468 log.debug("allocating symbol indexes for {s}", .{name});
39003469
39013470 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
3902 const local_sym_index = try self.allocateLocalSymbol();
3903 const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), math.log2(required_alignment));
3471 const sym_index = try self.allocateLocalSymbol();
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
39053482 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,
39073484 });
39083485 const code = switch (res) {
39093486 .externally_managed => |x| x,
......@@ -3917,7 +3494,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
39173494 };
39183495
39193496 atom.code.clearRetainingCapacity();
3920 try atom.code.appendSlice(self.base.allocator, code);
3497 try atom.code.appendSlice(gpa, code);
39213498
39223499 const match = try self.getMatchingSectionAtom(
39233500 atom,
......@@ -3933,18 +3510,18 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
39333510
39343511 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];
39373514 symbol.* = .{
39383515 .n_strx = name_str_index,
39393516 .n_type = macho.N_SECT,
3940 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,
3517 .n_sect = self.getSectionOrdinal(match),
39413518 .n_desc = 0,
39423519 .n_value = addr,
39433520 };
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;
39483525}
39493526
39503527pub 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)
39863563 }, &code_buffer, .{
39873564 .dwarf = ds,
39883565 }, .{
3989 .parent_atom_index = decl.link.macho.local_sym_index,
3566 .parent_atom_index = decl.link.macho.sym_index,
39903567 })
39913568 else
39923569 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
39933570 .ty = decl.ty,
39943571 .val = decl_val,
39953572 }, &code_buffer, .none, .{
3996 .parent_atom_index = decl.link.macho.local_sym_index,
3573 .parent_atom_index = decl.link.macho.sym_index,
39973574 });
39983575
39993576 const code = blk: {
......@@ -4168,8 +3745,7 @@ fn getMatchingSectionAtom(
41683745 .@"align" = align_log_2,
41693746 })).?;
41703747 };
4171 const seg = self.load_commands.items[match.seg].segment;
4172 const sect = seg.sections.items[match.sect];
3748 const sect = self.getSection(match);
41733749 log.debug(" allocating atom '{s}' in '{s},{s}' ({d},{d})", .{
41743750 name,
41753751 sect.segName(),
......@@ -4184,8 +3760,8 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
41843760 const module = self.base.options.module.?;
41853761 const decl = module.declPtr(decl_index);
41863762 const required_alignment = decl.getAlignment(self.base.options.target);
4187 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
4188 const symbol = &self.locals.items[decl.link.macho.local_sym_index];
3763 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
3764 const symbol = &self.locals.items[decl.link.macho.sym_index];
41893765
41903766 const sym_name = try decl.getFullyQualifiedName(module);
41913767 defer self.base.allocator.free(sym_name);
......@@ -4203,7 +3779,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
42033779 const match = decl_ptr.*.?;
42043780
42053781 if (decl.link.macho.size != 0) {
4206 const capacity = decl.link.macho.capacity(self.*);
3782 const capacity = decl.link.macho.capacity(self);
42073783 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
42083784
42093785 if (need_realloc) {
......@@ -4217,12 +3793,12 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
42173793 decl.link.macho.size = code_len;
42183794 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);
42213797 symbol.n_type = macho.N_SECT;
42223798 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
42233799 symbol.n_desc = 0;
42243800 } 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);
42263802 const addr = try self.allocateAtom(&decl.link.macho, code_len, required_alignment, match);
42273803
42283804 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
42333809 symbol.* = .{
42343810 .n_strx = name_str_index,
42353811 .n_type = macho.N_SECT,
4236 .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).?) + 1,
3812 .n_sect = self.getSectionOrdinal(match),
42373813 .n_desc = 0,
42383814 .n_value = addr,
42393815 };
4240 const got_index = self.got_entries_table.get(.{ .local = decl.link.macho.local_sym_index }).?;
4241 const got_atom = self.got_entries.items[got_index].atom;
4242 const got_sym = &self.locals.items[got_atom.local_sym_index];
4243 const vaddr = try self.allocateAtom(got_atom, @sizeOf(u64), 8, .{
4244 .seg = self.data_const_segment_cmd_index.?,
4245 .sect = self.got_section_index.?,
4246 });
4247 got_sym.n_value = vaddr;
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);
3816
3817 const got_target = SymbolWithLoc{
3818 .sym_index = decl.link.macho.sym_index,
3819 .file = null,
3820 };
3821 const got_index = self.got_entries_table.get(got_target).?;
3822 const got_atom = try self.createGotAtom(got_target);
3823 self.got_entries.items[got_index].atom = got_atom;
42523824 }
42533825
42543826 return symbol;
......@@ -4278,8 +3850,8 @@ pub fn updateDeclExports(
42783850
42793851 try self.globals.ensureUnusedCapacity(self.base.allocator, exports.len);
42803852 const decl = module.declPtr(decl_index);
4281 if (decl.link.macho.local_sym_index == 0) return;
4282 const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
3853 if (decl.link.macho.sym_index == 0) return;
3854 const decl_sym = &self.locals.items[decl.link.macho.sym_index];
42833855
42843856 for (exports) |exp| {
42853857 const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
......@@ -4316,46 +3888,47 @@ pub fn updateDeclExports(
43163888 }
43173889
43183890 const is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
4319 const n_strx = try self.makeString(exp_name);
4320 if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
4321 switch (resolv.where) {
4322 .global => {
4323 if (resolv.local_sym_index == decl.link.macho.local_sym_index) continue;
4324
4325 const sym = &self.globals.items[resolv.where_index];
4326
4327 if (sym.tentative()) {
4328 assert(self.tentatives.swapRemove(resolv.where_index));
4329 } else if (!is_weak and !(sym.weakDef() or sym.pext())) {
4330 _ = try module.failed_exports.put(
4331 module.gpa,
4332 exp,
4333 try Module.ErrorMsg.create(
4334 self.base.allocator,
4335 decl.srcLoc(),
4336 \\LinkError: symbol '{s}' defined multiple times
4337 \\ first definition in '{s}'
4338 ,
4339 .{ exp_name, self.objects.items[resolv.file.?].name },
4340 ),
4341 );
4342 continue;
4343 } else if (is_weak) continue; // Current symbol is weak, so skip it.
4344
4345 // Otherwise, update the resolver and the global symbol.
4346 sym.n_type = macho.N_SECT | macho.N_EXT;
4347 resolv.local_sym_index = decl.link.macho.local_sym_index;
4348 resolv.file = null;
4349 exp.link.macho.sym_index = resolv.where_index;
4350
4351 continue;
4352 },
4353 .undef => {
4354 assert(self.unresolved.swapRemove(resolv.where_index));
4355 _ = self.symbol_resolver.remove(n_strx);
4356 },
4357 }
4358 }
3891 _ = is_weak;
3892 const n_strx = try self.strtab.insert(self.base.allocator, exp_name);
3893 // if (self.symbol_resolver.getPtr(n_strx)) |resolv| {
3894 // switch (resolv.where) {
3895 // .global => {
3896 // if (resolv.sym_index == decl.link.macho.sym_index) continue;
3897
3898 // const sym = &self.globals.items[resolv.where_index];
3899
3900 // if (sym.tentative()) {
3901 // assert(self.tentatives.swapRemove(resolv.where_index));
3902 // } else if (!is_weak and !(sym.weakDef() or sym.pext())) {
3903 // _ = try module.failed_exports.put(
3904 // module.gpa,
3905 // exp,
3906 // try Module.ErrorMsg.create(
3907 // self.base.allocator,
3908 // decl.srcLoc(),
3909 // \\LinkError: symbol '{s}' defined multiple times
3910 // \\ first definition in '{s}'
3911 // ,
3912 // .{ exp_name, self.objects.items[resolv.file.?].name },
3913 // ),
3914 // );
3915 // continue;
3916 // } else if (is_weak) continue; // Current symbol is weak, so skip it.
3917
3918 // // Otherwise, update the resolver and the global symbol.
3919 // sym.n_type = macho.N_SECT | macho.N_EXT;
3920 // resolv.sym_index = decl.link.macho.sym_index;
3921 // resolv.file = null;
3922 // exp.link.macho.sym_index = resolv.where_index;
3923
3924 // continue;
3925 // },
3926 // .undef => {
3927 // assert(self.unresolved.swapRemove(resolv.where_index));
3928 // _ = self.symbol_resolver.remove(n_strx);
3929 // },
3930 // }
3931 // }
43593932
43603933 var n_type: u8 = macho.N_SECT | macho.N_EXT;
43613934 var n_desc: u16 = 0;
......@@ -4377,41 +3950,44 @@ pub fn updateDeclExports(
43773950 else => unreachable,
43783951 }
43793952
4380 const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
4381 const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
4382 _ = self.globals.addOneAssumeCapacity();
4383 break :inner @intCast(u32, self.globals.items.len - 1);
4384 };
4385 break :blk i;
4386 };
4387 const sym = &self.globals.items[global_sym_index];
3953 const global_sym_index: u32 = 0;
3954 // const global_sym_index = if (exp.link.macho.sym_index) |i| i else blk: {
3955 // const i = if (self.globals_free_list.popOrNull()) |i| i else inner: {
3956 // _ = self.globals.addOneAssumeCapacity();
3957 // break :inner @intCast(u32, self.globals.items.len - 1);
3958 // };
3959 // break :blk i;
3960 // };
3961 const sym = &self.locals.items[global_sym_index];
43883962 sym.* = .{
4389 .n_strx = try self.makeString(exp_name),
3963 .n_strx = try self.strtab.insert(self.base.allocator, exp_name),
43903964 .n_type = n_type,
43913965 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
43923966 .n_desc = n_desc,
43933967 .n_value = decl_sym.n_value,
43943968 };
43953969 exp.link.macho.sym_index = global_sym_index;
3970 _ = n_strx;
43963971
4397 try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
4398 .where = .global,
4399 .where_index = global_sym_index,
4400 .local_sym_index = decl.link.macho.local_sym_index,
4401 });
3972 // try self.symbol_resolver.putNoClobber(self.base.allocator, n_strx, .{
3973 // .where = .global,
3974 // .where_index = global_sym_index,
3975 // .sym_index = decl.link.macho.sym_index,
3976 // });
44023977 }
44033978}
44043979
44053980pub fn deleteExport(self: *MachO, exp: Export) void {
44063981 if (self.llvm_object) |_| return;
44073982 const sym_index = exp.sym_index orelse return;
4408 self.globals_free_list.append(self.base.allocator, sym_index) catch {};
4409 const global = &self.globals.items[sym_index];
4410 log.debug("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });
4411 assert(self.symbol_resolver.remove(global.n_strx));
4412 global.n_type = 0;
4413 global.n_strx = 0;
4414 global.n_value = 0;
3983 _ = sym_index;
3984 // self.globals_free_list.append(self.base.allocator, sym_index) catch {};
3985 // const global = &self.globals.items[sym_index];
3986 // log.warn("deleting export '{s}': {}", .{ self.getString(global.n_strx), global });
3987 // assert(self.symbol_resolver.remove(global.n_strx));
3988 // global.n_type = 0;
3989 // global.n_strx = 0;
3990 // global.n_value = 0;
44153991}
44163992
44173993fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
......@@ -4421,11 +3997,11 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
44213997 .seg = self.text_segment_cmd_index.?,
44223998 .sect = self.text_const_section_index.?,
44233999 }, true);
4424 self.locals_free_list.append(self.base.allocator, atom.local_sym_index) catch {};
4425 self.locals.items[atom.local_sym_index].n_type = 0;
4426 _ = self.atom_by_index_table.remove(atom.local_sym_index);
4427 log.debug(" adding local symbol index {d} to free list", .{atom.local_sym_index});
4428 atom.local_sym_index = 0;
4000 self.locals_free_list.append(self.base.allocator, atom.sym_index) catch {};
4001 self.locals.items[atom.sym_index].n_type = 0;
4002 _ = self.atom_by_index_table.remove(atom.sym_index);
4003 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
4004 atom.sym_index = 0;
44294005 }
44304006 unnamed_consts.clearAndFree(self.base.allocator);
44314007}
......@@ -4443,29 +4019,30 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
44434019 self.freeUnnamedConsts(decl_index);
44444020 }
44454021 // 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) {
4447 self.locals_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
4022 if (decl.link.macho.sym_index != 0) {
4023 self.locals_free_list.append(self.base.allocator, decl.link.macho.sym_index) catch {};
44484024
44494025 // 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| {
44514028 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 };
4453 _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });
4029 self.got_entries.items[got_index] = .{ .target = .{ .sym_index = 0, .file = null }, .atom = undefined };
4030 _ = self.got_entries_table.swapRemove(got_target);
44544031
44554032 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);
44574034 }
44584035
44594036 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
44604037 got_index,
4461 decl.link.macho.local_sym_index,
4038 decl.link.macho.sym_index,
44624039 });
44634040 }
44644041
4465 self.locals.items[decl.link.macho.local_sym_index].n_type = 0;
4466 _ = self.atom_by_index_table.remove(decl.link.macho.local_sym_index);
4467 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.local_sym_index});
4468 decl.link.macho.local_sym_index = 0;
4042 self.locals.items[decl.link.macho.sym_index].n_type = 0;
4043 _ = self.atom_by_index_table.remove(decl.link.macho.sym_index);
4044 log.debug(" adding local symbol index {d} to free list", .{decl.link.macho.sym_index});
4045 decl.link.macho.sym_index = 0;
44694046 }
44704047 if (self.d_sym) |*d_sym| {
44714048 d_sym.dwarf.freeDecl(decl);
......@@ -4477,12 +4054,12 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
44774054 const decl = mod.declPtr(decl_index);
44784055
44794056 assert(self.llvm_object == null);
4480 assert(decl.link.macho.local_sym_index != 0);
4057 assert(decl.link.macho.sym_index != 0);
44814058
44824059 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
44834060 try atom.relocs.append(self.base.allocator, .{
44844061 .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 },
44864063 .addend = reloc_info.addend,
44874064 .subtractor = null,
44884065 .pcrel = false,
......@@ -5019,8 +4596,6 @@ fn populateMissingMetadata(self: *MachO) !void {
50194596 });
50204597 self.load_commands_dirty = true;
50214598 }
5022
5023 self.cold_start = true;
50244599}
50254600
50264601fn calcMinHeaderpad(self: *MachO) u64 {
......@@ -5121,7 +4696,7 @@ fn allocateSegment(self: *MachO, maybe_index: ?u16, indices: []const ?u16, init_
51214696
51224697 // Allocate the sections according to their alignment at the beginning of the segment.
51234698 var start = init_size;
5124 for (seg.sections.items) |*sect, sect_id| {
4699 for (seg.sections.items) |*sect| {
51254700 const is_zerofill = sect.flags == macho.S_ZEROFILL or sect.flags == macho.S_THREAD_LOCAL_ZEROFILL;
51264701 const use_llvm = build_options.have_llvm and self.base.options.use_llvm;
51274702 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_
51294704 const start_aligned = mem.alignForwardGeneric(u64, start, alignment);
51304705
51314706 // 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);
51334711 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
51584713 start = start_aligned + sect.size;
51594714
51604715 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
54104965 return max_alignment;
54114966}
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 {
54144988 const tracy = trace(@src());
54154989 defer tracy.end();
54164990
5417 const seg = &self.load_commands.items[match.seg].segment;
5418 const sect = &seg.sections.items[match.sect];
4991 const sect = self.getSectionPtr(match);
54194992 var free_list = self.atom_free_lists.get(match).?;
54204993 const needs_padding = match.seg == self.text_segment_cmd_index.? and match.sect == self.text_section_index.?;
54214994 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
54365009 const big_atom = free_list.items[i];
54375010 // We now have a pointer to a live atom that has too much capacity.
54385011 // Is it enough that we could fit this new atom?
5439 const sym = self.locals.items[big_atom.local_sym_index];
5440 const capacity = big_atom.capacity(self.*);
5012 const sym = self.locals.items[big_atom.sym_index];
5013 const capacity = big_atom.capacity(self);
54415014 const ideal_capacity = if (needs_padding) padToIdeal(capacity) else capacity;
54425015 const ideal_capacity_end_vaddr = math.add(u64, sym.n_value, ideal_capacity) catch ideal_capacity;
54435016 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
54475020 // Additional bookkeeping here to notice if this free list node
54485021 // should be deleted because the atom that it points to has grown to take up
54495022 // more of the extra capacity.
5450 if (!big_atom.freeListEligible(self.*)) {
5023 if (!big_atom.freeListEligible(self)) {
54515024 _ = free_list.swapRemove(i);
54525025 } else {
54535026 i += 1;
......@@ -5467,7 +5040,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64, m
54675040 }
54685041 break :blk new_start_vaddr;
54695042 } 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];
54715044 const ideal_capacity = if (needs_padding) padToIdeal(last.size) else last.size;
54725045 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
54735046 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
55165089 return vaddr;
55175090}
55185091
5519fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
5092pub fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
55205093 if (self.atoms.getPtr(match)) |last| {
55215094 last.*.next = atom;
55225095 atom.prev = last.*;
......@@ -5524,34 +5097,38 @@ fn addAtomToSection(self: *MachO, atom: *Atom, match: MatchingSection) !void {
55245097 } else {
55255098 try self.atoms.putNoClobber(self.base.allocator, match, atom);
55265099 }
5527 const seg = &self.load_commands.items[match.seg].segment;
5528 const sect = &seg.sections.items[match.sect];
5529 sect.size += atom.size;
5100 const sect = self.getSectionPtr(match);
5101 const atom_alignment = try math.powi(u32, 2, atom.alignment);
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);
55305106}
55315107
55325108pub fn getGlobalSymbol(self: *MachO, name: []const u8) !u32 {
5533 const sym_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{name});
5534 defer self.base.allocator.free(sym_name);
5535 const n_strx = try self.makeString(sym_name);
5536
5537 if (!self.symbol_resolver.contains(n_strx)) {
5538 log.debug("adding new extern function '{s}'", .{sym_name});
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);
5109 const gpa = self.base.allocator;
5110 const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
5111 defer gpa.free(sym_name);
5112
5113 if (self.globals.getIndex(sym_name)) |global_index| {
5114 return @intCast(u32, global_index);
55525115 }
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);
55555132}
55565133
55575134fn getSegmentAllocBase(self: MachO, indices: []const ?u16) struct { vmaddr: u64, fileoff: u64 } {
......@@ -5579,15 +5156,44 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
55795156
55805157 for (indices) |maybe_index| {
55815158 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
55835189 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() });
55855191 maybe_index.* = null;
55865192 seg.inner.cmdsize -= @sizeOf(macho.section_64);
55875193 seg.inner.nsects -= 1;
55885194 } else {
55895195 maybe_index.* = @intCast(u16, seg.sections.items.len);
5590 seg.sections.appendAssumeCapacity(sect);
5196 seg.sections.appendAssumeCapacity(sect.*);
55915197 }
55925198 try mapping.putNoClobber(old_idx, maybe_index.*);
55935199 }
......@@ -5614,7 +5220,7 @@ fn pruneAndSortSectionsInSegment(self: *MachO, maybe_seg_id: *?u16, indices: []*
56145220
56155221 if (seg.inner.nsects == 0 and !mem.eql(u8, "__TEXT", seg.inner.segName())) {
56165222 // 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()});
56185224 seg.inner.cmd = @intToEnum(macho.LC, 0);
56195225 maybe_seg_id.* = null;
56205226 }
......@@ -5697,36 +5303,22 @@ fn pruneAndSortSections(self: *MachO) !void {
56975303}
56985304
56995305fn 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;
57015307 if (!dead_strip) return;
57025308
5309 const gpa = self.base.allocator;
5310
57035311 // Add all exports as GC roots
5704 for (self.globals.items) |sym| {
5705 if (sym.n_type == 0) continue;
5706 const resolv = self.symbol_resolver.get(sym.n_strx).?;
5707 assert(resolv.where == .global);
5708 const gc_root = self.atom_by_index_table.get(resolv.local_sym_index) orelse {
5709 log.warn("skipping {s}", .{self.getString(sym.n_strx)});
5312 for (self.globals.values()) |global| {
5313 const sym = self.getSymbol(global);
5314 if (!sym.sect()) continue;
5315 const gc_root = self.getAtomForSymbol(global) orelse {
5316 log.debug("skipping {s}", .{self.getSymbolName(global)});
57105317 continue;
57115318 };
5712 _ = try self.gc_roots.getOrPut(self.base.allocator, gc_root);
5319 _ = try self.gc_roots.getOrPut(gpa, gc_root);
57135320 }
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
57305322 // Add any atom targeting an import as GC root
57315323 var atoms_it = self.atoms.iterator();
57325324 while (atoms_it.next()) |entry| {
......@@ -5734,19 +5326,13 @@ fn gcAtoms(self: *MachO) !void {
57345326
57355327 while (true) {
57365328 for (atom.relocs.items) |rel| {
5737 if ((try Atom.getTargetAtom(rel, self)) == null) switch (rel.target) {
5738 .local => {},
5739 .global => |n_strx| {
5740 const resolv = self.symbol_resolver.get(n_strx).?;
5741 switch (resolv.where) {
5742 .global => {},
5743 .undef => {
5744 _ = try self.gc_roots.getOrPut(self.base.allocator, atom);
5745 break;
5746 },
5747 }
5748 },
5749 };
5329 if ((try rel.getTargetAtom(self)) == null) {
5330 const target_sym = self.getSymbol(rel.target);
5331 if (target_sym.undf()) {
5332 _ = try self.gc_roots.getOrPut(gpa, atom);
5333 break;
5334 }
5335 }
57505336 }
57515337
57525338 if (atom.prev) |prev| {
......@@ -5755,15 +5341,15 @@ fn gcAtoms(self: *MachO) !void {
57555341 }
57565342 }
57575343
5758 var stack = std.ArrayList(*Atom).init(self.base.allocator);
5344 var stack = std.ArrayList(*Atom).init(gpa);
57595345 defer stack.deinit();
57605346 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);
57635349 defer retained.deinit();
57645350 try retained.ensureUnusedCapacity(self.gc_roots.count());
57655351
5766 log.warn("GC roots:", .{});
5352 log.debug("GC roots:", .{});
57675353 var gc_roots_it = self.gc_roots.keyIterator();
57685354 while (gc_roots_it.next()) |gc_root| {
57695355 self.logAtom(gc_root.*);
......@@ -5772,15 +5358,15 @@ fn gcAtoms(self: *MachO) !void {
57725358 retained.putAssumeCapacityNoClobber(gc_root.*, {});
57735359 }
57745360
5775 log.warn("walking tree...", .{});
5361 log.debug("walking tree...", .{});
57765362 while (stack.popOrNull()) |source_atom| {
57775363 for (source_atom.relocs.items) |rel| {
5778 if (try Atom.getTargetAtom(rel, self)) |target_atom| {
5364 if (try rel.getTargetAtom(self)) |target_atom| {
57795365 const gop = try retained.getOrPut(target_atom);
57805366 if (!gop.found_existing) {
5781 log.warn(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{
5782 source_atom.local_sym_index,
5783 target_atom.local_sym_index,
5367 log.debug(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{
5368 source_atom.sym_index,
5369 target_atom.sym_index,
57845370 });
57855371 try stack.append(target_atom);
57865372 }
......@@ -5808,58 +5394,38 @@ fn gcAtoms(self: *MachO) !void {
58085394 }
58095395 }
58105396
5811 const seg = &self.load_commands.items[match.seg].segment;
5812 const sect = &seg.sections.items[match.sect];
5397 const sect = self.getSectionPtr(match);
58135398 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
58175402 while (true) {
58185403 const orig_prev = atom.prev;
58195404
58205405 if (!retained.contains(atom)) {
58215406 // 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);
58255410 sym.n_desc = N_DESC_GCED;
58265411
5827 if (self.symbol_resolver.getPtr(sym.n_strx)) |resolv| {
5828 if (resolv.local_sym_index == atom.local_sym_index) {
5829 const global = &self.globals.items[resolv.where_index];
5830 global.n_desc = N_DESC_GCED;
5831 }
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 }
5412 // TODO add full bookkeeping here
5413 const global = SymbolWithLoc{ .sym_index = atom.sym_index, .file = atom.file };
5414 _ = self.got_entries_table.swapRemove(global);
5415 _ = self.stubs_table.swapRemove(global);
5416 _ = self.tlv_ptr_entries_table.swapRemove(global);
58475417
58485418 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 });
58505423 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 }
58585424 }
5859
5860 log.warn(" BEFORE size = {x}", .{sect.size});
5425 // If we want to enable GC for incremental codepath, we need to take into
5426 // account any padding that might have been left here.
58615427 sect.size -= atom.size;
5862 log.warn(" AFTER size = {x}", .{sect.size});
5428
58635429 if (atom.prev) |prev| {
58645430 prev.next = atom.next;
58655431 }
......@@ -5870,6 +5436,8 @@ fn gcAtoms(self: *MachO) !void {
58705436 // The section will be GCed in the next step.
58715437 entry.value_ptr.* = if (atom.prev) |prev| prev else undefined;
58725438 }
5439
5440 _ = try self.gc_sections.getOrPut(gpa, match);
58735441 }
58745442
58755443 if (orig_prev) |prev| {
......@@ -5885,7 +5453,11 @@ fn updateSectionOrdinals(self: *MachO) !void {
58855453 const tracy = trace(@src());
58865454 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);
58895461 defer ordinal_remap.deinit();
58905462 var ordinals: std.AutoArrayHashMapUnmanaged(MatchingSection, void) = .{};
58915463
......@@ -5897,27 +5469,38 @@ fn updateSectionOrdinals(self: *MachO) !void {
58975469 }) |maybe_index| {
58985470 const index = maybe_index orelse continue;
58995471 const seg = self.load_commands.items[index].segment;
5900 for (seg.sections.items) |_, sect_id| {
5472 for (seg.sections.items) |sect, sect_id| {
59015473 const match = MatchingSection{
59025474 .seg = @intCast(u16, index),
59035475 .sect = @intCast(u16, sect_id),
59045476 };
5905 const old_ordinal = @intCast(u8, self.section_ordinals.getIndex(match).? + 1);
5477 const old_ordinal = self.getSectionOrdinal(match);
59065478 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 });
59075485 try ordinal_remap.putNoClobber(old_ordinal, new_ordinal);
5908 try ordinals.putNoClobber(self.base.allocator, match, {});
5486 try ordinals.putNoClobber(gpa, match, {});
59095487 }
59105488 }
59115489
59125490 for (self.locals.items) |*sym| {
5491 if (sym.undf()) continue;
59135492 if (sym.n_sect == 0) continue;
59145493 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
59155494 }
5916 for (self.globals.items) |*sym| {
5917 sym.n_sect = ordinal_remap.get(sym.n_sect).?;
5495 for (self.objects.items) |*object| {
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 }
59185501 }
59195502
5920 self.section_ordinals.deinit(self.base.allocator);
5503 self.section_ordinals.deinit(gpa);
59215504 self.section_ordinals = ordinals;
59225505}
59235506
......@@ -5925,11 +5508,13 @@ fn writeDyldInfoData(self: *MachO) !void {
59255508 const tracy = trace(@src());
59265509 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);
59295514 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);
59315516 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);
59335518 defer lazy_bind_pointers.deinit();
59345519
59355520 {
......@@ -5942,13 +5527,13 @@ fn writeDyldInfoData(self: *MachO) !void {
59425527 if (match.seg == seg) continue; // __TEXT is non-writable
59435528 }
59445529
5945 const seg = self.load_commands.items[match.seg].segment;
5946 const sect = seg.sections.items[match.sect];
5947 log.warn("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
5530 const seg = self.getSegment(match);
5531 const sect = self.getSection(match);
5532 log.debug("dyld info for {s},{s}", .{ sect.segName(), sect.sectName() });
59485533
59495534 while (true) {
5950 log.warn(" ATOM %{d}", .{atom.local_sym_index});
5951 const sym = self.locals.items[atom.local_sym_index];
5535 log.debug(" ATOM %{d}", .{atom.sym_index});
5536 const sym = atom.getSymbol(self);
59525537 const base_offset = sym.n_value - seg.inner.vmaddr;
59535538
59545539 for (atom.rebases.items) |offset| {
......@@ -5959,57 +5544,35 @@ fn writeDyldInfoData(self: *MachO) !void {
59595544 }
59605545
59615546 for (atom.bindings.items) |binding| {
5962 const resolv = self.symbol_resolver.get(binding.n_strx).?;
5963 switch (resolv.where) {
5964 .global => {
5965 // Turn into a rebase.
5966 try rebase_pointers.append(.{
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 },
5547 const global = self.globals.values()[binding.global_index];
5548 const bind_sym = self.getSymbol(global);
5549 var flags: u4 = 0;
5550 if (bind_sym.weakRef()) {
5551 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
59855552 }
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 });
59865560 }
59875561
59885562 for (atom.lazy_bindings.items) |binding| {
5989 const resolv = self.symbol_resolver.get(binding.n_strx).?;
5990 switch (resolv.where) {
5991 .global => {
5992 // Turn into a rebase.
5993 try rebase_pointers.append(.{
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 },
5563 const global = self.globals.values()[binding.global_index];
5564 const bind_sym = self.getSymbol(global);
5565 var flags: u4 = 0;
5566 if (bind_sym.weakRef()) {
5567 flags |= @truncate(u4, macho.BIND_SYMBOL_FLAGS_WEAK_IMPORT);
60125568 }
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 });
60135576 }
60145577
60155578 if (atom.prev) |prev| {
......@@ -6020,7 +5583,7 @@ fn writeDyldInfoData(self: *MachO) !void {
60205583 }
60215584
60225585 var trie: Trie = .{};
6023 defer trie.deinit(self.base.allocator);
5586 defer trie.deinit(gpa);
60245587
60255588 {
60265589 // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER.
......@@ -6029,19 +5592,22 @@ fn writeDyldInfoData(self: *MachO) !void {
60295592 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
60305593 const base_address = text_segment.inner.vmaddr;
60315594
6032 for (self.globals.items) |sym| {
6033 if (sym.n_type == 0) continue;
6034 const sym_name = self.getString(sym.n_strx);
5595 for (self.globals.values()) |global| {
5596 const sym = self.getSymbol(global);
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);
60355601 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, .{
60385604 .name = sym_name,
60395605 .vmaddr_offset = sym.n_value - base_address,
60405606 .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR,
60415607 });
60425608 }
60435609
6044 try trie.finalize(self.base.allocator);
5610 try trie.finalize(gpa);
60455611 }
60465612
60475613 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
......@@ -6086,8 +5652,8 @@ fn writeDyldInfoData(self: *MachO) !void {
60865652 seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff;
60875653
60885654 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);
6090 defer self.base.allocator.free(buffer);
5655 var buffer = try gpa.alloc(u8, needed_size);
5656 defer gpa.free(buffer);
60915657 mem.set(u8, buffer, 0);
60925658
60935659 var stream = std.io.fixedBufferStream(buffer);
......@@ -6114,10 +5680,12 @@ fn writeDyldInfoData(self: *MachO) !void {
61145680 try self.populateLazyBindOffsetsInStubHelper(
61155681 buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size],
61165682 );
5683
61175684 self.load_commands_dirty = true;
61185685}
61195686
61205687fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
5688 const gpa = self.base.allocator;
61215689 const text_segment_cmd_index = self.text_segment_cmd_index orelse return;
61225690 const stub_helper_section_index = self.stub_helper_section_index orelse return;
61235691 const last_atom = self.atoms.get(.{
......@@ -6127,7 +5695,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61275695 if (self.stub_helper_preamble_atom == null) return;
61285696 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);
61315699 defer table.deinit();
61325700
61335701 {
......@@ -6143,7 +5711,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61435711
61445712 while (true) {
61455713 const laptr_off = blk: {
6146 const sym = self.locals.items[laptr_atom.local_sym_index];
5714 const sym = laptr_atom.getSymbol(self);
61475715 break :blk @intCast(i64, sym.n_value - base_addr);
61485716 };
61495717 try table.putNoClobber(laptr_off, stub_atom);
......@@ -6156,7 +5724,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61565724
61575725 var stream = std.io.fixedBufferStream(buffer);
61585726 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);
61605728 try offsets.append(.{ .sym_offset = undefined, .offset = 0 });
61615729 defer offsets.deinit();
61625730 var valid_block = false;
......@@ -6199,10 +5767,10 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
61995767 }
62005768 }
62015769
6202 const sect = blk: {
6203 const seg = self.load_commands.items[text_segment_cmd_index].segment;
6204 break :blk seg.sections.items[stub_helper_section_index];
6205 };
5770 const sect = self.getSection(.{
5771 .seg = text_segment_cmd_index,
5772 .sect = stub_helper_section_index,
5773 });
62065774 const stub_offset: u4 = switch (self.base.options.target.cpu.arch) {
62075775 .x86_64 => 1,
62085776 .aarch64 => 2 * @sizeOf(u32),
......@@ -6213,79 +5781,63 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
62135781
62145782 while (offsets.popOrNull()) |bind_offset| {
62155783 const atom = table.get(bind_offset.sym_offset).?;
6216 const sym = self.locals.items[atom.local_sym_index];
5784 const sym = atom.getSymbol(self);
62175785 const file_offset = sect.offset + sym.n_value - sect.addr + stub_offset;
62185786 mem.writeIntLittle(u32, &buf, bind_offset.offset);
62195787 log.debug("writing lazy bind offset in stub helper of 0x{x} for symbol {s} at offset 0x{x}", .{
62205788 bind_offset.offset,
6221 self.getString(sym.n_strx),
5789 atom.getName(self),
62225790 file_offset,
62235791 });
62245792 try self.base.file.?.pwriteAll(&buf, file_offset);
62255793 }
62265794}
62275795
5796const asc_u64 = std.sort.asc(u64);
5797
62285798fn writeFunctionStarts(self: *MachO) !void {
6229 var atom = self.atoms.get(.{
6230 .seg = self.text_segment_cmd_index orelse return,
6231 .sect = self.text_section_index orelse return,
6232 }) orelse return;
5799 const text_seg_index = self.text_segment_cmd_index orelse return;
5800 const text_sect_index = self.text_section_index orelse return;
5801 const text_seg = self.load_commands.items[text_seg_index].segment;
62335802
62345803 const tracy = trace(@src());
62355804 defer tracy.end();
62365805
6237 while (atom.prev) |prev| {
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;
5806 const gpa = self.base.allocator;
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);
6262 last_off = offset;
6263 }
5813 for (self.globals.values()) |global| {
5814 const sym = self.getSymbol(global);
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| {
6266 const cont_sym = self.locals.items[cont.local_sym_index];
5820 addresses.appendAssumeCapacity(sym.n_value);
5821 }
62675822
6268 if (cont_sym.n_strx == 0) continue;
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 }
5823 std.sort.sort(u64, addresses.items, {}, asc_u64);
62735824
6274 const offset = @intCast(u32, cont_sym.n_value - text_seg.inner.vmaddr);
6275 const diff = offset - last_off;
5825 var offsets = std.ArrayList(u32).init(gpa);
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);
6280 last_off = offset;
6281 }
5834 if (diff == 0) continue;
62825835
6283 if (atom.next) |next| {
6284 atom = next;
6285 } else break;
5836 offsets.appendAssumeCapacity(diff);
5837 last_off = offset;
62865838 }
62875839
6288 var buffer = std.ArrayList(u8).init(self.base.allocator);
5840 var buffer = std.ArrayList(u8).init(gpa);
62895841 defer buffer.deinit();
62905842
62915843 const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
......@@ -6331,12 +5883,14 @@ fn writeDices(self: *MachO) !void {
63315883 atom = prev;
63325884 }
63335885
6334 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
6335 const text_sect = text_seg.sections.items[self.text_section_index.?];
5886 const text_sect = self.getSection(.{
5887 .seg = self.text_segment_cmd_index.?,
5888 .sect = self.text_section_index.?,
5889 });
63365890
63375891 while (true) {
63385892 if (atom.dices.items.len > 0) {
6339 const sym = self.locals.items[atom.local_sym_index];
5893 const sym = atom.getSymbol(self);
63405894 const base_off = math.cast(u32, sym.n_value - text_sect.addr + text_sect.offset) orelse return error.Overflow;
63415895
63425896 try buf.ensureUnusedCapacity(atom.dices.items.len * @sizeOf(macho.data_in_code_entry));
......@@ -6377,113 +5931,139 @@ fn writeSymbolTable(self: *MachO) !void {
63775931 const tracy = trace(@src());
63785932 defer tracy.end();
63795933
5934 const gpa = self.base.allocator;
63805935 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
63815936 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
63825937 const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64));
63835938 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);
63865941 defer locals.deinit();
63875942
6388 for (self.locals.items) |sym| {
6389 if (sym.n_strx == 0) continue;
6390 if (sym.n_desc == N_DESC_GCED) continue;
6391 if (self.symbol_resolver.get(sym.n_strx)) |_| continue;
5943 for (self.locals.items) |sym, sym_id| {
5944 if (sym.n_strx == 0) continue; // no name, skip
5945 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
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
63925949 try locals.append(sym);
63935950 }
63945951
6395 var globals = std.ArrayList(macho.nlist_64).init(self.base.allocator);
6396 defer globals.deinit();
6397
6398 for (self.globals.items) |sym| {
6399 if (sym.n_desc == N_DESC_GCED) continue;
6400 try globals.append(sym);
6401 }
5952 for (self.objects.items) |object, object_id| {
5953 if (self.has_stabs) {
5954 if (object.debug_info) |_| {
5955 // Open scope
5956 try locals.ensureUnusedCapacity(3);
5957 locals.appendAssumeCapacity(.{
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?
6404 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
6405 defer undefs.deinit();
6406 var undefs_table = std.AutoHashMap(u32, u32).init(self.base.allocator);
6407 defer undefs_table.deinit();
6408 try undefs.ensureTotalCapacity(self.undefs.items.len);
6409 try undefs_table.ensureTotalCapacity(@intCast(u32, self.undefs.items.len));
5979 for (object.managed_atoms.items) |atom| {
5980 for (atom.contained.items) |sym_at_off| {
5981 const stab = sym_at_off.stab orelse continue;
5982 const sym_loc = SymbolWithLoc{
5983 .sym_index = sym_at_off.sym_index,
5984 .file = atom.file,
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| {
6412 if (sym.n_strx == 0) continue;
6413 const new_index = @intCast(u32, undefs.items.len);
6414 undefs.appendAssumeCapacity(sym);
6415 undefs_table.putAssumeCapacityNoClobber(@intCast(u32, i), new_index);
6001 // Close scope
6002 try locals.append(.{
6003 .n_strx = 0,
6004 .n_type = macho.N_SO,
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 }
64166021 }
64176022
6418 if (self.has_stabs) {
6419 for (self.objects.items) |object| {
6420 if (object.debug_info == null) continue;
6023 var exports = std.ArrayList(macho.nlist_64).init(gpa);
6024 defer exports.deinit();
64216025
6422 // Open scope
6423 try locals.ensureUnusedCapacity(3);
6424 locals.appendAssumeCapacity(.{
6425 .n_strx = try self.makeString(object.tu_comp_dir.?),
6426 .n_type = macho.N_SO,
6427 .n_sect = 0,
6428 .n_desc = 0,
6429 .n_value = 0,
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 });
6026 for (self.globals.values()) |global| {
6027 const sym = self.getSymbol(global);
6028 if (sym.undf()) continue; // import, skip
6029 if (sym.n_desc == N_DESC_GCED) continue; // GCed, skip
6030 var out_sym = sym;
6031 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6032 try exports.append(out_sym);
6033 }
64456034
6446 for (object.contained_atoms.items) |atom| {
6447 for (atom.contained.items) |sym_at_off| {
6448 const stab = sym_at_off.stab orelse continue;
6449 const nlists = try stab.asNlists(sym_at_off.local_sym_index, self);
6450 defer self.base.allocator.free(nlists);
6451 try locals.appendSlice(nlists);
6452 }
6453 }
6035 var imports = std.ArrayList(macho.nlist_64).init(gpa);
6036 defer imports.deinit();
6037 var imports_table = std.AutoHashMap(SymbolWithLoc, u32).init(gpa);
6038 defer imports_table.deinit();
64546039
6455 // Close scope
6456 try locals.append(.{
6457 .n_strx = 0,
6458 .n_type = macho.N_SO,
6459 .n_sect = 0,
6460 .n_desc = 0,
6461 .n_value = 0,
6462 });
6463 }
6040 for (self.globals.values()) |global| {
6041 const sym = self.getSymbol(global);
6042 if (sym.n_strx == 0) continue; // no name, skip
6043 if (!sym.undf()) continue; // not an import, skip
6044 const new_index = @intCast(u32, imports.items.len);
6045 var out_sym = sym;
6046 out_sym.n_strx = try self.strtab.insert(gpa, self.getSymbolName(global));
6047 try imports.append(out_sym);
6048 try imports_table.putNoClobber(global, new_index);
64646049 }
64656050
64666051 const nlocals = locals.items.len;
6467 const nexports = globals.items.len;
6468 const nundefs = undefs.items.len;
6052 const nexports = exports.items.len;
6053 const nimports = imports.items.len;
6054 symtab.nsyms = @intCast(u32, nlocals + nexports + nimports);
64696055
6470 const locals_off = symtab.symoff;
6471 const locals_size = nlocals * @sizeOf(macho.nlist_64);
6472 log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
6473 try self.base.file.?.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
6474
6475 const exports_off = locals_off + locals_size;
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);
6056 var buffer = std.ArrayList(u8).init(gpa);
6057 defer buffer.deinit();
6058 try buffer.ensureTotalCapacityPrecise(symtab.nsyms * @sizeOf(macho.nlist_64));
6059 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(locals.items));
6060 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(exports.items));
6061 buffer.appendSliceAssumeCapacity(mem.sliceAsBytes(imports.items));
64796062
6480 const undefs_off = exports_off + exports_size;
6481 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
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);
6063 log.debug("writing symtab from 0x{x} to 0x{x}", .{ symtab.symoff, symtab.symoff + buffer.items.len });
6064 try self.base.file.?.pwriteAll(buffer.items, symtab.symoff);
64846065
6485 symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs);
6486 seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff;
6066 seg.inner.filesize = symtab.symoff + buffer.items.len - seg.inner.fileoff;
64876067
64886068 // Update dynamic symbol table.
64896069 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab;
......@@ -6491,7 +6071,7 @@ fn writeSymbolTable(self: *MachO) !void {
64916071 dysymtab.iextdefsym = dysymtab.nlocalsym;
64926072 dysymtab.nextdefsym = @intCast(u32, nexports);
64936073 dysymtab.iundefsym = dysymtab.nlocalsym + dysymtab.nextdefsym;
6494 dysymtab.nundefsym = @intCast(u32, nundefs);
6074 dysymtab.nundefsym = @intCast(u32, nimports);
64956075
64966076 const nstubs = @intCast(u32, self.stubs_table.count());
64976077 const ngot_entries = @intCast(u32, self.got_entries_table.count());
......@@ -6507,55 +6087,53 @@ fn writeSymbolTable(self: *MachO) !void {
65076087 dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32),
65086088 });
65096089
6510 var buf = std.ArrayList(u8).init(self.base.allocator);
6090 var buf = std.ArrayList(u8).init(gpa);
65116091 defer buf.deinit();
65126092 try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32));
65136093 const writer = buf.writer();
65146094
65156095 if (self.text_segment_cmd_index) |text_segment_cmd_index| blk: {
65166096 const stubs_section_index = self.stubs_section_index orelse break :blk;
6517 const text_segment = &self.load_commands.items[text_segment_cmd_index].segment;
6518 const stubs = &text_segment.sections.items[stubs_section_index];
6097 const stubs = self.getSectionPtr(.{
6098 .seg = text_segment_cmd_index,
6099 .sect = stubs_section_index,
6100 });
65196101 stubs.reserved1 = 0;
6520 for (self.stubs_table.keys()) |key| {
6521 const resolv = self.symbol_resolver.get(key).?;
6522 switch (resolv.where) {
6523 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6524 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6525 }
6102 for (self.stubs_table.keys()) |target| {
6103 const sym = self.getSymbol(target);
6104 assert(sym.undf());
6105 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
65266106 }
65276107 }
65286108
65296109 if (self.data_const_segment_cmd_index) |data_const_segment_cmd_index| blk: {
65306110 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;
6532 const got = &data_const_segment.sections.items[got_section_index];
6111 const got = self.getSectionPtr(.{
6112 .seg = data_const_segment_cmd_index,
6113 .sect = got_section_index,
6114 });
65336115 got.reserved1 = nstubs;
6534 for (self.got_entries_table.keys()) |key| {
6535 switch (key) {
6536 .local => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6537 .global => |n_strx| {
6538 const resolv = self.symbol_resolver.get(n_strx).?;
6539 switch (resolv.where) {
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 },
6116 for (self.got_entries_table.keys()) |target| {
6117 const sym = self.getSymbol(target);
6118 if (sym.undf()) {
6119 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
6120 } else {
6121 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
65446122 }
65456123 }
65466124 }
65476125
65486126 if (self.data_segment_cmd_index) |data_segment_cmd_index| blk: {
65496127 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;
6551 const la_symbol_ptr = &data_segment.sections.items[la_symbol_ptr_section_index];
6128 const la_symbol_ptr = self.getSectionPtr(.{
6129 .seg = data_segment_cmd_index,
6130 .sect = la_symbol_ptr_section_index,
6131 });
65526132 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6553 for (self.stubs_table.keys()) |key| {
6554 const resolv = self.symbol_resolver.get(key).?;
6555 switch (resolv.where) {
6556 .global => try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL),
6557 .undef => try writer.writeIntLittle(u32, dysymtab.iundefsym + undefs_table.get(resolv.where_index).?),
6558 }
6133 for (self.stubs_table.keys()) |target| {
6134 const sym = self.getSymbol(target);
6135 assert(sym.undf());
6136 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);
65596137 }
65606138 }
65616139
......@@ -6572,14 +6150,15 @@ fn writeStringTable(self: *MachO) !void {
65726150 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
65736151 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
65746152 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;
65766155 symtab.stroff = @intCast(u32, stroff);
65776156 symtab.strsize = @intCast(u32, strsize);
65786157 seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff;
65796158
65806159 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
65846163 self.load_commands_dirty = true;
65856164}
......@@ -6737,42 +6316,81 @@ pub fn makeStaticString(bytes: []const u8) [16]u8 {
67376316 return buf;
67386317}
67396318
6740pub fn makeString(self: *MachO, string: []const u8) !u32 {
6741 const gop = try self.strtab_dir.getOrPutContextAdapted(self.base.allocator, @as([]const u8, string), StringIndexAdapter{
6742 .bytes = &self.strtab,
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);
6319pub fn getSectionOrdinal(self: *MachO, match: MatchingSection) u8 {
6320 return @intCast(u8, self.section_ordinals.getIndex(match).?) + 1;
6321}
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);
6758 self.strtab.appendAssumeCapacity(0);
6329pub fn getSegmentPtr(self: *MachO, match: MatchingSection) *macho.SegmentCommand {
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];
67636342}
67646343
6765pub fn getString(self: MachO, off: u32) []const u8 {
6766 assert(off < self.strtab.items.len);
6767 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.items.ptr + off), 0);
6344pub fn getSection(self: *MachO, match: MatchingSection) macho.section_64 {
6345 return self.getSectionPtr(match).*;
67686346}
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);
67716350 if (!sym.sect()) return false;
67726351 if (sym.ext()) return false;
6352 const sym_name = self.getSymbolName(sym_with_loc);
67736353 return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L");
67746354}
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
67766394pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
67776395 if (!@hasDecl(@TypeOf(predicate), "predicate"))
67786396 @compileError("Predicate is required to define fn predicate(@This(), T) bool");
......@@ -6835,7 +6453,7 @@ fn snapshotState(self: *MachO) !void {
68356453 const arena = arena_allocator.allocator();
68366454
68376455 const out_file = try emit.directory.handle.createFile("snapshots.json", .{
6838 .truncate = self.cold_start,
6456 .truncate = false,
68396457 .read = true,
68406458 });
68416459 defer out_file.close();
......@@ -6855,8 +6473,7 @@ fn snapshotState(self: *MachO) !void {
68556473 var nodes = std.ArrayList(Snapshot.Node).init(arena);
68566474
68576475 for (self.section_ordinals.keys()) |key| {
6858 const seg = self.load_commands.items[key.seg].segment;
6859 const sect = seg.sections.items[key.sect];
6476 const sect = self.getSection(key);
68606477 const sect_name = try std.fmt.allocPrint(arena, "{s},{s}", .{ sect.segName(), sect.sectName() });
68616478 try nodes.append(.{
68626479 .address = sect.addr,
......@@ -6878,10 +6495,10 @@ fn snapshotState(self: *MachO) !void {
68786495 }
68796496
68806497 while (true) {
6881 const atom_sym = self.locals.items[atom.local_sym_index];
6498 const atom_sym = self.locals.items[atom.sym_index];
68826499 const should_skip_atom: bool = blk: {
68836500 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;
68856502 }
68866503 if (mem.eql(u8, self.getString(atom_sym.n_strx), "___dso_handle")) break :blk true;
68876504 break :blk false;
......@@ -6906,7 +6523,7 @@ fn snapshotState(self: *MachO) !void {
69066523 var aliases = std.ArrayList([]const u8).init(arena);
69076524 for (atom.contained.items) |sym_off| {
69086525 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));
69106527 }
69116528 }
69126529 node.payload.aliases = aliases.toOwnedSlice();
......@@ -6916,7 +6533,7 @@ fn snapshotState(self: *MachO) !void {
69166533 for (atom.relocs.items) |rel| {
69176534 const arch = self.base.options.target.cpu.arch;
69186535 const source_addr = blk: {
6919 const sym = self.locals.items[atom.local_sym_index];
6536 const sym = self.locals.items[atom.sym_index];
69206537 break :blk sym.n_value + rel.offset;
69216538 };
69226539 const target_addr = blk: {
......@@ -6937,14 +6554,14 @@ fn snapshotState(self: *MachO) !void {
69376554 if (is_via_got) {
69386555 const got_index = self.got_entries_table.get(rel.target) orelse break :blk 0;
69396556 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;
69416558 }
69426559
69436560 switch (rel.target) {
69446561 .local => |sym_index| {
69456562 const sym = self.locals.items[sym_index];
69466563 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];
69486565 const match = self.section_ordinals.keys()[source_sym.n_sect - 1];
69496566 const match_seg = self.load_commands.items[match.seg].segment;
69506567 const match_sect = match_seg.sections.items[match.sect];
......@@ -6970,7 +6587,7 @@ fn snapshotState(self: *MachO) !void {
69706587 .undef => {
69716588 if (self.stubs_table.get(n_strx)) |stub_index| {
69726589 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;
69746591 }
69756592 break :blk 0;
69766593 },
......@@ -6998,7 +6615,7 @@ fn snapshotState(self: *MachO) !void {
69986615 var last_rel: usize = 0;
69996616 while (next_i < atom.contained.items.len) : (next_i += 1) {
70006617 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];
70026619 const cont_sym_name = self.getString(cont_sym.n_strx);
70036620 var contained_node = Snapshot.Node{
70046621 .address = cont_sym.n_value,
......@@ -7013,7 +6630,7 @@ fn snapshotState(self: *MachO) !void {
70136630 var inner_aliases = std.ArrayList([]const u8).init(arena);
70146631 while (true) {
70156632 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];
70176634 if (next_sym.n_value != cont_sym.n_value) break;
70186635 const next_sym_name = self.getString(next_sym.n_strx);
70196636 if (self.symbol_resolver.contains(next_sym.n_strx)) {
......@@ -7025,7 +6642,7 @@ fn snapshotState(self: *MachO) !void {
70256642 }
70266643
70276644 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_value
6645 self.locals.items[atom.contained.items[next_i + 1].sym_index].n_value - cont_sym.n_value
70296646 else
70306647 atom_sym.n_value + atom.size - cont_sym.n_value;
70316648
......@@ -7072,75 +6689,117 @@ fn snapshotState(self: *MachO) !void {
70726689 try writer.writeByte(']');
70736690}
70746691
7075fn logSymtab(self: MachO) void {
7076 log.warn("locals:", .{});
7077 for (self.locals.items) |sym, id| {
7078 log.warn(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.n_strx), sym.n_value, sym.n_sect });
6692pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
6693 mem.set(u8, buf, '_');
6694 if (sym.sect()) {
6695 buf[0] = 's';
70796696 }
7080
7081 log.warn("globals:", .{});
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 });
6697 if (sym.ext()) {
6698 buf[1] = 'e';
70846699 }
7085
7086 log.warn("undefs:", .{});
7087 for (self.undefs.items) |sym, id| {
7088 log.warn(" {d}: {s}: in {d}", .{ id, self.getString(sym.n_strx), sym.n_desc });
6700 if (sym.tentative()) {
6701 buf[2] = 't';
70896702 }
6703 if (sym.undf()) {
6704 buf[3] = 'u';
6705 }
6706 return buf[0..];
6707}
70906708
7091 {
7092 log.warn("resolver:", .{});
7093 var it = self.symbol_resolver.iterator();
7094 while (it.next()) |entry| {
7095 log.warn(" {s} => {}", .{ self.getString(entry.key_ptr.*), entry.value_ptr.* });
6709fn logSymtab(self: *MachO) void {
6710 var buf: [4]u8 = undefined;
6711
6712 log.debug("symtab:", .{});
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 });
70966729 }
70976730 }
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:", .{});
71006755 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);
71026758 const atom = self.got_entries.items[value].atom;
7103 const n_value = self.locals.items[atom.local_sym_index].n_value;
7104 switch (key) {
7105 .local => |ndx| log.warn(" {d}: @{x}", .{ ndx, n_value }),
7106 .global => |n_strx| log.warn(" {s}: @{x}", .{ self.getString(n_strx), n_value }),
6759 const atom_sym = atom.getSymbol(self);
6760
6761 if (target_sym.undf()) {
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 });
71076770 }
71086771 }
71096772
7110 log.warn("__thread_ptrs entries:", .{});
6773 log.debug("__thread_ptrs entries:", .{});
71116774 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);
71136777 const atom = self.tlv_ptr_entries.items[value].atom;
7114 const n_value = self.locals.items[atom.local_sym_index].n_value;
7115 assert(key == .global);
7116 log.warn(" {s}: @{x}", .{ self.getString(key.global), n_value });
6778 const atom_sym = atom.getSymbol(self);
6779 assert(target_sym.undf());
6780 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });
71176781 }
71186782
7119 log.warn("stubs:", .{});
7120 for (self.stubs_table.keys()) |key| {
7121 const value = self.stubs_table.get(key).?;
7122 const atom = self.stubs.items[value];
7123 const sym = self.locals.items[atom.local_sym_index];
7124 log.warn(" {s}: @{x}", .{ self.getString(key), sym.n_value });
6783 log.debug("stubs entries:", .{});
6784 for (self.stubs_table.values()) |value| {
6785 const target = self.stubs.items[value].target;
6786 const target_sym = self.getSymbol(target);
6787 const atom = self.stubs.items[value].atom;
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) });
71256791 }
71266792}
71276793
7128fn logSectionOrdinals(self: MachO) void {
6794fn logSectionOrdinals(self: *MachO) void {
71296795 for (self.section_ordinals.keys()) |match, i| {
7130 const seg = self.load_commands.items[match.seg].segment;
7131 const sect = seg.sections.items[match.sect];
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 });
6796 const sect = self.getSection(match);
6797 log.debug("sect({d}, '{s},{s}')", .{ i + 1, sect.segName(), sect.sectName() });
71396798 }
71406799}
71416800
7142fn logAtoms(self: MachO) void {
7143 log.warn("atoms:", .{});
6801fn logAtoms(self: *MachO) void {
6802 log.debug("atoms:", .{});
71446803 var it = self.atoms.iterator();
71456804 while (it.next()) |entry| {
71466805 const match = entry.key_ptr.*;
......@@ -7150,9 +6809,8 @@ fn logAtoms(self: MachO) void {
71506809 atom = prev;
71516810 }
71526811
7153 const seg = self.load_commands.items[match.seg].segment;
7154 const sect = seg.sections.items[match.sect];
7155 log.warn("{s},{s}", .{ sect.segName(), sect.sectName() });
6812 const sect = self.getSection(match);
6813 log.debug("{s},{s}", .{ sect.segName(), sect.sectName() });
71566814
71576815 while (true) {
71586816 self.logAtom(atom);
......@@ -7164,16 +6822,28 @@ fn logAtoms(self: MachO) void {
71646822 }
71656823}
71666824
7167fn logAtom(self: MachO, atom: *const Atom) void {
7168 const sym = self.locals.items[atom.local_sym_index];
7169 log.warn(" ATOM(%{d}) @ {x}", .{ atom.local_sym_index, sym.n_value });
6825pub fn logAtom(self: *MachO, atom: *const Atom) void {
6826 const sym = atom.getSymbol(self);
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
71716835 for (atom.contained.items) |sym_off| {
7172 const inner_sym = self.locals.items[sym_off.local_sym_index];
7173 log.warn(" %{d} ('{s}') @ {x}", .{
7174 sym_off.local_sym_index,
7175 self.getString(inner_sym.n_strx),
6836 const inner_sym = self.getSymbol(.{
6837 .sym_index = sym_off.sym_index,
6838 .file = atom.file,
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,
71766844 inner_sym.n_value,
6845 sym_off.offset,
6846 atom.file,
71776847 });
71786848 }
71796849}
src/link/MachO/Atom.zig+272-369
......@@ -16,7 +16,7 @@ const Arch = std.Target.Cpu.Arch;
1616const Dwarf = @import("../Dwarf.zig");
1717const MachO = @import("../MachO.zig");
1818const Object = @import("Object.zig");
19const StringIndexAdapter = std.hash_map.StringIndexAdapter;
19const SymbolWithLoc = MachO.SymbolWithLoc;
2020
2121/// Each decl always gets a local symbol with the fully qualified name.
2222/// The vaddr and size are found here directly.
......@@ -24,7 +24,10 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
2424/// the symbol references, and adding that to the file offset of the section.
2525/// If this field is 0, it means the codegen size = 0 and there is no symbol or
2626/// offset table entry.
27local_sym_index: u32,
27sym_index: u32,
28
29/// null means symbol defined by Zig source.
30file: ?u32,
2831
2932/// List of symbols contained within this atom
3033contained: std.ArrayListUnmanaged(SymbolAtOffset) = .{},
......@@ -45,15 +48,15 @@ alignment: u32,
4548relocs: std.ArrayListUnmanaged(Relocation) = .{},
4649
4750/// 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.
4952rebases: std.ArrayListUnmanaged(u64) = .{},
5053
5154/// List of offsets contained within this atom that will be dynamically bound
5255/// 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).
5457bindings: std.ArrayListUnmanaged(Binding) = .{},
5558
56/// List of lazy bindings
59/// List of lazy bindings (cf bindings above).
5760lazy_bindings: std.ArrayListUnmanaged(Binding) = .{},
5861
5962/// List of data-in-code entries. This is currently specific to x86_64 only.
......@@ -68,12 +71,12 @@ dbg_info_atom: Dwarf.Atom,
6871dirty: bool = true,
6972
7073pub const Binding = struct {
71 n_strx: u32,
74 global_index: u32,
7275 offset: u64,
7376};
7477
7578pub const SymbolAtOffset = struct {
76 local_sym_index: u32,
79 sym_index: u32,
7780 offset: u64,
7881 stab: ?Stab = null,
7982};
......@@ -83,11 +86,14 @@ pub const Stab = union(enum) {
8386 static,
8487 global,
8588
86 pub fn asNlists(stab: Stab, local_sym_index: u32, macho_file: anytype) ![]macho.nlist_64 {
87 var nlists = std.ArrayList(macho.nlist_64).init(macho_file.base.allocator);
89 pub fn asNlists(stab: Stab, sym_loc: SymbolWithLoc, macho_file: *MachO) ![]macho.nlist_64 {
90 const gpa = macho_file.base.allocator;
91
92 var nlists = std.ArrayList(macho.nlist_64).init(gpa);
8893 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);
9197 switch (stab) {
9298 .function => |size| {
9399 try nlists.ensureUnusedCapacity(4);
......@@ -99,7 +105,7 @@ pub const Stab = union(enum) {
99105 .n_value = sym.n_value,
100106 });
101107 nlists.appendAssumeCapacity(.{
102 .n_strx = sym.n_strx,
108 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
103109 .n_type = macho.N_FUN,
104110 .n_sect = sym.n_sect,
105111 .n_desc = 0,
......@@ -122,7 +128,7 @@ pub const Stab = union(enum) {
122128 },
123129 .global => {
124130 try nlists.append(.{
125 .n_strx = sym.n_strx,
131 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
126132 .n_type = macho.N_GSYM,
127133 .n_sect = 0,
128134 .n_desc = 0,
......@@ -131,7 +137,7 @@ pub const Stab = union(enum) {
131137 },
132138 .static => {
133139 try nlists.append(.{
134 .n_strx = sym.n_strx,
140 .n_strx = try macho_file.strtab.insert(gpa, sym_name),
135141 .n_type = macho.N_STSYM,
136142 .n_sect = sym.n_sect,
137143 .n_desc = 0,
......@@ -145,30 +151,66 @@ pub const Stab = union(enum) {
145151};
146152
147153pub const Relocation = struct {
148 pub const Target = union(enum) {
149 local: u32,
150 global: u32,
151 };
152
153154 /// Offset within the atom's code buffer.
154155 /// Note relocation size can be inferred by relocation's kind.
155156 offset: u32,
156157
157 target: Target,
158 target: MachO.SymbolWithLoc,
158159
159160 addend: i64,
160161
161 subtractor: ?u32,
162 subtractor: ?MachO.SymbolWithLoc,
162163
163164 pcrel: bool,
164165
165166 length: u2,
166167
167168 @"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 }
168209};
169210
170211pub const empty = Atom{
171 .local_sym_index = 0,
212 .sym_index = 0,
213 .file = null,
172214 .size = 0,
173215 .alignment = 0,
174216 .prev = null,
......@@ -196,13 +238,45 @@ pub fn clearRetainingCapacity(self: *Atom) void {
196238 self.code.clearRetainingCapacity();
197239}
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
199273/// Returns how much room there is to grow in virtual address space.
200274/// File offset relocation happens transparently, so it is not included in
201275/// this calculation.
202pub fn capacity(self: Atom, macho_file: MachO) u64 {
203 const self_sym = macho_file.locals.items[self.local_sym_index];
276pub fn capacity(self: Atom, macho_file: *MachO) u64 {
277 const self_sym = self.getSymbol(macho_file);
204278 if (self.next) |next| {
205 const next_sym = macho_file.locals.items[next.local_sym_index];
279 const next_sym = next.getSymbol(macho_file);
206280 return next_sym.n_value - self_sym.n_value;
207281 } else {
208282 // We are the last atom.
......@@ -211,11 +285,11 @@ pub fn capacity(self: Atom, macho_file: MachO) u64 {
211285 }
212286}
213287
214pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
288pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
215289 // No need to keep a free list node for the last atom.
216290 const next = self.next orelse return false;
217 const self_sym = macho_file.locals.items[self.local_sym_index];
218 const next_sym = macho_file.locals.items[next.local_sym_index];
291 const self_sym = self.getSymbol(macho_file);
292 const next_sym = next.getSymbol(macho_file);
219293 const cap = next_sym.n_value - self_sym.n_value;
220294 const ideal_cap = MachO.padToIdeal(self.size);
221295 if (cap <= ideal_cap) return false;
......@@ -224,20 +298,20 @@ pub fn freeListEligible(self: Atom, macho_file: MachO) bool {
224298}
225299
226300const RelocContext = struct {
301 macho_file: *MachO,
227302 base_addr: u64 = 0,
228303 base_offset: i32 = 0,
229 allocator: Allocator,
230 object: *Object,
231 macho_file: *MachO,
232304};
233305
234306pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context: RelocContext) !void {
235307 const tracy = trace(@src());
236308 defer tracy.end();
237309
310 const gpa = context.macho_file.base.allocator;
311
238312 const arch = context.macho_file.base.options.target.cpu.arch;
239313 var addend: i64 = 0;
240 var subtractor: ?u32 = null;
314 var subtractor: ?SymbolWithLoc = null;
241315
242316 for (relocs) |rel, i| {
243317 blk: {
......@@ -274,20 +348,16 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
274348 }
275349
276350 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);
278356 if (sym.sect() and !sym.ext()) {
279 subtractor = context.object.symbol_mapping.get(rel.r_symbolnum).?;
357 subtractor = sym_loc;
280358 } else {
281 const sym_name = context.object.getString(sym.n_strx);
282 const n_strx = context.macho_file.strtab_dir.getKeyAdapted(
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;
359 const sym_name = context.macho_file.getSymbolName(sym_loc);
360 subtractor = context.macho_file.globals.get(sym_name).?;
291361 }
292362 // Verify that *_SUBTRACTOR is followed by *_UNSIGNED.
293363 if (relocs.len <= i + 1) {
......@@ -318,43 +388,40 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
318388 continue;
319389 }
320390
391 const object = &context.macho_file.objects.items[self.file.?];
321392 const target = target: {
322393 if (rel.r_extern == 0) {
323394 const sect_id = @intCast(u16, rel.r_symbolnum - 1);
324 const local_sym_index = context.object.sections_as_symbols.get(sect_id) orelse blk: {
325 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
326 const sect = seg.sections.items[sect_id];
395 const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
396 const sect = object.getSection(sect_id);
327397 const match = (try context.macho_file.getMatchingSection(sect)) orelse
328398 unreachable;
329 const local_sym_index = @intCast(u32, context.macho_file.locals.items.len);
330 try context.macho_file.locals.append(context.allocator, .{
399 const sym_index = @intCast(u32, object.symtab.items.len);
400 try object.symtab.append(gpa, .{
331401 .n_strx = 0,
332402 .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),
334404 .n_desc = 0,
335405 .n_value = 0,
336406 });
337 try context.object.sections_as_symbols.putNoClobber(context.allocator, sect_id, local_sym_index);
338 break :blk local_sym_index;
407 try object.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
408 break :blk sym_index;
339409 };
340 break :target Relocation.Target{ .local = local_sym_index };
410 break :target MachO.SymbolWithLoc{ .sym_index = sym_index, .file = self.file };
341411 }
342412
343 const sym = context.object.symtab[rel.r_symbolnum];
344 const sym_name = context.object.getString(sym.n_strx);
413 const sym_loc = MachO.SymbolWithLoc{
414 .sym_index = rel.r_symbolnum,
415 .file = self.file,
416 };
417 const sym = context.macho_file.getSymbol(sym_loc);
345418
346419 if (sym.sect() and !sym.ext()) {
347 const sym_index = context.object.symbol_mapping.get(rel.r_symbolnum) orelse unreachable;
348 break :target Relocation.Target{ .local = sym_index };
420 break :target sym_loc;
421 } else {
422 const sym_name = context.macho_file.getSymbolName(sym_loc);
423 break :target context.macho_file.globals.get(sym_name).?;
349424 }
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 };
358425 };
359426 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:
378445 else
379446 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
380447 if (rel.r_extern == 0) {
381 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
382 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
448 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
383449 addend -= @intCast(i64, target_sect_base_addr);
384450 }
385451 try self.addPtrBindingOrRebase(rel, target, context);
......@@ -387,9 +453,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
387453 .ARM64_RELOC_TLVP_LOAD_PAGE21,
388454 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12,
389455 => {
390 if (target == .global) {
391 try addTlvPtrEntry(target, context);
392 }
456 try addTlvPtrEntry(target, context);
393457 },
394458 else => {},
395459 }
......@@ -413,8 +477,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
413477 else
414478 mem.readIntLittle(i32, self.code.items[offset..][0..4]);
415479 if (rel.r_extern == 0) {
416 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
417 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
480 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
418481 addend -= @intCast(i64, target_sect_base_addr);
419482 }
420483 try self.addPtrBindingOrRebase(rel, target, context);
......@@ -435,16 +498,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
435498 if (rel.r_extern == 0) {
436499 // Note for the future self: when r_extern == 0, we should subtract correction from the
437500 // addend.
438 const seg = context.object.load_commands.items[context.object.segment_cmd_index.?].segment;
439 const target_sect_base_addr = seg.sections.items[rel.r_symbolnum - 1].addr;
501 const target_sect_base_addr = object.getSection(@intCast(u16, rel.r_symbolnum - 1)).addr;
440502 addend += @intCast(i64, context.base_addr + offset + 4) -
441503 @intCast(i64, target_sect_base_addr);
442504 }
443505 },
444506 .X86_64_RELOC_TLV => {
445 if (target == .global) {
446 try addTlvPtrEntry(target, context);
447 }
507 try addTlvPtrEntry(target, context);
448508 },
449509 else => {},
450510 }
......@@ -452,7 +512,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
452512 else => unreachable,
453513 }
454514
455 try self.relocs.append(context.allocator, .{
515 try self.relocs.append(gpa, .{
456516 .offset = offset,
457517 .target = target,
458518 .addend = addend,
......@@ -470,338 +530,181 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
470530fn addPtrBindingOrRebase(
471531 self: *Atom,
472532 rel: macho.relocation_info,
473 target: Relocation.Target,
533 target: MachO.SymbolWithLoc,
474534 context: RelocContext,
475535) !void {
476 switch (target) {
477 .global => |n_strx| {
478 try self.bindings.append(context.allocator, .{
479 .n_strx = n_strx,
480 .offset = @intCast(u32, rel.r_address - context.base_offset),
481 });
482 },
483 .local => {
484 const source_sym = context.macho_file.locals.items[self.local_sym_index];
485 const match = context.macho_file.section_ordinals.keys()[source_sym.n_sect - 1];
486 const seg = context.macho_file.load_commands.items[match.seg].segment;
487 const sect = seg.sections.items[match.sect];
488 const sect_type = sect.type_();
489
490 const should_rebase = rebase: {
491 if (rel.r_length != 3) break :rebase false;
492
493 // TODO actually, a check similar to what dyld is doing, that is, verifying
494 // that the segment is writable should be enough here.
495 const is_right_segment = blk: {
496 if (context.macho_file.data_segment_cmd_index) |idx| {
497 if (match.seg == idx) {
498 break :blk true;
499 }
536 const gpa = context.macho_file.base.allocator;
537 const sym = context.macho_file.getSymbol(target);
538 if (sym.undf()) {
539 const sym_name = context.macho_file.getSymbolName(target);
540 const global_index = @intCast(u32, context.macho_file.globals.getIndex(sym_name).?);
541 try self.bindings.append(gpa, .{
542 .global_index = global_index,
543 .offset = @intCast(u32, rel.r_address - context.base_offset),
544 });
545 } else {
546 const source_sym = self.getSymbol(context.macho_file);
547 const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
548 const sect = context.macho_file.getSection(match);
549 const sect_type = sect.type_();
550
551 const should_rebase = rebase: {
552 if (rel.r_length != 3) break :rebase false;
553
554 // TODO actually, a check similar to what dyld is doing, that is, verifying
555 // that the segment is writable should be enough here.
556 const is_right_segment = blk: {
557 if (context.macho_file.data_segment_cmd_index) |idx| {
558 if (match.seg == idx) {
559 break :blk true;
500560 }
501 if (context.macho_file.data_const_segment_cmd_index) |idx| {
502 if (match.seg == idx) {
503 break :blk true;
504 }
561 }
562 if (context.macho_file.data_const_segment_cmd_index) |idx| {
563 if (match.seg == idx) {
564 break :blk true;
505565 }
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;
516566 }
517
518 break :rebase true;
567 break :blk false;
519568 };
520569
521 if (should_rebase) {
522 try self.rebases.append(
523 context.allocator,
524 @intCast(u32, rel.r_address - context.base_offset),
525 );
570 if (!is_right_segment) break :rebase false;
571 if (sect_type != macho.S_LITERAL_POINTERS and
572 sect_type != macho.S_REGULAR and
573 sect_type != macho.S_MOD_INIT_FUNC_POINTERS and
574 sect_type != macho.S_MOD_TERM_FUNC_POINTERS)
575 {
576 break :rebase false;
526577 }
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 }
528585 }
529586}
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;
532591 if (context.macho_file.tlv_ptr_entries_table.contains(target)) return;
533592
534593 const index = try context.macho_file.allocateTlvPtrEntry(target);
535594 const atom = try context.macho_file.createTlvPtrAtom(target);
536595 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 }
553596}
554597
555fn addGotEntry(target: Relocation.Target, context: RelocContext) !void {
598fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
556599 if (context.macho_file.got_entries_table.contains(target)) return;
557600
558601 const index = try context.macho_file.allocateGotEntry(target);
559602 const atom = try context.macho_file.createGotAtom(target);
560603 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 }
576604}
577605
578fn addStub(target: Relocation.Target, context: RelocContext) !void {
579 if (target != .global) return;
580 if (context.macho_file.stubs_table.contains(target.global)) return;
581 // If the symbol has been resolved as defined globally elsewhere (in a different translation unit),
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}
606fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
607 const target_sym = context.macho_file.getSymbol(target);
608 if (!target_sym.undf()) return;
609 if (context.macho_file.stubs_table.contains(target)) return;
645610
646pub fn getTargetAtom(rel: Relocation, macho_file: *MachO) !?*Atom {
647 const is_via_got = got: {
648 switch (macho_file.base.options.target.cpu.arch) {
649 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
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 }
611 const stub_index = try context.macho_file.allocateStubEntry(target);
612 const stub_helper_atom = try context.macho_file.createStubHelperAtom();
613 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
614 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
676615
677 switch (rel.target) {
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 }
616 context.macho_file.stubs.items[stub_index].atom = stub_atom;
698617}
699618
700619pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
701620 const tracy = trace(@src());
702621 defer tracy.end();
703622
623 log.debug("ATOM(%{d}, '{s}')", .{ self.sym_index, self.getName(macho_file) });
624
704625 for (self.relocs.items) |rel| {
705 log.debug("relocating {}", .{rel});
706626 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
707647 const source_addr = blk: {
708 const sym = macho_file.locals.items[self.local_sym_index];
709 break :blk sym.n_value + rel.offset;
648 const source_sym = self.getSymbol(macho_file);
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;
710656 };
711 var is_via_thread_ptrs: bool = false;
712657 const target_addr = blk: {
713 const is_via_got = got: {
714 switch (arch) {
715 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
716 .ARM64_RELOC_GOT_LOAD_PAGE21,
717 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
718 .ARM64_RELOC_POINTER_TO_GOT,
719 => true,
720 else => false,
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,
658 const target_atom = (try rel.getTargetAtom(macho_file)) orelse {
659 // If there is no atom for target, we still need to check for special, atom-less
660 // symbols such as `___dso_handle`.
661 const target_name = macho_file.getSymbolName(rel.target);
662 if (macho_file.globals.contains(target_name)) {
663 const atomless_sym = macho_file.getSymbol(rel.target);
664 log.debug(" | atomless target '{s}'", .{target_name});
665 break :blk atomless_sym.n_value;
727666 }
667 log.debug(" | undef target '{s}'", .{target_name});
668 break :blk 0;
728669 };
729
730 if (is_via_got) {
731 const got_index = macho_file.got_entries_table.get(rel.target) orelse {
732 log.err("expected GOT entry for symbol", .{});
733 switch (rel.target) {
734 .local => |sym_index| log.err(" local @{d}", .{sym_index}),
735 .global => |n_strx| log.err(" global @'{s}'", .{macho_file.getString(n_strx)}),
670 log.debug(" | target ATOM(%{d}, '{s}') in object({d})", .{
671 target_atom.sym_index,
672 target_atom.getName(macho_file),
673 target_atom.file,
674 });
675 // If `rel.target` is contained within the target atom, pull its address value.
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;
736696 }
737 log.err(" this is an internal linker error", .{});
738 return error.FailedToResolveRelocationTarget;
739697 };
740 const atom = macho_file.got_entries.items[got_index].atom;
741 break :blk macho_file.locals.items[atom.local_sym_index].n_value;
742 }
743
744 switch (rel.target) {
745 .local => |sym_index| {
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 }
698 break :base_address macho_file.getSection(.{
699 .seg = macho_file.data_segment_cmd_index.?,
700 .sect = sect_id,
701 }).addr;
702 } else 0;
703 break :blk target_sym.n_value - base_address;
801704 };
802705
803 log.debug(" | source_addr = 0x{x}", .{source_addr});
804 log.debug(" | target_addr = 0x{x}", .{target_addr});
706 log.debug(" | source_addr = 0x{x}", .{source_addr});
707 log.debug(" | target_addr = 0x{x}", .{target_addr});
805708
806709 switch (arch) {
807710 .aarch64 => {
......@@ -933,7 +836,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
933836 }
934837 };
935838 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: {
937840 const offset = try math.divExact(u12, narrowed, 8);
938841 break :blk aarch64.Instruction{
939842 .load_store_register = .{
......@@ -966,7 +869,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
966869 .ARM64_RELOC_UNSIGNED => {
967870 const result = blk: {
968871 if (rel.subtractor) |subtractor| {
969 const sym = macho_file.locals.items[subtractor];
872 const sym = macho_file.getSymbol(subtractor);
970873 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
971874 } else {
972875 break :blk @intCast(i64, target_addr) + rel.addend;
......@@ -1004,7 +907,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1004907 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
1005908 },
1006909 .X86_64_RELOC_TLV => {
1007 if (!is_via_thread_ptrs) {
910 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {
1008911 // We need to rewrite the opcode from movq to leaq.
1009912 self.code.items[rel.offset - 2] = 0x8d;
1010913 }
......@@ -1036,7 +939,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
1036939 .X86_64_RELOC_UNSIGNED => {
1037940 const result = blk: {
1038941 if (rel.subtractor) |subtractor| {
1039 const sym = macho_file.locals.items[subtractor];
942 const sym = macho_file.getSymbol(subtractor);
1040943 break :blk @intCast(i64, target_addr) - @intCast(i64, sym.n_value) + rel.addend;
1041944 } else {
1042945 break :blk @intCast(i64, target_addr) + rel.addend;
src/link/MachO/DebugSymbols.zig+45-14
......@@ -17,6 +17,7 @@ const Allocator = mem.Allocator;
1717const Dwarf = @import("../Dwarf.zig");
1818const MachO = @import("../MachO.zig");
1919const Module = @import("../../Module.zig");
20const StringTable = @import("../strtab.zig").StringTable;
2021const TextBlock = MachO.TextBlock;
2122const Type = @import("../../type.zig").Type;
2223
......@@ -59,6 +60,8 @@ debug_aranges_section_dirty: bool = false,
5960debug_info_header_dirty: bool = false,
6061debug_line_header_dirty: bool = false,
6162
63strtab: StringTable(.link) = .{},
64
6265relocs: std.ArrayListUnmanaged(Reloc) = .{},
6366
6467pub const Reloc = struct {
......@@ -93,6 +96,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
9396 .strsize = 0,
9497 },
9598 });
99 try self.strtab.buffer.append(allocator, 0);
96100 self.load_commands_dirty = true;
97101 }
98102
......@@ -269,22 +273,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
269273
270274 for (self.relocs.items) |*reloc| {
271275 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 }),
273277 .got_load => blk: {
274 const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?;
275 const got_entry = self.base.got_entries.items[got_index];
276 break :blk self.base.locals.items[got_entry.atom.local_sym_index];
278 const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?;
279 const got_atom = self.base.got_entries.items[got_index].atom;
280 break :blk got_atom.getSymbol(self.base);
277281 },
278282 };
279283 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 };
281293 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
282294 const sect = &seg.sections.items[self.debug_info_section_index.?];
283295 const file_offset = sect.offset + reloc.offset;
284296 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
285297 reloc.target,
286298 sym.n_value,
287 self.base.getString(sym.n_strx),
299 sym_name,
288300 file_offset,
289301 });
290302 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);
......@@ -367,6 +379,7 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
367379 }
368380 self.load_commands.deinit(allocator);
369381 self.dwarf.deinit();
382 self.strtab.deinit(allocator);
370383 self.relocs.deinit(allocator);
371384}
372385
......@@ -582,21 +595,39 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
582595 const tracy = trace(@src());
583596 defer tracy.end();
584597
598 const gpa = self.base.base.allocator;
585599 const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
586600 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
587601 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);
590604 defer locals.deinit();
591605
592 for (self.base.locals.items) |sym| {
593 if (sym.n_strx == 0) continue;
594 if (self.base.symbol_resolver.get(sym.n_strx)) |_| continue;
595 try locals.append(sym);
606 for (self.base.locals.items) |sym, sym_id| {
607 if (sym.n_strx == 0) continue; // no name, skip
608 if (sym.n_desc == MachO.N_DESC_GCED) continue; // GCed, skip
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);
596627 }
597628
598629 const nlocals = locals.items.len;
599 const nexports = self.base.globals.items.len;
630 const nexports = exports.items.len;
600631 const locals_off = symtab.symoff;
601632 const locals_size = nlocals * @sizeOf(macho.nlist_64);
602633 const exports_off = locals_off + locals_size;
......@@ -641,7 +672,7 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
641672 try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
642673
643674 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
646677 self.load_commands_dirty = true;
647678}
......@@ -655,7 +686,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
655686 const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
656687 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));
659690 symtab.strsize = @intCast(u32, needed_size);
660691
661692 if (symtab_size + needed_size > seg.inner.filesize) {
......@@ -692,7 +723,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
692723
693724 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
697728 self.load_commands_dirty = true;
698729}
src/link/MachO/Object.zig+210-227
......@@ -47,7 +47,7 @@ dwarf_debug_line_index: ?u16 = null,
4747dwarf_debug_line_str_index: ?u16 = null,
4848dwarf_debug_ranges_index: ?u16 = null,
4949
50symtab: []const macho.nlist_64 = &.{},
50symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
5151strtab: []const u8 = &.{},
5252data_in_code_entries: []const macho.data_in_code_entry = &.{},
5353
......@@ -57,17 +57,13 @@ tu_name: ?[]const u8 = null,
5757tu_comp_dir: ?[]const u8 = null,
5858mtime: ?u64 = null,
5959
60contained_atoms: std.ArrayListUnmanaged(*Atom) = .{},
61start_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
62end_atoms: std.AutoHashMapUnmanaged(MachO.MatchingSection, *Atom) = .{},
6360sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
6461
65// TODO symbol mapping and its inverse can probably be simple arrays
66// instead of hash maps.
67symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
68reverse_symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{},
62/// List of atoms that map to the symbols parsed from this object file.
63managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
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
7268const DebugInfo = struct {
7369 inner: dwarf.DwarfInfo,
......@@ -135,97 +131,25 @@ const DebugInfo = struct {
135131 }
136132};
137133
138pub fn deinit(self: *Object, allocator: Allocator) void {
134pub fn deinit(self: *Object, gpa: Allocator) void {
139135 for (self.load_commands.items) |*lc| {
140 lc.deinit(allocator);
136 lc.deinit(gpa);
141137 }
142 self.load_commands.deinit(allocator);
143 allocator.free(self.contents);
144 self.sections_as_symbols.deinit(allocator);
145 self.symbol_mapping.deinit(allocator);
146 self.reverse_symbol_mapping.deinit(allocator);
147 allocator.free(self.name);
148
149 self.contained_atoms.deinit(allocator);
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 }
138 self.load_commands.deinit(gpa);
139 gpa.free(self.contents);
140 self.sections_as_symbols.deinit(gpa);
141 self.atom_by_index_table.deinit(gpa);
142
143 for (self.managed_atoms.items) |atom| {
144 atom.deinit(gpa);
145 gpa.destroy(atom);
195146 }
147 self.managed_atoms.deinit(gpa);
196148
197 self.freeAtoms(macho_file);
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 }
149 gpa.free(self.name);
217150
218 if (first_atom.prev) |prev| {
219 prev.next = last_atom.next;
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 }
151 if (self.debug_info) |*db| {
152 db.deinit(gpa);
229153 }
230154}
231155
......@@ -327,24 +251,40 @@ pub fn parse(self: *Object, allocator: Allocator, target: std.Target) !void {
327251 self.load_commands.appendAssumeCapacity(cmd);
328252 }
329253
330 self.parseSymtab();
254 try self.parseSymtab(allocator);
331255 self.parseDataInCode();
332256 try self.parseDebugInfo(allocator);
333257}
334258
335const NlistWithIndex = struct {
336 nlist: macho.nlist_64,
259const Context = struct {
260 symtab: []const macho.nlist_64,
261 strtab: []const u8,
262};
263
264const SymbolAtIndex = struct {
337265 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 {
340278 // We sort by type: defined < undefined, and
341279 // afterwards by address in each group. Normally, dysymtab should
342280 // be enough to guarantee the sort, but turns out not every compiler
343281 // is kind enough to specify the symbols in the correct order.
344 if (lhs.nlist.sect()) {
345 if (rhs.nlist.sect()) {
282 const lhs = lhs_index.getSymbol(ctx);
283 const rhs = rhs_index.getSymbol(ctx);
284 if (lhs.sect()) {
285 if (rhs.sect()) {
346286 // Same group, sort by address.
347 return lhs.nlist.n_value < rhs.nlist.n_value;
287 return lhs.n_value < rhs.n_value;
348288 } else {
349289 return true;
350290 }
......@@ -352,26 +292,34 @@ const NlistWithIndex = struct {
352292 return false;
353293 }
354294 }
295};
355296
356 fn filterByAddress(symbols: []NlistWithIndex, start_addr: u64, end_addr: u64) []NlistWithIndex {
357 const Predicate = struct {
358 addr: u64,
297fn filterSymbolsByAddress(
298 indexes: []SymbolAtIndex,
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 {
361 return symbol.nlist.n_value >= self.addr;
362 }
363 };
307 pub fn predicate(pred: @This(), index: SymbolAtIndex) bool {
308 return index.getSymbol(pred.ctx).n_value >= pred.addr;
309 }
310 };
364311
365 const start = MachO.findFirst(NlistWithIndex, symbols, 0, Predicate{
366 .addr = start_addr,
367 });
368 const end = MachO.findFirst(NlistWithIndex, symbols, start, Predicate{
369 .addr = end_addr,
370 });
312 const start = MachO.findFirst(SymbolAtIndex, indexes, 0, Predicate{
313 .addr = start_addr,
314 .ctx = ctx,
315 });
316 const end = MachO.findFirst(SymbolAtIndex, indexes, start, Predicate{
317 .addr = end_addr,
318 .ctx = ctx,
319 });
371320
372 return symbols[start..end];
373 }
374};
321 return indexes[start..end];
322}
375323
376324fn filterRelocs(
377325 relocs: []const macho.relocation_info,
......@@ -411,29 +359,32 @@ fn filterDice(
411359 return dices[start..end];
412360}
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 {
415364 const tracy = trace(@src());
416365 defer tracy.end();
417366
367 const gpa = macho_file.base.allocator;
418368 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
422372 // You would expect that the symbol table is at least pre-sorted based on symbol's type:
423373 // local < extern defined < undefined. Unfortunately, this is not guaranteed! For instance,
424374 // the GO compiler does not necessarily respect that therefore we sort immediately by type
425375 // and address within.
426 var sorted_all_nlists = try std.ArrayList(NlistWithIndex).initCapacity(allocator, self.symtab.len);
427 defer sorted_all_nlists.deinit();
376 const context = Context{
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| {
430 sorted_all_nlists.appendAssumeCapacity(.{
431 .nlist = nlist,
432 .index = @intCast(u32, index),
433 });
383 for (context.symtab) |_, index| {
384 sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
434385 }
435386
436 sort.sort(NlistWithIndex, sorted_all_nlists.items, {}, NlistWithIndex.lessThan);
387 sort.sort(SymbolAtIndex, sorted_all_syms.items, context, SymbolAtIndex.lessThan);
437388
438389 // Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
439390 // 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) !
441392 const dysymtab = self.load_commands.items[cmd_index].dysymtab;
442393 break :blk dysymtab.iundefsym;
443394 } else blk: {
444 var iundefsym: usize = sorted_all_nlists.items.len;
395 var iundefsym: usize = sorted_all_syms.items.len;
445396 while (iundefsym > 0) : (iundefsym -= 1) {
446 const nlist = sorted_all_nlists.items[iundefsym - 1];
447 if (nlist.nlist.sect()) break;
397 const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
398 if (sym.sect()) break;
448399 }
449400 break :blk iundefsym;
450401 };
451402
452403 // We only care about defined symbols, so filter every other out.
453 const sorted_nlists = sorted_all_nlists.items[0..iundefsym];
454
404 const sorted_syms = sorted_all_syms.items[0..iundefsym];
455405 const dead_strip = macho_file.base.options.gc_sections orelse false;
456406 const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0 and
457407 (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
459410 for (seg.sections.items) |sect, id| {
460411 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
463414 // Get matching segment/section in the final artifact.
464415 const match = (try macho_file.getMatchingSection(sect)) orelse {
465 log.debug("unhandled section", .{});
416 log.debug(" unhandled section", .{});
466417 continue;
467418 };
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
469426 const is_zerofill = blk: {
470427 const section_type = sect.type_();
......@@ -482,10 +439,11 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
482439 );
483440
484441 // Symbols within this section only.
485 const filtered_nlists = NlistWithIndex.filterByAddress(
486 sorted_nlists,
442 const filtered_syms = filterSymbolsByAddress(
443 sorted_syms,
487444 sect.addr,
488445 sect.addr + sect.size,
446 context,
489447 );
490448
491449 macho_file.has_dices = macho_file.has_dices or blk: {
......@@ -498,32 +456,33 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
498456 };
499457 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) {
502460 // If the first nlist does not match the start of the section,
503461 // then we need to encapsulate the memory range [section start, first symbol)
504462 // as a temporary symbol and insert the matching Atom.
505 const first_nlist = filtered_nlists[0].nlist;
506 if (first_nlist.n_value > sect.addr) {
507 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
508 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
509 try macho_file.locals.append(allocator, .{
463 const first_sym = filtered_syms[0].getSymbol(context);
464 if (first_sym.n_value > sect.addr) {
465 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
466 const sym_index = @intCast(u32, self.symtab.items.len);
467 try self.symtab.append(gpa, .{
510468 .n_strx = 0,
511469 .n_type = macho.N_SECT,
512 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
470 .n_sect = macho_file.getSectionOrdinal(match),
513471 .n_desc = 0,
514472 .n_value = sect.addr,
515473 });
516 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
517 break :blk local_sym_index;
474 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
475 break :blk sym_index;
518476 };
519 const atom_size = first_nlist.n_value - sect.addr;
477 const atom_size = first_sym.n_value - sect.addr;
520478 const atom_code: ?[]const u8 = if (code) |cc|
521479 cc[0..atom_size]
522480 else
523481 null;
524 try self.parseIntoAtom(
525 allocator,
526 local_sym_index,
482 const atom = try self.createAtomFromSubsection(
483 macho_file,
484 object_id,
485 sym_index,
527486 atom_size,
528487 sect.@"align",
529488 atom_code,
......@@ -531,33 +490,27 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
531490 &.{},
532491 match,
533492 sect,
534 macho_file,
535493 );
494 try macho_file.addAtomToSection(atom, match);
536495 }
537496
538 var next_nlist_count: usize = 0;
539 while (next_nlist_count < filtered_nlists.len) {
540 const next_nlist = filtered_nlists[next_nlist_count];
541 const addr = next_nlist.nlist.n_value;
542 const atom_nlists = NlistWithIndex.filterByAddress(
543 filtered_nlists[next_nlist_count..],
497 var next_sym_count: usize = 0;
498 while (next_sym_count < filtered_syms.len) {
499 const next_sym = filtered_syms[next_sym_count].getSymbol(context);
500 const addr = next_sym.n_value;
501 const atom_syms = filterSymbolsByAddress(
502 filtered_syms[next_sym_count..],
544503 addr,
545504 addr + 1,
505 context,
546506 );
547 next_nlist_count += atom_nlists.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 });
507 next_sym_count += atom_syms.len;
557508
509 assert(atom_syms.len > 0);
510 const sym_index = atom_syms[0].index;
558511 const atom_size = blk: {
559 const end_addr = if (next_nlist_count < filtered_nlists.len)
560 filtered_nlists[next_nlist_count].nlist.n_value
512 const end_addr = if (next_sym_count < filtered_syms.len)
513 filtered_syms[next_sym_count].getSymbol(context).n_value
561514 else
562515 sect.addr + sect.size;
563516 break :blk end_addr - addr;
......@@ -570,86 +523,91 @@ pub fn parseIntoAtoms(self: *Object, allocator: Allocator, macho_file: *MachO) !
570523 math.min(@ctz(u64, addr), sect.@"align")
571524 else
572525 sect.@"align";
573 try self.parseIntoAtom(
574 allocator,
575 local_sym_index,
526 const atom = try self.createAtomFromSubsection(
527 macho_file,
528 object_id,
529 sym_index,
576530 atom_size,
577531 atom_align,
578532 atom_code,
579533 relocs,
580 atom_nlists,
534 atom_syms[1..],
581535 match,
582536 sect,
583 macho_file,
584537 );
538 try macho_file.addAtomToSection(atom, match);
585539 }
586540 } else {
587541 // If there is no symbol to refer to this atom, we create
588542 // a temp one, unless we already did that when working out the relocations
589543 // of other atoms.
590 const local_sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
591 const local_sym_index = @intCast(u32, macho_file.locals.items.len);
592 try macho_file.locals.append(allocator, .{
544 const sym_index = self.sections_as_symbols.get(sect_id) orelse blk: {
545 const sym_index = @intCast(u32, self.symtab.items.len);
546 try self.symtab.append(gpa, .{
593547 .n_strx = 0,
594548 .n_type = macho.N_SECT,
595 .n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1),
549 .n_sect = macho_file.getSectionOrdinal(match),
596550 .n_desc = 0,
597551 .n_value = sect.addr,
598552 });
599 try self.sections_as_symbols.putNoClobber(allocator, sect_id, local_sym_index);
600 break :blk local_sym_index;
553 try self.sections_as_symbols.putNoClobber(gpa, sect_id, sym_index);
554 break :blk sym_index;
601555 };
602 try self.parseIntoAtom(
603 allocator,
604 local_sym_index,
556 const atom = try self.createAtomFromSubsection(
557 macho_file,
558 object_id,
559 sym_index,
605560 sect.size,
606561 sect.@"align",
607562 code,
608563 relocs,
609 filtered_nlists,
564 filtered_syms,
610565 match,
611566 sect,
612 macho_file,
613567 );
568 try macho_file.addAtomToSection(atom, match);
614569 }
615570 }
616571}
617572
618fn parseIntoAtom(
573fn createAtomFromSubsection(
619574 self: *Object,
620 allocator: Allocator,
621 local_sym_index: u32,
575 macho_file: *MachO,
576 object_id: u32,
577 sym_index: u32,
622578 size: u64,
623579 alignment: u32,
624580 code: ?[]const u8,
625581 relocs: []const macho.relocation_info,
626 nlists: []const NlistWithIndex,
582 indexes: []const SymbolAtIndex,
627583 match: MatchingSection,
628584 sect: macho.section_64,
629 macho_file: *MachO,
630) !void {
631 const sym = macho_file.locals.items[local_sym_index];
632 const align_pow_2 = try math.powi(u32, 2, alignment);
633 const aligned_size = mem.alignForwardGeneric(u64, size, align_pow_2);
634 const atom = try macho_file.createEmptyAtom(local_sym_index, aligned_size, alignment);
585) !*Atom {
586 const gpa = macho_file.base.allocator;
587 const sym = &self.symtab.items[sym_index];
588 const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
589 atom.file = object_id;
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
636595 if (code) |cc| {
596 assert(size == cc.len);
637597 mem.copy(u8, atom.code.items, cc);
638598 }
639599
640600 const base_offset = sym.n_value - sect.addr;
641601 const filtered_relocs = filterRelocs(relocs, base_offset, base_offset + size);
642602 try atom.parseRelocs(filtered_relocs, .{
603 .macho_file = macho_file,
643604 .base_addr = sect.addr,
644605 .base_offset = @intCast(i32, base_offset),
645 .allocator = allocator,
646 .object = self,
647 .macho_file = macho_file,
648606 });
649607
650608 if (macho_file.has_dices) {
651609 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
654612 for (dices) |dice| {
655613 atom.dices.appendAssumeCapacity(.{
......@@ -665,19 +623,41 @@ fn parseIntoAtom(
665623 // the filtered symbols and note which symbol is contained within so that
666624 // we can properly allocate addresses down the line.
667625 // 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| {
671 const nlist = nlist_with_index.nlist;
672 const sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable;
673 const this_sym = &macho_file.locals.items[sym_index];
674 this_sym.n_sect = @intCast(u8, macho_file.section_ordinals.getIndex(match).? + 1);
645 atom.contained.appendAssumeCapacity(.{
646 .sym_index = sym_index,
647 .offset = 0,
648 .stab = stab,
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
676656 const stab: ?Atom.Stab = if (self.debug_info) |di| blk: {
677657 // TODO there has to be a better to handle this.
678658 for (di.inner.func_list.items) |func| {
679659 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) {
681661 break :blk Atom.Stab{
682662 .function = range.end - range.start,
683663 };
......@@ -690,12 +670,12 @@ fn parseIntoAtom(
690670 } else null;
691671
692672 atom.contained.appendAssumeCapacity(.{
693 .local_sym_index = sym_index,
694 .offset = nlist.n_value - sym.n_value,
673 .sym_index = inner_sym_index.index,
674 .offset = inner_sym.n_value - sym.n_value,
695675 .stab = stab,
696676 });
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);
699679 }
700680
701681 const is_gc_root = blk: {
......@@ -714,30 +694,28 @@ fn parseIntoAtom(
714694 }
715695 };
716696 if (is_gc_root) {
717 try macho_file.gc_roots.putNoClobber(allocator, atom, {});
697 try macho_file.gc_roots.putNoClobber(gpa, atom, {});
718698 }
719699
720 if (!self.start_atoms.contains(match)) {
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);
700 return atom;
732701}
733702
734fn parseSymtab(self: *Object) void {
703fn parseSymtab(self: *Object, allocator: Allocator) !void {
735704 const index = self.symtab_cmd_index orelse return;
736705 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;
737713 const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
738714 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));
740 self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
715 return mem.bytesAsSlice(
716 macho.nlist_64,
717 @alignCast(@alignOf(macho.nlist_64), raw_symtab),
718 );
741719}
742720
743721fn parseDebugInfo(self: *Object, allocator: Allocator) !void {
......@@ -783,8 +761,7 @@ fn parseDataInCode(self: *Object) void {
783761}
784762
785763fn getSectionContents(self: Object, sect_id: u16) []const u8 {
786 const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
787 const sect = seg.sections.items[sect_id];
764 const sect = self.getSection(sect_id);
788765 log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
789766 sect.segName(),
790767 sect.sectName(),
......@@ -798,3 +775,9 @@ pub fn getString(self: Object, off: u32) []const u8 {
798775 assert(off < self.strtab.len);
799776 return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
800777}
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}