From 3f6a90766c7ababdaaca629cc5165c5ff5b5dcd7 Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 2 Sep 2025 13:07:42 +0100 Subject: [PATCH] sky pirates! which are even better! --- lib/std/debug/Dwarf/Unwind.zig | 89 +++++++++++++++------------------- lib/std/debug/SelfInfo.zig | 64 ++++++++++++------------ 2 files changed, 74 insertions(+), 79 deletions(-) diff --git a/lib/std/debug/Dwarf/Unwind.zig b/lib/std/debug/Dwarf/Unwind.zig index c5f1158026..09cad0db23 100644 --- a/lib/std/debug/Dwarf/Unwind.zig +++ b/lib/std/debug/Dwarf/Unwind.zig @@ -2,7 +2,7 @@ pub const VirtualMachine = @import("Unwind/VirtualMachine.zig"); -frame_section: ?struct { +frame_section: struct { id: Section, /// The virtual address of the start of the section. "Virtual address" refers to the address in /// the binary (e.g. `sh_addr` in an ELF file); the equivalent runtime address may be relocated @@ -42,10 +42,41 @@ const SortedFdeEntry = struct { const Section = enum { debug_frame, eh_frame }; // MLUGG TODO deinit? -pub const init: Unwind = .{ - .frame_section = null, - .lookup = null, -}; + +/// Initialize with unwind information from the contents of a `.debug_frame` or `.eh_frame` section. +/// +/// If the `.eh_frame_hdr` section is available, consider instead using `initEhFrameHdr`. This +/// allows the implementation to use a search table embedded in that section if it is available. +pub fn initSection(section: Section, section_vaddr: u64, section_bytes: []const u8) Unwind { + return .{ + .frame_section = .{ + .id = section, + .bytes = section_bytes, + .vaddr = section_vaddr, + }, + .lookup = null, + }; +} + +/// Initialize with unwind information from a header loaded from an `.eh_frame_hdr` section, and a +/// pointer to the contents of the `.eh_frame` section. +/// +/// This differs from `loadFromSection` because `.eh_frame_hdr` may embed a binary search table, and +/// if it does, this function will use that for address lookups instead of constructing our own +/// search table. +pub fn initEhFrameHdr(header: EhFrameHeader, section_vaddr: u64, section_bytes_ptr: [*]const u8) Unwind { + return .{ + .frame_section = .{ + .id = .eh_frame, + .bytes = maxSlice(section_bytes_ptr), + .vaddr = header.eh_frame_vaddr, + }, + .lookup = if (header.search_table) |table| .{ .eh_frame_hdr = .{ + .vaddr = section_vaddr, + .table = table, + } } else null, + }; +} /// This represents the decoded .eh_frame_hdr header pub const EhFrameHeader = struct { @@ -371,51 +402,11 @@ pub const FrameDescriptionEntry = struct { } }; -/// Load unwind information from the contents of an `.eh_frame` or `.debug_frame` section. -/// -/// If the `.eh_frame_hdr` section is available, consider instead using `loadFromEhFrameHdr`. This -/// allows the implementation to use a search table embedded in that section if it is available. -pub fn loadFromSection(unwind: *Unwind, section: Section, section_vaddr: u64, section_bytes: []const u8) void { - assert(unwind.frame_section == null); - assert(unwind.lookup == null); - unwind.frame_section = .{ - .id = section, - .bytes = section_bytes, - .vaddr = section_vaddr, - }; -} - -/// Load unwind information from a header loaded from an `.eh_frame_hdr` section, and a pointer to -/// the contents of the `.eh_frame` section. -/// -/// This differs from `loadFromSection` because `.eh_frame_hdr` may embed a binary search table, and -/// if it does, this function will use that for address lookups instead of constructing our own -/// search table. -pub fn loadFromEhFrameHdr( - unwind: *Unwind, - header: EhFrameHeader, - section_vaddr: u64, - section_bytes_ptr: [*]const u8, -) !void { - assert(unwind.frame_section == null); - assert(unwind.lookup == null); - unwind.frame_section = .{ - .id = .eh_frame, - .bytes = maxSlice(section_bytes_ptr), - .vaddr = header.eh_frame_vaddr, - }; - if (header.search_table) |table| { - unwind.lookup = .{ .eh_frame_hdr = .{ - .vaddr = section_vaddr, - .table = table, - } }; - } -} - pub fn prepareLookup(unwind: *Unwind, gpa: Allocator, addr_size_bytes: u8, endian: Endian) !void { - const section = unwind.frame_section.?; if (unwind.lookup != null) return; + const section = unwind.frame_section; + var r: Reader = .fixed(section.bytes); var fde_list: std.ArrayList(SortedFdeEntry) = .empty; defer fde_list.deinit(gpa); @@ -477,7 +468,7 @@ pub fn lookupPc(unwind: *const Unwind, pc: u64, addr_size_bytes: u8, endian: End addr_size_bytes, endian, ) orelse return null; - return std.math.sub(u64, fde_vaddr, unwind.frame_section.?.vaddr) catch bad(); // convert vaddr to offset + return std.math.sub(u64, fde_vaddr, unwind.frame_section.vaddr) catch bad(); // convert vaddr to offset }, .sorted_fdes => |sorted_fdes| sorted_fdes, }; @@ -493,7 +484,7 @@ pub fn lookupPc(unwind: *const Unwind, pc: u64, addr_size_bytes: u8, endian: End } pub fn getFde(unwind: *const Unwind, fde_offset: u64, addr_size_bytes: u8, endian: Endian) !struct { Format, CommonInformationEntry, FrameDescriptionEntry } { - const section = unwind.frame_section.?; + const section = unwind.frame_section; var fde_reader: Reader = .fixed(section.bytes[fde_offset..]); const fde_info = switch (try EntryHeader.read(&fde_reader, fde_offset, section.id, endian)) { diff --git a/lib/std/debug/SelfInfo.zig b/lib/std/debug/SelfInfo.zig index b875e74b4c..64ccb34e25 100644 --- a/lib/std/debug/SelfInfo.zig +++ b/lib/std/debug/SelfInfo.zig @@ -73,44 +73,26 @@ pub fn deinit(self: *SelfInfo) void { pub fn unwindFrame(self: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usize { comptime assert(target_supported); - const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); // MLUGG TODO: don't take gpa + const module: Module = try .lookup(&self.lookup_cache, gpa, context.pc); const gop = try self.modules.getOrPut(gpa, module.load_offset); if (!gop.found_existing) gop.value_ptr.* = .init; if (!gop.value_ptr.loaded_unwind) { try module.loadUnwindInfo(gpa, &gop.value_ptr.di); gop.value_ptr.loaded_unwind = true; } - // MLUGG TODO: the stuff below is impl! - if (native_os.isDarwin()) { - // __unwind_info is a requirement for unwinding on Darwin. It may fall back to DWARF, but unwinding - // via DWARF before attempting to use the compact unwind info will produce incorrect results. - if (gop.value_ptr.di.unwind_info) |unwind_info| { - if (unwindFrameMachO( - module.text_base, - module.load_offset, - context, - unwind_info, - gop.value_ptr.di.eh_frame, - )) |return_address| { - return return_address; - } else |err| { - if (err != error.RequiresDWARFUnwind) return err; - } - } - return error.MissingUnwindInfo; - } - return unwindFrameDwarf(&gop.value_ptr.di.unwind, module.load_offset, context, null); + return module.unwindFrame(gpa, &gop.value_ptr.di, context); } pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std.debug.Symbol { comptime assert(target_supported); - const module: Module = try .lookup(&self.lookup_cache, gpa, address); // MLUGG TODO: don't take gpa + const module: Module = try .lookup(&self.lookup_cache, gpa, address); const gop = try self.modules.getOrPut(gpa, module.key()); if (!gop.found_existing) gop.value_ptr.* = .init; if (!gop.value_ptr.loaded_debug) { // MLUGG TODO: this overloads the name 'debug info' with including vs excluding unwind info // figure out a better name for one or the other (i think the inner one is maybe 'symbol info' or something idk) try module.loadDebugInfo(gpa, &gop.value_ptr.di); + gop.value_ptr.loaded_debug = true; } return module.getSymbolAtAddress(gpa, &gop.value_ptr.di, address); } @@ -120,7 +102,7 @@ pub fn getSymbolAtAddress(self: *SelfInfo, gpa: Allocator, address: usize) !std. /// a path that doesn't rely on any side-effects of a prior successful module lookup. pub fn getModuleNameForAddress(self: *SelfInfo, gpa: Allocator, address: usize) error{ Unexpected, OutOfMemory, MissingDebugInfo }![]const u8 { comptime assert(target_supported); - const module: Module = try .lookup(&self.lookup_cache, gpa, address); // MLUGG TODO: don't take gpa + const module: Module = try .lookup(&self.lookup_cache, gpa, address); return module.name; } @@ -251,6 +233,18 @@ const Module = switch (native_os) { }, }; } + fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { + _ = gpa; + const unwind_info = di.unwind_info orelse return error.MissingUnwindInfo; + // MLUGG TODO: inline + return unwindFrameMachO( + module.text_base, + module.load_offset, + context, + unwind_info, + di.eh_frame, + ); + } const LookupCache = void; const DebugInfo = struct { mapped_memory: []align(std.heap.page_size_min) const u8, @@ -499,12 +493,16 @@ const Module = switch (native_os) { const section_bytes = module.gnu_eh_frame orelse return error.MissingUnwindInfo; // MLUGG TODO: load from file const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset; const header: Dwarf.Unwind.EhFrameHeader = try .parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian); - try di.unwind.loadFromEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); + di.unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr)); try di.unwind.prepareLookup(gpa, @sizeOf(usize), native_endian); } fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol { return di.em.getSymbolAtAddress(gpa, native_endian, module.load_offset, address); } + fn unwindFrame(module: *const Module, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) !usize { + _ = gpa; + return unwindFrameDwarf(&di.unwind, module.load_offset, context, null); + } }, .uefi, .windows => struct { base_address: usize, @@ -1507,9 +1505,12 @@ fn unwindFrameMachO( .DWARF => { const eh_frame = opt_eh_frame orelse return error.MissingEhFrame; const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset; - var dwarf_unwind: Dwarf.Unwind = .init; - dwarf_unwind.loadFromSection(.eh_frame, eh_frame_vaddr, eh_frame); - return unwindFrameDwarf(&dwarf_unwind, load_offset, context, @intCast(encoding.value.x86_64.dwarf)); + return unwindFrameDwarf( + &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), + load_offset, + context, + @intCast(encoding.value.x86_64.dwarf), + ); }, }, .aarch64, .aarch64_be => switch (encoding.mode.arm64) { @@ -1524,9 +1525,12 @@ fn unwindFrameMachO( .DWARF => { const eh_frame = opt_eh_frame orelse return error.MissingEhFrame; const eh_frame_vaddr = @intFromPtr(eh_frame.ptr) - load_offset; - var dwarf_unwind: Dwarf.Unwind = .init; - dwarf_unwind.loadFromSection(.eh_frame, eh_frame_vaddr, eh_frame); - return unwindFrameDwarf(dwarf_unwind, load_offset, context, @intCast(encoding.value.arm64.dwarf)); + return unwindFrameDwarf( + &.initSection(.eh_frame, eh_frame_vaddr, eh_frame), + load_offset, + context, + @intCast(encoding.value.x86_64.dwarf), + ); }, .FRAME => ip: { const frame = encoding.value.arm64.frame;