std.debug.Dwarf.SelfUnwinder: skip caching rules for unsupported registers

For unwinding purposes, we don't care about unsupported registers. Yet because
we added these rules to the cache entry, we'd later try to evaluate them and
thus fail the unwind attempt for no good reason. They'd also take up cache rule
slots that would be better spent on actually relevant registers.

Note that any attempt to read unsupported registers during unwinding will still
fail the unwind attempt as expected.
This commit is contained in:
Alex Rønne Petersen 2025-10-07 09:28:43 +02:00
parent 6893e7feee
commit c23a5ccd19
No known key found for this signature in database

View file

@ -15,14 +15,14 @@ cfi_vm: Dwarf.Unwind.VirtualMachine,
expr_vm: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), expr_vm: Dwarf.expression.StackMachine(.{ .call_frame_context = true }),
pub const CacheEntry = struct { pub const CacheEntry = struct {
const max_regs = 32; const max_rules = 32;
pc: usize, pc: usize,
cie: *const Dwarf.Unwind.CommonInformationEntry, cie: *const Dwarf.Unwind.CommonInformationEntry,
cfa_rule: Dwarf.Unwind.VirtualMachine.CfaRule, cfa_rule: Dwarf.Unwind.VirtualMachine.CfaRule,
num_rules: u8, num_rules: u8,
rules_regs: [max_regs]u16, rules_regs: [max_rules]u16,
rules: [max_regs]Dwarf.Unwind.VirtualMachine.RegisterRule, rules: [max_rules]Dwarf.Unwind.VirtualMachine.RegisterRule,
pub fn find(entries: []const CacheEntry, pc: usize) ?*const CacheEntry { pub fn find(entries: []const CacheEntry, pc: usize) ?*const CacheEntry {
assert(pc != 0); assert(pc != 0);
@ -108,22 +108,30 @@ pub fn computeRules(
unwinder.cfi_vm.reset(); unwinder.cfi_vm.reset();
const row = try unwinder.cfi_vm.runTo(gpa, pc_vaddr, cie, &fde, @sizeOf(usize), native_endian); const row = try unwinder.cfi_vm.runTo(gpa, pc_vaddr, cie, &fde, @sizeOf(usize), native_endian);
const cols = unwinder.cfi_vm.rowColumns(&row);
if (cols.len > CacheEntry.max_regs) return error.UnsupportedDebugInfo;
var entry: CacheEntry = .{ var entry: CacheEntry = .{
.pc = unwinder.pc, .pc = unwinder.pc,
.cie = cie, .cie = cie,
.cfa_rule = row.cfa, .cfa_rule = row.cfa,
.num_rules = @intCast(cols.len), .num_rules = undefined,
.rules_regs = undefined, .rules_regs = undefined,
.rules = undefined, .rules = undefined,
}; };
for (cols, 0..) |col, i| { var i: usize = 0;
for (unwinder.cfi_vm.rowColumns(&row)) |col| {
if (i == CacheEntry.max_rules) return error.UnsupportedDebugInfo;
_ = unwinder.cpu_state.dwarfRegisterBytes(col.register) catch |err| switch (err) {
// Reading an unsupported register during unwinding will result in an error, so there is
// no point wasting a rule slot in the cache entry for it.
error.UnsupportedRegister => continue,
error.InvalidRegister => return error.InvalidDebugInfo,
};
entry.rules_regs[i] = col.register; entry.rules_regs[i] = col.register;
entry.rules[i] = col.rule; entry.rules[i] = col.rule;
i += 1;
} }
entry.num_rules = @intCast(i);
return entry; return entry;
} }