authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-19 15:55:49+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-07-22 16:58:21+02:00
log39df241df4ac177503d899dd8b53a632e4e29334
tree853d25436b0afb913b5165aeb467e1b4817d2eff
parenta089a6dc4ff04a10360019185ecaacd0564eb84c

macho: do not GC local symbols unless reference dead symbols

If a local references another local, we keep it. If it doesn't reference anything, we keep it. Otherwise, we dead strip it.

4 files changed, 293 insertions(+), 196 deletions(-)

src/link/MachO.zig+251-164
...@@ -171,17 +171,19 @@ stub_helper_preamble_atom: ?*Atom = null,...@@ -171,17 +171,19 @@ stub_helper_preamble_atom: ?*Atom = null,
171171
172strtab: StringTable(.strtab) = .{},172strtab: StringTable(.strtab) = .{},
173173
174// TODO I think synthetic tables are a perfect match for some generic refactoring,
175// and probably reusable between linker backends too.
174tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},176tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{},
175tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},177tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
176tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},178tlv_ptr_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
177179
178got_entries: std.ArrayListUnmanaged(Entry) = .{},180got_entries: std.ArrayListUnmanaged(Entry) = .{},
179got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},181got_entries_free_list: std.ArrayListUnmanaged(u32) = .{},
180got_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},182got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
181183
182stubs: std.ArrayListUnmanaged(Entry) = .{},184stubs: std.ArrayListUnmanaged(Entry) = .{},
183stubs_free_list: std.ArrayListUnmanaged(u32) = .{},185stubs_free_list: std.ArrayListUnmanaged(u32) = .{},
184stubs_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{},186stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{},
185187
186error_flags: File.ErrorFlags = File.ErrorFlags{},188error_flags: File.ErrorFlags = File.ErrorFlags{},
187189
...@@ -251,7 +253,24 @@ decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},...@@ -251,7 +253,24 @@ decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{},
251253
252const Entry = struct {254const Entry = struct {
253 target: SymbolWithLoc,255 target: SymbolWithLoc,
254 atom: *Atom,256 // Index into the synthetic symbol table (i.e., file == null).
257 sym_index: u32,
258
259 pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 {
260 return macho_file.getSymbol(.{ .sym_index = entry.sym_index, .file = null });
261 }
262
263 pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 {
264 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
265 }
266
267 pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom {
268 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?;
269 }
270
271 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
272 return macho_file.getSymbolName(.{ .sym_index = entry.sym_index, .file = null });
273 }
255};274};
256275
257const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));276const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
...@@ -1652,6 +1671,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any...@@ -1652,6 +1671,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any
1652pub const MatchingSection = struct {1671pub const MatchingSection = struct {
1653 seg: u16,1672 seg: u16,
1654 sect: u16,1673 sect: u16,
1674
1675 pub fn eql(this: MatchingSection, other: struct {
1676 seg: ?u16,
1677 sect: ?u16,
1678 }) bool {
1679 const seg = other.seg orelse return false;
1680 const sect = other.sect orelse return false;
1681 return this.seg == seg and this.sect == sect;
1682 }
1655};1683};
16561684
1657pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {1685pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection {
...@@ -3153,8 +3181,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -3153,8 +3181,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void {
3153 const stub_helper_atom = try self.createStubHelperAtom();3181 const stub_helper_atom = try self.createStubHelperAtom();
3154 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);3182 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
3155 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);3183 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
31563184 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
3157 self.stubs.items[stub_index].atom = stub_atom;
3158 }3185 }
31593186
3160 continue :loop;3187 continue :loop;
...@@ -3251,7 +3278,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {...@@ -3251,7 +3278,7 @@ fn resolveDyldStubBinder(self: *MachO) !void {
3251 // Add dyld_stub_binder as the final GOT entry.3278 // Add dyld_stub_binder as the final GOT entry.
3252 const got_index = try self.allocateGotEntry(global);3279 const got_index = try self.allocateGotEntry(global);
3253 const got_atom = try self.createGotAtom(global);3280 const got_atom = try self.createGotAtom(global);
3254 self.got_entries.items[got_index].atom = got_atom;3281 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
3255}3282}
32563283
3257fn addLoadDylibLC(self: *MachO, id: u16) !void {3284fn addLoadDylibLC(self: *MachO, id: u16) !void {
...@@ -3288,11 +3315,7 @@ fn setEntryPoint(self: *MachO) !void {...@@ -3288,11 +3315,7 @@ fn setEntryPoint(self: *MachO) !void {
3288 if (self.base.options.output_mode != .Exe) return;3315 if (self.base.options.output_mode != .Exe) return;
32893316
3290 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;3317 const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment;
3291 const global = self.getEntryPoint() orelse {3318 const global = try self.getEntryPoint();
3292 const name = self.base.options.entry orelse "_main";
3293 log.err("entrypoint '{s}' not found", .{name});
3294 return error.MissingMainEntrypoint;
3295 };
3296 const sym = self.getSymbol(global);3319 const sym = self.getSymbol(global);
3297 const ec = &self.load_commands.items[self.main_cmd_index.?].main;3320 const ec = &self.load_commands.items[self.main_cmd_index.?].main;
3298 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);3321 ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr);
...@@ -3508,7 +3531,8 @@ fn allocateSymbol(self: *MachO) !u32 {...@@ -3508,7 +3531,8 @@ fn allocateSymbol(self: *MachO) !u32 {
3508}3531}
35093532
3510pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {3533pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3511 try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1);3534 const gpa = self.base.allocator;
3535 try self.got_entries.ensureUnusedCapacity(gpa, 1);
35123536
3513 const index = blk: {3537 const index = blk: {
3514 if (self.got_entries_free_list.popOrNull()) |index| {3538 if (self.got_entries_free_list.popOrNull()) |index| {
...@@ -3522,8 +3546,8 @@ pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {...@@ -3522,8 +3546,8 @@ pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3522 }3546 }
3523 };3547 };
35243548
3525 self.got_entries.items[index] = .{ .target = target, .atom = undefined };3549 self.got_entries.items[index] = .{ .target = target, .sym_index = 0 };
3526 try self.got_entries_table.putNoClobber(self.base.allocator, target, index);3550 try self.got_entries_table.putNoClobber(gpa, target, index);
35273551
3528 return index;3552 return index;
3529}3553}
...@@ -3543,7 +3567,7 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {...@@ -3543,7 +3567,7 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3543 }3567 }
3544 };3568 };
35453569
3546 self.stubs.items[index] = .{ .target = target, .atom = undefined };3570 self.stubs.items[index] = .{ .target = target, .sym_index = 0 };
3547 try self.stubs_table.putNoClobber(self.base.allocator, target, index);3571 try self.stubs_table.putNoClobber(self.base.allocator, target, index);
35483572
3549 return index;3573 return index;
...@@ -3564,7 +3588,7 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {...@@ -3564,7 +3588,7 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 {
3564 }3588 }
3565 };3589 };
35663590
3567 self.tlv_ptr_entries.items[index] = .{ .target = target, .atom = undefined };3591 self.tlv_ptr_entries.items[index] = .{ .target = target, .sym_index = 0 };
3568 try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index);3592 try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index);
35693593
3570 return index;3594 return index;
...@@ -4029,7 +4053,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac...@@ -4029,7 +4053,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac
4029 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };4053 const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null };
4030 const got_index = try self.allocateGotEntry(got_target);4054 const got_index = try self.allocateGotEntry(got_target);
4031 const got_atom = try self.createGotAtom(got_target);4055 const got_atom = try self.createGotAtom(got_target);
4032 self.got_entries.items[got_index].atom = got_atom;4056 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
4033 }4057 }
40344058
4035 return symbol;4059 return symbol;
...@@ -4219,9 +4243,9 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -4219,9 +4243,9 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
4219 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};4243 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
4220 self.got_entries.items[got_index] = .{4244 self.got_entries.items[got_index] = .{
4221 .target = .{ .sym_index = 0, .file = null },4245 .target = .{ .sym_index = 0, .file = null },
4222 .atom = undefined,4246 .sym_index = 0,
4223 };4247 };
4224 _ = self.got_entries_table.swapRemove(got_target);4248 _ = self.got_entries_table.remove(got_target);
42254249
4226 if (self.d_sym) |*d_sym| {4250 if (self.d_sym) |*d_sym| {
4227 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);4251 d_sym.swapRemoveRelocs(decl.link.macho.sym_index);
...@@ -5493,46 +5517,26 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {...@@ -5493,46 +5517,26 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {
54935517
5494 if (self.base.options.output_mode == .Exe) {5518 if (self.base.options.output_mode == .Exe) {
5495 // Add entrypoint as GC root5519 // Add entrypoint as GC root
5496 if (self.getEntryPoint()) |global| {5520 const global = try self.getEntryPoint();
5497 if (self.getAtomForSymbol(global)) |gc_root| {5521 const atom = self.getAtomForSymbol(global).?; // panic here means fatal error
5498 _ = try gc_roots.getOrPut(gc_root);5522 _ = try gc_roots.getOrPut(atom);
5499 } else {
5500 log.debug("skipping {s}", .{self.getSymbolName(global)});
5501 }
5502 }
5503 } else {5523 } else {
5504 assert(self.base.options.output_mode == .Lib);5524 assert(self.base.options.output_mode == .Lib);
5505 // Add exports as GC roots5525 // Add exports as GC roots
5506 for (self.globals.values()) |global| {5526 for (self.globals.values()) |global| {
5507 const sym = self.getSymbol(global);5527 const sym = self.getSymbol(global);
5508 if (!sym.sect()) continue;5528 if (!sym.sect()) continue;
5509 const gc_root = self.getAtomForSymbol(global) orelse {5529 const atom = self.getAtomForSymbol(global) orelse {
5510 log.debug("skipping {s}", .{self.getSymbolName(global)});5530 log.debug("skipping {s}", .{self.getSymbolName(global)});
5511 continue;5531 continue;
5512 };5532 };
5513 _ = try gc_roots.getOrPut(gc_root);5533 _ = try gc_roots.getOrPut(atom);
5514 }5534 }
5515 }5535 }
55165536 // TODO just a temp until we learn how to parse unwind records
5517 // Add any atom targeting an import as GC root5537 if (self.globals.get("___gxx_personality_v0")) |global| {
5518 var atoms_it = self.atoms.iterator();5538 if (self.getAtomForSymbol(global)) |atom| {
5519 while (atoms_it.next()) |entry| {5539 _ = try gc_roots.getOrPut(atom);
5520 var atom = entry.value_ptr.*;
5521
5522 while (true) {
5523 for (atom.relocs.items) |rel| {
5524 if ((try rel.getTargetAtom(self)) == null) {
5525 const target_sym = self.getSymbol(rel.target);
5526 if (target_sym.undf()) {
5527 _ = try gc_roots.getOrPut(atom);
5528 break;
5529 }
5530 }
5531 }
5532
5533 if (atom.prev) |prev| {
5534 atom = prev;
5535 } else break;
5536 }5540 }
5537 }5541 }
55385542
...@@ -5540,80 +5544,80 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {...@@ -5540,80 +5544,80 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {
5540 defer stack.deinit();5544 defer stack.deinit();
5541 try stack.ensureUnusedCapacity(gc_roots.count());5545 try stack.ensureUnusedCapacity(gc_roots.count());
55425546
5543 var retained = std.AutoHashMap(*Atom, void).init(gpa);5547 var alive = std.AutoHashMap(*Atom, void).init(gpa);
5544 defer retained.deinit();5548 defer alive.deinit();
5545 try retained.ensureUnusedCapacity(gc_roots.count());5549 try alive.ensureUnusedCapacity(gc_roots.count());
55465550
5547 log.debug("GC roots:", .{});5551 log.debug("GC roots:", .{});
5548 var gc_roots_it = gc_roots.keyIterator();5552 var gc_roots_it = gc_roots.keyIterator();
5549 while (gc_roots_it.next()) |gc_root| {5553 while (gc_roots_it.next()) |gc_root| {
5550 self.logAtom(gc_root.*);5554 self.logAtom(gc_root.*);
5551
5552 stack.appendAssumeCapacity(gc_root.*);5555 stack.appendAssumeCapacity(gc_root.*);
5553 retained.putAssumeCapacityNoClobber(gc_root.*, {});5556 alive.putAssumeCapacity(gc_root.*, {});
5554 }5557 }
55555558
5556 log.debug("walking tree...", .{});
5557 while (stack.popOrNull()) |source_atom| {5559 while (stack.popOrNull()) |source_atom| {
5558 for (source_atom.relocs.items) |rel| {5560 for (source_atom.relocs.items) |rel| {
5559 if (try rel.getTargetAtom(self)) |target_atom| {5561 if (rel.getTargetAtom(self)) |target_atom| {
5560 const gop = try retained.getOrPut(target_atom);5562 const gop = try alive.getOrPut(target_atom);
5561 if (!gop.found_existing) {5563 if (!gop.found_existing) {
5562 log.debug(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{5564 log.debug(" retained ATOM(%{d}, '{s}') in object({d})", .{
5563 source_atom.sym_index,
5564 target_atom.sym_index,5565 target_atom.sym_index,
5566 target_atom.getName(self),
5567 target_atom.file,
5568 });
5569 log.debug(" referenced by ATOM(%{d}, '{s}') in object({d})", .{
5570 source_atom.sym_index,
5571 source_atom.getName(self),
5572 source_atom.file,
5565 });5573 });
5566 try stack.append(target_atom);5574 try stack.append(target_atom);
5567 }5575 }
5568 }5576 }
5569 }5577 }
5570 }5578 }
5579 // TODO live support
55715580
5572 // Any section that ends up here will be updated, that is,5581 // Any section that ends up here will be updated, that is,
5573 // its size and alignment recalculated.5582 // its size and alignment recalculated.
5574 var gc_sections = std.AutoHashMap(MatchingSection, void).init(gpa);5583 var gc_sections = std.AutoHashMap(MatchingSection, void).init(gpa);
5575 defer gc_sections.deinit();5584 defer gc_sections.deinit();
55765585
5577 atoms_it = self.atoms.iterator();5586 var loop: bool = true;
5578 while (atoms_it.next()) |entry| {5587 while (loop) {
5579 const match = entry.key_ptr.*;5588 loop = false;
5580
5581 if (self.text_segment_cmd_index) |seg| {
5582 if (seg == match.seg) {
5583 if (self.eh_frame_section_index) |sect| {
5584 if (sect == match.sect) continue;
5585 }
5586 }
5587 }
55885589
5589 if (self.data_segment_cmd_index) |seg| {5590 for (self.objects.items) |object| {
5590 if (seg == match.seg) {5591 for (object.getSourceSymtab()) |_, source_index| {
5591 if (self.rustc_section_index) |sect| {5592 const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
5592 if (sect == match.sect) continue;5593 if (alive.contains(atom)) continue;
5593 }
5594 }
5595 }
55965594
5597 const sect = self.getSectionPtr(match);5595 const global = atom.getSymbolWithLoc();
5598 var atom = entry.value_ptr.*;5596 const sym = atom.getSymbolPtr(self);
55995597
5600 log.debug("GCing atoms in {s},{s}", .{ sect.segName(), sect.sectName() });5598 if (sym.n_desc == N_DESC_GCED) continue;
5599 if (!sym.ext()) {
5600 for (atom.relocs.items) |rel| {
5601 if (rel.getTargetAtom(self)) |target_atom| {
5602 const target_sym = target_atom.getSymbol(self);
5603 if (target_sym.n_desc == N_DESC_GCED) break;
5604 }
5605 } else continue;
5606 }
56015607
5602 while (true) {5608 loop = true;
5603 const orig_prev = atom.prev;5609 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
56045610
5605 if (!retained.contains(atom)) {5611 // TODO don't dedup eh_frame info yet until we actually implement parsing unwind records
5606 // Dead atom; remove.5612 if (match.eql(.{
5607 log.debug(" DEAD ATOM(%{d})", .{atom.sym_index});5613 .seg = self.text_segment_cmd_index,
5614 .sect = self.eh_frame_section_index,
5615 })) continue;
56085616
5609 const sym = atom.getSymbolPtr(self);5617 self.logAtom(atom);
5610 sym.n_desc = N_DESC_GCED;5618 sym.n_desc = N_DESC_GCED;
56115619 self.removeAtomFromSection(atom, match);
5612 // TODO add full bookkeeping here5620 _ = try gc_sections.put(match, {});
5613 const global = SymbolWithLoc{ .sym_index = atom.sym_index, .file = atom.file };
5614 _ = self.got_entries_table.swapRemove(global);
5615 _ = self.stubs_table.swapRemove(global);
5616 _ = self.tlv_ptr_entries_table.swapRemove(global);
56175621
5618 for (atom.contained.items) |sym_off| {5622 for (atom.contained.items) |sym_off| {
5619 const inner = self.getSymbolPtr(.{5623 const inner = self.getSymbolPtr(.{
...@@ -5622,34 +5626,64 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {...@@ -5622,34 +5626,64 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {
5622 });5626 });
5623 inner.n_desc = N_DESC_GCED;5627 inner.n_desc = N_DESC_GCED;
5624 }5628 }
5625 // If we want to enable GC for incremental codepath, we need to take into
5626 // account any padding that might have been left here.
5627 sect.size -= atom.size;
56285629
5629 _ = try gc_sections.put(match, {});5630 if (self.got_entries_table.contains(global)) {
5631 const got_atom = self.getGotAtomForSymbol(global).?;
5632 const got_sym = got_atom.getSymbolPtr(self);
5633 got_sym.n_desc = N_DESC_GCED;
5634 }
56305635
5631 if (atom.prev) |prev| {5636 if (self.stubs_table.contains(global)) {
5632 prev.next = atom.next;5637 const stubs_atom = self.getStubsAtomForSymbol(global).?;
5638 const stubs_sym = stubs_atom.getSymbolPtr(self);
5639 stubs_sym.n_desc = N_DESC_GCED;
5633 }5640 }
5634 if (atom.next) |next| {5641
5635 next.prev = atom.prev;5642 if (self.tlv_ptr_entries_table.contains(global)) {
5636 } else {5643 const tlv_ptr_atom = self.getTlvPtrAtomForSymbol(global).?;
5637 if (atom.prev) |prev| {5644 const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(self);
5638 entry.value_ptr.* = prev;5645 tlv_ptr_sym.n_desc = N_DESC_GCED;
5639 } else {
5640 // The section will be GCed in the next step.
5641 entry.value_ptr.* = undefined;
5642 sect.size = 0;
5643 }
5644 }5646 }
5645 }5647 }
5646
5647 if (orig_prev) |prev| {
5648 atom = prev;
5649 } else break;
5650 }5648 }
5651 }5649 }
56525650
5651 for (self.got_entries.items) |entry| {
5652 const sym = entry.getSymbol(self);
5653 if (sym.n_desc != N_DESC_GCED) continue;
5654
5655 // TODO tombstone
5656 const atom = entry.getAtom(self);
5657 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5658 self.removeAtomFromSection(atom, match);
5659 _ = try gc_sections.put(match, {});
5660 _ = self.got_entries_table.remove(entry.target);
5661 }
5662
5663 for (self.stubs.items) |entry| {
5664 const sym = entry.getSymbol(self);
5665 if (sym.n_desc != N_DESC_GCED) continue;
5666
5667 // TODO tombstone
5668 const atom = entry.getAtom(self);
5669 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5670 self.removeAtomFromSection(atom, match);
5671 _ = try gc_sections.put(match, {});
5672 _ = self.stubs_table.remove(entry.target);
5673 }
5674
5675 for (self.tlv_ptr_entries.items) |entry| {
5676 const sym = entry.getSymbol(self);
5677 if (sym.n_desc != N_DESC_GCED) continue;
5678
5679 // TODO tombstone
5680 const atom = entry.getAtom(self);
5681 const match = self.getMatchingSectionFromOrdinal(sym.n_sect);
5682 self.removeAtomFromSection(atom, match);
5683 _ = try gc_sections.put(match, {});
5684 _ = self.tlv_ptr_entries_table.remove(entry.target);
5685 }
5686
5653 var gc_sections_it = gc_sections.iterator();5687 var gc_sections_it = gc_sections.iterator();
5654 while (gc_sections_it.next()) |entry| {5688 while (gc_sections_it.next()) |entry| {
5655 const match = entry.key_ptr.*;5689 const match = entry.key_ptr.*;
...@@ -5679,6 +5713,30 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {...@@ -5679,6 +5713,30 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void {
5679 }5713 }
5680}5714}
56815715
5716fn removeAtomFromSection(self: *MachO, atom: *Atom, match: MatchingSection) void {
5717 const sect = self.getSectionPtr(match);
5718
5719 // If we want to enable GC for incremental codepath, we need to take into
5720 // account any padding that might have been left here.
5721 sect.size -= atom.size;
5722
5723 if (atom.prev) |prev| {
5724 prev.next = atom.next;
5725 }
5726 if (atom.next) |next| {
5727 next.prev = atom.prev;
5728 } else {
5729 const last = self.atoms.getPtr(match).?;
5730 if (atom.prev) |prev| {
5731 last.* = prev;
5732 } else {
5733 // The section will be GCed in the next step.
5734 last.* = undefined;
5735 sect.size = 0;
5736 }
5737 }
5738}
5739
5682fn updateSectionOrdinals(self: *MachO) !void {5740fn updateSectionOrdinals(self: *MachO) !void {
5683 if (!self.sections_order_dirty) return;5741 if (!self.sections_order_dirty) return;
56845742
...@@ -5849,7 +5907,7 @@ fn writeDyldInfoData(self: *MachO) !void {...@@ -5849,7 +5907,7 @@ fn writeDyldInfoData(self: *MachO) !void {
58495907
5850 if (self.base.options.output_mode == .Exe) {5908 if (self.base.options.output_mode == .Exe) {
5851 for (&[_]SymbolWithLoc{5909 for (&[_]SymbolWithLoc{
5852 self.getEntryPoint().?, // We would already errored out if no entrypoint was found.5910 try self.getEntryPoint(),
5853 self.globals.get("__mh_execute_header").?,5911 self.globals.get("__mh_execute_header").?,
5854 }) |global| {5912 }) |global| {
5855 const sym = self.getSymbol(global);5913 const sym = self.getSymbol(global);
...@@ -6337,10 +6395,13 @@ fn writeSymtab(self: *MachO) !void {...@@ -6337,10 +6395,13 @@ fn writeSymtab(self: *MachO) !void {
6337 .sect = stubs_section_index,6395 .sect = stubs_section_index,
6338 });6396 });
6339 stubs.reserved1 = 0;6397 stubs.reserved1 = 0;
6340 for (self.stubs_table.keys()) |target| {6398 for (self.stubs.items) |entry| {
6341 const sym = self.getSymbol(target);6399 if (entry.sym_index == 0) continue;
6342 assert(sym.undf());6400 const atom_sym = entry.getSymbol(self);
6343 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);6401 if (atom_sym.n_desc == N_DESC_GCED) continue;
6402 const target_sym = self.getSymbol(entry.target);
6403 assert(target_sym.undf());
6404 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6344 }6405 }
6345 }6406 }
63466407
...@@ -6351,10 +6412,13 @@ fn writeSymtab(self: *MachO) !void {...@@ -6351,10 +6412,13 @@ fn writeSymtab(self: *MachO) !void {
6351 .sect = got_section_index,6412 .sect = got_section_index,
6352 });6413 });
6353 got.reserved1 = nstubs;6414 got.reserved1 = nstubs;
6354 for (self.got_entries_table.keys()) |target| {6415 for (self.got_entries.items) |entry| {
6355 const sym = self.getSymbol(target);6416 if (entry.sym_index == 0) continue;
6356 if (sym.undf()) {6417 const atom_sym = entry.getSymbol(self);
6357 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);6418 if (atom_sym.n_desc == N_DESC_GCED) continue;
6419 const target_sym = self.getSymbol(entry.target);
6420 if (target_sym.undf()) {
6421 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6358 } else {6422 } else {
6359 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);6423 try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL);
6360 }6424 }
...@@ -6368,10 +6432,13 @@ fn writeSymtab(self: *MachO) !void {...@@ -6368,10 +6432,13 @@ fn writeSymtab(self: *MachO) !void {
6368 .sect = la_symbol_ptr_section_index,6432 .sect = la_symbol_ptr_section_index,
6369 });6433 });
6370 la_symbol_ptr.reserved1 = nstubs + ngot_entries;6434 la_symbol_ptr.reserved1 = nstubs + ngot_entries;
6371 for (self.stubs_table.keys()) |target| {6435 for (self.stubs.items) |entry| {
6372 const sym = self.getSymbol(target);6436 if (entry.sym_index == 0) continue;
6373 assert(sym.undf());6437 const atom_sym = entry.getSymbol(self);
6374 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?);6438 if (atom_sym.n_desc == N_DESC_GCED) continue;
6439 const target_sym = self.getSymbol(entry.target);
6440 assert(target_sym.undf());
6441 try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?);
6375 }6442 }
6376 }6443 }
63776444
...@@ -6623,7 +6690,7 @@ pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {...@@ -6623,7 +6690,7 @@ pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 {
6623pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {6690pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6624 if (sym_with_loc.file) |file| {6691 if (sym_with_loc.file) |file| {
6625 const object = self.objects.items[file];6692 const object = self.objects.items[file];
6626 return object.atom_by_index_table.get(sym_with_loc.sym_index);6693 return object.getAtomForSymbol(sym_with_loc.sym_index);
6627 } else {6694 } else {
6628 return self.atom_by_index_table.get(sym_with_loc.sym_index);6695 return self.atom_by_index_table.get(sym_with_loc.sym_index);
6629 }6696 }
...@@ -6633,28 +6700,32 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {...@@ -6633,28 +6700,32 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6633/// Returns null otherwise.6700/// Returns null otherwise.
6634pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {6701pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6635 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;6702 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
6636 return self.got_entries.items[got_index].atom;6703 return self.got_entries.items[got_index].getAtom(self);
6637}6704}
66386705
6639/// Returns stubs atom that references `sym_with_loc` if one exists.6706/// Returns stubs atom that references `sym_with_loc` if one exists.
6640/// Returns null otherwise.6707/// Returns null otherwise.
6641pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {6708pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6642 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;6709 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
6643 return self.stubs.items[stubs_index].atom;6710 return self.stubs.items[stubs_index].getAtom(self);
6644}6711}
66456712
6646/// Returns TLV pointer atom that references `sym_with_loc` if one exists.6713/// Returns TLV pointer atom that references `sym_with_loc` if one exists.
6647/// Returns null otherwise.6714/// Returns null otherwise.
6648pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {6715pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
6649 const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null;6716 const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null;
6650 return self.tlv_ptr_entries.items[tlv_ptr_index].atom;6717 return self.tlv_ptr_entries.items[tlv_ptr_index].getAtom(self);
6651}6718}
66526719
6653/// Returns symbol location corresponding to the set entrypoint.6720/// Returns symbol location corresponding to the set entrypoint.
6654/// Asserts output mode is executable.6721/// Asserts output mode is executable.
6655pub fn getEntryPoint(self: MachO) ?SymbolWithLoc {6722pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc {
6656 const entry_name = self.base.options.entry orelse "_main";6723 const entry_name = self.base.options.entry orelse "_main";
6657 return self.globals.get(entry_name);6724 const global = self.globals.get(entry_name) orelse {
6725 log.err("entrypoint '{s}' not found", .{entry_name});
6726 return error.MissingMainEntrypoint;
6727 };
6728 return global;
6658}6729}
66596730
6660pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {6731pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize {
...@@ -6986,7 +7057,7 @@ fn snapshotState(self: *MachO) !void {...@@ -6986,7 +7057,7 @@ fn snapshotState(self: *MachO) !void {
6986 break :blk source_sym.n_value + rel.offset;7057 break :blk source_sym.n_value + rel.offset;
6987 };7058 };
6988 const target_addr = blk: {7059 const target_addr = blk: {
6989 const target_atom = (try rel.getTargetAtom(self)) orelse {7060 const target_atom = rel.getTargetAtom(self) orelse {
6990 // If there is no atom for target, we still need to check for special, atom-less7061 // If there is no atom for target, we still need to check for special, atom-less
6991 // symbols such as `___dso_handle`.7062 // symbols such as `___dso_handle`.
6992 const target_name = self.getSymbolName(rel.target);7063 const target_name = self.getSymbolName(rel.target);
...@@ -7119,8 +7190,9 @@ fn snapshotState(self: *MachO) !void {...@@ -7119,8 +7190,9 @@ fn snapshotState(self: *MachO) !void {
7119 try writer.writeByte(']');7190 try writer.writeByte(']');
7120}7191}
71217192
7122pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {7193fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 {
7123 mem.set(u8, buf, '_');7194 mem.set(u8, buf[0..4], '_');
7195 mem.set(u8, buf[4..], ' ');
7124 if (sym.sect()) {7196 if (sym.sect()) {
7125 buf[0] = 's';7197 buf[0] = 's';
7126 }7198 }
...@@ -7137,11 +7209,14 @@ pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {...@@ -7137,11 +7209,14 @@ pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 {
7137 if (sym.undf()) {7209 if (sym.undf()) {
7138 buf[3] = 'u';7210 buf[3] = 'u';
7139 }7211 }
7212 if (sym.n_desc == N_DESC_GCED) {
7213 mem.copy(u8, buf[5..], "DEAD");
7214 }
7140 return buf[0..];7215 return buf[0..];
7141}7216}
71427217
7143fn logSymtab(self: *MachO) void {7218fn logSymtab(self: *MachO) void {
7144 var buf: [4]u8 = undefined;7219 var buf: [9]u8 = undefined;
71457220
7146 log.debug("symtab:", .{});7221 log.debug("symtab:", .{});
7147 for (self.objects.items) |object, id| {7222 for (self.objects.items) |object, id| {
...@@ -7186,42 +7261,50 @@ fn logSymtab(self: *MachO) void {...@@ -7186,42 +7261,50 @@ fn logSymtab(self: *MachO) void {
7186 }7261 }
71877262
7188 log.debug("GOT entries:", .{});7263 log.debug("GOT entries:", .{});
7189 for (self.got_entries_table.values()) |value| {7264 for (self.got_entries.items) |entry, i| {
7190 const target = self.got_entries.items[value].target;7265 const atom_sym = entry.getSymbol(self);
7191 const target_sym = self.getSymbol(target);7266 if (atom_sym.n_desc == N_DESC_GCED) continue;
7192 const atom = self.got_entries.items[value].atom;7267 const target_sym = self.getSymbol(entry.target);
7193 const atom_sym = atom.getSymbol(self);
7194
7195 if (target_sym.undf()) {7268 if (target_sym.undf()) {
7196 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });7269 log.debug(" {d}@{x} => import('{s}')", .{
7270 i,
7271 atom_sym.n_value,
7272 self.getSymbolName(entry.target),
7273 });
7197 } else {7274 } else {
7198 log.debug(" {d}@{x} => local(%{d}) in object({d})", .{7275 log.debug(" {d}@{x} => local(%{d}) in object({d}) {s}", .{
7199 value,7276 i,
7200 atom_sym.n_value,7277 atom_sym.n_value,
7201 target.sym_index,7278 entry.target.sym_index,
7202 target.file,7279 entry.target.file,
7280 logSymAttributes(target_sym, &buf),
7203 });7281 });
7204 }7282 }
7205 }7283 }
72067284
7207 log.debug("__thread_ptrs entries:", .{});7285 log.debug("__thread_ptrs entries:", .{});
7208 for (self.tlv_ptr_entries_table.values()) |value| {7286 for (self.tlv_ptr_entries.items) |entry, i| {
7209 const target = self.tlv_ptr_entries.items[value].target;7287 const atom_sym = entry.getSymbol(self);
7210 const target_sym = self.getSymbol(target);7288 if (atom_sym.n_desc == N_DESC_GCED) continue;
7211 const atom = self.tlv_ptr_entries.items[value].atom;7289 const target_sym = self.getSymbol(entry.target);
7212 const atom_sym = atom.getSymbol(self);
7213 assert(target_sym.undf());7290 assert(target_sym.undf());
7214 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });7291 log.debug(" {d}@{x} => import('{s}')", .{
7292 i,
7293 atom_sym.n_value,
7294 self.getSymbolName(entry.target),
7295 });
7215 }7296 }
72167297
7217 log.debug("stubs entries:", .{});7298 log.debug("stubs entries:", .{});
7218 for (self.stubs_table.values()) |value| {7299 for (self.stubs.items) |entry, i| {
7219 const target = self.stubs.items[value].target;7300 const target_sym = self.getSymbol(entry.target);
7220 const target_sym = self.getSymbol(target);7301 const atom_sym = entry.getSymbol(self);
7221 const atom = self.stubs.items[value].atom;
7222 const atom_sym = atom.getSymbol(self);
7223 assert(target_sym.undf());7302 assert(target_sym.undf());
7224 log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) });7303 log.debug(" {d}@{x} => import('{s}')", .{
7304 i,
7305 atom_sym.n_value,
7306 self.getSymbolName(entry.target),
7307 });
7225 }7308 }
7226}7309}
72277310
...@@ -7248,7 +7331,6 @@ fn logAtoms(self: *MachO) void {...@@ -7248,7 +7331,6 @@ fn logAtoms(self: *MachO) void {
72487331
7249 while (true) {7332 while (true) {
7250 self.logAtom(atom);7333 self.logAtom(atom);
7251
7252 if (atom.next) |next| {7334 if (atom.next) |next| {
7253 atom = next;7335 atom = next;
7254 } else break;7336 } else break;
...@@ -7256,14 +7338,17 @@ fn logAtoms(self: *MachO) void {...@@ -7256,14 +7338,17 @@ fn logAtoms(self: *MachO) void {
7256 }7338 }
7257}7339}
72587340
7259pub fn logAtom(self: *MachO, atom: *const Atom) void {7341fn logAtom(self: *MachO, atom: *const Atom) void {
7260 const sym = atom.getSymbol(self);7342 const sym = atom.getSymbol(self);
7261 const sym_name = atom.getName(self);7343 const sym_name = atom.getName(self);
7262 log.debug(" ATOM(%{d}, '{s}') @ {x} in object({d})", .{7344 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{
7263 atom.sym_index,7345 atom.sym_index,
7264 sym_name,7346 sym_name,
7265 sym.n_value,7347 sym.n_value,
7348 atom.size,
7349 atom.alignment,
7266 atom.file,7350 atom.file,
7351 sym.n_sect,
7267 });7352 });
72687353
7269 for (atom.contained.items) |sym_off| {7354 for (atom.contained.items) |sym_off| {
...@@ -7271,13 +7356,15 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {...@@ -7271,13 +7356,15 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void {
7271 .sym_index = sym_off.sym_index,7356 .sym_index = sym_off.sym_index,
7272 .file = atom.file,7357 .file = atom.file,
7273 });7358 });
7274 const inner_sym_name = self.getSymbolName(.{ .sym_index = sym_off.sym_index, .file = atom.file });7359 const inner_sym_name = self.getSymbolName(.{
7275 log.debug(" (%{d}, '{s}') @ {x} ({x}) in object({d})", .{7360 .sym_index = sym_off.sym_index,
7361 .file = atom.file,
7362 });
7363 log.debug(" (%{d}, '{s}') @ {x} ({x})", .{
7276 sym_off.sym_index,7364 sym_off.sym_index,
7277 inner_sym_name,7365 inner_sym_name,
7278 inner_sym.n_value,7366 inner_sym.n_value,
7279 sym_off.offset,7367 sym_off.offset,
7280 atom.file,
7281 });7368 });
7282 }7369 }
7283}7370}
src/link/MachO/Atom.zig+23-19
...@@ -94,7 +94,7 @@ pub const Relocation = struct {...@@ -94,7 +94,7 @@ pub const Relocation = struct {
9494
95 @"type": u4,95 @"type": u4,
9696
97 pub fn getTargetAtom(self: Relocation, macho_file: *MachO) !?*Atom {97 pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
98 const is_via_got = got: {98 const is_via_got = got: {
99 switch (macho_file.base.options.target.cpu.arch) {99 switch (macho_file.base.options.target.cpu.arch) {
100 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {100 .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) {
...@@ -112,21 +112,9 @@ pub const Relocation = struct {...@@ -112,21 +112,9 @@ pub const Relocation = struct {
112 }112 }
113 };113 };
114114
115 const target_sym = macho_file.getSymbol(self.target);
116 if (is_via_got) {115 if (is_via_got) {
117 const got_atom = macho_file.getGotAtomForSymbol(self.target) orelse {116 return macho_file.getGotAtomForSymbol(self.target).?; // panic means fatal error
118 log.err("expected GOT entry for symbol", .{});
119 if (target_sym.undf()) {
120 log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)});
121 } else {
122 log.err(" local(%{d}) in object({d})", .{ self.target.sym_index, self.target.file });
123 }
124 log.err(" this is an internal linker error", .{});
125 return error.FailedToResolveRelocationTarget;
126 };
127 return got_atom;
128 }117 }
129
130 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;118 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
131 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;119 if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom;
132 return macho_file.getAtomForSymbol(self.target);120 return macho_file.getAtomForSymbol(self.target);
...@@ -174,6 +162,10 @@ pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {...@@ -174,6 +162,10 @@ pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
174 });162 });
175}163}
176164
165pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
166 return .{ .sym_index = self.sym_index, .file = self.file };
167}
168
177/// Returns true if the symbol pointed at with `sym_loc` is contained within this atom.169/// Returns true if the symbol pointed at with `sym_loc` is contained within this atom.
178/// WARNING this function assumes all atoms have been allocated in the virtual memory.170/// WARNING this function assumes all atoms have been allocated in the virtual memory.
179/// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will171/// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will
...@@ -515,7 +507,7 @@ fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {...@@ -515,7 +507,7 @@ fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
515507
516 const index = try context.macho_file.allocateTlvPtrEntry(target);508 const index = try context.macho_file.allocateTlvPtrEntry(target);
517 const atom = try context.macho_file.createTlvPtrAtom(target);509 const atom = try context.macho_file.createTlvPtrAtom(target);
518 context.macho_file.tlv_ptr_entries.items[index].atom = atom;510 context.macho_file.tlv_ptr_entries.items[index].sym_index = atom.sym_index;
519}511}
520512
521fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {513fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
...@@ -523,7 +515,7 @@ fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {...@@ -523,7 +515,7 @@ fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void {
523515
524 const index = try context.macho_file.allocateGotEntry(target);516 const index = try context.macho_file.allocateGotEntry(target);
525 const atom = try context.macho_file.createGotAtom(target);517 const atom = try context.macho_file.createGotAtom(target);
526 context.macho_file.got_entries.items[index].atom = atom;518 context.macho_file.got_entries.items[index].sym_index = atom.sym_index;
527}519}
528520
529fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {521fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
...@@ -536,7 +528,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {...@@ -536,7 +528,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void {
536 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);528 const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target);
537 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);529 const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index);
538530
539 context.macho_file.stubs.items[stub_index].atom = stub_atom;531 context.macho_file.stubs.items[stub_index].sym_index = stub_atom.sym_index;
540}532}
541533
542pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {534pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
...@@ -578,7 +570,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -578,7 +570,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
578 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;570 break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
579 };571 };
580 const target_addr = blk: {572 const target_addr = blk: {
581 const target_atom = (try rel.getTargetAtom(macho_file)) orelse {573 const target_atom = rel.getTargetAtom(macho_file) orelse {
582 // If there is no atom for target, we still need to check for special, atom-less574 // If there is no atom for target, we still need to check for special, atom-less
583 // symbols such as `___dso_handle`.575 // symbols such as `___dso_handle`.
584 const target_name = macho_file.getSymbolName(rel.target);576 const target_name = macho_file.getSymbolName(rel.target);
...@@ -597,6 +589,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -597,6 +589,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
597 macho_file.getSymbol(rel.target)589 macho_file.getSymbol(rel.target)
598 else590 else
599 target_atom.getSymbol(macho_file);591 target_atom.getSymbol(macho_file);
592 assert(target_sym.n_desc != MachO.N_DESC_GCED);
600 const base_address: u64 = if (is_tlv) base_address: {593 const base_address: u64 = if (is_tlv) base_address: {
601 // For TLV relocations, the value specified as a relocation is the displacement from the594 // For TLV relocations, the value specified as a relocation is the displacement from the
602 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first595 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
...@@ -624,12 +617,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -624,12 +617,12 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
624 };617 };
625618
626 log.debug(" | source_addr = 0x{x}", .{source_addr});619 log.debug(" | source_addr = 0x{x}", .{source_addr});
627 log.debug(" | target_addr = 0x{x}", .{target_addr});
628620
629 switch (arch) {621 switch (arch) {
630 .aarch64 => {622 .aarch64 => {
631 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {623 switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) {
632 .ARM64_RELOC_BRANCH26 => {624 .ARM64_RELOC_BRANCH26 => {
625 log.debug(" | target_addr = 0x{x}", .{target_addr});
633 const displacement = math.cast(626 const displacement = math.cast(
634 i28,627 i28,
635 @intCast(i64, target_addr) - @intCast(i64, source_addr),628 @intCast(i64, target_addr) - @intCast(i64, source_addr),
...@@ -658,6 +651,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -658,6 +651,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
658 .ARM64_RELOC_TLVP_LOAD_PAGE21,651 .ARM64_RELOC_TLVP_LOAD_PAGE21,
659 => {652 => {
660 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;653 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
654 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
661 const source_page = @intCast(i32, source_addr >> 12);655 const source_page = @intCast(i32, source_addr >> 12);
662 const target_page = @intCast(i32, actual_target_addr >> 12);656 const target_page = @intCast(i32, actual_target_addr >> 12);
663 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));657 const pages = @bitCast(u21, @intCast(i21, target_page - source_page));
...@@ -675,6 +669,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -675,6 +669,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
675 .ARM64_RELOC_PAGEOFF12 => {669 .ARM64_RELOC_PAGEOFF12 => {
676 const code = self.code.items[rel.offset..][0..4];670 const code = self.code.items[rel.offset..][0..4];
677 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;671 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
672 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
678 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));673 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
679 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {674 if (isArithmeticOp(self.code.items[rel.offset..][0..4])) {
680 var inst = aarch64.Instruction{675 var inst = aarch64.Instruction{
...@@ -712,6 +707,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -712,6 +707,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
712 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {707 .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => {
713 const code = self.code.items[rel.offset..][0..4];708 const code = self.code.items[rel.offset..][0..4];
714 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;709 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
710 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
715 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));711 const narrowed = @truncate(u12, @intCast(u64, actual_target_addr));
716 var inst: aarch64.Instruction = .{712 var inst: aarch64.Instruction = .{
717 .load_store_register = mem.bytesToValue(meta.TagPayload(713 .load_store_register = mem.bytesToValue(meta.TagPayload(
...@@ -726,6 +722,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -726,6 +722,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
726 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {722 .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => {
727 const code = self.code.items[rel.offset..][0..4];723 const code = self.code.items[rel.offset..][0..4];
728 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;724 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
725 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
729726
730 const RegInfo = struct {727 const RegInfo = struct {
731 rd: u5,728 rd: u5,
...@@ -783,6 +780,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -783,6 +780,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
783 mem.writeIntLittle(u32, code, inst.toU32());780 mem.writeIntLittle(u32, code, inst.toU32());
784 },781 },
785 .ARM64_RELOC_POINTER_TO_GOT => {782 .ARM64_RELOC_POINTER_TO_GOT => {
783 log.debug(" | target_addr = 0x{x}", .{target_addr});
786 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow;784 const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow;
787 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));785 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result));
788 },786 },
...@@ -795,6 +793,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -795,6 +793,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
795 break :blk @intCast(i64, target_addr) + rel.addend;793 break :blk @intCast(i64, target_addr) + rel.addend;
796 }794 }
797 };795 };
796 log.debug(" | target_addr = 0x{x}", .{result});
798797
799 if (rel.length == 3) {798 if (rel.length == 3) {
800 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));799 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
...@@ -813,6 +812,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -813,6 +812,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
813 .x86_64 => {812 .x86_64 => {
814 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {813 switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) {
815 .X86_64_RELOC_BRANCH => {814 .X86_64_RELOC_BRANCH => {
815 log.debug(" | target_addr = 0x{x}", .{target_addr});
816 const displacement = math.cast(816 const displacement = math.cast(
817 i32,817 i32,
818 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,818 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
...@@ -820,6 +820,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -820,6 +820,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
820 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));820 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
821 },821 },
822 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {822 .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => {
823 log.debug(" | target_addr = 0x{x}", .{target_addr});
823 const displacement = math.cast(824 const displacement = math.cast(
824 i32,825 i32,
825 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,826 @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend,
...@@ -827,6 +828,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -827,6 +828,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
827 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));828 mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement));
828 },829 },
829 .X86_64_RELOC_TLV => {830 .X86_64_RELOC_TLV => {
831 log.debug(" | target_addr = 0x{x}", .{target_addr});
830 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {832 if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) {
831 // We need to rewrite the opcode from movq to leaq.833 // We need to rewrite the opcode from movq to leaq.
832 self.code.items[rel.offset - 2] = 0x8d;834 self.code.items[rel.offset - 2] = 0x8d;
...@@ -850,6 +852,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -850,6 +852,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
850 else => unreachable,852 else => unreachable,
851 };853 };
852 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;854 const actual_target_addr = @intCast(i64, target_addr) + rel.addend;
855 log.debug(" | target_addr = 0x{x}", .{actual_target_addr});
853 const displacement = math.cast(856 const displacement = math.cast(
854 i32,857 i32,
855 actual_target_addr - @intCast(i64, source_addr + correction + 4),858 actual_target_addr - @intCast(i64, source_addr + correction + 4),
...@@ -865,6 +868,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {...@@ -865,6 +868,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
865 break :blk @intCast(i64, target_addr) + rel.addend;868 break :blk @intCast(i64, target_addr) + rel.addend;
866 }869 }
867 };870 };
871 log.debug(" | target_addr = 0x{x}", .{result});
868872
869 if (rel.length == 3) {873 if (rel.length == 3) {
870 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));874 mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result));
src/link/MachO/DebugSymbols.zig+12-6
...@@ -275,9 +275,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -275,9 +275,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
275 const sym = switch (reloc.@"type") {275 const sym = switch (reloc.@"type") {
276 .direct_load => self.base.getSymbol(.{ .sym_index = reloc.target, .file = null }),276 .direct_load => self.base.getSymbol(.{ .sym_index = reloc.target, .file = null }),
277 .got_load => blk: {277 .got_load => blk: {
278 const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?;278 const got_index = self.base.got_entries_table.get(.{
279 const got_atom = self.base.got_entries.items[got_index].atom;279 .sym_index = reloc.target,
280 break :blk got_atom.getSymbol(self.base);280 .file = null,
281 }).?;
282 const got_entry = self.base.got_entries.items[got_index];
283 break :blk got_entry.getSymbol(self.base);
281 },284 },
282 };285 };
283 if (sym.n_value == reloc.prev_vaddr) continue;286 if (sym.n_value == reloc.prev_vaddr) continue;
...@@ -285,9 +288,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -285,9 +288,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
285 const sym_name = switch (reloc.@"type") {288 const sym_name = switch (reloc.@"type") {
286 .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }),289 .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }),
287 .got_load => blk: {290 .got_load => blk: {
288 const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?;291 const got_index = self.base.got_entries_table.get(.{
289 const got_atom = self.base.got_entries.items[got_index].atom;292 .sym_index = reloc.target,
290 break :blk got_atom.getName(self.base);293 .file = null,
294 }).?;
295 const got_entry = self.base.got_entries.items[got_index];
296 break :blk got_entry.getName(self.base);
291 },297 },
292 };298 };
293 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;299 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
src/link/MachO/Object.zig+7-7
...@@ -410,7 +410,9 @@ pub fn splitIntoAtomsOneShot(...@@ -410,7 +410,9 @@ pub fn splitIntoAtomsOneShot(
410 next_sym_count += atom_syms.len;410 next_sym_count += atom_syms.len;
411411
412 assert(atom_syms.len > 0);412 assert(atom_syms.len > 0);
413 const sym_index = atom_syms[0].index;413 const sym_index = for (atom_syms) |atom_sym| {
414 if (atom_sym.getSymbol(context).ext()) break atom_sym.index;
415 } else atom_syms[0].index;
414 const atom_size = blk: {416 const atom_size = blk: {
415 const end_addr = if (next_sym_count < filtered_syms.len)417 const end_addr = if (next_sym_count < filtered_syms.len)
416 filtered_syms[next_sym_count].getSymbol(context).n_value418 filtered_syms[next_sym_count].getSymbol(context).n_value
...@@ -570,12 +572,6 @@ fn createAtomFromSubsection(...@@ -570,12 +572,6 @@ fn createAtomFromSubsection(
570 if (gc_roots) |gcr| {572 if (gc_roots) |gcr| {
571 const is_gc_root = blk: {573 const is_gc_root = blk: {
572 if (sect.isDontDeadStrip()) break :blk true;574 if (sect.isDontDeadStrip()) break :blk true;
573 if (sect.isDontDeadStripIfReferencesLive()) {
574 // TODO if isDontDeadStripIfReferencesLive we should analyse the edges
575 // before making it a GC root
576 break :blk true;
577 }
578 if (mem.eql(u8, "__StaticInit", sect.sectName())) break :blk true;
579 switch (sect.type_()) {575 switch (sect.type_()) {
580 macho.S_MOD_INIT_FUNC_POINTERS,576 macho.S_MOD_INIT_FUNC_POINTERS,
581 macho.S_MOD_TERM_FUNC_POINTERS,577 macho.S_MOD_TERM_FUNC_POINTERS,
...@@ -641,3 +637,7 @@ pub fn getSection(self: Object, n_sect: u16) macho.section_64 {...@@ -641,3 +637,7 @@ pub fn getSection(self: Object, n_sect: u16) macho.section_64 {
641 assert(n_sect < seg.sections.items.len);637 assert(n_sect < seg.sections.items.len);
642 return seg.sections.items[n_sect];638 return seg.sections.items[n_sect];
643}639}
640
641pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
642 return self.atom_by_index_table.get(sym_index);
643}