Compute the rule set for the address unwinder.pc from the information in unwind. The caller
may store the returned rule set in a simple fixed-size cache keyed on the pc field to avoid
frequently recomputing register rules when unwinding many times.
To actually apply the computed rules, see next.
pub fn computeRules(
unwinder: *SelfUnwinder,
gpa: Allocator,
unwind: *const Dwarf.Unwind,
load_offset: usize,
explicit_fde_offset: ?usize,
) !CacheEntry
pub fn computeRules(
unwinder: *SelfUnwinder,
gpa: Allocator,
unwind: *const Dwarf.Unwind,
load_offset: usize,
explicit_fde_offset: ?usize,
) !CacheEntry {
assert(unwinder.pc != 0);
const pc_vaddr = unwinder.pc - load_offset;
const fde_offset = explicit_fde_offset orelse try unwind.lookupPc(
pc_vaddr,
@sizeOf(usize),
native_endian,
) orelse return error.MissingDebugInfo;
const cie, const fde = try unwind.getFde(fde_offset, native_endian);
// `lookupPc` can return false positives, so check if the FDE *actually* includes the pc
if (pc_vaddr < fde.pc_begin or pc_vaddr >= fde.pc_begin + fde.pc_range) {
return error.MissingDebugInfo;
}
unwinder.cfi_vm.reset();
const row = try unwinder.cfi_vm.runTo(gpa, pc_vaddr, cie, &fde, @sizeOf(usize), native_endian);
var entry: CacheEntry = .{
.pc = unwinder.pc,
.cie = cie,
.cfa_rule = row.cfa,
.num_rules = undefined,
.rules_regs = undefined,
.rules = undefined,
};
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[i] = col.rule;
i += 1;
}
entry.num_rules = @intCast(i);
return entry;
}