diff options
| author | kcbanner <kcbanner@gmail.com> | 2023-05-29 01:26:30 -0400 |
|---|---|---|
| committer | kcbanner <kcbanner@gmail.com> | 2023-07-20 22:58:13 -0400 |
| commit | 5781016c35c27a64b08c0701a92ad7c1a253a869 (patch) | |
| tree | 00d90503c3fbdedd43c77365104e95a38d9f9cab /lib/std/debug.zig | |
| parent | dd2035735fa2160f85bf8fb8cd8b978d77fac292 (diff) | |
| download | zig-5781016c35c27a64b08c0701a92ad7c1a253a869.tar.gz zig-5781016c35c27a64b08c0701a92ad7c1a253a869.zip | |
dwarf: add support for .eh_frame_hdr when unwinding
- .eh_frame_hdr contains a binary-searchable data structure for finding an FDE. If present, we can use this
section to avoid having to parse the entire FDE/CIE list in the binary, instead only entries that are actually
required for unwinding are read.
- rework the inputs pc-relative pointer decoding to support both already-mapped sections as well as sections
mapped from a file
- store the VirtualMachine on UnwindContext so the allocations can be reused
Diffstat (limited to 'lib/std/debug.zig')
| -rw-r--r-- | lib/std/debug.zig | 64 |
1 files changed, 44 insertions, 20 deletions
diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 248ace447d..d749391212 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -171,6 +171,7 @@ pub fn dumpStackTraceFromBase(context: StackTraceContext) void { } var it = StackIterator.initWithContext(null, debug_info, context) catch return; + defer it.deinit(); printSourceAtAddress(debug_info, stderr, it.dwarf_context.pc, tty_config) catch return; while (it.next()) |return_address| { @@ -219,6 +220,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT } else { // TODO: This should use the dwarf unwinder if it's available var it = StackIterator.init(first_address, null); + defer it.deinit(); for (stack_trace.instruction_addresses, 0..) |*addr, i| { addr.* = it.next() orelse { stack_trace.index = i; @@ -445,10 +447,18 @@ pub const StackIterator = struct { pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator { var iterator = init(first_address, null); iterator.debug_info = debug_info; - iterator.dwarf_context = try DW.UnwindContext.init(context); + iterator.dwarf_context = try DW.UnwindContext.init(context, &isValidMemory); return iterator; } + pub fn deinit(self: *StackIterator) void { + if (supports_context) { + if (self.debug_info) |debug_info| { + self.dwarf_context.deinit(debug_info.allocator); + } + } + } + // Offset of the saved BP wrt the frame pointer. const fp_offset = if (native_arch.isRISCV()) // On RISC-V the frame pointer points to the top of the saved register @@ -599,6 +609,8 @@ pub fn writeCurrentStackTrace( // TODO: Capture a context and use initWithContext var it = StackIterator.init(start_addr, null); + defer it.deinit(); + while (it.next()) |return_address| { // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS, // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid @@ -957,19 +969,15 @@ pub fn readElfDebugInfo( var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; - // Take ownership over any owned sections from the parent scope + // Combine section list. This takes ownership over any owned sections from the parent scope. for (parent_sections, §ions) |*parent, *section| { if (parent.*) |*p| { section.* = p.*; p.owned = false; } } - errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data); - // TODO: This function should take a ptr to GNU_EH_FRAME (which is .eh_frame_hdr) from the ELF headers - // and prefil sections[.eh_frame_hdr] - var separate_debug_filename: ?[]const u8 = null; var separate_debug_crc: ?u32 = null; @@ -992,6 +1000,7 @@ pub fn readElfDebugInfo( if (mem.eql(u8, "." ++ section.name, name)) section_index = i; } if (section_index == null) continue; + if (sections[section_index.?] != null) continue; const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size); sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: { @@ -1496,7 +1505,8 @@ pub const DebugInfo = struct { // Output base_address: usize = undefined, name: []const u8 = undefined, - build_id: ?[]const u8 = undefined, + build_id: ?[]const u8 = null, + gnu_eh_frame: ?[]const u8 = null, } = .{ .address = address }; const CtxTy = @TypeOf(ctx); @@ -1523,19 +1533,24 @@ pub const DebugInfo = struct { } } else return; - // TODO: Look for the GNU_EH_FRAME section and pass it to readElfDebugInfo - for (info.dlpi_phdr[0..info.dlpi_phnum]) |phdr| { - if (phdr.p_type != elf.PT_NOTE) continue; - - const note_bytes = @intToPtr([*]const u8, info.dlpi_addr + phdr.p_vaddr)[0..phdr.p_memsz]; - const name_size = mem.readIntSliceNative(u32, note_bytes[0..4]); - if (name_size != 4) continue; - const desc_size = mem.readIntSliceNative(u32, note_bytes[4..8]); - const note_type = mem.readIntSliceNative(u32, note_bytes[8..12]); - if (note_type != elf.NT_GNU_BUILD_ID) continue; - if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue; - context.build_id = note_bytes[16..][0..desc_size]; + switch (phdr.p_type) { + elf.PT_NOTE => { + // Look for .note.gnu.build-id + const note_bytes = @intToPtr([*]const u8, info.dlpi_addr + phdr.p_vaddr)[0..phdr.p_memsz]; + const name_size = mem.readIntSliceNative(u32, note_bytes[0..4]); + if (name_size != 4) continue; + const desc_size = mem.readIntSliceNative(u32, note_bytes[4..8]); + const note_type = mem.readIntSliceNative(u32, note_bytes[8..12]); + if (note_type != elf.NT_GNU_BUILD_ID) continue; + if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue; + context.build_id = note_bytes[16..][0..desc_size]; + }, + elf.PT_GNU_EH_FRAME => { + context.gnu_eh_frame = @intToPtr([*]const u8, info.dlpi_addr + phdr.p_vaddr)[0..phdr.p_memsz]; + }, + else => {}, + } } // Stop the iteration @@ -1555,7 +1570,16 @@ pub const DebugInfo = struct { errdefer self.allocator.destroy(obj_di); var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array; - // TODO: If GNU_EH_FRAME was found, set it in sections + if (ctx.gnu_eh_frame) |eh_frame_hdr| { + // This is a special case - pointer offsets inside .eh_frame_hdr + // are encoded relative to its base address, so we must use the + // version that is already memory mapped, and not the one that + // will be mapped separately from the ELF file. + sections[@enumToInt(DW.DwarfSection.eh_frame_hdr)] = .{ + .data = eh_frame_hdr, + .owned = false, + }; + } 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; |
