Commit 41e9b8b6c8

Jakub Konka <kubkon@jakubkonka.com>
2024-08-01 22:28:28
elf: fix compile errors
1 parent deeaa1b
src/arch/aarch64/CodeGen.zig
@@ -4354,8 +4354,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
     if (try self.air.value(callee, pt)) |func_value| {
         if (func_value.getFunction(mod)) |func| {
             if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-                const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
-                const sym = elf_file.symbol(sym_index);
+                const zo = elf_file.zigObjectPtr().?;
+                const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
+                const sym = zo.symbol(sym_index);
                 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
                 const got_addr = @as(u32, @intCast(sym.zigGotAddress(elf_file)));
                 try self.genSetReg(Type.usize, .x30, .{ .memory = got_addr });
src/arch/arm/CodeGen.zig
@@ -4336,8 +4336,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
     if (try self.air.value(callee, pt)) |func_value| {
         if (func_value.getFunction(mod)) |func| {
             if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-                const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
-                const sym = elf_file.symbol(sym_index);
+                const zo = elf_file.zigObjectPtr().?;
+                const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
+                const sym = zo.symbol(sym_index);
                 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
                 const got_addr: u32 = @intCast(sym.zigGotAddress(elf_file));
                 try self.genSetReg(Type.usize, .lr, .{ .memory = got_addr });
src/arch/riscv64/CodeGen.zig
@@ -1409,12 +1409,13 @@ fn genLazy(func: *Func, lazy_sym: link.File.LazySymbol) InnerError!void {
             defer func.register_manager.unlockReg(data_lock);
 
             const elf_file = func.bin_file.cast(link.File.Elf).?;
-            const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, .{
+            const zo = elf_file.zigObjectPtr().?;
+            const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, .{
                 .kind = .const_data,
                 .ty = enum_ty,
             }) catch |err|
                 return func.fail("{s} creating lazy symbol", .{@errorName(err)});
-            const sym = elf_file.symbol(sym_index);
+            const sym = zo.symbol(sym_index);
 
             try func.genSetReg(Type.u64, data_reg, .{ .lea_symbol = .{ .sym = sym.esym_index } });
 
@@ -4946,8 +4947,9 @@ fn genCall(
                 }) {
                     .func => |func_val| {
                         if (func.bin_file.cast(link.File.Elf)) |elf_file| {
-                            const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func_val.owner_decl);
-                            const sym = elf_file.symbol(sym_index);
+                            const zo = elf_file.zigObjectPtr().?;
+                            const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func_val.owner_decl);
+                            const sym = zo.symbol(sym_index);
 
                             if (func.mod.pic) {
                                 return func.fail("TODO: genCall pic", .{});
@@ -7822,9 +7824,10 @@ fn airTagName(func: *Func, inst: Air.Inst.Index) !void {
 
         const lazy_sym = link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(zcu), zcu);
         const elf_file = func.bin_file.cast(link.File.Elf).?;
-        const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
+        const zo = elf_file.zigObjectPtr().?;
+        const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
             return func.fail("{s} creating lazy symbol", .{@errorName(err)});
-        const sym = elf_file.symbol(sym_index);
+        const sym = zo.symbol(sym_index);
 
         if (func.mod.pic) {
             return func.fail("TODO: airTagName pic", .{});
@@ -8049,7 +8052,8 @@ fn genTypedValue(func: *Func, val: Value) InnerError!MCValue {
         switch (lf.tag) {
             .elf => {
                 const elf_file = lf.cast(link.File.Elf).?;
-                const local = elf_file.symbol(local_sym_index);
+                const zo = elf_file.zigObjectPtr().?;
+                const local = zo.symbol(local_sym_index);
                 return MCValue{ .undef = local.esym_index };
             },
             else => unreachable,
src/arch/riscv64/Emit.zig
@@ -50,16 +50,16 @@ pub fn emitMir(emit: *Emit) Error!void {
                     };
 
                     const elf_file = emit.bin_file.cast(link.File.Elf).?;
+                    const zo = elf_file.zigObjectPtr().?;
 
-                    const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
-                    const sym_index = elf_file.zigObjectPtr().?.symbol(symbol.sym_index);
-                    const sym = elf_file.symbol(sym_index);
+                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
+                    const sym = zo.symbol(symbol.sym_index);
 
                     var hi_r_type: u32 = @intFromEnum(std.elf.R_RISCV.HI20);
                     var lo_r_type: u32 = @intFromEnum(std.elf.R_RISCV.LO12_I);
 
                     if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
-                        _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+                        _ = try sym.getOrCreateZigGotEntry(symbol.sym_index, elf_file);
 
                         hi_r_type = Elf.R_ZIG_GOT_HI20;
                         lo_r_type = Elf.R_ZIG_GOT_LO12;
@@ -82,8 +82,9 @@ pub fn emitMir(emit: *Emit) Error!void {
                 },
                 .load_tlv_reloc => |symbol| {
                     const elf_file = emit.bin_file.cast(link.File.Elf).?;
+                    const zo = elf_file.zigObjectPtr().?;
 
-                    const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
+                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
 
                     const R_RISCV = std.elf.R_RISCV;
 
@@ -107,7 +108,8 @@ pub fn emitMir(emit: *Emit) Error!void {
                 },
                 .call_extern_fn_reloc => |symbol| {
                     const elf_file = emit.bin_file.cast(link.File.Elf).?;
-                    const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
+                    const zo = elf_file.zigObjectPtr().?;
+                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
 
                     const r_type: u32 = @intFromEnum(std.elf.R_RISCV.CALL_PLT);
 
src/arch/sparc64/CodeGen.zig
@@ -1354,8 +1354,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
             switch (mod.intern_pool.indexToKey(func_value.ip_index)) {
                 .func => |func| {
                     const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
-                        const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
-                        const sym = elf_file.symbol(sym_index);
+                        const zo = elf_file.zigObjectPtr().?;
+                        const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
+                        const sym = zo.symbol(sym_index);
                         _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
                         break :blk @as(u32, @intCast(sym.zigGotAddress(elf_file)));
                     } else unreachable;
src/arch/x86_64/CodeGen.zig
@@ -12327,8 +12327,9 @@ fn genCall(self: *Self, info: union(enum) {
             }) {
                 .func => |func| {
                     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-                        const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
-                        const sym = elf_file.symbol(sym_index);
+                        const zo = elf_file.zigObjectPtr().?;
+                        const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, func.owner_decl);
+                        const sym = zo.symbol(sym_index);
                         if (self.mod.pic) {
                             const callee_reg: Register = switch (resolved_cc) {
                                 .SysV => callee: {
@@ -15320,9 +15321,10 @@ fn genLazySymbolRef(
 ) InnerError!void {
     const pt = self.pt;
     if (self.bin_file.cast(link.File.Elf)) |elf_file| {
-        const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
+        const zo = elf_file.zigObjectPtr().?;
+        const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err|
             return self.fail("{s} creating lazy symbol", .{@errorName(err)});
-        const sym = elf_file.symbol(sym_index);
+        const sym = zo.symbol(sym_index);
         if (self.mod.pic) {
             switch (tag) {
                 .lea, .call => try self.genSetReg(reg, Type.usize, .{
src/arch/x86_64/Emit.zig
@@ -42,7 +42,8 @@ pub fn emitMir(emit: *Emit) Error!void {
                 }),
                 .linker_extern_fn => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
                     // Add relocation to the decl.
-                    const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
+                    const zo = elf_file.zigObjectPtr().?;
+                    const atom_ptr = zo.symbol(symbol.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.PLT32);
                     try atom_ptr.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
@@ -88,7 +89,8 @@ pub fn emitMir(emit: *Emit) Error!void {
                 }),
                 .linker_tlsld => |data| {
                     const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
-                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
+                    const zo = elf_file.zigObjectPtr().?;
+                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.TLSLD);
                     try atom.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
@@ -98,7 +100,8 @@ pub fn emitMir(emit: *Emit) Error!void {
                 },
                 .linker_dtpoff => |data| {
                     const elf_file = emit.lower.bin_file.cast(link.File.Elf).?;
-                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
+                    const zo = elf_file.zigObjectPtr().?;
+                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
                     const r_type = @intFromEnum(std.elf.R_X86_64.DTPOFF32);
                     try atom.addReloc(elf_file, .{
                         .r_offset = end_offset - 4,
@@ -112,11 +115,11 @@ pub fn emitMir(emit: *Emit) Error!void {
                         .Obj => true,
                         .Lib => emit.lower.link_mode == .static,
                     };
-                    const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
-                    const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index);
-                    const sym = elf_file.symbol(sym_index);
+                    const zo = elf_file.zigObjectPtr().?;
+                    const atom = zo.symbol(data.atom_index).atom(elf_file).?;
+                    const sym = zo.symbol(data.sym_index);
                     if (sym.flags.needs_zig_got and !is_obj_or_static_lib) {
-                        _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file);
+                        _ = try sym.getOrCreateZigGotEntry(data.sym_index, elf_file);
                     }
                     if (emit.lower.pic) {
                         const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib)
src/arch/x86_64/Lower.zig
@@ -349,8 +349,8 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
                     assert(mem_op.sib.scale_index.scale == 0);
 
                     if (lower.bin_file.cast(link.File.Elf)) |elf_file| {
-                        const sym_index = elf_file.zigObjectPtr().?.symbol(sym.sym_index);
-                        const elf_sym = elf_file.symbol(sym_index);
+                        const zo = elf_file.zigObjectPtr().?;
+                        const elf_sym = zo.symbol(sym.sym_index);
 
                         if (elf_sym.flags.is_tls) {
                             // TODO handle extern TLS vars, i.e., emit GD model
src/link/Elf/Atom.zig
@@ -326,12 +326,8 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El
     const cpu_arch = elf_file.getTarget().cpu.arch;
     const file_ptr = self.file(elf_file).?;
     for (self.relocs(elf_file)) |rel| {
-        const target_index = switch (file_ptr) {
-            .zig_object => |x| x.symbol(rel.r_sym()),
-            .object => |x| x.symbols.items[rel.r_sym()],
-            else => unreachable,
-        };
-        const target = elf_file.symbol(target_index);
+        const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+        const target = elf_file.symbol(target_ref).?;
         const r_type = rel.r_type();
         const r_offset: u64 = @intCast(self.value + @as(i64, @intCast(rel.r_offset)));
         var r_addend = rel.r_addend;
@@ -422,12 +418,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
         const r_kind = relocation.decode(rel.r_type(), cpu_arch);
         if (r_kind == .none) continue;
 
-        const symbol_index = switch (file_ptr) {
-            .zig_object => |x| x.symbol(rel.r_sym()),
-            .object => |x| x.symbols.items[rel.r_sym()],
-            else => unreachable,
+        const symbol_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+        const symbol = elf_file.symbol(symbol_ref) orelse {
+            const sym_name = switch (file_ptr) {
+                .zig_object => |x| x.symbol(rel.r_sym()).name(elf_file),
+                inline else => |x| x.symbols.items[rel.r_sym()].name(elf_file),
+            };
+            // Violation of One Definition Rule for COMDATs.
+            // TODO convert into an error
+            log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
+                file_ptr.fmtPath(),
+                self.name(elf_file),
+                sym_name,
+            });
+            continue;
         };
-        const symbol = elf_file.symbol(symbol_index);
 
         const is_synthetic_symbol = switch (file_ptr) {
             .zig_object => false, // TODO: implement this once we support merge sections in ZigObject
@@ -435,19 +440,8 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
             else => unreachable,
         };
 
-        // Check for violation of One Definition Rule for COMDATs.
-        if (symbol.file(elf_file) == null) {
-            // TODO convert into an error
-            log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
-                file_ptr.fmtPath(),
-                self.name(elf_file),
-                symbol.name(elf_file),
-            });
-            continue;
-        }
-
         // Report an undefined symbol.
-        if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs)))
+        if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, symbol, rel, undefs)))
             continue;
 
         if (symbol.isIFunc(elf_file)) {
@@ -694,16 +688,15 @@ fn reportUndefined(
     self: Atom,
     elf_file: *Elf,
     sym: *const Symbol,
-    sym_index: Symbol.Index,
     rel: elf.Elf64_Rela,
     undefs: anytype,
 ) !bool {
     const comp = elf_file.base.comp;
     const gpa = comp.gpa;
-    const rel_esym = switch (self.file(elf_file).?) {
-        .zig_object => |x| x.elfSym(rel.r_sym()).*,
-        .object => |x| x.symtab.items[rel.r_sym()],
-        else => unreachable,
+    const file_ptr = self.file(elf_file).?;
+    const rel_esym = switch (file_ptr) {
+        .zig_object => |x| x.symbol(rel.r_sym()).elfSym(elf_file),
+        inline else => |x| x.symtab.items[rel.r_sym()],
     };
     const esym = sym.elfSym(elf_file);
     if (rel_esym.st_shndx == elf.SHN_UNDEF and
@@ -712,7 +705,12 @@ fn reportUndefined(
         !sym.flags.import and
         esym.st_shndx == elf.SHN_UNDEF)
     {
-        const gop = try undefs.getOrPut(sym_index);
+        const idx = switch (file_ptr) {
+            .zig_object => |x| x.symbols_resolver.items[rel.r_sym() & ZigObject.symbol_mask],
+            .object => |x| x.symbols_resolver.items[rel.r_sym() - x.first_global.?],
+            inline else => |x| x.symbols_resolver.items[rel.r_sym()],
+        };
+        const gop = try undefs.getOrPut(idx);
         if (!gop.found_existing) {
             gop.value_ptr.* = std.ArrayList(Elf.Ref).init(gpa);
         }
@@ -737,11 +735,8 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi
         const r_kind = relocation.decode(rel.r_type(), cpu_arch);
         if (r_kind == .none) continue;
 
-        const target = switch (file_ptr) {
-            .zig_object => |x| elf_file.symbol(x.symbol(rel.r_sym())),
-            .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
-            else => unreachable,
-        };
+        const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+        const target = elf_file.symbol(target_ref).?;
         const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
 
         // We will use equation format to resolve relocations:
@@ -923,31 +918,29 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any
 
         const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow;
 
-        const target_index = switch (file_ptr) {
-            .zig_object => |x| x.symbol(rel.r_sym()),
-            .object => |x| x.symbols.items[rel.r_sym()],
-            else => unreachable,
-        };
-        const target = elf_file.symbol(target_index);
-        const is_synthetic_symbol = switch (file_ptr) {
-            .zig_object => false, // TODO: implement this once we support merge sections in ZigObject
-            .object => |x| rel.r_sym() >= x.symtab.items.len,
-            else => unreachable,
-        };
-
-        // Check for violation of One Definition Rule for COMDATs.
-        if (target.file(elf_file) == null) {
+        const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
+        const target = elf_file.symbol(target_ref) orelse {
+            const sym_name = switch (file_ptr) {
+                .zig_object => |x| x.symbol(rel.r_sym()).name(elf_file),
+                inline else => |x| x.symbols.items[rel.r_sym()].name(elf_file),
+            };
+            // Violation of One Definition Rule for COMDATs.
             // TODO convert into an error
             log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
                 file_ptr.fmtPath(),
                 self.name(elf_file),
-                target.name(elf_file),
+                sym_name,
             });
             continue;
-        }
+        };
+        const is_synthetic_symbol = switch (file_ptr) {
+            .zig_object => false, // TODO: implement this once we support merge sections in ZigObject
+            .object => |x| rel.r_sym() >= x.symtab.items.len,
+            else => unreachable,
+        };
 
         // Report an undefined symbol.
-        if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, target, target_index, rel, undefs)))
+        if (!is_synthetic_symbol and (try self.reportUndefined(elf_file, target, rel, undefs)))
             continue;
 
         // We will use equation format to resolve relocations:
@@ -1766,11 +1759,7 @@ const aarch64 = struct {
             => {
                 const disp: i28 = math.cast(i28, S + A - P) orelse blk: {
                     const th = atom.thunk(elf_file);
-                    const target_index = switch (file_ptr) {
-                        .zig_object => |x| x.symbol(rel.r_sym()),
-                        .object => |x| x.symbols.items[rel.r_sym()],
-                        else => unreachable,
-                    };
+                    const target_index = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
                     const S_ = th.targetAddress(target_index, elf_file);
                     break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow;
                 };
@@ -2106,11 +2095,8 @@ const riscv = struct {
                     return error.RelocFailure;
                 };
                 it.pos = pos;
-                const target_ = switch (file_ptr) {
-                    .zig_object => |x| elf_file.symbol(x.symbol(pair.r_sym())),
-                    .object => |x| elf_file.symbol(x.symbols.items[pair.r_sym()]),
-                    else => unreachable,
-                };
+                const target_ref_ = file_ptr.resolveSymbol(pair.r_sym(), elf_file);
+                const target_ = elf_file.symbol(target_ref_).?;
                 const S_ = target_.address(.{}, elf_file);
                 const A_ = pair.r_addend;
                 const P_ = atom_addr + @as(i64, @intCast(pair.r_offset));
@@ -2313,4 +2299,5 @@ const File = @import("file.zig").File;
 const Object = @import("Object.zig");
 const Symbol = @import("Symbol.zig");
 const Thunk = @import("thunks.zig").Thunk;
+const ZigObject = @import("ZigObject.zig");
 const dev = @import("../../dev.zig");
src/link/Elf/eh_frame.zig
@@ -339,7 +339,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
             const contents = cie.data(elf_file);
 
             for (cie.relocs(elf_file)) |rel| {
-                const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+                const ref = object.resolveSymbol(rel.r_sym(), elf_file);
+                const sym = elf_file.symbol(ref).?;
                 resolveReloc(cie, sym, rel, elf_file, contents) catch |err| switch (err) {
                     error.RelocFailure => has_reloc_errors = true,
                     else => |e| return e,
@@ -366,7 +367,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void {
             );
 
             for (fde.relocs(elf_file)) |rel| {
-                const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+                const ref = object.resolveSymbol(rel.r_sym(), elf_file);
+                const sym = elf_file.symbol(ref).?;
                 resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) {
                     error.RelocFailure => has_reloc_errors = true,
                     else => |e| return e,
@@ -452,7 +454,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
         for (object.cies.items) |cie| {
             if (!cie.alive) continue;
             for (cie.relocs(elf_file)) |rel| {
-                const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+                const ref = object.resolveSymbol(rel.r_sym(), elf_file);
+                const sym = elf_file.symbol(ref).?;
                 const out_rel = emitReloc(elf_file, cie, sym, rel);
                 try writer.writeStruct(out_rel);
             }
@@ -461,7 +464,8 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void {
         for (object.fdes.items) |fde| {
             if (!fde.alive) continue;
             for (fde.relocs(elf_file)) |rel| {
-                const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+                const ref = object.resolveSymbol(rel.r_sym(), elf_file);
+                const sym = elf_file.symbol(ref).?;
                 const out_rel = emitReloc(elf_file, fde, sym, rel);
                 try writer.writeStruct(out_rel);
             }
@@ -513,7 +517,8 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
             const relocs = fde.relocs(elf_file);
             assert(relocs.len > 0); // Should this be an error? Things are completely broken anyhow if this trips...
             const rel = relocs[0];
-            const sym = elf_file.symbol(object.symbols.items[rel.r_sym()]);
+            const ref = object.resolveSymbol(rel.r_sym(), elf_file);
+            const sym = elf_file.symbol(ref).?;
             const P = @as(i64, @intCast(fde.address(elf_file)));
             const S = @as(i64, @intCast(sym.address(.{}, elf_file)));
             const A = rel.r_addend;
src/link/Elf/file.zig
@@ -61,10 +61,10 @@ pub const File = union(enum) {
         return (@as(u32, base) << 24) + file.index();
     }
 
-    pub fn resolveSymbols(file: File, elf_file: *Elf) void {
-        switch (file) {
+    pub fn resolveSymbols(file: File, elf_file: *Elf) !void {
+        return switch (file) {
             inline else => |x| x.resolveSymbols(elf_file),
-        }
+        };
     }
 
     pub fn resetGlobals(file: File, elf_file: *Elf) void {
@@ -100,6 +100,13 @@ pub const File = union(enum) {
         }
     }
 
+    pub fn createSymbolIndirection(file: File, elf_file: *Elf) !void {
+        return switch (file) {
+            .linker_defined, .shared_object => unreachable,
+            inline else => |x| x.createSymbolIndirection(elf_file),
+        };
+    }
+
     pub fn atom(file: File, atom_index: Atom.Index) ?*Atom {
         return switch (file) {
             .shared_object => unreachable,
@@ -146,10 +153,16 @@ pub const File = union(enum) {
         };
     }
 
-    pub fn symbol(file: File, ind: Symbol.Index) Symbol.Index {
+    pub fn resolveSymbol(file: File, ind: Symbol.Index, elf_file: *Elf) Elf.Ref {
+        return switch (file) {
+            inline else => |x| x.resolveSymbol(ind, elf_file),
+        };
+    }
+
+    pub fn symbol(file: File, ind: Symbol.Index) *Symbol {
         return switch (file) {
             .zig_object => |x| x.symbol(ind),
-            inline else => |x| x.symbols.items[ind],
+            inline else => |x| &x.symbols.items[ind],
         };
     }
 
src/link/Elf/gc.zig
@@ -1,71 +1,84 @@
 pub fn gcAtoms(elf_file: *Elf) !void {
     const comp = elf_file.base.comp;
     const gpa = comp.gpa;
-    const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null);
-    var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files);
-    defer files.deinit();
-    if (elf_file.zig_object_index) |index| files.appendAssumeCapacity(index);
-    for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
-
     var roots = std.ArrayList(*Atom).init(gpa);
     defer roots.deinit();
-    try collectRoots(&roots, files.items, elf_file);
-
+    try collectRoots(&roots, elf_file);
     mark(roots, elf_file);
-    prune(files.items, elf_file);
+    prune(elf_file);
 }
 
-fn collectRoots(roots: *std.ArrayList(*Atom), files: []const File.Index, elf_file: *Elf) !void {
+fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void {
     if (elf_file.linkerDefinedPtr()) |obj| {
-        if (obj.entry_index) |index| {
-            const global = elf_file.symbol(index);
-            try markSymbol(global, roots, elf_file);
+        if (obj.entrySymbol(elf_file)) |sym| {
+            try markSymbol(sym, roots, elf_file);
         }
     }
 
-    for (files) |index| {
-        for (elf_file.file(index).?.globals()) |global_index| {
-            const global = elf_file.symbol(global_index);
-            if (global.file(elf_file)) |file| {
-                if (file.index() == index and global.flags.@"export")
-                    try markSymbol(global, roots, elf_file);
+    if (elf_file.zigObjectPtr()) |zo| {
+        for (0..zo.global_symbols.items.len) |i| {
+            const ref = zo.resolveSymbol(@intCast(i | ZigObject.global_symbol_bit), elf_file);
+            const sym = elf_file.symbol(ref) orelse continue;
+            if (sym.file(elf_file).?.index() != zo.index) continue;
+            if (sym.flags.@"export") {
+                try markSymbol(sym, roots, elf_file);
             }
         }
     }
 
-    for (files) |index| {
-        const file = elf_file.file(index).?;
-
-        for (file.atoms()) |atom_index| {
-            const atom = file.atom(atom_index) orelse continue;
-            if (!atom.alive) continue;
-
-            const shdr = atom.inputShdr(elf_file);
-            const name = atom.name(elf_file);
-            const is_gc_root = blk: {
-                if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true;
-                if (shdr.sh_type == elf.SHT_NOTE) break :blk true;
-                if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true;
-                if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true;
-                if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true;
-                if (mem.startsWith(u8, name, ".ctors")) break :blk true;
-                if (mem.startsWith(u8, name, ".dtors")) break :blk true;
-                if (mem.startsWith(u8, name, ".init")) break :blk true;
-                if (mem.startsWith(u8, name, ".fini")) break :blk true;
-                if (Elf.isCIdentifier(name)) break :blk true;
-                break :blk false;
-            };
-            if (is_gc_root and markAtom(atom)) try roots.append(atom);
-            if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true;
+    for (elf_file.objects.items) |index| {
+        const object = elf_file.file(index).?.object;
+        for (0..object.globals().len) |i| {
+            const ref = object.resolveSymbol(@intCast(i), elf_file);
+            const sym = elf_file.symbol(ref) orelse continue;
+            if (sym.file(elf_file).?.index() != object.index) continue;
+            if (sym.flags.@"export") {
+                try markSymbol(sym, roots, elf_file);
+            }
         }
+    }
 
-        // Mark every atom referenced by CIE as alive.
-        for (file.cies()) |cie| {
-            for (cie.relocs(elf_file)) |rel| {
-                const sym = elf_file.symbol(file.symbol(rel.r_sym()));
-                try markSymbol(sym, roots, elf_file);
+    const atomRoots = struct {
+        fn atomRoots(file: File, rs: anytype, ef: *Elf) !void {
+            for (file.atoms()) |atom_index| {
+                const atom = file.atom(atom_index) orelse continue;
+                if (!atom.alive) continue;
+
+                const shdr = atom.inputShdr(ef);
+                const name = atom.name(ef);
+                const is_gc_root = blk: {
+                    if (shdr.sh_flags & elf.SHF_GNU_RETAIN != 0) break :blk true;
+                    if (shdr.sh_type == elf.SHT_NOTE) break :blk true;
+                    if (shdr.sh_type == elf.SHT_PREINIT_ARRAY) break :blk true;
+                    if (shdr.sh_type == elf.SHT_INIT_ARRAY) break :blk true;
+                    if (shdr.sh_type == elf.SHT_FINI_ARRAY) break :blk true;
+                    if (mem.startsWith(u8, name, ".ctors")) break :blk true;
+                    if (mem.startsWith(u8, name, ".dtors")) break :blk true;
+                    if (mem.startsWith(u8, name, ".init")) break :blk true;
+                    if (mem.startsWith(u8, name, ".fini")) break :blk true;
+                    if (Elf.isCIdentifier(name)) break :blk true;
+                    break :blk false;
+                };
+                if (is_gc_root and markAtom(atom)) try rs.append(atom);
+                if (shdr.sh_flags & elf.SHF_ALLOC == 0) atom.visited = true;
+            }
+
+            // Mark every atom referenced by CIE as alive.
+            for (file.cies()) |cie| {
+                for (cie.relocs(ef)) |rel| {
+                    const ref = file.resolveSymbol(rel.r_sym(), ef);
+                    const sym = ef.symbol(ref) orelse continue;
+                    try markSymbol(sym, rs, ef);
+                }
             }
         }
+    }.atomRoots;
+
+    if (elf_file.zigObjectPtr()) |zo| {
+        try atomRoots(zo.asFile(), roots, elf_file);
+    }
+    for (elf_file.objects.items) |index| {
+        try atomRoots(elf_file.file(index).?, roots, elf_file);
     }
 }
 
@@ -92,7 +105,8 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
 
     for (atom.fdes(elf_file)) |fde| {
         for (fde.relocs(elf_file)[1..]) |rel| {
-            const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
+            const ref = file.resolveSymbol(rel.r_sym(), elf_file);
+            const target_sym = elf_file.symbol(ref) orelse continue;
             const target_atom = target_sym.atom(elf_file) orelse continue;
             target_atom.alive = true;
             gc_track_live_log.debug("{}marking live atom({d})", .{ track_live_level, target_atom.atom_index });
@@ -101,7 +115,8 @@ fn markLive(atom: *Atom, elf_file: *Elf) void {
     }
 
     for (atom.relocs(elf_file)) |rel| {
-        const target_sym = elf_file.symbol(file.symbol(rel.r_sym()));
+        const ref = file.resolveSymbol(rel.r_sym(), elf_file);
+        const target_sym = elf_file.symbol(ref) orelse continue;
         if (target_sym.mergeSubsection(elf_file)) |msub| {
             msub.alive = true;
             continue;
@@ -120,16 +135,23 @@ fn mark(roots: std.ArrayList(*Atom), elf_file: *Elf) void {
     }
 }
 
-fn prune(files: []const File.Index, elf_file: *Elf) void {
-    for (files) |index| {
-        const file = elf_file.file(index).?;
-        for (file.atoms()) |atom_index| {
-            const atom = file.atom(atom_index) orelse continue;
-            if (atom.alive and !atom.visited) {
-                atom.alive = false;
-                atom.markFdesDead(elf_file);
+fn prune(elf_file: *Elf) void {
+    const pruneInFile = struct {
+        fn pruneInFile(file: File, ef: *Elf) void {
+            for (file.atoms()) |atom_index| {
+                const atom = file.atom(atom_index) orelse continue;
+                if (atom.alive and !atom.visited) {
+                    atom.alive = false;
+                    atom.markFdesDead(ef);
+                }
             }
         }
+    }.pruneInFile;
+    if (elf_file.zigObjectPtr()) |zo| {
+        pruneInFile(zo.asFile(), elf_file);
+    }
+    for (elf_file.objects.items) |index| {
+        pruneInFile(elf_file.file(index).?, elf_file);
     }
 }
 
@@ -181,3 +203,4 @@ const Atom = @import("Atom.zig");
 const Elf = @import("../Elf.zig");
 const File = @import("file.zig").File;
 const Symbol = @import("Symbol.zig");
+const ZigObject = @import("ZigObject.zig");
src/link/Elf/LinkerDefined.zig
@@ -44,7 +44,7 @@ pub fn init(self: *LinkerDefined, allocator: Allocator) !void {
 
 pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
     const newSymbolAssumeCapacity = struct {
-        fn newSymbolAssumeCapacity(ld: *LinkerDefined, name_off: u32) Symbol.Index {
+        fn newSymbolAssumeCapacity(ld: *LinkerDefined, name_off: u32, ef: *Elf) Symbol.Index {
             const esym_index: u32 = @intCast(ld.symtab.items.len);
             const esym = ld.symtab.addOneAssumeCapacity();
             esym.* = .{
@@ -61,7 +61,8 @@ pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
             symbol.extra_index = ld.addSymbolExtraAssumeCapacity(.{});
             symbol.ref = .{ .index = 0, .file = 0 };
             symbol.esym_index = esym_index;
-            symbol.version_index = elf_file.default_sym_version;
+            symbol.version_index = ef.default_sym_version;
+            return index;
         }
     }.newSymbolAssumeCapacity;
 
@@ -111,31 +112,31 @@ pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
     @memset(self.symbols_resolver.items, 0);
 
     if (elf_file.entry_name) |name| {
-        self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, name));
+        self.entry_index = newSymbolAssumeCapacity(self, try self.addString(gpa, name), elf_file);
     }
 
-    self.dynamic_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_DYNAMIC"));
-    self.ehdr_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__ehdr_start"));
-    self.init_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__init_array_start"));
-    self.init_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__init_array_end"));
-    self.fini_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__fini_array_start"));
-    self.fini_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__fini_array_end"));
-    self.preinit_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__preinit_array_start"));
-    self.preinit_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__preinit_array_end"));
-    self.got_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_GLOBAL_OFFSET_TABLE_"));
-    self.plt_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_PROCEDURE_LINKAGE_TABLE_"));
-    self.end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_end"));
+    self.dynamic_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_DYNAMIC"), elf_file);
+    self.ehdr_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__ehdr_start"), elf_file);
+    self.init_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__init_array_start"), elf_file);
+    self.init_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__init_array_end"), elf_file);
+    self.fini_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__fini_array_start"), elf_file);
+    self.fini_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__fini_array_end"), elf_file);
+    self.preinit_array_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__preinit_array_start"), elf_file);
+    self.preinit_array_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__preinit_array_end"), elf_file);
+    self.got_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_GLOBAL_OFFSET_TABLE_"), elf_file);
+    self.plt_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_PROCEDURE_LINKAGE_TABLE_"), elf_file);
+    self.end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "_end"), elf_file);
 
     if (elf_file.base.comp.link_eh_frame_hdr) {
-        self.gnu_eh_frame_hdr_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__GNU_EH_FRAME_HDR"));
+        self.gnu_eh_frame_hdr_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__GNU_EH_FRAME_HDR"), elf_file);
     }
 
-    self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__dso_handle"));
-    self.rela_iplt_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__rela_iplt_start"));
-    self.rela_iplt_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__rela_iplt_end"));
+    self.dso_handle_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__dso_handle"), elf_file);
+    self.rela_iplt_start_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__rela_iplt_start"), elf_file);
+    self.rela_iplt_end_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__rela_iplt_end"), elf_file);
 
     if (elf_file.getTarget().cpu.arch.isRISCV() and elf_file.isEffectivelyDynLib()) {
-        self.global_pointer_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__global_pointer$"));
+        self.global_pointer_index = newSymbolAssumeCapacity(self, try self.addString(gpa, "__global_pointer$"), elf_file);
     }
 
     for (elf_file.objects.items) |index| {
@@ -145,8 +146,8 @@ pub fn initSymbols(self: *LinkerDefined, elf_file: *Elf) !void {
                 defer gpa.free(start_name);
                 const stop_name = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name});
                 defer gpa.free(stop_name);
-                const start = newSymbolAssumeCapacity(self, try self.addString(gpa, start_name));
-                const stop = newSymbolAssumeCapacity(self, try self.addString(gpa, stop_name));
+                const start = newSymbolAssumeCapacity(self, try self.addString(gpa, start_name), elf_file);
+                const stop = newSymbolAssumeCapacity(self, try self.addString(gpa, stop_name), elf_file);
                 self.start_stop_indexes.appendSliceAssumeCapacity(&.{ start, stop });
             }
         }
@@ -268,7 +269,7 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void {
         for (elf_file.shdrs.items, 0..) |shdr, shndx| {
             if (shdr.sh_flags & elf.SHF_ALLOC != 0) {
                 value = shdr.sh_addr + shdr.sh_size;
-                osec = shndx;
+                osec = @intCast(shndx);
             }
         }
         allocSymbol(self, self.end_index.?, value, osec, elf_file);
@@ -287,10 +288,10 @@ pub fn allocateSymbols(self: *LinkerDefined, elf_file: *Elf) void {
     {
         var index: usize = 0;
         while (index < self.start_stop_indexes.items.len) : (index += 2) {
-            const start_ref = self.resolveSymbol(self.start_stop_indexes.items[index]);
+            const start_ref = self.resolveSymbol(self.start_stop_indexes.items[index], elf_file);
             const start = elf_file.symbol(start_ref).?;
             const name = start.name(elf_file);
-            const stop_ref = self.resolveSymbol(self.start_stop_indexes.items[index + 1]);
+            const stop_ref = self.resolveSymbol(self.start_stop_indexes.items[index + 1], elf_file);
             const stop = elf_file.symbol(stop_ref).?;
             const shndx = elf_file.sectionByName(name["__start_".len..]).?;
             const shdr = &elf_file.shdrs.items[shndx];
@@ -334,6 +335,18 @@ pub fn writeSymtab(self: *LinkerDefined, elf_file: *Elf) void {
     }
 }
 
+pub fn dynamicSymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol {
+    const index = self.dynamic_index orelse return null;
+    const resolv = self.resolveSymbol(index, elf_file);
+    return elf_file.symbol(resolv);
+}
+
+pub fn entrySymbol(self: LinkerDefined, elf_file: *Elf) ?*Symbol {
+    const index = self.entry_index orelse return null;
+    const resolv = self.resolveSymbol(index, elf_file);
+    return elf_file.symbol(resolv);
+}
+
 pub fn asFile(self: *LinkerDefined) File {
     return .{ .linker_defined = self };
 }
@@ -356,8 +369,12 @@ pub fn resolveSymbol(self: LinkerDefined, index: Symbol.Index, elf_file: *Elf) E
     return elf_file.resolver.get(resolv).?;
 }
 
-pub fn addSymbol(self: *LinkerDefined, allocator: Allocator) !Symbol.Index {
+fn addSymbol(self: *LinkerDefined, allocator: Allocator) !Symbol.Index {
     try self.symbols.ensureUnusedCapacity(allocator, 1);
+    return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *LinkerDefined) Symbol.Index {
     const index: Symbol.Index = @intCast(self.symbols.items.len);
     self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
     return index;
@@ -425,10 +442,11 @@ fn formatSymtab(
 ) !void {
     _ = unused_fmt_string;
     _ = options;
+    const self = ctx.self;
+    const elf_file = ctx.elf_file;
     try writer.writeAll("  globals\n");
-    for (ctx.self.globals()) |index| {
-        const global = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{global.fmt(ctx.elf_file)});
+    for (self.symbols.items) |sym| {
+        try writer.print("    {}\n", .{sym.fmt(elf_file)});
     }
 }
 
src/link/Elf/Object.zig
@@ -394,10 +394,9 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void {
         sym_ptr.value = @intCast(sym.st_value);
         sym_ptr.name_offset = sym.st_name;
         sym_ptr.esym_index = @intCast(i);
-        sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{
-            .weak = sym.st_bind() == elf.STB_WEAK,
-        });
+        sym_ptr.extra_index = self.addSymbolExtraAssumeCapacity(.{});
         sym_ptr.version_index = if (i >= first_global) elf_file.default_sym_version else elf.VER_NDX_LOCAL;
+        sym_ptr.flags.weak = sym.st_bind() == elf.STB_WEAK;
         if (sym.st_shndx != elf.SHN_ABS and sym.st_shndx != elf.SHN_COMMON) {
             sym_ptr.ref = .{ .index = self.atoms_indexes.items[sym.st_shndx], .file = self.index };
         }
@@ -548,7 +547,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
 
     for (self.cies.items) |cie| {
         for (cie.relocs(elf_file)) |rel| {
-            const sym = elf_file.symbol(self.resolveSymbol(rel.r_sym()));
+            const sym = elf_file.symbol(self.resolveSymbol(rel.r_sym(), elf_file)).?;
             if (sym.flags.import) {
                 if (sym.type(elf_file) != elf.STT_FUNC)
                     // TODO convert into an error
@@ -562,6 +561,51 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
     }
 }
 
+pub fn createSymbolIndirection(self: *Object, elf_file: *Elf) !void {
+    for (self.symbols.items, 0..) |*sym, i| {
+        const ref = self.resolveSymbol(@intCast(i), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
+        if (!sym.isLocal(elf_file) and !sym.flags.has_dynamic) {
+            log.debug("'{s}' is non-local", .{sym.name(elf_file)});
+            try elf_file.dynsym.addSymbol(ref, elf_file);
+        }
+        if (sym.flags.needs_got) {
+            log.debug("'{s}' needs GOT", .{sym.name(elf_file)});
+            _ = try elf_file.got.addGotSymbol(ref, elf_file);
+        }
+        if (sym.flags.needs_plt) {
+            if (sym.flags.is_canonical) {
+                log.debug("'{s}' needs CPLT", .{sym.name(elf_file)});
+                sym.flags.@"export" = true;
+                try elf_file.plt.addSymbol(ref, elf_file);
+            } else if (sym.flags.needs_got) {
+                log.debug("'{s}' needs PLTGOT", .{sym.name(elf_file)});
+                try elf_file.plt_got.addSymbol(ref, elf_file);
+            } else {
+                log.debug("'{s}' needs PLT", .{sym.name(elf_file)});
+                try elf_file.plt.addSymbol(ref, elf_file);
+            }
+        }
+        if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) {
+            log.debug("'{s}' needs COPYREL", .{sym.name(elf_file)});
+            try elf_file.copy_rel.addSymbol(ref, elf_file);
+        }
+        if (sym.flags.needs_tlsgd) {
+            log.debug("'{s}' needs TLSGD", .{sym.name(elf_file)});
+            try elf_file.got.addTlsGdSymbol(ref, elf_file);
+        }
+        if (sym.flags.needs_gottp) {
+            log.debug("'{s}' needs GOTTP", .{sym.name(elf_file)});
+            try elf_file.got.addGotTpSymbol(ref, elf_file);
+        }
+        if (sym.flags.needs_tlsdesc) {
+            log.debug("'{s}' needs TLSDESC", .{sym.name(elf_file)});
+            try elf_file.got.addTlsDescSymbol(ref, elf_file);
+        }
+    }
+}
+
 pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void {
     const gpa = elf_file.base.comp.gpa;
 
@@ -643,7 +687,7 @@ pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void {
         sym.file_index = self.index;
 
         const idx = self.symbols_resolver.items[i];
-        elf_file.resolver.items[idx - 1] = .{ .index = esym_index, .file = self.index };
+        elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index };
     }
 }
 
@@ -1120,7 +1164,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
     }
 
     for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
-        const ref = elf_file.resolver.items[resolv];
+        const ref = elf_file.resolver.values.items[resolv];
         const ref_sym = elf_file.symbol(ref) orelse continue;
         if (ref_sym.file(elf_file).?.index() != self.index) continue;
         if (!isAlive(global, elf_file)) continue;
@@ -1136,7 +1180,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
     }
 }
 
-pub fn writeSymtab(self: Object, elf_file: *Elf) void {
+pub fn writeSymtab(self: *Object, elf_file: *Elf) void {
     for (self.locals()) |local| {
         const idx = local.outputSymtabIndex(elf_file) orelse continue;
         const out_sym = &elf_file.symtab.items[idx];
@@ -1147,7 +1191,7 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void {
     }
 
     for (self.globals(), self.symbols_resolver.items) |global, resolv| {
-        const ref = elf_file.resolver.items[resolv];
+        const ref = elf_file.resolver.values.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;
@@ -1199,7 +1243,7 @@ fn locals(self: *Object) []Symbol {
     return self.symbols.items[0..end];
 }
 
-fn globals(self: *Object) []Symbol {
+pub 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;
@@ -1214,8 +1258,12 @@ pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref
     return elf_file.resolver.get(resolv).?;
 }
 
-pub fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
+fn addSymbol(self: *Object, allocator: Allocator) !Symbol.Index {
     try self.symbols.ensureUnusedCapacity(allocator, 1);
+    return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *Object) Symbol.Index {
     const index: Symbol.Index = @intCast(self.symbols.items.len);
     self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
     return index;
@@ -1430,15 +1478,14 @@ fn formatSymtab(
     _ = unused_fmt_string;
     _ = options;
     const object = ctx.object;
+    const elf_file = ctx.elf_file;
     try writer.writeAll("  locals\n");
-    for (object.locals()) |index| {
-        const local = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{local.fmt(ctx.elf_file)});
+    for (object.locals()) |sym| {
+        try writer.print("    {}\n", .{sym.fmt(elf_file)});
     }
     try writer.writeAll("  globals\n");
-    for (object.globals()) |index| {
-        const global = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{global.fmt(ctx.elf_file)});
+    for (object.globals()) |sym| {
+        try writer.print("    {}\n", .{sym.fmt(elf_file)});
     }
 }
 
src/link/Elf/relocatable.zig
@@ -37,7 +37,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]co
 
     // First, we flush relocatable object file generated with our backends.
     if (elf_file.zigObjectPtr()) |zig_object| {
-        zig_object.resolveSymbols(elf_file);
+        try zig_object.resolveSymbols(elf_file);
         try elf_file.addCommentString();
         try elf_file.finalizeMergeSections();
         zig_object.claimUnresolvedObject(elf_file);
@@ -383,7 +383,7 @@ fn updateComdatGroupsSizes(elf_file: *Elf) void {
         shdr.sh_size = cg.size(elf_file);
         shdr.sh_link = elf_file.symtab_section_index.?;
 
-        const sym = elf_file.symbol(cg.symbol(elf_file));
+        const sym = cg.symbol(elf_file);
         shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse
             elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx(elf_file).?);
     }
src/link/Elf/SharedObject.zig
@@ -286,8 +286,8 @@ 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));
+        const ref_file = ref_sym.file(elf_file).?;
+        const vis = @as(elf.STV, @enumFromInt(ref_sym.elfSym(elf_file).st_other));
         if (ref_file != .shared_object and vis != .HIDDEN) ref_sym.flags.@"export" = true;
     }
 }
@@ -349,9 +349,12 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
     assert(self.aliases == null);
 
     const SortAlias = struct {
-        pub fn lessThan(ctx: *Elf, lhs: Symbol.Index, rhs: Symbol.Index) bool {
-            const lhs_sym = ctx.symbol(lhs).elfSym(ctx);
-            const rhs_sym = ctx.symbol(rhs).elfSym(ctx);
+        so: *SharedObject,
+        ef: *Elf,
+
+        pub fn lessThan(ctx: @This(), lhs: Symbol.Index, rhs: Symbol.Index) bool {
+            const lhs_sym = ctx.so.symbols.items[lhs].elfSym(ctx.ef);
+            const rhs_sym = ctx.so.symbols.items[rhs].elfSym(ctx.ef);
             return lhs_sym.st_value < rhs_sym.st_value;
         }
     };
@@ -362,14 +365,14 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
     defer aliases.deinit();
     try aliases.ensureTotalCapacityPrecise(self.symbols.items.len);
 
-    for (self.symbols_resolvers.items, 0..) |resolv, index| {
+    for (self.symbols_resolver.items, 0..) |resolv, index| {
         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;
-        aliases.appendAssumeCapacity(index);
+        aliases.appendAssumeCapacity(@intCast(index));
     }
 
-    std.mem.sort(u32, aliases.items, elf_file, SortAlias.lessThan);
+    std.mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan);
 
     self.aliases = aliases.moveToUnmanaged();
 }
@@ -377,16 +380,16 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
 pub fn symbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 {
     assert(self.aliases != null);
 
-    const symbol = self.symbol(index).elfSym(elf_file);
+    const symbol = self.symbols.items[index].elfSym(elf_file);
     const aliases = self.aliases.?;
 
     const start = for (aliases.items, 0..) |alias, i| {
-        const alias_sym = self.symbol(alias).elfSym(elf_file);
+        const alias_sym = self.symbols.items[alias].elfSym(elf_file);
         if (symbol.st_value == alias_sym.st_value) break i;
     } else aliases.items.len;
 
     const end = for (aliases.items[start..], 0..) |alias, i| {
-        const alias_sym = self.symbol(alias).elfSym(elf_file);
+        const alias_sym = self.symbols.items[alias].elfSym(elf_file);
         if (symbol.st_value < alias_sym.st_value) break i + start;
     } else aliases.items.len;
 
@@ -403,8 +406,12 @@ pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) El
     return elf_file.resolver.get(resolv).?;
 }
 
-pub fn addSymbol(self: *SharedObject, allocator: Allocator) !Symbol.Index {
+fn addSymbol(self: *SharedObject, allocator: Allocator) !Symbol.Index {
     try self.symbols.ensureUnusedCapacity(allocator, 1);
+    return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *SharedObject) Symbol.Index {
     const index: Symbol.Index = @intCast(self.symbols.items.len);
     self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
     return index;
@@ -486,10 +493,10 @@ fn formatSymtab(
     _ = unused_fmt_string;
     _ = options;
     const shared = ctx.shared;
+    const elf_file = ctx.elf_file;
     try writer.writeAll("  globals\n");
-    for (shared.symbols.items) |index| {
-        const global = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{global.fmt(ctx.elf_file)});
+    for (shared.symbols.items) |sym| {
+        try writer.print("    {}\n", .{sym.fmt(elf_file)});
     }
 }
 
src/link/Elf/Symbol.zig
@@ -86,6 +86,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
 
 pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
     return switch (symbol.file(elf_file).?) {
+        .zig_object => |x| x.symtab.items(.elf_sym)[symbol.esym_index],
         inline else => |x| x.symtab.items[symbol.esym_index],
     };
 }
@@ -261,7 +262,7 @@ const AddExtraOpts = struct {
     zig_got: ?u32 = null,
 };
 
-pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
+pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void {
     var extras = symbol.extra(elf_file);
     inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| {
         if (@field(opts, field.name)) |x| {
@@ -272,11 +273,15 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void {
 }
 
 pub fn extra(symbol: Symbol, elf_file: *Elf) Extra {
-    return elf_file.symbolExtra(symbol.extra_index);
+    return switch (symbol.file(elf_file).?) {
+        inline else => |x| x.symbolExtra(symbol.extra_index),
+    };
 }
 
 pub fn setExtra(symbol: Symbol, extras: Extra, elf_file: *Elf) void {
-    elf_file.setSymbolExtra(symbol.extra_index, extras);
+    return switch (symbol.file(elf_file).?) {
+        inline else => |x| x.setSymbolExtra(symbol.extra_index, extras),
+    };
 }
 
 pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void {
src/link/Elf/synthetic_sections.zig
@@ -251,15 +251,16 @@ pub const ZigGotSection = struct {
     pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
+        const zo = elf_file.zigObjectPtr().?;
         const index = try zig_got.allocateEntry(gpa);
         const entry = &zig_got.entries.items[index];
         entry.* = sym_index;
-        const symbol = elf_file.symbol(sym_index);
+        const symbol = zo.symbol(sym_index);
         symbol.flags.has_zig_got = true;
         if (elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) {
             zig_got.flags.needs_rela = true;
         }
-        try symbol.addExtra(.{ .zig_got = index }, elf_file);
+        symbol.addExtra(.{ .zig_got = index }, elf_file);
         return index;
     }
 
@@ -282,6 +283,7 @@ pub const ZigGotSection = struct {
     }
 
     pub fn writeOne(zig_got: *ZigGotSection, elf_file: *Elf, index: Index) !void {
+        const zo = elf_file.zigObjectPtr().?;
         if (zig_got.flags.dirty) {
             const needed_size = zig_got.size(elf_file);
             try elf_file.growAllocSection(elf_file.zig_got_section_index.?, needed_size);
@@ -293,7 +295,7 @@ pub const ZigGotSection = struct {
         const off = zig_got.entryOffset(index, elf_file);
         const vaddr: u64 = @intCast(zig_got.entryAddress(index, elf_file));
         const entry = zig_got.entries.items[index];
-        const value = elf_file.symbol(entry).address(.{}, elf_file);
+        const value = zo.symbol(entry).address(.{}, elf_file);
         switch (entry_size) {
             2 => {
                 var buf: [2]u8 = undefined;
@@ -336,8 +338,9 @@ pub const ZigGotSection = struct {
     }
 
     pub fn writeAll(zig_got: ZigGotSection, elf_file: *Elf, writer: anytype) !void {
+        const zo = elf_file.zigObjectPtr().?;
         for (zig_got.entries.items) |entry| {
-            const symbol = elf_file.symbol(entry);
+            const symbol = zo.symbol(entry);
             const value = symbol.address(.{ .plt = false }, elf_file);
             try writeInt(value, elf_file, writer);
         }
@@ -351,9 +354,10 @@ pub const ZigGotSection = struct {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const cpu_arch = elf_file.getTarget().cpu.arch;
+        const zo = elf_file.zigObjectPtr().?;
         try elf_file.rela_dyn.ensureUnusedCapacity(gpa, zig_got.numRela());
         for (zig_got.entries.items) |entry| {
-            const symbol = elf_file.symbol(entry);
+            const symbol = zo.symbol(entry);
             const offset = symbol.zigGotAddress(elf_file);
             elf_file.addRelaDynAssumeCapacity(.{
                 .offset = @intCast(offset),
@@ -364,16 +368,18 @@ pub const ZigGotSection = struct {
     }
 
     pub fn updateSymtabSize(zig_got: *ZigGotSection, elf_file: *Elf) void {
+        const zo = elf_file.zigObjectPtr().?;
         zig_got.output_symtab_ctx.nlocals = @as(u32, @intCast(zig_got.entries.items.len));
         for (zig_got.entries.items) |entry| {
-            const name = elf_file.symbol(entry).name(elf_file);
+            const name = zo.symbol(entry).name(elf_file);
             zig_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$ziggot".len)) + 1;
         }
     }
 
     pub fn writeSymtab(zig_got: ZigGotSection, elf_file: *Elf) void {
+        const zo = elf_file.zigObjectPtr().?;
         for (zig_got.entries.items, zig_got.output_symtab_ctx.ilocal.., 0..) |entry, ilocal, index| {
-            const symbol = elf_file.symbol(entry);
+            const symbol = zo.symbol(entry);
             const symbol_name = symbol.name(elf_file);
             const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
             elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
@@ -409,15 +415,18 @@ pub const ZigGotSection = struct {
     ) !void {
         _ = options;
         _ = unused_fmt_string;
+        const zig_got = ctx.zig_got;
+        const elf_file = ctx.elf_file;
+        const zo = elf_file.zigObjectPtr().?;
         try writer.writeAll(".zig.got\n");
-        for (ctx.zig_got.entries.items, 0..) |entry, index| {
-            const symbol = ctx.elf_file.symbol(entry);
+        for (zig_got.entries.items, 0..) |entry, index| {
+            const symbol = zo.symbol(entry);
             try writer.print("  {d}@0x{x} => {d}@0x{x} ({s})\n", .{
                 index,
-                ctx.zig_got.entryAddress(@intCast(index), ctx.elf_file),
+                zig_got.entryAddress(@intCast(index), elf_file),
                 entry,
-                symbol.address(.{}, ctx.elf_file),
-                symbol.name(ctx.elf_file),
+                symbol.address(.{}, elf_file),
+                symbol.name(elf_file),
             });
         }
     }
@@ -446,7 +455,7 @@ pub const GotSection = struct {
 
     const Entry = struct {
         tag: Tag,
-        symbol_index: Symbol.Index,
+        ref: Elf.Ref,
         cell_index: Index,
 
         /// Returns how many indexes in the GOT this entry uses.
@@ -477,25 +486,25 @@ pub const GotSection = struct {
             const last = got.entries.items[index - 1];
             break :blk last.cell_index + @as(Index, @intCast(last.len()));
         } else 0;
-        entry.* = .{ .tag = undefined, .symbol_index = undefined, .cell_index = cell_index };
+        entry.* = .{ .tag = undefined, .ref = undefined, .cell_index = cell_index };
         return index;
     }
 
-    pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index {
+    pub fn addGotSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !Index {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = try got.allocateEntry(gpa);
         const entry = &got.entries.items[index];
         entry.tag = .got;
-        entry.symbol_index = sym_index;
-        const symbol = elf_file.symbol(sym_index);
+        entry.ref = ref;
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_got = true;
         if (symbol.flags.import or symbol.isIFunc(elf_file) or
             ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file)))
         {
             got.flags.needs_rela = true;
         }
-        try symbol.addExtra(.{ .got = index }, elf_file);
+        symbol.addExtra(.{ .got = index }, elf_file);
         return index;
     }
 
@@ -506,48 +515,48 @@ pub const GotSection = struct {
         const index = try got.allocateEntry(gpa);
         const entry = &got.entries.items[index];
         entry.tag = .tlsld;
-        entry.symbol_index = undefined; // unused
+        entry.ref = .{ .index = 0, .file = 0 }; // unused
         got.flags.needs_rela = true;
         got.tlsld_index = index;
     }
 
-    pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addTlsGdSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = try got.allocateEntry(gpa);
         const entry = &got.entries.items[index];
         entry.tag = .tlsgd;
-        entry.symbol_index = sym_index;
-        const symbol = elf_file.symbol(sym_index);
+        entry.ref = ref;
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_tlsgd = true;
         if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true;
-        try symbol.addExtra(.{ .tlsgd = index }, elf_file);
+        symbol.addExtra(.{ .tlsgd = index }, elf_file);
     }
 
-    pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addGotTpSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = try got.allocateEntry(gpa);
         const entry = &got.entries.items[index];
         entry.tag = .gottp;
-        entry.symbol_index = sym_index;
-        const symbol = elf_file.symbol(sym_index);
+        entry.ref = ref;
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_gottp = true;
         if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true;
-        try symbol.addExtra(.{ .gottp = index }, elf_file);
+        symbol.addExtra(.{ .gottp = index }, elf_file);
     }
 
-    pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addTlsDescSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = try got.allocateEntry(gpa);
         const entry = &got.entries.items[index];
         entry.tag = .tlsdesc;
-        entry.symbol_index = sym_index;
-        const symbol = elf_file.symbol(sym_index);
+        entry.ref = ref;
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_tlsdesc = true;
         got.flags.needs_rela = true;
-        try symbol.addExtra(.{ .tlsdesc = index }, elf_file);
+        symbol.addExtra(.{ .tlsdesc = index }, elf_file);
     }
 
     pub fn size(got: GotSection, elf_file: *Elf) usize {
@@ -564,10 +573,7 @@ pub const GotSection = struct {
         const apply_relocs = true; // TODO add user option for this
 
         for (got.entries.items) |entry| {
-            const symbol = switch (entry.tag) {
-                .tlsld => null,
-                inline else => elf_file.symbol(entry.symbol_index),
-            };
+            const symbol = elf_file.symbol(entry.ref);
             switch (entry.tag) {
                 .got => {
                     const value = blk: {
@@ -637,10 +643,7 @@ pub const GotSection = struct {
         try elf_file.rela_dyn.ensureUnusedCapacity(gpa, got.numRela(elf_file));
 
         for (got.entries.items) |entry| {
-            const symbol = switch (entry.tag) {
-                .tlsld => null,
-                inline else => elf_file.symbol(entry.symbol_index),
-            };
+            const symbol = elf_file.symbol(entry.ref);
             const extra = if (symbol) |s| s.extra(elf_file) else null;
 
             switch (entry.tag) {
@@ -740,10 +743,7 @@ pub const GotSection = struct {
         const is_dyn_lib = elf_file.isEffectivelyDynLib();
         var num: usize = 0;
         for (got.entries.items) |entry| {
-            const symbol = switch (entry.tag) {
-                .tlsld => null,
-                inline else => elf_file.symbol(entry.symbol_index),
-            };
+            const symbol = elf_file.symbol(entry.ref);
             switch (entry.tag) {
                 .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or
                     ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and
@@ -775,24 +775,15 @@ pub const GotSection = struct {
     pub fn updateSymtabSize(got: *GotSection, elf_file: *Elf) void {
         got.output_symtab_ctx.nlocals = @as(u32, @intCast(got.entries.items.len));
         for (got.entries.items) |entry| {
-            const symbol_name = switch (entry.tag) {
-                .tlsld => "",
-                inline else => elf_file.symbol(entry.symbol_index).name(elf_file),
-            };
+            const symbol_name = if (elf_file.symbol(entry.ref)) |sym| sym.name(elf_file) else "";
             got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len)) + 1 + 1;
         }
     }
 
     pub fn writeSymtab(got: GotSection, elf_file: *Elf) void {
         for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| {
-            const symbol = switch (entry.tag) {
-                .tlsld => null,
-                inline else => elf_file.symbol(entry.symbol_index),
-            };
-            const symbol_name = switch (entry.tag) {
-                .tlsld => "",
-                inline else => symbol.?.name(elf_file),
-            };
+            const symbol = elf_file.symbol(entry.ref);
+            const symbol_name = if (symbol) |s| s.name(elf_file) else "";
             const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
             elf_file.strtab.appendSliceAssumeCapacity(symbol_name);
             elf_file.strtab.appendAssumeCapacity('$');
@@ -828,36 +819,38 @@ pub const GotSection = struct {
     ) !void {
         _ = options;
         _ = unused_fmt_string;
+        const got = ctx.got;
+        const elf_file = ctx.elf_file;
         try writer.writeAll("GOT\n");
-        for (ctx.got.entries.items) |entry| {
-            const symbol = ctx.elf_file.symbol(entry.symbol_index);
-            try writer.print("  {d}@0x{x} => {d}@0x{x} ({s})\n", .{
+        for (got.entries.items) |entry| {
+            const symbol = elf_file.symbol(entry.ref).?;
+            try writer.print("  {d}@0x{x} => {}@0x{x} ({s})\n", .{
                 entry.cell_index,
-                entry.address(ctx.elf_file),
-                entry.symbol_index,
-                symbol.address(.{}, ctx.elf_file),
-                symbol.name(ctx.elf_file),
+                entry.address(elf_file),
+                entry.ref,
+                symbol.address(.{}, elf_file),
+                symbol.name(elf_file),
             });
         }
     }
 };
 
 pub const PltSection = struct {
-    symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+    symbols: std.ArrayListUnmanaged(Elf.Ref) = .{},
     output_symtab_ctx: Elf.SymtabCtx = .{},
 
     pub fn deinit(plt: *PltSection, allocator: Allocator) void {
         plt.symbols.deinit(allocator);
     }
 
-    pub fn addSymbol(plt: *PltSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addSymbol(plt: *PltSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = @as(u32, @intCast(plt.symbols.items.len));
-        const symbol = elf_file.symbol(sym_index);
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_plt = true;
-        try symbol.addExtra(.{ .plt = index }, elf_file);
-        try plt.symbols.append(gpa, sym_index);
+        symbol.addExtra(.{ .plt = index }, elf_file);
+        try plt.symbols.append(gpa, ref);
     }
 
     pub fn size(plt: PltSection, elf_file: *Elf) usize {
@@ -895,8 +888,8 @@ pub const PltSection = struct {
         const gpa = comp.gpa;
         const cpu_arch = elf_file.getTarget().cpu.arch;
         try elf_file.rela_plt.ensureUnusedCapacity(gpa, plt.numRela());
-        for (plt.symbols.items) |sym_index| {
-            const sym = elf_file.symbol(sym_index);
+        for (plt.symbols.items) |ref| {
+            const sym = elf_file.symbol(ref).?;
             assert(sym.flags.import);
             const extra = sym.extra(elf_file);
             const r_offset: u64 = @intCast(sym.gotPltAddress(elf_file));
@@ -916,16 +909,16 @@ pub const PltSection = struct {
 
     pub fn updateSymtabSize(plt: *PltSection, elf_file: *Elf) void {
         plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len));
-        for (plt.symbols.items) |sym_index| {
-            const name = elf_file.symbol(sym_index).name(elf_file);
+        for (plt.symbols.items) |ref| {
+            const name = elf_file.symbol(ref).?.name(elf_file);
             plt.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$plt".len)) + 1;
         }
     }
 
     pub fn writeSymtab(plt: PltSection, elf_file: *Elf) void {
         const cpu_arch = elf_file.getTarget().cpu.arch;
-        for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
-            const sym = elf_file.symbol(sym_index);
+        for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |ref, ilocal| {
+            const sym = elf_file.symbol(ref).?;
             const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
             elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
             elf_file.strtab.appendSliceAssumeCapacity("$plt");
@@ -958,15 +951,17 @@ pub const PltSection = struct {
     ) !void {
         _ = options;
         _ = unused_fmt_string;
+        const plt = ctx.plt;
+        const elf_file = ctx.elf_file;
         try writer.writeAll("PLT\n");
-        for (ctx.plt.symbols.items, 0..) |symbol_index, i| {
-            const symbol = ctx.elf_file.symbol(symbol_index);
-            try writer.print("  {d}@0x{x} => {d}@0x{x} ({s})\n", .{
+        for (plt.symbols.items, 0..) |ref, i| {
+            const symbol = elf_file.symbol(ref).?;
+            try writer.print("  {d}@0x{x} => {}@0x{x} ({s})\n", .{
                 i,
-                symbol.pltAddress(ctx.elf_file),
-                symbol_index,
-                symbol.address(.{}, ctx.elf_file),
-                symbol.name(ctx.elf_file),
+                symbol.pltAddress(elf_file),
+                ref,
+                symbol.address(.{}, elf_file),
+                symbol.name(elf_file),
             });
         }
     }
@@ -988,8 +983,8 @@ pub const PltSection = struct {
             try writer.writeAll(&preamble);
             try writer.writeByteNTimes(0xcc, preambleSize(.x86_64) - preamble.len);
 
-            for (plt.symbols.items, 0..) |sym_index, i| {
-                const sym = elf_file.symbol(sym_index);
+            for (plt.symbols.items, 0..) |ref, i| {
+                const sym = elf_file.symbol(ref).?;
                 const target_addr = sym.gotPltAddress(elf_file);
                 const source_addr = sym.pltAddress(elf_file);
                 disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4;
@@ -1037,8 +1032,8 @@ pub const PltSection = struct {
                 }
             }
 
-            for (plt.symbols.items) |sym_index| {
-                const sym = elf_file.symbol(sym_index);
+            for (plt.symbols.items) |ref| {
+                const sym = elf_file.symbol(ref).?;
                 const target_addr = sym.gotPltAddress(elf_file);
                 const source_addr = sym.pltAddress(elf_file);
                 const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr);
@@ -1075,7 +1070,7 @@ pub const GotPltSection = struct {
         _ = got_plt;
         {
             // [0]: _DYNAMIC
-            const symbol = elf_file.symbol(elf_file.linkerDefinedPtr().?.dynamic_index.?);
+            const symbol = elf_file.linkerDefinedPtr().?.dynamicSymbol(elf_file).?;
             try writer.writeInt(u64, @intCast(symbol.address(.{}, elf_file)), .little);
         }
         // [1]: 0x0
@@ -1093,22 +1088,22 @@ pub const GotPltSection = struct {
 };
 
 pub const PltGotSection = struct {
-    symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+    symbols: std.ArrayListUnmanaged(Elf.Ref) = .{},
     output_symtab_ctx: Elf.SymtabCtx = .{},
 
     pub fn deinit(plt_got: *PltGotSection, allocator: Allocator) void {
         plt_got.symbols.deinit(allocator);
     }
 
-    pub fn addSymbol(plt_got: *PltGotSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addSymbol(plt_got: *PltGotSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = @as(u32, @intCast(plt_got.symbols.items.len));
-        const symbol = elf_file.symbol(sym_index);
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.has_plt = true;
         symbol.flags.has_got = true;
-        try symbol.addExtra(.{ .plt_got = index }, elf_file);
-        try plt_got.symbols.append(gpa, sym_index);
+        symbol.addExtra(.{ .plt_got = index }, elf_file);
+        try plt_got.symbols.append(gpa, ref);
     }
 
     pub fn size(plt_got: PltGotSection, elf_file: *Elf) usize {
@@ -1134,15 +1129,15 @@ pub const PltGotSection = struct {
 
     pub fn updateSymtabSize(plt_got: *PltGotSection, elf_file: *Elf) void {
         plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len));
-        for (plt_got.symbols.items) |sym_index| {
-            const name = elf_file.symbol(sym_index).name(elf_file);
+        for (plt_got.symbols.items) |ref| {
+            const name = elf_file.symbol(ref).?.name(elf_file);
             plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(name.len + "$pltgot".len)) + 1;
         }
     }
 
     pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void {
-        for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
-            const sym = elf_file.symbol(sym_index);
+        for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |ref, ilocal| {
+            const sym = elf_file.symbol(ref).?;
             const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
             elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
             elf_file.strtab.appendSliceAssumeCapacity("$pltgot");
@@ -1160,8 +1155,8 @@ pub const PltGotSection = struct {
 
     const x86_64 = struct {
         pub fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void {
-            for (plt_got.symbols.items) |sym_index| {
-                const sym = elf_file.symbol(sym_index);
+            for (plt_got.symbols.items) |ref| {
+                const sym = elf_file.symbol(ref).?;
                 const target_addr = sym.gotAddress(elf_file);
                 const source_addr = sym.pltGotAddress(elf_file);
                 const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4;
@@ -1178,8 +1173,8 @@ pub const PltGotSection = struct {
 
     const aarch64 = struct {
         fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void {
-            for (plt_got.symbols.items) |sym_index| {
-                const sym = elf_file.symbol(sym_index);
+            for (plt_got.symbols.items) |ref| {
+                const sym = elf_file.symbol(ref).?;
                 const target_addr = sym.gotAddress(elf_file);
                 const source_addr = sym.pltGotAddress(elf_file);
                 const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr);
@@ -1204,56 +1199,56 @@ pub const PltGotSection = struct {
 };
 
 pub const CopyRelSection = struct {
-    symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+    symbols: std.ArrayListUnmanaged(Elf.Ref) = .{},
 
     pub fn deinit(copy_rel: *CopyRelSection, allocator: Allocator) void {
         copy_rel.symbols.deinit(allocator);
     }
 
-    pub fn addSymbol(copy_rel: *CopyRelSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addSymbol(copy_rel: *CopyRelSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = @as(u32, @intCast(copy_rel.symbols.items.len));
-        const symbol = elf_file.symbol(sym_index);
+        const symbol = elf_file.symbol(ref).?;
         symbol.flags.import = true;
         symbol.flags.@"export" = true;
         symbol.flags.has_copy_rel = true;
         symbol.flags.weak = false;
-        try symbol.addExtra(.{ .copy_rel = index }, elf_file);
-        try copy_rel.symbols.append(gpa, sym_index);
+        symbol.addExtra(.{ .copy_rel = index }, elf_file);
+        try copy_rel.symbols.append(gpa, ref);
 
         const shared_object = symbol.file(elf_file).?.shared_object;
         if (shared_object.aliases == null) {
             try shared_object.initSymbolAliases(elf_file);
         }
 
-        const aliases = shared_object.symbolAliases(sym_index, elf_file);
+        const aliases = shared_object.symbolAliases(ref.index, elf_file);
         for (aliases) |alias| {
-            if (alias == sym_index) continue;
-            const alias_sym = elf_file.symbol(alias);
+            if (alias == ref.index) continue;
+            const alias_sym = &shared_object.symbols.items[alias];
             alias_sym.flags.import = true;
             alias_sym.flags.@"export" = true;
             alias_sym.flags.has_copy_rel = true;
             alias_sym.flags.needs_copy_rel = true;
             alias_sym.flags.weak = false;
-            try elf_file.dynsym.addSymbol(alias, elf_file);
+            try elf_file.dynsym.addSymbol(.{ .index = alias, .file = shared_object.index }, elf_file);
         }
     }
 
     pub fn updateSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void {
         const shdr = &elf_file.shdrs.items[shndx];
-        for (copy_rel.symbols.items) |sym_index| {
-            const symbol = elf_file.symbol(sym_index);
+        for (copy_rel.symbols.items) |ref| {
+            const symbol = elf_file.symbol(ref).?;
             const shared_object = symbol.file(elf_file).?.shared_object;
             const alignment = try symbol.dsoAlignment(elf_file);
             symbol.value = @intCast(mem.alignForward(u64, shdr.sh_size, alignment));
             shdr.sh_addralign = @max(shdr.sh_addralign, alignment);
             shdr.sh_size = @as(u64, @intCast(symbol.value)) + symbol.elfSym(elf_file).st_size;
 
-            const aliases = shared_object.symbolAliases(sym_index, elf_file);
+            const aliases = shared_object.symbolAliases(ref.index, elf_file);
             for (aliases) |alias| {
-                if (alias == sym_index) continue;
-                const alias_sym = elf_file.symbol(alias);
+                if (alias == ref.index) continue;
+                const alias_sym = &shared_object.symbols.items[alias];
                 alias_sym.value = symbol.value;
             }
         }
@@ -1264,8 +1259,8 @@ pub const CopyRelSection = struct {
         const gpa = comp.gpa;
         const cpu_arch = elf_file.getTarget().cpu.arch;
         try elf_file.rela_dyn.ensureUnusedCapacity(gpa, copy_rel.numRela());
-        for (copy_rel.symbols.items) |sym_index| {
-            const sym = elf_file.symbol(sym_index);
+        for (copy_rel.symbols.items) |ref| {
+            const sym = elf_file.symbol(ref).?;
             assert(sym.flags.import and sym.flags.has_copy_rel);
             const extra = sym.extra(elf_file);
             elf_file.addRelaDynAssumeCapacity(.{
@@ -1285,8 +1280,8 @@ pub const DynsymSection = struct {
     entries: std.ArrayListUnmanaged(Entry) = .{},
 
     pub const Entry = struct {
-        /// Index of the symbol which gets privilege of getting a dynamic treatment
-        symbol_index: Symbol.Index,
+        /// Ref of the symbol which gets privilege of getting a dynamic treatment
+        ref: Elf.Ref,
         /// Offset into .dynstrtab
         off: u32,
     };
@@ -1295,22 +1290,22 @@ pub const DynsymSection = struct {
         dynsym.entries.deinit(allocator);
     }
 
-    pub fn addSymbol(dynsym: *DynsymSection, sym_index: Symbol.Index, elf_file: *Elf) !void {
+    pub fn addSymbol(dynsym: *DynsymSection, ref: Elf.Ref, elf_file: *Elf) !void {
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
         const index = @as(u32, @intCast(dynsym.entries.items.len + 1));
-        const sym = elf_file.symbol(sym_index);
+        const sym = elf_file.symbol(ref).?;
         sym.flags.has_dynamic = true;
-        try sym.addExtra(.{ .dynamic = index }, elf_file);
+        sym.addExtra(.{ .dynamic = index }, elf_file);
         const off = try elf_file.insertDynString(sym.name(elf_file));
-        try dynsym.entries.append(gpa, .{ .symbol_index = sym_index, .off = off });
+        try dynsym.entries.append(gpa, .{ .ref = ref, .off = off });
     }
 
     pub fn sort(dynsym: *DynsymSection, elf_file: *Elf) void {
         const Sort = struct {
             pub fn lessThan(ctx: *Elf, lhs: Entry, rhs: Entry) bool {
-                const lhs_sym = ctx.symbol(lhs.symbol_index);
-                const rhs_sym = ctx.symbol(rhs.symbol_index);
+                const lhs_sym = ctx.symbol(lhs.ref).?;
+                const rhs_sym = ctx.symbol(rhs.ref).?;
 
                 if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") {
                     return rhs_sym.flags.@"export";
@@ -1329,7 +1324,7 @@ pub const DynsymSection = struct {
 
         var num_exports: u32 = 0;
         for (dynsym.entries.items) |entry| {
-            const sym = elf_file.symbol(entry.symbol_index);
+            const sym = elf_file.symbol(entry.ref).?;
             if (sym.flags.@"export") num_exports += 1;
         }
 
@@ -1338,7 +1333,7 @@ pub const DynsymSection = struct {
         std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan);
 
         for (dynsym.entries.items, 1..) |entry, index| {
-            const sym = elf_file.symbol(entry.symbol_index);
+            const sym = elf_file.symbol(entry.ref).?;
             var extra = sym.extra(elf_file);
             extra.dynamic = @as(u32, @intCast(index));
             sym.setExtra(extra, elf_file);
@@ -1356,7 +1351,7 @@ pub const DynsymSection = struct {
     pub fn write(dynsym: DynsymSection, elf_file: *Elf, writer: anytype) !void {
         try writer.writeStruct(Elf.null_sym);
         for (dynsym.entries.items) |entry| {
-            const sym = elf_file.symbol(entry.symbol_index);
+            const sym = elf_file.symbol(entry.ref).?;
             var out_sym: elf.Elf64_Sym = Elf.null_sym;
             sym.setOutputSym(elf_file, &out_sym);
             out_sym.st_name = entry.off;
@@ -1429,7 +1424,7 @@ pub const GnuHashSection = struct {
 
     fn getExports(elf_file: *Elf) []const DynsymSection.Entry {
         const start = for (elf_file.dynsym.entries.items, 0..) |entry, i| {
-            const sym = elf_file.symbol(entry.symbol_index);
+            const sym = elf_file.symbol(entry.ref).?;
             if (sym.flags.@"export") break i;
         } else elf_file.dynsym.entries.items.len;
         return elf_file.dynsym.entries.items[start..];
@@ -1477,7 +1472,7 @@ pub const GnuHashSection = struct {
         @memset(bloom, 0);
 
         for (exports, 0..) |entry, i| {
-            const sym = elf_file.symbol(entry.symbol_index);
+            const sym = elf_file.symbol(entry.ref).?;
             const h = hasher(sym.name(elf_file));
             hashes[i] = h;
             indices[i] = h % hash.num_buckets;
@@ -1574,7 +1569,7 @@ pub const VerneedSection = struct {
         try verneed.ensureTotalCapacity(dynsyms.len);
 
         for (dynsyms, 1..) |entry, i| {
-            const symbol = elf_file.symbol(entry.symbol_index);
+            const symbol = elf_file.symbol(entry.ref).?;
             if (symbol.flags.import and symbol.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) {
                 const shared_object = symbol.file(elf_file).?.shared_object;
                 verneed.appendAssumeCapacity(.{
@@ -1677,11 +1672,11 @@ pub const ComdatGroupSection = struct {
         return cg_file.object.comdatGroup(cgs.cg_ref.index);
     }
 
-    pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index {
+    pub fn symbol(cgs: ComdatGroupSection, elf_file: *Elf) *Symbol {
         const cg = cgs.comdatGroup(elf_file);
         const object = cg.file(elf_file).object;
         const shdr = object.shdrs.items[cg.shndx];
-        return object.symbols.items[shdr.sh_info];
+        return &object.symbols.items[shdr.sh_info];
     }
 
     pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {
src/link/Elf/thunks.zig
@@ -43,11 +43,7 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void {
                     else => @panic("unsupported arch"),
                 };
                 if (is_reachable) continue;
-                const target = switch (file) {
-                    .zig_object => |x| x.symbol(rel.r_sym()),
-                    .object => |x| x.symbols.items[rel.r_sym()],
-                    else => unreachable,
-                };
+                const target = file.resolveSymbol(rel.r_sym(), elf_file);
                 try thunk.symbols.put(gpa, target, {});
             }
             atom.addExtra(.{ .thunk = thunk_index }, elf_file);
@@ -80,7 +76,7 @@ fn maxAllowedDistance(cpu_arch: std.Target.Cpu.Arch) u32 {
 pub const Thunk = struct {
     value: i64 = 0,
     output_section_index: u32 = 0,
-    symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
+    symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .{},
     output_symtab_ctx: Elf.SymtabCtx = .{},
 
     pub fn deinit(thunk: *Thunk, allocator: Allocator) void {
@@ -97,9 +93,9 @@ pub const Thunk = struct {
         return @as(i64, @intCast(shdr.sh_addr)) + thunk.value;
     }
 
-    pub fn targetAddress(thunk: Thunk, sym_index: Symbol.Index, elf_file: *Elf) i64 {
+    pub fn targetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 {
         const cpu_arch = elf_file.getTarget().cpu.arch;
-        return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(sym_index).? * trampolineSize(cpu_arch)));
+        return thunk.address(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch)));
     }
 
     pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
@@ -112,16 +108,16 @@ pub const Thunk = struct {
 
     pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void {
         thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len));
-        for (thunk.symbols.keys()) |sym_index| {
-            const sym = elf_file.symbol(sym_index);
+        for (thunk.symbols.keys()) |ref| {
+            const sym = elf_file.symbol(ref).?;
             thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.name(elf_file).len + "$thunk".len + 1));
         }
     }
 
     pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void {
         const cpu_arch = elf_file.getTarget().cpu.arch;
-        for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |sym_index, ilocal| {
-            const sym = elf_file.symbol(sym_index);
+        for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| {
+            const sym = elf_file.symbol(ref).?;
             const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
             elf_file.strtab.appendSliceAssumeCapacity(sym.name(elf_file));
             elf_file.strtab.appendSliceAssumeCapacity("$thunk");
@@ -131,7 +127,7 @@ pub const Thunk = struct {
                 .st_info = elf.STT_FUNC,
                 .st_other = 0,
                 .st_shndx = @intCast(thunk.output_section_index),
-                .st_value = @intCast(thunk.targetAddress(sym_index, elf_file)),
+                .st_value = @intCast(thunk.targetAddress(ref, elf_file)),
                 .st_size = trampolineSize(cpu_arch),
             };
         }
@@ -181,9 +177,9 @@ pub const Thunk = struct {
         const thunk = ctx.thunk;
         const elf_file = ctx.elf_file;
         try writer.print("@{x} : size({x})\n", .{ thunk.value, thunk.size(elf_file) });
-        for (thunk.symbols.keys()) |index| {
-            const sym = elf_file.symbol(index);
-            try writer.print("  %{d} : {s} : @{x}\n", .{ index, sym.name(elf_file), sym.value });
+        for (thunk.symbols.keys()) |ref| {
+            const sym = elf_file.symbol(ref).?;
+            try writer.print("  {} : {s} : @{x}\n", .{ ref, sym.name(elf_file), sym.value });
         }
     }
 
@@ -195,12 +191,8 @@ const aarch64 = struct {
         const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
         if (r_type != .CALL26 and r_type != .JUMP26) return true;
         const file = atom.file(elf_file).?;
-        const target_index = switch (file) {
-            .zig_object => |x| x.symbol(rel.r_sym()),
-            .object => |x| x.symbols.items[rel.r_sym()],
-            else => unreachable,
-        };
-        const target = elf_file.symbol(target_index);
+        const target_ref = file.resolveSymbol(rel.r_sym(), elf_file);
+        const target = elf_file.symbol(target_ref).?;
         if (target.flags.has_plt) return false;
         if (atom.output_section_index != target.output_section_index) return false;
         const target_atom = target.atom(elf_file).?;
@@ -212,8 +204,8 @@ const aarch64 = struct {
     }
 
     fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void {
-        for (thunk.symbols.keys(), 0..) |sym_index, i| {
-            const sym = elf_file.symbol(sym_index);
+        for (thunk.symbols.keys(), 0..) |ref, i| {
+            const sym = elf_file.symbol(ref).?;
             const saddr = thunk.address(elf_file) + @as(i64, @intCast(i * trampoline_size));
             const taddr = sym.address(.{}, elf_file);
             const pages = try util.calcNumberOfPages(saddr, taddr);
src/link/Elf/ZigObject.zig
@@ -89,19 +89,11 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
     try self.strtab.buffer.append(gpa, 0);
 
     const name_off = try self.strtab.insert(gpa, self.path);
-    const symbol_index = try elf_file.addSymbol();
-    try self.local_symbols.append(gpa, symbol_index);
-    const symbol_ptr = elf_file.symbol(symbol_index);
-    symbol_ptr.file_index = self.index;
-    symbol_ptr.name_offset = name_off;
-    symbol_ptr.extra_index = try elf_file.addSymbolExtra(.{});
-
-    const esym_index = try self.addLocalEsym(gpa);
-    const esym = &self.local_esyms.items(.elf_sym)[esym_index];
-    esym.st_name = name_off;
+    const symbol_index = try self.newLocalSymbol(gpa, name_off);
+    const sym = self.symbol(symbol_index);
+    const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
     esym.st_info = elf.STT_FILE;
     esym.st_shndx = elf.SHN_ABS;
-    symbol_ptr.esym_index = esym_index;
 
     switch (comp.config.debug_format) {
         .strip => {},
@@ -275,7 +267,7 @@ fn newSymbol(self: *ZigObject, allocator: Allocator, name_off: u32, st_bind: u4)
     const index = self.addSymbolAssumeCapacity();
     const sym = &self.symbols.items[index];
     sym.name_offset = name_off;
-    sym.extra = self.addSymbolExtraAssumeCapacity(.{});
+    sym.extra_index = self.addSymbolExtraAssumeCapacity(.{});
 
     const esym_idx: u32 = @intCast(self.symtab.addOneAssumeCapacity());
     const esym = ElfSym{ .elf_sym = .{
@@ -377,7 +369,7 @@ pub fn resolveSymbols(self: *ZigObject, elf_file: *Elf) !void {
             continue;
         }
 
-        if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
+        if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
             gop.ref.* = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
         }
     }
@@ -428,7 +420,7 @@ pub fn claimUnresolvedObject(self: ZigObject, elf_file: *Elf) void {
         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 };
+        elf_file.resolver.values.items[idx - 1] = .{ .index = @intCast(i | global_symbol_bit), .file = self.index };
     }
 }
 
@@ -450,6 +442,64 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
     }
 }
 
+pub fn createSymbolIndirection(self: *ZigObject, elf_file: *Elf) !void {
+    const impl = struct {
+        fn impl(sym: *Symbol, ref: Elf.Ref, ef: *Elf) !void {
+            if (!sym.isLocal(ef) and !sym.flags.has_dynamic) {
+                log.debug("'{s}' is non-local", .{sym.name(ef)});
+                try ef.dynsym.addSymbol(ref, ef);
+            }
+            if (sym.flags.needs_got) {
+                log.debug("'{s}' needs GOT", .{sym.name(ef)});
+                _ = try ef.got.addGotSymbol(ref, ef);
+            }
+            if (sym.flags.needs_plt) {
+                if (sym.flags.is_canonical) {
+                    log.debug("'{s}' needs CPLT", .{sym.name(ef)});
+                    sym.flags.@"export" = true;
+                    try ef.plt.addSymbol(ref, ef);
+                } else if (sym.flags.needs_got) {
+                    log.debug("'{s}' needs PLTGOT", .{sym.name(ef)});
+                    try ef.plt_got.addSymbol(ref, ef);
+                } else {
+                    log.debug("'{s}' needs PLT", .{sym.name(ef)});
+                    try ef.plt.addSymbol(ref, ef);
+                }
+            }
+            if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) {
+                log.debug("'{s}' needs COPYREL", .{sym.name(ef)});
+                try ef.copy_rel.addSymbol(ref, ef);
+            }
+            if (sym.flags.needs_tlsgd) {
+                log.debug("'{s}' needs TLSGD", .{sym.name(ef)});
+                try ef.got.addTlsGdSymbol(ref, ef);
+            }
+            if (sym.flags.needs_gottp) {
+                log.debug("'{s}' needs GOTTP", .{sym.name(ef)});
+                try ef.got.addGotTpSymbol(ref, ef);
+            }
+            if (sym.flags.needs_tlsdesc) {
+                log.debug("'{s}' needs TLSDESC", .{sym.name(ef)});
+                try ef.got.addTlsDescSymbol(ref, ef);
+            }
+        }
+    }.impl;
+    for (self.local_symbols.items, 0..) |index, i| {
+        const sym = &self.symbols.items[index];
+        const ref = self.resolveSymbol(@intCast(i), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
+        try impl(sym, ref, elf_file);
+    }
+    for (self.global_symbols.items, 0..) |index, i| {
+        const sym = &self.symbols.items[index];
+        const ref = self.resolveSymbol(@intCast(i | global_symbol_bit), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
+        try impl(sym, ref, elf_file);
+    }
+}
+
 pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
     for (self.global_symbols.items, 0..) |index, i| {
         const global = self.symbols.items[index];
@@ -468,7 +518,7 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
     }
 }
 
-pub fn markImportsExports(self: *Object, elf_file: *Elf) void {
+pub fn markImportsExports(self: *ZigObject, 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;
@@ -604,7 +654,7 @@ pub fn updateSymtabSize(self: *ZigObject, elf_file: *Elf) !void {
 
     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 = elf_file.resolver.values.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;
@@ -633,7 +683,7 @@ pub fn writeSymtab(self: ZigObject, elf_file: *Elf) void {
 
     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 = elf_file.resolver.values.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;
@@ -678,13 +728,13 @@ pub fn getDeclVAddr(
     reloc_info: link.File.RelocInfo,
 ) !u64 {
     const this_sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index);
-    const this_sym = elf_file.symbol(this_sym_index);
+    const this_sym = self.symbol(this_sym_index);
     const vaddr = this_sym.address(.{}, elf_file);
-    const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
+    const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
     const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
     try parent_atom.addReloc(elf_file, .{
         .r_offset = reloc_info.offset,
-        .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | r_type,
+        .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | r_type,
         .r_addend = reloc_info.addend,
     });
     return @intCast(vaddr);
@@ -697,13 +747,13 @@ pub fn getAnonDeclVAddr(
     reloc_info: link.File.RelocInfo,
 ) !u64 {
     const sym_index = self.anon_decls.get(decl_val).?.symbol_index;
-    const sym = elf_file.symbol(sym_index);
+    const sym = self.symbol(sym_index);
     const vaddr = sym.address(.{}, elf_file);
-    const parent_atom = elf_file.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
+    const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(elf_file).?;
     const r_type = relocation.encode(.abs, elf_file.getTarget().cpu.arch);
     try parent_atom.addReloc(elf_file, .{
         .r_offset = reloc_info.offset,
-        .r_info = (@as(u64, @intCast(sym.esym_index)) << 32) | r_type,
+        .r_info = (@as(u64, @intCast(sym_index)) << 32) | r_type,
         .r_addend = reloc_info.addend,
     });
     return @intCast(vaddr);
@@ -725,7 +775,7 @@ pub fn lowerAnonDecl(
         else => explicit_alignment,
     };
     if (self.anon_decls.get(decl_val)) |metadata| {
-        const existing_alignment = elf_file.symbol(metadata.symbol_index).atom(elf_file).?.alignment;
+        const existing_alignment = self.symbol(metadata.symbol_index).atom(elf_file).?.alignment;
         if (decl_alignment.order(existing_alignment).compare(.lte))
             return .ok;
     }
@@ -811,11 +861,10 @@ fn freeUnnamedConsts(self: *ZigObject, elf_file: *Elf, decl_index: InternPool.De
 }
 
 fn freeDeclMetadata(self: *ZigObject, elf_file: *Elf, sym_index: Symbol.Index) void {
-    _ = self;
-    const sym = elf_file.symbol(sym_index);
+    const sym = self.symbol(sym_index);
     sym.atom(elf_file).?.free(elf_file);
     log.debug("adding %{d} to local symbols free list", .{sym_index});
-    elf_file.symbols.items[sym_index] = .{};
+    self.symbols.items[sym_index] = .{};
     // TODO free GOT entry here
 }
 
@@ -940,8 +989,8 @@ fn updateDeclCode(
         target_util.minFunctionAlignment(mod.getTarget()),
     );
 
-    const sym = elf_file.symbol(sym_index);
-    const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
+    const sym = self.symbol(sym_index);
+    const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
     const atom_ptr = sym.atom(elf_file).?;
     const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
 
@@ -1038,8 +1087,8 @@ fn updateTlv(
 
     const required_alignment = decl.getAlignment(pt);
 
-    const sym = elf_file.symbol(sym_index);
-    const esym = &self.local_esyms.items(.elf_sym)[sym.esym_index];
+    const sym = self.symbol(sym_index);
+    const esym = &self.symtab.items(.elf_sym)[sym.esym_index];
     const atom_ptr = sym.atom(elf_file).?;
     const name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
 
@@ -1264,9 +1313,9 @@ fn updateLazySymbol(
         .code => elf_file.zig_text_section_index.?,
         .const_data => elf_file.zig_data_rel_ro_section_index.?,
     };
-    const local_sym = elf_file.symbol(symbol_index);
+    const local_sym = self.symbol(symbol_index);
     local_sym.name_offset = name_str_index;
-    const local_esym = &self.local_esyms.items(.elf_sym)[local_sym.esym_index];
+    const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
     local_esym.st_name = name_str_index;
     local_esym.st_info |= elf.STT_OBJECT;
     local_esym.st_size = code.len;
@@ -1372,7 +1421,7 @@ fn lowerConst(
     };
 
     const local_sym = self.symbol(sym_index);
-    const local_esym = local_sym.elfSym(elf_file);
+    const local_esym = &self.symtab.items(.elf_sym)[local_sym.esym_index];
     local_esym.st_info |= elf.STT_OBJECT;
     local_esym.st_size = code.len;
     const atom_ptr = local_sym.atom(elf_file).?;
@@ -1473,9 +1522,9 @@ pub fn updateExports(
         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.version_index = elf_file.default_sym_version;
         global_sym.ref = .{ .index = esym_shndx, .file = self.index };
-        const global_esym = global_sym.elfSym(elf_file);
+        const global_esym = &self.symtab.items(.elf_sym)[global_sym.esym_index];
         global_esym.st_value = @intCast(value);
         global_esym.st_shndx = esym.st_shndx;
         global_esym.st_info = (stb_bits << 4) | stt_bits;
@@ -1517,16 +1566,16 @@ pub fn deleteExport(
     const exp_name = name.toSlice(&mod.intern_pool);
     const esym_index = metadata.@"export"(self, exp_name) orelse return;
     log.debug("deleting export '{s}'", .{exp_name});
-    const esym = &self.global_esyms.items(.elf_sym)[esym_index.*];
+    const esym = &self.symtab.items(.elf_sym)[esym_index.*];
     _ = self.globals_lookup.remove(esym.st_name);
-    const sym_index = elf_file.resolver.get(esym.st_name).?;
-    const sym = elf_file.symbol(sym_index);
-    if (sym.file_index == self.index) {
-        _ = elf_file.resolver.swapRemove(esym.st_name);
-        sym.* = .{};
-    }
+    // const sym_index = elf_file.resolver.get(esym.st_name).?;
+    // const sym = elf_file.symbol(sym_index);
+    // if (sym.file_index == self.index) {
+    //     _ = elf_file.resolver.swapRemove(esym.st_name);
+    //     sym.* = .{};
+    // }
     esym.* = Elf.null_sym;
-    self.global_esyms.items(.shndx)[esym_index.*] = elf.SHN_UNDEF;
+    self.symtab.items(.shndx)[esym_index.*] = elf.SHN_UNDEF;
 }
 
 pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
@@ -1535,7 +1584,7 @@ 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) {
-        lookup_gop.value_ptr.* = try self.newSymbol(gpa, off);
+        lookup_gop.value_ptr.* = try self.newGlobalSymbol(gpa, off);
     }
     return lookup_gop.value_ptr.*;
 }
@@ -1636,8 +1685,12 @@ pub fn resolveSymbol(self: ZigObject, index: Symbol.Index, elf_file: *Elf) Elf.R
     return .{ .index = index, .file = self.index };
 }
 
-pub fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
+fn addSymbol(self: *ZigObject, allocator: Allocator) !Symbol.Index {
     try self.symbols.ensureUnusedCapacity(allocator, 1);
+    return self.addSymbolAssumeCapacity();
+}
+
+fn addSymbolAssumeCapacity(self: *ZigObject) Symbol.Index {
     const index: Symbol.Index = @intCast(self.symbols.items.len);
     self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
     return index;
@@ -1705,15 +1758,17 @@ fn formatSymtab(
 ) !void {
     _ = unused_fmt_string;
     _ = options;
+    const self = ctx.self;
+    const elf_file = ctx.elf_file;
     try writer.writeAll("  locals\n");
-    for (ctx.self.locals()) |index| {
-        const local = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{local.fmt(ctx.elf_file)});
+    for (self.local_symbols.items) |index| {
+        const local = self.symbols.items[index];
+        try writer.print("    {}\n", .{local.fmt(elf_file)});
     }
     try writer.writeAll("  globals\n");
-    for (ctx.self.globals()) |index| {
-        const global = ctx.elf_file.symbol(index);
-        try writer.print("    {}\n", .{global.fmt(ctx.elf_file)});
+    for (ctx.self.global_symbols.items) |index| {
+        const global = self.symbols.items[index];
+        try writer.print("    {}\n", .{global.fmt(elf_file)});
     }
 }
 
@@ -1759,7 +1814,7 @@ const DeclMetadata = struct {
 
     fn @"export"(m: DeclMetadata, zo: *ZigObject, name: []const u8) ?*u32 {
         for (m.exports.items) |*exp| {
-            const exp_name = zo.getString(zo.symbol(exp.*).name_off);
+            const exp_name = zo.getString(zo.symbol(exp.*).name_offset);
             if (mem.eql(u8, name, exp_name)) return exp;
         }
         return null;
src/link/Elf.zig
@@ -2013,11 +2013,10 @@ fn claimUnresolved(self: *Elf) void {
 fn scanRelocs(self: *Elf) !void {
     const gpa = self.base.comp.gpa;
 
-    var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa);
+    var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa);
     defer {
-        var it = undefs.iterator();
-        while (it.next()) |entry| {
-            entry.value_ptr.deinit();
+        for (undefs.values()) |*refs| {
+            refs.deinit();
         }
         undefs.deinit();
     }
@@ -2028,7 +2027,18 @@ fn scanRelocs(self: *Elf) !void {
     objects.appendSliceAssumeCapacity(self.objects.items);
 
     var has_reloc_errors = false;
-    for (objects.items) |index| {
+    if (self.zigObjectPtr()) |zo| {
+        zo.asFile().scanRelocs(self, &undefs) catch |err| switch (err) {
+            error.RelaxFailure => unreachable,
+            error.UnsupportedCpuArch => {
+                try self.reportUnsupportedCpuArch();
+                return error.FlushFailure;
+            },
+            error.RelocFailure => has_reloc_errors = true,
+            else => |e| return e,
+        };
+    }
+    for (self.objects.items) |index| {
         self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
             error.RelaxFailure => unreachable,
             error.UnsupportedCpuArch => {
@@ -2044,47 +2054,12 @@ fn scanRelocs(self: *Elf) !void {
 
     if (has_reloc_errors) return error.FlushFailure;
 
-    for (self.symbols.items, 0..) |*sym, i| {
-        const index = @as(u32, @intCast(i));
-        if (!sym.isLocal(self) and !sym.flags.has_dynamic) {
-            log.debug("'{s}' is non-local", .{sym.name(self)});
-            try self.dynsym.addSymbol(index, self);
-        }
-        if (sym.flags.needs_got) {
-            log.debug("'{s}' needs GOT", .{sym.name(self)});
-            _ = try self.got.addGotSymbol(index, self);
-        }
-        if (sym.flags.needs_plt) {
-            if (sym.flags.is_canonical) {
-                log.debug("'{s}' needs CPLT", .{sym.name(self)});
-                sym.flags.@"export" = true;
-                try self.plt.addSymbol(index, self);
-            } else if (sym.flags.needs_got) {
-                log.debug("'{s}' needs PLTGOT", .{sym.name(self)});
-                try self.plt_got.addSymbol(index, self);
-            } else {
-                log.debug("'{s}' needs PLT", .{sym.name(self)});
-                try self.plt.addSymbol(index, self);
-            }
-        }
-        if (sym.flags.needs_copy_rel and !sym.flags.has_copy_rel) {
-            log.debug("'{s}' needs COPYREL", .{sym.name(self)});
-            try self.copy_rel.addSymbol(index, self);
-        }
-        if (sym.flags.needs_tlsgd) {
-            log.debug("'{s}' needs TLSGD", .{sym.name(self)});
-            try self.got.addTlsGdSymbol(index, self);
-        }
-        if (sym.flags.needs_gottp) {
-            log.debug("'{s}' needs GOTTP", .{sym.name(self)});
-            try self.got.addGotTpSymbol(index, self);
-        }
-        if (sym.flags.needs_tlsdesc) {
-            log.debug("'{s}' needs TLSDESC", .{sym.name(self)});
-            try self.got.addTlsDescSymbol(index, self);
-        }
+    if (self.zigObjectPtr()) |zo| {
+        try zo.asFile().createSymbolIndirection(self);
+    }
+    for (self.objects.items) |index| {
+        try self.file(index).?.createSymbolIndirection(self);
     }
-
     if (self.got.flags.needs_tlsld) {
         log.debug("program needs TLSLD", .{});
         try self.got.addTlsLdSymbol(self);
@@ -2861,8 +2836,8 @@ pub fn writeElfHeader(self: *Elf) !void {
     index += 4;
 
     const e_entry: u64 = if (self.linkerDefinedPtr()) |obj| blk: {
-        const entry_index = obj.entry_index orelse break :blk 0;
-        break :blk @intCast(self.symbol(entry_index).address(.{}, self));
+        const entry_sym = obj.entrySymbol(self) orelse break :blk 0;
+        break :blk @intCast(entry_sym.address(.{}, self));
     } else 0;
     const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0;
     switch (self.ptr_width) {
@@ -2993,7 +2968,7 @@ pub fn deleteExport(
 fn checkDuplicates(self: *Elf) !void {
     const gpa = self.base.comp.gpa;
 
-    var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
+    var dupes = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
     defer {
         for (dupes.values()) |*list| {
             list.deinit(gpa);
@@ -3301,7 +3276,7 @@ fn initSyntheticSections(self: *Elf) !void {
         });
 
         const needs_versions = for (self.dynsym.entries.items) |entry| {
-            const sym = self.symbol(entry.symbol_index);
+            const sym = self.symbol(entry.ref).?;
             if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
         } else false;
         if (needs_versions) {
@@ -3515,7 +3490,7 @@ fn setVersionSymtab(self: *Elf) !void {
     try self.versym.resize(gpa, self.dynsym.count());
     self.versym.items[0] = elf.VER_NDX_LOCAL;
     for (self.dynsym.entries.items, 1..) |entry, i| {
-        const sym = self.symbol(entry.symbol_index);
+        const sym = self.symbol(entry.ref).?;
         self.versym.items[i] = sym.version_index;
     }
 
@@ -4307,11 +4282,10 @@ fn allocateSpecialPhdrs(self: *Elf) void {
 fn writeAtoms(self: *Elf) !void {
     const gpa = self.base.comp.gpa;
 
-    var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Ref)).init(gpa);
+    var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa);
     defer {
-        var it = undefs.iterator();
-        while (it.next()) |entry| {
-            entry.value_ptr.deinit();
+        for (undefs.values()) |*refs| {
+            refs.deinit();
         }
         undefs.deinit();
     }
@@ -5261,7 +5235,7 @@ pub fn calcNumIRelativeRelocs(self: *Elf) usize {
 
     for (self.got.entries.items) |entry| {
         if (entry.tag != .got) continue;
-        const sym = self.symbol(entry.symbol_index);
+        const sym = self.symbol(entry.ref).?;
         if (sym.isIFunc(self)) count += 1;
     }
 
@@ -5443,36 +5417,34 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
 
     try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
 
-    var it = undefs.iterator();
-    while (it.next()) |entry| {
-        const undef_index = entry.key_ptr.*;
-        const atoms = entry.value_ptr.*.items;
-        const natoms = @min(atoms.len, max_notes);
-        const nnotes = natoms + @intFromBool(atoms.len > max_notes);
+    for (undefs.keys(), undefs.values()) |key, refs| {
+        const undef_sym = self.resolver.keys.items[key - 1];
+        const nrefs = @min(refs.items.len, max_notes);
+        const nnotes = nrefs + @intFromBool(refs.items.len > max_notes);
 
         var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
-        try err.addMsg("undefined symbol: {s}", .{self.symbol(undef_index).name(self)});
+        try err.addMsg("undefined symbol: {s}", .{undef_sym.name(self)});
 
-        for (atoms[0..natoms]) |ref| {
+        for (refs.items[0..nrefs]) |ref| {
             const atom_ptr = self.atom(ref).?;
-            const file_ptr = self.file(ref.file).?;
+            const file_ptr = atom_ptr.file(self).?;
             try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
         }
 
-        if (atoms.len > max_notes) {
-            const remaining = atoms.len - max_notes;
+        if (refs.items.len > max_notes) {
+            const remaining = refs.items.len - max_notes;
             try err.addNote("referenced {d} more times", .{remaining});
         }
     }
 }
 
 fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
+    if (dupes.keys().len == 0) return; // Nothing to do
+
     const max_notes = 3;
-    var has_dupes = false;
-    var it = dupes.iterator();
-    while (it.next()) |entry| {
-        const sym = self.symbol(entry.key_ptr.*);
-        const notes = entry.value_ptr.*;
+
+    for (dupes.keys(), dupes.values()) |key, notes| {
+        const sym = self.resolver.keys.items[key - 1];
         const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
 
         var err = try self.base.addErrorWithNotes(nnotes + 1);
@@ -5489,11 +5461,9 @@ fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemor
             const remaining = notes.items.len - max_notes;
             try err.addNote("defined {d} more times", .{remaining});
         }
-
-        has_dupes = true;
     }
 
-    if (has_dupes) return error.HasDuplicates;
+    return error.HasDuplicates;
 }
 
 fn reportMissingLibraryError(
@@ -5918,7 +5888,7 @@ pub const SymbolResolver = struct {
         elf_file: *Elf,
     ) !Result {
         const adapter = Adapter{ .keys = resolver.keys.items, .elf_file = elf_file };
-        const key = Key{ .index = ref.index, .file = ref.file };
+        const key = Key{ .index = ref.index, .file_index = ref.file };
         const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
         if (!gop.found_existing) {
             try resolver.keys.append(allocator, key);
@@ -5944,16 +5914,15 @@ pub const SymbolResolver = struct {
 
     const Key = struct {
         index: Symbol.Index,
-        file: File.Index,
+        file_index: File.Index,
 
         fn name(key: Key, elf_file: *Elf) [:0]const u8 {
-            const ref = Ref{ .index = key.index, .file = key.file };
-            return ref.symbol(elf_file).?.name(elf_file);
+            const ref = Ref{ .index = key.index, .file = key.file_index };
+            return elf_file.symbol(ref).?.name(elf_file);
         }
 
-        pub fn file(key: Key, elf_file: *Elf) ?File {
-            const ref = Ref{ .index = key.index, .file = key.file };
-            return ref.file(elf_file);
+        fn file(key: Key, elf_file: *Elf) ?File {
+            return elf_file.file(key.file_index);
         }
 
         fn eql(key: Key, other: Key, elf_file: *Elf) bool {
src/codegen.zig
@@ -906,20 +906,20 @@ fn genDeclRef(
     const is_extern = decl.isExtern(zcu);
 
     if (lf.cast(link.File.Elf)) |elf_file| {
+        const zo = elf_file.zigObjectPtr().?;
         if (is_extern) {
             const name = decl.name.toSlice(ip);
             // TODO audit this
             const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null;
             const sym_index = try elf_file.getGlobalSymbol(name, lib_name);
-            elf_file.symbol(elf_file.zigObjectPtr().?.symbol(sym_index)).flags.needs_got = true;
+            zo.symbol(sym_index).flags.needs_got = true;
             return GenResult.mcv(.{ .load_symbol = sym_index });
         }
-        const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index);
-        const sym = elf_file.symbol(sym_index);
+        const sym_index = try zo.getOrCreateMetadataForDecl(elf_file, decl_index);
         if (is_threadlocal) {
-            return GenResult.mcv(.{ .load_tlv = sym.esym_index });
+            return GenResult.mcv(.{ .load_tlv = sym_index });
         }
-        return GenResult.mcv(.{ .load_symbol = sym.esym_index });
+        return GenResult.mcv(.{ .load_symbol = sym_index });
     } else if (lf.cast(link.File.MachO)) |macho_file| {
         const zo = macho_file.getZigObject().?;
         if (is_extern) {
@@ -971,9 +971,7 @@ fn genUnnamedConst(
     };
     switch (lf.tag) {
         .elf => {
-            const elf_file = lf.cast(link.File.Elf).?;
-            const local = elf_file.symbol(local_sym_index);
-            return GenResult.mcv(.{ .load_symbol = local.esym_index });
+            return GenResult.mcv(.{ .load_symbol = local_sym_index });
         },
         .macho => {
             const macho_file = lf.cast(link.File.MachO).?;