Commit 9fe69cc0b5

Jakub Konka <kubkon@jakubkonka.com>
2024-07-31 15:45:36
elf: move symbol ownership to SharedObject
1 parent d0367b0
Changed files (2)
src/link/Elf/Object.zig
@@ -562,7 +562,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
     }
 }
 
-pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
+pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void {
     const gpa = elf_file.base.comp.gpa;
 
     const first_global = self.first_global orelse return;
@@ -585,12 +585,12 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
         resolv.* = gop.index;
 
         if (esym.st_shndx == elf.SHN_UNDEF) continue;
-        if (elf_file.symbol(gop.ref) == null) {
+        if (elf_file.symbol(gop.ref.*) == null) {
             gop.ref.* = .{ .index = @intCast(i), .file = self.index };
             continue;
         }
 
-        if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref).?.symbolRank(elf_file)) {
+        if (self.asFile().symbolRank(esym, !self.alive) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
             gop.ref.* = .{ .index = @intCast(i), .file = self.index };
         }
     }
@@ -625,34 +625,40 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void {
 
 pub fn claimUnresolvedObject(self: *Object, elf_file: *Elf) void {
     const first_global = self.first_global orelse return;
-    for (self.globals(), 0..) |index, i| {
+    for (self.globals(), 0..) |*sym, i| {
         const esym_index = @as(u32, @intCast(first_global + i));
         const esym = self.symtab.items[esym_index];
         if (esym.st_shndx != elf.SHN_UNDEF) continue;
+        if (elf_file.symbol(self.resolveSymbol(esym_index, elf_file)) != null) continue;
 
-        const global = elf_file.symbol(index);
-        if (global.file(elf_file)) |file| {
-            if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue;
-        }
+        // TODO: audit this
+        // const global = elf_file.symbol(index);
+        // if (global.file(elf_file)) |file| {
+        //     if (global.elfSym(elf_file).st_shndx != elf.SHN_UNDEF or file.index() <= self.index) continue;
+        // }
 
-        global.value = 0;
-        global.ref = .{ .index = 0, .file = 0 };
-        global.esym_index = esym_index;
-        global.file_index = self.index;
+        sym.value = 0;
+        sym.ref = .{ .index = 0, .file = 0 };
+        sym.esym_index = esym_index;
+        sym.file_index = self.index;
+
+        const idx = self.symbols_resolver.items[i];
+        elf_file.resolver.items[idx - 1] = .{ .index = esym_index, .file = self.index };
     }
 }
 
 pub fn markLive(self: *Object, elf_file: *Elf) void {
     const first_global = self.first_global orelse return;
-    for (self.globals(), 0..) |index, i| {
-        const sym_idx = first_global + i;
-        const sym = self.symtab.items[sym_idx];
-        if (sym.st_bind() == elf.STB_WEAK) continue;
-
-        const global = elf_file.symbol(index);
-        const file = global.file(elf_file) orelse continue;
-        const should_keep = sym.st_shndx == elf.SHN_UNDEF or
-            (sym.st_shndx == elf.SHN_COMMON and global.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
+    for (0..self.globals().len) |i| {
+        const esym_idx = first_global + i;
+        const esym = self.symtab.items[esym_idx];
+        if (esym.st_bind() == elf.STB_WEAK) continue;
+
+        const ref = self.resolveSymbol(@intCast(esym_idx), elf_file);
+        const sym = elf_file.symbol(ref) orelse continue;
+        const file = sym.file(elf_file).?;
+        const should_keep = esym.st_shndx == elf.SHN_UNDEF or
+            (esym.st_shndx == elf.SHN_COMMON and sym.elfSym(elf_file).st_shndx != elf.SHN_COMMON);
         if (should_keep and !file.isAlive()) {
             file.setAlive();
             file.markLive(elf_file);
@@ -672,24 +678,25 @@ pub fn markEhFrameAtomsDead(self: *Object, elf_file: *Elf) void {
 
 pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
     const first_global = self.first_global orelse return;
-    for (self.globals(), 0..) |index, i| {
-        const sym_idx = first_global + i;
-        const sym = self.symtab.items[sym_idx];
-        const global = elf_file.symbol(index);
-        const global_file = global.file(elf_file) orelse continue;
-
-        if (self.index == global_file.index() or
-            sym.st_shndx == elf.SHN_UNDEF or
-            sym.st_bind() == elf.STB_WEAK or
-            sym.st_shndx == elf.SHN_COMMON) continue;
-
-        if (sym.st_shndx != elf.SHN_ABS) {
-            const atom_index = self.atoms_indexes.items[sym.st_shndx];
+    for (0..self.globals().len) |i| {
+        const esym_idx = first_global + i;
+        const esym = self.symtab.items[esym_idx];
+        const ref = self.resolveSymbol(@intCast(esym_idx), elf_file);
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        const ref_file = ref_sym.file(elf_file).?;
+
+        if (self.index == ref_file.index() or
+            esym.st_shndx == elf.SHN_UNDEF or
+            esym.st_bind() == elf.STB_WEAK or
+            esym.st_shndx == elf.SHN_COMMON) continue;
+
+        if (esym.st_shndx != elf.SHN_ABS) {
+            const atom_index = self.atoms_indexes.items[esym.st_shndx];
             const atom_ptr = self.atom(atom_index) orelse continue;
             if (!atom_ptr.alive) continue;
         }
 
-        const gop = try dupes.getOrPut(index);
+        const gop = try dupes.getOrPut(self.symbols_resolver.items[i]);
         if (!gop.found_existing) {
             gop.value_ptr.* = .{};
         }
@@ -819,8 +826,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
     }
 
     for (self.symtab.items, 0..) |*esym, idx| {
-        const sym_index = self.symbols.items[idx];
-        const sym = elf_file.symbol(sym_index);
+        const sym = &self.symbols.items[idx];
+        // TODO: do we need ref here?
 
         if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue;
 
@@ -860,23 +867,20 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
                 return error.MalformedObject;
             };
 
-            const out_sym_idx: u64 = @intCast(self.symbols.items.len);
-            try self.symbols.ensureUnusedCapacity(gpa, 1);
+            const sym_index = try self.addSymbol(gpa);
+            const sym = &self.symbols.items[sym_index];
             const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.name(elf_file), res.msub_index });
             defer gpa.free(name);
-            const sym_index = try elf_file.addSymbol();
-            const sym = elf_file.symbol(sym_index);
             sym.* = .{
                 .value = @bitCast(@as(i64, @intCast(res.offset)) - rel.r_addend),
                 .name_offset = try self.addString(gpa, name),
                 .esym_index = rel.r_sym(),
                 .file_index = self.index,
-                .extra_index = try elf_file.addSymbolExtra(.{}),
+                .extra_index = try self.addSymbolExtra(gpa, .{}),
             };
             sym.ref = .{ .index = res.msub_index, .file = imsec.merge_section_index };
             sym.flags.merge_subsection = true;
-            self.symbols.addOneAssumeCapacity().* = sym_index;
-            rel.r_info = (out_sym_idx << 32) | rel.r_type();
+            rel.r_info = (@as(u64, @intCast(sym_index)) << 32) | rel.r_type();
         }
     }
 }
@@ -885,27 +889,16 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void {
 /// play nicely with the rest of the system.
 pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
     const first_global = self.first_global orelse return;
-    for (self.globals(), 0..) |index, i| {
-        const sym_idx = @as(u32, @intCast(first_global + i));
-        const this_sym = self.symtab.items[sym_idx];
-        if (this_sym.st_shndx != elf.SHN_COMMON) continue;
-
-        const global = elf_file.symbol(index);
-        const global_file = global.file(elf_file).?;
-        if (global_file.index() != self.index) {
-            // if (elf_file.options.warn_common) {
-            //     elf_file.base.warn("{}: multiple common symbols: {s}", .{
-            //         self.fmtPath(),
-            //         global.getName(elf_file),
-            //     });
-            // }
-            continue;
-        }
+    for (self.globals(), self.symbols_resolver.items, 0..) |*sym, resolv, i| {
+        const esym_idx = @as(u32, @intCast(first_global + i));
+        const esym = self.symtab.items[esym_idx];
+        if (esym.st_shndx != elf.SHN_COMMON) continue;
+        if (elf_file.resolver.get(resolv).?.file != self.index) continue;
 
         const comp = elf_file.base.comp;
         const gpa = comp.gpa;
 
-        const is_tls = global.type(elf_file) == elf.STT_TLS;
+        const is_tls = sym.type(elf_file) == elf.STT_TLS;
         const name = if (is_tls) ".tls_common" else ".common";
         const name_offset = @as(u32, @intCast(self.strtab.items.len));
         try self.strtab.writer(gpa).print("{s}\x00", .{name});
@@ -914,7 +907,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
         if (is_tls) sh_flags |= elf.SHF_TLS;
         const shndx = @as(u32, @intCast(self.shdrs.items.len));
         const shdr = try self.shdrs.addOne(gpa);
-        const sh_size = math.cast(usize, this_sym.st_size) orelse return error.Overflow;
+        const sh_size = math.cast(usize, esym.st_size) orelse return error.Overflow;
         shdr.* = .{
             .sh_name = name_offset,
             .sh_type = elf.SHT_NOBITS,
@@ -924,21 +917,21 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void {
             .sh_size = sh_size,
             .sh_link = 0,
             .sh_info = 0,
-            .sh_addralign = this_sym.st_value,
+            .sh_addralign = esym.st_value,
             .sh_entsize = 0,
         };
 
         const atom_index = try self.addAtom(gpa, .{
             .name = name_offset,
             .shndx = shndx,
-            .size = this_sym.st_size,
-            .alignment = Alignment.fromNonzeroByteUnits(this_sym.st_value),
+            .size = esym.st_size,
+            .alignment = Alignment.fromNonzeroByteUnits(esym.st_value),
         });
         try self.atoms_indexes.append(gpa, atom_index);
 
-        global.value = 0;
-        global.ref = .{ .index = atom_index, .file = self.index };
-        global.flags.weak = false;
+        sym.value = 0;
+        sym.ref = .{ .index = atom_index, .file = self.index };
+        sym.flags.weak = false;
     }
 }
 
@@ -1080,7 +1073,7 @@ pub fn writeAr(self: Object, elf_file: *Elf, writer: anytype) !void {
     try writer.writeAll(data);
 }
 
-pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
+pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void {
     const isAlive = struct {
         fn isAlive(sym: *const Symbol, ctx: *Elf) bool {
             if (sym.mergeSubsection(ctx)) |msub| return msub.alive;
@@ -1089,8 +1082,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
         }
     }.isAlive;
 
-    for (self.locals()) |local_index| {
-        const local = elf_file.symbol(local_index);
+    for (self.locals()) |*local| {
         if (!isAlive(local, elf_file)) continue;
         const esym = local.elfSym(elf_file);
         switch (esym.st_type()) {
@@ -1099,22 +1091,22 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
             else => {},
         }
         local.flags.output_symtab = true;
-        try local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
+        local.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
         self.output_symtab_ctx.nlocals += 1;
         self.output_symtab_ctx.strsize += @as(u32, @intCast(local.name(elf_file).len)) + 1;
     }
 
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+    for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
+        const ref = elf_file.resolver.items[resolv];
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         if (!isAlive(global, elf_file)) continue;
         global.flags.output_symtab = true;
         if (global.isLocal(elf_file)) {
-            try global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
+            global.addExtra(.{ .symtab = self.output_symtab_ctx.nlocals }, elf_file);
             self.output_symtab_ctx.nlocals += 1;
         } else {
-            try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
+            global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
             self.output_symtab_ctx.nglobals += 1;
         }
         self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
@@ -1122,8 +1114,7 @@ pub fn updateSymtabSize(self: *Object, elf_file: *Elf) !void {
 }
 
 pub fn writeSymtab(self: Object, elf_file: *Elf) void {
-    for (self.locals()) |local_index| {
-        const local = elf_file.symbol(local_index);
+    for (self.locals()) |local| {
         const idx = local.outputSymtabIndex(elf_file) orelse continue;
         const out_sym = &elf_file.symtab.items[idx];
         out_sym.st_name = @intCast(elf_file.strtab.items.len);
@@ -1132,10 +1123,10 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void {
         local.setOutputSym(elf_file, out_sym);
     }
 
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+    for (self.globals(), self.symbols_resolver.items) |global, resolv| {
+        const ref = elf_file.resolver.items[resolv];
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         const idx = global.outputSymtabIndex(elf_file) orelse continue;
         const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
         elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
src/link/Elf/SharedObject.zig
@@ -10,7 +10,10 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
 versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{},
 verstrings: std.ArrayListUnmanaged(u32) = .{},
 
-symbols: std.ArrayListUnmanaged(Symbol.Index) = .{},
+symbols: std.ArrayListUnmanaged(Symbol) = .{},
+symbols_extra: std.ArrayListUnmanaged(u32) = .{},
+symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{},
+
 aliases: ?std.ArrayListUnmanaged(u32) = null,
 dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{},
 
@@ -38,6 +41,8 @@ pub fn deinit(self: *SharedObject, allocator: Allocator) void {
     self.versyms.deinit(allocator);
     self.verstrings.deinit(allocator);
     self.symbols.deinit(allocator);
+    self.symbols_extra.deinit(allocator);
+    self.symbols_resolver.deinit(allocator);
     if (self.aliases) |*aliases| aliases.deinit(allocator);
     self.dynamic_table.deinit(allocator);
 }
@@ -129,7 +134,7 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, handle: std.fs.File) !void {
         .versym_sect_index = versym_sect_index,
     });
 
-    try self.initSymtab(elf_file, .{
+    try self.initSymbols(elf_file, .{
         .symtab = symtab,
         .strtab = strtab,
     });
@@ -188,16 +193,20 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf, handle: std.fs.File, opts:
     }
 }
 
-fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct {
+fn initSymbols(self: *SharedObject, elf_file: *Elf, opts: struct {
     symtab: []align(1) const elf.Elf64_Sym,
     strtab: []const u8,
 }) !void {
-    const comp = elf_file.base.comp;
-    const gpa = comp.gpa;
+    const gpa = elf_file.base.comp.gpa;
+    const nsyms = opts.symtab.len;
 
     try self.strtab.appendSlice(gpa, opts.strtab);
-    try self.symtab.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
-    try self.symbols.ensureTotalCapacityPrecise(gpa, opts.symtab.len);
+    try self.symtab.ensureTotalCapacityPrecise(gpa, nsyms);
+    try self.symbols.ensureTotalCapacityPrecise(gpa, nsyms);
+    try self.symbols_extra.ensureTotalCapacityPrecise(gpa, nsyms * @sizeOf(Symbol.Extra));
+    try self.symbols_resolver.ensureTotalCapacityPrecise(gpa, nsyms);
+    self.symbols_resolver.resize(gpa, nsyms) catch unreachable;
+    @memset(self.symbols_resolver.items, 0);
 
     for (opts.symtab, 0..) |sym, i| {
         const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0;
@@ -214,41 +223,56 @@ fn initSymtab(self: *SharedObject, elf_file: *Elf, opts: struct {
             try self.strtab.writer(gpa).print("{s}\x00", .{mangled});
             break :blk name_off;
         } else sym.st_name;
-        const out_sym = self.symtab.addOneAssumeCapacity();
-        out_sym.* = sym;
-        out_sym.st_name = name_off;
-        const gop = try elf_file.getOrPutGlobal(self.getString(name_off));
-        self.symbols.addOneAssumeCapacity().* = gop.index;
+        const out_esym_index: u32 = @intCast(self.symtab.items.len);
+        const out_esym = self.symtab.addOneAssumeCapacity();
+        out_esym.* = sym;
+        out_esym.st_name = name_off;
+        const out_sym_index = self.addSymbolAssumeCapacity();
+        const out_sym = &self.symbols.items[out_sym_index];
+        out_sym.* = .{
+            .value = @intCast(out_esym.st_value),
+            .ref = .{ .index = 0, .file = 0 },
+            .esym_index = out_esym_index,
+            .version_index = self.versyms.items[out_esym_index],
+            .extra_index = self.addSymbolExtraAssumeCapacity(.{}),
+        };
     }
 }
 
-pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const esym_index = @as(u32, @intCast(i));
-        const this_sym = self.symtab.items[esym_index];
+pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void {
+    const gpa = elf_file.base.comp.gpa;
+
+    for (self.symtab.items, self.symbols_resolver.items, 0..) |esym, *resolv, i| {
+        const gop = try elf_file.resolver.getOrPut(gpa, .{
+            .index = @intCast(i),
+            .file = self.index,
+        }, elf_file);
+        if (!gop.found_existing) {
+            gop.ref.* = .{ .index = 0, .file = 0 };
+        }
+        resolv.* = gop.index;
 
-        if (this_sym.st_shndx == elf.SHN_UNDEF) continue;
+        if (esym.st_shndx == elf.SHN_UNDEF) continue;
+        if (elf_file.symbol(gop.ref.*) == null) {
+            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
+            continue;
+        }
 
-        const global = elf_file.symbol(index);
-        if (self.asFile().symbolRank(this_sym, false) < global.symbolRank(elf_file)) {
-            global.value = @intCast(this_sym.st_value);
-            global.ref = .{ .index = 0, .file = 0 };
-            global.esym_index = esym_index;
-            global.version_index = self.versyms.items[esym_index];
-            global.file_index = self.index;
+        if (self.asFile().symbolRank(esym, false) < elf_file.symbol(gop.ref.*).?.symbolRank(elf_file)) {
+            gop.ref.* = .{ .index = @intCast(i), .file = self.index };
         }
     }
 }
 
 pub fn markLive(self: *SharedObject, elf_file: *Elf) void {
-    for (self.globals(), 0..) |index, i| {
-        const sym = self.symtab.items[i];
-        if (sym.st_shndx != elf.SHN_UNDEF) continue;
+    for (self.symtab.items, 0..) |esym, i| {
+        if (esym.st_shndx != elf.SHN_UNDEF) continue;
 
-        const global = elf_file.symbol(index);
-        const file = global.file(elf_file) orelse continue;
+        const ref = self.resolveSymbol(@intCast(i), elf_file);
+        const sym = elf_file.symbol(ref) orelse continue;
+        const file = sym.file(elf_file).?;
         const should_drop = switch (file) {
-            .shared_object => |sh| !sh.needed and sym.st_bind() == elf.STB_WEAK,
+            .shared_object => |sh| !sh.needed and esym.st_bind() == elf.STB_WEAK,
             else => false,
         };
         if (!should_drop and !file.isAlive()) {
@@ -262,24 +286,24 @@ pub fn globals(self: SharedObject) []const Symbol.Index {
     return self.symbols.items;
 }
 
-pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) !void {
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+pub fn updateSymtabSize(self: *SharedObject, elf_file: *Elf) void {
+    for (self.globals(), self.symbols_resolver.items) |*global, resolv| {
+        const ref = elf_file.resolver.get(resolv).?;
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         if (global.isLocal(elf_file)) continue;
         global.flags.output_symtab = true;
-        try global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
+        global.addExtra(.{ .symtab = self.output_symtab_ctx.nglobals }, elf_file);
         self.output_symtab_ctx.nglobals += 1;
         self.output_symtab_ctx.strsize += @as(u32, @intCast(global.name(elf_file).len)) + 1;
     }
 }
 
 pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void {
-    for (self.globals()) |global_index| {
-        const global = elf_file.symbol(global_index);
-        const file_ptr = global.file(elf_file) orelse continue;
-        if (file_ptr.index() != self.index) continue;
+    for (self.globals(), self.symbols_resolver.items) |global, resolv| {
+        const ref = elf_file.resolver.get(resolv).?;
+        const ref_sym = elf_file.symbol(ref) orelse continue;
+        if (ref_sym.file(elf_file).?.index() != self.index) continue;
         const idx = global.outputSymtabIndex(elf_file) orelse continue;
         const st_name = @as(u32, @intCast(elf_file.strtab.items.len));
         elf_file.strtab.appendSliceAssumeCapacity(global.name(elf_file));
@@ -332,10 +356,10 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void {
     defer aliases.deinit();
     try aliases.ensureTotalCapacityPrecise(self.globals().len);
 
-    for (self.globals()) |index| {
-        const global = elf_file.symbol(index);
-        const global_file = global.file(elf_file) orelse continue;
-        if (global_file.index() != self.index) continue;
+    for (self.symbols_resolvers.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);
     }
 
@@ -347,16 +371,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 = elf_file.symbol(index).elfSym(elf_file);
+    const symbol = self.symbol(index).elfSym(elf_file);
     const aliases = self.aliases.?;
 
     const start = for (aliases.items, 0..) |alias, i| {
-        const alias_sym = elf_file.symbol(alias).elfSym(elf_file);
+        const alias_sym = self.symbol(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 = elf_file.symbol(alias).elfSym(elf_file);
+        const alias_sym = self.symbol(alias).elfSym(elf_file);
         if (symbol.st_value < alias_sym.st_value) break i + start;
     } else aliases.items.len;
 
@@ -368,6 +392,60 @@ pub fn getString(self: SharedObject, off: u32) [:0]const u8 {
     return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0);
 }
 
+pub fn resolveSymbol(self: SharedObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref {
+    const resolv = self.symbols_resolver.items[index];
+    return elf_file.resolver.get(resolv).?;
+}
+
+pub fn addSymbol(self: *SharedObject, allocator: Allocator) !Symbol.Index {
+    try self.symbols.ensureUnusedCapacity(allocator, 1);
+    const index: Symbol.Index = @intCast(self.symbols.items.len);
+    self.symbols.appendAssumeCapacity(.{ .file_index = self.index });
+    return index;
+}
+
+pub fn addSymbolExtra(self: *SharedObject, allocator: Allocator, extra: Symbol.Extra) !u32 {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    try self.symbols_extra.ensureUnusedCapacity(allocator, fields.len);
+    return self.addSymbolExtraAssumeCapacity(extra);
+}
+
+pub fn addSymbolExtraAssumeCapacity(self: *SharedObject, extra: Symbol.Extra) u32 {
+    const index = @as(u32, @intCast(self.symbols_extra.items.len));
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    inline for (fields) |field| {
+        self.symbols_extra.appendAssumeCapacity(switch (field.type) {
+            u32 => @field(extra, field.name),
+            else => @compileError("bad field type"),
+        });
+    }
+    return index;
+}
+
+pub fn symbolExtra(self: *SharedObject, index: u32) Symbol.Extra {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    var i: usize = index;
+    var result: Symbol.Extra = undefined;
+    inline for (fields) |field| {
+        @field(result, field.name) = switch (field.type) {
+            u32 => self.symbols_extra.items[i],
+            else => @compileError("bad field type"),
+        };
+        i += 1;
+    }
+    return result;
+}
+
+pub fn setSymbolExtra(self: *SharedObject, index: u32, extra: Symbol.Extra) void {
+    const fields = @typeInfo(Symbol.Extra).Struct.fields;
+    inline for (fields, 0..) |field, i| {
+        self.symbols_extra.items[index + i] = switch (field.type) {
+            u32 => @field(extra, field.name),
+            else => @compileError("bad field type"),
+        };
+    }
+}
+
 pub fn format(
     self: SharedObject,
     comptime unused_fmt_string: []const u8,