elf: add simplistic reloc scanning mechanism

This commit is contained in:
Jakub Konka 2023-09-12 16:32:55 +02:00
parent c654f3b0ee
commit 44e84af874
7 changed files with 115 additions and 20 deletions

View File

@ -8156,6 +8156,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
if (self.bin_file.cast(link.File.Elf)) |elf_file| {
const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
const sym = elf_file.symbol(sym_index);
sym.flags.needs_got = true;
_ = try sym.getOrCreateGotEntry(elf_file);
const got_addr = sym.gotAddress(elf_file);
try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{
@ -10234,6 +10235,7 @@ fn genLazySymbolRef(
const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
return self.fail("{s} creating lazy symbol", .{@errorName(err)});
const sym = elf_file.symbol(sym_index);
sym.flags.needs_got = true;
_ = try sym.getOrCreateGotEntry(elf_file);
const got_addr = sym.gotAddress(elf_file);
const got_mem =

View File

@ -856,6 +856,7 @@ fn genDeclRef(
if (bin_file.cast(link.File.Elf)) |elf_file| {
const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
const sym = elf_file.symbol(sym_index);
sym.flags.needs_got = true;
_ = try sym.getOrCreateGotEntry(elf_file);
return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) });
} else if (bin_file.cast(link.File.MachO)) |macho_file| {

View File

@ -1049,8 +1049,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
self.resolveSymbols();
self.markImportsExports();
self.claimUnresolved();
if (self.unresolved.keys().len > 0) try self.reportUndefined();
try self.scanRelocs();
self.allocateLinkerDefinedSymbols();
@ -1381,6 +1380,28 @@ fn claimUnresolved(self: *Elf) void {
}
}
fn scanRelocs(self: *Elf) !void {
if (self.zig_module_index) |index| {
const zig_module = self.file(index).?.zig_module;
try zig_module.scanRelocs(self);
}
for (self.objects.items) |index| {
const object = self.file(index).?.object;
try object.scanRelocs(self);
}
// try self.reportUndefined();
for (self.symbols.items) |*sym| {
if (sym.flags.needs_got) {
log.debug("'{s}' needs GOT", .{sym.name(self)});
// TODO how can we tell we need to write it again, aka the entry is dirty?
const gop = try sym.getOrCreateGotEntry(self);
try self.got.writeEntry(self, gop.index);
}
}
}
fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@ -2375,8 +2396,9 @@ fn updateDeclCode(
sym.value = atom_ptr.value;
esym.st_value = atom_ptr.value;
const got_index = try sym.getOrCreateGotEntry(self);
try self.got.writeEntry(self, got_index);
sym.flags.needs_got = true;
const gop = try sym.getOrCreateGotEntry(self);
try self.got.writeEntry(self, gop.index);
}
const phdr_index = self.phdr_to_shdr_table.get(shdr_index).?;
@ -2609,8 +2631,9 @@ fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.
local_sym.value = atom_ptr.value;
local_esym.st_value = atom_ptr.value;
const got_index = try local_sym.getOrCreateGotEntry(self);
try self.got.writeEntry(self, got_index);
local_sym.flags.needs_got = true;
const gop = try local_sym.getOrCreateGotEntry(self);
try self.got.writeEntry(self, gop.index);
const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset;

View File

@ -311,6 +311,57 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
}
pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {
const file_ptr = elf_file.file(self.file_index).?;
const rels = self.relocs(elf_file);
var i: usize = 0;
while (i < rels.len) : (i += 1) {
const rel = rels[i];
if (rel.r_type() == elf.R_X86_64_NONE) continue;
const symbol = switch (file_ptr) {
.zig_module => elf_file.symbol(rel.r_sym()),
.object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
else => unreachable,
};
// While traversing relocations, mark symbols that require special handling such as
// pointer indirection via GOT, or a stub trampoline via PLT.
switch (rel.r_type()) {
elf.R_X86_64_64 => {},
elf.R_X86_64_32,
elf.R_X86_64_32S,
=> {},
elf.R_X86_64_GOT32,
elf.R_X86_64_GOT64,
elf.R_X86_64_GOTPC32,
elf.R_X86_64_GOTPC64,
elf.R_X86_64_GOTPCREL,
elf.R_X86_64_GOTPCREL64,
elf.R_X86_64_GOTPCRELX,
elf.R_X86_64_REX_GOTPCRELX,
=> {
symbol.flags.needs_got = true;
},
elf.R_X86_64_PLT32,
elf.R_X86_64_PLTOFF64,
=> {
if (symbol.flags.import) {
symbol.flags.needs_plt = true;
}
},
elf.R_X86_64_PC32 => {},
else => @panic("TODO"),
}
}
}
/// TODO mark relocs dirty
pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
@ -484,6 +535,9 @@ fn format2(
}
}
// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for
// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near
// future.
pub const Index = u16;
const std = @import("std");

View File

@ -367,15 +367,16 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void {
}
for (self.cies.items) |cie| {
for (cie.getRelocs(elf_file)) |rel| {
for (cie.relocs(elf_file)) |rel| {
const sym = elf_file.symbol(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", .{
if (sym.type(elf_file) != elf.STT_FUNC)
// TODO convert into an error
log.debug("{s}: {s}: CIE referencing external data reference", .{
self.fmtPath(),
sym.getName(elf_file),
sym.name(elf_file),
});
sym.flags.plt = true;
sym.flags.needs_plt = true;
}
}
}

View File

@ -111,19 +111,23 @@ pub fn address(symbol: Symbol, opts: struct {
}
pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
if (!symbol.flags.got) return 0;
if (!symbol.flags.has_got) return 0;
const extras = symbol.extra(elf_file).?;
const entry = elf_file.got.entries.items[extras.got];
return entry.address(elf_file);
}
pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GotSection.Index {
const index = if (symbol.flags.got)
symbol.extra(elf_file).?.got
else
try elf_file.got.addGotSymbol(symbol.index, elf_file);
symbol.flags.got = true;
return index;
const GetOrCreateGotEntryResult = struct {
found_existing: bool,
index: GotSection.Index,
};
pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult {
assert(symbol.flags.needs_got);
if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got };
const index = try elf_file.got.addGotSymbol(symbol.index, elf_file);
symbol.flags.has_got = true;
return .{ .found_existing = false, .index = index };
}
// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
@ -310,9 +314,11 @@ pub const Flags = packed struct {
output_symtab: bool = false,
/// Whether the symbol contains GOT indirection.
got: bool = false,
needs_got: bool = false,
has_got: bool = false,
/// Whether the symbol contains PLT indirection.
needs_plt: bool = false,
plt: bool = false,
/// Whether the PLT entry is canonical.
is_canonical: bool = false,

View File

@ -130,6 +130,14 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
}
}
pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void {
for (self.atoms.keys()) |atom_index| {
const atom = elf_file.atom(atom_index) orelse continue;
if (!atom.alive) continue;
try atom.scanRelocs(elf_file);
}
}
pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
for (self.locals()) |local_index| {
const local = elf_file.symbol(local_index);