structstd.debug.Dwarf.ExceptionFrameHeader[src]

This represents the decoded .eh_frame_hdr header

Fields

eh_frame_ptr: usize
table_enc: u8
fde_count: usize
entries: []const u8

Functions

FunctionentrySize[src]

pub fn entrySize(table_enc: u8) !u8

Parameters

table_enc: u8

Source Code

Source code
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(),
    };
}

FunctionfindEntry[src]

pub fn findEntry( self: ExceptionFrameHeader, ma: *MemoryAccessor, eh_frame_len: ?usize, eh_frame_hdr_ptr: usize, pc: usize, cie: *CommonInformationEntry, fde: *FrameDescriptionEntry, ) !void

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.

Parameters

eh_frame_len: ?usize
eh_frame_hdr_ptr: usize
pc: usize

Source Code

Source code
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,
    );
}

Source Code

Source code
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,
        );
    }
}