mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
dwarf: add explicit_fde_offset to support more optimal __unwind_info dwarf lookups
This commit is contained in:
parent
bdb0a6fa77
commit
774dc2fdb7
@ -653,7 +653,7 @@ pub const StackIterator = struct {
|
||||
}
|
||||
|
||||
if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
|
||||
return di.unwindFrame(&unwind_state.dwarf_context, module.base_address);
|
||||
return di.unwindFrame(&unwind_state.dwarf_context, null);
|
||||
} else return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
@ -1894,7 +1894,6 @@ pub const DebugInfo = struct {
|
||||
obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, §ions, null);
|
||||
obj_di.base_address = ctx.base_address;
|
||||
|
||||
// TODO: Don't actually scan everything, search on demand
|
||||
// Missing unwind info isn't treated as a failure, as the unwinder will fall back to FP-based unwinding
|
||||
obj_di.dwarf.scanAllUnwindInfo(self.allocator, ctx.base_address) catch {};
|
||||
|
||||
|
||||
@ -1562,8 +1562,7 @@ pub const DwarfInfo = struct {
|
||||
/// If .eh_frame_hdr is present, then only the header needs to be parsed.
|
||||
///
|
||||
/// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list
|
||||
/// of FDEs is built. In this case, the decoded PC ranges in the FDEs
|
||||
/// are all normalized to be relative to the module's base.
|
||||
/// of FDEs is built for binary searching during unwinding.
|
||||
pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, base_address: usize) !void {
|
||||
if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
|
||||
var stream = io.fixedBufferStream(eh_frame_hdr);
|
||||
@ -1650,7 +1649,14 @@ pub const DwarfInfo = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, module_base_address: usize) !usize {
|
||||
/// Unwind a stack frame using DWARF unwinding info, updating the register context.
|
||||
///
|
||||
/// If `.eh_frame_hdr` is available, it will be used to binary search for the FDE.
|
||||
/// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE.
|
||||
///
|
||||
/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
|
||||
/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
|
||||
pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, explicit_fde_offset: ?usize) !usize {
|
||||
if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
|
||||
if (context.pc == 0) return 0;
|
||||
|
||||
@ -1660,26 +1666,54 @@ pub const DwarfInfo = struct {
|
||||
var cie: CommonInformationEntry = undefined;
|
||||
var fde: FrameDescriptionEntry = undefined;
|
||||
|
||||
// In order to support reading .eh_frame from the ELF file (vs using the already-mapped section),
|
||||
// scanAllUnwindInfo has already mapped any pc-relative offsets such that they will be relative to zero
|
||||
// instead of the actual base address of the module. When using .eh_frame_hdr, PC can be used directly
|
||||
// as pointers will be decoded relative to the already-mapped .eh_frame.
|
||||
var mapped_pc: usize = undefined;
|
||||
if (di.eh_frame_hdr) |header| {
|
||||
if (explicit_fde_offset) |fde_offset| {
|
||||
const dwarf_section: DwarfSection = .eh_frame;
|
||||
const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
|
||||
if (fde_offset >= frame_section.len) return error.MissingFDE;
|
||||
|
||||
var stream = io.fixedBufferStream(frame_section);
|
||||
const fde_entry_header = try EntryHeader.read(&stream, dwarf_section, di.endian);
|
||||
if (fde_entry_header.type != .fde) return error.MissingFDE;
|
||||
|
||||
const cie_offset = fde_entry_header.type.fde;
|
||||
try stream.seekTo(cie_offset);
|
||||
|
||||
const cie_entry_header = try EntryHeader.read(&stream, dwarf_section, builtin.cpu.arch.endian());
|
||||
if (cie_entry_header.type != .cie) return badDwarf();
|
||||
|
||||
cie = try CommonInformationEntry.parse(
|
||||
cie_entry_header.entry_bytes,
|
||||
0,
|
||||
true,
|
||||
cie_entry_header.is_64,
|
||||
dwarf_section,
|
||||
cie_entry_header.length_offset,
|
||||
@sizeOf(usize),
|
||||
builtin.cpu.arch.endian(),
|
||||
);
|
||||
|
||||
fde = try FrameDescriptionEntry.parse(
|
||||
fde_entry_header.entry_bytes,
|
||||
0,
|
||||
true,
|
||||
cie,
|
||||
@sizeOf(usize),
|
||||
builtin.cpu.arch.endian(),
|
||||
);
|
||||
} else if (di.eh_frame_hdr) |header| {
|
||||
std.debug.print("EH_FRAME_HDR\n", .{});
|
||||
|
||||
const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
|
||||
mapped_pc = context.pc;
|
||||
try header.findEntry(
|
||||
context.isValidMemory,
|
||||
eh_frame_len,
|
||||
@intFromPtr(di.section(.eh_frame_hdr).?.ptr),
|
||||
mapped_pc,
|
||||
context.pc,
|
||||
&cie,
|
||||
&fde,
|
||||
);
|
||||
} else {
|
||||
//mapped_pc = context.pc - module_base_address;
|
||||
mapped_pc = context.pc;
|
||||
const index = std.sort.binarySearch(FrameDescriptionEntry, mapped_pc, di.fde_list.items, {}, struct {
|
||||
const index = std.sort.binarySearch(FrameDescriptionEntry, context.pc, di.fde_list.items, {}, struct {
|
||||
pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
|
||||
if (pc < mid_item.pc_begin) return .lt;
|
||||
|
||||
@ -1707,7 +1741,7 @@ pub const DwarfInfo = struct {
|
||||
context.reg_context.eh_frame = cie.version != 4;
|
||||
context.reg_context.is_macho = di.is_macho;
|
||||
|
||||
_ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
|
||||
_ = try context.vm.runToNative(context.allocator, context.pc, cie, fde);
|
||||
const row = &context.vm.current_row;
|
||||
|
||||
context.cfa = switch (row.cfa.rule) {
|
||||
@ -2056,7 +2090,7 @@ pub const ExceptionFrameHeader = struct {
|
||||
if (!self.isValidPtr(@intFromPtr(&fde_entry_header.entry_bytes[fde_entry_header.entry_bytes.len - 1]), isValidMemory, eh_frame_len)) return badDwarf();
|
||||
if (fde_entry_header.type != .fde) return badDwarf();
|
||||
|
||||
// CIEs always come before FDEs (the offset is a subtration), so we can assume this memory is readable
|
||||
// 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_stream.seekTo(cie_offset);
|
||||
const cie_entry_header = try EntryHeader.read(&eh_frame_stream, .eh_frame, builtin.cpu.arch.endian());
|
||||
|
||||
@ -8,6 +8,14 @@ pub fn build(b: *std.Build) void {
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
// Unwinding pure zig code, with a frame pointer
|
||||
//
|
||||
// getcontext version: zig std
|
||||
//
|
||||
// Unwind info type:
|
||||
// - ELF: DWARF .debug_frame
|
||||
// - MachO: __unwind_info encodings:
|
||||
// - x86_64: RBP_FRAME
|
||||
// - aarch64: FRAME, DWARF
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zig_unwind_fp",
|
||||
@ -23,7 +31,15 @@ pub fn build(b: *std.Build) void {
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// Unwinding pure zig code, without a frame pointer
|
||||
// Unwinding pure zig code, without a frame pointer.
|
||||
//
|
||||
// getcontext version: zig std
|
||||
//
|
||||
// Unwind info type:
|
||||
// - ELF: DWARF .eh_frame_hdr + .eh_frame
|
||||
// - MachO: __unwind_info encodings:
|
||||
// - x86_64: STACK_IMMD, STACK_IND
|
||||
// - aarch64: FRAMELESS, DWARF
|
||||
{
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "zig_unwind_nofp",
|
||||
@ -34,12 +50,21 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
if (target.isDarwin()) exe.unwind_tables = true;
|
||||
exe.omit_frame_pointer = true;
|
||||
exe.unwind_tables = true;
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
|
||||
// Unwinding through a C shared library without a frame pointer (libc)
|
||||
//
|
||||
// getcontext version: libc
|
||||
//
|
||||
// Unwind info type:
|
||||
// - ELF: DWARF .eh_frame + .debug_frame
|
||||
// - MachO: __unwind_info encodings:
|
||||
// - x86_64: STACK_IMMD, STACK_IND
|
||||
// - aarch64: FRAMELESS, DWARF
|
||||
{
|
||||
const c_shared_lib = b.addSharedLibrary(.{
|
||||
.name = "c_shared_lib",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user