authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 16:32:55+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 16:32:55+02:00
log44e84af874bc01fe0657d20548f69801cf18dccd
tree625548683a305d96c3b3505cbb658d114e3f5a7e
parentc654f3b0ee8d02d809bb458e1e006b4aa7c3cbc6

elf: add simplistic reloc scanning mechanism


7 files changed, 115 insertions(+), 20 deletions(-)

src/arch/x86_64/CodeGen.zig+2
......@@ -8156,6 +8156,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
81568156 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
81578157 const sym_index = try elf_file.getOrCreateMetadataForDecl(owner_decl);
81588158 const sym = elf_file.symbol(sym_index);
8159 sym.flags.needs_got = true;
81598160 _ = try sym.getOrCreateGotEntry(elf_file);
81608161 const got_addr = sym.gotAddress(elf_file);
81618162 try self.asmMemory(.{ ._, .call }, Memory.sib(.qword, .{
......@@ -10234,6 +10235,7 @@ fn genLazySymbolRef(
1023410235 const sym_index = elf_file.getOrCreateMetadataForLazySymbol(lazy_sym) catch |err|
1023510236 return self.fail("{s} creating lazy symbol", .{@errorName(err)});
1023610237 const sym = elf_file.symbol(sym_index);
10238 sym.flags.needs_got = true;
1023710239 _ = try sym.getOrCreateGotEntry(elf_file);
1023810240 const got_addr = sym.gotAddress(elf_file);
1023910241 const got_mem =
src/codegen.zig+1
......@@ -856,6 +856,7 @@ fn genDeclRef(
856856 if (bin_file.cast(link.File.Elf)) |elf_file| {
857857 const sym_index = try elf_file.getOrCreateMetadataForDecl(decl_index);
858858 const sym = elf_file.symbol(sym_index);
859 sym.flags.needs_got = true;
859860 _ = try sym.getOrCreateGotEntry(elf_file);
860861 return GenResult.mcv(.{ .memory = sym.gotAddress(elf_file) });
861862 } else if (bin_file.cast(link.File.MachO)) |macho_file| {
src/link/Elf.zig+29-6
......@@ -1049,8 +1049,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10491049 self.resolveSymbols();
10501050 self.markImportsExports();
10511051 self.claimUnresolved();
1052
1053 if (self.unresolved.keys().len > 0) try self.reportUndefined();
1052 try self.scanRelocs();
10541053
10551054 self.allocateLinkerDefinedSymbols();
10561055
......@@ -1381,6 +1380,28 @@ fn claimUnresolved(self: *Elf) void {
13811380 }
13821381}
13831382
1383fn scanRelocs(self: *Elf) !void {
1384 if (self.zig_module_index) |index| {
1385 const zig_module = self.file(index).?.zig_module;
1386 try zig_module.scanRelocs(self);
1387 }
1388 for (self.objects.items) |index| {
1389 const object = self.file(index).?.object;
1390 try object.scanRelocs(self);
1391 }
1392
1393 // try self.reportUndefined();
1394
1395 for (self.symbols.items) |*sym| {
1396 if (sym.flags.needs_got) {
1397 log.debug("'{s}' needs GOT", .{sym.name(self)});
1398 // TODO how can we tell we need to write it again, aka the entry is dirty?
1399 const gop = try sym.getOrCreateGotEntry(self);
1400 try self.got.writeEntry(self, gop.index);
1401 }
1402 }
1403}
1404
13841405fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
13851406 const tracy = trace(@src());
13861407 defer tracy.end();
......@@ -2375,8 +2396,9 @@ fn updateDeclCode(
23752396 sym.value = atom_ptr.value;
23762397 esym.st_value = atom_ptr.value;
23772398
2378 const got_index = try sym.getOrCreateGotEntry(self);
2379 try self.got.writeEntry(self, got_index);
2399 sym.flags.needs_got = true;
2400 const gop = try sym.getOrCreateGotEntry(self);
2401 try self.got.writeEntry(self, gop.index);
23802402 }
23812403
23822404 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.
26092631 local_sym.value = atom_ptr.value;
26102632 local_esym.st_value = atom_ptr.value;
26112633
2612 const got_index = try local_sym.getOrCreateGotEntry(self);
2613 try self.got.writeEntry(self, got_index);
2634 local_sym.flags.needs_got = true;
2635 const gop = try local_sym.getOrCreateGotEntry(self);
2636 try self.got.writeEntry(self, gop.index);
26142637
26152638 const section_offset = atom_ptr.value - self.phdrs.items[phdr_index].p_vaddr;
26162639 const file_offset = self.shdrs.items[local_sym.output_section_index].sh_offset + section_offset;
src/link/Elf/Atom.zig+54
......@@ -311,6 +311,57 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
311311 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
312312}
313313
314pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {
315 const file_ptr = elf_file.file(self.file_index).?;
316 const rels = self.relocs(elf_file);
317 var i: usize = 0;
318 while (i < rels.len) : (i += 1) {
319 const rel = rels[i];
320
321 if (rel.r_type() == elf.R_X86_64_NONE) continue;
322
323 const symbol = switch (file_ptr) {
324 .zig_module => elf_file.symbol(rel.r_sym()),
325 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
326 else => unreachable,
327 };
328
329 // While traversing relocations, mark symbols that require special handling such as
330 // pointer indirection via GOT, or a stub trampoline via PLT.
331 switch (rel.r_type()) {
332 elf.R_X86_64_64 => {},
333
334 elf.R_X86_64_32,
335 elf.R_X86_64_32S,
336 => {},
337
338 elf.R_X86_64_GOT32,
339 elf.R_X86_64_GOT64,
340 elf.R_X86_64_GOTPC32,
341 elf.R_X86_64_GOTPC64,
342 elf.R_X86_64_GOTPCREL,
343 elf.R_X86_64_GOTPCREL64,
344 elf.R_X86_64_GOTPCRELX,
345 elf.R_X86_64_REX_GOTPCRELX,
346 => {
347 symbol.flags.needs_got = true;
348 },
349
350 elf.R_X86_64_PLT32,
351 elf.R_X86_64_PLTOFF64,
352 => {
353 if (symbol.flags.import) {
354 symbol.flags.needs_plt = true;
355 }
356 },
357
358 elf.R_X86_64_PC32 => {},
359
360 else => @panic("TODO"),
361 }
362 }
363}
364
314365/// TODO mark relocs dirty
315366pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
316367 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
......@@ -484,6 +535,9 @@ fn format2(
484535 }
485536}
486537
538// TODO this has to be u32 but for now, to avoid redesigning elfSym machinery for
539// ZigModule, keep it at u16 with the intention of bumping it to u32 in the near
540// future.
487541pub const Index = u16;
488542
489543const std = @import("std");
src/link/Elf/Object.zig+6-5
......@@ -367,15 +367,16 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf) !void {
367367 }
368368
369369 for (self.cies.items) |cie| {
370 for (cie.getRelocs(elf_file)) |rel| {
370 for (cie.relocs(elf_file)) |rel| {
371371 const sym = elf_file.symbol(self.symbols.items[rel.r_sym()]);
372372 if (sym.flags.import) {
373 if (sym.getType(elf_file) != elf.STT_FUNC)
374 elf_file.base.fatal("{s}: {s}: CIE referencing external data reference", .{
373 if (sym.type(elf_file) != elf.STT_FUNC)
374 // TODO convert into an error
375 log.debug("{s}: {s}: CIE referencing external data reference", .{
375376 self.fmtPath(),
376 sym.getName(elf_file),
377 sym.name(elf_file),
377378 });
378 sym.flags.plt = true;
379 sym.flags.needs_plt = true;
379380 }
380381 }
381382 }
src/link/Elf/Symbol.zig+15-9
......@@ -111,19 +111,23 @@ pub fn address(symbol: Symbol, opts: struct {
111111}
112112
113113pub fn gotAddress(symbol: Symbol, elf_file: *Elf) u64 {
114 if (!symbol.flags.got) return 0;
114 if (!symbol.flags.has_got) return 0;
115115 const extras = symbol.extra(elf_file).?;
116116 const entry = elf_file.got.entries.items[extras.got];
117117 return entry.address(elf_file);
118118}
119119
120pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GotSection.Index {
121 const index = if (symbol.flags.got)
122 symbol.extra(elf_file).?.got
123 else
124 try elf_file.got.addGotSymbol(symbol.index, elf_file);
125 symbol.flags.got = true;
126 return index;
120const GetOrCreateGotEntryResult = struct {
121 found_existing: bool,
122 index: GotSection.Index,
123};
124
125pub fn getOrCreateGotEntry(symbol: *Symbol, elf_file: *Elf) !GetOrCreateGotEntryResult {
126 assert(symbol.flags.needs_got);
127 if (symbol.flags.has_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.got };
128 const index = try elf_file.got.addGotSymbol(symbol.index, elf_file);
129 symbol.flags.has_got = true;
130 return .{ .found_existing = false, .index = index };
127131}
128132
129133// pub fn tlsGdAddress(symbol: Symbol, elf_file: *Elf) u64 {
......@@ -310,9 +314,11 @@ pub const Flags = packed struct {
310314 output_symtab: bool = false,
311315
312316 /// Whether the symbol contains GOT indirection.
313 got: bool = false,
317 needs_got: bool = false,
318 has_got: bool = false,
314319
315320 /// Whether the symbol contains PLT indirection.
321 needs_plt: bool = false,
316322 plt: bool = false,
317323 /// Whether the PLT entry is canonical.
318324 is_canonical: bool = false,
src/link/Elf/ZigModule.zig+8
......@@ -130,6 +130,14 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
130130 }
131131}
132132
133pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void {
134 for (self.atoms.keys()) |atom_index| {
135 const atom = elf_file.atom(atom_index) orelse continue;
136 if (!atom.alive) continue;
137 try atom.scanRelocs(elf_file);
138 }
139}
140
133141pub fn updateSymtabSize(self: *ZigModule, elf_file: *Elf) void {
134142 for (self.locals()) |local_index| {
135143 const local = elf_file.symbol(local_index);