diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 7aee8e0aa3..298f1f7ec4 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1740,21 +1740,7 @@ pub const DwarfInfo = struct { context.reg_context.eh_frame = cie.version != 4; context.reg_context.is_macho = di.is_macho; - if (comptime builtin.target.isDarwin()) { - std.debug.print(" state before:\n", .{}); - std.debug.print(" cfa {?x}:\n", .{context.cfa}); - for (context.thread_context.mcontext.ss.regs, 0..) |reg, i| { - std.debug.print(" {}:0x{x}\n", .{i, reg}); - } - std.debug.print(" fp:0x{x}\n", .{context.thread_context.mcontext.ss.fp}); - std.debug.print(" lr:0x{x}\n", .{context.thread_context.mcontext.ss.lr}); - std.debug.print(" sp:0x{x}\n", .{context.thread_context.mcontext.ss.sp}); - std.debug.print(" pc:0x{x}\n", .{context.thread_context.mcontext.ss.pc}); - } - const row = try context.vm.runToNative(context.allocator, context.pc, cie, fde); - std.debug.print(" ran to 0x{x}\n", .{row.offset + fde.pc_begin}); - context.cfa = switch (row.cfa.rule) { .val_offset => |offset| blk: { const register = row.cfa.register orelse return error.InvalidCFARule; @@ -1789,47 +1775,48 @@ pub const DwarfInfo = struct { const RegisterUpdate = struct { // Backed by thread_context - old_value: []u8, + dest: []u8, // Backed by arena - new_value: []const u8, + src: []const u8, prev: ?*@This(), }; var update_tail: ?*RegisterUpdate = null; - var has_next_ip = true; + var has_return_address= true; for (context.vm.rowColumns(row)) |column| { if (column.register) |register| { if (register == cie.return_address_register) { - has_next_ip = column.rule != .undefined; + has_return_address = column.rule != .undefined; } - std.debug.print(" updated {}\n", .{register}); - const old_value = try abi.regBytes(context.thread_context, register, context.reg_context); - const new_value = try update_allocator.alloc(u8, old_value.len); + const dest = try abi.regBytes(context.thread_context, register, context.reg_context); + const src = try update_allocator.alloc(u8, dest.len); const prev = update_tail; update_tail = try update_allocator.create(RegisterUpdate); update_tail.?.* = .{ - .old_value = old_value, - .new_value = new_value, + .dest = dest, + .src = src, .prev = prev, }; try column.resolveValue( context, expression_context, - new_value, + src, ); } } + // On all implemented architectures, the CFA is defined as being the previous frame's SP (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?; + while (update_tail) |tail| { - @memcpy(tail.old_value, tail.new_value); + @memcpy(tail.dest, tail.src); update_tail = tail.prev; } - if (has_next_ip) { + if (has_return_address) { context.pc = abi.stripInstructionPtrAuthCode(mem.readIntSliceNative(usize, try abi.regBytes( context.thread_context, cie.return_address_register, @@ -1838,10 +1825,8 @@ pub const DwarfInfo = struct { } else { context.pc = 0; } - (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc; - std.debug.print(" new context.pc: 0x{x}\n", .{context.pc}); - (try abi.regValueNative(usize, context.thread_context, abi.spRegNum(context.reg_context), context.reg_context)).* = context.cfa.?; + (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc; // The call instruction will have pushed the address of the instruction that follows the call as the return address // However, this return address may be past the end of the function if the caller was `noreturn`. By subtracting one, diff --git a/lib/std/dwarf/abi.zig b/lib/std/dwarf/abi.zig index d56ae2733d..7f349d97ad 100644 --- a/lib/std/dwarf/abi.zig +++ b/lib/std/dwarf/abi.zig @@ -367,12 +367,21 @@ pub fn regBytes( } /// Returns the ABI-defined default value this register has in the unwinding table -/// before running any of the CIE instructions. The DWARF spec defines these values -/// to be undefined, but allows ABI authors to override that default. -pub fn getRegDefaultValue(reg_number: u8, out: []u8) void { +/// before running any of the CIE instructions. The DWARF spec defines these as having +/// the .undefined rule by default, but allows ABI authors to override that. +pub fn getRegDefaultValue(reg_number: u8, context: *std.dwarf.UnwindContext, out: []u8) !void { + switch (builtin.cpu.arch) { + .aarch64 => { + // Callee-saved registers are initialized as if they had the .same_value rule + if (reg_number >= 19 and reg_number <= 28) { + const src = try regBytes(context.thread_context, reg_number, context.reg_context); + if (src.len != out.len) return error.RegisterSizeMismatch; + @memcpy(out, src); + return; + } + }, + else => {}, + } - // Implement any ABI-specific rules here - - _ = reg_number; @memset(out, undefined); } diff --git a/lib/std/dwarf/call_frame.zig b/lib/std/dwarf/call_frame.zig index 1243673da6..c83cbad815 100644 --- a/lib/std/dwarf/call_frame.zig +++ b/lib/std/dwarf/call_frame.zig @@ -295,12 +295,18 @@ pub const VirtualMachine = struct { switch (self.rule) { .default => { const register = self.register orelse return error.InvalidRegister; - abi.getRegDefaultValue(register, out); + try abi.getRegDefaultValue(register, context, out); }, .undefined => { @memset(out, undefined); }, - .same_value => {}, + .same_value => { + // TODO: This copy could be eliminated if callers always copy the state then call this function to update it + const register = self.register orelse return error.InvalidRegister; + const src = try abi.regBytes(context.thread_context, register, context.reg_context); + if (src.len != out.len) return error.RegisterSizeMismatch; + @memcpy(out, src); + }, .offset => |offset| { if (context.cfa) |cfa| { const addr = try applyOffset(cfa, offset); @@ -316,7 +322,7 @@ pub const VirtualMachine = struct { }, .register => |register| { const src = try abi.regBytes(context.thread_context, register, context.reg_context); - if (src.len != out.len) return error.RegisterTypeMismatch; + if (src.len != out.len) return error.RegisterSizeMismatch; @memcpy(out, try abi.regBytes(context.thread_context, register, context.reg_context)); }, .expression => |expression| {