From dc6c8db87773711f3d2903f099dec41cee5b90e3 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 Sep 2024 06:12:42 +0200 Subject: [PATCH 01/19] elf: move ownership of InputMergeSections into owning objects --- src/Elf.zig | 19 ------------------- src/Elf/Object.zig | 45 +++++++++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index 8e1e8643..a9532de9 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -90,7 +90,6 @@ thunks: std.ArrayListUnmanaged(Thunk) = .{}, merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, -merge_input_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, @@ -155,10 +154,6 @@ pub fn deinit(self: *Elf) void { } self.merge_sections.deinit(gpa); self.merge_subsections.deinit(gpa); - for (self.merge_input_sections.items) |*sect| { - sect.deinit(gpa); - } - self.merge_input_sections.deinit(gpa); self.comdat_groups.deinit(gpa); self.comdat_groups_owners.deinit(gpa); self.comdat_groups_table.deinit(gpa); @@ -296,8 +291,6 @@ pub fn flush(self: *Elf) !void { try self.symbols_extra.append(gpa, 0); // Append null file. try self.files.append(gpa, .null); - // Append null input merge section. - try self.merge_input_sections.append(gpa, .{}); var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); @@ -2986,18 +2979,6 @@ pub fn getGlobalByName(self: *Elf, name: []const u8) ?u32 { return self.globals.get(off); } -pub fn addInputMergeSection(self: *Elf) !InputMergeSection.Index { - const index: InputMergeSection.Index = @intCast(self.merge_input_sections.items.len); - const msec = try self.merge_input_sections.addOne(self.base.allocator); - msec.* = .{}; - return index; -} - -pub fn getInputMergeSection(self: *Elf, index: InputMergeSection.Index) ?*InputMergeSection { - if (index == 0) return null; - return &self.merge_input_sections.items[index]; -} - 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); diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index d4a3b61c..918cdb7c 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -16,7 +16,8 @@ comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, -merge_sections: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, +merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, +merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, @@ -50,7 +51,11 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.relocs.deinit(allocator); self.fdes.deinit(allocator); self.cies.deinit(allocator); + for (self.merge_sections.items) |*sec| { + sec.deinit(allocator); + } self.merge_sections.deinit(allocator); + self.merge_sections_indexes.deinit(allocator); } pub fn parse(self: *Object, elf_file: *Elf) !void { @@ -107,6 +112,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { try self.strtab.appendSlice(gpa, strtab); } + // Append null input merge section + try self.merge_sections.append(gpa, .{}); + try self.initAtoms(gpa, file, elf_file); try self.initSymtab(gpa, elf_file); @@ -600,8 +608,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - try self.merge_sections.resize(gpa, self.shdrs.items.len); - @memset(self.merge_sections.items, 0); + try self.merge_sections.ensureUnusedCapacity(gpa, self.shdrs.items.len); + try self.merge_sections_indexes.resize(gpa, self.shdrs.items.len); + @memset(self.merge_sections_indexes.items, 0); for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_flags & elf.SHF_MERGE == 0) continue; @@ -611,9 +620,9 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { if (!atom.flags.alive) continue; if (atom.getRelocs(elf_file).len > 0) continue; - const imsec_idx = try elf_file.addInputMergeSection(); - const imsec = elf_file.getInputMergeSection(imsec_idx).?; - self.merge_sections.items[shndx] = imsec_idx; + const imsec_idx = try self.addInputMergeSection(gpa); + const imsec = self.getInputMergeSection(imsec_idx).?; + self.merge_sections_indexes.items[shndx] = imsec_idx; imsec.merge_section = try elf_file.getOrCreateMergeSection(atom.getName(elf_file), shdr.sh_flags, shdr.sh_type); imsec.atom = atom_index; @@ -679,8 +688,8 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; - for (self.merge_sections.items) |index| { - const imsec = elf_file.getInputMergeSection(index) orelse continue; + for (self.merge_sections_indexes.items) |index| { + const imsec = self.getInputMergeSection(index) orelse continue; if (imsec.offsets.items.len == 0) continue; const msec = elf_file.getMergeSection(imsec.merge_section); const atom = elf_file.getAtom(imsec.atom).?; @@ -714,8 +723,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { 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.items[esym.st_shndx]; - const imsec = elf_file.getInputMergeSection(imsec_index) orelse continue; + const imsec_index = self.merge_sections_indexes.items[esym.st_shndx]; + const imsec = self.getInputMergeSection(imsec_index) orelse continue; if (imsec.offsets.items.len == 0) continue; const msub_index, const offset = imsec.findSubsection(@intCast(esym.st_value)) orelse { elf_file.base.fatal("{}: invalid symbol value: {s}:{x}", .{ @@ -741,8 +750,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const esym = self.symtab.items[rel.r_sym()]; if (esym.st_type() != elf.STT_SECTION) continue; - const imsec_index = self.merge_sections.items[esym.st_shndx]; - const imsec = elf_file.getInputMergeSection(imsec_index) orelse continue; + const imsec_index = self.merge_sections_indexes.items[esym.st_shndx]; + const imsec = self.getInputMergeSection(imsec_index) orelse continue; if (imsec.offsets.items.len == 0) continue; const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { elf_file.base.fatal("{}: {s}: invalid relocation at offset 0x{x}", .{ @@ -957,6 +966,18 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } +fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index { + const index: InputMergeSection.Index = @intCast(self.merge_sections.items.len); + const msec = try self.merge_sections.addOne(allocator); + msec.* = .{}; + return index; +} + +fn getInputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMergeSection { + if (index == 0) return null; + return &self.merge_sections.items[index]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, From 6aba6f32f2bb4a38f11e5075b45be89b0f0748ff Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 9 Sep 2024 06:54:35 +0200 Subject: [PATCH 02/19] elf: move ownership of ComdatGroups into objects --- src/Elf.zig | 120 +++++++++++++++------------------------- src/Elf/Object.zig | 118 ++++++++++++++++++++++++++------------- src/Elf/file.zig | 7 +++ src/Elf/relocatable.zig | 18 ++---- src/Elf/synthetic.zig | 32 ++++++----- 5 files changed, 157 insertions(+), 138 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index a9532de9..95c60614 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -91,10 +91,6 @@ thunks: std.ArrayListUnmanaged(Thunk) = .{}, merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, -comdat_groups: std.ArrayListUnmanaged(ComdatGroup) = .{}, -comdat_groups_owners: std.ArrayListUnmanaged(ComdatGroupOwner) = .{}, -comdat_groups_table: std.AutoHashMapUnmanaged(u32, ComdatGroupOwner.Index) = .{}, - has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, default_sym_version: elf.Elf64_Versym, @@ -154,9 +150,6 @@ pub fn deinit(self: *Elf) void { } self.merge_sections.deinit(gpa); self.merge_subsections.deinit(gpa); - self.comdat_groups.deinit(gpa); - self.comdat_groups_owners.deinit(gpa); - self.comdat_groups_table.deinit(gpa); self.symbols.deinit(gpa); self.symbols_extra.deinit(gpa); self.globals.deinit(gpa); @@ -2046,34 +2039,17 @@ fn resolveSymbols(self: *Elf) !void { } else i += 1; } - // Dedup comdat groups. - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.getComdatGroup(cg_index); - const cg_owner = self.getComdatGroupOwner(cg.owner); - const owner_file_index = if (self.getFile(cg_owner.file)) |file| - file.object.index - else - std.math.maxInt(File.Index); - cg_owner.file = @min(owner_file_index, index); + { + // Dedup comdat groups. + var table = std.StringHashMap(Ref).init(self.base.allocator); + defer table.deinit(); + + for (self.objects.items) |index| { + try self.getFile(index).?.object.resolveComdatGroups(self, &table); } - } - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.comdat_groups.items) |cg_index| { - const cg = self.getComdatGroup(cg_index); - const cg_owner = self.getComdatGroupOwner(cg.owner); - if (cg_owner.file != index) { - for (cg.getComdatGroupMembers(self)) |shndx| { - const atom_index = object.atoms.items[shndx]; - if (self.getAtom(atom_index)) |atom| { - atom.flags.alive = false; - atom.markFdesDead(self); - } - } - } + for (self.objects.items) |index| { + self.getFile(index).?.object.markComdatGroupsDead(self); } } @@ -3019,40 +2995,8 @@ pub fn getMergeSection(self: *Elf, index: MergeSection.Index) *MergeSection { return &self.merge_sections.items[index]; } -const GetOrCreateComdatGroupOwnerResult = struct { - found_existing: bool, - index: ComdatGroupOwner.Index, -}; - -pub fn getOrCreateComdatGroupOwner(self: *Elf, off: u32) !GetOrCreateComdatGroupOwnerResult { - const gpa = self.base.allocator; - const gop = try self.comdat_groups_table.getOrPut(gpa, off); - if (!gop.found_existing) { - const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); - const owner = try self.comdat_groups_owners.addOne(gpa); - owner.* = .{}; - gop.value_ptr.* = index; - } - return .{ - .found_existing = gop.found_existing, - .index = gop.value_ptr.*, - }; -} - -pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index { - const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); - _ = try self.comdat_groups.addOne(self.base.allocator); - return index; -} - -pub inline fn getComdatGroup(self: *Elf, index: ComdatGroup.Index) *ComdatGroup { - assert(index < self.comdat_groups.items.len); - return &self.comdat_groups.items[index]; -} - -pub inline fn getComdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupOwner { - assert(index < self.comdat_groups_owners.items.len); - return &self.comdat_groups_owners.items[index]; +pub fn getComdatGroup(self: *Elf, ref: Ref) *ComdatGroup { + return self.getFile(ref.file).?.getComdatGroup(ref.index); } const RelaDyn = struct { @@ -3294,7 +3238,12 @@ fn fmtDumpState( try writer.print("merge_sect({d}) : {}\n", .{ index, msec.fmt(self) }); } try writer.writeByte('\n'); - try writer.writeAll("Output sections\n"); + try writer.writeAll("Output COMDAT groups\n"); + for (self.comdat_group_sections.items) |cg| { + try writer.print(" shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref }); + } + try writer.writeByte('\n'); + try writer.writeAll("Output shdrs\n"); try writer.print("{}\n", .{self.fmtSections()}); try writer.writeAll("Output phdrs\n"); try writer.print("{}\n", .{self.fmtPhdrs()}); @@ -3313,21 +3262,44 @@ const Section = struct { sym_index: u32 = 0, }; -const ComdatGroupOwner = struct { - file: File.Index = 0, +pub const Ref = struct { + index: u32 = 0, + file: u32 = 0, + + pub fn eql(ref: Ref, other: Ref) bool { + return ref.index == other.index and ref.file == other.file; + } - const Index = u32; + pub fn format( + ref: Ref, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + try writer.print("ref({},{})", .{ ref.index, ref.file }); + } }; pub const ComdatGroup = struct { - owner: ComdatGroupOwner.Index, - file: File.Index, + signature_off: u32, + file_index: File.Index, shndx: u32, members_start: u32, members_len: u32, + alive: bool = true, + + pub fn getFile(cg: ComdatGroup, elf_file: *Elf) File { + return elf_file.getFile(cg.file_index).?; + } + + pub fn getSignature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 { + return cg.getFile(elf_file).object.getString(cg.signature_off); + } pub fn getComdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 { - const object = elf_file.getFile(cg.file).?.object; + const object = cg.getFile(elf_file).object; return object.comdat_group_data.items[cg.members_start..][0..cg.members_len]; } diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index 918cdb7c..e0f4e3ad 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -5,20 +5,20 @@ index: File.Index, header: ?elf.Elf64_Ehdr = null, shdrs: std.ArrayListUnmanaged(elf.Elf64_Shdr) = .{}, -shstrtab: StringTable = .{}, symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, -comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup.Index) = .{}, -comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, +comdat_groups: std.ArrayListUnmanaged(Elf.ComdatGroup) = .{}, +comdat_group_data: std.ArrayListUnmanaged(u32) = .{}, + fdes: std.ArrayListUnmanaged(Fde) = .{}, cies: std.ArrayListUnmanaged(Cie) = .{}, eh_frame_data: std.ArrayListUnmanaged(u8) = .{}, @@ -41,7 +41,6 @@ pub fn deinit(self: *Object, allocator: Allocator) void { if (self.archive) |*ar| allocator.free(ar.path); allocator.free(self.path); self.shdrs.deinit(allocator); - self.shstrtab.deinit(allocator); self.symtab.deinit(allocator); self.strtab.deinit(allocator); self.symbols.deinit(allocator); @@ -88,7 +87,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { const shstrtab = try self.preadShdrContentsAlloc(gpa, file, self.header.?.e_shstrndx); defer gpa.free(shstrtab); - try self.shstrtab.buffer.appendSlice(gpa, shstrtab); + try self.strtab.appendSlice(gpa, shstrtab); const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_SYMTAB => break @as(u32, @intCast(i)), @@ -107,9 +106,17 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { }; try self.symtab.appendUnalignedSlice(gpa, @as([*]align(1) const elf.Elf64_Sym, @ptrCast(symtab.ptr))[0..nsyms]); + const strtab_bias = @as(u32, @intCast(self.strtab.items.len)); const strtab = try self.preadShdrContentsAlloc(gpa, file, shdr.sh_link); defer gpa.free(strtab); try self.strtab.appendSlice(gpa, strtab); + + for (self.symtab.items) |*sym| { + sym.st_name = if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) + shdrs[sym.st_shndx].sh_name + else + sym.st_name + strtab_bias; + } } // Append null input merge section @@ -152,9 +159,9 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * const group_signature = blk: { if (group_info_sym.st_name == 0 and group_info_sym.st_type() == elf.STT_SECTION) { const sym_shdr = shdrs[group_info_sym.st_shndx]; - break :blk self.getShString(sym_shdr.sh_name); + break :blk sym_shdr.sh_name; } - break :blk self.getString(group_info_sym.st_name); + break :blk group_info_sym.st_name; }; const group_raw_data = try self.preadShdrContentsAlloc(allocator, file, @intCast(i)); @@ -170,18 +177,15 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * const group_start = @as(u32, @intCast(self.comdat_group_data.items.len)); try self.comdat_group_data.appendUnalignedSlice(allocator, group_members[1..]); - const group_signature_off = try elf_file.internString("{s}", .{group_signature}); - const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature_off); - const comdat_group_index = try elf_file.addComdatGroup(); - const comdat_group = elf_file.getComdatGroup(comdat_group_index); + const comdat_group_index = try self.addComdatGroup(allocator); + const comdat_group = self.getComdatGroup(comdat_group_index); comdat_group.* = .{ - .owner = gop.index, - .file = self.index, + .signature_off = group_signature, + .file_index = self.index, .shndx = @intCast(i), .members_start = group_start, .members_len = @intCast(group_nmembers - 1), }; - try self.comdat_groups.append(allocator, comdat_group_index); }, elf.SHT_SYMTAB_SHNDX => @panic("TODO"), @@ -194,7 +198,7 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * => {}, else => { - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); const shndx = @as(u32, @intCast(i)); if (mem.eql(u8, ".note.GNU-stack", name)) { @@ -258,7 +262,7 @@ fn addAtom(self: *Object, allocator: Allocator, file: std.fs.File, shdr: elf.Elf fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { const shdr = self.shdrs.items[index]; - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; @@ -276,7 +280,6 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { defer tracy.end(); const first_global = self.first_global orelse self.symtab.items.len; - const shdrs = self.shdrs.items; try self.symbols.ensureTotalCapacityPrecise(allocator, self.symtab.items.len); @@ -284,13 +287,7 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { const index = try elf_file.addSymbol(); self.symbols.appendAssumeCapacity(index); const symbol = elf_file.getSymbol(index); - const name = blk: { - if (sym.st_name == 0 and sym.st_type() == elf.STT_SECTION) { - const shdr = shdrs[sym.st_shndx]; - break :blk self.getShString(shdr.sh_name); - } - break :blk self.getString(sym.st_name); - }; + 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), @@ -310,7 +307,7 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) !u32 { const name = blk: { - const name = self.getShString(shdr.sh_name); + const name = self.getString(shdr.sh_name); if (elf_file.options.relocatable) break :blk name; if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ @@ -829,7 +826,7 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { const shndx = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); shdr.* = .{ - .sh_name = try self.shstrtab.insert(gpa, name), + .sh_name = try self.addString(gpa, name), .sh_type = elf.SHT_NOBITS, .sh_flags = sh_flags, .sh_addr = 0, @@ -848,6 +845,37 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { } } +pub fn resolveComdatGroups(self: *Object, elf_file: *Elf, table: anytype) !void { + for (self.comdat_groups.items, 0..) |*cg, cgi| { + const signature = cg.getSignature(elf_file); + const gop = try table.getOrPut(signature); + if (!gop.found_existing) { + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + continue; + } + const current = elf_file.getComdatGroup(gop.value_ptr.*); + cg.alive = false; + if (self.index < current.file_index) { + current.alive = false; + cg.alive = true; + gop.value_ptr.* = .{ .index = @intCast(cgi), .file = self.index }; + } + } +} + +pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void { + for (self.comdat_groups.items) |cg| { + if (cg.alive) continue; + for (cg.getComdatGroupMembers(elf_file)) |shndx| { + const atom_index = self.atoms.items[shndx]; + if (elf_file.getAtom(atom_index)) |atom_ptr| { + atom_ptr.flags.alive = false; + atom_ptr.markFdesDead(elf_file); + } + } + } +} + pub fn calcSymtabSize(self: *Object, elf_file: *Elf) !void { if (elf_file.options.strip_all) return; @@ -953,13 +981,17 @@ fn preadRelocsAlloc(self: Object, allocator: Allocator, file: std.fs.File, shndx return @as([*]align(1) const elf.Elf64_Rela, @ptrCast(raw.ptr))[0..num]; } -inline fn getString(self: Object, 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); +fn addString(self: *Object, 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; } -inline fn getShString(self: Object, off: u32) [:0]const u8 { - return self.shstrtab.getAssumeExists(off); +pub fn getString(self: Object, 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 asFile(self: *Object) File { @@ -978,6 +1010,17 @@ fn getInputMergeSection(self: *Object, index: InputMergeSection.Index) ?*InputMe return &self.merge_sections.items[index]; } +fn addComdatGroup(self: *Object, allocator: Allocator) !Elf.ComdatGroup.Index { + const index = @as(Elf.ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); + _ = try self.comdat_groups.addOne(allocator); + return index; +} + +pub fn getComdatGroup(self: *Object, index: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + assert(index < self.comdat_groups.items.len); + return &self.comdat_groups.items[index]; +} + pub fn format( self: *Object, comptime unused_fmt_string: []const u8, @@ -1108,12 +1151,13 @@ fn formatComdatGroups( _ = options; const object = ctx.object; const elf_file = ctx.elf_file; - try writer.writeAll(" comdat groups\n"); - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.getComdatGroup(cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - if (cg_owner.file != object.index) continue; - for (cg.getComdatGroupMembers(elf_file)) |shndx| { + try writer.writeAll(" COMDAT groups\n"); + for (object.comdat_groups.items, 0..) |cg, cg_index| { + try writer.print(" COMDAT({d})", .{cg_index}); + if (!cg.alive) try writer.writeAll(" : [*]"); + try writer.writeByte('\n'); + const cg_members = cg.getComdatGroupMembers(elf_file); + for (cg_members) |shndx| { const atom_index = object.atoms.items[shndx]; const atom = elf_file.getAtom(atom_index) orelse continue; try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.getName(elf_file) }); diff --git a/src/Elf/file.zig b/src/Elf/file.zig index 8abe4321..18bb24c1 100644 --- a/src/Elf/file.zig +++ b/src/Elf/file.zig @@ -85,6 +85,13 @@ pub const File = union(enum) { } } + pub fn getComdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup { + return switch (file) { + .internal, .shared => unreachable, + .object => |x| x.getComdatGroup(ind), + }; + } + pub fn getLocals(file: File) []const Symbol.Index { return switch (file) { .object => |x| x.getLocals(), diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index a82338d7..6cfd63f8 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -122,19 +122,12 @@ fn initSections(elf_file: *Elf) !void { } fn initComdatGroups(elf_file: *Elf) !void { - const tracy = trace(@src()); - defer tracy.end(); - const gpa = elf_file.base.allocator; for (elf_file.objects.items) |index| { const object = elf_file.getFile(index).?.object; - - for (object.comdat_groups.items) |cg_index| { - const cg = elf_file.getComdatGroup(cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - if (cg_owner.file != index) continue; - + for (object.comdat_groups.items, 0..) |cg, cg_index| { + if (!cg.alive) continue; const cg_sec = try elf_file.comdat_group_sections.addOne(gpa); cg_sec.* = .{ .shndx = try elf_file.addSection(.{ @@ -143,7 +136,7 @@ fn initComdatGroups(elf_file: *Elf) !void { .entsize = @sizeOf(u32), .addralign = @alignOf(u32), }), - .cg_index = cg_index, + .cg_ref = .{ .index = @intCast(cg_index), .file = index }, }; } } @@ -205,9 +198,8 @@ fn calcComdatGroupsSizes(elf_file: *Elf) void { shdr.sh_size = cg.size(elf_file); shdr.sh_link = elf_file.symtab_sect_index.?; - const sym = elf_file.getSymbol(cg.getSymbol(elf_file)); - shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse - elf_file.sections.items(.sym_index)[sym.shndx]; + const sym = cg.getSymbol(elf_file); + shdr.sh_info = sym.getOutputSymtabIndex(elf_file) orelse sym.shndx; } } diff --git a/src/Elf/synthetic.zig b/src/Elf/synthetic.zig index 73b5add2..a825e692 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -1409,30 +1409,30 @@ pub const CopyRelSection = struct { pub const ComdatGroupSection = struct { shndx: u32, - cg_index: u32, + cg_ref: Elf.Ref, - fn getFile(cgs: ComdatGroupSection, elf_file: *Elf) ?File { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const cg_owner = elf_file.getComdatGroupOwner(cg.owner); - return elf_file.getFile(cg_owner.file); + fn getComdatGroup(cgs: ComdatGroupSection, elf_file: *Elf) *Elf.ComdatGroup { + const cg_file = elf_file.getFile(cgs.cg_ref.file).?; + return cg_file.object.getComdatGroup(cgs.cg_ref.index); } - pub fn getSymbol(cgs: ComdatGroupSection, elf_file: *Elf) Symbol.Index { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const object = cgs.getFile(elf_file).?.object; + pub fn getSymbol(cgs: ComdatGroupSection, elf_file: *Elf) *Symbol { + const cg = cgs.getComdatGroup(elf_file); + const object = cg.getFile(elf_file).object; const shdr = object.shdrs.items[cg.shndx]; - return object.symbols.items[shdr.sh_info]; + const sym_index = object.symbols.items[shdr.sh_info]; + return elf_file.getSymbol(sym_index); } pub fn size(cgs: ComdatGroupSection, elf_file: *Elf) usize { - const cg = elf_file.getComdatGroup(cgs.cg_index); + const cg = cgs.getComdatGroup(elf_file); const members = cg.getComdatGroupMembers(elf_file); return (members.len + 1) * @sizeOf(u32); } pub fn write(cgs: ComdatGroupSection, elf_file: *Elf, writer: anytype) !void { - const cg = elf_file.getComdatGroup(cgs.cg_index); - const object = cgs.getFile(elf_file).?.object; + const cg = cgs.getComdatGroup(elf_file); + const object = cg.getFile(elf_file).object; const members = cg.getComdatGroupMembers(elf_file); try writer.writeInt(u32, elf.GRP_COMDAT, .little); for (members) |shndx| { @@ -1441,8 +1441,12 @@ pub const ComdatGroupSection = struct { elf.SHT_RELA => { const atom_index = object.atoms.items[shdr.sh_info]; const atom = elf_file.getAtom(atom_index).?; - const rela_shndx = elf_file.sections.items(.rela_shndx)[atom.out_shndx]; - try writer.writeInt(u32, rela_shndx, .little); + const rela_shndx = for (elf_file.sections.items(.shdr), 0..) |rela_shdr, rela_shndx| { + if (rela_shdr.sh_type == elf.SHT_RELA and + atom.out_shndx == rela_shdr.sh_info) + break rela_shndx; + } else unreachable; + try writer.writeInt(u32, @intCast(rela_shndx), .little); }, else => { const atom_index = object.atoms.items[shndx]; From 31621f0ce6aee08fff037136cecdfdc411b4cd65 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 10 Sep 2024 06:51:12 +0200 Subject: [PATCH 03/19] elf: move ownership of atoms into objects --- src/Elf.zig | 137 +++++++--------------- src/Elf/Atom.zig | 35 +++--- src/Elf/InternalObject.zig | 2 +- src/Elf/Object.zig | 227 +++++++++++++++++++++++++------------ src/Elf/SharedObject.zig | 2 +- src/Elf/Symbol.zig | 5 +- src/Elf/eh_frame.zig | 4 +- src/Elf/file.zig | 8 ++ src/Elf/gc.zig | 14 ++- src/Elf/relocatable.zig | 22 ++-- src/Elf/synthetic.zig | 8 +- 11 files changed, 250 insertions(+), 214 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index 95c60614..aa05df11 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -60,7 +60,7 @@ symbols_extra: std.ArrayListUnmanaged(u32) = .{}, globals: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, /// This table will be populated after `scanRelocs` has run. /// Key is symbol index. -undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Atom.Index)) = .{}, +undefs: std.AutoHashMapUnmanaged(Symbol.Index, std.ArrayListUnmanaged(Ref)) = .{}, string_intern: StringTable = .{}, @@ -84,8 +84,6 @@ rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, -atoms: std.ArrayListUnmanaged(Atom) = .{}, -atoms_extra: std.ArrayListUnmanaged(u32) = .{}, thunks: std.ArrayListUnmanaged(Thunk) = .{}, merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, @@ -139,8 +137,6 @@ pub fn deinit(self: *Elf) void { self.shstrtab.deinit(gpa); self.symtab.deinit(gpa); self.strtab.deinit(gpa); - self.atoms.deinit(gpa); - self.atoms_extra.deinit(gpa); for (self.thunks.items) |*thunk| { thunk.deinit(gpa); } @@ -275,9 +271,6 @@ pub fn flush(self: *Elf) !void { try self.dynstrtab.buffer.append(gpa, 0); // Append null section. _ = try self.addSection(.{ .name = "" }); - // Append null atom. - try self.atoms.append(gpa, .{}); - try self.atoms_extra.append(gpa, 0); // Append null symbols. try self.symtab.append(gpa, null_sym); try self.symbols.append(gpa, .{}); @@ -456,11 +449,11 @@ fn sortInitFini(self: *Elf) !void { const Entry = struct { priority: i32, - atom_index: Atom.Index, + atom_ref: Elf.Ref, pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool { if (lhs.priority == rhs.priority) { - return ctx.getAtom(lhs.atom_index).?.getPriority(ctx) < ctx.getAtom(rhs.atom_index).?.getPriority(ctx); + return ctx.getAtom(lhs.atom_ref).?.getPriority(ctx) < ctx.getAtom(rhs.atom_ref).?.getPriority(ctx); } return lhs.priority < rhs.priority; } @@ -490,8 +483,8 @@ fn sortInitFini(self: *Elf) !void { try entries.ensureTotalCapacityPrecise(atoms.items.len); defer entries.deinit(); - for (atoms.items) |atom_index| { - const atom = self.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = self.getAtom(ref).?; const file = atom.getObject(self); const priority = blk: { if (is_ctor_dtor) { @@ -504,14 +497,14 @@ fn sortInitFini(self: *Elf) !void { const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default; break :blk priority; }; - entries.appendAssumeCapacity(.{ .priority = priority, .atom_index = atom_index }); + entries.appendAssumeCapacity(.{ .priority = priority, .atom_ref = ref }); } mem.sort(Entry, entries.items, self, Entry.lessThan); atoms.clearRetainingCapacity(); for (entries.items) |entry| { - atoms.appendAssumeCapacity(entry.atom_index); + atoms.appendAssumeCapacity(entry.atom_ref); } } } @@ -519,8 +512,8 @@ fn sortInitFini(self: *Elf) !void { fn initOutputSections(self: *Elf) !void { for (self.objects.items) |index| { const object = self.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_shndx = try object.initOutputSection(self, atom.getInputShdr(self)); } @@ -746,11 +739,14 @@ pub fn initShStrtab(self: *Elf) !void { pub fn addAtomsToSections(self: *Elf) !void { for (self.objects.items) |index| { const object = self.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const atoms = &self.sections.items(.atoms)[atom.out_shndx]; - try atoms.append(self.base.allocator, atom_index); + try atoms.append(self.base.allocator, .{ + .index = atom_index, + .file = index, + }); } for (object.getLocals()) |local_index| { @@ -835,8 +831,8 @@ fn calcSectionSizes(self: *Elf) !void { if (atoms.items.len == 0) continue; if (self.requiresThunks() and shdr.sh_flags & elf.SHF_EXECINSTR != 0) continue; - for (atoms.items) |atom_index| { - const atom = self.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = self.getAtom(ref).?; const alignment = try math.powi(u64, 2, atom.alignment); const offset = mem.alignForward(u64, shdr.sh_size, alignment); const padding = offset - shdr.sh_size; @@ -1551,8 +1547,9 @@ pub fn sortSections(self: *Elf) !void { } for (self.objects.items) |index| { - for (self.getFile(index).?.object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + const object = self.getFile(index).?.object; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_shndx = backlinks[atom.out_shndx]; } @@ -2084,8 +2081,8 @@ fn markEhFrameAtomsDead(self: *Elf) void { const file = self.getFile(index).?; if (!file.isAlive()) continue; const object = file.object; - for (object.atoms.items) |atom_index| { - const atom = self.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; const is_eh_frame = (self.options.cpu_arch.? == .x86_64 and atom.getInputShdr(self).sh_type == elf.SHT_X86_64_UNWIND) or mem.eql(u8, atom.getName(self), ".eh_frame"); if (atom.flags.alive and is_eh_frame) atom.flags.alive = false; @@ -2234,7 +2231,7 @@ fn claimUnresolved(self: *Elf) void { }; global.value = 0; - global.atom = 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; @@ -2403,12 +2400,12 @@ fn writeAtoms(self: *Elf) !void { var stream = std.io.fixedBufferStream(buffer); - for (atoms.items) |atom_index| { - const atom = self.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = self.getAtom(ref).?; assert(atom.flags.alive); const off: u64 = @intCast(atom.value); - log.debug("writing ATOM(%{d},'{s}') at offset 0x{x}", .{ - atom_index, + log.debug("writing ATOM({},'{s}') at offset 0x{x}", .{ + ref, atom.getName(self), shdr.sh_offset + off, }); @@ -2733,63 +2730,6 @@ pub fn getFileHandle(self: Elf, index: File.HandleIndex) File.Handle { return self.file_handles.items[index]; } -pub fn addAtom(self: *Elf) !Atom.Index { - const index = @as(u32, @intCast(self.atoms.items.len)); - const atom = try self.atoms.addOne(self.base.allocator); - atom.* = .{}; - return index; -} - -pub fn getAtom(self: Elf, atom_index: Atom.Index) ?*Atom { - if (atom_index == 0) return null; - assert(atom_index < self.atoms.items.len); - return &self.atoms.items[atom_index]; -} - -pub fn addAtomExtra(self: *Elf, extra: Atom.Extra) !u32 { - const fields = @typeInfo(Atom.Extra).@"struct".fields; - try self.atoms_extra.ensureUnusedCapacity(self.base.allocator, fields.len); - return self.addAtomExtraAssumeCapacity(extra); -} - -pub fn addAtomExtraAssumeCapacity(self: *Elf, extra: Atom.Extra) u32 { - const index = @as(u32, @intCast(self.atoms_extra.items.len)); - const fields = @typeInfo(Atom.Extra).@"struct".fields; - inline for (fields) |field| { - self.atoms_extra.appendAssumeCapacity(switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }); - } - return index; -} - -pub fn getAtomExtra(self: *Elf, index: u32) ?Atom.Extra { - if (index == 0) return null; - const fields = @typeInfo(Atom.Extra).@"struct".fields; - var i: usize = index; - var result: Atom.Extra = undefined; - inline for (fields) |field| { - @field(result, field.name) = switch (field.type) { - u32 => self.atoms_extra.items[i], - else => @compileError("bad field type"), - }; - i += 1; - } - return result; -} - -pub fn setAtomExtra(self: *Elf, index: u32, extra: Atom.Extra) void { - assert(index > 0); - const fields = @typeInfo(Atom.Extra).@"struct".fields; - inline for (fields, 0..) |field, i| { - self.atoms_extra.items[index + i] = switch (field.type) { - u32 => @field(extra, field.name), - else => @compileError("bad field type"), - }; - } -} - fn addThunk(self: *Elf) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const thunk = try self.thunks.addOne(self.base.allocator); @@ -2821,8 +2761,8 @@ fn createThunks(self: *Elf, shndx: u32) !void { const atoms = slice.items(.atoms)[shndx].items; assert(atoms.len > 0); - for (atoms) |atom_index| { - self.getAtom(atom_index).?.value = -1; + for (atoms) |ref| { + self.getAtom(ref).?.value = -1; } var i: usize = 0; @@ -2834,8 +2774,8 @@ fn createThunks(self: *Elf, shndx: u32) !void { i += 1; while (i < atoms.len) : (i += 1) { - const atom_index = atoms[i]; - const atom = self.getAtom(atom_index).?; + const ref = atoms[i]; + const atom = self.getAtom(ref).?; assert(atom.flags.alive); const alignment = try math.powi(u32, 2, atom.alignment); if (@as(i64, @intCast(mem.alignForward(u64, shdr.sh_size, alignment))) - start_atom.value >= Thunk.maxAllowedDistance(cpu_arch)) break; @@ -2848,16 +2788,16 @@ fn createThunks(self: *Elf, shndx: u32) !void { thunk.out_shndx = shndx; // Scan relocs in the group and create trampolines for any unreachable callsite - for (atoms[start..i]) |atom_index| { - const atom = self.getAtom(atom_index).?; + for (atoms[start..i]) |ref| { + const atom = self.getAtom(ref).?; const object = atom.getObject(self); - log.debug("atom({d}) {s}", .{ atom_index, atom.getName(self) }); + 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()]; try thunk.symbols.put(gpa, target, {}); } - try atom.addExtra(.{ .thunk = thunk_index }, self); + atom.addExtra(.{ .thunk = thunk_index }, self); atom.flags.thunk = true; } @@ -2995,6 +2935,11 @@ pub fn getMergeSection(self: *Elf, index: MergeSection.Index) *MergeSection { return &self.merge_sections.items[index]; } +pub fn getAtom(self: *Elf, ref: Ref) ?*Atom { + const file = self.getFile(ref.file) orelse return null; + return file.getAtom(ref.index); +} + pub fn getComdatGroup(self: *Elf, ref: Ref) *ComdatGroup { return self.getFile(ref.file).?.getComdatGroup(ref.index); } @@ -3257,7 +3202,7 @@ pub const LinkObject = struct { const Section = struct { shdr: elf.Elf64_Shdr, - atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, + atoms: std.ArrayListUnmanaged(Ref) = .{}, rela_shndx: u32 = 0, sym_index: u32 = 0, }; diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index 0fdbf406..3eea23d4 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -30,7 +30,7 @@ flags: Flags = .{}, extra: u32 = 0, pub fn getName(self: Atom, elf_file: *Elf) [:0]const u8 { - return elf_file.string_intern.getAssumeExists(self.name); + return self.getObject(elf_file).getString(self.name); } pub fn getAddress(self: Atom, elf_file: *Elf) i64 { @@ -99,14 +99,14 @@ pub fn getPriority(self: Atom, elf_file: *Elf) u64 { pub fn getRelocs(self: Atom, elf_file: *Elf) []const elf.Elf64_Rela { if (self.relocs_shndx == 0) return &[0]elf.Elf64_Rela{}; - const extra = self.getExtra(elf_file).?; + const extra = self.getExtra(elf_file); const object = self.getObject(elf_file); return object.relocs.items[extra.rel_index..][0..extra.rel_count]; } pub fn getThunk(self: Atom, elf_file: *Elf) *Thunk { assert(self.flags.thunk); - const extra = self.getExtra(elf_file).?; + const extra = self.getExtra(elf_file); return elf_file.getThunk(extra.thunk); } @@ -118,25 +118,23 @@ const AddExtraOpts = struct { rel_count: ?u32 = null, }; -pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) !void { - if (atom.getExtra(elf_file) == null) { - atom.extra = try elf_file.addAtomExtra(.{}); - } - var extra = atom.getExtra(elf_file).?; +pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) void { + const object = atom.getObject(elf_file); + var extras = object.getAtomExtra(atom.extra); inline for (@typeInfo(@TypeOf(opts)).@"struct".fields) |field| { if (@field(opts, field.name)) |x| { - @field(extra, field.name) = x; + @field(extras, field.name) = x; } } - atom.setExtra(extra, elf_file); + object.setAtomExtra(atom.extra, extras); } -pub inline fn getExtra(atom: Atom, elf_file: *Elf) ?Extra { - return elf_file.getAtomExtra(atom.extra); +pub fn getExtra(atom: Atom, elf_file: *Elf) Extra { + return atom.getObject(elf_file).getAtomExtra(atom.extra); } -pub inline fn setExtra(atom: Atom, extra: Extra, elf_file: *Elf) void { - elf_file.setAtomExtra(atom.extra, extra); +pub fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void { + atom.getObject(elf_file).setAtomExtra(atom.extra, extras); } pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.Elf64_Rela)) !void { @@ -184,7 +182,7 @@ pub fn writeRelocs(self: Atom, elf_file: *Elf, out_relocs: *std.ArrayList(elf.El pub fn getFdes(self: Atom, elf_file: *Elf) []Fde { if (!self.flags.fde) return &[0]Fde{}; - const extra = self.getExtra(elf_file).?; + const extra = self.getExtra(elf_file); const object = self.getObject(elf_file); return object.fdes.items[extra.fde_start..][0..extra.fde_count]; } @@ -444,7 +442,10 @@ fn reportUndefSymbol(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) !bool { if (!gop.found_existing) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(gpa, self.atom_index); + try gop.value_ptr.append(gpa, .{ + .index = self.atom_index, + .file = self.file, + }); return true; } @@ -737,7 +738,7 @@ fn format2( }); if (atom.flags.fde) { try writer.writeAll(" : fdes{ "); - const extra = atom.getExtra(elf_file).?; + const extra = atom.getExtra(elf_file); for (atom.getFdes(elf_file), extra.fde_start..) |fde, i| { try writer.print("{d}", .{i}); if (!fde.alive) try writer.writeAll("([*])"); diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index 2ef72f69..75396c6d 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -38,7 +38,7 @@ pub fn resolveSymbols(self: *InternalObject, elf_file: *Elf) void { const global = elf_file.getSymbol(index); if (self.asFile().getSymbolRank(this_sym, false) < global.getSymbolRank(elf_file)) { global.value = 0; - global.atom = 0; + global.atom_ref = .{}; global.file = self.index; global.sym_idx = sym_idx; global.ver_idx = elf_file.default_sym_version; diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index e0f4e3ad..953ce428 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -10,9 +10,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, first_global: ?Symbol.Index = null, symbols: std.ArrayListUnmanaged(Symbol.Index) = .{}, -atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, relocs: std.ArrayListUnmanaged(elf.Elf64_Rela) = .{}, +atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_indexes: std.ArrayListUnmanaged(Atom.Index) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, + merge_sections: std.ArrayListUnmanaged(InputMergeSection) = .{}, merge_sections_indexes: std.ArrayListUnmanaged(InputMergeSection.Index) = .{}, @@ -45,6 +48,8 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.strtab.deinit(allocator); self.symbols.deinit(allocator); self.atoms.deinit(allocator); + self.atoms_indexes.deinit(allocator); + self.atoms_extra.deinit(allocator); self.comdat_groups.deinit(allocator); self.comdat_group_data.deinit(allocator); self.relocs.deinit(allocator); @@ -119,6 +124,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { } } + // Allocate atom index 0 to null atom + try self.atoms.append(gpa, .{ .extra = try self.addAtomExtra(gpa, .{}) }); // Append null input merge section try self.merge_sections.append(gpa, .{}); @@ -126,7 +133,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { try self.initSymtab(gpa, elf_file); for (self.shdrs.items, 0..) |shdr, i| { - const atom = elf_file.getAtom(self.atoms.items[i]) orelse continue; + const atom = self.getAtom(self.atoms_indexes.items[i]) orelse continue; if (!atom.flags.alive) continue; if ((elf_file.options.cpu_arch.? == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) or mem.eql(u8, atom.getName(elf_file), ".eh_frame")) @@ -141,8 +148,11 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * defer tracy.end(); const shdrs = self.shdrs.items; - try self.atoms.resize(allocator, shdrs.len); - @memset(self.atoms.items, 0); + try self.atoms.ensureTotalCapacityPrecise(allocator, shdrs.len); + try self.atoms_extra.ensureTotalCapacityPrecise(allocator, shdrs.len * @sizeOf(Atom.Extra)); + try self.atoms_indexes.ensureTotalCapacityPrecise(allocator, shdrs.len); + try self.atoms_indexes.resize(allocator, shdrs.len); + @memset(self.atoms_indexes.items, 0); for (shdrs, 0..) |shdr, i| { if (shdr.sh_flags & elf.SHF_EXCLUDE != 0 and @@ -215,7 +225,19 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * } if (self.skipShdr(shndx, elf_file)) continue; - try self.addAtom(allocator, file, shdr, shndx, name, elf_file); + const size, const alignment = if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) blk: { + const data = try self.preadShdrContentsAlloc(allocator, file, shndx); + defer allocator.free(data); + const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; + break :blk .{ chdr.ch_size, chdr.ch_addralign }; + } else .{ shdr.sh_size, shdr.sh_addralign }; + const atom_index = self.addAtomAssumeCapacity(.{ + .name = shdr.sh_name, + .shndx = shndx, + .size = size, + .alignment = alignment, + }); + self.atoms_indexes.items[shndx] = atom_index; }, } } @@ -223,14 +245,14 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * // Parse relocs sections if any. for (shdrs, 0..) |shdr, i| switch (shdr.sh_type) { elf.SHT_REL, elf.SHT_RELA => { - const atom_index = self.atoms.items[shdr.sh_info]; - if (elf_file.getAtom(atom_index)) |atom| { + const atom_index = self.atoms_indexes.items[shdr.sh_info]; + if (self.getAtom(atom_index)) |atom| { const relocs = try self.preadRelocsAlloc(allocator, file, @intCast(i)); defer allocator.free(relocs); atom.relocs_shndx = @intCast(i); const rel_index: u32 = @intCast(self.relocs.items.len); const rel_count: u32 = @intCast(relocs.len); - try atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file); + atom.addExtra(.{ .rel_index = rel_index, .rel_count = rel_count }, elf_file); try self.relocs.appendUnalignedSlice(allocator, relocs); sortRelocs(self.relocs.items[rel_index..][0..rel_count], elf_file); } @@ -239,27 +261,6 @@ fn initAtoms(self: *Object, allocator: Allocator, file: std.fs.File, elf_file: * }; } -fn addAtom(self: *Object, allocator: Allocator, file: std.fs.File, shdr: elf.Elf64_Shdr, shndx: u32, name: [:0]const u8, elf_file: *Elf) !void { - const atom_index = try elf_file.addAtom(); - const atom = elf_file.getAtom(atom_index).?; - atom.atom_index = atom_index; - atom.name = try elf_file.string_intern.insert(elf_file.base.allocator, name); - atom.file = self.index; - atom.shndx = shndx; - self.atoms.items[shndx] = atom_index; - - if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) { - const data = try self.preadShdrContentsAlloc(allocator, file, shndx); - defer allocator.free(data); - const chdr = @as(*align(1) const elf.Elf64_Chdr, @ptrCast(data.ptr)).*; - atom.size = chdr.ch_size; - atom.alignment = math.log2_int(u64, chdr.ch_addralign); - } else { - atom.size = shdr.sh_size; - atom.alignment = math.log2_int(u64, shdr.sh_addralign); - } -} - fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { const shdr = self.shdrs.items[index]; const name = self.getString(shdr.sh_name); @@ -292,7 +293,10 @@ fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { .value = @intCast(sym.st_value), .name = try elf_file.string_intern.insert(elf_file.base.allocator, name), .sym_idx = @as(u32, @intCast(i)), - .atom = if (sym.st_shndx == elf.SHN_ABS) 0 else self.atoms.items[sym.st_shndx], + .atom_ref = if (sym.st_shndx == elf.SHN_ABS) .{} else .{ + .index = self.atoms_indexes.items[sym.st_shndx], + .file = self.index, + }, .file = self.index, }; } @@ -439,7 +443,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u const next_fde = self.fdes.items[i]; if (atom.atom_index != next_fde.getAtom(elf_file).atom_index) break; } - try atom.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file); + atom.addExtra(.{ .fde_start = start, .fde_count = i - start }, elf_file); atom.flags.fde = true; } } @@ -490,8 +494,8 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); - for (self.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + for (self.atoms_indexes.items) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const shdr = atom.getInputShdr(elf_file); if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; @@ -527,19 +531,22 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void { if (this_sym.st_shndx == elf.SHN_UNDEF) continue; if (this_sym.st_shndx != elf.SHN_ABS and this_sym.st_shndx != elf.SHN_COMMON) { - const atom_index = self.atoms.items[this_sym.st_shndx]; - const atom = elf_file.getAtom(atom_index) orelse continue; + const atom_index = self.atoms_indexes.items[this_sym.st_shndx]; + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; } const global = elf_file.getSymbol(index); if (self.asFile().getSymbolRank(this_sym, !self.alive) < global.getSymbolRank(elf_file)) { - const atom = switch (this_sym.st_shndx) { - elf.SHN_ABS, elf.SHN_COMMON => 0, - else => self.atoms.items[this_sym.st_shndx], + 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 = atom; + global.atom_ref = atom_ref; global.sym_idx = sym_idx; global.file = self.index; global.ver_idx = elf_file.default_sym_version; @@ -584,8 +591,8 @@ pub fn checkDuplicates(self: *Object, elf_file: *Elf) bool { this_sym.st_shndx == elf.SHN_COMMON) continue; if (this_sym.st_shndx != elf.SHN_ABS) { - const atom_index = self.atoms.items[this_sym.st_shndx]; - const atom = elf_file.getAtom(atom_index) orelse continue; + const atom_index = self.atoms_indexes.items[this_sym.st_shndx]; + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; } @@ -612,8 +619,8 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { for (self.shdrs.items, 0..) |shdr, shndx| { if (shdr.sh_flags & elf.SHF_MERGE == 0) continue; - const atom_index = self.atoms.items[shndx]; - const atom = elf_file.getAtom(atom_index) orelse continue; + const atom_index = self.atoms_indexes.items[shndx]; + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; if (atom.getRelocs(elf_file).len > 0) continue; @@ -689,7 +696,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const imsec = self.getInputMergeSection(index) orelse continue; if (imsec.offsets.items.len == 0) continue; const msec = elf_file.getMergeSection(imsec.merge_section); - const atom = elf_file.getAtom(imsec.atom).?; + const atom = self.getAtom(imsec.atom).?; const isec = atom.getInputShdr(elf_file); try imsec.subsections.resize(gpa, imsec.strings.items.len); @@ -737,10 +744,10 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { sym.value = offset; } - for (self.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + for (self.atoms_indexes.items) |atom_index| { + const atom = self.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; - const extra = atom.getExtra(elf_file) orelse continue; + const extra = atom.getExtra(elf_file); if (extra.rel_count == 0) continue; const relocs = self.relocs.items[extra.rel_index..][0..extra.rel_count]; for (relocs) |*rel| { @@ -789,9 +796,9 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { 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]; - if (this_sym.st_shndx != elf.SHN_COMMON) continue; + 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).?; @@ -806,41 +813,39 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { } const gpa = elf_file.base.allocator; - - const atom_index = try elf_file.addAtom(); - try self.atoms.append(gpa, atom_index); - const is_tls = global.getType(elf_file) == elf.STT_TLS; const name = if (is_tls) ".tls_common" else ".common"; - - const atom = elf_file.getAtom(atom_index).?; - atom.atom_index = atom_index; - atom.name = try elf_file.string_intern.insert(gpa, name); - atom.file = self.index; - atom.size = this_sym.st_size; - const alignment = this_sym.st_value; - atom.alignment = math.log2_int(u64, alignment); + const name_offset = @as(u32, @intCast(self.strtab.items.len)); + try self.strtab.writer(gpa).print("{s}\x00", .{name}); var sh_flags: u32 = elf.SHF_ALLOC | elf.SHF_WRITE; if (is_tls) sh_flags |= elf.SHF_TLS; const shndx = @as(u32, @intCast(self.shdrs.items.len)); const shdr = try self.shdrs.addOne(gpa); + const sh_size = math.cast(usize, esym.st_size) orelse return error.Overflow; shdr.* = .{ - .sh_name = try self.addString(gpa, name), + .sh_name = name_offset, .sh_type = elf.SHT_NOBITS, .sh_flags = sh_flags, .sh_addr = 0, .sh_offset = 0, - .sh_size = this_sym.st_size, + .sh_size = sh_size, .sh_link = 0, .sh_info = 0, - .sh_addralign = alignment, + .sh_addralign = esym.st_value, .sh_entsize = 0, }; - atom.shndx = shndx; + + const atom_index = try self.addAtom(gpa, .{ + .name = name_offset, + .shndx = shndx, + .size = esym.st_size, + .alignment = esym.st_value, + }); + try self.atoms_indexes.append(gpa, atom_index); global.value = 0; - global.atom = atom_index; + global.atom_ref = .{ .index = atom_index, .file = self.index }; global.flags.weak = false; } } @@ -867,8 +872,8 @@ pub fn markComdatGroupsDead(self: *Object, elf_file: *Elf) void { for (self.comdat_groups.items) |cg| { if (cg.alive) continue; for (cg.getComdatGroupMembers(elf_file)) |shndx| { - const atom_index = self.atoms.items[shndx]; - if (elf_file.getAtom(atom_index)) |atom_ptr| { + const atom_index = self.atoms_indexes.items[shndx]; + if (self.getAtom(atom_index)) |atom_ptr| { atom_ptr.flags.alive = false; atom_ptr.markFdesDead(elf_file); } @@ -998,6 +1003,82 @@ pub fn asFile(self: *Object) File { return .{ .object = self }; } +const AddAtomArgs = struct { + name: u32, + shndx: u32, + size: u64, + alignment: u64, +}; + +fn addAtom(self: *Object, allocator: Allocator, args: AddAtomArgs) !Atom.Index { + try self.atoms.ensureUnusedCapacity(allocator, 1); + try self.atoms_extra.ensureUnusedCapacity(allocator, @sizeOf(Atom.Extra)); + return self.addAtomAssumeCapacity(args); +} + +fn addAtomAssumeCapacity(self: *Object, args: AddAtomArgs) Atom.Index { + const atom_index: Atom.Index = @intCast(self.atoms.items.len); + const atom_ptr = self.atoms.addOneAssumeCapacity(); + atom_ptr.* = .{ + .atom_index = atom_index, + .name = args.name, + .file = self.index, + .shndx = args.shndx, + .extra = self.addAtomExtraAssumeCapacity(.{}), + .size = args.size, + .alignment = math.log2_int(u64, args.alignment), + }; + return atom_index; +} + +pub fn getAtom(self: *Object, atom_index: Atom.Index) ?*Atom { + if (atom_index == 0) return null; + assert(atom_index < self.atoms.items.len); + return &self.atoms.items[atom_index]; +} + +pub fn addAtomExtra(self: *Object, allocator: Allocator, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).@"struct".fields; + try self.atoms_extra.ensureUnusedCapacity(allocator, fields.len); + return self.addAtomExtraAssumeCapacity(extra); +} + +pub fn addAtomExtraAssumeCapacity(self: *Object, extra: Atom.Extra) u32 { + const index = @as(u32, @intCast(self.atoms_extra.items.len)); + const fields = @typeInfo(Atom.Extra).@"struct".fields; + inline for (fields) |field| { + self.atoms_extra.appendAssumeCapacity(switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }); + } + return index; +} + +pub fn getAtomExtra(self: *Object, index: u32) Atom.Extra { + const fields = @typeInfo(Atom.Extra).@"struct".fields; + var i: usize = index; + var result: Atom.Extra = undefined; + inline for (fields) |field| { + @field(result, field.name) = switch (field.type) { + u32 => self.atoms_extra.items[i], + else => @compileError("bad field type"), + }; + i += 1; + } + return result; +} + +pub fn setAtomExtra(self: *Object, index: u32, extra: Atom.Extra) void { + const fields = @typeInfo(Atom.Extra).@"struct".fields; + inline for (fields, 0..) |field, i| { + self.atoms_extra.items[index + i] = switch (field.type) { + u32 => @field(extra, field.name), + else => @compileError("bad field type"), + }; + } +} + fn addInputMergeSection(self: *Object, allocator: Allocator) !InputMergeSection.Index { const index: InputMergeSection.Index = @intCast(self.merge_sections.items.len); const msec = try self.merge_sections.addOne(allocator); @@ -1084,8 +1165,8 @@ fn formatAtoms( _ = options; const object = ctx.object; try writer.writeAll(" atoms\n"); - for (object.atoms.items) |atom_index| { - const atom = ctx.elf_file.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; try writer.print(" {}\n", .{atom.fmt(ctx.elf_file)}); } } @@ -1158,8 +1239,8 @@ fn formatComdatGroups( try writer.writeByte('\n'); const cg_members = cg.getComdatGroupMembers(elf_file); for (cg_members) |shndx| { - const atom_index = object.atoms.items[shndx]; - const atom = elf_file.getAtom(atom_index) orelse continue; + const atom_index = object.atoms_indexes.items[shndx]; + const atom = object.getAtom(atom_index) orelse continue; try writer.print(" atom({d}) : {s}\n", .{ atom_index, atom.getName(elf_file) }); } } diff --git a/src/Elf/SharedObject.zig b/src/Elf/SharedObject.zig index 16b4de90..79a0ff99 100644 --- a/src/Elf/SharedObject.zig +++ b/src/Elf/SharedObject.zig @@ -201,7 +201,7 @@ pub fn resolveSymbols(self: *SharedObject, elf_file: *Elf) void { 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 = 0; + global.atom_ref = .{}; global.sym_idx = sym_idx; global.ver_idx = self.versyms.items[sym_idx]; global.file = self.index; diff --git a/src/Elf/Symbol.zig b/src/Elf/Symbol.zig index 2c27f2fd..42ad0e1d 100644 --- a/src/Elf/Symbol.zig +++ b/src/Elf/Symbol.zig @@ -10,9 +10,8 @@ name: u32 = 0, file: File.Index = 0, /// Atom containing this symbol if any. -/// Index of 0 means there is no associated atom with this symbol. /// Use `getAtom` to get the pointer to the atom. -atom: Atom.Index = 0, +atom_ref: Elf.Ref = .{}, /// Assigned output section index for this symbol. shndx: u32 = 0, @@ -57,7 +56,7 @@ pub fn getName(symbol: Symbol, elf_file: *Elf) [:0]const u8 { } pub fn getAtom(symbol: Symbol, elf_file: *Elf) ?*Atom { - return elf_file.getAtom(symbol.atom); + return elf_file.getAtom(symbol.atom_ref); } pub fn getMergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection { diff --git a/src/Elf/eh_frame.zig b/src/Elf/eh_frame.zig index 1d27c426..5038a2d7 100644 --- a/src/Elf/eh_frame.zig +++ b/src/Elf/eh_frame.zig @@ -47,8 +47,8 @@ pub const Fde = struct { const relocs = fde.getRelocs(elf_file); const rel = relocs[0]; const sym = object.symtab.items[rel.r_sym()]; - const atom_index = object.atoms.items[sym.st_shndx]; - return elf_file.getAtom(atom_index).?; + const atom_index = object.atoms_indexes.items[sym.st_shndx]; + return object.getAtom(atom_index).?; } pub fn getRelocs(fde: Fde, elf_file: *Elf) []align(1) const elf.Elf64_Rela { diff --git a/src/Elf/file.zig b/src/Elf/file.zig index 18bb24c1..0b608d8e 100644 --- a/src/Elf/file.zig +++ b/src/Elf/file.zig @@ -85,6 +85,13 @@ pub const File = union(enum) { } } + pub fn getAtom(file: File, ind: Atom.Index) ?*Atom { + return switch (file) { + .internal, .shared => unreachable, + .object => |x| x.getAtom(ind), + }; + } + pub fn getComdatGroup(file: File, ind: Elf.ComdatGroup.Index) *Elf.ComdatGroup { return switch (file) { .internal, .shared => unreachable, @@ -134,6 +141,7 @@ const std = @import("std"); const elf = std.elf; const Allocator = std.mem.Allocator; +const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const InternalObject = @import("InternalObject.zig"); const Object = @import("Object.zig"); diff --git a/src/Elf/gc.zig b/src/Elf/gc.zig index 084f4a73..c94ce0b4 100644 --- a/src/Elf/gc.zig +++ b/src/Elf/gc.zig @@ -31,8 +31,8 @@ fn collectRoots(roots: *std.ArrayList(*Atom), elf_file: *Elf) !void { for (elf_file.objects.items) |index| { const object = elf_file.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; const shdr = atom.getInputShdr(elf_file); @@ -125,8 +125,9 @@ fn prune(elf_file: *Elf) void { defer tracy.end(); for (elf_file.objects.items) |index| { - for (elf_file.getFile(index).?.object.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + const object = elf_file.getFile(index).?.object; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (atom.flags.alive and !atom.flags.visited) { atom.flags.alive = false; atom.markFdesDead(elf_file); @@ -138,8 +139,9 @@ fn prune(elf_file: *Elf) void { pub fn dumpPrunedAtoms(elf_file: *Elf) !void { const stderr = std.io.getStdErr().writer(); for (elf_file.objects.items) |index| { - for (elf_file.getFile(index).?.object.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + const object = elf_file.getFile(index).?.object; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) try stderr.print("ld.zld: removing unused section '{s}' in file '{}'\n", .{ atom.getName(elf_file), diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index 6cfd63f8..67bf2943 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -46,7 +46,7 @@ fn claimUnresolved(elf_file: *Elf) void { } global.value = 0; - global.atom = 0; + global.atom_ref = .{}; global.sym_idx = sym_idx; global.file = object.index; } @@ -60,8 +60,8 @@ fn initSections(elf_file: *Elf) !void { for (elf_file.objects.items) |index| { const object = elf_file.getFile(index).?.object; - for (object.atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + for (object.atoms_indexes.items) |atom_index| { + const atom = object.getAtom(atom_index) orelse continue; if (!atom.flags.alive) continue; atom.out_shndx = try object.initOutputSection(elf_file, atom.getInputShdr(elf_file)); @@ -155,8 +155,8 @@ fn calcSectionSizes(elf_file: *Elf) !void { const rela_shdr = if (rela_shndx != 0) &elf_file.sections.items(.shdr)[rela_shndx] else null; - for (atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = elf_file.getAtom(ref).?; const alignment = try math.powi(u64, 2, atom.alignment); const offset = mem.alignForward(u64, shdr.sh_size, alignment); const padding = offset - shdr.sh_size; @@ -235,12 +235,12 @@ fn writeAtoms(elf_file: *Elf) !void { 0; @memset(buffer, padding_byte); - for (atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index).?; + for (atoms.items) |ref| { + const atom = elf_file.getAtom(ref).?; assert(atom.flags.alive); const off: u64 = @intCast(atom.value); - log.debug("writing ATOM(%{d},'{s}') at offset 0x{x}", .{ - atom_index, + log.debug("writing ATOM({},'{s}') at offset 0x{x}", .{ + ref, atom.getName(elf_file), shdr.sh_offset + off, }); @@ -272,8 +272,8 @@ fn writeSyntheticSections(elf_file: *Elf) !void { var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs); defer relocs.deinit(); - for (atoms.items) |atom_index| { - const atom = elf_file.getAtom(atom_index) orelse continue; + for (atoms.items) |ref| { + const atom = elf_file.getAtom(ref) orelse continue; if (!atom.flags.alive) continue; try atom.writeRelocs(elf_file, &relocs); } diff --git a/src/Elf/synthetic.zig b/src/Elf/synthetic.zig index a825e692..33444a41 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -1439,8 +1439,8 @@ pub const ComdatGroupSection = struct { const shdr = object.shdrs.items[shndx]; switch (shdr.sh_type) { elf.SHT_RELA => { - const atom_index = object.atoms.items[shdr.sh_info]; - const atom = elf_file.getAtom(atom_index).?; + const atom_index = object.atoms_indexes.items[shdr.sh_info]; + const atom = object.getAtom(atom_index).?; const rela_shndx = for (elf_file.sections.items(.shdr), 0..) |rela_shdr, rela_shndx| { if (rela_shdr.sh_type == elf.SHT_RELA and atom.out_shndx == rela_shdr.sh_info) @@ -1449,8 +1449,8 @@ pub const ComdatGroupSection = struct { try writer.writeInt(u32, @intCast(rela_shndx), .little); }, else => { - const atom_index = object.atoms.items[shndx]; - const atom = elf_file.getAtom(atom_index).?; + const atom_index = object.atoms_indexes.items[shndx]; + const atom = object.getAtom(atom_index).?; try writer.writeInt(u32, atom.out_shndx, .little); }, } From 1445d7a9857a710fc9eb73d5127e8e2fb5b224ad Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 12 Sep 2024 06:00:43 +0200 Subject: [PATCH 04/19] elf: rename Object.initSymtab to Object.initSymbols --- src/Elf/Object.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index 953ce428..5b85dd45 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -130,7 +130,7 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { try self.merge_sections.append(gpa, .{}); try self.initAtoms(gpa, file, elf_file); - try self.initSymtab(gpa, elf_file); + try self.initSymbols(gpa, elf_file); for (self.shdrs.items, 0..) |shdr, i| { const atom = self.getAtom(self.atoms_indexes.items[i]) orelse continue; @@ -276,7 +276,7 @@ fn skipShdr(self: *Object, index: u32, elf_file: *Elf) bool { return ignore; } -fn initSymtab(self: *Object, allocator: Allocator, elf_file: *Elf) !void { +fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); From 2c928eb292f7668691dd09cf3475f1e2f50e9162 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 15 Sep 2024 06:05:06 +0200 Subject: [PATCH 05/19] 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 { From 7c8c9af50cb0f267a2f24057b61029e697bc66e0 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 07:31:28 +0200 Subject: [PATCH 06/19] elf: fix handling of section names in shstrtab --- src/Elf.zig | 51 ++++++++++++++++++++--------------------- src/Elf/Object.zig | 2 +- src/Elf/relocatable.zig | 11 ++++----- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index 1ea7e0ab..4dec945e 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -243,7 +243,7 @@ pub fn flush(self: *Elf) !void { try self.strtab.append(gpa, 0); try self.dynstrtab.append(gpa, 0); // Append null section. - _ = try self.addSection(.{ .name = "" }); + _ = try self.addSection(.{}); // Append null symbol. try self.symtab.append(gpa, null_sym); // Append null file. @@ -483,9 +483,8 @@ fn initOutputSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { if (msec.subsections.items.len == 0) continue; - const name = msec.getName(self); - const shndx = self.getSectionByName(name) orelse try self.addSection(.{ - .name = name, + const shndx = self.getSectionByName(msec.getName(self)) orelse try self.addSection(.{ + .name = msec.name, .type = msec.type, .flags = msec.flags, }); @@ -509,7 +508,7 @@ fn initSyntheticSections(self: *Elf) !void { } else false; if (needs_eh_frame) { self.eh_frame_sect_index = try self.addSection(.{ - .name = ".eh_frame", + .name = try self.insertShString(".eh_frame"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = @alignOf(u64), @@ -517,7 +516,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.options.eh_frame_hdr) { self.eh_frame_hdr_sect_index = try self.addSection(.{ - .name = ".eh_frame_hdr", + .name = try self.insertShString(".eh_frame_hdr"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = @alignOf(u32), @@ -527,7 +526,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.got.entries.items.len > 0) { self.got_sect_index = try self.addSection(.{ - .name = ".got", + .name = try self.insertShString(".got"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .addralign = @alignOf(u64), @@ -535,7 +534,7 @@ fn initSyntheticSections(self: *Elf) !void { } self.got_plt_sect_index = try self.addSection(.{ - .name = ".got.plt", + .name = try self.insertShString(".got.plt"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .addralign = @alignOf(u64), @@ -551,7 +550,7 @@ fn initSyntheticSections(self: *Elf) !void { }; if (needs_rela_dyn) { self.rela_dyn_sect_index = try self.addSection(.{ - .name = ".rela.dyn", + .name = try self.insertShString(".rela.dyn"), .type = elf.SHT_RELA, .flags = elf.SHF_ALLOC, .addralign = @alignOf(elf.Elf64_Rela), @@ -561,13 +560,13 @@ fn initSyntheticSections(self: *Elf) !void { if (self.plt.symbols.items.len > 0) { self.plt_sect_index = try self.addSection(.{ - .name = ".plt", + .name = try self.insertShString(".plt"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 16, }); self.rela_plt_sect_index = try self.addSection(.{ - .name = ".rela.plt", + .name = try self.insertShString(".rela.plt"), .type = elf.SHT_RELA, .flags = elf.SHF_ALLOC, .addralign = @alignOf(elf.Elf64_Rela), @@ -577,7 +576,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.plt_got.symbols.items.len > 0) { self.plt_got_sect_index = try self.addSection(.{ - .name = ".plt.got", + .name = try self.insertShString(".plt.got"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, .addralign = 16, @@ -586,7 +585,7 @@ fn initSyntheticSections(self: *Elf) !void { if (self.copy_rel.symbols.items.len > 0) { self.copy_rel_sect_index = try self.addSection(.{ - .name = ".copyrel", + .name = try self.insertShString(".copyrel"), .type = elf.SHT_NOBITS, .flags = elf.SHF_ALLOC | elf.SHF_WRITE, }); @@ -609,7 +608,7 @@ fn initSyntheticSections(self: *Elf) !void { }; if (needs_interp) { self.interp_sect_index = try self.addSection(.{ - .name = ".interp", + .name = try self.insertShString(".interp"), .type = elf.SHT_PROGBITS, .flags = elf.SHF_ALLOC, .addralign = 1, @@ -618,35 +617,35 @@ fn initSyntheticSections(self: *Elf) !void { if (self.options.shared or self.shared_objects.items.len > 0 or self.options.pie) { self.dynstrtab_sect_index = try self.addSection(.{ - .name = ".dynstr", + .name = try self.insertShString(".dynstr"), .flags = elf.SHF_ALLOC, .type = elf.SHT_STRTAB, .entsize = 1, .addralign = 1, }); self.dynamic_sect_index = try self.addSection(.{ - .name = ".dynamic", + .name = try self.insertShString(".dynamic"), .flags = elf.SHF_ALLOC | elf.SHF_WRITE, .type = elf.SHT_DYNAMIC, .entsize = @sizeOf(elf.Elf64_Dyn), .addralign = @alignOf(elf.Elf64_Dyn), }); self.dynsymtab_sect_index = try self.addSection(.{ - .name = ".dynsym", + .name = try self.insertShString(".dynsym"), .flags = elf.SHF_ALLOC, .type = elf.SHT_DYNSYM, .addralign = @alignOf(elf.Elf64_Sym), .entsize = @sizeOf(elf.Elf64_Sym), }); self.hash_sect_index = try self.addSection(.{ - .name = ".hash", + .name = try self.insertShString(".hash"), .flags = elf.SHF_ALLOC, .type = elf.SHT_HASH, .addralign = 4, .entsize = 4, }); self.gnu_hash_sect_index = try self.addSection(.{ - .name = ".gnu.hash", + .name = try self.insertShString(".gnu.hash"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_HASH, .addralign = 8, @@ -658,14 +657,14 @@ fn initSyntheticSections(self: *Elf) !void { } else false; if (needs_versions) { self.versym_sect_index = try self.addSection(.{ - .name = ".gnu.version", + .name = try self.insertShString(".gnu.version"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_VERSYM, .addralign = @alignOf(elf.Elf64_Versym), .entsize = @sizeOf(elf.Elf64_Versym), }); self.verneed_sect_index = try self.addSection(.{ - .name = ".gnu.version_r", + .name = try self.insertShString(".gnu.version_r"), .flags = elf.SHF_ALLOC, .type = elf.SHT_GNU_VERNEED, .addralign = @alignOf(elf.Elf64_Verneed), @@ -676,13 +675,13 @@ fn initSyntheticSections(self: *Elf) !void { pub fn initSymtab(self: *Elf) !void { self.strtab_sect_index = try self.addSection(.{ - .name = ".strtab", + .name = try self.insertShString(".strtab"), .type = elf.SHT_STRTAB, .entsize = 1, .addralign = 1, }); self.symtab_sect_index = try self.addSection(.{ - .name = ".symtab", + .name = try self.insertShString(".symtab"), .type = elf.SHT_SYMTAB, .addralign = @alignOf(elf.Elf64_Sym), .entsize = @sizeOf(elf.Elf64_Sym), @@ -691,7 +690,7 @@ pub fn initSymtab(self: *Elf) !void { pub fn initShStrtab(self: *Elf) !void { self.shstrtab_sect_index = try self.addSection(.{ - .name = ".shstrtab", + .name = try self.insertShString(".shstrtab"), .type = elf.SHT_STRTAB, .entsize = 1, .addralign = 1, @@ -2332,7 +2331,7 @@ fn writeHeader(self: *Elf) !void { } pub const AddSectionOpts = struct { - name: [:0]const u8, + name: u32 = 0, type: u32 = elf.SHT_NULL, flags: u64 = 0, link: u32 = 0, @@ -2347,7 +2346,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.insertShString(opts.name), + .sh_name = opts.name, .sh_type = opts.type, .sh_flags = opts.flags, .sh_addr = 0, diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index ec10910a..dd813a8f 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -354,7 +354,7 @@ pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) !u3 return elf_file.getSectionByName(name) orelse try elf_file.addSection(.{ .type = @"type", .flags = flags, - .name = name, + .name = try elf_file.insertShString(name), }); } diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index bb14de7f..b58f66ff 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -63,9 +63,8 @@ fn initSections(elf_file: *Elf) !void { for (elf_file.merge_sections.items) |*msec| { if (msec.subsections.items.len == 0) continue; - const name = msec.getName(elf_file); - const shndx = elf_file.getSectionByName(name) orelse try elf_file.addSection(.{ - .name = name, + const shndx = elf_file.getSectionByName(msec.getName(elf_file)) orelse try elf_file.addSection(.{ + .name = msec.name, .type = msec.type, .flags = msec.flags, }); @@ -85,13 +84,13 @@ fn initSections(elf_file: *Elf) !void { } else false; if (needs_eh_frame) { elf_file.eh_frame_sect_index = try elf_file.addSection(.{ - .name = ".eh_frame", + .name = try elf_file.insertShString(".eh_frame"), .flags = elf.SHF_ALLOC, .type = elf.SHT_PROGBITS, .addralign = @alignOf(u64), }); const rela_shndx = try elf_file.addSection(.{ - .name = ".rela.eh_frame", + .name = try elf_file.insertShString(".rela.eh_frame"), .type = elf.SHT_RELA, .flags = elf.SHF_INFO_LINK, .entsize = @sizeOf(elf.Elf64_Rela), @@ -115,7 +114,7 @@ fn initComdatGroups(elf_file: *Elf) !void { const cg_sec = try elf_file.comdat_group_sections.addOne(gpa); cg_sec.* = .{ .shndx = try elf_file.addSection(.{ - .name = ".group", + .name = try elf_file.insertShString(".group"), .type = elf.SHT_GROUP, .entsize = @sizeOf(u32), .addralign = @alignOf(u32), From 95c3abe17bcaa0e41330c52a8bcddaa21754472d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 07:50:15 +0200 Subject: [PATCH 07/19] elf: fix resolving relocs targets --- src/Elf.zig | 6 +----- src/Elf/Atom.zig | 15 +++++++++------ src/Elf/synthetic.zig | 30 ++++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index 4dec945e..2118b985 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -2794,11 +2794,7 @@ fn fmtDumpState( try writer.print("thunk({d}) : {}\n", .{ index, thunk.fmt(self) }); } try writer.print("GOT\n{}\n", .{self.got.fmt(self)}); - try writer.writeAll("PLT\n"); - 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.print("PLT\n{}\n", .{self.plt.fmt(self)}); try writer.writeAll("PLTGOT\n"); for (self.plt_got.symbols.items, 0..) |ref, i| { try writer.print(" {d} => {} '{s}'\n", .{ i, ref, self.getSymbol(ref).?.getName(self) }); diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index 203cb257..e5869d93 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -146,7 +146,8 @@ 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.symbols.items[rel.r_sym()]; + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.getSymbol(target_ref).?; const r_type = rel.r_type(); const r_offset: u64 = @intCast(self.value + @as(i64, @intCast(rel.r_offset))); var r_addend = rel.r_addend; @@ -475,7 +476,8 @@ 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.symbols.items[rel.r_sym()]; + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target = elf_file.getSymbol(target_ref).?; // We will use equation format to resolve relocations: // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ @@ -509,15 +511,15 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, writer: anytype) !void { const args = ResolveArgs{ P, A, S, GOT, G, TP, DTP }; switch (cpu_arch) { - .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { + .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target.*, args, &it, code, &stream) catch |err| switch (err) { error.RelocError => has_reloc_errors = true, else => |e| return e, }, - .aarch64 => aarch64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { + .aarch64 => aarch64.resolveRelocAlloc(self, elf_file, rel, target.*, args, &it, code, &stream) catch |err| switch (err) { error.RelocError => has_reloc_errors = true, else => |e| return e, }, - .riscv64 => riscv.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { + .riscv64 => riscv.resolveRelocAlloc(self, elf_file, rel, target.*, args, &it, code, &stream) catch |err| switch (err) { error.RelocError => has_reloc_errors = true, else => |e| return e, }, @@ -1759,7 +1761,8 @@ const riscv = struct { return error.RelocError; }; it.pos = pos; - const target_ = object.symbols.items[pair.r_sym()]; + const target_ref_ = object.resolveSymbol(pair.r_sym(), elf_file); + const target_ = elf_file.getSymbol(target_ref_).?; 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/synthetic.zig b/src/Elf/synthetic.zig index 0c5775dd..133e542b 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -1084,6 +1084,36 @@ pub const PltSection = struct { } } + const FormatCtx = struct { + plt: PltSection, + elf_file: *Elf, + }; + + pub fn fmt(plt: PltSection, elf_file: *Elf) std.fmt.Formatter(format2) { + return .{ .data = .{ .plt = plt, .elf_file = elf_file } }; + } + + pub fn format2( + ctx: FormatCtx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = options; + _ = unused_fmt_string; + const plt = ctx.plt; + const elf_file = ctx.elf_file; + for (plt.symbols.items, 0..) |ref, i| { + const symbol = elf_file.getSymbol(ref).?; + try writer.print(" {d}@0x{x} => {} ({s})\n", .{ + i, + symbol.getAddress(.{}, elf_file), + ref, + symbol.getName(elf_file), + }); + } + } + const x86_64 = struct { fn write(plt: PltSection, elf_file: *Elf, writer: anytype) !void { const plt_addr = elf_file.sections.items(.shdr)[elf_file.plt_sect_index.?].sh_addr; From 0af7370d9185a46bdbf7d5663b54b0c5aab02d28 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 07:59:17 +0200 Subject: [PATCH 08/19] elf: fix gc --- src/Elf/gc.zig | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Elf/gc.zig b/src/Elf/gc.zig index faa853a7..0291190e 100644 --- a/src/Elf/gc.zig +++ b/src/Elf/gc.zig @@ -60,7 +60,8 @@ 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.symbols.items[rel.r_sym()]; + const ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(ref) orelse continue; try markSymbol(sym, roots, elf_file); } } @@ -97,14 +98,16 @@ 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.symbols.items[rel.r_sym()]; + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target_sym = elf_file.getSymbol(target_ref) orelse continue; 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.symbols.items[rel.r_sym()]; + const target_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const target_sym = elf_file.getSymbol(target_ref) orelse continue; if (target_sym.getMergeSubsection(elf_file)) |msub| { msub.alive = true; continue; From 9cdcf6158caa43c62341a678d6522267d8676015 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 12:22:06 +0200 Subject: [PATCH 09/19] elf: fix typo in Symbol.isAbs --- src/Elf/Symbol.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elf/Symbol.zig b/src/Elf/Symbol.zig index 9e8107e5..c1dd0d68 100644 --- a/src/Elf/Symbol.zig +++ b/src/Elf/Symbol.zig @@ -32,7 +32,7 @@ extra: u32 = 0, pub fn isAbs(symbol: Symbol, elf_file: *Elf) bool { const file = symbol.getFile(elf_file).?; 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; + return !symbol.flags.import and symbol.getAtom(elf_file) == null and symbol.getMergeSubsection(elf_file) == null and symbol.getShndx(elf_file) == null and file != .internal; } pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { From e7219431276cc1363d903d9a384398d4dff56bd7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 13:20:37 +0200 Subject: [PATCH 10/19] elf: fix spawning merge sections --- src/Elf.zig | 146 ++++++++++++++++++++------------------ src/Elf/Atom.zig | 4 ++ src/Elf/Object.zig | 101 +++++++++++--------------- src/Elf/Symbol.zig | 6 +- src/Elf/merge_section.zig | 75 +++++++++++++++----- src/Elf/relocatable.zig | 36 ++-------- 6 files changed, 184 insertions(+), 184 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index 2118b985..b9007714 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -64,7 +64,6 @@ comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .{}, thunks: std.ArrayListUnmanaged(Thunk) = .{}, merge_sections: std.ArrayListUnmanaged(MergeSection) = .{}, -merge_subsections: std.ArrayListUnmanaged(MergeSubsection) = .{}, has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, @@ -121,7 +120,6 @@ pub fn deinit(self: *Elf) void { sect.deinit(gpa); } self.merge_sections.deinit(gpa); - self.merge_subsections.deinit(gpa); self.resolver.deinit(gpa); self.got.deinit(gpa); self.plt.deinit(gpa); @@ -364,7 +362,6 @@ pub fn flush(self: *Elf) !void { try self.initSyntheticSections(); try self.sortSections(); - try self.addAtomsToSections(); try self.sortInitFini(); try self.setDynamic(); self.setDynsym(); @@ -473,32 +470,12 @@ fn sortInitFini(self: *Elf) !void { fn initOutputSections(self: *Elf) !void { for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.atoms_indexes.items) |atom_index| { - const atom = object.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - atom.out_shndx = try object.initOutputSection(self, atom.getInputShdr(self)); - } + try self.getFile(index).?.object.initOutputSections(self); } - for (self.merge_sections.items) |*msec| { - if (msec.subsections.items.len == 0) continue; - const shndx = self.getSectionByName(msec.getName(self)) orelse try self.addSection(.{ - .name = msec.name, - .type = msec.type, - .flags = msec.flags, - }); - msec.out_shndx = shndx; - - var entsize = self.getMergeSubsection(msec.subsections.items[0]).entsize; - for (msec.subsections.items) |index| { - const msub = self.getMergeSubsection(index); - entsize = @min(entsize, msub.entsize); - } - const shdr = &self.sections.items(.shdr)[shndx]; - shdr.sh_entsize = entsize; + if (msec.finalized_subsections.items.len == 0) continue; + try msec.initOutputSection(self); } - self.text_sect_index = self.getSectionByName(".text"); } @@ -697,28 +674,13 @@ pub fn initShStrtab(self: *Elf) !void { }); } -pub fn addAtomsToSections(self: *Elf) !void { - for (self.objects.items) |index| { - const object = self.getFile(index).?.object; - for (object.atoms_indexes.items) |atom_index| { - const atom = object.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - const atoms = &self.sections.items(.atoms)[atom.out_shndx]; - try atoms.append(self.base.allocator, .{ - .index = atom_index, - .file = index, - }); - } - } -} - pub fn addCommentString(self: *Elf) !void { const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS); const msec = self.getMergeSection(msec_index); const res = try msec.insertZ(self.base.allocator, Options.version); if (res.found_existing) return; - const msub_index = try self.addMergeSubsection(); - const msub = self.getMergeSubsection(msub_index); + const msub_index = try msec.addMergeSubsection(self.base.allocator); + const msub = msec.getMergeSubsection(msub_index); msub.merge_section = msec_index; msub.string_index = res.key.pos; msub.alignment = 0; @@ -730,23 +692,23 @@ pub fn addCommentString(self: *Elf) !void { pub fn finalizeMergeSections(self: *Elf) !void { for (self.merge_sections.items) |*msec| { - try msec.finalize(self); + try msec.finalize(self.base.allocator); } } pub fn calcMergeSectionSizes(self: *Elf) !void { + for (self.merge_sections.items) |*msec| { + try msec.calcSize(); + } for (self.merge_sections.items) |*msec| { const shdr = &self.sections.items(.shdr)[msec.out_shndx]; - for (msec.subsections.items) |msub_index| { - const msub = self.getMergeSubsection(msub_index); - assert(msub.alive); - const alignment = try math.powi(u64, 2, msub.alignment); - const offset = mem.alignForward(u64, shdr.sh_size, alignment); - const padding = offset - shdr.sh_size; - msub.value = @intCast(offset); - shdr.sh_size += padding + msub.size; - shdr.sh_addralign = @max(shdr.sh_addralign, alignment); - } + const alignment = try math.powi(u64, 2, msec.alignment); + const offset = mem.alignForward(u64, shdr.sh_size, alignment); + const padding = offset - shdr.sh_size; + msec.value = @intCast(offset); + shdr.sh_size += padding + msec.size; + shdr.sh_addralign = @max(shdr.sh_addralign, alignment); + shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize); } } @@ -2118,21 +2080,22 @@ pub fn writeMergeSections(self: *Elf) !void { var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); - for (self.merge_sections.items) |msec| { + for (self.merge_sections.items) |*msec| { const shdr = self.sections.items(.shdr)[msec.out_shndx]; + const fileoff = msec.value + shdr.sh_offset; - try buffer.ensureTotalCapacity(shdr.sh_size); - buffer.appendNTimesAssumeCapacity(0, shdr.sh_size); + try buffer.ensureTotalCapacity(msec.size); + buffer.appendNTimesAssumeCapacity(0, msec.size); - for (msec.subsections.items) |msub_index| { - const msub = self.getMergeSubsection(msub_index); + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.getMergeSubsection(msub_index); assert(msub.alive); - const string = msub.getString(self); + const string = msub.getString(msec); const off: u64 = @intCast(msub.value); @memcpy(buffer.items[off..][0..string.len], string); } - try self.base.file.pwriteAll(buffer.items, shdr.sh_offset); + try self.base.file.pwriteAll(buffer.items, fileoff); buffer.clearRetainingCapacity(); } } @@ -2496,16 +2459,57 @@ fn createThunks(self: *Elf, shndx: u32) !void { } } -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); - msec.* = .{}; - return index; -} - -pub fn getMergeSubsection(self: *Elf, index: MergeSubsection.Index) *MergeSubsection { - assert(index < self.merge_subsections.items.len); - return &self.merge_subsections.items[index]; +pub fn initOutputSection(self: *Elf, args: struct { + name: [:0]const u8, + flags: u64, + type: u32, +}) error{OutOfMemory}!u32 { + const name = blk: { + if (self.options.relocatable) break :blk args.name; + if (args.flags & elf.SHF_MERGE != 0) break :blk args.name; + const name_prefixes: []const [:0]const u8 = &.{ + ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", + ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors", + ".dtors", ".gnu.warning", + }; + inline for (name_prefixes) |prefix| { + if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) { + break :blk prefix; + } + } + break :blk args.name; + }; + const @"type" = tt: { + if (self.options.cpu_arch.? == .x86_64 and args.type == elf.SHT_X86_64_UNWIND) + break :tt elf.SHT_PROGBITS; + switch (args.type) { + elf.SHT_NULL => unreachable, + elf.SHT_PROGBITS => { + if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array.")) + break :tt elf.SHT_INIT_ARRAY; + if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array.")) + break :tt elf.SHT_FINI_ARRAY; + break :tt args.type; + }, + else => break :tt args.type, + } + }; + const flags = blk: { + var flags = args.flags; + if (!self.options.relocatable) { + flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); + } + break :blk switch (@"type") { + elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE, + else => flags, + }; + }; + const out_shndx = self.getSectionByName(name) orelse try self.addSection(.{ + .type = @"type", + .flags = flags, + .name = try self.insertShString(name), + }); + return out_shndx; } pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index { diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index e5869d93..c3e163da 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -38,6 +38,10 @@ pub fn getAddress(self: Atom, elf_file: *Elf) i64 { return @as(i64, @intCast(shdr.sh_addr)) + self.value; } +pub fn getRef(self: Atom) Elf.Ref { + return .{ .index = self.atom_index, .file = self.file }; +} + pub fn getDebugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 { if (target.getMergeSubsection(elf_file)) |msub| { if (msub.alive) return null; diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index dd813a8f..b957c44f 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -308,56 +308,6 @@ fn initSymbols(self: *Object, allocator: Allocator, elf_file: *Elf) !void { } } -pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) !u32 { - const name = blk: { - const name = self.getString(shdr.sh_name); - if (elf_file.options.relocatable) break :blk name; - if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; - const sh_name_prefixes: []const [:0]const u8 = &.{ - ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", - ".init_array", ".fini_array", ".tbss", ".tdata", ".gcc_except_table", ".ctors", - ".dtors", ".gnu.warning", - }; - inline for (sh_name_prefixes) |prefix| { - if (std.mem.eql(u8, name, prefix) or std.mem.startsWith(u8, name, prefix ++ ".")) { - break :blk prefix; - } - } - break :blk name; - }; - const @"type" = tt: { - if (elf_file.options.cpu_arch.? == .x86_64 and shdr.sh_type == elf.SHT_X86_64_UNWIND) break :tt elf.SHT_PROGBITS; - - const @"type" = switch (shdr.sh_type) { - elf.SHT_NULL => unreachable, - elf.SHT_PROGBITS => blk: { - if (std.mem.eql(u8, name, ".init_array") or std.mem.startsWith(u8, name, ".init_array.")) - break :blk elf.SHT_INIT_ARRAY; - if (std.mem.eql(u8, name, ".fini_array") or std.mem.startsWith(u8, name, ".fini_array.")) - break :blk elf.SHT_FINI_ARRAY; - break :blk shdr.sh_type; - }, - else => shdr.sh_type, - }; - break :tt @"type"; - }; - const flags = blk: { - var flags = shdr.sh_flags; - if (!elf_file.options.relocatable) { - flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); - } - break :blk switch (@"type") { - elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE, - else => flags, - }; - }; - return elf_file.getSectionByName(name) orelse try elf_file.addSection(.{ - .type = @"type", - .flags = flags, - .name = try elf_file.insertShString(name), - }); -} - fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u32, elf_file: *Elf) !void { const tracy = trace(@src()); defer tracy.end(); @@ -754,6 +704,42 @@ pub fn initMergeSections(self: *Object, elf_file: *Elf) !void { } } +pub fn initOutputSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.getAtom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + const shdr = atom_ptr.getInputShdr(elf_file); + const osec = try elf_file.initOutputSection(.{ + .name = self.getString(shdr.sh_name), + .flags = shdr.sh_flags, + .type = shdr.sh_type, + }); + atom_ptr.out_shndx = osec; + const atoms = &elf_file.sections.items(.atoms)[osec]; + try atoms.append(elf_file.base.allocator, atom_ptr.getRef()); + } +} + +pub fn initRelaSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms_indexes.items) |atom_index| { + const atom_ptr = self.getAtom(atom_index) orelse continue; + if (!atom_ptr.flags.alive) continue; + if (atom_ptr.getRelocs(elf_file).len == 0) continue; + const shdr = self.shdrs.items[atom_ptr.relocs_shndx]; + const out_shndx = try elf_file.initOutputSection(.{ + .name = self.getString(shdr.sh_name), + .flags = shdr.sh_flags, + .type = shdr.sh_type, + }); + const out_shdr = &elf_file.sections.items(.shdr)[out_shndx]; + out_shdr.sh_type = elf.SHT_RELA; + out_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); + out_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); + out_shdr.sh_flags |= elf.SHF_INFO_LINK; + elf_file.sections.items(.rela_shndx)[atom_ptr.out_shndx] = out_shndx; + } +} + pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const gpa = elf_file.base.allocator; @@ -770,8 +756,8 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const string = imsec.bytes.items[str.pos..][0..str.len]; const res = try msec.insert(gpa, string); if (!res.found_existing) { - const msub_index = try elf_file.addMergeSubsection(); - const msub = elf_file.getMergeSubsection(msub_index); + const msub_index = try msec.addMergeSubsection(gpa); + const msub = msec.getMergeSubsection(msub_index); msub.merge_section = imsec.merge_section; msub.string_index = res.key.pos; msub.entsize = @intCast(isec.sh_entsize); @@ -820,6 +806,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { const imsec_index = self.merge_sections_indexes.items[esym.st_shndx]; const imsec = self.getInputMergeSection(imsec_index) orelse continue; if (imsec.offsets.items.len == 0) continue; + const msec = elf_file.getMergeSection(imsec.merge_section); const msub_index, const offset = imsec.findSubsection(@intCast(@as(i64, @intCast(esym.st_value)) + rel.r_addend)) orelse { elf_file.base.fatal("{}: {s}: invalid relocation at offset 0x{x}", .{ self.fmtPath(), @@ -828,18 +815,14 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { }); return error.ParseFailed; }; - const msub = elf_file.getMergeSubsection(msub_index); - const msec = msub.getMergeSection(elf_file); - const out_sym_idx: u64 = @intCast(self.symbols.items.len); - try self.symbols.ensureUnusedCapacity(gpa, 1); + const sym_index = try self.addSymbol(gpa); + const sym = &self.symbols.items[sym_index]; const name = try std.fmt.allocPrint(gpa, "{s}$subsection{d}", .{ msec.getName(elf_file), msub_index, }); defer gpa.free(name); - 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 self.addString(gpa, name), @@ -849,7 +832,7 @@ pub fn resolveMergeSubsections(self: *Object, elf_file: *Elf) !void { }; sym.ref = .{ .index = msub_index, .file = imsec.merge_section }; sym.flags.merge_subsection = true; - rel.r_info = (out_sym_idx << 32) | rel.r_type(); + rel.r_info = (@as(u64, @intCast(sym_index)) << 32) | rel.r_type(); } } } diff --git a/src/Elf/Symbol.zig b/src/Elf/Symbol.zig index c1dd0d68..44905e08 100644 --- a/src/Elf/Symbol.zig +++ b/src/Elf/Symbol.zig @@ -73,8 +73,8 @@ pub fn getAtom(symbol: Symbol, elf_file: *Elf) ?*Atom { pub fn getMergeSubsection(symbol: Symbol, elf_file: *Elf) ?*MergeSubsection { if (!symbol.flags.merge_subsection) return null; - const extra = symbol.getExtra(elf_file); - return elf_file.getMergeSubsection(extra.subsection); + const msec = elf_file.getMergeSection(symbol.ref.file); + return msec.getMergeSubsection(symbol.ref.index); } pub fn getFile(symbol: Symbol, elf_file: *Elf) ?File { @@ -238,7 +238,6 @@ const AddExtraOpts = struct { tlsgd: ?u32 = null, gottp: ?u32 = null, tlsdesc: ?u32 = null, - subsection: ?u32 = null, }; pub fn addExtra(symbol: *Symbol, opts: AddExtraOpts, elf_file: *Elf) void { @@ -436,7 +435,6 @@ pub const Extra = struct { tlsgd: u32 = 0, gottp: u32 = 0, tlsdesc: u32 = 0, - subsection: u32 = 0, }; pub const Index = u32; diff --git a/src/Elf/merge_section.zig b/src/Elf/merge_section.zig index 3837b552..917122d7 100644 --- a/src/Elf/merge_section.zig +++ b/src/Elf/merge_section.zig @@ -1,4 +1,8 @@ pub const MergeSection = struct { + value: u64 = 0, + size: u64 = 0, + alignment: u8 = 0, + entsize: u32 = 0, name: u32 = 0, type: u32 = 0, flags: u64 = 0, @@ -10,12 +14,14 @@ pub const MergeSection = struct { IndexContext, std.hash_map.default_max_load_percentage, ) = .{}, - subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .{}, + subsections: std.ArrayListUnmanaged(MergeSubsection) = .empty, + finalized_subsections: std.ArrayListUnmanaged(MergeSubsection.Index) = .empty, pub fn deinit(msec: *MergeSection, allocator: Allocator) void { msec.bytes.deinit(allocator); msec.table.deinit(allocator); msec.subsections.deinit(allocator); + msec.finalized_subsections.deinit(allocator); } pub fn getName(msec: MergeSection, elf_file: *Elf) [:0]const u8 { @@ -24,7 +30,7 @@ pub const MergeSection = struct { pub fn getAddress(msec: MergeSection, elf_file: *Elf) i64 { const shdr = elf_file.sections.items(.shdr)[msec.out_shndx]; - return @intCast(shdr.sh_addr); + return @intCast(shdr.sh_addr + msec.value); } const InsertResult = struct { @@ -58,20 +64,19 @@ pub const MergeSection = struct { /// Finalizes the merge section and clears hash table. /// Sorts all owned subsections. - pub fn finalize(msec: *MergeSection, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; - try msec.subsections.ensureTotalCapacityPrecise(gpa, msec.table.count()); + pub fn finalize(msec: *MergeSection, allocator: Allocator) !void { + try msec.finalized_subsections.ensureTotalCapacityPrecise(allocator, msec.subsections.items.len); var it = msec.table.iterator(); while (it.next()) |entry| { - const msub = elf_file.getMergeSubsection(entry.value_ptr.*); + const msub = msec.getMergeSubsection(entry.value_ptr.*); if (!msub.alive) continue; - msec.subsections.appendAssumeCapacity(entry.value_ptr.*); + msec.finalized_subsections.appendAssumeCapacity(entry.value_ptr.*); } - msec.table.clearAndFree(gpa); + msec.table.clearAndFree(allocator); const sortFn = struct { - pub fn sortFn(ctx: *Elf, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool { + pub fn sortFn(ctx: *MergeSection, lhs: MergeSubsection.Index, rhs: MergeSubsection.Index) bool { const lhs_msub = ctx.getMergeSubsection(lhs); const rhs_msub = ctx.getMergeSubsection(rhs); if (lhs_msub.alignment == rhs_msub.alignment) { @@ -84,7 +89,41 @@ pub const MergeSection = struct { } }.sortFn; - std.mem.sort(MergeSubsection.Index, msec.subsections.items, elf_file, sortFn); + std.mem.sort(MergeSubsection.Index, msec.finalized_subsections.items, msec, sortFn); + } + + pub fn calcSize(msec: *MergeSection) !void { + for (msec.finalized_subsections.items) |msub_index| { + const msub = msec.getMergeSubsection(msub_index); + assert(msub.alive); + const alignment = try std.math.powi(u64, 2, msub.alignment); + const offset = mem.alignForward(u64, msec.size, alignment); + const padding = offset - msec.size; + msub.value = @intCast(offset); + msec.size += padding + msub.size; + msec.alignment = @max(msec.alignment, msub.alignment); + msec.entsize = if (msec.entsize == 0) msub.entsize else @min(msec.entsize, msub.entsize); + } + } + + pub fn initOutputSection(msec: *MergeSection, elf_file: *Elf) !void { + msec.out_shndx = elf_file.getSectionByName(msec.getName(elf_file)) orelse try elf_file.addSection(.{ + .name = msec.name, + .type = msec.type, + .flags = msec.flags, + }); + } + + pub fn addMergeSubsection(msec: *MergeSection, allocator: Allocator) !MergeSubsection.Index { + const index: MergeSubsection.Index = @intCast(msec.subsections.items.len); + const msub = try msec.subsections.addOne(allocator); + msub.* = .{}; + return index; + } + + pub fn getMergeSubsection(msec: *MergeSection, index: MergeSubsection.Index) *MergeSubsection { + assert(index < msec.subsections.items.len); + return &msec.subsections.items[index]; } pub const IndexContext = struct { @@ -148,15 +187,17 @@ pub const MergeSection = struct { _ = unused_fmt_string; const msec = ctx.msec; const elf_file = ctx.elf_file; - try writer.print("{s} : @{x} : sect({d}) : type({x}) : flags({x})\n", .{ + try writer.print("{s} : @{x} : size({x}) : align({x}) : entsize({x}) : type({x}) : flags({x})\n", .{ msec.getName(elf_file), msec.getAddress(elf_file), - msec.out_shndx, + msec.size, + msec.alignment, + msec.entsize, msec.type, msec.flags, }); - for (msec.subsections.items) |index| { - try writer.print(" subsection({d}) : {}\n", .{ index, elf_file.getMergeSubsection(index).fmt(elf_file) }); + for (msec.subsections.items) |msub| { + try writer.print(" {}\n", .{msub.fmt(elf_file)}); } } @@ -180,8 +221,7 @@ pub const MergeSubsection = struct { return elf_file.getMergeSection(msub.merge_section); } - pub fn getString(msub: MergeSubsection, elf_file: *Elf) []const u8 { - const msec = msub.getMergeSection(elf_file); + pub fn getString(msub: MergeSubsection, msec: *MergeSection) []const u8 { return msec.bytes.items[msub.string_index..][0..msub.size]; } @@ -220,9 +260,8 @@ pub const MergeSubsection = struct { _ = unused_fmt_string; const msub = ctx.msub; const elf_file = ctx.elf_file; - try writer.print("@{x} : merge_sect({d}) : align({x}) : size({x})", .{ + try writer.print("@{x} : align({x}) : size({x})", .{ msub.getAddress(elf_file), - msub.merge_section, msub.alignment, msub.size, }); diff --git a/src/Elf/relocatable.zig b/src/Elf/relocatable.zig index b58f66ff..6027f1d4 100644 --- a/src/Elf/relocatable.zig +++ b/src/Elf/relocatable.zig @@ -7,7 +7,6 @@ pub fn flush(elf_file: *Elf) !void { try elf_file.finalizeMergeSections(); try initSections(elf_file); try elf_file.sortSections(); - try elf_file.addAtomsToSections(); try elf_file.calcMergeSectionSizes(); try calcSectionSizes(elf_file); @@ -43,40 +42,13 @@ fn initSections(elf_file: *Elf) !void { for (elf_file.objects.items) |index| { const object = elf_file.getFile(index).?.object; - - for (object.atoms_indexes.items) |atom_index| { - const atom = object.getAtom(atom_index) orelse continue; - if (!atom.flags.alive) continue; - atom.out_shndx = try object.initOutputSection(elf_file, atom.getInputShdr(elf_file)); - - if (atom.getRelocs(elf_file).len > 0) { - const rela_shdr = object.getShdrs()[atom.relocs_shndx]; - const out_rela_shndx = try object.initOutputSection(elf_file, rela_shdr); - const out_rela_shdr = &elf_file.sections.items(.shdr)[out_rela_shndx]; - out_rela_shdr.sh_flags |= elf.SHF_INFO_LINK; - out_rela_shdr.sh_addralign = @alignOf(elf.Elf64_Rela); - out_rela_shdr.sh_entsize = @sizeOf(elf.Elf64_Rela); - elf_file.sections.items(.rela_shndx)[atom.out_shndx] = out_rela_shndx; - } - } + try object.initOutputSections(elf_file); + try object.initRelaSections(elf_file); } for (elf_file.merge_sections.items) |*msec| { - if (msec.subsections.items.len == 0) continue; - const shndx = elf_file.getSectionByName(msec.getName(elf_file)) orelse try elf_file.addSection(.{ - .name = msec.name, - .type = msec.type, - .flags = msec.flags, - }); - msec.out_shndx = shndx; - - var entsize = elf_file.getMergeSubsection(msec.subsections.items[0]).entsize; - for (msec.subsections.items) |index| { - const msub = elf_file.getMergeSubsection(index); - entsize = @min(entsize, msub.entsize); - } - const shdr = &elf_file.sections.items(.shdr)[shndx]; - shdr.sh_entsize = entsize; + if (msec.finalized_subsections.items.len == 0) continue; + try msec.initOutputSection(elf_file); } const needs_eh_frame = for (elf_file.objects.items) |index| { From b3d3a67ba4fcdfe812a793a6bb82772f3acabe4f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 13:27:37 +0200 Subject: [PATCH 11/19] elf: fix emitting relocs in .eh_frame section --- src/Elf/eh_frame.zig | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Elf/eh_frame.zig b/src/Elf/eh_frame.zig index ed5109f1..41753ec4 100644 --- a/src/Elf/eh_frame.zig +++ b/src/Elf/eh_frame.zig @@ -152,9 +152,11 @@ 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).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; + const cie_object = elf_file.getFile(cie.file).?.object; + const cie_ref = cie_object.resolveSymbol(cie_rel.r_sym(), elf_file); + const other_object = elf_file.getFile(other.file).?.object; + const other_ref = other_object.resolveSymbol(other_rel.r_sym(), elf_file); + if (!cie_ref.eql(other_ref)) return false; } return true; } @@ -485,8 +487,9 @@ 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.symbols.items[rel.r_sym()]; - const out_rel = emitReloc(elf_file, cie, sym, rel); + const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(sym_ref).?; + const out_rel = emitReloc(elf_file, cie, sym.*, rel); try writer.writeStruct(out_rel); } } @@ -494,8 +497,9 @@ 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.symbols.items[rel.r_sym()]; - const out_rel = emitReloc(elf_file, fde, sym, rel); + const sym_ref = object.resolveSymbol(rel.r_sym(), elf_file); + const sym = elf_file.getSymbol(sym_ref).?; + const out_rel = emitReloc(elf_file, fde, sym.*, rel); try writer.writeStruct(out_rel); } } From 341255bb141c053866c5a3c67c3fb2f3f403fb31 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 15:05:54 +0200 Subject: [PATCH 12/19] elf: do not relocs_log when scanning relocs --- src/Elf/Atom.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Elf/Atom.zig b/src/Elf/Atom.zig index c3e163da..8ac00777 100644 --- a/src/Elf/Atom.zig +++ b/src/Elf/Atom.zig @@ -873,7 +873,7 @@ const x86_64 = struct { .GOTTPOFF => { const should_relax = blk: { if (!elf_file.options.relax or is_shared or symbol.flags.import) break :blk false; - relaxGotTpOff(code[rel.r_offset - 3 ..]) catch break :blk false; + relaxGotTpOff(code[rel.r_offset - 3 ..], false) catch break :blk false; break :blk true; }; if (!should_relax) { @@ -984,7 +984,7 @@ const x86_64 = struct { const S_ = target.getGotTpAddress(elf_file); try cwriter.writeInt(i32, @as(i32, @intCast(S_ + A - P)), .little); } else { - try relaxGotTpOff(code[rel.r_offset - 3 ..]); + try relaxGotTpOff(code[rel.r_offset - 3 ..], true); try cwriter.writeInt(i32, @as(i32, @intCast(S - TP)), .little); } }, @@ -1228,7 +1228,7 @@ const x86_64 = struct { } } - fn relaxGotTpOff(code: []u8) !void { + fn relaxGotTpOff(code: []u8, comptime should_log: bool) !void { const old_inst = disassemble(code) orelse return error.RelaxFail; switch (old_inst.encoding.mnemonic) { .mov => { @@ -1237,7 +1237,7 @@ const x86_64 = struct { // TODO: hack to force imm32s in the assembler .{ .imm = Immediate.s(-129) }, }); - relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); + if (should_log) relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); encode(&.{inst}, code) catch return error.RelaxFail; }, else => return error.RelaxFail, From 5fd13e5473eb6c8d073a94c2945dec4151886739 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 15:39:50 +0200 Subject: [PATCH 13/19] elf: skip invalid FDE records --- src/Elf/Object.zig | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index b957c44f..c5de3926 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -332,7 +332,7 @@ fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u const fdes_start = self.fdes.items.len; const cies_start = self.cies.items.len; - var it = eh_frame.Iterator{ .data = self.eh_frame_data.items }; + var it = eh_frame.Iterator{ .data = raw }; while (try it.next()) |rec| { const rel_range = filterRelocs(self.relocs.items[rel_start..][0..relocs.len], rec.offset, rec.size + 4); switch (rec.tag) { @@ -344,15 +344,24 @@ fn parseEhFrame(self: *Object, allocator: Allocator, file: std.fs.File, shndx: u .shndx = shndx, .file = self.index, }), - .fde => try self.fdes.append(allocator, .{ - .offset = data_start + rec.offset, - .size = rec.size, - .cie_index = undefined, - .rel_index = rel_start + @as(u32, @intCast(rel_range.start)), - .rel_num = @as(u32, @intCast(rel_range.len)), - .shndx = shndx, - .file = self.index, - }), + .fde => { + if (rel_range.len == 0) { + // No relocs for an FDE means we cannot associate this FDE to an Atom + // so we skip it. According to mold source code + // (https://github.com/rui314/mold/blob/a3e69502b0eaf1126d6093e8ea5e6fdb95219811/src/input-files.cc#L525-L528) + // this can happen for object files built with -r flag by the linker. + continue; + } + try self.fdes.append(allocator, .{ + .offset = data_start + rec.offset, + .size = rec.size, + .cie_index = undefined, + .rel_index = rel_start + @as(u32, @intCast(rel_range.start)), + .rel_num = @as(u32, @intCast(rel_range.len)), + .shndx = shndx, + .file = self.index, + }); + }, } } From 1feb380a8f6aa9a8502e19c25f5dd7e7eb5e74e5 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 20:35:54 +0200 Subject: [PATCH 14/19] elf: fix condition for skipping symbols if atom is dead --- src/Elf/Object.zig | 11 +++++------ src/Elf/eh_frame.zig | 5 +++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Elf/Object.zig b/src/Elf/Object.zig index c5de3926..20721672 100644 --- a/src/Elf/Object.zig +++ b/src/Elf/Object.zig @@ -486,12 +486,6 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void { const first_global = self.first_global orelse return; 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; - } - const resolv = &self.symbols_resolver.items[i - first_global]; const gop = try elf_file.resolver.getOrPut(gpa, .{ .index = @intCast(i), @@ -503,6 +497,11 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) !void { resolv.* = gop.index; if (esym.st_shndx == elf.SHN_UNDEF) continue; + if (esym.st_shndx != elf.SHN_ABS and esym.st_shndx != elf.SHN_COMMON) { + 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 (elf_file.getSymbol(gop.ref.*) == null) { gop.ref.* = .{ .index = @intCast(i), .file = self.index }; continue; diff --git a/src/Elf/eh_frame.zig b/src/Elf/eh_frame.zig index 41753ec4..0970dc60 100644 --- a/src/Elf/eh_frame.zig +++ b/src/Elf/eh_frame.zig @@ -147,14 +147,15 @@ pub const Cie = struct { const other_relocs = other.getRelocs(elf_file); if (cie_relocs.len != other_relocs.len) return false; + const cie_object = elf_file.getFile(cie.file).?.object; + const other_object = elf_file.getFile(other.file).?.object; + for (cie_relocs, other_relocs) |cie_rel, other_rel| { if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false; if (cie_rel.r_type() != other_rel.r_type()) return false; if (cie_rel.r_addend != other_rel.r_addend) return false; - const cie_object = elf_file.getFile(cie.file).?.object; const cie_ref = cie_object.resolveSymbol(cie_rel.r_sym(), elf_file); - const other_object = elf_file.getFile(other.file).?.object; const other_ref = other_object.resolveSymbol(other_rel.r_sym(), elf_file); if (!cie_ref.eql(other_ref)) return false; } From 7df93e6522ee0361f06365a1d000add7481d0a34 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 20:49:32 +0200 Subject: [PATCH 15/19] elf: remove obsolete code --- src/Elf/synthetic.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Elf/synthetic.zig b/src/Elf/synthetic.zig index 133e542b..a7902e07 100644 --- a/src/Elf/synthetic.zig +++ b/src/Elf/synthetic.zig @@ -1247,8 +1247,6 @@ pub const PltGotSection = struct { const index = @as(u32, @intCast(plt_got.symbols.items.len)); 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); } From 75b1acc187e824953af0f78e81fab7cdf60607e7 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 21:07:57 +0200 Subject: [PATCH 16/19] elf: actually allocate synthetic internal symbols --- src/Elf.zig | 7 +++++-- src/Elf/InternalObject.zig | 26 +++++++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Elf.zig b/src/Elf.zig index b9007714..c253a7c3 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -371,6 +371,9 @@ pub fn flush(self: *Elf) !void { try self.calcSectionSizes(); try self.allocateSections(); + if (self.getInternalObject()) |obj| { + obj.allocateSymbols(self); + } self.shoff = blk: { const shdr = self.sections.items(.shdr)[self.sections.len - 1]; @@ -2598,12 +2601,12 @@ fn sortRelaDyn(self: *Elf) void { mem.sort(elf.Elf64_Rela, self.rela_dyn.items, self, Sort.lessThan); } -fn getNumIRelativeRelocs(self: *Elf) usize { +pub fn getNumIRelativeRelocs(self: *Elf) usize { var count: usize = self.num_ifunc_dynrelocs; for (self.got.entries.items) |entry| { if (entry.tag != .got) continue; - const symbol = self.getSymbol(entry.symbol_index); + const symbol = self.getSymbol(entry.ref).?; if (symbol.isIFunc(self)) count += 1; } diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index 35d0553a..3396a087 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -198,20 +198,20 @@ pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { 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)).?; + const sym = ef.getSymbol(int.resolveSymbol(index, ef)).?; sym.value = @intCast(value); - sym.output_section_index = osec; + sym.shndx = osec; } }.allocSymbol; // _DYNAMIC - if (elf_file.dynamic_section_index) |shndx| { + if (elf_file.dynamic_sect_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); + allocSymbol(self, self.ehdr_start_index.?, elf_file.options.image_base, 1, elf_file); // __init_array_start, __init_array_end if (elf_file.getSectionByName(".init_array")) |shndx| { @@ -236,19 +236,19 @@ pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { // _GLOBAL_OFFSET_TABLE_ if (elf_file.options.cpu_arch.? == .x86_64) { - if (elf_file.got_plt_section_index) |shndx| { + if (elf_file.got_plt_sect_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| { + if (elf_file.got_sect_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| { + if (elf_file.plt_sect_index) |shndx| { const shdr = shdrs[shndx]; allocSymbol(self, self.plt_index.?, shdr.sh_addr, shndx, elf_file); } @@ -260,17 +260,17 @@ pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { } // __GNU_EH_FRAME_HDR - if (elf_file.eh_frame_hdr_section_index) |shndx| { + if (elf_file.eh_frame_hdr_sect_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.rela_dyn_sect_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); + const start_addr = end_addr - elf_file.getNumIRelativeRelocs() * @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); } @@ -303,15 +303,15 @@ pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { 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 name = start.getName(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; + start.shndx = shndx; stop.value = @intCast(shdr.sh_addr + shdr.sh_size); - stop.output_section_index = shndx; + stop.shndx = shndx; } } } From cef102659e2f28fc98f0bbcf810bec48c04880bb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 22:31:04 +0200 Subject: [PATCH 17/19] elf: only allocate __dso_handle if not found in input object --- src/Elf/InternalObject.zig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index 3396a087..f4c60307 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -49,7 +49,7 @@ fn newSymbolAssumeCapacity(self: *InternalObject, name_off: u32, elf_file: *Elf) const esym = self.symtab.addOneAssumeCapacity(); esym.* = .{ .st_name = name_off, - .st_info = elf.STB_GLOBAL << 4, + .st_info = elf.STB_WEAK << 4, .st_other = @intFromEnum(elf.STV.HIDDEN), .st_shndx = elf.SHN_ABS, .st_value = 0, @@ -255,8 +255,10 @@ pub fn allocateSymbols(self: *InternalObject, elf_file: *Elf) void { // __dso_handle if (self.dso_handle_index) |index| { - const shdr = shdrs[1]; - allocSymbol(self, index, shdr.sh_addr, 0, elf_file); + if (self.resolveSymbol(index, elf_file).file == self.index) { + const shdr = shdrs[1]; + allocSymbol(self, index, shdr.sh_addr, 0, elf_file); + } } // __GNU_EH_FRAME_HDR From 727c55d67fb7f784b8d57645e64cb0dbfa9e607e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 22:43:13 +0200 Subject: [PATCH 18/19] elf: actually init __start/__stop symbols --- src/Elf.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Elf.zig b/src/Elf.zig index c253a7c3..7ff921bf 100644 --- a/src/Elf.zig +++ b/src/Elf.zig @@ -347,6 +347,9 @@ pub fn flush(self: *Elf) !void { try self.addCommentString(); try self.finalizeMergeSections(); try self.initOutputSections(); + if (self.getInternalObject()) |obj| { + try obj.initStartStopSymbols(self); + } if (self.options.z_execstack_if_needed) { for (self.objects.items) |index| { From 63c0a7618c7af6a601b04e8ed98688f8a342fede Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Sep 2024 22:45:53 +0200 Subject: [PATCH 19/19] elf: always override existing __start/__stop symbols --- src/Elf/InternalObject.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Elf/InternalObject.zig b/src/Elf/InternalObject.zig index f4c60307..a299b6c2 100644 --- a/src/Elf/InternalObject.zig +++ b/src/Elf/InternalObject.zig @@ -160,7 +160,6 @@ pub fn initStartStopSymbols(self: *InternalObject, elf_file: *Elf) !void { .index = index, .file = self.index, }, elf_file); - assert(!gop.found_existing); gop.ref.* = .{ .index = index, .file = self.index }; self.symbols_resolver.appendAssumeCapacity(gop.index); }