From d07edfabd6e45a486d1611e357887d01b38db32e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Sep 2023 22:46:59 +0200 Subject: [PATCH] elf: simplify handling of relocs for atoms --- src/link/Elf.zig | 56 ++++--------------------- src/link/Elf/Atom.zig | 81 +++++++++++++++++++++++++------------ src/link/Elf/Relocation.zig | 8 ++++ src/link/Elf/ZigModule.zig | 12 +++++- 4 files changed, 82 insertions(+), 75 deletions(-) create mode 100644 src/link/Elf/Relocation.zig diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2fade5c82f..e9b804801f 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -135,10 +135,6 @@ last_atom_and_free_list_table: std.AutoArrayHashMapUnmanaged(u16, LastAtomAndFre /// with `Decl` `main`, and lives as long as that `Decl`. unnamed_consts: UnnamedConstTable = .{}, -/// A table of relocations indexed by the owning them `TextBlock`. -relocs: RelocTable = .{}, - -const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Symbol.Index)); const LazySymbolTable = std.AutoArrayHashMapUnmanaged(Module.Decl.OptionalIndex, LazySymbolMetadata); @@ -293,14 +289,6 @@ pub fn deinit(self: *Elf) void { self.unnamed_consts.deinit(gpa); } - { - var it = self.relocs.valueIterator(); - while (it.next()) |relocs| { - relocs.deinit(gpa); - } - self.relocs.deinit(gpa); - } - if (self.dwarf) |*dw| { dw.deinit(); } @@ -312,12 +300,11 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. const this_sym_index = try self.getOrCreateMetadataForDecl(decl_index); const this_sym = self.symbol(this_sym_index); const vaddr = this_sym.value; - const parent_atom_index = self.symbol(reloc_info.parent_atom_index).atom_index; - try Atom.addRelocation(self, parent_atom_index, .{ + const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?; + try parent_atom.addReloc(self, .{ .target = this_sym_index, .offset = reloc_info.offset, .addend = reloc_info.addend, - .prev_vaddr = vaddr, }); return vaddr; @@ -1032,38 +1019,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Beyond this point, everything has been allocated a virtual address and we can resolve // the relocations. - { - var it = self.relocs.iterator(); - while (it.next()) |entry| { - const atom_index = entry.key_ptr.*; - const relocs = entry.value_ptr.*; - const atom_ptr = self.atom(atom_index).?; - const source_shdr = &self.shdrs.items[atom_ptr.output_section_index]; - - log.debug("relocating '{s}'", .{atom_ptr.name(self)}); - - for (relocs.items) |*reloc| { - const target_sym = self.symbol(reloc.target); - const target_vaddr = target_sym.value + reloc.addend; - - if (target_vaddr == reloc.prev_vaddr) continue; - - const section_offset = (atom_ptr.value + reloc.offset) - source_shdr.sh_addr; - const file_offset = source_shdr.sh_offset + section_offset; - - log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ - reloc.offset, - target_vaddr, - target_sym.name(self), - }); - - switch (self.ptr_width) { - .p32 => try self.base.file.?.pwriteAll(mem.asBytes(&@as(u32, @intCast(target_vaddr))), file_offset), - .p64 => try self.base.file.?.pwriteAll(mem.asBytes(&target_vaddr), file_offset), - } - - reloc.prev_vaddr = target_vaddr; - } + if (self.zig_module_index) |index| { + for (self.file(index).?.zig_module.atoms.items) |atom_index| { + try self.atom(atom_index).?.resolveRelocs(self); } } @@ -2313,7 +2271,7 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A const sym_index = try self.getOrCreateMetadataForDecl(decl_index); self.freeUnnamedConsts(decl_index); - Atom.freeRelocations(self, self.symbol(sym_index).atom_index); + self.symbol(sym_index).atom(self).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); @@ -2378,7 +2336,7 @@ pub fn updateDecl( } const sym_index = try self.getOrCreateMetadataForDecl(decl_index); - Atom.freeRelocations(self, self.symbol(sym_index).atom_index); + self.symbol(sym_index).atom(self).?.freeRelocs(self); var code_buffer = std.ArrayList(u8).init(self.base.allocator); defer code_buffer.deinit(); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index ead42917a1..69950f9f32 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -14,13 +14,13 @@ size: u64 = 0, alignment: u8 = 0, /// Index of the input section. -input_section_index: u16 = 0, +input_section_index: Index = 0, /// Index of the output section. output_section_index: u16 = 0, /// Index of the input section containing this atom's relocs. -relocs_section_index: u16 = 0, +relocs_section_index: Index = 0, /// Index of this atom in the linker's atoms table. atom_index: Index = 0, @@ -64,20 +64,6 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { return surplus >= Elf.min_text_capacity; } -pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void { - const gpa = elf_file.base.allocator; - const gop = try elf_file.relocs.getOrPut(gpa, atom_index); - if (!gop.found_existing) { - gop.value_ptr.* = .{}; - } - try gop.value_ptr.append(gpa, reloc); -} - -pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { - var removed_relocs = elf_file.relocs.fetchRemove(atom_index); - if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); -} - pub fn allocate(self: *Atom, elf_file: *Elf) !void { const shdr = &elf_file.shdrs.items[self.output_section_index]; const meta = elf_file.last_atom_and_free_list_table.getPtr(self.output_section_index).?; @@ -205,8 +191,6 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { pub fn free(self: *Atom, elf_file: *Elf) void { log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); - Atom.freeRelocations(elf_file, self.atom_index); - const gpa = elf_file.base.allocator; const shndx = self.output_section_index; const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; @@ -256,23 +240,70 @@ pub fn free(self: *Atom, elf_file: *Elf) void { self.next_index = 0; } + // TODO create relocs free list + self.freeRelocs(elf_file); self.* = .{}; } -pub const Index = u32; +pub fn relocs(self: Atom, elf_file: *Elf) []const Relocation { + const file_ptr = elf_file.file(self.file_index).?; + if (file_ptr != .zig_module) @panic("TODO"); + const zig_module = file_ptr.zig_module; + return zig_module.relocs.items[self.relocs_section_index].items; +} -pub const Reloc = struct { - target: u32, - offset: u64, - addend: u32, - prev_vaddr: u64, -}; +pub fn addReloc(self: Atom, elf_file: *Elf, reloc: Relocation) !void { + const gpa = elf_file.base.allocator; + const file_ptr = elf_file.file(self.file_index).?; + assert(file_ptr == .zig_module); + const zig_module = file_ptr.zig_module; + const rels = &zig_module.relocs.items[self.relocs_section_index]; + try rels.append(gpa, reloc); +} + +pub fn freeRelocs(self: Atom, elf_file: *Elf) void { + const file_ptr = elf_file.file(self.file_index).?; + assert(file_ptr == .zig_module); + const zig_module = file_ptr.zig_module; + zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity(); +} + +/// TODO mark relocs dirty +pub fn resolveRelocs(self: Atom, elf_file: *Elf) !void { + relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); + const shdr = &elf_file.shdrs.items[self.output_section_index]; + for (self.relocs(elf_file)) |reloc| { + const target_sym = elf_file.symbol(reloc.target); + const target_vaddr = target_sym.value + reloc.addend; + const section_offset = (self.value + reloc.offset) - shdr.sh_addr; + const file_offset = shdr.sh_offset + section_offset; + + relocs_log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ + reloc.offset, + target_vaddr, + target_sym.name(elf_file), + }); + + switch (elf_file.ptr_width) { + .p32 => try elf_file.base.file.?.pwriteAll( + std.mem.asBytes(&@as(u32, @intCast(target_vaddr))), + file_offset, + ), + .p64 => try elf_file.base.file.?.pwriteAll(std.mem.asBytes(&target_vaddr), file_offset), + } + } +} + +pub const Index = u32; const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; const log = std.log.scoped(.link); +const relocs_log = std.log.scoped(.link_relocs); +const Allocator = std.mem.Allocator; const Atom = @This(); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; +const Relocation = @import("Relocation.zig"); diff --git a/src/link/Elf/Relocation.zig b/src/link/Elf/Relocation.zig new file mode 100644 index 0000000000..719a211238 --- /dev/null +++ b/src/link/Elf/Relocation.zig @@ -0,0 +1,8 @@ +target: Symbol.Index, +offset: u64, +addend: u32, + +const std = @import("std"); + +const Symbol = @import("Symbol.zig"); +const Relocation = @This(); diff --git a/src/link/Elf/ZigModule.zig b/src/link/Elf/ZigModule.zig index b0d500f689..b7616edc81 100644 --- a/src/link/Elf/ZigModule.zig +++ b/src/link/Elf/ZigModule.zig @@ -9,6 +9,7 @@ elf_global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{}, global_symbols: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{}, atoms: std.ArrayListUnmanaged(Atom.Index) = .{}, +relocs: std.ArrayListUnmanaged(std.ArrayListUnmanaged(Relocation)) = .{}, alive: bool = true, @@ -20,6 +21,10 @@ pub fn deinit(self: *ZigModule, allocator: Allocator) void { self.elf_global_symbols.deinit(allocator); self.global_symbols.deinit(allocator); self.atoms.deinit(allocator); + for (self.relocs.items) |*list| { + list.deinit(allocator); + } + self.relocs.deinit(allocator); } pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index { @@ -34,6 +39,10 @@ pub fn createAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) ! symbol_ptr.output_section_index = output_section_index; const local_esym = symbol_ptr.sourceSymbol(elf_file); local_esym.st_shndx = output_section_index; + const relocs_index = @as(Atom.Index, @intCast(self.relocs.items.len)); + const relocs = try self.relocs.addOne(gpa); + relocs.* = .{}; + atom_ptr.relocs_section_index = relocs_index; try self.atoms.append(gpa, atom_index); return symbol_index; } @@ -184,5 +193,6 @@ const Atom = @import("Atom.zig"); const Elf = @import("../Elf.zig"); const File = @import("file.zig").File; const Module = @import("../../Module.zig"); -const ZigModule = @This(); +const Relocation = @import("Relocation.zig"); const Symbol = @import("Symbol.zig"); +const ZigModule = @This();