struct ExceptionFrameHeader [src]
This represents the decoded .eh_frame_hdr header
Fields
eh_frame_ptr: usize
table_enc: u8
fde_count: usize
entries: []const u8
Members
Source
pub const ExceptionFrameHeader = struct {
eh_frame_ptr: usize,
table_enc: u8,
fde_count: usize,
entries: []const u8,
pub fn entrySize(table_enc: u8) !u8 {
return switch (table_enc & EH.PE.type_mask) {
EH.PE.udata2,
EH.PE.sdata2,
=> 4,
EH.PE.udata4,
EH.PE.sdata4,
=> 8,
EH.PE.udata8,
EH.PE.sdata8,
=> 16,
// This is a binary search table, so all entries must be the same length
else => return bad(),
};
}
fn isValidPtr(
self: ExceptionFrameHeader,
comptime T: type,
ptr: usize,
ma: *MemoryAccessor,
eh_frame_len: ?usize,
) bool {
if (eh_frame_len) |len| {
return ptr >= self.eh_frame_ptr and ptr <= self.eh_frame_ptr + len - @sizeOf(T);
} else {
return ma.load(T, ptr) != null;
}
}
/// Find an entry by binary searching the eh_frame_hdr section.
///
/// Since the length of the eh_frame section (`eh_frame_len`) may not be known by the caller,
/// MemoryAccessor will be used to verify readability of the header entries.
/// If `eh_frame_len` is provided, then these checks can be skipped.
pub fn findEntry(
self: ExceptionFrameHeader,
ma: *MemoryAccessor,
eh_frame_len: ?usize,
eh_frame_hdr_ptr: usize,
pc: usize,
cie: *CommonInformationEntry,
fde: *FrameDescriptionEntry,
) !void {
const entry_size = try entrySize(self.table_enc);
var left: usize = 0;
var len: usize = self.fde_count;
var fbr: FixedBufferReader = .{ .buf = self.entries, .endian = native_endian };
while (len > 1) {
const mid = left + len / 2;
fbr.pos = mid * entry_size;
const pc_begin = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
.pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
.follow_indirect = true,
.data_rel_base = eh_frame_hdr_ptr,
}) orelse return bad();
if (pc < pc_begin) {
len /= 2;
} else {
left = mid;
if (pc == pc_begin) break;
len -= len / 2;
}
}
if (len == 0) return bad();
fbr.pos = left * entry_size;
// Read past the pc_begin field of the entry
_ = try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
.pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
.follow_indirect = true,
.data_rel_base = eh_frame_hdr_ptr,
}) orelse return bad();
const fde_ptr = cast(usize, try readEhPointer(&fbr, self.table_enc, @sizeOf(usize), .{
.pc_rel_base = @intFromPtr(&self.entries[fbr.pos]),
.follow_indirect = true,
.data_rel_base = eh_frame_hdr_ptr,
}) orelse return bad()) orelse return bad();
if (fde_ptr < self.eh_frame_ptr) return bad();
// Even if eh_frame_len is not specified, all ranges accssed are checked via MemoryAccessor
const eh_frame = @as([*]const u8, @ptrFromInt(self.eh_frame_ptr))[0 .. eh_frame_len orelse maxInt(u32)];
const fde_offset = fde_ptr - self.eh_frame_ptr;
var eh_frame_fbr: FixedBufferReader = .{
.buf = eh_frame,
.pos = fde_offset,
.endian = native_endian,
};
const fde_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
if (fde_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
if (fde_entry_header.type != .fde) return bad();
// CIEs always come before FDEs (the offset is a subtraction), so we can assume this memory is readable
const cie_offset = fde_entry_header.type.fde;
try eh_frame_fbr.seekTo(cie_offset);
const cie_entry_header = try EntryHeader.read(&eh_frame_fbr, if (eh_frame_len == null) ma else null, .eh_frame);
if (cie_entry_header.entry_bytes.len > 0 and !self.isValidPtr(u8, @intFromPtr(&cie_entry_header.entry_bytes[cie_entry_header.entry_bytes.len - 1]), ma, eh_frame_len)) return bad();
if (cie_entry_header.type != .cie) return bad();
cie.* = try CommonInformationEntry.parse(
cie_entry_header.entry_bytes,
0,
true,
cie_entry_header.format,
.eh_frame,
cie_entry_header.length_offset,
@sizeOf(usize),
native_endian,
);
fde.* = try FrameDescriptionEntry.parse(
fde_entry_header.entry_bytes,
0,
true,
cie.*,
@sizeOf(usize),
native_endian,
);
}
}