From 700644d35dd674c7381c4794d566e0e64f45c174 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 15 Apr 2024 19:54:53 +0200 Subject: [PATCH] link/elf: introduce Atom extras for out-of-band storage --- src/link/Elf.zig | 50 +++++++++++++++++++++++++++++++++++++++++ src/link/Elf/Atom.zig | 47 ++++++++++++++++++++++++++++++++------ src/link/Elf/thunks.zig | 3 ++- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 03451d140e..b8829bebe9 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -205,6 +205,7 @@ num_ifunc_dynrelocs: usize = 0, /// List of atoms that are owned directly by the linker. atoms: std.ArrayListUnmanaged(Atom) = .{}, +atoms_extra: std.ArrayListUnmanaged(u32) = .{}, /// List of range extension thunks. thunks: std.ArrayListUnmanaged(Thunk) = .{}, @@ -369,6 +370,7 @@ pub fn createEmpty( try self.symbols_extra.append(gpa, 0); // Allocate atom index 0 to null atom try self.atoms.append(gpa, .{}); + try self.atoms_extra.append(gpa, 0); // Append null file at index 0 try self.files.append(gpa, .null); // Append null byte to string tables @@ -491,6 +493,10 @@ pub fn deinit(self: *Elf) void { self.start_stop_indexes.deinit(gpa); self.atoms.deinit(gpa); + self.atoms_extra.deinit(gpa); + for (self.thunks.items) |*th| { + th.deinit(gpa); + } self.thunks.deinit(gpa); for (self.last_atom_and_free_list_table.values()) |*value| { value.free_list.deinit(gpa); @@ -5458,6 +5464,50 @@ pub fn addAtom(self: *Elf) !Atom.Index { return index; } +pub fn addAtomExtra(self: *Elf, extra: Atom.Extra) !u32 { + const fields = @typeInfo(Atom.Extra).Struct.fields; + try self.atoms_extra.ensureUnusedCapacity(self.base.comp.gpa, 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 atomExtra(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"), + }; + } +} + pub fn addThunk(self: *Elf) !Thunk.Index { const index = @as(Thunk.Index, @intCast(self.thunks.items.len)); const th = try self.thunks.addOne(self.base.comp.gpa); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index b5ceeb24b8..d4332c89b6 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -31,12 +31,6 @@ rel_num: u32 = 0, /// Index of this atom in the linker's atoms table. atom_index: Index = 0, -/// Index of the thunk for this atom. -thunk_index: Thunk.Index = 0, - -/// Flags we use for state tracking. -flags: Flags = .{}, - /// Start index of FDEs referencing this atom. fde_start: u32 = 0, @@ -48,6 +42,11 @@ fde_end: u32 = 0, prev_index: Index = 0, next_index: Index = 0, +/// Flags we use for state tracking. +flags: Flags = .{}, + +extra_index: u32 = 0, + pub const Alignment = @import("../../InternPool.zig").Alignment; pub fn name(self: Atom, elf_file: *Elf) []const u8 { @@ -68,7 +67,9 @@ pub fn file(self: Atom, elf_file: *Elf) ?File { } pub fn thunk(self: Atom, elf_file: *Elf) *Thunk { - return elf_file.thunk(self.thunk_index); + assert(self.flags.thunk); + const extras = self.extra(elf_file).?; + return elf_file.thunk(extras.thunk); } pub fn inputShdr(self: Atom, elf_file: *Elf) elf.Elf64_Shdr { @@ -981,6 +982,31 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any if (has_reloc_errors) return error.RelocFailure; } +const AddExtraOpts = struct { + thunk: ?u32 = null, +}; + +pub fn addExtra(atom: *Atom, opts: AddExtraOpts, elf_file: *Elf) !void { + if (atom.extra(elf_file) == null) { + atom.extra_index = try elf_file.addAtomExtra(.{}); + } + var extras = atom.extra(elf_file).?; + inline for (@typeInfo(@TypeOf(opts)).Struct.fields) |field| { + if (@field(opts, field.name)) |x| { + @field(extras, field.name) = x; + } + } + atom.setExtra(extras, elf_file); +} + +pub inline fn extra(atom: Atom, elf_file: *Elf) ?Extra { + return elf_file.atomExtra(atom.extra_index); +} + +pub inline fn setExtra(atom: Atom, extras: Extra, elf_file: *Elf) void { + elf_file.setAtomExtra(atom.extra_index, extras); +} + pub fn format( atom: Atom, comptime unused_fmt_string: []const u8, @@ -1042,6 +1068,9 @@ pub const Flags = packed struct { /// Specifies if the atom has been visited during garbage collection. visited: bool = false, + + /// Whether this symbol has a range extension thunk. + thunk: bool = false, }; const x86_64 = struct { @@ -2167,6 +2196,10 @@ const RelocsIterator = struct { } }; +pub const Extra = struct { + thunk: u32 = 0, +}; + const std = @import("std"); const assert = std.debug.assert; const elf = std.elf; diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig index 7d06b3b190..6e1f8aeee6 100644 --- a/src/link/Elf/thunks.zig +++ b/src/link/Elf/thunks.zig @@ -50,7 +50,8 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { }; try thunk.symbols.put(gpa, target, {}); } - atom.thunk_index = thunk_index; + try atom.addExtra(.{ .thunk = thunk_index }, elf_file); + atom.flags.thunk = true; } thunk.value = try advance(shdr, thunk.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));