diff --git a/lib/std/debug/Dwarf/SelfUnwinder.zig b/lib/std/debug/Dwarf/SelfUnwinder.zig index 3a7b70b037..24afdaba1d 100644 --- a/lib/std/debug/Dwarf/SelfUnwinder.zig +++ b/lib/std/debug/Dwarf/SelfUnwinder.zig @@ -15,14 +15,14 @@ cfi_vm: Dwarf.Unwind.VirtualMachine, expr_vm: Dwarf.expression.StackMachine(.{ .call_frame_context = true }), pub const CacheEntry = struct { - const max_regs = 32; + const max_rules = 32; pc: usize, cie: *const Dwarf.Unwind.CommonInformationEntry, cfa_rule: Dwarf.Unwind.VirtualMachine.CfaRule, num_rules: u8, - rules_regs: [max_regs]u16, - rules: [max_regs]Dwarf.Unwind.VirtualMachine.RegisterRule, + rules_regs: [max_rules]u16, + rules: [max_rules]Dwarf.Unwind.VirtualMachine.RegisterRule, pub fn find(entries: []const CacheEntry, pc: usize) ?*const CacheEntry { assert(pc != 0); @@ -108,22 +108,30 @@ pub fn computeRules( unwinder.cfi_vm.reset(); 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 = .{ .pc = unwinder.pc, .cie = cie, .cfa_rule = row.cfa, - .num_rules = @intCast(cols.len), + .num_rules = undefined, .rules_regs = 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[i] = col.rule; + i += 1; } + entry.num_rules = @intCast(i); return entry; } diff --git a/lib/std/debug/SelfInfo/MachO.zig b/lib/std/debug/SelfInfo/MachO.zig index eb4d0854d8..a89a2f0fb5 100644 --- a/lib/std/debug/SelfInfo/MachO.zig +++ b/lib/std/debug/SelfInfo/MachO.zig @@ -401,21 +401,11 @@ fn unwindFrameInner(si: *SelfInfo, gpa: Allocator, context: *UnwindContext) !usi } } - inline for (@typeInfo(@TypeOf(frame.d_reg_pairs)).@"struct".fields, 0..) |field, i| { - if (@field(frame.d_reg_pairs, field.name) != 0) { - // Only the lower half of the 128-bit V registers are restored during unwinding - { - const dest: *align(1) usize = @ptrCast(try context.cpu_state.dwarfRegisterBytes(64 + 8 + i)); - dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; - } - reg_addr += @sizeOf(usize); - { - const dest: *align(1) usize = @ptrCast(try context.cpu_state.dwarfRegisterBytes(64 + 9 + i)); - dest.* = @as(*const usize, @ptrFromInt(reg_addr)).*; - } - reg_addr += @sizeOf(usize); - } - } + // We intentionally skip restoring `frame.d_reg_pairs`; we know we don't support + // vector registers in the AArch64 `cpu_context` anyway, so there's no reason to + // fail a legitimate unwind just because we're asked to restore the registers here. + // If some weird/broken unwind info tells us to read them later, we will fail then. + reg_addr += 16 * @as(usize, @popCount(@as(u4, @bitCast(frame.d_reg_pairs)))); const new_ip = @as(*const usize, @ptrFromInt(ip_ptr)).*; const new_fp = @as(*const usize, @ptrFromInt(fp)).*;