diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 326764070a..0b1c92d098 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -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");