mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.debug: Fall back to .eh_frame/.debug_frame if .eh_frame_hdr is incomplete.
When using the self-hosted backends, especially in incremental mode, the .eh_frame_hdr section may be incomplete, so we can't treat it as authoritative. Instead, if we started out intending to use .eh_frame_hdr but find that it's incomplete, load .eh_frame/.debug_frame on demand and use that info going forward.
This commit is contained in:
parent
41185d297f
commit
180db2bf23
3 changed files with 88 additions and 39 deletions
|
|
@ -732,11 +732,12 @@ pub const StackIterator = struct {
|
||||||
// via DWARF before attempting to use the compact unwind info will produce incorrect results.
|
// via DWARF before attempting to use the compact unwind info will produce incorrect results.
|
||||||
if (module.unwind_info) |unwind_info| {
|
if (module.unwind_info) |unwind_info| {
|
||||||
if (SelfInfo.unwindFrameMachO(
|
if (SelfInfo.unwindFrameMachO(
|
||||||
|
unwind_state.debug_info.allocator,
|
||||||
|
module.base_address,
|
||||||
&unwind_state.dwarf_context,
|
&unwind_state.dwarf_context,
|
||||||
&it.ma,
|
&it.ma,
|
||||||
unwind_info,
|
unwind_info,
|
||||||
module.eh_frame,
|
module.eh_frame,
|
||||||
module.base_address,
|
|
||||||
)) |return_address| {
|
)) |return_address| {
|
||||||
return return_address;
|
return return_address;
|
||||||
} else |err| {
|
} else |err| {
|
||||||
|
|
@ -748,7 +749,14 @@ pub const StackIterator = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
|
if (try module.getDwarfInfoForAddress(unwind_state.debug_info.allocator, unwind_state.dwarf_context.pc)) |di| {
|
||||||
return SelfInfo.unwindFrameDwarf(di, &unwind_state.dwarf_context, &it.ma, null);
|
return SelfInfo.unwindFrameDwarf(
|
||||||
|
unwind_state.debug_info.allocator,
|
||||||
|
di,
|
||||||
|
module.base_address,
|
||||||
|
&unwind_state.dwarf_context,
|
||||||
|
&it.ma,
|
||||||
|
null,
|
||||||
|
);
|
||||||
} else return error.MissingDebugInfo;
|
} else return error.MissingDebugInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ compile_unit_list: std.ArrayListUnmanaged(CompileUnit) = .empty,
|
||||||
/// Filled later by the initializer
|
/// Filled later by the initializer
|
||||||
func_list: std.ArrayListUnmanaged(Func) = .empty,
|
func_list: std.ArrayListUnmanaged(Func) = .empty,
|
||||||
|
|
||||||
|
/// Starts out non-`null` if the `.eh_frame_hdr` section is present. May become `null` later if we
|
||||||
|
/// find that `.eh_frame_hdr` is incomplete.
|
||||||
eh_frame_hdr: ?ExceptionFrameHeader = null,
|
eh_frame_hdr: ?ExceptionFrameHeader = null,
|
||||||
/// These lookup tables are only used if `eh_frame_hdr` is null
|
/// These lookup tables are only used if `eh_frame_hdr` is null
|
||||||
cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .empty,
|
cie_map: std.AutoArrayHashMapUnmanaged(u64, CommonInformationEntry) = .empty,
|
||||||
|
|
@ -1754,10 +1756,12 @@ fn readDebugAddr(di: Dwarf, compile_unit: CompileUnit, index: u64) !u64 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If .eh_frame_hdr is present, then only the header needs to be parsed.
|
/// 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 for binary searching during
|
||||||
|
/// unwinding. Even if `.eh_frame_hdr` is used, we may find during unwinding that it's incomplete,
|
||||||
|
/// in which case we build the sorted list of FDEs at that point.
|
||||||
///
|
///
|
||||||
/// Otherwise, .eh_frame and .debug_frame are scanned and a sorted list
|
/// See also `scanCieFdeInfo`.
|
||||||
/// of FDEs is built for binary searching during unwinding.
|
|
||||||
pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
|
pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
|
||||||
if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
|
if (di.section(.eh_frame_hdr)) |eh_frame_hdr| blk: {
|
||||||
var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
|
var fbr: FixedBufferReader = .{ .buf = eh_frame_hdr, .endian = native_endian };
|
||||||
|
|
@ -1797,6 +1801,12 @@ pub fn scanAllUnwindInfo(di: *Dwarf, allocator: Allocator, base_address: usize)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try di.scanCieFdeInfo(allocator, base_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scan `.eh_frame` and `.debug_frame` and build a sorted list of FDEs for binary searching during
|
||||||
|
/// unwinding.
|
||||||
|
pub fn scanCieFdeInfo(di: *Dwarf, allocator: Allocator, base_address: usize) !void {
|
||||||
const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
|
const frame_sections = [2]Section.Id{ .eh_frame, .debug_frame };
|
||||||
for (frame_sections) |frame_section| {
|
for (frame_sections) |frame_section| {
|
||||||
if (di.section(frame_section)) |section_data| {
|
if (di.section(frame_section)) |section_data| {
|
||||||
|
|
@ -2125,7 +2135,7 @@ pub const ElfModule = struct {
|
||||||
return self.dwarf.getSymbol(allocator, relocated_address);
|
return self.dwarf.getSymbol(allocator, relocated_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf {
|
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
|
||||||
_ = allocator;
|
_ = allocator;
|
||||||
_ = address;
|
_ = address;
|
||||||
return &self.dwarf;
|
return &self.dwarf;
|
||||||
|
|
|
||||||
|
|
@ -707,7 +707,7 @@ pub const Module = switch (native_os) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf {
|
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
|
||||||
return if ((try self.getOFileInfoForAddress(allocator, address)).o_file_info) |o_file_info| &o_file_info.di else null;
|
return if ((try self.getOFileInfoForAddress(allocator, address)).o_file_info) |o_file_info| &o_file_info.di else null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -784,7 +784,7 @@ pub const Module = switch (native_os) {
|
||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf {
|
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
|
||||||
_ = allocator;
|
_ = allocator;
|
||||||
_ = address;
|
_ = address;
|
||||||
|
|
||||||
|
|
@ -808,7 +808,7 @@ pub const Module = switch (native_os) {
|
||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf {
|
pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf {
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = allocator;
|
_ = allocator;
|
||||||
_ = address;
|
_ = address;
|
||||||
|
|
@ -1156,11 +1156,12 @@ test machoSearchSymbols {
|
||||||
/// If the compact encoding can't encode a way to unwind a frame, it will
|
/// If the compact encoding can't encode a way to unwind a frame, it will
|
||||||
/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
|
/// defer unwinding to DWARF, in which case `.eh_frame` will be used if available.
|
||||||
pub fn unwindFrameMachO(
|
pub fn unwindFrameMachO(
|
||||||
|
allocator: Allocator,
|
||||||
|
base_address: usize,
|
||||||
context: *UnwindContext,
|
context: *UnwindContext,
|
||||||
ma: *std.debug.MemoryAccessor,
|
ma: *std.debug.MemoryAccessor,
|
||||||
unwind_info: []const u8,
|
unwind_info: []const u8,
|
||||||
eh_frame: ?[]const u8,
|
eh_frame: ?[]const u8,
|
||||||
module_base_address: usize,
|
|
||||||
) !usize {
|
) !usize {
|
||||||
const header = std.mem.bytesAsValue(
|
const header = std.mem.bytesAsValue(
|
||||||
macho.unwind_info_section_header,
|
macho.unwind_info_section_header,
|
||||||
|
|
@ -1172,7 +1173,7 @@ pub fn unwindFrameMachO(
|
||||||
);
|
);
|
||||||
if (indices.len == 0) return error.MissingUnwindInfo;
|
if (indices.len == 0) return error.MissingUnwindInfo;
|
||||||
|
|
||||||
const mapped_pc = context.pc - module_base_address;
|
const mapped_pc = context.pc - base_address;
|
||||||
const second_level_index = blk: {
|
const second_level_index = blk: {
|
||||||
var left: usize = 0;
|
var left: usize = 0;
|
||||||
var len: usize = indices.len;
|
var len: usize = indices.len;
|
||||||
|
|
@ -1351,7 +1352,7 @@ pub fn unwindFrameMachO(
|
||||||
else stack_size: {
|
else stack_size: {
|
||||||
// In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
|
// In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function.
|
||||||
const sub_offset_addr =
|
const sub_offset_addr =
|
||||||
module_base_address +
|
base_address +
|
||||||
entry.function_offset +
|
entry.function_offset +
|
||||||
encoding.value.x86_64.frameless.stack.indirect.sub_offset;
|
encoding.value.x86_64.frameless.stack.indirect.sub_offset;
|
||||||
if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
|
if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo;
|
||||||
|
|
@ -1416,7 +1417,7 @@ pub fn unwindFrameMachO(
|
||||||
break :blk new_ip;
|
break :blk new_ip;
|
||||||
},
|
},
|
||||||
.DWARF => {
|
.DWARF => {
|
||||||
return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
|
return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.x86_64.dwarf));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.aarch64, .aarch64_be => switch (encoding.mode.arm64) {
|
.aarch64, .aarch64_be => switch (encoding.mode.arm64) {
|
||||||
|
|
@ -1430,7 +1431,7 @@ pub fn unwindFrameMachO(
|
||||||
break :blk new_ip;
|
break :blk new_ip;
|
||||||
},
|
},
|
||||||
.DWARF => {
|
.DWARF => {
|
||||||
return unwindFrameMachODwarf(context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
|
return unwindFrameMachODwarf(allocator, base_address, context, ma, eh_frame orelse return error.MissingEhFrame, @intCast(encoding.value.arm64.dwarf));
|
||||||
},
|
},
|
||||||
.FRAME => blk: {
|
.FRAME => blk: {
|
||||||
const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
|
const fp = (try regValueNative(context.thread_context, fpRegNum(reg_context), reg_context)).*;
|
||||||
|
|
@ -1555,13 +1556,16 @@ pub inline fn stripInstructionPtrAuthCode(ptr: usize) usize {
|
||||||
|
|
||||||
/// Unwind a stack frame using DWARF unwinding info, updating the register context.
|
/// 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.
|
/// If `.eh_frame_hdr` is available and complete, 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.
|
/// Otherwise, a linear scan of `.eh_frame` and `.debug_frame` is done to find the FDE. The latter
|
||||||
|
/// may require lazily loading the data in those sections.
|
||||||
///
|
///
|
||||||
/// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info
|
/// `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.
|
/// defers unwinding to DWARF. This is an offset into the `.eh_frame` section.
|
||||||
pub fn unwindFrameDwarf(
|
pub fn unwindFrameDwarf(
|
||||||
di: *const Dwarf,
|
allocator: Allocator,
|
||||||
|
di: *Dwarf,
|
||||||
|
base_address: usize,
|
||||||
context: *UnwindContext,
|
context: *UnwindContext,
|
||||||
ma: *std.debug.MemoryAccessor,
|
ma: *std.debug.MemoryAccessor,
|
||||||
explicit_fde_offset: ?usize,
|
explicit_fde_offset: ?usize,
|
||||||
|
|
@ -1570,10 +1574,7 @@ pub fn unwindFrameDwarf(
|
||||||
if (context.pc == 0) return 0;
|
if (context.pc == 0) return 0;
|
||||||
|
|
||||||
// Find the FDE and CIE
|
// Find the FDE and CIE
|
||||||
var cie: Dwarf.CommonInformationEntry = undefined;
|
const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: {
|
||||||
var fde: Dwarf.FrameDescriptionEntry = undefined;
|
|
||||||
|
|
||||||
if (explicit_fde_offset) |fde_offset| {
|
|
||||||
const dwarf_section: Dwarf.Section.Id = .eh_frame;
|
const dwarf_section: Dwarf.Section.Id = .eh_frame;
|
||||||
const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
|
const frame_section = di.section(dwarf_section) orelse return error.MissingFDE;
|
||||||
if (fde_offset >= frame_section.len) return error.MissingFDE;
|
if (fde_offset >= frame_section.len) return error.MissingFDE;
|
||||||
|
|
@ -1594,7 +1595,7 @@ pub fn unwindFrameDwarf(
|
||||||
const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);
|
const cie_entry_header = try Dwarf.EntryHeader.read(&fbr, null, dwarf_section);
|
||||||
if (cie_entry_header.type != .cie) return Dwarf.bad();
|
if (cie_entry_header.type != .cie) return Dwarf.bad();
|
||||||
|
|
||||||
cie = try Dwarf.CommonInformationEntry.parse(
|
const cie = try Dwarf.CommonInformationEntry.parse(
|
||||||
cie_entry_header.entry_bytes,
|
cie_entry_header.entry_bytes,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
|
|
@ -1604,8 +1605,7 @@ pub fn unwindFrameDwarf(
|
||||||
@sizeOf(usize),
|
@sizeOf(usize),
|
||||||
native_endian,
|
native_endian,
|
||||||
);
|
);
|
||||||
|
const fde = try Dwarf.FrameDescriptionEntry.parse(
|
||||||
fde = try Dwarf.FrameDescriptionEntry.parse(
|
|
||||||
fde_entry_header.entry_bytes,
|
fde_entry_header.entry_bytes,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
|
|
@ -1613,17 +1613,44 @@ pub fn unwindFrameDwarf(
|
||||||
@sizeOf(usize),
|
@sizeOf(usize),
|
||||||
native_endian,
|
native_endian,
|
||||||
);
|
);
|
||||||
} else if (di.eh_frame_hdr) |header| {
|
|
||||||
const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
|
break :blk .{ cie, fde };
|
||||||
try header.findEntry(
|
} else blk: {
|
||||||
ma,
|
// `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall
|
||||||
eh_frame_len,
|
// back to loading `.eh_frame`/`.debug_frame` and using those from that point on.
|
||||||
@intFromPtr(di.section(.eh_frame_hdr).?.ptr),
|
|
||||||
context.pc,
|
if (di.eh_frame_hdr) |header| hdr: {
|
||||||
&cie,
|
const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null;
|
||||||
&fde,
|
|
||||||
);
|
var cie: Dwarf.CommonInformationEntry = undefined;
|
||||||
} else {
|
var fde: Dwarf.FrameDescriptionEntry = undefined;
|
||||||
|
|
||||||
|
header.findEntry(
|
||||||
|
ma,
|
||||||
|
eh_frame_len,
|
||||||
|
@intFromPtr(di.section(.eh_frame_hdr).?.ptr),
|
||||||
|
context.pc,
|
||||||
|
&cie,
|
||||||
|
&fde,
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
error.InvalidDebugInfo => {
|
||||||
|
// `.eh_frame_hdr` appears to be incomplete, so go ahead and populate `cie_map`
|
||||||
|
// and `fde_list`, and fall back to the binary search logic below.
|
||||||
|
try di.scanCieFdeInfo(allocator, base_address);
|
||||||
|
|
||||||
|
// Since `.eh_frame_hdr` is incomplete, we're very likely to get more lookup
|
||||||
|
// failures using it, and we've just built a complete, sorted list of FDEs
|
||||||
|
// anyway, so just stop using `.eh_frame_hdr` altogether.
|
||||||
|
di.eh_frame_hdr = null;
|
||||||
|
|
||||||
|
break :hdr;
|
||||||
|
},
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
|
||||||
|
break :blk .{ cie, fde };
|
||||||
|
}
|
||||||
|
|
||||||
const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, di.fde_list.items, context.pc, struct {
|
const index = std.sort.binarySearch(Dwarf.FrameDescriptionEntry, di.fde_list.items, context.pc, struct {
|
||||||
pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order {
|
pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order {
|
||||||
if (pc < item.pc_begin) return .lt;
|
if (pc < item.pc_begin) return .lt;
|
||||||
|
|
@ -1635,9 +1662,11 @@ pub fn unwindFrameDwarf(
|
||||||
}
|
}
|
||||||
}.compareFn);
|
}.compareFn);
|
||||||
|
|
||||||
fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
|
const fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE;
|
||||||
cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
|
const cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE;
|
||||||
}
|
|
||||||
|
break :blk .{ cie, fde };
|
||||||
|
};
|
||||||
|
|
||||||
var expression_context: Dwarf.expression.Context = .{
|
var expression_context: Dwarf.expression.Context = .{
|
||||||
.format = cie.format,
|
.format = cie.format,
|
||||||
|
|
@ -1802,6 +1831,8 @@ pub fn supportsUnwinding(target: std.Target) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwindFrameMachODwarf(
|
fn unwindFrameMachODwarf(
|
||||||
|
allocator: Allocator,
|
||||||
|
base_address: usize,
|
||||||
context: *UnwindContext,
|
context: *UnwindContext,
|
||||||
ma: *std.debug.MemoryAccessor,
|
ma: *std.debug.MemoryAccessor,
|
||||||
eh_frame: []const u8,
|
eh_frame: []const u8,
|
||||||
|
|
@ -1818,7 +1849,7 @@ fn unwindFrameMachODwarf(
|
||||||
.owned = false,
|
.owned = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return unwindFrameDwarf(&di, context, ma, fde_offset);
|
return unwindFrameDwarf(allocator, &di, base_address, context, ma, fde_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a virtual machine that runs DWARF call frame instructions.
|
/// This is a virtual machine that runs DWARF call frame instructions.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue