Commit deeaa1bb0c

Jakub Konka <kubkon@jakubkonka.com>
2024-08-01 08:50:01
elf: redo symbol mgmt and ownership in ZigObject
1 parent de80e4f
src/link/Elf/file.zig
@@ -153,19 +153,6 @@ pub const File = union(enum) {
         };
     }
 
-    pub fn locals(file: File) []const Symbol.Index {
-        return switch (file) {
-            .linker_defined, .shared_object => &[0]Symbol.Index{},
-            inline else => |x| x.locals(),
-        };
-    }
-
-    pub fn globals(file: File) []const Symbol.Index {
-        return switch (file) {
-            inline else => |x| x.globals(),
-        };
-    }
-
     pub fn getString(file: File, off: u32) [:0]const u8 {
         return switch (file) {
             inline else => |x| x.getString(off),
src/link/Elf/LinkerDefined.zig
@@ -302,12 +302,8 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void {
     }
 }
 
-pub fn globals(self: *LinkerDefined) []Symbol {
-    return self.symbols.items;
-}
-
 pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
-    for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
+    for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| {
         const ref = elf_file.resolver.get(resolv).?;
         const ref_sym = elf_file.symbol(ref) orelse continue;
         if (ref_sym.file(elf_file).?.index() != self.index) continue;
@@ -324,7 +320,7 @@ pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
 }
 
 pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf) void {
-    for (self.globals(), self.symbols_resolver.items) |global, resolv| {
+    for (self.symbols.items, self.symbols_resolver.items) |global, resolv| {
         const ref = elf_file.resolver.get(resolv).?;
         const ref_sym = elf_file.symbol(ref) orelse continue;
         if (ref_sym.file(elf_file).?.index() != self.index) continue;
src/link/Elf/Object.zig
@@ -676,6 +676,29 @@ pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void {
     }
 }
 
+pub fn markImportsExports(self: *Object, elf_file: *Elf) void {
+    const first_global = self.first_global orelse return;
+    for (0..self.globals().len) |i| {
+        const idx = first_global + i;
+        const ref = self.resolveSymbol(@intCast(idx), elf_file);
+        const sym = elf_file.symbol(ref) orelse continue;
+        const file = sym.file(elf_file).?;
+        if (sym.version_index == elf.VER_NDX_LOCAL) continue;
+        const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other));
+        if (vis == .HIDDEN) continue;
+        if (file == .shared_object and !sym.isAbs(elf_file)) {
+            sym.flags.import = true;
+            continue;
+        }
+        if (file.index() == self.index) {
+            sym.flags.@"export" = true;
+            if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) {
+                sym.flags.import = true;
+            }
+        }
+    }
+}
+
 pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
     const first_global = self.first_global orelse return;
     for (0..self.globals().len) |i| {
@@ -1169,14 +1192,14 @@ pub fn codeDecompressAlloc(self: *Object, elf_file: *Elf, atom_index: Atom.Index
     return data;
 }
 
-pub fn locals(self: *Object) []Symbol {
+fn locals(self: *Object) []Symbol {
     if (self.symbols.items.len == 0) return &[0]Symbol{};
     assert(self.symbols.items.len >= self.symtab.items.len);
     const end = self.first_global orelse self.symtab.items.len;
     return self.symbols.items[0..end];
 }
 
-pub fn globals(self: *Object) []Symbol {
+fn globals(self: *Object) []Symbol {
     if (self.symbols.items.len == 0) return &[0]Symbol{};
     assert(self.symbols.items.len >= self.symtab.items.len);
     const start = self.first_global orelse self.symtab.items.len;
src/link/Elf/SharedObject.zig
@@ -282,12 +282,18 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
     }
 }
 
-pub fn globals(self: *SharedObject) []Symbol {
-    return self.symbols.items;
+pub fn markImportExports(self: *SharedObject, elf_file: *Elf) void {
+    for (0..self.symbols.items.len) |i| {
+        const ref = self.resolveSymbol(@intCast(i), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        const ref_file = ref_sym.file(self).?;
+        const vis = @as(elf.STV, @enumFromInt(ref_sym.elfSym(self).st_other));
+        if (ref_file != .shared_object and vis != .HIDDEN) ref_sym.flags.@"export" = true;
+    }
 }
 
 pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
-    for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
+    for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| {
         const ref = elf_file.resolver.get(resolv).?;
         const ref_sym = elf_file.symbol(ref) orelse continue;
         if (ref_sym.file(elf_file).?.index() != self.index) continue;
@@ -300,7 +306,7 @@ pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
 }
 
 pub fn writeSymtab(self: *SharedObject, elf_file: *Elf) void {
-    for (self.globals(), self.symbols_resolver.items) |global, resolv| {
+    for (self.symbols.items, self.symbols_resolver.items) |global, resolv| {
         const ref = elf_file.resolver.get(resolv).?;
         const ref_sym = elf_file.symbol(ref) orelse continue;
         if (ref_sym.file(elf_file).?.index() != self.index) continue;
@@ -354,7 +360,7 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
     const gpa = comp.gpa;
     var aliases = std.ArrayList(Symbol.Index).init(gpa);
     defer aliases.deinit();
-    try aliases.ensureTotalCapacityPrecise(self.globals().len);
+    try aliases.ensureTotalCapacityPrecise(self.symbols.items.len);
 
     for (self.symbols_resolvers.items, 0..) |resolv, index| {
         const ref = elf_file.resolver.get(resolv).?;
src/link/Elf/Symbol.zig
@@ -63,9 +63,7 @@ pub fn @"type"(symbol: Symbol, elf_file: *Elf) u4 {
 }
 
 pub fn name(symbol: Symbol, elf_file: *Elf) [:0]const u8 {
-    if (symbol.flags.global) return elf_file.strings.getAssumeExists(symbol.name_offset);
-    const file_ptr = symbol.file(elf_file).?;
-    return switch (file_ptr) {
+    return switch (symbol.file(elf_file).?) {
         inline else => |x| x.getString(symbol.name_offset),
     };
 }
@@ -87,9 +85,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
 }
 
 pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
-    const file_ptr = symbol.file(elf_file).?;
-    return switch (file_ptr) {
-        .zig_object => |x| x.elfSym(symbol.esym_index).*,
+    return switch (symbol.file(elf_file).?) {
         inline else => |x| x.symtab.items[symbol.esym_index],
     };
 }
@@ -423,12 +419,6 @@ pub const Flags = packed struct {
     /// Whether this symbol is weak.
     weak: bool = false,
 
-    /// Whether the symbol has its name interned in global symbol
-    /// resolver table.
-    /// This happens for any symbol that is considered a global
-    /// symbol, but is not necessarily an import or export.
-    global: bool = false,
-
     /// Whether the symbol makes into the output symtab.
     output_symtab: bool = false,
 
src/link/Elf/ZigObject.zig
@@ -8,9 +8,11 @@ data: std.ArrayListUnmanaged(u8) = .{},
 path: []const u8,
 index: File.Index,
 
-local_esyms: std.MultiArrayList(ElfSym) = .{},
-global_esyms: std.MultiArrayList(ElfSym) = .{},
+symtab: std.MultiArrayList(ElfSym) = .{},
 strtab: StringTable = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{},
 local_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
 global_symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
 globals_lookup: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{},
@@ -113,9 +115,11 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
 
 pub fn deinit(self: *ZigObject, allocator: Allocator) void {
     self.data.deinit(allocator);
-    self.local_esyms.deinit(allocator);
-    self.global_esyms.deinit(allocator);
+    self.symtab.deinit(allocator);
     self.strtab.deinit(allocator);
+    self.symbols.deinit(allocator);
+    self.symbols_extra.deinit(allocator);
+    self.symbols_resolver.deinit(allocator);
     self.local_symbols.deinit(allocator);
     self.global_symbols.deinit(allocator);
     self.globals_lookup.deinit(allocator);
@@ -263,51 +267,73 @@ fn saveDebugSectionsSizes(self: *ZigObject, elf_file: *Elf) void {
     }
 }
 
-pub fn addLocalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
-    try self.local_esyms.ensureUnusedCapacity(allocator, 1);
-    const index = @as(Symbol.Index, @intCast(self.local_esyms.addOneAssumeCapacity()));
-    var esym = ElfSym{ .elf_sym = Elf.null_sym };
-    esym.elf_sym.st_info = elf.STB_LOCAL << 4;
-    self.local_esyms.set(index, esym);
+fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4) !Symbol.Index {
+    try self.symtab.ensureUnusedCapacity(allocator, 1);
+    try self.symbols.ensureUnusedCapacity(allocator, 1);
+    try self.symbols_extra.ensureUnusedCapacity(allocator, @sizeOf(Symbol.Extra));
+
+    const index = self.addSymbolAssumeCapacity();
+    const sym = &self.symbols.items[index];
+    sym.name_offset = name_off;
+    sym.extra = self.addSymbolExtraAssumeCapacity(.{});
+
+    const esym_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
+    const esym = ElfSym{ .elf_sym = .{
+        .st_value = 0,
+        .st_name = name_off,
+        .st_info = @as(u8, @intCast(st_bind)) << 4,
+        .st_other = 0,
+        .st_size = 0,
+        .st_shndx = 0,
+    } };
+    self.symtab.set(index, esym);
+    sym.esym_index = esym_idx;
+
     return index;
 }
 
-pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index {
-    try self.global_esyms.ensureUnusedCapacity(allocator, 1);
-    const index = @as(Symbol.Index, @intCast(self.global_esyms.addOneAssumeCapacity()));
-    var esym = ElfSym{ .elf_sym = Elf.null_sym };
-    esym.elf_sym.st_info = elf.STB_GLOBAL << 4;
-    self.global_esyms.set(index, esym);
-    return index | global_symbol_bit;
+fn newLocalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
+    try self.local_symbols.ensureUnusedCapacity(allocator, 1);
+    const fake_index: Symbol.Index = @intCast(self.local_symbols.items.len);
+    const index = try self.newSymbol(allocator, name_off, elf.STB_LOCAL);
+    self.local_symbols.appendAssumeCapacity(index);
+    return fake_index;
 }
 
-pub fn newAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index {
-    const gpa = elf_file.base.comp.gpa;
-    const atom_index = try self.addAtom(gpa);
-    const symbol_index = try elf_file.addSymbol();
-    const esym_index = try self.addLocalEsym(gpa);
-
-    try self.atoms_indexes.append(gpa, atom_index);
-    try self.local_symbols.append(gpa, symbol_index);
-
-    const symbol_ptr = elf_file.symbol(symbol_index);
-    symbol_ptr.file_index = self.index;
-    symbol_ptr.ref = .{ .index = atom_index, .file = self.index };
-    symbol_ptr.extra_index = try elf_file.addSymbolExtra(.{});
+fn newGlobalSymbol(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
+    try self.global_symbols.ensureUnusedCapacity(allocator, 1);
+    const fake_index: Symbol.Index = @intCast(self.global_symbols.items.len);
+    const index = try self.newSymbol(allocator, name_off, elf.STB_GLOBAL);
+    self.global_symbols.appendAssumeCapacity(index);
+    return fake_index | global_symbol_bit;
+}
 
-    self.local_esyms.items(.shndx)[esym_index] = atom_index;
-    self.local_esyms.items(.elf_sym)[esym_index].st_shndx = SHN_ATOM;
-    symbol_ptr.esym_index = esym_index;
+fn newAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Atom.Index {
+    try self.atoms.ensureUnusedCapacity(allocator, 1);
+    try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra));
+    try self.atoms_indexes.ensureUnusedCapacity(allocator, 1);
+    try self.relocs.ensureUnusedCapacity(allocator, 1);
 
-    // TODO I'm thinking that maybe we shouldn' set this value unless it's actually needed?
-    const relocs_index = @as(u32, @intCast(self.relocs.items.len));
-    const relocs = try self.relocs.addOne(gpa);
-    relocs.* = .{};
+    const index = self.addAtomAssumeCapacity();
+    self.atoms_indexes.appendAssumeCapacity(index);
+    const atom_ptr = self.atom(index).?;
+    atom_ptr.name_offset = name_off;
 
-    const atom_ptr = self.atom(atom_index).?;
+    const relocs_index: u32 = @intCast(self.relocs.items.len);
+    self.relocs.addOneAssumeCapacity().* = .{};
     atom_ptr.relocs_section_index = relocs_index;
 
-    return symbol_index;
+    return index;
+}
+
+fn newSymbolWithAtom(self: *ZigObject, allocator: Allocator, name_off: u32) !Symbol.Index {
+    const atom_index = try self.newAtom(allocator, name_off);
+    const sym_index = try self.newLocalSymbol(allocator, name_off);
+    const sym = self.symbol(sym_index);
+    sym.ref = .{ .index = atom_index, .file = self.index };
+    self.symtab.items(.shndx)[sym.esym_index] = atom_index;
+    self.symtab.items(.elf_sym)[sym.esym_index].st_shndx = SHN_ATOM;
+    return sym_index;
 }
 
 /// TODO actually create fake input shdrs and return that instead.
@@ -322,48 +348,47 @@ pub fn inputShdr(self: *ZigObject, atom_index: Atom.Index, elf_file: *Elf) elf.E
     return shdr;
 }
 
-pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
-        const esym = self.global_esyms.items(.elf_sym)[i];
-        const shndx = self.global_esyms.items(.shndx)[i];
-
-        if (esym.st_shndx == elf.SHN_UNDEF) continue;
+pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) !void {
+    const gpa = elf_file.base.comp.gpa;
 
+    for (self.global_symbols.items, 0..) |index, i| {
+        const global = &self.symbols.items[index];
+        const esym = global.elfSym(elf_file);
+        const shndx = self.symtab.items(.shndx)[global.esym_index];
         if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) {
             assert(esym.st_shndx == SHN_ATOM);
             const atom_ptr = self.atom(shndx) orelse continue;
             if (!atom_ptr.alive) continue;
         }
 
-        const global = elf_file.symbol(index);
-        if (self.asFile().symbolRank(esym, false) < global.symbolRank(elf_file)) {
-            const atom_index = switch (esym.st_shndx) {
-                elf.SHN_ABS, elf.SHN_COMMON => 0,
-                SHN_ATOM => shndx,
-                else => unreachable,
-            };
-            global.value = @intCast(esym.st_value);
-            global.ref = .{ .index = atom_index, .file = self.index };
-            global.esym_index = esym_index;
-            global.file_index = self.index;
-            global.version_index = elf_file.default_sym_version;
-            if (esym.st_bind() == elf.STB_WEAK) global.flags.weak = true;
+        const resolv = &self.symbols_resolver.items[i];
+        const gop = try elf_file.resolver.getOrPut(gpa, .{
+            .index = @intCast(i | global_symbol_bit),
+            .file = self.index,
+        }, elf_file);
+        if (!gop.found_existing) {
+            gop.ref.* = .{ .index = 0, .file = 0 };
+        }
+        resolv.* = gop.index;
+
+        if (esym.st_shndx == elf.SHN_UNDEF) continue;
+        if (elf_file.symbol(gop.ref.*) == null) {
+            gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
+            continue;
+        }
+
+        if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
+            gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
         }
     }
 }
 
-pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
-        const esym = self.global_esyms.items(.elf_sym)[i];
-
+pub fn claimUnresolved(self: *ZigObject, elf_file: *Elf) void {
+    for (self.global_symbols.items, 0..) |index, i| {
+        const global = &self.symbols.items[index];
+        const esym = self.symtab.items(.elf_sym)[index];
         if (esym.st_shndx != elf.SHN_UNDEF) continue;
-
-        const global = elf_file.symbol(index);
-        if (global.file(elf_file)) |_| {
-            if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF) continue;
-        }
+        if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue;
 
         const is_import = blk: {
             if (!elf_file.isEffectivelyDynLib()) break :blk false;
@@ -374,29 +399,36 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void {
 
         global.value = 0;
         global.ref = .{ .index = 0, .file = 0 };
-        global.esym_index = esym_index;
+        global.esym_index = @intCast(index);
         global.file_index = self.index;
         global.version_index = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version;
         global.flags.import = is_import;
+
+        const idx = self.symbols_resolver.items[i];
+        elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
     }
 }
 
 pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(Symbol.Index, @intCast(i)) | global_symbol_bit;
-        const esym = self.global_esyms.items(.elf_sym)[i];
-
+    for (self.global_symbols.items, 0..) |index, i| {
+        const global = &self.symbols.items[index];
+        const esym = self.symtab.items(.elf_sym)[index];
         if (esym.st_shndx != elf.SHN_UNDEF) continue;
+        if (elf_file.symbol(self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file)) != null) continue;
 
-        const global = elf_file.symbol(index);
-        if (global.file(elf_file)) |file| {
-            if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue;
-        }
+        // TODO: audit this
+        // const global = elf_file.symbol(index);
+        // if (global.file(elf_file)) |file| {
+        //     if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue;
+        // }
 
         global.value = 0;
         global.ref = .{ .index = 0, .file = 0 };
-        global.esym_index = esym_index;
+        global.esym_index = @intCast(index);
         global.file_index = self.index;
+
+        const idx = self.symbols_resolver.items[i];
+        elf_file.resolver.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
     }
 }
 
@@ -419,12 +451,14 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
 }
 
 pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const esym = self.global_esyms.items(.elf_sym)[i];
+    for (self.global_symbols.items, 0..) |index, i| {
+        const global = self.symbols.items[index];
+        const esym = self.symtab.items(.elf_sym)[index];
         if (esym.st_bind() == elf.STB_WEAK) continue;
 
-        const global = elf_file.symbol(index);
-        const file = global.file(elf_file) orelse continue;
+        const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
+        const sym = elf_file.symbol(ref) orelse continue;
+        const file = sym.file(elf_file).?;
         const should_keep = esym.st_shndx == elf.SHN_UNDEF or
             (esym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
         if (should_keep and !file.isAlive()) {
@@ -434,14 +468,36 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
     }
 }
 
-pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
-    for (self.globals(), 0..) |index, i| {
-        const esym = self.global_esyms.items(.elf_sym)[i];
-        const shndx = self.global_esyms.items(.shndx)[i];
-        const global = elf_file.symbol(index);
-        const global_file = global.file(elf_file) orelse continue;
+pub fn markImportsExports(self: *Object, elf_file: *Elf) void {
+    for (0..self.global_symbols.items.len) |i| {
+        const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
+        const sym = elf_file.symbol(ref) orelse continue;
+        const file = sym.file(elf_file).?;
+        if (sym.version_index == elf.VER_NDX_LOCAL) continue;
+        const vis = @as(elf.STV, @enumFromInt(sym.elfSym(elf_file).st_other));
+        if (vis == .HIDDEN) continue;
+        if (file == .shared_object and !sym.isAbs(elf_file)) {
+            sym.flags.import = true;
+            continue;
+        }
+        if (file.index() == self.index) {
+            sym.flags.@"export" = true;
+            if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) {
+                sym.flags.import = true;
+            }
+        }
+    }
+}
 
-        if (self.index == global_file.index() or
+pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
+    for (self.global_symbols.items, 0..) |index, i| {
+        const esym = self.symtab.items(.elf_sym)[index];
+        const shndx = self.symtab.items(.shndx)[index];
+        const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        const ref_file = ref_sym.file(elf_file).?;
+
+        if (self.index == ref_file.index() or
             esym.st_shndx == elf.SHN_UNDEF or
             esym.st_bind() == elf.STB_WEAK or
             esym.st_shndx == elf.SHN_COMMON) continue;
@@ -451,7 +507,7 @@ pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{O
             if (!atom_ptr.alive) continue;
         }
 
-        const gop = try dupes.getOrPut(index);
+        const gop = try dupes.getOrPut(self.symbols_resolver.items[i]);
         if (!gop.found_existing) {
             gop.value_ptr.* = .{};
         }
@@ -483,12 +539,13 @@ pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void {
 pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
     const gpa = elf_file.base.comp.gpa;
 
-    try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len);
+    try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.global_symbols.items.len);
 
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file).?;
-        assert(file_ptr.index() == self.index);
+    for (self.global_symbols.items, 0..) |index, i| {
+        const global = self.symbols.items[index];
+        const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
+        const sym = elf_file.symbol(ref).?;
+        assert(sym.file(elf_file).?.index() == self.index);
         if (global.outputShndx(elf_file) == null) continue;
 
         const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file));
@@ -530,33 +587,9 @@ pub fn addAtomsToRelaSections(self: *ZigObject, elf_file: *Elf) !void {
     }
 }
 
-inline fn isGlobal(index: Symbol.Index) bool {
-    return index & global_symbol_bit != 0;
-}
-
-pub fn symbol(self: ZigObject, index: Symbol.Index) Symbol.Index {
-    const actual_index = index & symbol_mask;
-    if (isGlobal(index)) return self.globals()[actual_index];
-    return self.locals()[actual_index];
-}
-
-pub fn elfSym(self: *ZigObject, index: Symbol.Index) *elf.Elf64_Sym {
-    const actual_index = index & symbol_mask;
-    if (isGlobal(index)) return &self.global_esyms.items(.elf_sym)[actual_index];
-    return &self.local_esyms.items(.elf_sym)[actual_index];
-}
-
-pub fn locals(self: ZigObject) []const Symbol.Index {
-    return self.local_symbols.items;
-}
-
-pub fn globals(self: ZigObject) []const Symbol.Index {
-    return self.global_symbols.items;
-}
-
 pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
-    for (self.locals()) |local_index| {
-        const local = elf_file.symbol(local_index);
+    for (self.local_symbols.items) |index| {
+        const local = &self.symbols.items[index];
         if (local.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
         const esym = local.elfSym(elf_file);
         switch (esym.st_type()) {
@@ -564,22 +597,23 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
             else => {},
         }
         local.flags.output_symtab = true;
-        try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
+        local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
         self.output_symtab_ctx.nlocals += 1;
         self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
     }
 
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+    for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| {
+        const global = &self.symbols.items[index];
+        const ref = elf_file.resolver.items[resolv];
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         if (global.atom(elf_file)) |atom_ptr| if (!atom_ptr.alive) continue;
         global.flags.output_symtab = true;
         if (global.isLocal(elf_file)) {
-            try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
+            global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
             self.output_symtab_ctx.nlocals += 1;
         } else {
-            try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
+            global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
             self.output_symtab_ctx.nglobals += 1;
         }
         self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
@@ -587,8 +621,8 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
 }
 
 pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void {
-    for (self.locals()) |local_index| {
-        const local = elf_file.symbol(local_index);
+    for (self.local_symbols.items) |index| {
+        const local = &self.symbols.items[index];
         const idx = local.outputSymtabIndex(elf_file) orelse continue;
         const out_sym = &elf_file.symtab.items[idx];
         out_sym.st_name = @intCast(elf_file.strtab.items.len);
@@ -597,10 +631,11 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void {
         local.setOutputSym(elf_file, out_sym);
     }
 
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+    for (self.global_symbols.items, self.symbols_resolver.items) |index, resolv| {
+        const global = self.symbols.items[index];
+        const ref = elf_file.resolver.items[resolv];
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         const idx = global.outputSymtabIndex(elf_file) orelse continue;
         const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
         elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
@@ -611,10 +646,6 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void {
     }
 }
 
-pub fn asFile(self: *ZigObject) File {
-    return .{ .zig_object = self };
-}
-
 /// Returns atom's code.
 /// Caller owns the memory.
 pub fn codeAlloc(self: *ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 {
@@ -755,8 +786,8 @@ pub fn getOrCreateMetadataForLazySymbol(
     };
     switch (metadata.state.*) {
         .unused => {
-            const symbol_index = try self.newAtom(elf_file);
-            const sym = elf_file.symbol(symbol_index);
+            const symbol_index = try self.newSymbolWithAtom(gpa, 0);
+            const sym = self.symbol(symbol_index);
             sym.flags.needs_zig_got = true;
             metadata.symbol_index.* = symbol_index;
         },
@@ -817,10 +848,10 @@ pub fn getOrCreateMetadataForDecl(
     const gop = try self.decls.getOrPut(gpa, decl_index);
     if (!gop.found_existing) {
         const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded;
-        const symbol_index = try self.newAtom(elf_file);
+        const symbol_index = try self.newSymbolWithAtom(gpa, 0);
         const mod = elf_file.base.comp.module.?;
         const decl = mod.declPtr(decl_index);
-        const sym = elf_file.symbol(symbol_index);
+        const sym = self.symbol(symbol_index);
         if (decl.getOwnedVariable(mod)) |variable| {
             if (variable.is_threadlocal and any_non_single_threaded) {
                 sym.flags.is_tls = true;
@@ -1064,7 +1095,7 @@ pub fn updateFunc(
 
     const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
     self.freeUnnamedConsts(elf_file, decl_index);
-    elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
+    self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
 
     var code_buffer = std.ArrayList(u8).init(gpa);
     defer code_buffer.deinit();
@@ -1096,7 +1127,7 @@ pub fn updateFunc(
     try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_FUNC);
 
     if (decl_state) |*ds| {
-        const sym = elf_file.symbol(sym_index);
+        const sym = self.symbol(sym_index);
         try self.dwarf.?.commitDeclState(
             pt,
             decl_index,
@@ -1130,13 +1161,13 @@ pub fn updateDecl(
         const variable = decl.getOwnedVariable(mod).?;
         const name = decl.name.toSlice(&mod.intern_pool);
         const lib_name = variable.lib_name.toSlice(&mod.intern_pool);
-        const esym_index = try self.getGlobalSymbol(elf_file, name, lib_name);
-        elf_file.symbol(self.symbol(esym_index)).flags.needs_got = true;
+        const sym_index = try self.getGlobalSymbol(elf_file, name, lib_name);
+        self.symbol(sym_index).flags.needs_got = true;
         return;
     }
 
     const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
-    elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
+    self.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file);
 
     const gpa = elf_file.base.comp.gpa;
     var code_buffer = std.ArrayList(u8).init(gpa);
@@ -1174,7 +1205,7 @@ pub fn updateDecl(
         try self.updateDeclCode(elf_file, pt, decl_index, sym_index, shndx, code, elf.STT_OBJECT);
 
     if (decl_state) |*ds| {
-        const sym = elf_file.symbol(sym_index);
+        const sym = self.symbol(sym_index);
         try self.dwarf.?.commitDeclState(
             pt,
             decl_index,
@@ -1323,7 +1354,8 @@ fn lowerConst(
     var code_buffer = std.ArrayList(u8).init(gpa);
     defer code_buffer.deinit();
 
-    const sym_index = try self.newAtom(elf_file);
+    const name_off = try self.addString(gpa, name);
+    const sym_index = try self.newSymbolWithAtom(gpa, name_off);
 
     const res = try codegen.generateSymbol(
         &elf_file.base,
@@ -1339,27 +1371,19 @@ fn lowerConst(
         .fail => |em| return .{ .fail = em },
     };
 
-    const local_sym = elf_file.symbol(sym_index);
-    const name_str_index = try self.strtab.insert(gpa, name);
-    local_sym.name_offset = name_str_index;
-    const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
-    local_esym.st_name = name_str_index;
+    const local_sym = self.symbol(sym_index);
+    const local_esym = local_sym.elfSym(elf_file);
     local_esym.st_info |= elf.STT_OBJECT;
     local_esym.st_size = code.len;
     const atom_ptr = local_sym.atom(elf_file).?;
     atom_ptr.alive = true;
-    atom_ptr.name_offset = name_str_index;
     atom_ptr.alignment = required_alignment;
     atom_ptr.size = code.len;
     atom_ptr.output_section_index = output_section_index;
 
     try atom_ptr.allocate(elf_file);
-    // TODO rename and re-audit this method
     errdefer self.freeDeclMetadata(elf_file, sym_index);
 
-    local_sym.value = 0;
-    local_esym.st_value = 0;
-
     const shdr = elf_file.shdrs.items[output_section_index];
     const file_offset = shdr.sh_offset + @as(u64, @intCast(atom_ptr.value));
     try elf_file.base.file.?.pwriteAll(code, file_offset);
@@ -1401,9 +1425,9 @@ pub fn updateExports(
         },
     };
     const sym_index = metadata.symbol_index;
-    const esym_index = elf_file.symbol(sym_index).esym_index;
-    const esym = self.local_esyms.items(.elf_sym)[esym_index];
-    const esym_shndx = self.local_esyms.items(.shndx)[esym_index];
+    const esym_index = self.symbol(sym_index).esym_index;
+    const esym = self.symtab.items(.elf_sym)[esym_index];
+    const esym_shndx = self.symtab.items(.shndx)[esym_index];
 
     for (export_indices) |export_idx| {
         const exp = mod.all_exports.items[export_idx];
@@ -1437,22 +1461,27 @@ pub fn updateExports(
         const stt_bits: u8 = @as(u4, @truncate(esym.st_info));
         const exp_name = exp.opts.name.toSlice(&mod.intern_pool);
         const name_off = try self.strtab.insert(gpa, exp_name);
-        const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index|
+        const global_sym_index = if (metadata.@"export"(self, exp_name)) |exp_index|
             exp_index.*
         else blk: {
-            const global_esym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
-            try metadata.exports.append(gpa, global_esym_index);
-            break :blk global_esym_index;
+            const global_sym_index = try self.getGlobalSymbol(elf_file, exp_name, null);
+            try metadata.exports.append(gpa, global_sym_index);
+            break :blk global_sym_index;
         };
 
-        const actual_esym_index = global_esym_index & symbol_mask;
-        const global_esym = &self.global_esyms.items(.elf_sym)[actual_esym_index];
-        global_esym.st_value = @intCast(elf_file.symbol(sym_index).value);
+        const value = self.symbol(sym_index).value;
+        const global_sym = self.symbol(global_sym_index);
+        global_sym.value = value;
+        global_sym.flags.weak = exp.opts.linkage == .weak;
+        global_sym.version_index = elf_file.default_version_index;
+        global_sym.ref = .{ .index = esym_shndx, .file = self.index };
+        const global_esym = global_sym.elfSym(elf_file);
+        global_esym.st_value = @intCast(value);
         global_esym.st_shndx = esym.st_shndx;
         global_esym.st_info = (stb_bits << 4) | stt_bits;
         global_esym.st_name = name_off;
         global_esym.st_size = esym.st_size;
-        self.global_esyms.items(.shndx)[actual_esym_index] = esym_shndx;
+        self.symtab.items(.shndx)[global_sym.esym_index] = esym_shndx;
     }
 }
 
@@ -1506,16 +1535,19 @@ pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_n
     const off = try self.strtab.insert(gpa, name);
     const lookup_gop = try self.globals_lookup.getOrPut(gpa, off);
     if (!lookup_gop.found_existing) {
-        const esym_index = try self.addGlobalEsym(gpa);
-        const esym = self.elfSym(esym_index);
-        esym.st_name = off;
-        lookup_gop.value_ptr.* = esym_index;
-        const gop = try elf_file.getOrPutGlobal(name);
-        try self.global_symbols.append(gpa, gop.index);
+        lookup_gop.value_ptr.* = try self.newSymbol(gpa, off);
     }
     return lookup_gop.value_ptr.*;
 }
 
+pub fn asFile(self: *ZigObject) File {
+    return .{ .zig_object = self };
+}
+
+fn addString(self: *ZigObject, allocator: Allocator, string: []const u8) !u32 {
+    return self.strtab.insert(allocator, string);
+}
+
 pub fn getString(self: ZigObject, off: u32) [:0]const u8 {
     return self.strtab.getAssumeExists(off);
 }
@@ -1586,6 +1618,73 @@ pub fn setAtomExtra(self: *ZigObject, index: u32, extra: Atom.Extra) void {
     }
 }
 
+inline fn isGlobal(index: Symbol.Index) bool {
+    return index & global_symbol_bit != 0;
+}
+
+pub fn symbol(self: *ZigObject, index: Symbol.Index) *Symbol {
+    const actual_index = index & symbol_mask;
+    if (isGlobal(index)) return &self.symbols.items[self.global_symbols.items[actual_index]];
+    return &self.symbols.items[self.local_symbols.items[actual_index]];
+}
+
+pub fn resolveSymbol(self: ZigObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref {
+    if (isGlobal(index)) {
+        const resolv = self.symbols_resolver.items[index & symbol_mask];
+        return elf_file.resolver.get(resolv).?;
+    }
+    return .{ .index = index, .file = self.index };
+}
+
+pub fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
+    try self.symbols.ensureUnusedCapacity(allocator, 1);
+    const index: Symbol.Index = @intCast(self.symbols.items.len);
+    self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
+    return index;
+}
+
+pub fn addSymbolExtra(self: *ZigObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+    return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+pub fn addSymbolExtraAssumeCapacity(self: *ZigObject, extra: Symbol.Extra) u32 {
+    const index = @as(u32, @intCast(self.symbols_extra.items.len));
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    inline for (fields) |field| {
+        self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+            u32 => @field(extra, field.name),
+            else => @compileError("bad field type"),
+        });
+    }
+    return index;
+}
+
+pub fn symbolExtra(self: *ZigObject, index: u32) Symbol.Extra {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    var i: usize = index;
+    var result: Symbol.Extra = undefined;
+    inline for (fields) |field| {
+        @field(result, field.name) = switch (field.type) {
+            u32 => self.symbols_extra.items[i],
+            else => @compileError("bad field type"),
+        };
+        i += 1;
+    }
+    return result;
+}
+
+pub fn setSymbolExtra(self: *ZigObject, index: u32, extra: Symbol.Extra) void {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    inline for (fields, 0..) |field, i| {
+        self.symbols_extra.items[index + i] = switch (field.type) {
+            u32 => @field(extra, field.name),
+            else => @compileError("bad field type"),
+        };
+    }
+}
+
 pub fn fmtSymtab(self: *ZigObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) {
     return .{ .data = .{
         .self = self,
@@ -1658,9 +1757,9 @@ const DeclMetadata = struct {
     /// A list of all exports aliases of this Decl.
     exports: std.ArrayListUnmanaged(Symbol.Index) = .{},
 
-    fn @"export"(m: DeclMetadata, zig_object: *ZigObject, name: []const u8) ?*u32 {
+    fn @"export"(m: DeclMetadata, zo: *ZigObject, name: []const u8) ?*u32 {
         for (m.exports.items) |*exp| {
-            const exp_name = zig_object.getString(zig_object.elfSym(exp.*).st_name);
+            const exp_name = zo.getString(zo.symbol(exp.*).name_off);
             if (mem.eql(u8, name, exp_name)) return exp;
         }
         return null;
src/link/Elf.zig
@@ -173,11 +173,7 @@ shstrtab_section_index: ?u32 = null,
 strtab_section_index: ?u32 = null,
 symtab_section_index: ?u32 = null,
 
-/// An array of symbols parsed across all input files.
-symbols: std.ArrayListUnmanaged(Symbol) = .{},
-symbols_extra: std.ArrayListUnmanaged(u32) = .{},
-
-resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
+resolver: SymbolResolver = .{},
 
 has_text_reloc: bool = false,
 num_ifunc_dynrelocs: usize = 0,
@@ -191,10 +187,6 @@ merge_sections: std.ArrayListUnmanaged(MergeSection) = .{},
 /// Table of last atom index in a section and matching atom free list if any.
 last_atom_and_free_list_table: LastAtomAndFreeListTable = .{},
 
-/// Global string table used to provide quick access to global symbol resolvers
-/// such as `resolver`.
-strings: StringTable = .{},
-
 first_eflags: ?elf.Elf64_Word = null,
 
 /// When allocating, the ideal_capacity is calculated by
@@ -455,8 +447,6 @@ pub fn deinit(self: *Elf) void {
     self.shstrtab.deinit(gpa);
     self.symtab.deinit(gpa);
     self.strtab.deinit(gpa);
-    self.symbols.deinit(gpa);
-    self.symbols_extra.deinit(gpa);
     self.resolver.deinit(gpa);
 
     for (self.thunks.items) |*th| {
@@ -472,8 +462,6 @@ pub fn deinit(self: *Elf) void {
     }
     self.last_atom_and_free_list_table.deinit(gpa);
 
-    self.strings.deinit(gpa);
-
     self.got.deinit(gpa);
     self.plt.deinit(gpa);
     self.plt_got.deinit(gpa);
@@ -1916,20 +1904,17 @@ fn accessLibPath(
 /// 6. Re-run symbol resolution on pruned objects and shared objects sets.
 pub fn resolveSymbols(self: *Elf) !void {
     // Resolve symbols in the ZigObject. For now, we assume that it's always live.
-    if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
+    if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self);
     // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
-    for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
-    for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
-    if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self);
+    for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self);
+    for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self);
+    if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self);
 
     // Mark live objects.
     self.markLive();
 
     // Reset state of all globals after marking live objects.
-    if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resetGlobals(self);
-    for (self.objects.items) |index| self.file(index).?.resetGlobals(self);
-    for (self.shared_objects.items) |index| self.file(index).?.resetGlobals(self);
-    if (self.linkerDefinedPtr()) |obj| obj.asFile().resetGlobals(self);
+    self.resolver.reset();
 
     // Prune dead objects and shared objects.
     var i: usize = 0;
@@ -1962,10 +1947,10 @@ pub fn resolveSymbols(self: *Elf) !void {
     }
 
     // Re-resolve the symbols.
-    if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
-    for (self.objects.items) |index| self.file(index).?.resolveSymbols(self);
-    for (self.shared_objects.items) |index| self.file(index).?.resolveSymbols(self);
-    if (self.linkerDefinedPtr()) |obj| obj.asFile().resolveSymbols(self);
+    if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self);
+    for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self);
+    for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self);
+    if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self);
 }
 
 /// Traverses all objects and shared objects marking any object referenced by
@@ -1999,46 +1984,17 @@ fn convertCommonSymbols(self: *Elf) !void {
 }
 
 fn markImportsExports(self: *Elf) void {
-    const mark = struct {
-        fn mark(elf_file: *Elf, file_index: File.Index) void {
-            for (elf_file.file(file_index).?.globals()) |global_index| {
-                const global = elf_file.symbol(global_index);
-                if (global.version_index == elf.VER_NDX_LOCAL) continue;
-                const file_ptr = global.file(elf_file) orelse continue;
-                const vis = @as(elf.STV, @enumFromInt(global.elfSym(elf_file).st_other));
-                if (vis == .HIDDEN) continue;
-                if (file_ptr == .shared_object and !global.isAbs(elf_file)) {
-                    global.flags.import = true;
-                    continue;
-                }
-                if (file_ptr.index() == file_index) {
-                    global.flags.@"export" = true;
-                    if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) {
-                        global.flags.import = true;
-                    }
-                }
-            }
-        }
-    }.mark;
-
+    if (self.zigObjectPtr()) |zo| {
+        zo.markImportsExports(self);
+    }
+    for (self.objects.items) |index| {
+        self.file(index).?.object.markImportsExports(self);
+    }
     if (!self.isEffectivelyDynLib()) {
         for (self.shared_objects.items) |index| {
-            for (self.file(index).?.globals()) |global_index| {
-                const global = self.symbol(global_index);
-                const file_ptr = global.file(self) orelse continue;
-                const vis = @as(elf.STV, @enumFromInt(global.elfSym(self).st_other));
-                if (file_ptr != .shared_object and vis != .HIDDEN) global.flags.@"export" = true;
-            }
+            self.file(index).?.shared_object.markImportExports(self);
         }
     }
-
-    if (self.zig_object_index) |index| {
-        mark(self, index);
-    }
-
-    for (self.objects.items) |index| {
-        mark(self, index);
-    }
 }
 
 fn claimUnresolved(self: *Elf) void {