diff options
| author | Alex Rønne Petersen <alex@alexrp.com> | 2025-01-23 20:55:20 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-01-23 23:22:38 +0000 |
| commit | 180db2bf23f05a02876d4567cac3b04842c11acb (patch) | |
| tree | c30cddc29ae1f1162f3ec38fcaf89b94ecb4e6dd /lib/std/debug/SelfInfo.zig | |
| parent | 41185d297ffcaf61776aa8e7610aea9d00fce3a4 (diff) | |
| download | zig-180db2bf23f05a02876d4567cac3b04842c11acb.tar.gz zig-180db2bf23f05a02876d4567cac3b04842c11acb.zip | |
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.
Diffstat (limited to 'lib/std/debug/SelfInfo.zig')
| -rw-r--r-- | lib/std/debug/SelfInfo.zig | 97 |
1 files changed, 64 insertions, 33 deletions
diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index 4dd0b4e842..a2cea70d37 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -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; } }, @@ -784,7 +784,7 @@ pub const Module = switch (native_os) { return .{}; } - pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf { + pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { _ = allocator; _ = address; @@ -808,7 +808,7 @@ pub const Module = switch (native_os) { return .{}; } - pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*const Dwarf { + pub fn getDwarfInfoForAddress(self: *@This(), allocator: Allocator, address: usize) !?*Dwarf { _ = self; _ = allocator; _ = address; @@ -1156,11 +1156,12 @@ test machoSearchSymbols { /// 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. pub fn unwindFrameMachO( + allocator: Allocator, + base_address: usize, context: *UnwindContext, ma: *std.debug.MemoryAccessor, unwind_info: []const u8, eh_frame: ?[]const u8, - module_base_address: usize, ) !usize { const header = std.mem.bytesAsValue( macho.unwind_info_section_header, @@ -1172,7 +1173,7 @@ pub fn unwindFrameMachO( ); 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: { var left: usize = 0; var len: usize = indices.len; @@ -1351,7 +1352,7 @@ pub fn unwindFrameMachO( else stack_size: { // In .STACK_IND, the stack size is inferred from the subq instruction at the beginning of the function. const sub_offset_addr = - module_base_address + + base_address + entry.function_offset + encoding.value.x86_64.frameless.stack.indirect.sub_offset; if (ma.load(usize, sub_offset_addr) == null) return error.InvalidUnwindInfo; @@ -1416,7 +1417,7 @@ pub fn unwindFrameMachO( break :blk new_ip; }, .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) { @@ -1430,7 +1431,7 @@ pub fn unwindFrameMachO( break :blk new_ip; }, .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: { 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. /// -/// 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. +/// 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. 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 /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. pub fn unwindFrameDwarf( - di: *const Dwarf, + allocator: Allocator, + di: *Dwarf, + base_address: usize, context: *UnwindContext, ma: *std.debug.MemoryAccessor, explicit_fde_offset: ?usize, @@ -1570,10 +1574,7 @@ pub fn unwindFrameDwarf( if (context.pc == 0) return 0; // Find the FDE and CIE - var cie: Dwarf.CommonInformationEntry = undefined; - var fde: Dwarf.FrameDescriptionEntry = undefined; - - if (explicit_fde_offset) |fde_offset| { + const cie, const fde = if (explicit_fde_offset) |fde_offset| blk: { const dwarf_section: Dwarf.Section.Id = .eh_frame; const frame_section = di.section(dwarf_section) orelse 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); 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, 0, true, @@ -1604,8 +1605,7 @@ pub fn unwindFrameDwarf( @sizeOf(usize), native_endian, ); - - fde = try Dwarf.FrameDescriptionEntry.parse( + const fde = try Dwarf.FrameDescriptionEntry.parse( fde_entry_header.entry_bytes, 0, true, @@ -1613,17 +1613,44 @@ pub fn unwindFrameDwarf( @sizeOf(usize), 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; - try header.findEntry( - ma, - eh_frame_len, - @intFromPtr(di.section(.eh_frame_hdr).?.ptr), - context.pc, - &cie, - &fde, - ); - } else { + + break :blk .{ cie, fde }; + } else blk: { + // `.eh_frame_hdr` may be incomplete. We'll try it first, but if the lookup fails, we fall + // back to loading `.eh_frame`/`.debug_frame` and using those from that point on. + + if (di.eh_frame_hdr) |header| hdr: { + const eh_frame_len = if (di.section(.eh_frame)) |eh_frame| eh_frame.len else null; + + var cie: Dwarf.CommonInformationEntry = undefined; + 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 { pub fn compareFn(pc: usize, item: Dwarf.FrameDescriptionEntry) std.math.Order { if (pc < item.pc_begin) return .lt; @@ -1635,9 +1662,11 @@ pub fn unwindFrameDwarf( } }.compareFn); - 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 fde = if (index) |i| di.fde_list.items[i] else return error.MissingFDE; + const cie = di.cie_map.get(fde.cie_length_offset) orelse return error.MissingCIE; + + break :blk .{ cie, fde }; + }; var expression_context: Dwarf.expression.Context = .{ .format = cie.format, @@ -1802,6 +1831,8 @@ pub fn supportsUnwinding(target: std.Target) bool { } fn unwindFrameMachODwarf( + allocator: Allocator, + base_address: usize, context: *UnwindContext, ma: *std.debug.MemoryAccessor, eh_frame: []const u8, @@ -1818,7 +1849,7 @@ fn unwindFrameMachODwarf( .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. |
