From 1436d488b9d64af6d7087c0070d18df3d23604c5 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Fri, 4 Sep 2026 10:40:22 +0100 Subject: [PATCH] std.debug.Dwarf: basic support for DW_LNE_ZIG_set_decl This makes stack traces with `-fincremental`, um, sort of work: ``` thread 29999 panic: this is the RIGHT panic /home/mlugg/panic.zig:15:25: 0x7a1907 in bar (panic.zig) 1, 2, 3, 123 => @panic("this is the RIGHT panic"), ^ /home/mlugg/panic.zig:7:12: 0x7a138c in foo (panic.zig) bar(123); ^ /home/mlugg/panic.zig:2:8: 0x7a12fe in main (panic.zig) foo(true); ^ /home/mlugg/zig/master/lib/std/start.zig:779:64: 0x7a0e3b in callMain (std.zig) ^ /home/mlugg/zig/master/lib/std/Target.zig:1651:5: 0x7a0861 in _start (std.zig) pub inline fn isSparc(arch: Arch) bool { ^ Aborted ./panic ``` The last two frames are clearly incorrect. However, we actually agree with lldb-zig about those source locations, so it seems like that's a bug in the compiler's `link.Dwarf2` implementation. --- lib/std/debug/Dwarf.zig | 87 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 3bbad635ca9f613f15dbfe2fa68996de39526b8f..0bd643c7eefab1c20858a44127dcb9e11e224400 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -114,12 +114,14 @@ pub const Abbrev = struct { }; pub const CompileUnit = struct { + offset: u64, + size: u64, version: u16, format: Format, addr_size_bytes: u8, + abbrev_offset: u64, die: Die, pc_range: ?PcRange, - str_offsets_base: usize, addr_base: usize, rnglists_base: usize, @@ -246,14 +248,6 @@ pub const Die = struct { return form_value.getUInt(u64); } - fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - .Const => |value| value.asUnsignedLe(), - else => bad(), - }; - } - fn getAttrRef(self: *const Die, id: u64, unit_offset: u64, unit_len: u64) !u64 { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { @@ -425,12 +419,14 @@ fn scanAllFunctions(di: *Dwarf, gpa: Allocator, endian: Endian) ScanError!void { const next_unit_pos = this_unit_offset + next_offset; var compile_unit: CompileUnit = .{ + .offset = this_unit_offset, + .size = next_offset, .version = version, .format = unit_header.format, .addr_size_bytes = address_size, + .abbrev_offset = debug_abbrev_offset, .die = undefined, .pc_range = null, - .str_offsets_base = 0, .addr_base = 0, .rnglists_base = 0, @@ -624,9 +620,12 @@ fn scanAllCompileUnits(di: *Dwarf, gpa: Allocator, endian: Endian) ScanError!voi compile_unit_die.attrs = try gpa.dupe(Die.Attr, compile_unit_die.attrs); var compile_unit: CompileUnit = .{ + .offset = this_unit_offset, + .size = next_offset, .version = version, .format = unit_header.format, .addr_size_bytes = address_size, + .abbrev_offset = debug_abbrev_offset, .pc_range = null, .die = compile_unit_die, .str_offsets_base = if (compile_unit_die.getAttr(AT.str_offsets_base)) |fv| try fv.getUInt(usize) else 0, @@ -1097,6 +1096,16 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: } } + const abbrev_table = try d.getAbbrevTable(gpa, compile_unit.abbrev_offset); + const attrs_buf = try gpa.alloc(Die.Attr, max_attrs: { + var max_attrs: usize = 0; + for (abbrev_table.abbrevs) |abbrev| { + max_attrs = @max(max_attrs, abbrev.attrs.len); + } + break :max_attrs max_attrs; + }); + defer gpa.free(attrs_buf); + var prog = LineNumberProgram.init(default_is_stmt, version); var line_table: CompileUnit.SrcLocCache.LineTable = .{}; errdefer line_table.deinit(gpa); @@ -1144,6 +1153,64 @@ fn runLineNumberProgram(d: *Dwarf, gpa: Allocator, endian: Endian, compile_unit: .size = size, }); }, + DW.LNE.ZIG_set_decl => { + const decl_die_offset = try readFormatSizedInt(&fr, unit_header.format, endian); + var di_fr: Reader = .fixed(d.section(.debug_info) orelse continue); + di_fr.seek = @intCast(decl_die_offset); + var die = (try parseDie( + &di_fr, + attrs_buf, + abbrev_table, + unit_header.format, + endian, + addr_size_bytes, + compile_unit.version, + )) orelse continue; + if (die.getAttr(AT.low_pc)) |_| { + prog.address = try die.getAttrAddr(d, endian, AT.low_pc, compile_unit); + } + if (die.getAttr(AT.decl_line)) |decl_line| { + prog.line = try decl_line.getUInt(i64); + } + if (die.getAttr(AT.decl_column)) |decl_column| { + prog.column = try decl_column.getUInt(u64); + } + while (die.getAttr(AT.decl_file) == null) { + if (die.getAttr(AT.abstract_origin)) |_| { + di_fr.seek = @intCast(try die.getAttrRef( + AT.abstract_origin, + compile_unit.offset, + compile_unit.size, + )); + } else if (die.getAttr(AT.specification)) |_| { + di_fr.seek = @intCast(try die.getAttrRef( + AT.specification, + compile_unit.offset, + compile_unit.size, + )); + } else if (die.getAttr(AT.ZIG_parent)) |_| { + di_fr.seek = @intCast(try die.getAttrRef( + AT.ZIG_parent, + compile_unit.offset, + compile_unit.size, + )); + } else { + // no parent, so we can't find DW_AT_decl_file + break; + } + die = (try parseDie( + &di_fr, + attrs_buf, + abbrev_table, + unit_header.format, + endian, + addr_size_bytes, + compile_unit.version, + )) orelse break; + } else { + prog.file = try die.getAttr(AT.decl_file).?.getUInt(usize); + } + }, else => try fr.discardAll64(op_size - 1), } } else if (opcode >= opcode_base) { -- 2.54.0