Decoded version of the .eh_frame_hdr section.
pub const EhFrameHeader = struct
pub const EhFrameHeader = struct {
/// The virtual address (i.e. as given in the binary, before relocations) of the `.eh_frame`
/// section. This value is important when using `.eh_frame_hdr` to find debug information for
/// the current binary, because it allows locating where the `.eh_frame` section is loaded in
/// memory (by adding it to the ELF module's base address).
eh_frame_vaddr: u64,
search_table: ?SearchTable,
pub const SearchTable = struct {
/// The byte offset of the search table into the `.eh_frame_hdr` section.
offset: u8,
encoding: EH.PE,
fde_count: usize,
/// The actual table entries are viewed as a plain byte slice because `encoding` causes the
/// size of entries in the table to vary.
entries: []const u8,
/// Returns the vaddr of the FDE for `pc`, or `null` if no matching FDE was found.
fn findEntry(
table: *const SearchTable,
eh_frame_hdr_vaddr: u64,
pc: u64,
addr_size_bytes: u8,
endian: Endian,
) !?u64 {
const table_vaddr = eh_frame_hdr_vaddr + table.offset;
const entry_size = try entrySize(table.encoding, addr_size_bytes);
var left: usize = 0;
var len: usize = table.fde_count;
while (len > 1) {
const mid = left + len / 2;
var entry_reader: Reader = .fixed(table.entries[mid * entry_size ..][0..entry_size]);
const pc_begin = try readEhPointer(&entry_reader, table.encoding, addr_size_bytes, .{
.pc_rel_base = table_vaddr + left * entry_size,
.data_rel_base = eh_frame_hdr_vaddr,
}, endian);
if (pc < pc_begin) {
len /= 2;
} else {
left = mid;
len -= len / 2;
}
}
if (len == 0) return null;
var entry_reader: Reader = .fixed(table.entries[left * entry_size ..][0..entry_size]);
// Skip past `pc_begin`; we're now interested in the fde offset
_ = try readEhPointerAbs(&entry_reader, table.encoding.type, addr_size_bytes, endian);
const fde_ptr = try readEhPointer(&entry_reader, table.encoding, addr_size_bytes, .{
.pc_rel_base = table_vaddr + left * entry_size,
.data_rel_base = eh_frame_hdr_vaddr,
}, endian);
return fde_ptr;
}
fn entrySize(table_enc: EH.PE, addr_size_bytes: u8) !u8 {
return switch (table_enc.type) {
.absptr => 2 * addr_size_bytes,
.udata2, .sdata2 => 4,
.udata4, .sdata4 => 8,
.udata8, .sdata8 => 16,
.uleb128, .sleb128 => return bad(), // this is a binary search table; all entries must be the same size
_ => return bad(),
};
}
};
pub fn parse(
eh_frame_hdr_vaddr: u64,
eh_frame_hdr_bytes: []const u8,
addr_size_bytes: u8,
endian: Endian,
) !EhFrameHeader {
var r: Reader = .fixed(eh_frame_hdr_bytes);
const version = try r.takeByte();
if (version != 1) return bad();
const eh_frame_ptr_enc: EH.PE = @bitCast(try r.takeByte());
const fde_count_enc: EH.PE = @bitCast(try r.takeByte());
const table_enc: EH.PE = @bitCast(try r.takeByte());
const eh_frame_ptr = try readEhPointer(&r, eh_frame_ptr_enc, addr_size_bytes, .{
.pc_rel_base = eh_frame_hdr_vaddr + r.seek,
}, endian);
const table: ?SearchTable = table: {
if (fde_count_enc == EH.PE.omit) break :table null;
if (table_enc == EH.PE.omit) break :table null;
const fde_count = try readEhPointer(&r, fde_count_enc, addr_size_bytes, .{
.pc_rel_base = eh_frame_hdr_vaddr + r.seek,
}, endian);
const entry_size = try SearchTable.entrySize(table_enc, addr_size_bytes);
const bytes_offset = r.seek;
const bytes_len = cast(usize, fde_count * entry_size) orelse return error.EndOfStream;
const bytes = try r.take(bytes_len);
break :table .{
.encoding = table_enc,
.fde_count = @intCast(fde_count),
.entries = bytes,
.offset = @intCast(bytes_offset),
};
};
return .{
.eh_frame_vaddr = eh_frame_ptr,
.search_table = table,
};
}
}