authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-16 12:58:25+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-07-17 15:56:36+02:00
log8f7fd5c7f363315f0f014cb9f1b8edac264bfb14
tree0fc27c709dc223e660e288ee1d23ea970f24bfb6
parentf1d454d535c96a1b1eee74a026a231af106448fb

std.debug.SelfInfo.Elf: fall back to symtab search more often

Symtab search is always a reasonable fallback if debug information is absent, invalid, or unsupported. Therefore, by changing the error handling here to get symbols from the symtab even if `std.debug.Dwarf` returns an error, we make stack traces slightly better in some cases. This affects the self-hosted `Elf2` linker: that linker does not yet understand DWARF sections, so it ends up emitting incomplete (or sometimes invalid) DWARF. However, it still has a valid symtab, so we should at least be able to see function symbol names in the stack trace! Before this patch, we just saw "???" for everything, but after this patch, we do indeed get the symbol names.

1 files changed, 36 insertions(+), 13 deletions(-)

lib/std/debug/SelfInfo/Elf.zig+36-13
......@@ -46,20 +46,30 @@ pub fn getSymbols(
4646 const vaddr = address - module.load_offset;
4747
4848 const loaded_elf = try module.getLoadedElf(gpa, io);
49 if (loaded_elf.file.dwarf) |*dwarf| {
50 if (!loaded_elf.scanned_dwarf) {
51 dwarf.open(gpa, native_endian) catch |err| switch (err) {
49 const dwarf_err: ?Error = err: {
50 const dwarf = &(loaded_elf.file.dwarf orelse break :err null);
51 switch (loaded_elf.dwarf) {
52 .not_scanned => if (dwarf.open(gpa, native_endian)) {
53 loaded_elf.dwarf = .ok;
54 } else |err| switch (err) {
5255 error.InvalidDebugInfo,
53 error.MissingDebugInfo,
54 error.OutOfMemory,
55 => |e| return e,
5656 error.EndOfStream,
5757 error.Overflow,
5858 error.ReadFailed,
5959 error.StreamTooLong,
60 => return error.InvalidDebugInfo,
61 };
62 loaded_elf.scanned_dwarf = true;
60 => {
61 loaded_elf.dwarf = .invalid;
62 break :err error.InvalidDebugInfo;
63 },
64 error.MissingDebugInfo => {
65 loaded_elf.dwarf = .missing;
66 break :err error.MissingDebugInfo;
67 },
68 error.OutOfMemory => |e| return e,
69 },
70 .invalid => break :err error.InvalidDebugInfo,
71 .missing => break :err error.MissingDebugInfo,
72 .ok => {},
6373 }
6474 return dwarf.getSymbols(
6575 symbol_allocator,
......@@ -68,14 +78,27 @@ pub fn getSymbols(
6878 vaddr,
6979 resolve_inline_callers,
7080 symbols,
71 );
72 }
81 ) catch |err| switch (err) {
82 error.InvalidDebugInfo,
83 error.MissingDebugInfo,
84 error.UnsupportedDebugInfo,
85 => |e| break :err e,
86
87 error.ReadFailed,
88 error.OutOfMemory,
89 error.Canceled,
90 error.Unexpected,
91 => |e| return e,
92 };
93 };
7394 // When DWARF is unavailable, fall back to searching the symtab.
7495 try symbols.append(symbol_allocator, loaded_elf.file.searchSymtab(gpa, vaddr) catch |err| switch (err) {
7596 error.NoSymtab, error.NoStrtab => return error.MissingDebugInfo,
7697 error.BadSymtab => return error.InvalidDebugInfo,
7798 error.OutOfMemory => |e| return e,
7899 });
100 // After searching the symtab, still report the DWARF error.
101 if (dwarf_err) |e| return e;
79102}
80103pub fn getModuleName(si: *SelfInfo, io: Io, address: usize) Error![]const u8 {
81104 const gpa = std.debug.getDebugInfoAllocator();
......@@ -251,7 +274,7 @@ const Module = struct {
251274
252275 const LoadedElf = struct {
253276 file: std.debug.ElfFile,
254 scanned_dwarf: bool,
277 dwarf: enum { not_scanned, invalid, missing, ok },
255278 };
256279
257280 const UnwindSections = struct {
......@@ -377,7 +400,7 @@ const Module = struct {
377400
378401 return .{
379402 .file = elf_file,
380 .scanned_dwarf = false,
403 .dwarf = .not_scanned,
381404 };
382405 }
383406};