diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 71c566b5dd..fa912c0119 100644 --- a/src/link/Elf/Object.zig +++ b/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; + 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 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); + 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; + 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 == 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 (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 (sym.st_shndx != elf.SHN_ABS) { - const atom_index = self.atoms_indexes.items[sym.st_shndx]; + 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)); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 461a56ae39..00a64c3950 100644 --- a/src/link/Elf/SharedObject.zig +++ b/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; - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + 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; - 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 (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.symbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + 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,