authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-08 15:17:20+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-09-30 13:44:52+01:00
logf40fbdb3b300233a2fc880bad77c986259c38b0f
treeabee715b0dc094c2ba3ad64e6eb39590b145adec
parentd9661e9e05af7a4be31c17cbfbbd8bc44d7c9000
signaturelock-open Commit is signed but in an unrecognized format.

link.Elf: restore eh_frame_hdr search table building

At least, when there's not a ZigObject. The old behavior was incorrect in the presence of a ZigObject, and this doesn't really mix nicely with incremental compilation anyway; but when the objects are all external, we may as well build the search table.

1 files changed, 65 insertions(+), 6 deletions(-)

src/link/Elf/eh_frame.zig+65-6
......@@ -234,7 +234,14 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize {
234234 return offset;
235235}
236236
237fn haveEhFrameHdrSearchTable(elf_file: *Elf) bool {
238 // Seach table generation is not implemented for the ZigObject. Also, it would be wasteful to
239 // re-do this work on every single incremental update.
240 return elf_file.zigObjectPtr() == null;
241}
242
237243pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
244 if (!haveEhFrameHdrSearchTable(elf_file)) return 8;
238245 var count: usize = 0;
239246 for (elf_file.objects.items) |index| {
240247 for (elf_file.file(index).?.object.fdes.items) |fde| {
......@@ -242,7 +249,7 @@ pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
242249 count += 1;
243250 }
244251 }
245 return eh_frame_hdr_header_size + count * 8;
252 return 12 + count * 8;
246253}
247254
248255pub fn calcEhFrameRelocs(elf_file: *Elf) usize {
......@@ -455,15 +462,23 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.array_list.Managed(elf.El
455462}
456463
457464pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
465 const endian = elf_file.getTarget().cpu.arch.endian();
466 const have_table = haveEhFrameHdrSearchTable(elf_file);
467
458468 try writer.writeByte(1); // version
459469 try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .pcrel }))); // eh_frame_ptr_enc
460 // Building the lookup table would be expensive work on every `flush` -- omit it.
461 try writer.writeByte(@bitCast(DW_EH_PE.omit)); // fde_count_enc
462 try writer.writeByte(@bitCast(DW_EH_PE.omit)); // table_enc
470 if (have_table) {
471 try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .udata4, .rel = .abs }))); // fde_count_enc
472 try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .datarel }))); // table_enc
473 } else {
474 try writer.writeByte(@bitCast(DW_EH_PE.omit)); // fde_count_enc
475 try writer.writeByte(@bitCast(DW_EH_PE.omit)); // table_enc
476 }
463477
464478 const shdrs = elf_file.sections.items(.shdr);
465479 const eh_frame_shdr = shdrs[elf_file.section_indexes.eh_frame.?];
466480 const eh_frame_hdr_shdr = shdrs[elf_file.section_indexes.eh_frame_hdr.?];
481 // eh_frame_ptr
467482 try writer.writeInt(
468483 u32,
469484 @as(u32, @bitCast(@as(
......@@ -472,9 +487,51 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
472487 ))),
473488 .little,
474489 );
475}
476490
477const eh_frame_hdr_header_size: usize = 12;
491 if (!have_table) return;
492
493 const gpa = elf_file.base.comp.gpa;
494
495 // This must be an `extern struct` because we will write the bytes directly to the file.
496 const Entry = extern struct {
497 first_pc_rel: i32,
498 fde_addr_rel: i32,
499 fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
500 return lhs.first_pc_rel < rhs.first_pc_rel;
501 }
502 };
503 // The number of entries was already computed by `calcEhFrameHdrSize`.
504 const num_fdes: u32 = @intCast(@divExact(eh_frame_hdr_shdr.sh_size - 12, 8));
505 try writer.writeInt(u32, num_fdes, endian);
506
507 var entries: std.ArrayList(Entry) = try .initCapacity(gpa, num_fdes);
508 defer entries.deinit(gpa);
509 for (elf_file.objects.items) |file_index| {
510 const object = elf_file.file(file_index).?.object;
511 for (object.fdes.items) |fde| {
512 if (!fde.alive) continue;
513 const relocs = fde.relocs(object);
514 // Should `relocs.len == 0` be an error? Things are completely broken anyhow in that case...
515 const rel = relocs[0];
516 const ref = object.resolveSymbol(rel.r_sym(), elf_file);
517 const sym = elf_file.symbol(ref).?;
518 const fde_addr_abs: i64 = @intCast(fde.address(elf_file));
519 const fde_addr_rel: i64 = fde_addr_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr));
520 const first_pc_abs: i64 = @as(i64, @intCast(sym.address(.{}, elf_file))) + rel.r_addend;
521 const first_pc_rel: i64 = first_pc_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr));
522 entries.appendAssumeCapacity(.{
523 .first_pc_rel = @truncate(first_pc_rel),
524 .fde_addr_rel = @truncate(fde_addr_rel),
525 });
526 }
527 }
528 assert(entries.items.len == num_fdes);
529 std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
530 if (endian != builtin.cpu.arch.endian()) {
531 std.mem.byteSwapAllElements(Entry, entries.items);
532 }
533 try writer.writeAll(@ptrCast(entries.items));
534}
478535
479536const x86_64 = struct {
480537 fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void {
......@@ -538,3 +595,5 @@ const DW_EH_PE = std.dwarf.EH.PE;
538595const Elf = @import("../Elf.zig");
539596const Object = @import("Object.zig");
540597const Symbol = @import("Symbol.zig");
598
599const builtin = @import("builtin");