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.
This commit is contained in:
mlugg 2025-09-08 15:17:20 +01:00
parent d9661e9e05
commit f40fbdb3b3
No known key found for this signature in database
GPG Key ID: 3F5B7DCCBF4AF02E

View File

@ -234,7 +234,14 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize {
return offset;
}
fn haveEhFrameHdrSearchTable(elf_file: *Elf) bool {
// Seach table generation is not implemented for the ZigObject. Also, it would be wasteful to
// re-do this work on every single incremental update.
return elf_file.zigObjectPtr() == null;
}
pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
if (!haveEhFrameHdrSearchTable(elf_file)) return 8;
var count: usize = 0;
for (elf_file.objects.items) |index| {
for (elf_file.file(index).?.object.fdes.items) |fde| {
@ -242,7 +249,7 @@ pub fn calcEhFrameHdrSize(elf_file: *Elf) usize {
count += 1;
}
}
return eh_frame_hdr_header_size + count * 8;
return 12 + count * 8;
}
pub fn calcEhFrameRelocs(elf_file: *Elf) usize {
@ -455,15 +462,23 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.array_list.Managed(elf.El
}
pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
const endian = elf_file.getTarget().cpu.arch.endian();
const have_table = haveEhFrameHdrSearchTable(elf_file);
try writer.writeByte(1); // version
try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .pcrel }))); // eh_frame_ptr_enc
// Building the lookup table would be expensive work on every `flush` -- omit it.
try writer.writeByte(@bitCast(DW_EH_PE.omit)); // fde_count_enc
try writer.writeByte(@bitCast(DW_EH_PE.omit)); // table_enc
if (have_table) {
try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .udata4, .rel = .abs }))); // fde_count_enc
try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .datarel }))); // table_enc
} else {
try writer.writeByte(@bitCast(DW_EH_PE.omit)); // fde_count_enc
try writer.writeByte(@bitCast(DW_EH_PE.omit)); // table_enc
}
const shdrs = elf_file.sections.items(.shdr);
const eh_frame_shdr = shdrs[elf_file.section_indexes.eh_frame.?];
const eh_frame_hdr_shdr = shdrs[elf_file.section_indexes.eh_frame_hdr.?];
// eh_frame_ptr
try writer.writeInt(
u32,
@as(u32, @bitCast(@as(
@ -472,9 +487,51 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void {
))),
.little,
);
}
const eh_frame_hdr_header_size: usize = 12;
if (!have_table) return;
const gpa = elf_file.base.comp.gpa;
// This must be an `extern struct` because we will write the bytes directly to the file.
const Entry = extern struct {
first_pc_rel: i32,
fde_addr_rel: i32,
fn lessThan(_: void, lhs: @This(), rhs: @This()) bool {
return lhs.first_pc_rel < rhs.first_pc_rel;
}
};
// The number of entries was already computed by `calcEhFrameHdrSize`.
const num_fdes: u32 = @intCast(@divExact(eh_frame_hdr_shdr.sh_size - 12, 8));
try writer.writeInt(u32, num_fdes, endian);
var entries: std.ArrayList(Entry) = try .initCapacity(gpa, num_fdes);
defer entries.deinit(gpa);
for (elf_file.objects.items) |file_index| {
const object = elf_file.file(file_index).?.object;
for (object.fdes.items) |fde| {
if (!fde.alive) continue;
const relocs = fde.relocs(object);
// Should `relocs.len == 0` be an error? Things are completely broken anyhow in that case...
const rel = relocs[0];
const ref = object.resolveSymbol(rel.r_sym(), elf_file);
const sym = elf_file.symbol(ref).?;
const fde_addr_abs: i64 = @intCast(fde.address(elf_file));
const fde_addr_rel: i64 = fde_addr_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr));
const first_pc_abs: i64 = @as(i64, @intCast(sym.address(.{}, elf_file))) + rel.r_addend;
const first_pc_rel: i64 = first_pc_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr));
entries.appendAssumeCapacity(.{
.first_pc_rel = @truncate(first_pc_rel),
.fde_addr_rel = @truncate(fde_addr_rel),
});
}
}
assert(entries.items.len == num_fdes);
std.mem.sort(Entry, entries.items, {}, Entry.lessThan);
if (endian != builtin.cpu.arch.endian()) {
std.mem.byteSwapAllElements(Entry, entries.items);
}
try writer.writeAll(@ptrCast(entries.items));
}
const x86_64 = struct {
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;
const Elf = @import("../Elf.zig");
const Object = @import("Object.zig");
const Symbol = @import("Symbol.zig");
const builtin = @import("builtin");