diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-07-19 15:55:49 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2022-07-22 16:58:21 +0200 |
| commit | 39df241df4ac177503d899dd8b53a632e4e29334 (patch) | |
| tree | 853d25436b0afb913b5165aeb467e1b4817d2eff /src | |
| parent | a089a6dc4ff04a10360019185ecaacd0564eb84c (diff) | |
| download | zig-39df241df4ac177503d899dd8b53a632e4e29334.tar.gz zig-39df241df4ac177503d899dd8b53a632e4e29334.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 415 | ||||
| -rw-r--r-- | src/link/MachO/Atom.zig | 42 | ||||
| -rw-r--r-- | src/link/MachO/DebugSymbols.zig | 18 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 14 |
4 files changed, 293 insertions, 196 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 165ff07521..898ad6732b 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -171,17 +171,19 @@ stub_helper_preamble_atom: ?*Atom = null, strtab: StringTable(.strtab) = .{}, +// TODO I think synthetic tables are a perfect match for some generic refactoring, +// and probably reusable between linker backends too. tlv_ptr_entries: std.ArrayListUnmanaged(Entry) = .{}, tlv_ptr_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, -tlv_ptr_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, +tlv_ptr_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, got_entries: std.ArrayListUnmanaged(Entry) = .{}, got_entries_free_list: std.ArrayListUnmanaged(u32) = .{}, -got_entries_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, +got_entries_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, stubs: std.ArrayListUnmanaged(Entry) = .{}, stubs_free_list: std.ArrayListUnmanaged(u32) = .{}, -stubs_table: std.AutoArrayHashMapUnmanaged(SymbolWithLoc, u32) = .{}, +stubs_table: std.AutoHashMapUnmanaged(SymbolWithLoc, u32) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, @@ -251,7 +253,24 @@ decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?MatchingSection) = .{}, const Entry = struct { target: SymbolWithLoc, - atom: *Atom, + // Index into the synthetic symbol table (i.e., file == null). + sym_index: u32, + + pub fn getSymbol(entry: Entry, macho_file: *MachO) macho.nlist_64 { + return macho_file.getSymbol(.{ .sym_index = entry.sym_index, .file = null }); + } + + pub fn getSymbolPtr(entry: Entry, macho_file: *MachO) *macho.nlist_64 { + return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null }); + } + + pub fn getAtom(entry: Entry, macho_file: *MachO) *Atom { + return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }).?; + } + + pub fn getName(entry: Entry, macho_file: *MachO) []const u8 { + return macho_file.getSymbolName(.{ .sym_index = entry.sym_index, .file = null }); + } }; const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); @@ -1652,6 +1671,15 @@ fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: any pub const MatchingSection = struct { seg: u16, sect: u16, + + pub fn eql(this: MatchingSection, other: struct { + seg: ?u16, + sect: ?u16, + }) bool { + const seg = other.seg orelse return false; + const sect = other.sect orelse return false; + return this.seg == seg and this.sect == sect; + } }; pub fn getMatchingSection(self: *MachO, sect: macho.section_64) !?MatchingSection { @@ -3153,8 +3181,7 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { const stub_helper_atom = try self.createStubHelperAtom(); const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global); const stub_atom = try self.createStubAtom(laptr_atom.sym_index); - - self.stubs.items[stub_index].atom = stub_atom; + self.stubs.items[stub_index].sym_index = stub_atom.sym_index; } continue :loop; @@ -3251,7 +3278,7 @@ fn resolveDyldStubBinder(self: *MachO) !void { // Add dyld_stub_binder as the final GOT entry. const got_index = try self.allocateGotEntry(global); const got_atom = try self.createGotAtom(global); - self.got_entries.items[got_index].atom = got_atom; + self.got_entries.items[got_index].sym_index = got_atom.sym_index; } fn addLoadDylibLC(self: *MachO, id: u16) !void { @@ -3288,11 +3315,7 @@ fn setEntryPoint(self: *MachO) !void { if (self.base.options.output_mode != .Exe) return; const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; - const global = self.getEntryPoint() orelse { - const name = self.base.options.entry orelse "_main"; - log.err("entrypoint '{s}' not found", .{name}); - return error.MissingMainEntrypoint; - }; + const global = try self.getEntryPoint(); const sym = self.getSymbol(global); const ec = &self.load_commands.items[self.main_cmd_index.?].main; ec.entryoff = @intCast(u32, sym.n_value - seg.inner.vmaddr); @@ -3508,7 +3531,8 @@ fn allocateSymbol(self: *MachO) !u32 { } pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 { - try self.got_entries.ensureUnusedCapacity(self.base.allocator, 1); + const gpa = self.base.allocator; + try self.got_entries.ensureUnusedCapacity(gpa, 1); const index = blk: { if (self.got_entries_free_list.popOrNull()) |index| { @@ -3522,8 +3546,8 @@ pub fn allocateGotEntry(self: *MachO, target: SymbolWithLoc) !u32 { } }; - self.got_entries.items[index] = .{ .target = target, .atom = undefined }; - try self.got_entries_table.putNoClobber(self.base.allocator, target, index); + self.got_entries.items[index] = .{ .target = target, .sym_index = 0 }; + try self.got_entries_table.putNoClobber(gpa, target, index); return index; } @@ -3543,7 +3567,7 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 { } }; - self.stubs.items[index] = .{ .target = target, .atom = undefined }; + self.stubs.items[index] = .{ .target = target, .sym_index = 0 }; try self.stubs_table.putNoClobber(self.base.allocator, target, index); return index; @@ -3564,7 +3588,7 @@ pub fn allocateTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !u32 { } }; - self.tlv_ptr_entries.items[index] = .{ .target = target, .atom = undefined }; + self.tlv_ptr_entries.items[index] = .{ .target = target, .sym_index = 0 }; try self.tlv_ptr_entries_table.putNoClobber(self.base.allocator, target, index); return index; @@ -4029,7 +4053,7 @@ fn placeDecl(self: *MachO, decl_index: Module.Decl.Index, code_len: usize) !*mac const got_target = SymbolWithLoc{ .sym_index = decl.link.macho.sym_index, .file = null }; const got_index = try self.allocateGotEntry(got_target); const got_atom = try self.createGotAtom(got_target); - self.got_entries.items[got_index].atom = got_atom; + self.got_entries.items[got_index].sym_index = got_atom.sym_index; } return symbol; @@ -4219,9 +4243,9 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {}; self.got_entries.items[got_index] = .{ .target = .{ .sym_index = 0, .file = null }, - .atom = undefined, + .sym_index = 0, }; - _ = self.got_entries_table.swapRemove(got_target); + _ = self.got_entries_table.remove(got_target); if (self.d_sym) |*d_sym| { d_sym.swapRemoveRelocs(decl.link.macho.sym_index); @@ -5493,46 +5517,26 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void { if (self.base.options.output_mode == .Exe) { // Add entrypoint as GC root - if (self.getEntryPoint()) |global| { - if (self.getAtomForSymbol(global)) |gc_root| { - _ = try gc_roots.getOrPut(gc_root); - } else { - log.debug("skipping {s}", .{self.getSymbolName(global)}); - } - } + const global = try self.getEntryPoint(); + const atom = self.getAtomForSymbol(global).?; // panic here means fatal error + _ = try gc_roots.getOrPut(atom); } else { assert(self.base.options.output_mode == .Lib); // Add exports as GC roots for (self.globals.values()) |global| { const sym = self.getSymbol(global); if (!sym.sect()) continue; - const gc_root = self.getAtomForSymbol(global) orelse { + const atom = self.getAtomForSymbol(global) orelse { log.debug("skipping {s}", .{self.getSymbolName(global)}); continue; }; - _ = try gc_roots.getOrPut(gc_root); + _ = try gc_roots.getOrPut(atom); } } - - // Add any atom targeting an import as GC root - var atoms_it = self.atoms.iterator(); - while (atoms_it.next()) |entry| { - var atom = entry.value_ptr.*; - - while (true) { - for (atom.relocs.items) |rel| { - if ((try rel.getTargetAtom(self)) == null) { - const target_sym = self.getSymbol(rel.target); - if (target_sym.undf()) { - _ = try gc_roots.getOrPut(atom); - break; - } - } - } - - if (atom.prev) |prev| { - atom = prev; - } else break; + // TODO just a temp until we learn how to parse unwind records + if (self.globals.get("___gxx_personality_v0")) |global| { + if (self.getAtomForSymbol(global)) |atom| { + _ = try gc_roots.getOrPut(atom); } } @@ -5540,80 +5544,80 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void { defer stack.deinit(); try stack.ensureUnusedCapacity(gc_roots.count()); - var retained = std.AutoHashMap(*Atom, void).init(gpa); - defer retained.deinit(); - try retained.ensureUnusedCapacity(gc_roots.count()); + var alive = std.AutoHashMap(*Atom, void).init(gpa); + defer alive.deinit(); + try alive.ensureUnusedCapacity(gc_roots.count()); log.debug("GC roots:", .{}); var gc_roots_it = gc_roots.keyIterator(); while (gc_roots_it.next()) |gc_root| { self.logAtom(gc_root.*); - stack.appendAssumeCapacity(gc_root.*); - retained.putAssumeCapacityNoClobber(gc_root.*, {}); + alive.putAssumeCapacity(gc_root.*, {}); } - log.debug("walking tree...", .{}); while (stack.popOrNull()) |source_atom| { for (source_atom.relocs.items) |rel| { - if (try rel.getTargetAtom(self)) |target_atom| { - const gop = try retained.getOrPut(target_atom); + if (rel.getTargetAtom(self)) |target_atom| { + const gop = try alive.getOrPut(target_atom); if (!gop.found_existing) { - log.debug(" RETAINED ATOM(%{d}) -> ATOM(%{d})", .{ - source_atom.sym_index, + log.debug(" retained ATOM(%{d}, '{s}') in object({d})", .{ target_atom.sym_index, + target_atom.getName(self), + target_atom.file, + }); + log.debug(" referenced by ATOM(%{d}, '{s}') in object({d})", .{ + source_atom.sym_index, + source_atom.getName(self), + source_atom.file, }); try stack.append(target_atom); } } } } + // TODO live support // Any section that ends up here will be updated, that is, // its size and alignment recalculated. var gc_sections = std.AutoHashMap(MatchingSection, void).init(gpa); defer gc_sections.deinit(); - atoms_it = self.atoms.iterator(); - while (atoms_it.next()) |entry| { - const match = entry.key_ptr.*; - - if (self.text_segment_cmd_index) |seg| { - if (seg == match.seg) { - if (self.eh_frame_section_index) |sect| { - if (sect == match.sect) continue; - } - } - } + var loop: bool = true; + while (loop) { + loop = false; - if (self.data_segment_cmd_index) |seg| { - if (seg == match.seg) { - if (self.rustc_section_index) |sect| { - if (sect == match.sect) continue; - } - } - } + for (self.objects.items) |object| { + for (object.getSourceSymtab()) |_, source_index| { + const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue; + if (alive.contains(atom)) continue; - const sect = self.getSectionPtr(match); - var atom = entry.value_ptr.*; + const global = atom.getSymbolWithLoc(); + const sym = atom.getSymbolPtr(self); - log.debug("GCing atoms in {s},{s}", .{ sect.segName(), sect.sectName() }); + if (sym.n_desc == N_DESC_GCED) continue; + if (!sym.ext()) { + for (atom.relocs.items) |rel| { + if (rel.getTargetAtom(self)) |target_atom| { + const target_sym = target_atom.getSymbol(self); + if (target_sym.n_desc == N_DESC_GCED) break; + } + } else continue; + } - while (true) { - const orig_prev = atom.prev; + loop = true; + const match = self.getMatchingSectionFromOrdinal(sym.n_sect); - if (!retained.contains(atom)) { - // Dead atom; remove. - log.debug(" DEAD ATOM(%{d})", .{atom.sym_index}); + // TODO don't dedup eh_frame info yet until we actually implement parsing unwind records + if (match.eql(.{ + .seg = self.text_segment_cmd_index, + .sect = self.eh_frame_section_index, + })) continue; - const sym = atom.getSymbolPtr(self); + self.logAtom(atom); sym.n_desc = N_DESC_GCED; - - // TODO add full bookkeeping here - const global = SymbolWithLoc{ .sym_index = atom.sym_index, .file = atom.file }; - _ = self.got_entries_table.swapRemove(global); - _ = self.stubs_table.swapRemove(global); - _ = self.tlv_ptr_entries_table.swapRemove(global); + self.removeAtomFromSection(atom, match); + _ = try gc_sections.put(match, {}); for (atom.contained.items) |sym_off| { const inner = self.getSymbolPtr(.{ @@ -5622,34 +5626,64 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void { }); inner.n_desc = N_DESC_GCED; } - // If we want to enable GC for incremental codepath, we need to take into - // account any padding that might have been left here. - sect.size -= atom.size; - _ = try gc_sections.put(match, {}); + if (self.got_entries_table.contains(global)) { + const got_atom = self.getGotAtomForSymbol(global).?; + const got_sym = got_atom.getSymbolPtr(self); + got_sym.n_desc = N_DESC_GCED; + } - if (atom.prev) |prev| { - prev.next = atom.next; + if (self.stubs_table.contains(global)) { + const stubs_atom = self.getStubsAtomForSymbol(global).?; + const stubs_sym = stubs_atom.getSymbolPtr(self); + stubs_sym.n_desc = N_DESC_GCED; } - if (atom.next) |next| { - next.prev = atom.prev; - } else { - if (atom.prev) |prev| { - entry.value_ptr.* = prev; - } else { - // The section will be GCed in the next step. - entry.value_ptr.* = undefined; - sect.size = 0; - } + + if (self.tlv_ptr_entries_table.contains(global)) { + const tlv_ptr_atom = self.getTlvPtrAtomForSymbol(global).?; + const tlv_ptr_sym = tlv_ptr_atom.getSymbolPtr(self); + tlv_ptr_sym.n_desc = N_DESC_GCED; } } - - if (orig_prev) |prev| { - atom = prev; - } else break; } } + for (self.got_entries.items) |entry| { + const sym = entry.getSymbol(self); + if (sym.n_desc != N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(self); + const match = self.getMatchingSectionFromOrdinal(sym.n_sect); + self.removeAtomFromSection(atom, match); + _ = try gc_sections.put(match, {}); + _ = self.got_entries_table.remove(entry.target); + } + + for (self.stubs.items) |entry| { + const sym = entry.getSymbol(self); + if (sym.n_desc != N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(self); + const match = self.getMatchingSectionFromOrdinal(sym.n_sect); + self.removeAtomFromSection(atom, match); + _ = try gc_sections.put(match, {}); + _ = self.stubs_table.remove(entry.target); + } + + for (self.tlv_ptr_entries.items) |entry| { + const sym = entry.getSymbol(self); + if (sym.n_desc != N_DESC_GCED) continue; + + // TODO tombstone + const atom = entry.getAtom(self); + const match = self.getMatchingSectionFromOrdinal(sym.n_sect); + self.removeAtomFromSection(atom, match); + _ = try gc_sections.put(match, {}); + _ = self.tlv_ptr_entries_table.remove(entry.target); + } + var gc_sections_it = gc_sections.iterator(); while (gc_sections_it.next()) |entry| { const match = entry.key_ptr.*; @@ -5679,6 +5713,30 @@ fn gcAtoms(self: *MachO, gc_roots: *std.AutoHashMap(*Atom, void)) !void { } } +fn removeAtomFromSection(self: *MachO, atom: *Atom, match: MatchingSection) void { + const sect = self.getSectionPtr(match); + + // If we want to enable GC for incremental codepath, we need to take into + // account any padding that might have been left here. + sect.size -= atom.size; + + if (atom.prev) |prev| { + prev.next = atom.next; + } + if (atom.next) |next| { + next.prev = atom.prev; + } else { + const last = self.atoms.getPtr(match).?; + if (atom.prev) |prev| { + last.* = prev; + } else { + // The section will be GCed in the next step. + last.* = undefined; + sect.size = 0; + } + } +} + fn updateSectionOrdinals(self: *MachO) !void { if (!self.sections_order_dirty) return; @@ -5849,7 +5907,7 @@ fn writeDyldInfoData(self: *MachO) !void { if (self.base.options.output_mode == .Exe) { for (&[_]SymbolWithLoc{ - self.getEntryPoint().?, // We would already errored out if no entrypoint was found. + try self.getEntryPoint(), self.globals.get("__mh_execute_header").?, }) |global| { const sym = self.getSymbol(global); @@ -6337,10 +6395,13 @@ fn writeSymtab(self: *MachO) !void { .sect = stubs_section_index, }); stubs.reserved1 = 0; - for (self.stubs_table.keys()) |target| { - const sym = self.getSymbol(target); - assert(sym.undf()); - try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?); + for (self.stubs.items) |entry| { + if (entry.sym_index == 0) continue; + const atom_sym = entry.getSymbol(self); + if (atom_sym.n_desc == N_DESC_GCED) continue; + const target_sym = self.getSymbol(entry.target); + assert(target_sym.undf()); + try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?); } } @@ -6351,10 +6412,13 @@ fn writeSymtab(self: *MachO) !void { .sect = got_section_index, }); got.reserved1 = nstubs; - for (self.got_entries_table.keys()) |target| { - const sym = self.getSymbol(target); - if (sym.undf()) { - try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?); + for (self.got_entries.items) |entry| { + if (entry.sym_index == 0) continue; + const atom_sym = entry.getSymbol(self); + if (atom_sym.n_desc == N_DESC_GCED) continue; + const target_sym = self.getSymbol(entry.target); + if (target_sym.undf()) { + try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?); } else { try writer.writeIntLittle(u32, macho.INDIRECT_SYMBOL_LOCAL); } @@ -6368,10 +6432,13 @@ fn writeSymtab(self: *MachO) !void { .sect = la_symbol_ptr_section_index, }); la_symbol_ptr.reserved1 = nstubs + ngot_entries; - for (self.stubs_table.keys()) |target| { - const sym = self.getSymbol(target); - assert(sym.undf()); - try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(target).?); + for (self.stubs.items) |entry| { + if (entry.sym_index == 0) continue; + const atom_sym = entry.getSymbol(self); + if (atom_sym.n_desc == N_DESC_GCED) continue; + const target_sym = self.getSymbol(entry.target); + assert(target_sym.undf()); + try writer.writeIntLittle(u32, dysymtab.iundefsym + imports_table.get(entry.target).?); } } @@ -6623,7 +6690,7 @@ pub fn getSymbolName(self: *MachO, sym_with_loc: SymbolWithLoc) []const u8 { pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { if (sym_with_loc.file) |file| { const object = self.objects.items[file]; - return object.atom_by_index_table.get(sym_with_loc.sym_index); + return object.getAtomForSymbol(sym_with_loc.sym_index); } else { return self.atom_by_index_table.get(sym_with_loc.sym_index); } @@ -6633,28 +6700,32 @@ pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { /// Returns null otherwise. pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { const got_index = self.got_entries_table.get(sym_with_loc) orelse return null; - return self.got_entries.items[got_index].atom; + return self.got_entries.items[got_index].getAtom(self); } /// Returns stubs atom that references `sym_with_loc` if one exists. /// Returns null otherwise. pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null; - return self.stubs.items[stubs_index].atom; + return self.stubs.items[stubs_index].getAtom(self); } /// Returns TLV pointer atom that references `sym_with_loc` if one exists. /// Returns null otherwise. pub fn getTlvPtrAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { const tlv_ptr_index = self.tlv_ptr_entries_table.get(sym_with_loc) orelse return null; - return self.tlv_ptr_entries.items[tlv_ptr_index].atom; + return self.tlv_ptr_entries.items[tlv_ptr_index].getAtom(self); } /// Returns symbol location corresponding to the set entrypoint. /// Asserts output mode is executable. -pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { +pub fn getEntryPoint(self: MachO) error{MissingMainEntrypoint}!SymbolWithLoc { const entry_name = self.base.options.entry orelse "_main"; - return self.globals.get(entry_name); + const global = self.globals.get(entry_name) orelse { + log.err("entrypoint '{s}' not found", .{entry_name}); + return error.MissingMainEntrypoint; + }; + return global; } pub fn findFirst(comptime T: type, haystack: []const T, start: usize, predicate: anytype) usize { @@ -6986,7 +7057,7 @@ fn snapshotState(self: *MachO) !void { break :blk source_sym.n_value + rel.offset; }; const target_addr = blk: { - const target_atom = (try rel.getTargetAtom(self)) orelse { + const target_atom = rel.getTargetAtom(self) orelse { // If there is no atom for target, we still need to check for special, atom-less // symbols such as `___dso_handle`. const target_name = self.getSymbolName(rel.target); @@ -7119,8 +7190,9 @@ fn snapshotState(self: *MachO) !void { try writer.writeByte(']'); } -pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 { - mem.set(u8, buf, '_'); +fn logSymAttributes(sym: macho.nlist_64, buf: *[9]u8) []const u8 { + mem.set(u8, buf[0..4], '_'); + mem.set(u8, buf[4..], ' '); if (sym.sect()) { buf[0] = 's'; } @@ -7137,11 +7209,14 @@ pub fn logSymAttributes(sym: macho.nlist_64, buf: *[4]u8) []const u8 { if (sym.undf()) { buf[3] = 'u'; } + if (sym.n_desc == N_DESC_GCED) { + mem.copy(u8, buf[5..], "DEAD"); + } return buf[0..]; } fn logSymtab(self: *MachO) void { - var buf: [4]u8 = undefined; + var buf: [9]u8 = undefined; log.debug("symtab:", .{}); for (self.objects.items) |object, id| { @@ -7186,42 +7261,50 @@ fn logSymtab(self: *MachO) void { } log.debug("GOT entries:", .{}); - for (self.got_entries_table.values()) |value| { - const target = self.got_entries.items[value].target; - const target_sym = self.getSymbol(target); - const atom = self.got_entries.items[value].atom; - const atom_sym = atom.getSymbol(self); - + for (self.got_entries.items) |entry, i| { + const atom_sym = entry.getSymbol(self); + if (atom_sym.n_desc == N_DESC_GCED) continue; + const target_sym = self.getSymbol(entry.target); if (target_sym.undf()) { - log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) }); + log.debug(" {d}@{x} => import('{s}')", .{ + i, + atom_sym.n_value, + self.getSymbolName(entry.target), + }); } else { - log.debug(" {d}@{x} => local(%{d}) in object({d})", .{ - value, + log.debug(" {d}@{x} => local(%{d}) in object({d}) {s}", .{ + i, atom_sym.n_value, - target.sym_index, - target.file, + entry.target.sym_index, + entry.target.file, + logSymAttributes(target_sym, &buf), }); } } log.debug("__thread_ptrs entries:", .{}); - for (self.tlv_ptr_entries_table.values()) |value| { - const target = self.tlv_ptr_entries.items[value].target; - const target_sym = self.getSymbol(target); - const atom = self.tlv_ptr_entries.items[value].atom; - const atom_sym = atom.getSymbol(self); + for (self.tlv_ptr_entries.items) |entry, i| { + const atom_sym = entry.getSymbol(self); + if (atom_sym.n_desc == N_DESC_GCED) continue; + const target_sym = self.getSymbol(entry.target); assert(target_sym.undf()); - log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) }); + log.debug(" {d}@{x} => import('{s}')", .{ + i, + atom_sym.n_value, + self.getSymbolName(entry.target), + }); } log.debug("stubs entries:", .{}); - for (self.stubs_table.values()) |value| { - const target = self.stubs.items[value].target; - const target_sym = self.getSymbol(target); - const atom = self.stubs.items[value].atom; - const atom_sym = atom.getSymbol(self); + for (self.stubs.items) |entry, i| { + const target_sym = self.getSymbol(entry.target); + const atom_sym = entry.getSymbol(self); assert(target_sym.undf()); - log.debug(" {d}@{x} => import('{s}')", .{ value, atom_sym.n_value, self.getSymbolName(target) }); + log.debug(" {d}@{x} => import('{s}')", .{ + i, + atom_sym.n_value, + self.getSymbolName(entry.target), + }); } } @@ -7248,7 +7331,6 @@ fn logAtoms(self: *MachO) void { while (true) { self.logAtom(atom); - if (atom.next) |next| { atom = next; } else break; @@ -7256,14 +7338,17 @@ fn logAtoms(self: *MachO) void { } } -pub fn logAtom(self: *MachO, atom: *const Atom) void { +fn logAtom(self: *MachO, atom: *const Atom) void { const sym = atom.getSymbol(self); const sym_name = atom.getName(self); - log.debug(" ATOM(%{d}, '{s}') @ {x} in object({d})", .{ + log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({d}) in sect({d})", .{ atom.sym_index, sym_name, sym.n_value, + atom.size, + atom.alignment, atom.file, + sym.n_sect, }); for (atom.contained.items) |sym_off| { @@ -7271,13 +7356,15 @@ pub fn logAtom(self: *MachO, atom: *const Atom) void { .sym_index = sym_off.sym_index, .file = atom.file, }); - const inner_sym_name = self.getSymbolName(.{ .sym_index = sym_off.sym_index, .file = atom.file }); - log.debug(" (%{d}, '{s}') @ {x} ({x}) in object({d})", .{ + const inner_sym_name = self.getSymbolName(.{ + .sym_index = sym_off.sym_index, + .file = atom.file, + }); + log.debug(" (%{d}, '{s}') @ {x} ({x})", .{ sym_off.sym_index, inner_sym_name, inner_sym.n_value, sym_off.offset, - atom.file, }); } } diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 2db680889a..acaeab7a88 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -94,7 +94,7 @@ pub const Relocation = struct { @"type": u4, - pub fn getTargetAtom(self: Relocation, macho_file: *MachO) !?*Atom { + pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom { const is_via_got = got: { switch (macho_file.base.options.target.cpu.arch) { .aarch64 => break :got switch (@intToEnum(macho.reloc_type_arm64, self.@"type")) { @@ -112,21 +112,9 @@ pub const Relocation = struct { } }; - const target_sym = macho_file.getSymbol(self.target); if (is_via_got) { - const got_atom = macho_file.getGotAtomForSymbol(self.target) orelse { - log.err("expected GOT entry for symbol", .{}); - if (target_sym.undf()) { - log.err(" import('{s}')", .{macho_file.getSymbolName(self.target)}); - } else { - log.err(" local(%{d}) in object({d})", .{ self.target.sym_index, self.target.file }); - } - log.err(" this is an internal linker error", .{}); - return error.FailedToResolveRelocationTarget; - }; - return got_atom; + return macho_file.getGotAtomForSymbol(self.target).?; // panic means fatal error } - if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom; if (macho_file.getTlvPtrAtomForSymbol(self.target)) |tlv_ptr_atom| return tlv_ptr_atom; return macho_file.getAtomForSymbol(self.target); @@ -174,6 +162,10 @@ pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 { }); } +pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { + return .{ .sym_index = self.sym_index, .file = self.file }; +} + /// Returns true if the symbol pointed at with `sym_loc` is contained within this atom. /// WARNING this function assumes all atoms have been allocated in the virtual memory. /// Calling it without allocating with `MachO.allocateSymbols` (or equivalent) will @@ -515,7 +507,7 @@ fn addTlvPtrEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void { const index = try context.macho_file.allocateTlvPtrEntry(target); const atom = try context.macho_file.createTlvPtrAtom(target); - context.macho_file.tlv_ptr_entries.items[index].atom = atom; + context.macho_file.tlv_ptr_entries.items[index].sym_index = atom.sym_index; } fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void { @@ -523,7 +515,7 @@ fn addGotEntry(target: MachO.SymbolWithLoc, context: RelocContext) !void { const index = try context.macho_file.allocateGotEntry(target); const atom = try context.macho_file.createGotAtom(target); - context.macho_file.got_entries.items[index].atom = atom; + context.macho_file.got_entries.items[index].sym_index = atom.sym_index; } fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void { @@ -536,7 +528,7 @@ fn addStub(target: MachO.SymbolWithLoc, context: RelocContext) !void { const laptr_atom = try context.macho_file.createLazyPointerAtom(stub_helper_atom.sym_index, target); const stub_atom = try context.macho_file.createStubAtom(laptr_atom.sym_index); - context.macho_file.stubs.items[stub_index].atom = stub_atom; + context.macho_file.stubs.items[stub_index].sym_index = stub_atom.sym_index; } pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { @@ -578,7 +570,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES; }; const target_addr = blk: { - const target_atom = (try rel.getTargetAtom(macho_file)) orelse { + const target_atom = rel.getTargetAtom(macho_file) orelse { // If there is no atom for target, we still need to check for special, atom-less // symbols such as `___dso_handle`. const target_name = macho_file.getSymbolName(rel.target); @@ -597,6 +589,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { macho_file.getSymbol(rel.target) else target_atom.getSymbol(macho_file); + assert(target_sym.n_desc != MachO.N_DESC_GCED); const base_address: u64 = if (is_tlv) base_address: { // For TLV relocations, the value specified as a relocation is the displacement from the // 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 { }; log.debug(" | source_addr = 0x{x}", .{source_addr}); - log.debug(" | target_addr = 0x{x}", .{target_addr}); switch (arch) { .aarch64 => { switch (@intToEnum(macho.reloc_type_arm64, rel.@"type")) { .ARM64_RELOC_BRANCH26 => { + log.debug(" | target_addr = 0x{x}", .{target_addr}); const displacement = math.cast( i28, @intCast(i64, target_addr) - @intCast(i64, source_addr), @@ -658,6 +651,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { .ARM64_RELOC_TLVP_LOAD_PAGE21, => { const actual_target_addr = @intCast(i64, target_addr) + rel.addend; + log.debug(" | target_addr = 0x{x}", .{actual_target_addr}); const source_page = @intCast(i32, source_addr >> 12); const target_page = @intCast(i32, actual_target_addr >> 12); const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); @@ -675,6 +669,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { .ARM64_RELOC_PAGEOFF12 => { const code = self.code.items[rel.offset..][0..4]; const actual_target_addr = @intCast(i64, target_addr) + rel.addend; + log.debug(" | target_addr = 0x{x}", .{actual_target_addr}); const narrowed = @truncate(u12, @intCast(u64, actual_target_addr)); if (isArithmeticOp(self.code.items[rel.offset..][0..4])) { var inst = aarch64.Instruction{ @@ -712,6 +707,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { .ARM64_RELOC_GOT_LOAD_PAGEOFF12 => { const code = self.code.items[rel.offset..][0..4]; const actual_target_addr = @intCast(i64, target_addr) + rel.addend; + log.debug(" | target_addr = 0x{x}", .{actual_target_addr}); const narrowed = @truncate(u12, @intCast(u64, actual_target_addr)); var inst: aarch64.Instruction = .{ .load_store_register = mem.bytesToValue(meta.TagPayload( @@ -726,6 +722,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { .ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => { const code = self.code.items[rel.offset..][0..4]; const actual_target_addr = @intCast(i64, target_addr) + rel.addend; + log.debug(" | target_addr = 0x{x}", .{actual_target_addr}); const RegInfo = struct { rd: u5, @@ -783,6 +780,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { mem.writeIntLittle(u32, code, inst.toU32()); }, .ARM64_RELOC_POINTER_TO_GOT => { + log.debug(" | target_addr = 0x{x}", .{target_addr}); const result = math.cast(i32, @intCast(i64, target_addr) - @intCast(i64, source_addr)) orelse return error.Overflow; mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, result)); }, @@ -795,6 +793,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { break :blk @intCast(i64, target_addr) + rel.addend; } }; + log.debug(" | target_addr = 0x{x}", .{result}); if (rel.length == 3) { 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 { .x86_64 => { switch (@intToEnum(macho.reloc_type_x86_64, rel.@"type")) { .X86_64_RELOC_BRANCH => { + log.debug(" | target_addr = 0x{x}", .{target_addr}); const displacement = math.cast( i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend, @@ -820,6 +820,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_GOT, .X86_64_RELOC_GOT_LOAD => { + log.debug(" | target_addr = 0x{x}", .{target_addr}); const displacement = math.cast( i32, @intCast(i64, target_addr) - @intCast(i64, source_addr) - 4 + rel.addend, @@ -827,6 +828,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { mem.writeIntLittle(u32, self.code.items[rel.offset..][0..4], @bitCast(u32, displacement)); }, .X86_64_RELOC_TLV => { + log.debug(" | target_addr = 0x{x}", .{target_addr}); if (!macho_file.tlv_ptr_entries_table.contains(rel.target)) { // We need to rewrite the opcode from movq to leaq. self.code.items[rel.offset - 2] = 0x8d; @@ -850,6 +852,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { else => unreachable, }; const actual_target_addr = @intCast(i64, target_addr) + rel.addend; + log.debug(" | target_addr = 0x{x}", .{actual_target_addr}); const displacement = math.cast( i32, actual_target_addr - @intCast(i64, source_addr + correction + 4), @@ -865,6 +868,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void { break :blk @intCast(i64, target_addr) + rel.addend; } }; + log.debug(" | target_addr = 0x{x}", .{result}); if (rel.length == 3) { mem.writeIntLittle(u64, self.code.items[rel.offset..][0..8], @bitCast(u64, result)); diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index a5c65abed3..4da106eca1 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -275,9 +275,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti const sym = switch (reloc.@"type") { .direct_load => self.base.getSymbol(.{ .sym_index = reloc.target, .file = null }), .got_load => blk: { - const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?; - const got_atom = self.base.got_entries.items[got_index].atom; - break :blk got_atom.getSymbol(self.base); + const got_index = self.base.got_entries_table.get(.{ + .sym_index = reloc.target, + .file = null, + }).?; + const got_entry = self.base.got_entries.items[got_index]; + break :blk got_entry.getSymbol(self.base); }, }; if (sym.n_value == reloc.prev_vaddr) continue; @@ -285,9 +288,12 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti const sym_name = switch (reloc.@"type") { .direct_load => self.base.getSymbolName(.{ .sym_index = reloc.target, .file = null }), .got_load => blk: { - const got_index = self.base.got_entries_table.get(.{ .sym_index = reloc.target, .file = null }).?; - const got_atom = self.base.got_entries.items[got_index].atom; - break :blk got_atom.getName(self.base); + const got_index = self.base.got_entries_table.get(.{ + .sym_index = reloc.target, + .file = null, + }).?; + const got_entry = self.base.got_entries.items[got_index]; + break :blk got_entry.getName(self.base); }, }; const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 07237d31aa..37b7d60e71 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -410,7 +410,9 @@ pub fn splitIntoAtomsOneShot( next_sym_count += atom_syms.len; assert(atom_syms.len > 0); - const sym_index = atom_syms[0].index; + const sym_index = for (atom_syms) |atom_sym| { + if (atom_sym.getSymbol(context).ext()) break atom_sym.index; + } else atom_syms[0].index; const atom_size = blk: { const end_addr = if (next_sym_count < filtered_syms.len) filtered_syms[next_sym_count].getSymbol(context).n_value @@ -570,12 +572,6 @@ fn createAtomFromSubsection( if (gc_roots) |gcr| { const is_gc_root = blk: { if (sect.isDontDeadStrip()) break :blk true; - if (sect.isDontDeadStripIfReferencesLive()) { - // TODO if isDontDeadStripIfReferencesLive we should analyse the edges - // before making it a GC root - break :blk true; - } - if (mem.eql(u8, "__StaticInit", sect.sectName())) break :blk true; switch (sect.type_()) { macho.S_MOD_INIT_FUNC_POINTERS, macho.S_MOD_TERM_FUNC_POINTERS, @@ -641,3 +637,7 @@ pub fn getSection(self: Object, n_sect: u16) macho.section_64 { assert(n_sect < seg.sections.items.len); return seg.sections.items[n_sect]; } + +pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom { + return self.atom_by_index_table.get(sym_index); +} |
