From 2c928eb292f7668691dd09cf3475f1e2f50e9162 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 Sep 2024 06:05:06 +0200 Subject: [PATCH] elf: move ownership of symbols into objects --- src/Elf.zig | 763 ++++++++++++------------------------- src/Elf/Atom.zig | 154 ++++---- src/Elf/InternalObject.zig | 442 ++++++++++++++++++--- src/Elf/Object.zig | 395 ++++++++++++------- src/Elf/SharedObject.zig | 330 +++++++++++----- src/Elf/Symbol.zig | 99 ++--- src/Elf/Thunk.zig | 29 +- src/Elf/eh_frame.zig | 21 +- src/Elf/file.zig | 79 +++- src/Elf/gc.zig | 25 +- src/Elf/merge_section.zig | 2 +- src/Elf/relocatable.zig | 28 +- src/Elf/synthetic.zig | 236 ++++++------ 13 files changed, 1493 insertions(+), 1110 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index aa05df11..1ea7e0ab 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -6,6 +6,7 @@ objects: std.ArrayListUnmanaged(File.Index) = .{}, shared_objects: std.ArrayListUnmanaged(File.Index) = .{}, files: std.MultiArrayList(File.Entry) = .{}, file_handles: std.ArrayListUnmanaged(File.Handle) = .{}, +internal_object_index: ?File.Index = null, sections: std.MultiArrayList(Section) = .{}, phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, @@ -34,41 +35,17 @@ gnu_hash_sect_index: ?u32 = null, versym_sect_index: ?u32 = null, verneed_sect_index: ?u32 = null, -internal_object_index: ?u32 = null, -dynamic_index: ?u32 = null, -ehdr_start_index: ?u32 = null, -init_array_start_index: ?u32 = null, -init_array_end_index: ?u32 = null, -fini_array_start_index: ?u32 = null, -fini_array_end_index: ?u32 = null, -preinit_array_start_index: ?u32 = null, -preinit_array_end_index: ?u32 = null, -got_index: ?u32 = null, -plt_index: ?u32 = null, -dso_handle_index: ?u32 = null, -gnu_eh_frame_hdr_index: ?u32 = null, -rela_iplt_start_index: ?u32 = null, -rela_iplt_end_index: ?u32 = null, -end_index: ?u32 = null, -global_pointer_index: ?u32 = null, -start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, - -entry_index: ?u32 = null, - -symbols: std.ArrayListUnmanaged(Symbol) = .{}, -symbols_extra: std.ArrayListUnmanaged(u32) = .{}, -globals: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, +resolver: SymbolResolver = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. -undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Ref)) = .{}, +undefs: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(Ref)) = .{}, +dupes: std.AutoArrayHashMapUnmanaged(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)) = .{}, -string_intern: StringTable = .{}, - -shstrtab: StringTable = .{}, +shstrtab: std.ArrayListUnmanaged(u8) = .empty, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -strtab: std.ArrayListUnmanaged(u8) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .empty, dynsym: DynsymSection = .{}, -dynstrtab: StringTable = .{}, +dynstrtab: std.ArrayListUnmanaged(u8) = .empty, versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verneed: VerneedSection = .{}, @@ -133,7 +110,6 @@ fn createEmpty(gpa: Allocator, options: Options, thread_pool: *ThreadPool) !*Elf pub fn deinit(self: *Elf) void { const gpa = self.base.allocator; - self.string_intern.deinit(gpa); self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); @@ -146,10 +122,7 @@ pub fn deinit(self: *Elf) void { } self.merge_sections.deinit(gpa); self.merge_subsections.deinit(gpa); - self.symbols.deinit(gpa); - self.symbols_extra.deinit(gpa); - self.globals.deinit(gpa); - self.start_stop_indexes.deinit(gpa); + self.resolver.deinit(gpa); self.got.deinit(gpa); self.plt.deinit(gpa); self.plt_got.deinit(gpa); @@ -183,13 +156,14 @@ pub fn deinit(self: *Elf) void { self.rela_dyn.deinit(gpa); self.rela_plt.deinit(gpa); self.comdat_group_sections.deinit(gpa); - { - var it = self.undefs.valueIterator(); - while (it.next()) |notes| { - notes.deinit(gpa); - } - self.undefs.deinit(gpa); + for (self.undefs.values()) |*value| { + value.deinit(gpa); + } + self.undefs.deinit(gpa); + for (self.dupes.values()) |*value| { + value.deinit(gpa); } + self.dupes.deinit(gpa); } fn resolveFile( @@ -265,16 +239,13 @@ pub fn flush(self: *Elf) !void { const gpa = self.base.allocator; // Append empty string to string tables. - try self.string_intern.buffer.append(gpa, 0); - try self.shstrtab.buffer.append(gpa, 0); + try self.shstrtab.append(gpa, 0); try self.strtab.append(gpa, 0); - try self.dynstrtab.buffer.append(gpa, 0); + try self.dynstrtab.append(gpa, 0); // Append null section. _ = try self.addSection(.{ .name = "" }); - // Append null symbols. + // Append null symbol. try self.symtab.append(gpa, null_sym); - try self.symbols.append(gpa, .{}); - try self.symbols_extra.append(gpa, 0); // Append null file. try self.files.append(gpa, .null); @@ -349,6 +320,9 @@ pub fn flush(self: *Elf) !void { const index = @as(File.Index, @intCast(try self.files.addOne(gpa))); self.files.set(index, .{ .internal = .{ .index = index } }); self.internal_object_index = index; + const object = self.getInternalObject().?; + try object.init(gpa); + try object.initSymbols(self); } try self.resolveSymbols(); @@ -360,16 +334,6 @@ pub fn flush(self: *Elf) !void { try self.convertCommonSymbols(); try self.markImportsAndExports(); - // Set the entrypoint if found - self.entry_index = blk: { - if (self.options.shared) break :blk null; - const entry_name = self.options.entry orelse "_start"; - break :blk self.getGlobalByName(entry_name); - }; - if (!self.options.shared and self.entry_index == null) { - self.base.fatal("no entrypoint found: '{s}'", .{self.options.entry orelse "_start"}); - } - if (self.options.gc_sections) { try gc.gcAtoms(self); @@ -385,7 +349,6 @@ pub fn flush(self: *Elf) !void { try self.addCommentString(); try self.finalizeMergeSections(); try self.initOutputSections(); - try self.resolveSyntheticSymbols(); if (self.options.z_execstack_if_needed) { for (self.objects.items) |index| { @@ -411,7 +374,6 @@ pub fn flush(self: *Elf) !void { try self.calcSectionSizes(); try self.allocateSections(); - self.allocateSyntheticSymbols(); self.shoff = blk: { const shdr = self.sections.items(.shdr)[self.sections.len - 1]; @@ -470,7 +432,7 @@ fn sortInitFini(self: *Elf) !void { elf.SHT_FINI_ARRAY, => is_init_fini = true, else => { - const name = self.shstrtab.getAssumeExists(shdr.sh_name); + const name = self.getShString(shdr.sh_name); is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null; }, } @@ -691,7 +653,7 @@ fn initSyntheticSections(self: *Elf) !void { }); const needs_versions = for (self.dynsym.entries.items) |dynsym| { - const symbol = self.getSymbol(dynsym.index); + const symbol = self.getSymbol(dynsym.ref).?; if (symbol.flags.import and symbol.ver_idx & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true; } else false; if (needs_versions) { @@ -748,38 +710,6 @@ pub fn addAtomsToSections(self: *Elf) !void { .file = index, }); } - - for (object.getLocals()) |local_index| { - const local = self.getSymbol(local_index); - if (local.getMergeSubsection(self)) |msub| { - if (!msub.alive) continue; - local.shndx = msub.getMergeSection(self).out_shndx; - continue; - } - const atom = local.getAtom(self) orelse continue; - if (!atom.flags.alive) continue; - local.shndx = atom.out_shndx; - } - - for (object.getGlobals()) |global_index| { - const global = self.getSymbol(global_index); - if (global.getFile(self).?.getIndex() != index) continue; - if (global.getMergeSubsection(self)) |msub| { - if (!msub.alive) continue; - global.shndx = msub.getMergeSection(self).out_shndx; - continue; - } - const atom = global.getAtom(self) orelse continue; - if (!atom.flags.alive) continue; - global.shndx = atom.out_shndx; - } - - for (object.symbols.items[object.symtab.items.len..]) |local_index| { - const local = self.getSymbol(local_index); - const msub = local.getMergeSubsection(self).?; - if (!msub.alive) continue; - local.shndx = msub.getMergeSection(self).out_shndx; - } } } @@ -935,7 +865,7 @@ fn calcSectionSizes(self: *Elf) !void { if (self.dynstrtab_sect_index) |index| { const shdr = &self.sections.items(.shdr)[index]; - shdr.sh_size = self.dynstrtab.buffer.items.len; + shdr.sh_size = self.dynstrtab.items.len; } if (self.versym_sect_index) |index| { @@ -954,7 +884,7 @@ fn calcSectionSizes(self: *Elf) !void { if (self.shstrtab_sect_index) |index| { const shdr = &self.sections.items(.shdr)[index]; - shdr.sh_size = self.shstrtab.buffer.items.len; + shdr.sh_size = self.shstrtab.items.len; } } @@ -1356,7 +1286,7 @@ fn allocateSectionsInMemory(self: *Elf, base_offset: u64) !void { var i: usize = 0; while (i < shdrs.len) : (i += 1) { const shdr = &shdrs[i]; - const name = self.shstrtab.getAssumeExists(shdr.sh_name); + const name = self.getShString(shdr.sh_name); if (!shdrIsAlloc(shdr.*)) continue; if (self.options.section_start.get(name)) |sh_addr| { addr = sh_addr; @@ -1468,7 +1398,7 @@ fn allocateSections(self: *Elf) !void { fn getSectionRank(self: *Elf, shndx: u32) u8 { const shdr = self.sections.items(.shdr)[shndx]; - const name = self.shstrtab.getAssumeExists(shdr.sh_name); + const name = self.getShString(shdr.sh_name); const flags = shdr.sh_flags; const rank: u8 = switch (shdr.sh_type) { elf.SHT_NULL => 0, @@ -1646,151 +1576,6 @@ pub fn sortSections(self: *Elf) !void { } } -fn allocateSyntheticSymbols(self: *Elf) void { - // _DYNAMIC - if (self.dynamic_sect_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - const symbol = self.getSymbol(self.dynamic_index.?); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = shndx; - } - - // __ehdr_start - { - const symbol = self.getSymbol(self.ehdr_start_index.?); - symbol.value = @intCast(self.options.image_base); - symbol.shndx = 1; - } - - // __init_array_start, __init_array_end - if (self.getSectionByName(".init_array")) |shndx| { - const start_sym = self.getSymbol(self.init_array_start_index.?); - const end_sym = self.getSymbol(self.init_array_end_index.?); - const shdr = self.sections.items(.shdr)[shndx]; - start_sym.shndx = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.shndx = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // __fini_array_start, __fini_array_end - if (self.getSectionByName(".fini_array")) |shndx| { - const start_sym = self.getSymbol(self.fini_array_start_index.?); - const end_sym = self.getSymbol(self.fini_array_end_index.?); - const shdr = self.sections.items(.shdr)[shndx]; - start_sym.shndx = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.shndx = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // __preinit_array_start, __preinit_array_end - if (self.getSectionByName(".preinit_array")) |shndx| { - const start_sym = self.getSymbol(self.preinit_array_start_index.?); - const end_sym = self.getSymbol(self.preinit_array_end_index.?); - const shdr = self.sections.items(.shdr)[shndx]; - start_sym.shndx = shndx; - start_sym.value = @intCast(shdr.sh_addr); - end_sym.shndx = shndx; - end_sym.value = @intCast(shdr.sh_addr + shdr.sh_size); - } - - // _GLOBAL_OFFSET_TABLE_ - if (self.options.cpu_arch.? == .x86_64) { - if (self.got_plt_sect_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - const symbol = self.getSymbol(self.got_index.?); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = shndx; - } - } else { - if (self.got_sect_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - const symbol = self.getSymbol(self.got_index.?); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = shndx; - } - } - - // _PROCEDURE_LINKAGE_TABLE_ - if (self.plt_sect_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - const symbol = self.getSymbol(self.plt_index.?); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = shndx; - } - - // __dso_handle - if (self.dso_handle_index) |index| { - const shdr = self.sections.items(.shdr)[1]; - const symbol = self.getSymbol(index); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = 0; - } - - // __GNU_EH_FRAME_HDR - if (self.eh_frame_hdr_sect_index) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - const symbol = self.getSymbol(self.gnu_eh_frame_hdr_index.?); - symbol.value = @intCast(shdr.sh_addr); - symbol.shndx = shndx; - } - - // __rela_iplt_start, __rela_iplt_end - if (self.rela_dyn_sect_index != null and self.options.static and !self.options.pie) { - const shndx = self.rela_dyn_sect_index.?; - const shdr = self.sections.items(.shdr)[shndx]; - const end_addr = shdr.sh_addr + shdr.sh_size; - const start_addr = end_addr - self.getNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); - const start_sym = self.getSymbol(self.rela_iplt_start_index.?); - const end_sym = self.getSymbol(self.rela_iplt_end_index.?); - start_sym.value = @intCast(start_addr); - start_sym.shndx = shndx; - end_sym.value = @intCast(end_addr); - end_sym.shndx = shndx; - } - - // _end - { - const end_symbol = self.getSymbol(self.end_index.?); - for (self.sections.items(.shdr), 0..) |shdr, shndx| { - if (shdr.sh_flags & elf.SHF_ALLOC != 0) { - end_symbol.value = @intCast(shdr.sh_addr + shdr.sh_size); - end_symbol.shndx = @intCast(shndx); - } - } - } - - // __start_*, __stop_* - { - var index: usize = 0; - while (index < self.start_stop_indexes.items.len) : (index += 2) { - const start = self.getSymbol(self.start_stop_indexes.items[index]); - const name = start.getName(self); - const stop = self.getSymbol(self.start_stop_indexes.items[index + 1]); - const shndx = self.getSectionByName(name["__start_".len..]).?; - const shdr = self.sections.items(.shdr)[shndx]; - start.value = @intCast(shdr.sh_addr); - start.shndx = shndx; - stop.value = @intCast(shdr.sh_addr + shdr.sh_size); - stop.shndx = shndx; - } - } - - // __global_pointer$ - if (self.global_pointer_index) |index| { - const sym = self.getSymbol(index); - if (self.getSectionByName(".sdata")) |shndx| { - const shdr = self.sections.items(.shdr)[shndx]; - sym.value = @intCast(shdr.sh_addr + 0x800); - sym.shndx = shndx; - } else { - sym.value = 0; - sym.shndx = 0; - } - } -} - fn parsePositional(self: *Elf, arena: Allocator, obj: LinkObject, search_dirs: []const []const u8) anyerror!void { const resolved_obj = try self.resolveFile(arena, obj, search_dirs); @@ -2009,15 +1794,15 @@ fn resolveSymbols(self: *Elf) !void { defer tracy.end(); // Resolve symbols on the set of all objects and shared objects (even if some are unneeded). - for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.getFile(index).?.resolveSymbols(self); + for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + for (self.shared_objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + if (self.getInternalObject()) |obj| try obj.asFile().resolveSymbols(self); // Mark live objects. self.markLive(); // Reset state of all globals after marking live objects. - for (self.objects.items) |index| self.getFile(index).?.resetGlobals(self); - for (self.shared_objects.items) |index| self.getFile(index).?.resetGlobals(self); + self.resolver.reset(); // Prune dead objects and shared objects. var i: usize = 0; @@ -2027,7 +1812,6 @@ fn resolveSymbols(self: *Elf) !void { _ = self.objects.orderedRemove(i); } else i += 1; } - i = 0; while (i < self.shared_objects.items.len) { const index = self.shared_objects.items[i]; @@ -2051,8 +1835,9 @@ fn resolveSymbols(self: *Elf) !void { } // Re-resolve the symbols. - for (self.objects.items) |index| self.getFile(index).?.resolveSymbols(self); - for (self.shared_objects.items) |index| self.getFile(index).?.resolveSymbols(self); + for (self.objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + for (self.shared_objects.items) |index| try self.getFile(index).?.resolveSymbols(self); + if (self.getInternalObject()) |obj| try obj.asFile().resolveSymbols(self); } /// Traverses all objects and shared objects marking any object referenced by @@ -2116,94 +1901,19 @@ fn convertCommonSymbols(self: *Elf) !void { fn markImportsAndExports(self: *Elf) !void { if (!self.options.shared) for (self.shared_objects.items) |index| { - for (self.getFile(index).?.shared.getGlobals()) |global_index| { - const global = self.getSymbol(global_index); - const file = global.getFile(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.getSourceSymbol(self).st_other)); - if (file != .shared and vis != .HIDDEN) global.flags.@"export" = true; - } + self.getFile(index).?.shared.markImportsExports(self); }; for (self.objects.items) |index| { - for (self.getFile(index).?.object.getGlobals()) |global_index| { - const global = self.getSymbol(global_index); - if (global.ver_idx == elf.VER_NDX_LOCAL) continue; - const file = global.getFile(self) orelse continue; - const vis = @as(elf.STV, @enumFromInt(global.getSourceSymbol(self).st_other)); - if (vis == .HIDDEN) continue; - if (file == .shared and !global.isAbs(self)) { - global.flags.import = true; - continue; - } - if (file.getIndex() == index) { - global.flags.@"export" = true; - - if (self.options.shared and vis != .PROTECTED) { - global.flags.import = true; - } - } - } + self.getFile(index).?.object.markImportsExports(self); } } -fn resolveSyntheticSymbols(self: *Elf) !void { - const is_shared = self.options.shared; - const internal_index = self.internal_object_index orelse return; - const internal = self.getFile(internal_index).?.internal; - self.dynamic_index = try internal.addSyntheticGlobal("_DYNAMIC", self); - self.ehdr_start_index = try internal.addSyntheticGlobal("__ehdr_start", self); - self.init_array_start_index = try internal.addSyntheticGlobal("__init_array_start", self); - self.init_array_end_index = try internal.addSyntheticGlobal("__init_array_end", self); - self.fini_array_start_index = try internal.addSyntheticGlobal("__fini_array_start", self); - self.fini_array_end_index = try internal.addSyntheticGlobal("__fini_array_end", self); - self.preinit_array_start_index = try internal.addSyntheticGlobal("__preinit_array_start", self); - self.preinit_array_end_index = try internal.addSyntheticGlobal("__preinit_array_end", self); - self.got_index = try internal.addSyntheticGlobal("_GLOBAL_OFFSET_TABLE_", self); - self.plt_index = try internal.addSyntheticGlobal("_PROCEDURE_LINKAGE_TABLE_", self); - self.end_index = try internal.addSyntheticGlobal("_end", self); - - if (self.options.eh_frame_hdr) { - self.gnu_eh_frame_hdr_index = try internal.addSyntheticGlobal("__GNU_EH_FRAME_HDR", self); - } - - if (self.getGlobalByName("__dso_handle")) |index| { - if (self.getSymbol(index).getFile(self) == null) - self.dso_handle_index = try internal.addSyntheticGlobal("__dso_handle", self); - } - - self.rela_iplt_start_index = try internal.addSyntheticGlobal("__rela_iplt_start", self); - self.rela_iplt_end_index = try internal.addSyntheticGlobal("__rela_iplt_end", self); - - for (self.sections.items(.shdr)) |shdr| { - if (self.getStartStopBasename(shdr)) |name| { - const gpa = self.base.allocator; - try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); - - const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); - defer gpa.free(start); - const stop = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); - defer gpa.free(stop); - - self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(start, self)); - self.start_stop_indexes.appendAssumeCapacity(try internal.addSyntheticGlobal(stop, self)); - } - } - - if (self.options.cpu_arch.? == .riscv64 and !is_shared) { - self.global_pointer_index = try internal.addSyntheticGlobal("__global_pointer$", self); - } - - internal.resolveSymbols(self); -} - fn checkDuplicates(self: *Elf) !void { - var has_dupes = false; for (self.objects.items) |index| { - if (self.getFile(index).?.object.checkDuplicates(self)) { - has_dupes = true; - } + try self.getFile(index).?.object.checkDuplicates(self); } - if (has_dupes) return error.MultipleSymbolDefinition; + try self.reportDuplicates(); } fn claimUnresolved(self: *Elf) void { @@ -2211,61 +1921,63 @@ fn claimUnresolved(self: *Elf) void { defer tracy.end(); for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - const first_global = object.first_global orelse return; - for (object.getGlobals(), 0..) |global_index, i| { - const sym_idx = @as(u32, @intCast(first_global + i)); - const sym = object.symtab.items[sym_idx]; - if (sym.st_shndx != elf.SHN_UNDEF) continue; - - const global = self.getSymbol(global_index); - if (global.getFile(self)) |_| { - if (global.getSourceSymbol(self).st_shndx != elf.SHN_UNDEF) continue; - } + self.getFile(index).?.object.claimUnresolved(self); + } +} - const is_import = blk: { - if (!self.options.shared) break :blk false; - const vis = @as(elf.STV, @enumFromInt(sym.st_other)); - if (vis == .HIDDEN) break :blk false; - break :blk true; - }; +fn reportDuplicates(self: *Elf) error{ HasDuplicates, OutOfMemory }!void { + if (self.dupes.keys().len == 0) return; // Nothing to do - global.value = 0; - global.atom_ref = .{}; - global.sym_idx = sym_idx; - global.file = object.index; - global.ver_idx = if (is_import) elf.VER_NDX_LOCAL else self.default_sym_version; - global.flags.import = is_import; + const max_notes = 3; + + for (self.dupes.keys(), self.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); + try err.addMsg("duplicate symbol definition: {s}", .{sym.getName(self)}); + try err.addNote("defined by {}", .{sym.getFile(self).?.fmtPath()}); + + var inote: usize = 0; + while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { + const file_ptr = self.getFile(notes.items[inote]).?; + try err.addNote("defined by {}", .{file_ptr.fmtPath()}); + } + + if (notes.items.len > max_notes) { + const remaining = notes.items.len - max_notes; + try err.addNote("defined {d} more times", .{remaining}); } } + + return error.HasDuplicates; } fn reportUndefs(self: *Elf) !void { - if (self.undefs.count() == 0) return; + if (self.undefs.keys().len == 0) return; const max_notes = 4; - var it = self.undefs.iterator(); - while (it.next()) |entry| { - const undef_sym = self.getSymbol(entry.key_ptr.*); - const notes = entry.value_ptr.*; - const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); + for (self.undefs.keys(), self.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); - const err = try self.base.addErrorWithNotes(nnotes); + var err = try self.base.addErrorWithNotes(nnotes); try err.addMsg("undefined symbol: {s}", .{undef_sym.getName(self)}); - var inote: usize = 0; - while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { - const atom = self.getAtom(notes.items[inote]).?; - const object = atom.getObject(self); - try err.addNote("referenced by {}:{s}", .{ object.fmtPath(), atom.getName(self) }); + for (refs.items[0..nrefs]) |ref| { + const atom_ptr = self.getAtom(ref).?; + const object = atom_ptr.getObject(self); + try err.addNote("referenced by {s}:{s}", .{ object.fmtPath(), atom_ptr.getName(self) }); } - if (notes.items.len > max_notes) { - const remaining = notes.items.len - max_notes; + if (refs.items.len > max_notes) { + const remaining = refs.items.len - max_notes; try err.addNote("referenced {d} more times", .{remaining}); } } + return error.UndefinedSymbols; } @@ -2280,52 +1992,21 @@ fn scanRelocs(self: *Elf) !void { else => |e| return e, }; } + try self.reportUndefs(); if (has_reloc_error) return error.RelocError; - for (self.symbols.items, 0..) |*symbol, i| { - const index = @as(u32, @intCast(i)); - if (!symbol.isLocal(self) and !symbol.flags.has_dynamic) { - log.debug("'{s}' is non-local", .{symbol.getName(self)}); - try self.dynsym.addSymbol(index, self); - } - if (symbol.flags.got) { - log.debug("'{s}' needs GOT", .{symbol.getName(self)}); - try self.got.addGotSymbol(index, self); - } - if (symbol.flags.plt) { - if (symbol.flags.is_canonical) { - log.debug("'{s}' needs CPLT", .{symbol.getName(self)}); - symbol.flags.@"export" = true; - try self.plt.addSymbol(index, self); - } else if (symbol.flags.got) { - log.debug("'{s}' needs PLTGOT", .{symbol.getName(self)}); - try self.plt_got.addSymbol(index, self); - } else { - log.debug("'{s}' needs PLT", .{symbol.getName(self)}); - try self.plt.addSymbol(index, self); - } - } - if (symbol.flags.copy_rel and !symbol.flags.has_copy_rel) { - log.debug("'{s}' needs COPYREL", .{symbol.getName(self)}); - try self.copy_rel.addSymbol(index, self); - } - if (symbol.flags.tlsgd) { - log.debug("'{s}' needs TLSGD", .{symbol.getName(self)}); - try self.got.addTlsGdSymbol(index, self); - } - if (symbol.flags.gottp) { - log.debug("'{s}' needs GOTTP", .{symbol.getName(self)}); - try self.got.addGotTpSymbol(index, self); - } - if (symbol.flags.tlsdesc) { - log.debug("'{s}' needs TLSDESC", .{symbol.getName(self)}); - try self.got.addTlsDescSymbol(index, self); - } + for (self.objects.items) |index| { + try self.getFile(index).?.createSymbolIndirection(self); + } + for (self.shared_objects.items) |index| { + try self.getFile(index).?.createSymbolIndirection(self); + } + if (self.getInternalObject()) |obj| { + try obj.asFile().createSymbolIndirection(self); } - if (self.got.flags.needs_tlsld) { - log.debug("needs TLSLD", .{}); + log.debug("program needs TLSLD", .{}); try self.got.addTlsLdSymbol(self); } } @@ -2356,7 +2037,7 @@ fn setVerSymtab(self: *Elf) !void { try self.versym.resize(self.base.allocator, self.dynsym.count()); self.versym.items[0] = elf.VER_NDX_LOCAL; for (self.dynsym.entries.items, 1..) |dynsym, i| { - const sym = self.getSymbol(dynsym.index); + const sym = self.getSymbol(dynsym.ref).?; self.versym.items[i] = sym.ver_idx; } @@ -2387,7 +2068,7 @@ fn writeAtoms(self: *Elf) !void { if (atoms.items.len == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; - log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)}); + log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)}); const buffer = try self.base.allocator.alloc(u8, shdr.sh_size); defer self.base.allocator.free(buffer); @@ -2514,7 +2195,7 @@ fn writeSyntheticSections(self: *Elf) !void { if (self.dynstrtab_sect_index) |shndx| { const shdr = self.sections.items(.shdr)[shndx]; - try self.base.file.pwriteAll(self.dynstrtab.buffer.items, shdr.sh_offset); + try self.base.file.pwriteAll(self.dynstrtab.items, shdr.sh_offset); } if (self.eh_frame_sect_index) |shndx| { @@ -2585,7 +2266,7 @@ fn writeSyntheticSections(self: *Elf) !void { if (self.shstrtab_sect_index) |shndx| { const shdr = self.sections.items(.shdr)[shndx]; - try self.base.file.pwriteAll(self.shstrtab.buffer.items, shdr.sh_offset); + try self.base.file.pwriteAll(self.shstrtab.items, shdr.sh_offset); } } @@ -2609,6 +2290,10 @@ fn getRiscvEFlags(self: *Elf) !u32 { } fn writeHeader(self: *Elf) !void { + const e_entry: u64 = if (self.getInternalObject()) |obj| blk: { + const entry_sym = obj.getEntrySymbol(self) orelse break :blk 0; + break :blk @intCast(entry_sym.getAddress(.{}, self)); + } else 0; var header = elf.Elf64_Ehdr{ .e_ident = undefined, .e_type = if (self.options.pic) .DYN else .EXEC, @@ -2619,7 +2304,7 @@ fn writeHeader(self: *Elf) !void { else => unreachable, }, .e_version = 1, - .e_entry = if (self.entry_index) |index| @as(u64, @intCast(self.getSymbol(index).getAddress(.{}, self))) else 0, + .e_entry = e_entry, .e_phoff = @sizeOf(elf.Elf64_Ehdr), .e_shoff = self.shoff, .e_flags = if (self.options.cpu_arch.? == .riscv64) try self.getRiscvEFlags() else 0, @@ -2662,7 +2347,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 { const index = @as(u32, @intCast(try self.sections.addOne(gpa))); self.sections.set(index, .{ .shdr = .{ - .sh_name = try self.shstrtab.insert(gpa, opts.name), + .sh_name = try self.insertShString(opts.name), .sh_type = opts.type, .sh_flags = opts.flags, .sh_addr = 0, @@ -2679,7 +2364,7 @@ pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 { pub fn getSectionByName(self: *Elf, name: [:0]const u8) ?u32 { for (self.sections.items(.shdr), 0..) |shdr, i| { - const this_name = self.shstrtab.getAssumeExists(shdr.sh_name); + const this_name = self.getShString(shdr.sh_name); if (mem.eql(u8, this_name, name)) return @intCast(i); } else return null; } @@ -2717,6 +2402,11 @@ pub fn getFile(self: *Elf, index: File.Index) ?File { }; } +pub fn getInternalObject(self: *Elf) ?*InternalObject { + const index = self.internal_object_index orelse return null; + return self.getFile(index).?.internal; +} + pub fn addFileHandle(self: *Elf, file: std.fs.File) !File.HandleIndex { const gpa = self.base.allocator; const index: File.HandleIndex = @intCast(self.file_handles.items.len); @@ -2794,7 +2484,7 @@ fn createThunks(self: *Elf, shndx: u32) !void { log.debug("atom({}) {s}", .{ ref, atom.getName(self) }); for (atom.getRelocs(self)) |rel| { if (Thunk.isReachable(atom, rel, self)) continue; - const target = object.symbols.items[rel.r_sym()]; + const target = object.resolveSymbol(rel.r_sym(), self); try thunk.symbols.put(gpa, target, {}); } atom.addExtra(.{ .thunk = thunk_index }, self); @@ -2807,94 +2497,6 @@ fn createThunks(self: *Elf, shndx: u32) !void { } } -pub fn addSymbol(self: *Elf) !Symbol.Index { - const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); - const symbol = try self.symbols.addOne(self.base.allocator); - symbol.* = .{}; - return index; -} - -pub fn getSymbol(self: *Elf, index: Symbol.Index) *Symbol { - assert(index < self.symbols.items.len); - return &self.symbols.items[index]; -} - -pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 { - const fields = @typeInfo(Symbol.Extra).@"struct".fields; - try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len); - return self.addSymbolExtraAssumeCapacity(extra); -} - -pub fn addSymbolExtraAssumeCapacity(self: *Elf, 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 getSymbolExtra(self: *Elf, index: u32) ?Symbol.Extra { - if (index == 0) return null; - 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: *Elf, index: u32, extra: Symbol.Extra) void { - assert(index > 0); - 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 internString(self: *Elf, comptime format: []const u8, args: anytype) !u32 { - const gpa = self.base.allocator; - const string = try std.fmt.allocPrintZ(gpa, format, args); - defer gpa.free(string); - return self.string_intern.insert(gpa, string); -} - -const GetOrCreateGlobalResult = struct { - found_existing: bool, - index: u32, -}; - -pub fn getOrCreateGlobal(self: *Elf, off: u32) !GetOrCreateGlobalResult { - const gpa = self.base.allocator; - const gop = try self.globals.getOrPut(gpa, off); - if (!gop.found_existing) { - const index = try self.addSymbol(); - const global = self.getSymbol(index); - global.name = off; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; -} - -pub fn getGlobalByName(self: *Elf, name: []const u8) ?u32 { - const off = self.string_intern.getOffset(name) orelse return null; - return self.globals.get(off); -} - pub fn addMergeSubsection(self: *Elf) !MergeSubsection.Index { const index: MergeSubsection.Index = @intCast(self.merge_subsections.items.len); const msec = try self.merge_subsections.addOne(self.base.allocator); @@ -2915,11 +2517,11 @@ pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"typ break :name if (flags & elf.SHF_STRINGS != 0) ".rodata.str" else ".rodata.cst"; break :name name; }; - const out_off = try self.string_intern.insert(gpa, out_name); - const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP); for (self.merge_sections.items, 0..) |msec, index| { - if (msec.name == out_off) return @intCast(index); + if (mem.eql(u8, msec.getName(self), out_name)) return @intCast(index); } + const out_off = try self.insertShString(out_name); + const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP); const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len)); const msec = try self.merge_sections.addOne(gpa); msec.* = .{ @@ -2940,6 +2542,11 @@ pub fn getAtom(self: *Elf, ref: Ref) ?*Atom { return file.getAtom(ref.index); } +pub fn getSymbol(self: *Elf, ref: Ref) ?*Symbol { + const file = self.getFile(ref.file) orelse return null; + return file.getSymbol(ref.index); +} + pub fn getComdatGroup(self: *Elf, ref: Ref) *ComdatGroup { return self.getFile(ref.file).?.getComdatGroup(ref.index); } @@ -3010,8 +2617,8 @@ pub fn isCIdentifier(name: []const u8) bool { return true; } -fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { - const name = self.shstrtab.get(shdr.sh_name) orelse return null; +pub fn getStartStopBasename(self: *Elf, shdr: elf.Elf64_Shdr) ?[]const u8 { + const name = self.getShString(shdr.sh_name); if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) { if (isCIdentifier(name)) return name; } @@ -3058,6 +2665,37 @@ fn requiresThunks(self: Elf) bool { }; } +pub fn getEntryName(self: Elf) ?[]const u8 { + if (self.options.shared) return null; + return self.options.entry orelse "_start"; +} + +pub fn getShString(self: Elf, off: u32) [:0]const u8 { + assert(off < self.shstrtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0); +} + +pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 { + const gpa = self.base.allocator; + const off = @as(u32, @intCast(self.shstrtab.items.len)); + try self.shstrtab.ensureUnusedCapacity(gpa, name.len + 1); + self.shstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; + return off; +} + +pub fn getDynString(self: Elf, off: u32) [:0]const u8 { + assert(off < self.dynstrtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0); +} + +pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 { + const gpa = self.base.allocator; + const off = @as(u32, @intCast(self.dynstrtab.items.len)); + try self.dynstrtab.ensureUnusedCapacity(gpa, name.len + 1); + self.dynstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; + return off; +} + /// Caller owns the memory. pub fn preadAllAlloc(allocator: Allocator, file: std.fs.File, offset: usize, size: usize) ![]u8 { const buffer = try allocator.alloc(u8, size); @@ -3081,8 +2719,8 @@ fn formatSections( _ = unused_fmt_string; for (self.sections.items(.shdr), self.sections.items(.rela_shndx), 0..) |shdr, rela_shndx, i| { try writer.print("sect({d}) : {s} : @{x} ({x}) : align({x}) : size({x}) : rela({d})\n", .{ - i, self.shstrtab.getAssumeExists(shdr.sh_name), shdr.sh_offset, shdr.sh_addr, - shdr.sh_addralign, shdr.sh_size, rela_shndx, + i, self.getShString(shdr.sh_name), shdr.sh_offset, shdr.sh_addr, + shdr.sh_addralign, shdr.sh_size, rela_shndx, }); } } @@ -3158,22 +2796,22 @@ fn fmtDumpState( } try writer.print("GOT\n{}\n", .{self.got.fmt(self)}); try writer.writeAll("PLT\n"); - for (self.plt.symbols.items, 0..) |sym_index, i| { - try writer.print(" {d} => {d} '{s}'\n", .{ i, sym_index, self.getSymbol(sym_index).getName(self) }); + for (self.plt.symbols.items, 0..) |ref, i| { + try writer.print(" {d} => {} '{s}'\n", .{ i, ref, self.getSymbol(ref).?.getName(self) }); } try writer.writeByte('\n'); try writer.writeAll("PLTGOT\n"); - for (self.plt_got.symbols.items, 0..) |sym_index, i| { - try writer.print(" {d} => {d} '{s}'\n", .{ i, sym_index, self.getSymbol(sym_index).getName(self) }); + for (self.plt_got.symbols.items, 0..) |ref, i| { + try writer.print(" {d} => {} '{s}'\n", .{ i, ref, self.getSymbol(ref).?.getName(self) }); } try writer.writeByte('\n'); try writer.writeAll("COPYREL\n"); - for (self.copy_rel.symbols.items, 0..) |sym_index, i| { - const symbol = self.getSymbol(sym_index); - try writer.print(" {d}@{x} => {d} '{s}'\n", .{ + for (self.copy_rel.symbols.items, 0..) |ref, i| { + const symbol = self.getSymbol(ref).?; + try writer.print(" {d}@{x} => {} '{s}'\n", .{ i, symbol.getAddress(.{}, self), - sym_index, + ref, symbol.getName(self), }); } @@ -3227,6 +2865,96 @@ pub const Ref = struct { } }; +pub const SymbolResolver = struct { + keys: std.ArrayListUnmanaged(Key) = .{}, + values: std.ArrayListUnmanaged(Ref) = .{}, + table: std.AutoArrayHashMapUnmanaged(void, void) = .{}, + + const Result = struct { + found_existing: bool, + index: Index, + ref: *Ref, + }; + + pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void { + resolver.keys.deinit(allocator); + resolver.values.deinit(allocator); + resolver.table.deinit(allocator); + } + + pub fn getOrPut( + resolver: *SymbolResolver, + allocator: Allocator, + ref: Ref, + elf_file: *Elf, + ) !Result { + const adapter = Adapter{ .keys = resolver.keys.items, .elf_file = elf_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); + _ = try resolver.values.addOne(allocator); + } + return .{ + .found_existing = gop.found_existing, + .index = @intCast(gop.index + 1), + .ref = &resolver.values.items[gop.index], + }; + } + + pub fn get(resolver: SymbolResolver, index: Index) ?Ref { + if (index == 0) return null; + return resolver.values.items[index - 1]; + } + + pub fn reset(resolver: *SymbolResolver) void { + resolver.keys.clearRetainingCapacity(); + resolver.values.clearRetainingCapacity(); + resolver.table.clearRetainingCapacity(); + } + + const Key = struct { + index: Symbol.Index, + file_index: File.Index, + + fn getName(key: Key, elf_file: *Elf) [:0]const u8 { + const ref = Ref{ .index = key.index, .file = key.file_index }; + return elf_file.getSymbol(ref).?.getName(elf_file); + } + + fn getFile(key: Key, elf_file: *Elf) ?File { + return elf_file.getFile(key.file_index); + } + + fn eql(key: Key, other: Key, elf_file: *Elf) bool { + const key_name = key.getName(elf_file); + const other_name = other.getName(elf_file); + return mem.eql(u8, key_name, other_name); + } + + fn hash(key: Key, elf_file: *Elf) u32 { + return @truncate(Hash.hash(0, key.getName(elf_file))); + } + }; + + const Adapter = struct { + keys: []const Key, + elf_file: *Elf, + + pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool { + _ = b_void; + const other = ctx.keys[b_map_index]; + return key.eql(other, ctx.elf_file); + } + + pub fn hash(ctx: @This(), key: Key) u32 { + return key.hash(ctx.elf_file); + } + }; + + pub const Index = u32; +}; + pub const ComdatGroup = struct { signature_off: u32, file_index: File.Index, @@ -3301,6 +3029,7 @@ const File = @import("Elf/file.zig").File; const GnuHashSection = synthetic.GnuHashSection; const GotSection = synthetic.GotSection; const GotPltSection = synthetic.GotPltSection; +const Hash = std.hash.Wyhash; const HashSection = synthetic.HashSection; const InputMergeSection = merge_section.InputMergeSection; const InternalObject = @import("Elf/InternalObject.zig"); diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index 3eea23d4..203cb257 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -146,7 +146,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El const cpu_arch = elf_file.options.cpu_arch.?; const object = self.getObject(elf_file); for (self.getRelocs(elf_file)) |rel| { - const target = object.getSymbol(rel.r_sym(), elf_file); + const target = object.symbols.items[rel.r_sym()]; 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; @@ -157,7 +157,10 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El r_sym = elf_file.sections.items(.sym_index)[msub.getMergeSection(elf_file).out_shndx]; } else { r_addend += @intCast(target.getAddress(.{}, elf_file)); - r_sym = elf_file.sections.items(.sym_index)[target.shndx]; + r_sym = if (target.getShndx(elf_file)) |shndx| + elf_file.sections.items(.sym_index)[shndx] + else + 0; }, else => { r_sym = target.getOutputSymtabIndex(elf_file) orelse 0; @@ -207,11 +210,25 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void { var it = RelocsIterator{ .relocs = relocs }; while (it.next()) |rel| { const r_kind = relocation.decode(rel.r_type(), cpu_arch); - if (r_kind == .none) continue; - if (try self.reportUndefSymbol(rel, elf_file)) continue; - const symbol = object.getSymbol(rel.r_sym(), elf_file); + const symbol_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const symbol = elf_file.getSymbol(symbol_ref) orelse { + const sym_name = object.symbols.items[rel.r_sym()].getName(elf_file); + // Violation of One Definition Rule for COMDATs. + elf_file.base.fatal("{}: {s}: {s} refers to a discarded COMDAT section", .{ + object.fmtPath(), + self.getName(elf_file), + sym_name, + }); + continue; + }; + + const is_synthetic_symbol = rel.r_sym() >= object.symtab.items.len; + + // Report an undefined symbol. + if (!is_synthetic_symbol and (try self.reportUndefSymbol(symbol, rel, elf_file))) + continue; if (symbol.isIFunc(elf_file)) { symbol.flags.got = true; @@ -250,23 +267,23 @@ fn scanReloc(self: Atom, symbol: *Symbol, rel: elf.Elf64_Rela, action: RelocActi .none => {}, .@"error" => if (symbol.isAbs(elf_file)) - try self.noPicError(symbol, rel, elf_file) + try self.noPicError(symbol.*, rel, elf_file) else - try self.picError(symbol, rel, elf_file), + try self.picError(symbol.*, rel, elf_file), .copyrel => { if (elf_file.options.z_nocopyreloc) { if (symbol.isAbs(elf_file)) - try self.noPicError(symbol, rel, elf_file) + try self.noPicError(symbol.*, rel, elf_file) else - try self.picError(symbol, rel, elf_file); + try self.picError(symbol.*, rel, elf_file); } symbol.flags.copy_rel = true; }, .dyn_copyrel => { if (is_writeable or elf_file.options.z_nocopyreloc) { - try self.textReloc(symbol, elf_file); + try self.textReloc(symbol.*, elf_file); object.num_dynrelocs += 1; } else { symbol.flags.copy_rel = true; @@ -292,7 +309,7 @@ fn scanReloc(self: Atom, symbol: *Symbol, rel: elf.Elf64_Rela, action: RelocActi }, .dynrel, .baserel, .ifunc => { - try self.textReloc(symbol, elf_file); + try self.textReloc(symbol.*, elf_file); object.num_dynrelocs += 1; if (action == .ifunc) elf_file.num_ifunc_dynrelocs += 1; @@ -300,7 +317,7 @@ fn scanReloc(self: Atom, symbol: *Symbol, rel: elf.Elf64_Rela, action: RelocActi } } -inline fn textReloc(self: Atom, symbol: *const Symbol, elf_file: *Elf) !void { +inline fn textReloc(self: Atom, symbol: Symbol, elf_file: *Elf) !void { const is_writeable = self.getInputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; if (!is_writeable) { if (elf_file.options.z_text) { @@ -316,7 +333,7 @@ inline fn textReloc(self: Atom, symbol: *const Symbol, elf_file: *Elf) !void { } } -inline fn noPicError(self: Atom, symbol: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf) !void { +inline fn noPicError(self: Atom, symbol: Symbol, rel: elf.Elf64_Rela, elf_file: *Elf) !void { elf_file.base.fatal( "{s}: {s}: {} relocation at offset 0x{x} against symbol '{s}' cannot be used; recompile with -fno-PIC", .{ @@ -330,7 +347,7 @@ inline fn noPicError(self: Atom, symbol: *const Symbol, rel: elf.Elf64_Rela, elf return error.RelocError; } -inline fn picError(self: Atom, symbol: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf) !void { +inline fn picError(self: Atom, symbol: Symbol, rel: elf.Elf64_Rela, elf_file: *Elf) !void { elf_file.base.fatal( "{s}: {s}: {} relocation at offset 0x{x} against symbol '{s}' cannot be used; recompile with -fPIC", .{ @@ -357,7 +374,7 @@ const RelocAction = enum { ifunc, }; -fn getPcRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { +fn getPcRelocAction(symbol: Symbol, elf_file: *Elf) RelocAction { // zig fmt: off const table: [3][4]RelocAction = .{ // Abs Local Import data Import func @@ -371,7 +388,7 @@ fn getPcRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { return table[output][data]; } -fn getAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { +fn getAbsRelocAction(symbol: Symbol, elf_file: *Elf) RelocAction { // zig fmt: off const table: [3][4]RelocAction = .{ // Abs Local Import data Import func @@ -385,7 +402,7 @@ fn getAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { return table[output][data]; } -fn getDynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { +fn getDynAbsRelocAction(symbol: Symbol, elf_file: *Elf) RelocAction { if (symbol.isIFunc(elf_file)) return .ifunc; // zig fmt: off const table: [3][4]RelocAction = .{ @@ -405,47 +422,30 @@ inline fn getOutputType(elf_file: *Elf) u2 { return if (elf_file.options.pie) 1 else 2; } -inline fn getDataType(symbol: *const Symbol, elf_file: *Elf) u2 { +inline fn getDataType(symbol: Symbol, elf_file: *Elf) u2 { if (symbol.isAbs(elf_file)) return 0; if (!symbol.flags.import) return 1; if (symbol.getType(elf_file) != elf.STT_FUNC) return 2; return 3; } -fn reportUndefSymbol(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) !bool { +fn reportUndefSymbol(self: Atom, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf) !bool { + const gpa = elf_file.base.allocator; const object = self.getObject(elf_file); - if (rel.r_sym() >= object.symtab.items.len) return false; - - const sym = object.getSymbol(rel.r_sym(), elf_file); - const s_rel_sym = object.symtab.items[rel.r_sym()]; - - // Check for violation of One Definition Rule for COMDATs. - if (sym.getFile(elf_file) == null) { - elf_file.base.fatal("{}: {s}: {s} refers to a discarded COMDAT section", .{ - object.fmtPath(), - self.getName(elf_file), - sym.getName(elf_file), - }); - return true; - } - - // Next, report any undefined non-weak symbols that are not imports. - const s_sym = sym.getSourceSymbol(elf_file); - if (s_rel_sym.st_shndx == elf.SHN_UNDEF and - s_rel_sym.st_bind() == elf.STB_GLOBAL and - sym.sym_idx > 0 and + const rel_esym = object.symtab.items[rel.r_sym()]; + const esym = sym.getElfSym(elf_file); + if (rel_esym.st_shndx == elf.SHN_UNDEF and + rel_esym.st_bind() == elf.STB_GLOBAL and + sym.esym_idx > 0 and !sym.flags.import and - s_sym.st_shndx == elf.SHN_UNDEF) + esym.st_shndx == elf.SHN_UNDEF) { - const gpa = elf_file.base.allocator; - const gop = try elf_file.undefs.getOrPut(gpa, object.symbols.items[rel.r_sym()]); + const idx = object.symbols_resolver.items[rel.r_sym() - object.first_global.?]; + const gop = try elf_file.undefs.getOrPut(gpa, idx); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(gpa, .{ - .index = self.atom_index, - .file = self.file, - }); + try gop.value_ptr.append(gpa, .{ .index = self.atom_index, .file = self.file }); return true; } @@ -475,7 +475,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, writer: anytype) !void { const r_kind = relocation.decode(rel.r_type(), cpu_arch); if (r_kind == .none) continue; - const target = object.getSymbol(rel.r_sym(), elf_file); + const target = object.symbols.items[rel.r_sym()]; // We will use equation format to resolve relocations: // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ @@ -535,7 +535,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, writer: anytype) !void { fn resolveDynAbsReloc( self: Atom, - target: *const Symbol, + target: Symbol, rel: elf.Elf64_Rela, action: RelocAction, elf_file: *Elf, @@ -567,7 +567,7 @@ fn resolveDynAbsReloc( if (is_writeable or elf_file.options.z_nocopyreloc) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.getExtra(elf_file).?.dynamic, + .sym = target.getExtra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -581,7 +581,7 @@ fn resolveDynAbsReloc( if (is_writeable) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.getExtra(elf_file).?.dynamic, + .sym = target.getExtra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -594,7 +594,7 @@ fn resolveDynAbsReloc( .dynrel => { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, - .sym = target.getExtra(elf_file).?.dynamic, + .sym = target.getExtra(elf_file).dynamic, .type = relocation.encode(.abs, cpu_arch), .addend = A, }); @@ -650,9 +650,23 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, writer: anytype) !void while (it.next()) |rel| { const r_kind = relocation.decode(rel.r_type(), cpu_arch); if (r_kind == .none) continue; - if (try self.reportUndefSymbol(rel, elf_file)) continue; - const target = object.getSymbol(rel.r_sym(), elf_file); + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.getSymbol(target_ref) orelse { + const sym_name = object.symbols.items[rel.r_sym()].getName(elf_file); + // Violation of One Definition Rule for COMDATs. + elf_file.base.fatal("{}: {s}: {s} refers to a discarded COMDAT section", .{ + object.fmtPath(), + self.getName(elf_file), + sym_name, + }); + continue; + }; + const is_synthetic_symbol = rel.r_sym() >= object.symtab.items.len; + + // Report an undefined symbol. + if (!is_synthetic_symbol and (try self.reportUndefSymbol(target, rel, elf_file))) + continue; const P = self.getAddress(elf_file) + @as(i64, @intCast(rel.r_offset)); const A = rel.r_addend; @@ -786,13 +800,13 @@ const x86_64 = struct { switch (r_type) { .@"64" => { - try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol.*, elf_file), elf_file); }, .@"32", .@"32S", => { - try atom.scanReloc(symbol, rel, getAbsRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getAbsRelocAction(symbol.*, elf_file), elf_file); }, .GOT32, @@ -816,7 +830,7 @@ const x86_64 = struct { }, .PC32 => { - try atom.scanReloc(symbol, rel, getPcRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getPcRelocAction(symbol.*, elf_file), elf_file); }, .TLSGD => { @@ -872,7 +886,7 @@ const x86_64 = struct { .TPOFF32, .TPOFF64, => { - if (is_shared) try atom.picError(symbol, rel, elf_file); + if (is_shared) try atom.picError(symbol.*, rel, elf_file); }, .GOTOFF64, @@ -897,7 +911,7 @@ const x86_64 = struct { atom: Atom, elf_file: *Elf, rel: elf.Elf64_Rela, - target: *const Symbol, + target: Symbol, args: ResolveArgs, it: *RelocsIterator, code: []u8, @@ -1063,11 +1077,11 @@ const x86_64 = struct { .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), .SIZE32 => { - const size = @as(i64, @intCast(target.getSourceSymbol(elf_file).st_size)); + const size = @as(i64, @intCast(target.getElfSym(elf_file).st_size)); try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); }, .SIZE64 => { - const size = @as(i64, @intCast(target.getSourceSymbol(elf_file).st_size)); + const size = @as(i64, @intCast(target.getElfSym(elf_file).st_size)); try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); }, else => { @@ -1279,11 +1293,11 @@ const aarch64 = struct { switch (r_type) { .ABS64 => { - try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol.*, elf_file), elf_file); }, .ADR_PREL_PG_HI21 => { - try atom.scanReloc(symbol, rel, getPcRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getPcRelocAction(symbol.*, elf_file), elf_file); }, .ADR_GOT_PAGE => { @@ -1308,7 +1322,7 @@ const aarch64 = struct { .TLSLE_ADD_TPREL_HI12, .TLSLE_ADD_TPREL_LO12_NC, => { - if (is_shared) try atom.picError(symbol, rel, elf_file); + if (is_shared) try atom.picError(symbol.*, rel, elf_file); }, .TLSIE_ADR_GOTTPREL_PAGE21, @@ -1360,7 +1374,7 @@ const aarch64 = struct { atom: Atom, elf_file: *Elf, rel: elf.Elf64_Rela, - target: *const Symbol, + target: Symbol, args: ResolveArgs, it: *RelocsIterator, code_buffer: []u8, @@ -1407,8 +1421,8 @@ const aarch64 = struct { => { const disp: i28 = math.cast(i28, S + A - P) orelse blk: { const thunk = atom.getThunk(elf_file); - const target_index = object.symbols.items[rel.r_sym()]; - const S_ = thunk.getTargetAddress(target_index, elf_file); + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const S_ = thunk.getTargetAddress(target_ref, elf_file); break :blk math.cast(i28, S_ + A - P) orelse return error.Overflow; }; aarch64_util.writeBranchImm(disp, code); @@ -1627,11 +1641,11 @@ const riscv = struct { switch (r_type) { .@"64" => { - try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getDynAbsRelocAction(symbol.*, elf_file), elf_file); }, .HI20 => { - try atom.scanReloc(symbol, rel, getAbsRelocAction(symbol, elf_file), elf_file); + try atom.scanReloc(symbol, rel, getAbsRelocAction(symbol.*, elf_file), elf_file); }, .CALL_PLT => if (symbol.flags.import) { @@ -1664,7 +1678,7 @@ const riscv = struct { atom: Atom, elf_file: *Elf, rel: elf.Elf64_Rela, - target: *const Symbol, + target: Symbol, args: ResolveArgs, it: *RelocsIterator, code: []u8, @@ -1745,7 +1759,7 @@ const riscv = struct { return error.RelocError; }; it.pos = pos; - const target_ = object.getSymbol(pair.r_sym(), elf_file); + const target_ = object.symbols.items[pair.r_sym()]; const S_ = target_.getAddress(.{}, elf_file); const A_ = pair.r_addend; const P_ = atom_addr + @as(i64, @intCast(pair.r_offset)); diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index 75396c6d..35d0553a 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -1,47 +1,317 @@ index: File.Index, + symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +strtab: std.ArrayListUnmanaged(u8) = .{}, + +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, + +entry_index: ?Symbol.Index = null, +dynamic_index: ?Symbol.Index = null, +ehdr_start_index: ?Symbol.Index = null, +init_array_start_index: ?Symbol.Index = null, +init_array_end_index: ?Symbol.Index = null, +fini_array_start_index: ?Symbol.Index = null, +fini_array_end_index: ?Symbol.Index = null, +preinit_array_start_index: ?Symbol.Index = null, +preinit_array_end_index: ?Symbol.Index = null, +got_index: ?Symbol.Index = null, +plt_index: ?Symbol.Index = null, +end_index: ?Symbol.Index = null, +gnu_eh_frame_hdr_index: ?Symbol.Index = null, +dso_handle_index: ?Symbol.Index = null, +rela_iplt_start_index: ?Symbol.Index = null, +rela_iplt_end_index: ?Symbol.Index = null, +global_pointer_index: ?Symbol.Index = null, +start_stop_indexes: std.ArrayListUnmanaged(u32) = .{}, + alive: bool = true, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(self: *InternalObject, allocator: Allocator) void { self.symtab.deinit(allocator); + self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); + self.start_stop_indexes.deinit(allocator); } -pub fn addSyntheticGlobal(self: *InternalObject, name: [:0]const u8, elf_file: *Elf) !u32 { - const gpa = elf_file.base.allocator; - try self.symtab.ensureUnusedCapacity(gpa, 1); - try self.symbols.ensureUnusedCapacity(gpa, 1); - self.symtab.appendAssumeCapacity(.{ - .st_name = try elf_file.string_intern.insert(gpa, name), +pub fn init(self: *InternalObject, allocator: Allocator) !void { + // Null byte in strtab + try self.strtab.append(allocator, 0); +} + +fn newSymbolAssumeCapacity(self: *InternalObject, name_off: u32, elf_file: *Elf) Symbol.Index { + const esym_index: u32 = @intCast(self.symtab.items.len); + const esym = self.symtab.addOneAssumeCapacity(); + esym.* = .{ + .st_name = name_off, .st_info = elf.STB_GLOBAL << 4, .st_other = @intFromEnum(elf.STV.HIDDEN), .st_shndx = elf.SHN_ABS, .st_value = 0, .st_size = 0, - }); - const off = try elf_file.internString("{s}", .{name}); - const gop = try elf_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; - return gop.index; -} - -pub fn resolveSymbols(self: *InternalObject, elf_file: *Elf) void { - for (self.getGlobals(), 0..) |index, i| { - const sym_idx = @as(Symbol.Index, @intCast(i)); - const this_sym = self.symtab.items[sym_idx]; - - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; - - const global = elf_file.getSymbol(index); - if (self.asFile().getSymbolRank(this_sym, false) < global.getSymbolRank(elf_file)) { - global.value = 0; - global.atom_ref = .{}; - global.file = self.index; - global.sym_idx = sym_idx; - global.ver_idx = elf_file.default_sym_version; + }; + const index = self.addSymbolAssumeCapacity(); + const symbol = &self.symbols.items[index]; + symbol.name = name_off; + symbol.extra = self.addSymbolExtraAssumeCapacity(.{}); + symbol.ref = .{ .index = 0, .file = 0 }; + symbol.esym_idx = esym_index; + symbol.ver_idx = elf_file.default_sym_version; + return index; +} + +pub fn initSymbols(self: *InternalObject, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + + var nsyms: usize = 0; + if (elf_file.getEntryName()) |_| { + nsyms += 1; // entry + } + nsyms += 1; // _DYNAMIC + nsyms += 1; // __ehdr_start + nsyms += 1; // __init_array_start + nsyms += 1; // __init_array_end + nsyms += 1; // __fini_array_start + nsyms += 1; // __fini_array_end + nsyms += 1; // __preinit_array_start + nsyms += 1; // __preinit_array_end + nsyms += 1; // _GLOBAL_OFFSET_TABLE_ + nsyms += 1; // _PROCEDURE_LINKAGE_TABLE_ + nsyms += 1; // _end + if (elf_file.options.eh_frame_hdr) { + nsyms += 1; // __GNU_EH_FRAME_HDR + } + nsyms += 1; // __dso_handle + nsyms += 1; // __rela_iplt_start + nsyms += 1; // __rela_iplt_end + if (elf_file.options.cpu_arch.?.isRISCV() and !elf_file.options.shared) { + nsyms += 1; // __global_pointer$ + } + + 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); + + if (elf_file.getEntryName()) |name| { + self.entry_index = self.newSymbolAssumeCapacity(try self.addString(gpa, name), elf_file); + } + + self.dynamic_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_DYNAMIC"), elf_file); + self.ehdr_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__ehdr_start"), elf_file); + self.init_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_start"), elf_file); + self.init_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__init_array_end"), elf_file); + self.fini_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_start"), elf_file); + self.fini_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__fini_array_end"), elf_file); + self.preinit_array_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_start"), elf_file); + self.preinit_array_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__preinit_array_end"), elf_file); + self.got_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_GLOBAL_OFFSET_TABLE_"), elf_file); + self.plt_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_PROCEDURE_LINKAGE_TABLE_"), elf_file); + self.end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "_end"), elf_file); + + if (elf_file.options.eh_frame_hdr) { + self.gnu_eh_frame_hdr_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__GNU_EH_FRAME_HDR"), elf_file); + } + + self.dso_handle_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__dso_handle"), elf_file); + self.rela_iplt_start_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_start"), elf_file); + self.rela_iplt_end_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__rela_iplt_end"), elf_file); + + if (elf_file.options.cpu_arch.?.isRISCV() and !elf_file.options.shared) { + self.global_pointer_index = self.newSymbolAssumeCapacity(try self.addString(gpa, "__global_pointer$"), elf_file); + } +} + +pub fn initStartStopSymbols(self: *InternalObject, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + const slice = elf_file.sections.slice(); + + var nsyms: usize = 0; + for (slice.items(.shdr)) |shdr| { + if (elf_file.getStartStopBasename(shdr)) |_| { + nsyms += 2; // __start_, __stop_ + } + } + + try self.start_stop_indexes.ensureTotalCapacityPrecise(gpa, nsyms); + try self.symtab.ensureUnusedCapacity(gpa, nsyms); + try self.symbols.ensureUnusedCapacity(gpa, nsyms); + try self.symbols_extra.ensureUnusedCapacity(gpa, nsyms * @sizeOf(Symbol.Extra)); + try self.symbols_resolver.ensureUnusedCapacity(gpa, nsyms); + + for (slice.items(.shdr)) |shdr| { + if (elf_file.getStartStopBasename(shdr)) |name| { + const start_name = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); + defer gpa.free(start_name); + const stop_name = try std.fmt.allocPrintZ(gpa, "__stop_{s}", .{name}); + defer gpa.free(stop_name); + + for (&[_][]const u8{ start_name, stop_name }) |nn| { + const index = self.newSymbolAssumeCapacity(try self.addString(gpa, nn), elf_file); + self.start_stop_indexes.appendAssumeCapacity(index); + const gop = try elf_file.resolver.getOrPut(gpa, .{ + .index = index, + .file = self.index, + }, elf_file); + assert(!gop.found_existing); + gop.ref.* = .{ .index = index, .file = self.index }; + self.symbols_resolver.appendAssumeCapacity(gop.index); + } + } + } +} + +pub fn resolveSymbols(self: *InternalObject, elf_file: *Elf) !void { + const gpa = elf_file.base.allocator; + + 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 (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.getSymbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } + + if (self.asFile().getSymbolRank(esym, false) < elf_file.getSymbol(gop.ref.*).?.getSymbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + } + } +} + +pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { + const shdrs = elf_file.sections.items(.shdr); + + const allocSymbol = struct { + fn allocSymbol(int: *InternalObject, index: Symbol.Index, value: u64, osec: u32, ef: *Elf) void { + const sym = ef.symbol(int.resolveSymbol(index, ef)).?; + sym.value = @intCast(value); + sym.output_section_index = osec; + } + }.allocSymbol; + + // _DYNAMIC + if (elf_file.dynamic_section_index) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.dynamic_index.?, shdr.sh_addr, shndx, elf_file); + } + + // __ehdr_start + allocSymbol(self, self.ehdr_start_index.?, elf_file.image_base, 1, elf_file); + + // __init_array_start, __init_array_end + if (elf_file.getSectionByName(".init_array")) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.init_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.init_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); + } + + // __fini_array_start, __fini_array_end + if (elf_file.getSectionByName(".fini_array")) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.fini_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.fini_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); + } + + // __preinit_array_start, __preinit_array_end + if (elf_file.getSectionByName(".preinit_array")) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.preinit_array_start_index.?, shdr.sh_addr, shndx, elf_file); + allocSymbol(self, self.preinit_array_end_index.?, shdr.sh_addr + shdr.sh_size, shndx, elf_file); + } + + // _GLOBAL_OFFSET_TABLE_ + if (elf_file.options.cpu_arch.? == .x86_64) { + if (elf_file.got_plt_section_index) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file); + } + } else { + if (elf_file.got_section_index) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.got_index.?, shdr.sh_addr, shndx, elf_file); + } + } + + // _PROCEDURE_LINKAGE_TABLE_ + if (elf_file.plt_section_index) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.plt_index.?, shdr.sh_addr, shndx, elf_file); + } + + // __dso_handle + if (self.dso_handle_index) |index| { + const shdr = shdrs[1]; + allocSymbol(self, index, shdr.sh_addr, 0, elf_file); + } + + // __GNU_EH_FRAME_HDR + if (elf_file.eh_frame_hdr_section_index) |shndx| { + const shdr = shdrs[shndx]; + allocSymbol(self, self.gnu_eh_frame_hdr_index.?, shdr.sh_addr, shndx, elf_file); + } + + // __rela_iplt_start, __rela_iplt_end + if (elf_file.rela_dyn_section_index) |shndx| blk: { + if (!elf_file.options.static or elf_file.options.pie) break :blk; + const shdr = shdrs[shndx]; + const end_addr = shdr.sh_addr + shdr.sh_size; + const start_addr = end_addr - elf_file.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); + allocSymbol(self, self.rela_iplt_start_index.?, start_addr, shndx, elf_file); + allocSymbol(self, self.rela_iplt_end_index.?, end_addr, shndx, elf_file); + } + + // _end + { + var value: u64 = 0; + var osec: u32 = 0; + for (shdrs, 0..) |shdr, shndx| { + if (shdr.sh_flags & elf.SHF_ALLOC != 0) { + value = shdr.sh_addr + shdr.sh_size; + osec = @intCast(shndx); + } + } + allocSymbol(self, self.end_index.?, value, osec, elf_file); + } + + // __global_pointer$ + if (self.global_pointer_index) |index| { + const value, const osec = if (elf_file.getSectionByName(".sdata")) |shndx| .{ + shdrs[shndx].sh_addr + 0x800, + shndx, + } else .{ 0, 0 }; + allocSymbol(self, index, value, osec, elf_file); + } + + // __start_*, __stop_* + { + 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], elf_file); + const start = elf_file.getSymbol(start_ref).?; + const name = start.name(elf_file); + const stop_ref = self.resolveSymbol(self.start_stop_indexes.items[index + 1], elf_file); + const stop = elf_file.getSymbol(stop_ref).?; + const shndx = elf_file.getSectionByName(name["__start_".len..]).?; + const shdr = shdrs[shndx]; + start.value = @intCast(shdr.sh_addr); + start.output_section_index = shndx; + stop.value = @intCast(shdr.sh_addr + shdr.sh_size); + stop.output_section_index = shndx; } } } @@ -53,16 +323,16 @@ pub fn asFile(self: *InternalObject) File { pub fn calcSymtabSize(self: *InternalObject, elf_file: *Elf) !void { if (elf_file.options.strip_all) return; - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != self.index) 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.getName(elf_file).len + 1)); @@ -72,10 +342,10 @@ pub fn calcSymtabSize(self: *InternalObject, elf_file: *Elf) !void { pub fn writeSymtab(self: InternalObject, elf_file: *Elf) void { if (elf_file.options.strip_all) return; - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != self.index) continue; const idx = global.getOutputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.getName(elf_file)); @@ -86,8 +356,87 @@ pub fn writeSymtab(self: InternalObject, elf_file: *Elf) void { } } -pub inline fn getGlobals(self: InternalObject) []const Symbol.Index { - return self.symbols.items; +pub fn getDynamicSymbol(self: InternalObject, elf_file: *Elf) ?*Symbol { + const index = self.dynamic_index orelse return null; + const resolv = self.resolveSymbol(index, elf_file); + return elf_file.getSymbol(resolv); +} + +pub fn getEntrySymbol(self: InternalObject, elf_file: *Elf) ?*Symbol { + const index = self.entry_index orelse return null; + const resolv = self.resolveSymbol(index, elf_file); + return elf_file.getSymbol(resolv); +} + +fn addString(self: *InternalObject, allocator: Allocator, str: []const u8) !u32 { + const off: u32 = @intCast(self.strtab.items.len); + try self.strtab.ensureUnusedCapacity(allocator, str.len + 1); + self.strtab.appendSliceAssumeCapacity(str); + self.strtab.appendAssumeCapacity(0); + return off; +} + +pub fn getString(self: InternalObject, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); +} + +pub fn resolveSymbol(self: InternalObject, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + const resolv = self.symbols_resolver.items[index]; + return elf_file.resolver.get(resolv).?; +} + +fn addSymbol(self: *InternalObject, allocator: Allocator) !Symbol.Index { + try self.symbols.ensureUnusedCapacity(allocator, 1); + return self.addSymbolAssumeCapacity(); +} + +fn addSymbolAssumeCapacity(self: *InternalObject) Symbol.Index { + const index: Symbol.Index = @intCast(self.symbols.items.len); + self.symbols.appendAssumeCapacity(.{ .file = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *InternalObject, 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: *InternalObject, 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 getSymbolExtra(self: *InternalObject, 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: *InternalObject, index: u32, extra: Symbol.Extra) void { + const fields = @typeInfo(Symbol.Extra).@"struct".fields; + inline for (fields, 0..) |field, i| { + self.symbols_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } } pub fn fmtSymtab(self: *InternalObject, elf_file: *Elf) std.fmt.Formatter(formatSymtab) { @@ -110,15 +459,22 @@ 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.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (self.symbols.items, 0..) |sym, i| { + const ref = self.resolveSymbol(@intCast(i), elf_file); + if (elf_file.getSymbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.getName(elf_file)}); + } } } const assert = std.debug.assert; const elf = std.elf; +const mem = std.mem; const std = @import("std"); const Allocator = std.mem.Allocator; diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index 5b85dd45..ec10910a 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -9,12 +9,14 @@ symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .{}, +symbols_extra: std.ArrayListUnmanaged(u32) = .{}, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom) = .{}, atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, atoms_extra: std.ArrayListUnmanaged(u32) = .{}, +relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, @@ -47,6 +49,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); + self.symbols_extra.deinit(allocator); + self.symbols_resolver.deinit(allocator); self.atoms.deinit(allocator); self.atoms_indexes.deinit(allocator); self.atoms_extra.deinit(allocator); @@ -281,31 +285,26 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { defer tracy.end(); const first_global = self.first_global orelse self.symtab.items.len; + const nglobals = self.symtab.items.len - first_global; try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); - - for (self.symtab.items[0..first_global], 0..) |sym, i| { - const index = try elf_file.addSymbol(); - self.symbols.appendAssumeCapacity(index); - const symbol = elf_file.getSymbol(index); - const name = self.getString(sym.st_name); - symbol.* = .{ - .value = @intCast(sym.st_value), - .name = try elf_file.string_intern.insert(elf_file.base.allocator, name), - .sym_idx = @as(u32, @intCast(i)), - .atom_ref = if (sym.st_shndx == elf.SHN_ABS) .{} else .{ - .index = self.atoms_indexes.items[sym.st_shndx], - .file = self.index, - }, - .file = self.index, - }; - } - - for (self.symtab.items[first_global..]) |sym| { - const name = self.getString(sym.st_name); - const off = try elf_file.internString("{s}", .{name}); - const gop = try elf_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; + try self.symbols_extra.ensureTotalCapacityPrecise(allocator, self.symtab.items.len * @sizeOf(Symbol.Extra)); + try self.symbols_resolver.ensureTotalCapacityPrecise(allocator, nglobals); + self.symbols_resolver.resize(allocator, nglobals) catch unreachable; + @memset(self.symbols_resolver.items, 0); + + for (self.symtab.items, 0..) |sym, i| { + const index = self.addSymbolAssumeCapacity(); + const sym_ptr = &self.symbols.items[index]; + sym_ptr.value = @intCast(sym.st_value); + sym_ptr.name = sym.st_name; + sym_ptr.esym_idx = @intCast(i); + sym_ptr.extra = self.addSymbolExtraAssumeCapacity(.{}); + sym_ptr.ver_idx = 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 }; + } } } @@ -505,7 +504,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { for (self.cies.items) |cie| { for (cie.getRelocs(elf_file)) |rel| { - const sym = self.getSymbol(rel.r_sym(), elf_file); + const sym = &self.symbols.items[rel.r_sym()]; if (sym.flags.import) { if (sym.getType(elf_file) != elf.STT_FUNC) { elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{ @@ -519,56 +518,100 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { } } -pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { +pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); + const gpa = elf_file.base.allocator; + const first_global = self.first_global orelse return; - for (self.getGlobals(), 0..) |index, i| { - const sym_idx = @as(Symbol.Index, @intCast(first_global + i)); - const this_sym = self.symtab.items[sym_idx]; + for (self.getGlobals(), first_global..) |_, i| { + const esym = self.symtab.items[i]; + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON and esym.st_shndx != elf.SHN_UNDEF) { + const atom_index = self.atoms_indexes.items[esym.st_shndx]; + const atom_ptr = self.getAtom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + } - if (this_sym.st_shndx == elf.SHN_UNDEF) continue; + const resolv = &self.symbols_resolver.items[i - first_global]; + 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_ABS and this_sym.st_shndx != elf.SHN_COMMON) { - const atom_index = self.atoms_indexes.items[this_sym.st_shndx]; - const atom = self.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.getSymbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; } - const global = elf_file.getSymbol(index); - if (self.asFile().getSymbolRank(this_sym, !self.alive) < global.getSymbolRank(elf_file)) { - const atom_ref: Elf.Ref = switch (this_sym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => .{}, - else => .{ - .index = self.atoms_indexes.items[this_sym.st_shndx], - .file = self.index, - }, - }; - global.value = @intCast(this_sym.st_value); - global.atom_ref = atom_ref; - global.sym_idx = sym_idx; - global.file = self.index; - global.ver_idx = elf_file.default_sym_version; - if (this_sym.st_bind() == elf.STB_WEAK) global.flags.weak = true; + if (self.asFile().getSymbolRank(esym, !self.alive) < elf_file.getSymbol(gop.ref.*).?.getSymbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } -pub fn markLive(self: *Object, elf_file: *Elf) void { - const tracy = trace(@src()); - defer tracy.end(); +pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (self.getGlobals(), 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.getSymbol(self.resolveSymbol(esym_index, elf_file)) != null) continue; + + const is_import = blk: { + if (!elf_file.options.shared) break :blk false; + const vis = @as(elf.STV, @enumFromInt(esym.st_other)); + if (vis == .HIDDEN) break :blk false; + break :blk true; + }; + + sym.value = 0; + sym.ref = .{ .index = 0, .file = 0 }; + sym.esym_idx = esym_index; + sym.file = self.index; + sym.ver_idx = if (is_import) elf.VER_NDX_LOCAL else elf_file.default_sym_version; + sym.flags.import = is_import; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.items[idx - 1] = .{ .index = esym_index, .file = self.index }; + } +} +pub fn claimUnresolvedRelocatable(self: *Object, elf_file: *Elf) void { const first_global = self.first_global orelse return; - for (self.getGlobals(), 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.getSymbol(index); - const file = global.getFile(elf_file) orelse continue; - const should_keep = sym.st_shndx == elf.SHN_UNDEF or - (sym.st_shndx == elf.SHN_COMMON and global.getSourceSymbol(elf_file).st_shndx != elf.SHN_COMMON); + for (self.getGlobals(), 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.getSymbol(self.resolveSymbol(esym_index, elf_file)) != null) continue; + + sym.value = 0; + sym.ref = .{ .index = 0, .file = 0 }; + sym.esym_idx = esym_index; + sym.file = self.index; + + const idx = self.symbols_resolver.items[i]; + elf_file.resolver.values.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 (0..self.getGlobals().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.getSymbol(ref) orelse continue; + const file = sym.getFile(elf_file).?; + const should_keep = esym.st_shndx == elf.SHN_UNDEF or + (esym.st_shndx == elf.SHN_COMMON and sym.getElfSym(elf_file).st_shndx != elf.SHN_COMMON); if (should_keep and !file.isAlive()) { file.setAlive(); file.markLive(elf_file); @@ -576,34 +619,56 @@ pub fn markLive(self: *Object, elf_file: *Elf) void { } } -pub fn checkDuplicates(self: *Object, elf_file: *Elf) bool { - const first_global = self.first_global orelse return false; - var has_dupes = false; - for (self.getGlobals(), 0..) |index, i| { - const sym_idx = @as(Symbol.Index, @intCast(first_global + i)); - const this_sym = self.symtab.items[sym_idx]; - const global = elf_file.getSymbol(index); - const global_file = global.getFile(elf_file) orelse continue; - - if (self.index == global_file.getIndex() or - this_sym.st_shndx == elf.SHN_UNDEF or - this_sym.st_bind() == elf.STB_WEAK or - this_sym.st_shndx == elf.SHN_COMMON) continue; - - if (this_sym.st_shndx != elf.SHN_ABS) { - const atom_index = self.atoms_indexes.items[this_sym.st_shndx]; - const atom = self.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; +pub fn markImportsExports(self: *Object, elf_file: *Elf) void { + const first_global = self.first_global orelse return; + for (0..self.getGlobals().len) |i| { + const idx = first_global + i; + const ref = self.resolveSymbol(@intCast(idx), elf_file); + const sym = elf_file.getSymbol(ref) orelse continue; + const file = sym.getFile(elf_file).?; + if (sym.ver_idx == elf.VER_NDX_LOCAL) continue; + const vis = @as(elf.STV, @enumFromInt(sym.getElfSym(elf_file).st_other)); + if (vis == .HIDDEN) continue; + if (file == .shared and !sym.isAbs(elf_file)) { + sym.flags.import = true; + continue; + } + if (file.getIndex() == self.index) { + sym.flags.@"export" = true; + if (elf_file.options.shared and vis != .PROTECTED) { + sym.flags.import = true; + } } + } +} - elf_file.base.fatal("multiple definition: {}: {}: {s}", .{ - self.fmtPath(), - global_file.fmtPath(), - global.getName(elf_file), - }); - has_dupes = true; +pub fn checkDuplicates(self: *Object, elf_file: *Elf) error{OutOfMemory}!void { + const gpa = elf_file.base.allocator; + const first_global = self.first_global orelse return; + for (0..self.getGlobals().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.getSymbol(ref) orelse continue; + const ref_file = ref_sym.getFile(elf_file).?; + + if (self.index == ref_file.getIndex() 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.getAtom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + } + + const gop = try elf_file.dupes.getOrPut(gpa, self.symbols_resolver.items[i]); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(gpa, self.index); } - return has_dupes; } pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { @@ -722,9 +787,7 @@ 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.getSymbol(sym_index); - + const sym = &self.symbols.items[idx]; if (esym.st_shndx == elf.SHN_COMMON or esym.st_shndx == elf.SHN_UNDEF or esym.st_shndx == elf.SHN_ABS) continue; const imsec_index = self.merge_sections_indexes.items[esym.st_shndx]; @@ -739,7 +802,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { return error.ParseFailed; }; - try sym.addExtra(.{ .subsection = msub_index }, elf_file); + sym.ref = .{ .index = msub_index, .file = imsec.merge_section }; sym.flags.merge_subsection = true; sym.value = offset; } @@ -775,17 +838,17 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { msub_index, }); defer gpa.free(name); - const sym_index = try elf_file.addSymbol(); - const sym = elf_file.getSymbol(sym_index); + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; sym.* = .{ .value = @bitCast(@as(i64, @intCast(offset)) - rel.r_addend), - .name = try elf_file.string_intern.insert(gpa, name), - .sym_idx = rel.r_sym(), + .name = try self.addString(gpa, name), + .esym_idx = rel.r_sym(), .file = self.index, + .extra = try self.addSymbolExtra(gpa, .{}), }; - try sym.addExtra(.{ .subsection = msub_index }, elf_file); + sym.ref = .{ .index = msub_index, .file = imsec.merge_section }; sym.flags.merge_subsection = true; - self.symbols.addOneAssumeCapacity().* = sym_index; rel.r_info = (out_sym_idx << 32) | rel.r_type(); } } @@ -795,25 +858,23 @@ 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.getGlobals(), 0..) |index, i| { + for (self.getGlobals(), self.symbols_resolver.items, 0..) |*sym, resolv, i| { const esym_idx = @as(Symbol.Index, @intCast(first_global + i)); const esym = self.symtab.items[esym_idx]; if (esym.st_shndx != elf.SHN_COMMON) continue; - const global = elf_file.getSymbol(index); - const global_file = global.getFile(elf_file).?; - if (global_file.getIndex() != self.index) { + if (elf_file.resolver.get(resolv).?.file != self.index) { if (elf_file.options.warn_common) { elf_file.base.warn("{}: multiple common symbols: {s}", .{ self.fmtPath(), - global.getName(elf_file), + self.getString(esym.st_name), }); } continue; } const gpa = elf_file.base.allocator; - const is_tls = global.getType(elf_file) == elf.STT_TLS; + const is_tls = sym.getType(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}); @@ -844,9 +905,9 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { }); try self.atoms_indexes.append(gpa, atom_index); - global.value = 0; - global.atom_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; } } @@ -894,33 +955,32 @@ pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { if (!elf_file.options.discard_all_locals) { // TODO: discard temp locals - for (self.getLocals()) |local_index| { - const local = elf_file.getSymbol(local_index); + for (self.getLocals()) |*local| { if (!isAlive(local, elf_file)) continue; - const s_sym = local.getSourceSymbol(elf_file); - switch (s_sym.st_type()) { + const esym = local.getElfSym(elf_file); + switch (esym.st_type()) { elf.STT_SECTION => continue, - elf.STT_NOTYPE => if (s_sym.st_shndx == elf.SHN_UNDEF) continue, + elf.STT_NOTYPE => if (esym.st_shndx == elf.SHN_UNDEF) continue, 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.getName(elf_file).len + 1)); } } - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.getGlobals(), self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != 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.getName(elf_file).len + 1)); @@ -930,8 +990,7 @@ pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { pub fn writeSymtab(self: Object, elf_file: *Elf) void { if (elf_file.options.strip_all) return; - for (self.getLocals()) |local_index| { - const local = elf_file.getSymbol(local_index); + for (self.getLocals()) |local| { const idx = local.getOutputSymtabIndex(elf_file) orelse continue; const out_sym = &elf_file.symtab.items[idx]; out_sym.st_name = @intCast(elf_file.strtab.items.len); @@ -940,10 +999,10 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void { local.setOutputSym(elf_file, out_sym); } - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.getGlobals(), self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.values.items[resolv - 1]; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != self.index) continue; const idx = global.getOutputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.getName(elf_file)); @@ -954,18 +1013,79 @@ pub fn writeSymtab(self: Object, elf_file: *Elf) void { } } -pub fn getLocals(self: Object) []const Symbol.Index { +pub fn getLocals(self: Object) []Symbol { + if (self.symbols.items.len == 0) return &[0]Symbol{}; + assert(self.symbols.items.len >= self.symtab.items.len); const end = self.first_global orelse self.symtab.items.len; return self.symbols.items[0..end]; } -pub fn getGlobals(self: Object) []const Symbol.Index { - const start = self.first_global orelse return &[0]Symbol.Index{}; +pub fn getGlobals(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; return self.symbols.items[start..self.symtab.items.len]; } -pub inline fn getSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) *Symbol { - return elf_file.getSymbol(self.symbols.items[index]); +pub fn resolveSymbol(self: Object, index: Symbol.Index, elf_file: *Elf) Elf.Ref { + const start = self.first_global orelse self.symtab.items.len; + const end = self.symtab.items.len; + if (index < start or index >= end) return .{ .index = index, .file = self.index }; + const resolv = self.symbols_resolver.items[index - start]; + return elf_file.resolver.get(resolv).?; +} + +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 = self.index }); + return index; +} + +pub fn addSymbolExtra(self: *Object, 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: *Object, 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 getSymbolExtra(self: *Object, 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: *Object, 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 inline fn getShdrs(self: Object) []const elf.Elf64_Shdr { @@ -1136,15 +1256,20 @@ fn formatSymtab( _ = unused_fmt_string; _ = options; const object = ctx.object; + const elf_file = ctx.elf_file; try writer.writeAll(" locals\n"); - for (object.getLocals()) |index| { - const local = ctx.elf_file.getSymbol(index); - try writer.print(" {}\n", .{local.fmt(ctx.elf_file)}); + for (object.getLocals()) |local| { + try writer.print(" {}\n", .{local.fmt(elf_file)}); } try writer.writeAll(" globals\n"); - for (object.getGlobals()) |index| { - const global = ctx.elf_file.getSymbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (object.getGlobals(), 0..) |global, i| { + const first_global = object.first_global.?; + const ref = object.resolveSymbol(@intCast(i + first_global), elf_file); + if (elf_file.getSymbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{global.getName(elf_file)}); + } } } diff --git a/src/Elf/SharedObject.zig b/src/Elf/SharedObject.zig index 79a0ff99..d9d4e3d3 100644 --- a/src/Elf/SharedObject.zig +++ b/src/Elf/SharedObject.zig @@ -3,15 +3,19 @@ index: File.Index, header: ?elf.Elf64_Ehdr = null, shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, + symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, /// Version symtab contains version strings of the symbols if present. versyms: std.ArrayListUnmanaged(elf.Elf64_Versym) = .{}, verstrings: std.ArrayListUnmanaged(u32) = .{}, -symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, +symbols: std.ArrayListUnmanaged(Symbol) = .empty, +symbols_extra: std.ArrayListUnmanaged(u32) = .empty, +symbols_resolver: std.ArrayListUnmanaged(Elf.SymbolResolver.Index) = .empty, + aliases: ?std.ArrayListUnmanaged(u32) = null, -dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .{}, +dynamic_table: std.ArrayListUnmanaged(elf.Elf64_Dyn) = .empty, needed: bool, alive: bool, @@ -42,6 +46,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); } @@ -70,32 +76,18 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, file: std.fs.File) !void { const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(shdrs_buffer.ptr))[0..self.header.?.e_shnum]; try self.shdrs.appendUnalignedSlice(gpa, shdrs); - var dynsym_index: ?u32 = null; + var dynsym_sect_index: ?u32 = null; var dynamic_sect_index: ?u32 = null; var versym_sect_index: ?u32 = null; var verdef_sect_index: ?u32 = null; for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { - elf.SHT_DYNSYM => dynsym_index = @as(u32, @intCast(i)), + elf.SHT_DYNSYM => dynsym_sect_index = @as(u32, @intCast(i)), elf.SHT_DYNAMIC => dynamic_sect_index = @as(u32, @intCast(i)), elf.SHT_GNU_VERSYM => versym_sect_index = @as(u32, @intCast(i)), elf.SHT_GNU_VERDEF => verdef_sect_index = @as(u32, @intCast(i)), else => {}, }; - if (dynsym_index) |index| { - const symtab_shdr = self.shdrs.items[index]; - const symtab_buffer = try Elf.preadAllAlloc(gpa, file, symtab_shdr.sh_offset, symtab_shdr.sh_size); - defer gpa.free(symtab_buffer); - const nsyms = @divExact(symtab_buffer.len, @sizeOf(elf.Elf64_Sym)); - const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab_buffer.ptr))[0..nsyms]; - try self.symtab.appendUnalignedSlice(gpa, symtab); - - const strtab_shdr = self.shdrs.items[symtab_shdr.sh_link]; - const strtab = try Elf.preadAllAlloc(gpa, file, strtab_shdr.sh_offset, strtab_shdr.sh_size); - defer gpa.free(strtab); - try self.strtab.appendSlice(gpa, strtab); - } - if (dynamic_sect_index) |index| { const shdr = self.shdrs.items[index]; const raw = try Elf.preadAllAlloc(gpa, file, shdr.sh_offset, shdr.sh_size); @@ -105,14 +97,35 @@ pub fn parse(self: *SharedObject, elf_file: *Elf, file: std.fs.File) !void { try self.dynamic_table.appendUnalignedSlice(gpa, dyntab); } + const symtab = if (dynsym_sect_index) |index| blk: { + const shdr = self.shdrs.items[index]; + const buffer = try Elf.preadAllAlloc(gpa, file, shdr.sh_offset, shdr.sh_size); + const nsyms = @divExact(buffer.len, @sizeOf(elf.Elf64_Sym)); + break :blk @as([*]align(1) const elf.Elf64_Sym, @ptrCast(buffer.ptr))[0..nsyms]; + } else &[0]elf.Elf64_Sym{}; + defer gpa.free(symtab); + + const strtab = if (dynsym_sect_index) |index| blk: { + const symtab_shdr = self.shdrs.items[index]; + const shdr = self.shdrs.items[symtab_shdr.sh_link]; + const buffer = try Elf.preadAllAlloc(gpa, file, shdr.sh_offset, shdr.sh_size); + break :blk buffer; + } else &[0]u8{}; + defer gpa.free(strtab); + try self.parseVersions(elf_file, file, .{ + .symtab = symtab, .versym_sect_index = versym_sect_index, .verdef_sect_index = verdef_sect_index, }); - try self.initSymtab(elf_file); + try self.initSymbols(elf_file, .{ + .symtab = symtab, + .strtab = strtab, + }); } fn parseVersions(self: *SharedObject, elf_file: *Elf, file: std.fs.File, opts: struct { + symtab: []align(1) const elf.Elf64_Sym, verdef_sect_index: ?u32, versym_sect_index: ?u32, }) !void { @@ -146,7 +159,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf, file: std.fs.File, opts: s } } - try self.versyms.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); + try self.versyms.ensureTotalCapacityPrecise(gpa, opts.symtab.len); if (opts.versym_sect_index) |shndx| { const shdr = self.shdrs.items[shndx]; @@ -161,50 +174,81 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf, file: std.fs.File, opts: s ver; self.versyms.appendAssumeCapacity(normalized_ver); } - } else for (0..self.symtab.items.len) |_| { + } else for (0..opts.symtab.len) |_| { self.versyms.appendAssumeCapacity(elf.VER_NDX_GLOBAL); } } -fn initSymtab(self: *SharedObject, elf_file: *Elf) !void { +fn initSymbols(self: *SharedObject, elf_file: *Elf, opts: struct { + symtab: []align(1) const elf.Elf64_Sym, + strtab: []const u8, +}) !void { const tracy = trace(@src()); defer tracy.end(); const gpa = elf_file.base.allocator; + const nsyms = opts.symtab.len; - try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); + try self.strtab.appendSlice(gpa, opts.strtab); + 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 (self.symtab.items, 0..) |sym, i| { + for (opts.symtab, 0..) |sym, i| { const hidden = self.versyms.items[i] & elf.VERSYM_HIDDEN != 0; const name = self.getString(sym.st_name); // We need to garble up the name so that we don't pick this symbol // during symbol resolution. Thank you GNU! - const off = if (hidden) try elf_file.internString("{s}@{s}", .{ - name, - self.getVersionString(self.versyms.items[i]), - }) else try elf_file.internString("{s}", .{name}); - const gop = try elf_file.getOrCreateGlobal(off); - self.symbols.addOneAssumeCapacity().* = gop.index; + const name_off = if (hidden) blk: { + const mangled = try std.fmt.allocPrint(gpa, "{s}@{s}", .{ + name, + self.getVersionString(self.versyms.items[i]), + }); + defer gpa.free(mangled); + break :blk try self.addString(gpa, mangled); + } else sym.st_name; + 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); + out_sym.name = name_off; + out_sym.ref = .{ .index = 0, .file = 0 }; + out_sym.esym_idx = out_esym_index; + out_sym.ver_idx = self.versyms.items[out_esym_index]; + out_sym.extra = self.addSymbolExtraAssumeCapacity(.{}); } } -pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { +pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.getGlobals(), 0..) |index, i| { - const sym_idx = @as(Symbol.Index, @intCast(i)); - const this_sym = self.symtab.items[sym_idx]; + const gpa = elf_file.base.allocator; - 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; + + if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (elf_file.getSymbol(gop.ref.*) == null) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; + continue; + } - const global = elf_file.getSymbol(index); - if (self.asFile().getSymbolRank(this_sym, false) < global.getSymbolRank(elf_file)) { - global.value = @intCast(this_sym.st_value); - global.atom_ref = .{}; - global.sym_idx = sym_idx; - global.ver_idx = self.versyms.items[sym_idx]; - global.file = self.index; + if (self.asFile().getSymbolRank(esym, false) < elf_file.getSymbol(gop.ref.*).?.getSymbolRank(elf_file)) { + gop.ref.* = .{ .index = @intCast(i), .file = self.index }; } } } @@ -213,14 +257,14 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void { const tracy = trace(@src()); defer tracy.end(); - for (self.symbols.items, 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.getSymbol(index); - const file = global.getFile(elf_file) orelse continue; + const ref = self.resolveSymbol(@intCast(i), elf_file); + const sym = elf_file.getSymbol(ref) orelse continue; + const file = sym.getFile(elf_file).?; const should_drop = switch (file) { - .shared => |sh| !sh.needed and sym.st_bind() == elf.STB_WEAK, + .shared => |sh| !sh.needed and esym.st_bind() == elf.STB_WEAK, else => false, }; if (!should_drop and !file.isAlive()) { @@ -230,46 +274,26 @@ pub fn markLive(self: *SharedObject, elf_file: *Elf) void { } } -pub inline fn getString(self: *SharedObject, off: u32) [:0]const u8 { - assert(off < self.strtab.items.len); - return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.strtab.items.ptr + off)), 0); -} - -pub inline fn getVersionString(self: *SharedObject, index: elf.Elf64_Versym) [:0]const u8 { - const off = self.verstrings.items[index & elf.VERSYM_VERSION]; - return self.getString(off); -} - -pub fn asFile(self: *SharedObject) File { - return .{ .shared = self }; -} - -fn getVerdefNum(self: *SharedObject) u32 { - for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), - else => {}, - }; - return 0; -} - -pub fn getSoname(self: *SharedObject) []const u8 { - for (self.dynamic_table.items) |entry| switch (entry.d_tag) { - elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), - else => {}, - }; - return std.fs.path.basename(self.path); +pub fn markImportsExports(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.getSymbol(ref) orelse continue; + const ref_file = ref_sym.getFile(elf_file).?; + const vis = @as(elf.STV, @enumFromInt(ref_sym.getElfSym(elf_file).st_other)); + if (ref_file != .shared and vis != .HIDDEN) ref_sym.flags.@"export" = true; + } } pub fn calcSymtabSize(self: *SharedObject, elf_file: *Elf) !void { if (elf_file.options.strip_all) return; - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.symbols.items, self.symbols_resolver.items) |*global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != 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.getName(elf_file).len + 1)); } @@ -278,10 +302,10 @@ pub fn calcSymtabSize(self: *SharedObject, elf_file: *Elf) !void { pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void { if (elf_file.options.strip_all) return; - for (self.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const file_ptr = global.getFile(elf_file) orelse continue; - if (file_ptr.getIndex() != self.index) continue; + for (self.symbols.items, self.symbols_resolver.items) |global, resolv| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != self.index) continue; const idx = global.getOutputSymtabIndex(elf_file) orelse continue; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(global.getName(elf_file)); @@ -292,8 +316,29 @@ pub fn writeSymtab(self: SharedObject, elf_file: *Elf) void { } } -pub inline fn getGlobals(self: SharedObject) []const Symbol.Index { - return self.symbols.items; +pub fn asFile(self: *SharedObject) File { + return .{ .shared = self }; +} + +pub fn getVersionString(self: *SharedObject, index: elf.Elf64_Versym) [:0]const u8 { + const off = self.verstrings.items[index & elf.VERSYM_VERSION]; + return self.getString(off); +} + +fn getVerdefNum(self: *SharedObject) u32 { + for (self.dynamic_table.items) |entry| switch (entry.d_tag) { + elf.DT_VERDEFNUM => return @as(u32, @intCast(entry.d_val)), + else => {}, + }; + return 0; +} + +pub fn getSoname(self: *SharedObject) []const u8 { + for (self.dynamic_table.items) |entry| switch (entry.d_tag) { + elf.DT_SONAME => return self.getString(@as(u32, @intCast(entry.d_val))), + else => {}, + }; + return std.fs.path.basename(self.path); } pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { @@ -303,9 +348,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.getSymbol(lhs).getSourceSymbol(ctx); - const rhs_sym = ctx.getSymbol(rhs).getSourceSymbol(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].getElfSym(ctx.ef); + const rhs_sym = ctx.so.symbols.items[rhs].getElfSym(ctx.ef); return lhs_sym.st_value < rhs_sym.st_value; } }; @@ -313,16 +361,16 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; var aliases = std.ArrayList(Symbol.Index).init(gpa); defer aliases.deinit(); - try aliases.ensureTotalCapacityPrecise(self.getGlobals().len); + try aliases.ensureTotalCapacityPrecise(self.symbols.items.len); - for (self.getGlobals()) |index| { - const global = elf_file.getSymbol(index); - const global_file = global.getFile(elf_file) orelse continue; - if (global_file.getIndex() != self.index) continue; - aliases.appendAssumeCapacity(index); + for (self.symbols_resolver.items, 0..) |resolv, index| { + const ref = elf_file.resolver.get(resolv).?; + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != self.index) continue; + aliases.appendAssumeCapacity(@intCast(index)); } - std.mem.sort(Symbol.Index, aliases.items, elf_file, SortAlias.lessThan); + std.mem.sort(u32, aliases.items, SortAlias{ .so = self, .ef = elf_file }, SortAlias.lessThan); self.aliases = aliases.moveToUnmanaged(); } @@ -330,22 +378,93 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { pub fn getSymbolAliases(self: *SharedObject, index: u32, elf_file: *Elf) []const u32 { assert(self.aliases != null); - const symbol = elf_file.getSymbol(index).getSourceSymbol(elf_file); + const symbol = self.symbols.items[index].getElfSym(elf_file); const aliases = self.aliases.?; const start = for (aliases.items, 0..) |alias, i| { - const alias_sym = elf_file.getSymbol(alias).getSourceSymbol(elf_file); + const alias_sym = self.symbols.items[alias].getElfSym(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.getSymbol(alias).getSourceSymbol(elf_file); + const alias_sym = self.symbols.items[alias].getElfSym(elf_file); if (symbol.st_value < alias_sym.st_value) break i + start; } else aliases.items.len; return aliases.items[start..end]; } +fn addString(self: *SharedObject, allocator: Allocator, str: []const u8) !u32 { + const off: u32 = @intCast(self.strtab.items.len); + try self.strtab.ensureUnusedCapacity(allocator, str.len + 1); + self.strtab.appendSliceAssumeCapacity(str); + self.strtab.appendAssumeCapacity(0); + return off; +} + +pub fn getString(self: SharedObject, off: u32) [:0]const u8 { + assert(off < self.strtab.items.len); + 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).?; +} + +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 = 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 getSymbolExtra(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, @@ -380,10 +499,15 @@ 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.getSymbol(index); - try writer.print(" {}\n", .{global.fmt(ctx.elf_file)}); + for (shared.symbols.items, 0..) |sym, i| { + const ref = shared.resolveSymbol(@intCast(i), elf_file); + if (elf_file.getSymbol(ref)) |ref_sym| { + try writer.print(" {}\n", .{ref_sym.fmt(elf_file)}); + } else { + try writer.print(" {s} : unclaimed\n", .{sym.getName(elf_file)}); + } } } diff --git a/src/Elf/Symbol.zig b/src/Elf/Symbol.zig index 42ad0e1d..9e8107e5 100644 --- a/src/Elf/Symbol.zig +++ b/src/Elf/Symbol.zig @@ -11,14 +11,14 @@ file: File.Index = 0, /// Atom containing this symbol if any. /// Use `getAtom` to get the pointer to the atom. -atom_ref: Elf.Ref = .{}, +ref: Elf.Ref = .{}, /// Assigned output section index for this symbol. shndx: u32 = 0, /// Index of the source symbol this symbol references. -/// Use `getSourceSymbol` to pull the source symbol from the relevant file. -sym_idx: Index = 0, +/// Use `getElfSym` to pull the source symbol from the relevant file. +esym_idx: Index = 0, /// Index of the source version symbol this symbol references if any. /// If the symbol is unversioned it will have either VER_NDX_LOCAL or VER_NDX_GLOBAL. @@ -31,54 +31,66 @@ extra: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file = symbol.getFile(elf_file).?; - if (file == .shared) return symbol.getSourceSymbol(elf_file).st_shndx == elf.SHN_ABS; - return !symbol.flags.import and symbol.getAtom(elf_file) == null and symbol.getMergeSubsection(elf_file) == null and symbol.shndx == 0 and file != .internal; + if (file == .shared) return symbol.getElfSym(elf_file).st_shndx == elf.SHN_ABS; + return !symbol.flags.import and symbol.getAtom(elf_file) == null and symbol.getMergeSubsection(elf_file) == null and symbol.getShndx(elf_file) == 0 and file != .internal; } pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { - if (elf_file.options.relocatable) return symbol.getSourceSymbol(elf_file).st_bind() == elf.STB_LOCAL; + if (elf_file.options.relocatable) return symbol.getElfSym(elf_file).st_bind() == elf.STB_LOCAL; return !(symbol.flags.import or symbol.flags.@"export"); } -pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { +pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool { return symbol.getType(elf_file) == elf.STT_GNU_IFUNC; } pub fn getType(symbol: Symbol, elf_file: *Elf) u4 { const file = symbol.getFile(elf_file).?; - const s_sym = symbol.getSourceSymbol(elf_file); + const s_sym = symbol.getElfSym(elf_file); if (s_sym.st_type() == elf.STT_GNU_IFUNC and file == .shared) return elf.STT_FUNC; return s_sym.st_type(); } pub fn getName(symbol: Symbol, elf_file: *Elf) [:0]const u8 { - return elf_file.string_intern.getAssumeExists(symbol.name); + return switch (symbol.getFile(elf_file).?) { + inline else => |x| x.getString(symbol.name), + }; +} + +pub fn getShndx(symbol: Symbol, elf_file: *Elf) ?u32 { + if (symbol.getMergeSubsection(elf_file)) |msub| + return if (msub.alive) msub.getMergeSection(elf_file).out_shndx else null; + if (symbol.getAtom(elf_file)) |atom_ptr| + return if (atom_ptr.flags.alive) atom_ptr.out_shndx else null; + if (symbol.shndx == 0) return null; + return symbol.shndx; } pub fn getAtom(symbol: Symbol, elf_file: *Elf) ?*Atom { - return elf_file.getAtom(symbol.atom_ref); + if (symbol.flags.merge_subsection) return null; + return elf_file.getAtom(symbol.ref); } pub fn getMergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection { if (!symbol.flags.merge_subsection) return null; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); return elf_file.getMergeSubsection(extra.subsection); } -pub inline fn getFile(symbol: Symbol, elf_file: *Elf) ?File { +pub fn getFile(symbol: Symbol, elf_file: *Elf) ?File { return elf_file.getFile(symbol.file); } -pub fn getSourceSymbol(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { +pub fn getElfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym { const file = symbol.getFile(elf_file).?; return switch (file) { - inline else => |x| x.symtab.items[symbol.sym_idx], + inline else => |x| x.symtab.items[symbol.esym_idx], }; } pub fn getSymbolRank(symbol: Symbol, elf_file: *Elf) u32 { const file = symbol.getFile(elf_file) orelse return std.math.maxInt(u32); - const sym = symbol.getSourceSymbol(elf_file); + const sym = symbol.getElfSym(elf_file); const in_archive = switch (file) { .object => |x| !x.alive, else => false, @@ -116,7 +128,7 @@ pub fn getAddress(symbol: Symbol, opts: struct { if (mem.startsWith(u8, sym_name, "__EH_FRAME_BEGIN__") or mem.startsWith(u8, sym_name, "__EH_FRAME_LIST__") or mem.startsWith(u8, sym_name, ".eh_frame_seg") or - symbol.getSourceSymbol(elf_file).st_type() == elf.STT_SECTION) + symbol.getElfSym(elf_file).st_type() == elf.STT_SECTION) { return @intCast(sh_addr); } @@ -143,20 +155,20 @@ pub fn getOutputSymtabIndex(symbol: Symbol, elf_file: *Elf) ?u32 { const symtab_ctx = switch (file) { inline else => |x| x.output_symtab_ctx, }; - const idx = symbol.getExtra(elf_file).?.symtab; + const idx = symbol.getExtra(elf_file).symtab; return if (symbol.isLocal(elf_file)) idx + symtab_ctx.ilocal else idx + symtab_ctx.iglobal; } pub fn getGotAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.got) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const entry = elf_file.got.entries.items[extra.got]; return entry.getAddress(elf_file); } pub fn getPltGotAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!(symbol.flags.plt and symbol.flags.got)) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const shdr = elf_file.sections.items(.shdr)[elf_file.plt_got_sect_index.?]; const cpu_arch = elf_file.options.cpu_arch.?; return @intCast(shdr.sh_addr + extra.plt_got * PltGotSection.entrySize(cpu_arch)); @@ -164,7 +176,7 @@ pub fn getPltGotAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn getPltAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.plt) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const shdr = elf_file.sections.items(.shdr)[elf_file.plt_sect_index.?]; const cpu_arch = elf_file.options.cpu_arch.?; return @intCast(shdr.sh_addr + extra.plt * PltSection.entrySize(cpu_arch) + PltSection.preambleSize(cpu_arch)); @@ -172,7 +184,7 @@ pub fn getPltAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn getGotPltAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.plt) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const shdr = elf_file.sections.items(.shdr)[elf_file.got_plt_sect_index.?]; return @intCast(shdr.sh_addr + extra.plt * 8 + GotPltSection.preamble_size); } @@ -185,21 +197,21 @@ pub fn getCopyRelAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn getTlsGdAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.tlsgd) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const entry = elf_file.got.entries.items[extra.tlsgd]; return entry.getAddress(elf_file); } pub fn getGotTpAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.gottp) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const entry = elf_file.got.entries.items[extra.gottp]; return entry.getAddress(elf_file); } pub fn getTlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 { if (!symbol.flags.tlsdesc) return 0; - const extra = symbol.getExtra(elf_file).?; + const extra = symbol.getExtra(elf_file); const entry = elf_file.got.entries.items[extra.tlsdesc]; return entry.getAddress(elf_file); } @@ -207,7 +219,7 @@ pub fn getTlsDescAddress(symbol: Symbol, elf_file: *Elf) i64 { pub fn getAlignment(symbol: Symbol, elf_file: *Elf) !u64 { const file = symbol.getFile(elf_file) orelse return 0; const shared = file.shared; - const s_sym = symbol.getSourceSymbol(elf_file); + const s_sym = symbol.getElfSym(elf_file); const shdr = shared.shdrs.items[s_sym.st_shndx]; const alignment = @max(1, shdr.sh_addralign); return if (s_sym.st_value == 0) @@ -229,11 +241,8 @@ const AddExtraOpts = struct { subsection: ?u32 = null, }; -pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void { - if (symbol.getExtra(elf_file) == null) { - symbol.extra = try elf_file.addSymbolExtra(.{}); - } - var extra = symbol.getExtra(elf_file).?; +pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { + var extra = symbol.getExtra(elf_file); inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { if (@field(opts, field.name)) |x| { @field(extra, field.name) = x; @@ -242,17 +251,21 @@ pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) !void { symbol.setExtra(extra, elf_file); } -pub inline fn getExtra(symbol: Symbol, elf_file: *Elf) ?Extra { - return elf_file.getSymbolExtra(symbol.extra); +pub fn getExtra(symbol: Symbol, elf_file: *Elf) Extra { + return switch (symbol.getFile(elf_file).?) { + inline else => |x| x.getSymbolExtra(symbol.extra), + }; } -pub inline fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void { - elf_file.setSymbolExtra(symbol.extra, extra); +pub fn setExtra(symbol: Symbol, extra: Extra, elf_file: *Elf) void { + return switch (symbol.getFile(elf_file).?) { + inline else => |x| x.setSymbolExtra(symbol.extra, extra), + }; } pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const file = symbol.getFile(elf_file).?; - const s_sym = symbol.getSourceSymbol(elf_file); + const s_sym = symbol.getElfSym(elf_file); const st_type = symbol.getType(elf_file); const st_bind: u8 = blk: { if (symbol.isLocal(elf_file)) break :blk 0; @@ -260,13 +273,13 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { if (file == .shared) break :blk elf.STB_GLOBAL; break :blk s_sym.st_bind(); }; - const st_shndx = blk: { - if (symbol.flags.copy_rel) break :blk elf_file.copy_rel_sect_index.?; + const st_shndx: u16 = blk: { + if (symbol.flags.copy_rel) break :blk @intCast(elf_file.copy_rel_sect_index.?); if (file == .shared or s_sym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; if (elf_file.options.relocatable and s_sym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; - if (symbol.getMergeSubsection(elf_file)) |msub| break :blk msub.getMergeSection(elf_file).out_shndx; + if (symbol.getMergeSubsection(elf_file)) |msub| break :blk @intCast(msub.getMergeSection(elf_file).out_shndx); if (symbol.getAtom(elf_file) == null and file != .internal) break :blk elf.SHN_ABS; - break :blk symbol.shndx; + break :blk @intCast(symbol.getShndx(elf_file) orelse elf.SHN_UNDEF); }; const st_value = blk: { if (symbol.flags.copy_rel) break :blk symbol.getAddress(.{}, elf_file); @@ -347,16 +360,16 @@ fn format2( _ = options; _ = unused_fmt_string; const symbol = ctx.symbol; - try writer.print("%{d} : {s} : @{x}", .{ symbol.sym_idx, symbol.fmtName(ctx.elf_file), symbol.getAddress(.{}, ctx.elf_file) }); + try writer.print("%{d} : {s} : @{x}", .{ symbol.esym_idx, symbol.fmtName(ctx.elf_file), symbol.getAddress(.{}, ctx.elf_file) }); if (symbol.getFile(ctx.elf_file)) |file| { if (symbol.isAbs(ctx.elf_file)) { - if (symbol.getSourceSymbol(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { + if (symbol.getElfSym(ctx.elf_file).st_shndx == elf.SHN_UNDEF) { try writer.writeAll(" : undef"); } else { try writer.writeAll(" : absolute"); } - } else if (symbol.shndx != 0) { - try writer.print(" : sect({d})", .{symbol.shndx}); + } else if (symbol.getShndx(ctx.elf_file)) |shndx| { + try writer.print(" : sect({d})", .{shndx}); } if (symbol.getAtom(ctx.elf_file)) |atom| { try writer.print(" : atom({d})", .{atom.atom_index}); diff --git a/src/Elf/Thunk.zig b/src/Elf/Thunk.zig index 380dd202..3315a1ca 100644 --- a/src/Elf/Thunk.zig +++ b/src/Elf/Thunk.zig @@ -1,6 +1,6 @@ value: i64 = 0, out_shndx: u32 = 0, -symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, +symbols: std.AutoArrayHashMapUnmanaged(Elf.Ref, void) = .empty, output_symtab_ctx: Elf.SymtabCtx = .{}, pub fn deinit(thunk: *Thunk, allocator: Allocator) void { @@ -17,9 +17,9 @@ pub fn getAddress(thunk: Thunk, elf_file: *Elf) i64 { return @as(i64, @intCast(shdr.sh_addr)) + thunk.value; } -pub fn getTargetAddress(thunk: Thunk, sym_index: Symbol.Index, elf_file: *Elf) i64 { +pub fn getTargetAddress(thunk: Thunk, ref: Elf.Ref, elf_file: *Elf) i64 { const cpu_arch = elf_file.options.cpu_arch.?; - return thunk.getAddress(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(sym_index).? * trampolineSize(cpu_arch))); + return thunk.getAddress(elf_file) + @as(i64, @intCast(thunk.symbols.getIndex(ref).? * trampolineSize(cpu_arch))); } pub fn isReachable(atom: *const Atom, rel: elf.Elf64_Rela, elf_file: *Elf) bool { @@ -52,8 +52,8 @@ pub fn calcSymtabSize(thunk: *Thunk, elf_file: *Elf) void { if (elf_file.options.strip_all) return; thunk.output_symtab_ctx.nlocals = @as(u32, @intCast(thunk.symbols.keys().len)); - for (thunk.symbols.keys()) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (thunk.symbols.keys()) |ref| { + const sym = elf_file.getSymbol(ref).?; thunk.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(elf_file).len + "$thunk".len + 1)); } } @@ -62,8 +62,8 @@ pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { if (elf_file.options.strip_all) return; const cpu_arch = elf_file.options.cpu_arch.?; - for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.getSymbol(sym_index); + for (thunk.symbols.keys(), thunk.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.getSymbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.getName(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$thunk"); @@ -73,7 +73,7 @@ pub fn writeSymtab(thunk: Thunk, elf_file: *Elf) void { .st_info = elf.STT_FUNC, .st_other = 0, .st_shndx = @intCast(thunk.out_shndx), - .st_value = @intCast(thunk.getTargetAddress(sym_index, elf_file)), + .st_value = @intCast(thunk.getTargetAddress(ref, elf_file)), .st_size = trampolineSize(cpu_arch), }; } @@ -123,9 +123,9 @@ fn format2( 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.getSymbol(index); - try writer.print(" %{d} : {s} : @{x}\n", .{ index, sym.getName(elf_file), sym.value }); + for (thunk.symbols.keys()) |ref| { + const sym = elf_file.getSymbol(ref).?; + try writer.print(" {} : {s} : @{x}\n", .{ ref, sym.getName(elf_file), sym.value }); } } @@ -136,7 +136,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 object = atom.getObject(elf_file); - const target = object.getSymbol(rel.r_sym(), elf_file); + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.getSymbol(target_ref).?; if (target.flags.plt) return false; if (atom.out_shndx != target.shndx) return false; const target_atom = target.getAtom(elf_file).?; @@ -148,8 +149,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.getSymbol(sym_index); + for (thunk.symbols.keys(), 0..) |ref, i| { + const sym = elf_file.getSymbol(ref).?; const saddr = thunk.getAddress(elf_file) + @as(i64, @intCast(i * trampoline_size)); const taddr = sym.getAddress(.{}, elf_file); const pages = try util.calcNumberOfPages(saddr, taddr); diff --git a/src/Elf/eh_frame.zig b/src/Elf/eh_frame.zig index 5038a2d7..ed5109f1 100644 --- a/src/Elf/eh_frame.zig +++ b/src/Elf/eh_frame.zig @@ -152,8 +152,8 @@ pub const Cie = struct { if (cie_rel.r_type() != other_rel.r_type()) return false; if (cie_rel.r_addend != other_rel.r_addend) return false; - const cie_sym = cie.getObject(elf_file).getSymbol(cie_rel.r_sym(), elf_file); - const other_sym = other.getObject(elf_file).getSymbol(other_rel.r_sym(), elf_file); + const cie_sym = cie.getObject(elf_file).symbols.items[cie_rel.r_sym()]; + const other_sym = other.getObject(elf_file).symbols.items[other_rel.r_sym()]; if (!std.mem.eql(u8, std.mem.asBytes(&cie_sym), std.mem.asBytes(&other_sym))) return false; } return true; @@ -361,7 +361,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { const data = cie.getData(elf_file); for (cie.getRelocs(elf_file)) |rel| { - const sym = object.getSymbol(rel.r_sym(), elf_file); + const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(sym_ref).?; resolveReloc(cie, sym, rel, elf_file, data) catch |err| switch (err) { error.RelocError => has_reloc_errors = true, else => |e| return e, @@ -388,7 +389,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { ); for (fde.getRelocs(elf_file)) |rel| { - const sym = object.getSymbol(rel.r_sym(), elf_file); + const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(sym_ref).?; resolveReloc(fde, sym, rel, elf_file, data) catch |err| switch (err) { error.RelocError => has_reloc_errors = true, else => |e| return e, @@ -437,7 +439,7 @@ pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void { } } -fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela { +fn emitReloc(elf_file: *Elf, rec: anytype, sym: Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela { const tracy = trace(@src()); defer tracy.end(); @@ -449,7 +451,7 @@ fn emitReloc(elf_file: *Elf, rec: anytype, sym: *const Symbol, rel: elf.Elf64_Re switch (sym.getType(elf_file)) { elf.STT_SECTION => { r_addend += @intCast(sym.getAddress(.{}, elf_file)); - r_sym = elf_file.sections.items(.sym_index)[sym.shndx]; + r_sym = elf_file.sections.items(.sym_index)[sym.getShndx(elf_file).?]; }, else => { r_sym = sym.getOutputSymtabIndex(elf_file) orelse 0; @@ -483,7 +485,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { for (object.cies.items) |cie| { if (!cie.alive) continue; for (cie.getRelocs(elf_file)) |rel| { - const sym = object.getSymbol(rel.r_sym(), elf_file); + const sym = object.symbols.items[rel.r_sym()]; const out_rel = emitReloc(elf_file, cie, sym, rel); try writer.writeStruct(out_rel); } @@ -492,7 +494,7 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { for (object.fdes.items) |fde| { if (!fde.alive) continue; for (fde.getRelocs(elf_file)) |rel| { - const sym = object.getSymbol(rel.r_sym(), elf_file); + const sym = object.symbols.items[rel.r_sym()]; const out_rel = emitReloc(elf_file, fde, sym, rel); try writer.writeStruct(out_rel); } @@ -544,7 +546,8 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { const relocs = fde.getRelocs(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 = object.getSymbol(rel.r_sym(), elf_file); + const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(sym_ref).?; const P = @as(i64, @intCast(fde.getAddress(elf_file))); const S = @as(i64, @intCast(sym.getAddress(.{}, elf_file))); const A = rel.r_addend; diff --git a/src/Elf/file.zig b/src/Elf/file.zig index 0b608d8e..cdc33552 100644 --- a/src/Elf/file.zig +++ b/src/Elf/file.zig @@ -28,19 +28,10 @@ pub const File = union(enum) { } } - 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 { - for (file.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - const name = global.name; - global.* = .{}; - global.name = name; - } + }; } pub fn isAlive(file: File) bool { @@ -85,6 +76,54 @@ pub const File = union(enum) { } } + pub fn createSymbolIndirection(file: File, elf_file: *Elf) !void { + const symbols = switch (file) { + inline else => |x| x.symbols.items, + }; + for (symbols, 0..) |*sym, i| { + const ref = file.resolveSymbol(@intCast(i), elf_file); + const ref_sym = elf_file.getSymbol(ref) orelse continue; + if (ref_sym.getFile(elf_file).?.getIndex() != file.getIndex()) continue; + if (!sym.isLocal(elf_file) and !sym.flags.has_dynamic) { + log.debug("'{s}' is non-local", .{sym.getName(elf_file)}); + try elf_file.dynsym.addSymbol(ref, elf_file); + } + if (sym.flags.got) { + log.debug("'{s}' needs GOT", .{sym.getName(elf_file)}); + _ = try elf_file.got.addGotSymbol(ref, elf_file); + } + if (sym.flags.plt) { + if (sym.flags.is_canonical) { + log.debug("'{s}' needs CPLT", .{sym.getName(elf_file)}); + sym.flags.@"export" = true; + try elf_file.plt.addSymbol(ref, elf_file); + } else if (sym.flags.got) { + log.debug("'{s}' needs PLTGOT", .{sym.getName(elf_file)}); + try elf_file.plt_got.addSymbol(ref, elf_file); + } else { + log.debug("'{s}' needs PLT", .{sym.getName(elf_file)}); + try elf_file.plt.addSymbol(ref, elf_file); + } + } + if (sym.flags.copy_rel and !sym.flags.has_copy_rel) { + log.debug("'{s}' needs COPYREL", .{sym.getName(elf_file)}); + try elf_file.copy_rel.addSymbol(ref, elf_file); + } + if (sym.flags.tlsgd) { + log.debug("'{s}' needs TLSGD", .{sym.getName(elf_file)}); + try elf_file.got.addTlsGdSymbol(ref, elf_file); + } + if (sym.flags.gottp) { + log.debug("'{s}' needs GOTTP", .{sym.getName(elf_file)}); + try elf_file.got.addGotTpSymbol(ref, elf_file); + } + if (sym.flags.tlsdesc) { + log.debug("'{s}' needs TLSDESC", .{sym.getName(elf_file)}); + try elf_file.got.addTlsDescSymbol(ref, elf_file); + } + } + } + pub fn getAtom(file: File, ind: Atom.Index) ?*Atom { return switch (file) { .internal, .shared => unreachable, @@ -99,16 +138,21 @@ pub const File = union(enum) { }; } - pub fn getLocals(file: File) []const 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 getSymbol(file: File, ind: Symbol.Index) *Symbol { return switch (file) { - .object => |x| x.getLocals(), - inline else => &[0]Symbol.Index{}, + inline else => |x| &x.symbols.items[ind], }; } - pub fn getGlobals(file: File) []const Symbol.Index { + pub fn getString(file: File, off: u32) [:0]const u8 { return switch (file) { - inline else => |x| x.getGlobals(), + inline else => |x| x.getString(off), }; } @@ -139,6 +183,7 @@ pub const File = union(enum) { const std = @import("std"); const elf = std.elf; +const log = std.log.scoped(.elf); const Allocator = std.mem.Allocator; const Atom = @import("Atom.zig"); diff --git a/src/Elf/gc.zig b/src/Elf/gc.zig index c94ce0b4..faa853a7 100644 --- a/src/Elf/gc.zig +++ b/src/Elf/gc.zig @@ -13,17 +13,20 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); - if (elf_file.entry_index) |index| { - const global = elf_file.getSymbol(index); - try markSymbol(global, roots, elf_file); + if (elf_file.getInternalObject()) |obj| { + if (obj.getEntrySymbol(elf_file)) |sym| { + try markSymbol(sym, roots, elf_file); + } } for (elf_file.objects.items) |index| { - for (elf_file.getFile(index).?.object.getGlobals()) |global_index| { - const global = elf_file.getSymbol(global_index); - if (global.getFile(elf_file)) |file| { - if (file.getIndex() == index and global.flags.@"export") - try markSymbol(global, roots, elf_file); + const object = elf_file.getFile(index).?.object; + for (0..object.getGlobals().len) |i| { + const ref = object.resolveSymbol(@intCast(i), elf_file); + const sym = elf_file.getSymbol(ref) orelse continue; + if (sym.getFile(elf_file).?.getIndex() != object.index) continue; + if (sym.flags.@"export") { + try markSymbol(sym, roots, elf_file); } } } @@ -57,7 +60,7 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { // Mark every atom referenced by CIE as alive. for (object.cies.items) |cie| { for (cie.getRelocs(elf_file)) |rel| { - const sym = object.getSymbol(rel.r_sym(), elf_file); + const sym = &object.symbols.items[rel.r_sym()]; try markSymbol(sym, roots, elf_file); } } @@ -94,14 +97,14 @@ fn markLive(atom: *Atom, elf_file: *Elf) void { for (atom.getFdes(elf_file)) |fde| { for (fde.getRelocs(elf_file)[1..]) |rel| { - const target_sym = object.getSymbol(rel.r_sym(), elf_file); + const target_sym = object.symbols.items[rel.r_sym()]; const target_atom = target_sym.getAtom(elf_file) orelse continue; if (markAtom(target_atom)) markLive(target_atom, elf_file); } } for (atom.getRelocs(elf_file)) |rel| { - const target_sym = object.getSymbol(rel.r_sym(), elf_file); + const target_sym = object.symbols.items[rel.r_sym()]; if (target_sym.getMergeSubsection(elf_file)) |msub| { msub.alive = true; continue; diff --git a/src/Elf/merge_section.zig b/src/Elf/merge_section.zig index 5ccf5b1e..3837b552 100644 --- a/src/Elf/merge_section.zig +++ b/src/Elf/merge_section.zig @@ -19,7 +19,7 @@ pub const MergeSection = struct { } pub fn getName(msec: MergeSection, elf_file: *Elf) [:0]const u8 { - return elf_file.string_intern.getAssumeExists(msec.name); + return elf_file.getShString(msec.name); } pub fn getAddress(msec: MergeSection, elf_file: *Elf) i64 { diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index 67bf2943..bb14de7f 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -33,23 +33,7 @@ fn claimUnresolved(elf_file: *Elf) void { defer tracy.end(); for (elf_file.objects.items) |index| { - const object = elf_file.getFile(index).?.object; - const first_global = object.first_global orelse return; - for (object.getGlobals(), 0..) |global_index, i| { - const sym_idx = @as(u32, @intCast(first_global + i)); - const sym = object.symtab.items[sym_idx]; - if (sym.st_shndx != elf.SHN_UNDEF) continue; - - const global = elf_file.getSymbol(global_index); - if (global.getFile(elf_file)) |_| { - if (global.getSourceSymbol(elf_file).st_shndx != elf.SHN_UNDEF) continue; - } - - global.value = 0; - global.atom_ref = .{}; - global.sym_idx = sym_idx; - global.file = object.index; - } + elf_file.getFile(index).?.object.claimUnresolvedRelocatable(elf_file); } } @@ -185,7 +169,7 @@ fn calcSectionSizes(elf_file: *Elf) !void { if (elf_file.shstrtab_sect_index) |index| { const shdr = &elf_file.sections.items(.shdr)[index]; - shdr.sh_size = elf_file.shstrtab.buffer.items.len; + shdr.sh_size = elf_file.shstrtab.items.len; } } @@ -199,7 +183,7 @@ fn calcComdatGroupsSizes(elf_file: *Elf) void { shdr.sh_link = elf_file.symtab_sect_index.?; const sym = cg.getSymbol(elf_file); - shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse sym.shndx; + shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse sym.getShndx(elf_file).?; } } @@ -223,7 +207,7 @@ fn writeAtoms(elf_file: *Elf) !void { if (atoms.items.len == 0) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue; - log.debug("writing atoms in '{s}' section", .{elf_file.shstrtab.getAssumeExists(shdr.sh_name)}); + log.debug("writing atoms in '{s}' section", .{elf_file.getShString(shdr.sh_name)}); var buffer = try gpa.alloc(u8, shdr.sh_size); defer gpa.free(buffer); @@ -289,7 +273,7 @@ fn writeSyntheticSections(elf_file: *Elf) !void { mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan); log.debug("writing {s} from 0x{x} to 0x{x}", .{ - elf_file.shstrtab.getAssumeExists(shdr.sh_name), + elf_file.getShString(shdr.sh_name), shdr.sh_offset, shdr.sh_offset + shdr.sh_size, }); @@ -331,7 +315,7 @@ fn writeSyntheticSections(elf_file: *Elf) !void { if (elf_file.shstrtab_sect_index) |shndx| { const shdr = elf_file.sections.items(.shdr)[shndx]; - try elf_file.base.file.pwriteAll(elf_file.shstrtab.buffer.items, shdr.sh_offset); + try elf_file.base.file.pwriteAll(elf_file.shstrtab.items, shdr.sh_offset); } } diff --git a/src/Elf/synthetic.zig b/src/Elf/synthetic.zig index 33444a41..0c5775dd 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -9,7 +9,7 @@ pub const DynamicSection = struct { pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - const off = try elf_file.dynstrtab.insert(gpa, shared.getSoname()); + const off = try elf_file.insertDynString(shared.getSoname()); try dt.needed.append(gpa, off); } @@ -22,11 +22,11 @@ pub const DynamicSection = struct { if (i > 0) try rpath.append(':'); try rpath.appendSlice(path); } - dt.rpath = try elf_file.dynstrtab.insert(gpa, rpath.items); + dt.rpath = try elf_file.insertDynString(rpath.items); } pub fn setSoname(dt: *DynamicSection, soname: []const u8, elf_file: *Elf) !void { - dt.soname = try elf_file.dynstrtab.insert(elf_file.base.allocator, soname); + dt.soname = try elf_file.insertDynString(soname); } fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 { @@ -245,7 +245,7 @@ pub const HashSection = struct { @memset(chains, 0); for (elf_file.dynsym.entries.items, 1..) |entry, i| { - const name = elf_file.dynstrtab.getAssumeExists(entry.off); + const name = elf_file.getDynString(entry.off); const hash = hasher(name) % buckets.len; chains[@as(u32, @intCast(i))] = buckets[hash]; buckets[hash] = @as(u32, @intCast(i)); @@ -286,7 +286,7 @@ pub const GnuHashSection = struct { fn getExports(elf_file: *Elf) []const DynsymSection.Entry { const start = for (elf_file.dynsym.entries.items, 0..) |dsym, i| { - const sym = elf_file.getSymbol(dsym.index); + const sym = elf_file.getSymbol(dsym.ref).?; if (sym.flags.@"export") break i; } else elf_file.dynsym.entries.items.len; return elf_file.dynsym.entries.items[start..]; @@ -333,7 +333,7 @@ pub const GnuHashSection = struct { @memset(bloom, 0); for (exports, 0..) |dsym, i| { - const sym = elf_file.getSymbol(dsym.index); + const sym = elf_file.getSymbol(dsym.ref).?; const h = hasher(sym.getName(elf_file)); hashes[i] = h; indices[i] = h % hash.num_buckets; @@ -389,8 +389,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 - index: Symbol.Index, + /// Ref of the symbol which gets privilege of getting a dynamic treatment + ref: Elf.Ref, /// Offset into .dynstrtab off: u32, }; @@ -399,21 +399,21 @@ 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 gpa = elf_file.base.allocator; const index = @as(u32, @intCast(dynsym.entries.items.len + 1)); - const sym = elf_file.getSymbol(sym_index); + const sym = elf_file.getSymbol(ref).?; sym.flags.has_dynamic = true; - try sym.addExtra(.{ .dynamic = index }, elf_file); - const name = try elf_file.dynstrtab.insert(gpa, sym.getName(elf_file)); - try dynsym.entries.append(gpa, .{ .index = sym_index, .off = name }); + sym.addExtra(.{ .dynamic = index }, elf_file); + const name = try elf_file.insertDynString(sym.getName(elf_file)); + try dynsym.entries.append(gpa, .{ .ref = ref, .off = name }); } 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.getSymbol(lhs.index); - const rhs_sym = ctx.getSymbol(rhs.index); + const lhs_sym = ctx.getSymbol(lhs.ref).?; + const rhs_sym = ctx.getSymbol(rhs.ref).?; if (lhs_sym.flags.@"export" != rhs_sym.flags.@"export") { return rhs_sym.flags.@"export"; @@ -425,14 +425,14 @@ pub const DynsymSection = struct { const rhs_hash = GnuHashSection.hasher(rhs_sym.getName(ctx)) % nbuckets; if (lhs_hash == rhs_hash) - return lhs_sym.getExtra(ctx).?.dynamic < rhs_sym.getExtra(ctx).?.dynamic; + return lhs_sym.getExtra(ctx).dynamic < rhs_sym.getExtra(ctx).dynamic; return lhs_hash < rhs_hash; } }; var num_exports: u32 = 0; - for (dynsym.entries.items) |dsym| { - const sym = elf_file.getSymbol(dsym.index); + for (dynsym.entries.items) |entry| { + const sym = elf_file.getSymbol(entry.ref).?; if (sym.flags.@"export") num_exports += 1; } @@ -440,26 +440,26 @@ pub const DynsymSection = struct { std.mem.sort(Entry, dynsym.entries.items, elf_file, Sort.lessThan); - for (dynsym.entries.items, 1..) |dsym, index| { - const sym = elf_file.getSymbol(dsym.index); - var extra = sym.getExtra(elf_file).?; + for (dynsym.entries.items, 1..) |entry, index| { + const sym = elf_file.getSymbol(entry.ref).?; + var extra = sym.getExtra(elf_file); extra.dynamic = @as(u32, @intCast(index)); sym.setExtra(extra, elf_file); } } - pub inline fn size(dynsym: DynsymSection) usize { + pub fn size(dynsym: DynsymSection) usize { return dynsym.count() * @sizeOf(elf.Elf64_Sym); } - pub inline fn count(dynsym: DynsymSection) u32 { + pub fn count(dynsym: DynsymSection) u32 { return @as(u32, @intCast(dynsym.entries.items.len + 1)); } 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.getSymbol(entry.index); + const sym = elf_file.getSymbol(entry.ref).?; var out_sym: elf.Elf64_Sym = Elf.null_sym; sym.setOutputSym(elf_file, &out_sym); out_sym.st_name = entry.off; @@ -509,7 +509,7 @@ pub const VerneedSection = struct { try verneed.ensureTotalCapacity(dynsyms.len); for (dynsyms, 1..) |dynsym, i| { - const symbol = elf_file.getSymbol(dynsym.index); + const symbol = elf_file.getSymbol(dynsym.ref).?; if (symbol.flags.import and symbol.ver_idx & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) { const shared = symbol.getFile(elf_file).?.shared; verneed.appendAssumeCapacity(.{ @@ -564,7 +564,7 @@ pub const VerneedSection = struct { sym.* = .{ .vn_version = 1, .vn_cnt = 0, - .vn_file = try elf_file.dynstrtab.insert(gpa, soname), + .vn_file = try elf_file.insertDynString(soname), .vn_aux = 0, .vn_next = 0, }; @@ -583,7 +583,7 @@ pub const VerneedSection = struct { .vna_hash = HashSection.hasher(version), .vna_flags = 0, .vna_other = vern.index, - .vna_name = try elf_file.dynstrtab.insert(gpa, version), + .vna_name = try elf_file.insertDynString(version), .vna_next = 0, }; verneed_sym.vn_cnt += 1; @@ -624,7 +624,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. @@ -653,19 +653,19 @@ 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) !void { + pub fn addGotSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .got; - entry.symbol_index = sym_index; - const symbol = elf_file.getSymbol(sym_index); + entry.ref = ref; + const symbol = elf_file.getSymbol(ref).?; if (symbol.flags.import or symbol.isIFunc(elf_file) or (elf_file.options.pic and !symbol.isAbs(elf_file))) got.flags.needs_rela = true; - try symbol.addExtra(.{ .got = index }, elf_file); + symbol.addExtra(.{ .got = index }, elf_file); } pub fn addTlsLdSymbol(got: *GotSection, elf_file: *Elf) !void { @@ -673,39 +673,39 @@ pub const GotSection = struct { const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .tlsld; - entry.symbol_index = undefined; // unused + entry.ref = .{}; // unused got.flags.needs_rela = true; got.tlsld_index = index; } - pub fn addTlsGdSymbol(got: *GotSection, sym_index: u32, elf_file: *Elf) !void { + pub fn addTlsGdSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .tlsgd; - entry.symbol_index = sym_index; - const symbol = elf_file.getSymbol(sym_index); + entry.ref = ref; + const symbol = elf_file.getSymbol(ref).?; if (symbol.flags.import or elf_file.options.shared) 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: u32, elf_file: *Elf) !void { + pub fn addGotTpSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .gottp; - entry.symbol_index = sym_index; - const symbol = elf_file.getSymbol(sym_index); + entry.ref = ref; + const symbol = elf_file.getSymbol(ref).?; if (symbol.flags.import or elf_file.options.shared) 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: u32, elf_file: *Elf) !void { + pub fn addTlsDescSymbol(got: *GotSection, ref: Elf.Ref, elf_file: *Elf) !void { const index = try got.allocateEntry(elf_file.base.allocator); const entry = &got.entries.items[index]; entry.tag = .tlsdesc; - entry.symbol_index = sym_index; - const symbol = elf_file.getSymbol(sym_index); + entry.ref = ref; + const symbol = elf_file.getSymbol(ref).?; got.flags.needs_rela = true; - try symbol.addExtra(.{ .tlsdesc = index }, elf_file); + symbol.addExtra(.{ .tlsdesc = index }, elf_file); } pub fn size(got: GotSection) usize { @@ -721,10 +721,7 @@ pub const GotSection = struct { const apply_relocs = elf_file.options.apply_dynamic_relocs; for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.getSymbol(entry.symbol_index), - }; + const symbol = elf_file.getSymbol(entry.ref); switch (entry.tag) { .got => { const value: i64 = blk: { @@ -793,11 +790,8 @@ pub const GotSection = struct { try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.getSymbol(entry.symbol_index), - }; - const extra = if (symbol) |s| s.getExtra(elf_file).? else null; + const symbol = elf_file.getSymbol(entry.ref); + const extra = if (symbol) |s| s.getExtra(elf_file) else null; switch (entry.tag) { .got => { @@ -896,10 +890,7 @@ pub const GotSection = struct { const is_shared = elf_file.options.shared; var num: usize = 0; for (got.entries.items) |entry| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.getSymbol(entry.symbol_index), - }; + const symbol = elf_file.getSymbol(entry.ref); switch (entry.tag) { .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or (elf_file.options.pic and !symbol.?.isAbs(elf_file))) @@ -931,10 +922,10 @@ pub const GotSection = struct { if (elf_file.options.strip_all) return; 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.getSymbol(entry.symbol_index).getName(elf_file), - }; + const symbol_name = if (elf_file.getSymbol(entry.ref)) |sym| + sym.getName(elf_file) + else + ""; got.output_symtab_ctx.strsize += @as(u32, @intCast(symbol_name.len + @tagName(entry.tag).len + 1 + 1)); } } @@ -943,14 +934,8 @@ pub const GotSection = struct { if (elf_file.options.strip_all) return; for (got.entries.items, got.output_symtab_ctx.ilocal..) |entry, ilocal| { - const symbol = switch (entry.tag) { - .tlsld => null, - inline else => elf_file.getSymbol(entry.symbol_index), - }; - const symbol_name = switch (entry.tag) { - .tlsld => "", - inline else => symbol.?.getName(elf_file), - }; + const symbol = elf_file.getSymbol(entry.ref); + const symbol_name = if (symbol) |s| s.getName(elf_file) else ""; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(symbol_name); elf_file.strtab.appendAssumeCapacity('$'); @@ -987,11 +972,11 @@ pub const GotSection = struct { _ = options; _ = unused_fmt_string; for (ctx.got.entries.items) |entry| { - const symbol = ctx.elf_file.getSymbol(entry.symbol_index); - try writer.print(" {d}@0x{x} => {d}@0x{x} ({s})\n", .{ + const symbol = ctx.elf_file.getSymbol(entry.ref).?; + try writer.print(" {d}@0x{x} => {}@0x{x} ({s})\n", .{ entry.cell_index, entry.getAddress(ctx.elf_file), - entry.symbol_index, + entry.ref, symbol.getAddress(.{}, ctx.elf_file), symbol.getName(ctx.elf_file), }); @@ -1000,18 +985,18 @@ pub const GotSection = struct { }; 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 index = @as(u32, @intCast(plt.symbols.items.len)); - const symbol = elf_file.getSymbol(sym_index); - try symbol.addExtra(.{ .plt = index }, elf_file); - try plt.symbols.append(elf_file.base.allocator, sym_index); + const symbol = elf_file.getSymbol(ref).?; + symbol.addExtra(.{ .plt = index }, elf_file); + try plt.symbols.append(elf_file.base.allocator, ref); } pub fn size(plt: PltSection, elf_file: *Elf) usize { @@ -1049,10 +1034,10 @@ pub const PltSection = struct { pub fn addRela(plt: PltSection, elf_file: *Elf) !void { try elf_file.rela_plt.ensureUnusedCapacity(elf_file.base.allocator, plt.numRela()); - for (plt.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (plt.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; assert(sym.flags.import); - const extra = sym.getExtra(elf_file).?; + const extra = sym.getExtra(elf_file); const r_offset: u64 = @intCast(sym.getGotPltAddress(elf_file)); const r_sym: u64 = extra.dynamic; const r_type = relocation.encode(.jump_slot, elf_file.options.cpu_arch.?); @@ -1072,8 +1057,8 @@ pub const PltSection = struct { if (elf_file.options.strip_all) return; plt.output_symtab_ctx.nlocals = @as(u32, @intCast(plt.symbols.items.len)); - for (plt.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (plt.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; plt.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(elf_file).len + "$plt".len + 1)); } } @@ -1082,8 +1067,8 @@ pub const PltSection = struct { if (elf_file.options.strip_all) return; const cpu_arch = elf_file.options.cpu_arch.?; - for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.getSymbol(sym_index); + for (plt.symbols.items, plt.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.getSymbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.getName(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$plt"); @@ -1116,8 +1101,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.getSymbol(sym_index); + for (plt.symbols.items, 0..) |ref, i| { + const sym = elf_file.getSymbol(ref).?; const target_addr = sym.getGotPltAddress(elf_file); const source_addr = sym.getPltAddress(elf_file); disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 12)) - 4; @@ -1165,8 +1150,8 @@ pub const PltSection = struct { } } - for (plt.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (plt.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; const target_addr = sym.getGotPltAddress(elf_file); const source_addr = sym.getPltAddress(elf_file); const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr); @@ -1203,8 +1188,8 @@ pub const GotPltSection = struct { _ = got_plt; { // [0]: _DYNAMIC - const symbol = elf_file.getSymbol(elf_file.dynamic_index.?); - try writer.writeInt(u64, @bitCast(symbol.value), .little); + const symbol = elf_file.getInternalObject().?.getDynamicSymbol(elf_file).?; + try writer.writeInt(u64, @bitCast(symbol.getAddress(.{}, elf_file)), .little); } // [1]: 0x0 // [2]: 0x0 @@ -1221,18 +1206,20 @@ 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 index = @as(u32, @intCast(plt_got.symbols.items.len)); - const symbol = elf_file.getSymbol(sym_index); - try symbol.addExtra(.{ .plt_got = index }, elf_file); - try plt_got.symbols.append(elf_file.base.allocator, sym_index); + const symbol = elf_file.getSymbol(ref).?; + symbol.addExtra(.{ .plt_got = index }, elf_file); + symbol.flags.plt = true; + symbol.flags.got = true; + try plt_got.symbols.append(elf_file.base.allocator, ref); } pub fn size(plt_got: PltGotSection, elf_file: *Elf) usize { @@ -1263,8 +1250,8 @@ pub const PltGotSection = struct { if (elf_file.options.strip_all) return; plt_got.output_symtab_ctx.nlocals = @as(u32, @intCast(plt_got.symbols.items.len)); - for (plt_got.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (plt_got.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; plt_got.output_symtab_ctx.strsize += @as(u32, @intCast(sym.getName(elf_file).len + "$pltgot".len + 1)); } } @@ -1272,8 +1259,8 @@ pub const PltGotSection = struct { pub fn writeSymtab(plt_got: PltGotSection, elf_file: *Elf) void { if (elf_file.options.strip_all) return; - for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |sym_index, ilocal| { - const sym = elf_file.getSymbol(sym_index); + for (plt_got.symbols.items, plt_got.output_symtab_ctx.ilocal..) |ref, ilocal| { + const sym = elf_file.getSymbol(ref).?; const st_name = @as(u32, @intCast(elf_file.strtab.items.len)); elf_file.strtab.appendSliceAssumeCapacity(sym.getName(elf_file)); elf_file.strtab.appendSliceAssumeCapacity("$pltgot"); @@ -1291,8 +1278,8 @@ pub const PltGotSection = struct { const x86_64 = struct { fn write(plt_got: PltGotSection, elf_file: *Elf, writer: anytype) !void { - for (plt_got.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (plt_got.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; const target_addr = sym.getGotAddress(elf_file); const source_addr = sym.getPltGotAddress(elf_file); const disp = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr + 6)) - 4; @@ -1309,8 +1296,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.getSymbol(sym_index); + for (plt_got.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; const target_addr = sym.getGotAddress(elf_file); const source_addr = sym.getPltGotAddress(elf_file); const pages = try aarch64_util.calcNumberOfPages(source_addr, target_addr); @@ -1335,54 +1322,54 @@ 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 index = @as(u32, @intCast(copy_rel.symbols.items.len)); - const symbol = elf_file.getSymbol(sym_index); + const symbol = elf_file.getSymbol(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(elf_file.base.allocator, sym_index); + symbol.addExtra(.{ .copy_rel = index }, elf_file); + try copy_rel.symbols.append(elf_file.base.allocator, ref); const shared = symbol.getFile(elf_file).?.shared; if (shared.aliases == null) { try shared.initSymbolAliases(elf_file); } - const aliases = shared.getSymbolAliases(sym_index, elf_file); + const aliases = shared.getSymbolAliases(ref.index, elf_file); for (aliases) |alias| { - if (alias == sym_index) continue; - const alias_sym = elf_file.getSymbol(alias); + if (alias == ref.index) continue; + const alias_sym = &shared.symbols.items[alias]; alias_sym.flags.import = true; alias_sym.flags.@"export" = true; alias_sym.flags.has_copy_rel = true; alias_sym.flags.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.index }, elf_file); } } pub fn calcSectionSize(copy_rel: CopyRelSection, shndx: u32, elf_file: *Elf) !void { const shdr = &elf_file.sections.items(.shdr)[shndx]; - for (copy_rel.symbols.items) |sym_index| { - const symbol = elf_file.getSymbol(sym_index); + for (copy_rel.symbols.items) |ref| { + const symbol = elf_file.getSymbol(ref).?; const shared = symbol.getFile(elf_file).?.shared; const alignment = try symbol.getAlignment(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.getSourceSymbol(elf_file).st_size; + shdr.sh_size = @as(u64, @intCast(symbol.value)) + symbol.getElfSym(elf_file).st_size; - const aliases = shared.getSymbolAliases(sym_index, elf_file); + const aliases = shared.getSymbolAliases(ref.index, elf_file); for (aliases) |alias| { - if (alias == sym_index) continue; - const alias_sym = elf_file.getSymbol(alias); + if (alias == ref.index) continue; + const alias_sym = &shared.symbols.items[alias]; alias_sym.value = symbol.value; } } @@ -1390,10 +1377,10 @@ pub const CopyRelSection = struct { pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void { try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, copy_rel.numRela()); - for (copy_rel.symbols.items) |sym_index| { - const sym = elf_file.getSymbol(sym_index); + for (copy_rel.symbols.items) |ref| { + const sym = elf_file.getSymbol(ref).?; assert(sym.flags.import and sym.flags.copy_rel); - const extra = sym.getExtra(elf_file).?; + const extra = sym.getExtra(elf_file); elf_file.addRelaDynAssumeCapacity(.{ .offset = @intCast(sym.getAddress(.{}, elf_file)), .sym = extra.dynamic, @@ -1420,8 +1407,7 @@ pub const ComdatGroupSection = struct { const cg = cgs.getComdatGroup(elf_file); const object = cg.getFile(elf_file).object; const shdr = object.shdrs.items[cg.shndx]; - const sym_index = object.symbols.items[shdr.sh_info]; - return elf_file.getSymbol(sym_index); + return &object.symbols.items[shdr.sh_info]; } pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize {