authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-11-08 11:12:40+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-11-12 21:02:38+00:00
log92bc619c49fb4b7281bdc1c3c0bc3e41a9d20d4d
tree01de65c840b64ee0958afe44daa0df48061ef4cc
parent49e19fc94fe70cecc1cba21a04c1f2f1c1e4deb8

std.debug: allow fp unwind from context

It's easy to do FP unwinding from a CPU context: you just report the captured ip/pc value first, and then unwind from the captured fp value. All this really needed was a couple of new functions on the `std.debug.cpu_context` implementations so that we don't need to rely on `std.debug.Dwarf` to access the captured registers. Resolves: #25576

3 files changed, 169 insertions(+), 41 deletions(-)

lib/std/debug.zig+30-25
...@@ -617,7 +617,7 @@ pub const StackUnwindOptions = struct {...@@ -617,7 +617,7 @@ pub const StackUnwindOptions = struct {
617pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {617pub noinline fn captureCurrentStackTrace(options: StackUnwindOptions, addr_buf: []usize) StackTrace {
618 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };618 const empty_trace: StackTrace = .{ .index = 0, .instruction_addresses = &.{} };
619 if (!std.options.allow_stack_tracing) return empty_trace;619 if (!std.options.allow_stack_tracing) return empty_trace;
620 var it = StackIterator.init(options.context) catch return empty_trace;620 var it: StackIterator = .init(options.context);
621 defer it.deinit();621 defer it.deinit();
622 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;622 if (!it.stratOk(options.allow_unsafe_unwind)) return empty_trace;
623 var total_frames: usize = 0;623 var total_frames: usize = 0;
...@@ -671,14 +671,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri...@@ -671,14 +671,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri
671 return;671 return;
672 },672 },
673 };673 };
674 var it = StackIterator.init(options.context) catch |err| switch (err) {674 var it: StackIterator = .init(options.context);
675 error.CannotUnwindFromContext => {
676 tty_config.setColor(writer, .dim) catch {};
677 try writer.print("Cannot print stack trace: context unwind unavailable for target\n", .{});
678 tty_config.setColor(writer, .reset) catch {};
679 return;
680 },
681 };
682 defer it.deinit();675 defer it.deinit();
683 if (!it.stratOk(options.allow_unsafe_unwind)) {676 if (!it.stratOk(options.allow_unsafe_unwind)) {
684 tty_config.setColor(writer, .dim) catch {};677 tty_config.setColor(writer, .dim) catch {};
...@@ -821,22 +814,32 @@ pub fn dumpStackTrace(st: *const StackTrace) void {...@@ -821,22 +814,32 @@ pub fn dumpStackTrace(st: *const StackTrace) void {
821}814}
822815
823const StackIterator = union(enum) {816const StackIterator = union(enum) {
817 /// We will first report the current PC of this `CpuContextPtr`, then we will switch to a
818 /// different strategy to actually unwind.
819 ctx_first: CpuContextPtr,
824 /// Unwinding using debug info (e.g. DWARF CFI).820 /// Unwinding using debug info (e.g. DWARF CFI).
825 di: if (SelfInfo != void and SelfInfo.can_unwind) SelfInfo.UnwindContext else noreturn,821 di: if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal)
826 /// We will first report the *current* PC of this `UnwindContext`, then we will switch to `di`.822 SelfInfo.UnwindContext
827 di_first: if (SelfInfo != void and SelfInfo.can_unwind) SelfInfo.UnwindContext else noreturn,823 else
824 noreturn,
828 /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable.825 /// Naive frame-pointer-based unwinding. Very simple, but typically unreliable.
829 fp: usize,826 fp: usize,
830827
831 /// It is important that this function is marked `inline` so that it can safely use828 /// It is important that this function is marked `inline` so that it can safely use
832 /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and829 /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and
833 /// our own are one and the same.830 /// our own are one and the same.
834 inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator {831 ///
832 /// `opt_context_ptr` must remain valid while the `StackIterator` is used.
833 inline fn init(opt_context_ptr: ?CpuContextPtr) StackIterator {
835 if (opt_context_ptr) |context_ptr| {834 if (opt_context_ptr) |context_ptr| {
836 if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext;835 // Use `ctx_first` here so we report the PC in the context before unwinding any further.
837 // Use `di_first` here so we report the PC in the context before unwinding any further.836 return .{ .ctx_first = context_ptr };
838 return .{ .di_first = .init(context_ptr) };
839 }837 }
838
839 // Otherwise, we're going to capture the current context or frame address, so we don't need
840 // `ctx_first`, because the first PC is in `std.debug` and we need to unwind before reaching
841 // a frame we want to report.
842
840 // Workaround the C backend being unable to use inline assembly on MSVC by disabling the843 // Workaround the C backend being unable to use inline assembly on MSVC by disabling the
841 // call to `current`. This effectively constrains stack trace collection and dumping to FP844 // call to `current`. This effectively constrains stack trace collection and dumping to FP
842 // unwinding when building with CBE for MSVC.845 // unwinding when building with CBE for MSVC.
...@@ -846,8 +849,6 @@ const StackIterator = union(enum) {...@@ -846,8 +849,6 @@ const StackIterator = union(enum) {
846 cpu_context.Native != noreturn and849 cpu_context.Native != noreturn and
847 fp_usability != .ideal)850 fp_usability != .ideal)
848 {851 {
849 // We don't need `di_first` here, because our PC is in `std.debug`; we're only interested
850 // in our caller's frame and above.
851 return .{ .di = .init(&.current()) };852 return .{ .di = .init(&.current()) };
852 }853 }
853 return .{854 return .{
...@@ -866,8 +867,9 @@ const StackIterator = union(enum) {...@@ -866,8 +867,9 @@ const StackIterator = union(enum) {
866 }867 }
867 fn deinit(si: *StackIterator) void {868 fn deinit(si: *StackIterator) void {
868 switch (si.*) {869 switch (si.*) {
870 .ctx_first => {},
869 .fp => {},871 .fp => {},
870 .di, .di_first => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()),872 .di => |*unwind_context| unwind_context.deinit(getDebugInfoAllocator()),
871 }873 }
872 }874 }
873875
...@@ -931,7 +933,7 @@ const StackIterator = union(enum) {...@@ -931,7 +933,7 @@ const StackIterator = union(enum) {
931 /// Whether the current unwind strategy is allowed given `allow_unsafe`.933 /// Whether the current unwind strategy is allowed given `allow_unsafe`.
932 fn stratOk(it: *const StackIterator, allow_unsafe: bool) bool {934 fn stratOk(it: *const StackIterator, allow_unsafe: bool) bool {
933 return switch (it.*) {935 return switch (it.*) {
934 .di, .di_first => true,936 .ctx_first, .di => true,
935 // If we omitted frame pointers from *this* compilation, FP unwinding would crash937 // If we omitted frame pointers from *this* compilation, FP unwinding would crash
936 // immediately regardless of anything. But FPs could also be omitted from a different938 // immediately regardless of anything. But FPs could also be omitted from a different
937 // linked object, so it's not guaranteed to be safe, unless the target specifically939 // linked object, so it's not guaranteed to be safe, unless the target specifically
...@@ -959,13 +961,16 @@ const StackIterator = union(enum) {...@@ -959,13 +961,16 @@ const StackIterator = union(enum) {
959961
960 fn next(it: *StackIterator) Result {962 fn next(it: *StackIterator) Result {
961 switch (it.*) {963 switch (it.*) {
962 .di_first => |unwind_context| {964 .ctx_first => |context_ptr| {
963 const first_pc = unwind_context.pc;965 // After the first frame, start actually unwinding.
964 if (first_pc == 0) return .end;966 it.* = if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal)
965 it.* = .{ .di = unwind_context };967 .{ .di = .init(context_ptr) }
968 else
969 .{ .fp = context_ptr.getFp() };
970
966 // The caller expects *return* addresses, where they will subtract 1 to find the address of the call.971 // The caller expects *return* addresses, where they will subtract 1 to find the address of the call.
967 // However, we have the actual current PC, which should not be adjusted. Compensate by adding 1.972 // However, we have the actual current PC, which should not be adjusted. Compensate by adding 1.
968 return .{ .frame = first_pc +| 1 };973 return .{ .frame = context_ptr.getPc() +| 1 };
969 },974 },
970 .di => |*unwind_context| {975 .di => |*unwind_context| {
971 const di = getSelfDebugInfo() catch unreachable;976 const di = getSelfDebugInfo() catch unreachable;
lib/std/debug/Dwarf/SelfUnwinder.zig+2-16
...@@ -47,16 +47,9 @@ pub const CacheEntry = struct {...@@ -47,16 +47,9 @@ pub const CacheEntry = struct {
47};47};
4848
49pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder {49pub fn init(cpu_context: *const std.debug.cpu_context.Native) SelfUnwinder {
50 // `@constCast` is safe because we aren't going to store to the resulting pointer.
51 const raw_pc_ptr = regNative(@constCast(cpu_context), ip_reg_num) catch |err| switch (err) {
52 error.InvalidRegister => unreachable, // `ip_reg_num` is definitely valid
53 error.UnsupportedRegister => unreachable, // the implementation needs to support ip
54 error.IncompatibleRegisterSize => unreachable, // ip is definitely `usize`-sized
55 };
56 const pc = stripInstructionPtrAuthCode(raw_pc_ptr.*);
57 return .{50 return .{
58 .cpu_state = cpu_context.*,51 .cpu_state = cpu_context.*,
59 .pc = pc,52 .pc = stripInstructionPtrAuthCode(cpu_context.getPc()),
60 .cfi_vm = .{},53 .cfi_vm = .{},
61 .expr_vm = .{},54 .expr_vm = .{},
62 };55 };
...@@ -69,13 +62,7 @@ pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void {...@@ -69,13 +62,7 @@ pub fn deinit(unwinder: *SelfUnwinder, gpa: Allocator) void {
69}62}
7063
71pub fn getFp(unwinder: *const SelfUnwinder) usize {64pub fn getFp(unwinder: *const SelfUnwinder) usize {
72 // `@constCast` is safe because we aren't going to store to the resulting pointer.65 return unwinder.cpu_state.getFp();
73 const ptr = regNative(@constCast(&unwinder.cpu_state), fp_reg_num) catch |err| switch (err) {
74 error.InvalidRegister => unreachable, // `fp_reg_num` is definitely valid
75 error.UnsupportedRegister => unreachable, // the implementation needs to support fp
76 error.IncompatibleRegisterSize => unreachable, // fp is a pointer so is `usize`-sized
77 };
78 return ptr.*;
79}66}
8067
81/// Compute the rule set for the address `unwinder.pc` from the information in `unwind`. The caller68/// Compute the rule set for the address `unwinder.pc` from the information in `unwind`. The caller
...@@ -332,7 +319,6 @@ fn applyOffset(base: usize, offset: i64) !usize {...@@ -332,7 +319,6 @@ fn applyOffset(base: usize, offset: i64) !usize {
332}319}
333320
334const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?;321const ip_reg_num = Dwarf.ipRegNum(builtin.target.cpu.arch).?;
335const fp_reg_num = Dwarf.fpRegNum(builtin.target.cpu.arch);
336const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch);322const sp_reg_num = Dwarf.spRegNum(builtin.target.cpu.arch);
337323
338const std = @import("std");324const std = @import("std");
lib/std/debug/cpu_context.zig+137
...@@ -250,6 +250,13 @@ const Aarch64 = extern struct {...@@ -250,6 +250,13 @@ const Aarch64 = extern struct {
250 return ctx;250 return ctx;
251 }251 }
252252
253 pub fn getFp(ctx: *const Aarch64) u64 {
254 return ctx.x[29];
255 }
256 pub fn getPc(ctx: *const Aarch64) u64 {
257 return ctx.pc;
258 }
259
253 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {260 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
254 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"261 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
255 switch (register_num) {262 switch (register_num) {
...@@ -324,6 +331,13 @@ const Arc = extern struct {...@@ -324,6 +331,13 @@ const Arc = extern struct {
324 return ctx;331 return ctx;
325 }332 }
326333
334 pub fn getFp(ctx: *const Arc) u32 {
335 return ctx.r[27];
336 }
337 pub fn getPc(ctx: *const Arc) u32 {
338 return ctx.pcl;
339 }
340
327 pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 {341 pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 {
328 switch (register_num) {342 switch (register_num) {
329 0...31 => return @ptrCast(&ctx.r[register_num]),343 0...31 => return @ptrCast(&ctx.r[register_num]),
...@@ -356,6 +370,13 @@ const Arm = struct {...@@ -356,6 +370,13 @@ const Arm = struct {
356 return ctx;370 return ctx;
357 }371 }
358372
373 pub fn getFp(ctx: *const Arm) u32 {
374 return ctx.r[11];
375 }
376 pub fn getPc(ctx: *const Arm) u32 {
377 return ctx.r[15];
378 }
379
359 pub fn dwarfRegisterBytes(ctx: *Arm, register_num: u16) DwarfRegisterError![]u8 {380 pub fn dwarfRegisterBytes(ctx: *Arm, register_num: u16) DwarfRegisterError![]u8 {
360 // DWARF for the Arm(r) Architecture § 4.1 "DWARF register names"381 // DWARF for the Arm(r) Architecture § 4.1 "DWARF register names"
361 switch (register_num) {382 switch (register_num) {
...@@ -415,6 +436,13 @@ const Csky = extern struct {...@@ -415,6 +436,13 @@ const Csky = extern struct {
415 return ctx;436 return ctx;
416 }437 }
417438
439 pub fn getFp(ctx: *const Csky) u32 {
440 return ctx.r[14];
441 }
442 pub fn getPc(ctx: *const Csky) u32 {
443 return ctx.pc;
444 }
445
418 pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 {446 pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 {
419 switch (register_num) {447 switch (register_num) {
420 0...31 => return @ptrCast(&ctx.r[register_num]),448 0...31 => return @ptrCast(&ctx.r[register_num]),
...@@ -476,6 +504,13 @@ const Hexagon = extern struct {...@@ -476,6 +504,13 @@ const Hexagon = extern struct {
476 return ctx;504 return ctx;
477 }505 }
478506
507 pub fn getFp(ctx: *const Hexagon) u32 {
508 return ctx.r[30];
509 }
510 pub fn getPc(ctx: *const Hexagon) u32 {
511 return ctx.pc;
512 }
513
479 pub fn dwarfRegisterBytes(ctx: *Hexagon, register_num: u16) DwarfRegisterError![]u8 {514 pub fn dwarfRegisterBytes(ctx: *Hexagon, register_num: u16) DwarfRegisterError![]u8 {
480 // Sourced from LLVM's HexagonRegisterInfo.td, which disagrees with LLDB...515 // Sourced from LLVM's HexagonRegisterInfo.td, which disagrees with LLDB...
481 switch (register_num) {516 switch (register_num) {
...@@ -544,6 +579,13 @@ const Kvx = extern struct {...@@ -544,6 +579,13 @@ const Kvx = extern struct {
544 return ctx;579 return ctx;
545 }580 }
546581
582 pub fn getFp(ctx: *const Kvx) u64 {
583 return ctx.r[14];
584 }
585 pub fn getPc(ctx: *const Kvx) u64 {
586 return ctx.pc;
587 }
588
547 pub fn dwarfRegisterBytes(ctx: *Kvx, register_num: u16) DwarfRegisterError![]u8 {589 pub fn dwarfRegisterBytes(ctx: *Kvx, register_num: u16) DwarfRegisterError![]u8 {
548 switch (register_num) {590 switch (register_num) {
549 0...63 => return @ptrCast(&ctx.r[register_num]),591 0...63 => return @ptrCast(&ctx.r[register_num]),
...@@ -604,6 +646,13 @@ const Lanai = extern struct {...@@ -604,6 +646,13 @@ const Lanai = extern struct {
604 return ctx;646 return ctx;
605 }647 }
606648
649 pub fn getFp(ctx: *const Lanai) u32 {
650 return ctx.r[5];
651 }
652 pub fn getPc(ctx: *const Lanai) u32 {
653 return ctx.r[2];
654 }
655
607 pub fn dwarfRegisterBytes(ctx: *Lanai, register_num: u16) DwarfRegisterError![]u8 {656 pub fn dwarfRegisterBytes(ctx: *Lanai, register_num: u16) DwarfRegisterError![]u8 {
608 switch (register_num) {657 switch (register_num) {
609 0...31 => return @ptrCast(&ctx.s[register_num]),658 0...31 => return @ptrCast(&ctx.s[register_num]),
...@@ -701,6 +750,13 @@ const LoongArch = extern struct {...@@ -701,6 +750,13 @@ const LoongArch = extern struct {
701 return ctx;750 return ctx;
702 }751 }
703752
753 pub fn getFp(ctx: *const LoongArch) Gpr {
754 return ctx.r[22];
755 }
756 pub fn getPc(ctx: *const LoongArch) Gpr {
757 return ctx.pc;
758 }
759
704 pub fn dwarfRegisterBytes(ctx: *LoongArch, register_num: u16) DwarfRegisterError![]u8 {760 pub fn dwarfRegisterBytes(ctx: *LoongArch, register_num: u16) DwarfRegisterError![]u8 {
705 switch (register_num) {761 switch (register_num) {
706 0...31 => return @ptrCast(&ctx.r[register_num]),762 0...31 => return @ptrCast(&ctx.r[register_num]),
...@@ -733,6 +789,13 @@ const M68k = extern struct {...@@ -733,6 +789,13 @@ const M68k = extern struct {
733 return ctx;789 return ctx;
734 }790 }
735791
792 pub fn getFp(ctx: *const M68k) u32 {
793 return ctx.a[6];
794 }
795 pub fn getPc(ctx: *const M68k) u32 {
796 return ctx.pc;
797 }
798
736 pub fn dwarfRegisterBytes(ctx: *M68k, register_num: u16) DwarfRegisterError![]u8 {799 pub fn dwarfRegisterBytes(ctx: *M68k, register_num: u16) DwarfRegisterError![]u8 {
737 switch (register_num) {800 switch (register_num) {
738 0...7 => return @ptrCast(&ctx.d[register_num]),801 0...7 => return @ptrCast(&ctx.d[register_num]),
...@@ -845,6 +908,15 @@ const Mips = extern struct {...@@ -845,6 +908,15 @@ const Mips = extern struct {
845 return ctx;908 return ctx;
846 }909 }
847910
911 pub fn getFp(ctx: *const Mips) usize {
912 // On N32, `Gpr` is 64 bits but `usize` is 32 bits.
913 return @intCast(ctx.r[30]);
914 }
915 pub fn getPc(ctx: *const Mips) usize {
916 // On N32, `Gpr` is 64 bits but `usize` is 32 bits.
917 return @intCast(ctx.pc);
918 }
919
848 pub fn dwarfRegisterBytes(ctx: *Mips, register_num: u16) DwarfRegisterError![]u8 {920 pub fn dwarfRegisterBytes(ctx: *Mips, register_num: u16) DwarfRegisterError![]u8 {
849 switch (register_num) {921 switch (register_num) {
850 0...31 => return @ptrCast(&ctx.r[register_num]),922 0...31 => return @ptrCast(&ctx.r[register_num]),
...@@ -917,6 +989,13 @@ const Or1k = extern struct {...@@ -917,6 +989,13 @@ const Or1k = extern struct {
917 return ctx;989 return ctx;
918 }990 }
919991
992 pub fn getFp(ctx: *const Or1k) u32 {
993 return ctx.r[2];
994 }
995 pub fn getPc(ctx: *const Or1k) u32 {
996 return ctx.pc;
997 }
998
920 pub fn dwarfRegisterBytes(ctx: *Or1k, register_num: u16) DwarfRegisterError![]u8 {999 pub fn dwarfRegisterBytes(ctx: *Or1k, register_num: u16) DwarfRegisterError![]u8 {
921 switch (register_num) {1000 switch (register_num) {
922 0...31 => return @ptrCast(&ctx.r[register_num]),1001 0...31 => return @ptrCast(&ctx.r[register_num]),
...@@ -1022,6 +1101,13 @@ const Powerpc = extern struct {...@@ -1022,6 +1101,13 @@ const Powerpc = extern struct {
1022 return ctx;1101 return ctx;
1023 }1102 }
10241103
1104 pub fn getFp(ctx: *const Powerpc) Gpr {
1105 return ctx.r[1];
1106 }
1107 pub fn getPc(ctx: *const Powerpc) Gpr {
1108 return ctx.pc;
1109 }
1110
1025 pub fn dwarfRegisterBytes(ctx: *Powerpc, register_num: u16) DwarfRegisterError![]u8 {1111 pub fn dwarfRegisterBytes(ctx: *Powerpc, register_num: u16) DwarfRegisterError![]u8 {
1026 // References:1112 // References:
1027 //1113 //
...@@ -1168,6 +1254,13 @@ const Riscv = extern struct {...@@ -1168,6 +1254,13 @@ const Riscv = extern struct {
1168 return ctx;1254 return ctx;
1169 }1255 }
11701256
1257 pub fn getFp(ctx: *const Riscv) Gpr {
1258 return ctx.x[8];
1259 }
1260 pub fn getPc(ctx: *const Riscv) Gpr {
1261 return ctx.pc;
1262 }
1263
1171 pub fn dwarfRegisterBytes(ctx: *Riscv, register_num: u16) DwarfRegisterError![]u8 {1264 pub fn dwarfRegisterBytes(ctx: *Riscv, register_num: u16) DwarfRegisterError![]u8 {
1172 switch (register_num) {1265 switch (register_num) {
1173 0...31 => return @ptrCast(&ctx.x[register_num]),1266 0...31 => return @ptrCast(&ctx.x[register_num]),
...@@ -1208,6 +1301,13 @@ const S390x = extern struct {...@@ -1208,6 +1301,13 @@ const S390x = extern struct {
1208 return ctx;1301 return ctx;
1209 }1302 }
12101303
1304 pub fn getFp(ctx: *const S390x) u64 {
1305 return ctx.r[11];
1306 }
1307 pub fn getPc(ctx: *const S390x) u64 {
1308 return ctx.psw.addr;
1309 }
1310
1211 pub fn dwarfRegisterBytes(ctx: *S390x, register_num: u16) DwarfRegisterError![]u8 {1311 pub fn dwarfRegisterBytes(ctx: *S390x, register_num: u16) DwarfRegisterError![]u8 {
1212 switch (register_num) {1312 switch (register_num) {
1213 0...15 => return @ptrCast(&ctx.r[register_num]),1313 0...15 => return @ptrCast(&ctx.r[register_num]),
...@@ -1310,6 +1410,13 @@ const Sparc = extern struct {...@@ -1310,6 +1410,13 @@ const Sparc = extern struct {
1310 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS1410 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
1311 }1411 }
13121412
1413 pub fn getFp(ctx: *const Sparc) Gpr {
1414 return ctx.i[6];
1415 }
1416 pub fn getPc(ctx: *const Sparc) Gpr {
1417 return ctx.pc;
1418 }
1419
1313 pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {1420 pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
1314 switch (register_num) {1421 switch (register_num) {
1315 0...7 => return @ptrCast(&ctx.g[register_num]),1422 0...7 => return @ptrCast(&ctx.g[register_num]),
...@@ -1404,6 +1511,13 @@ const Ve = extern struct {...@@ -1404,6 +1511,13 @@ const Ve = extern struct {
1404 return ctx;1511 return ctx;
1405 }1512 }
14061513
1514 pub fn getFp(ctx: *const Ve) u64 {
1515 return ctx.s[9];
1516 }
1517 pub fn getPc(ctx: *const Ve) u64 {
1518 return ctx.ic;
1519 }
1520
1407 pub fn dwarfRegisterBytes(ctx: *Ve, register_num: u16) DwarfRegisterError![]u8 {1521 pub fn dwarfRegisterBytes(ctx: *Ve, register_num: u16) DwarfRegisterError![]u8 {
1408 switch (register_num) {1522 switch (register_num) {
1409 0...63 => return @ptrCast(&ctx.s[register_num]),1523 0...63 => return @ptrCast(&ctx.s[register_num]),
...@@ -1444,6 +1558,13 @@ const X86_16 = struct {...@@ -1444,6 +1558,13 @@ const X86_16 = struct {
1444 return ctx;1558 return ctx;
1445 }1559 }
14461560
1561 pub fn getFp(ctx: *const X86_16) u16 {
1562 return ctx.regs.get(.bp);
1563 }
1564 pub fn getPc(ctx: *const X86_16) u16 {
1565 return ctx.regs.get(.ip);
1566 }
1567
1447 // NOTE: There doesn't seem to be any standard for DWARF x86-16 so we'll just reuse the ones for x86.1568 // NOTE: There doesn't seem to be any standard for DWARF x86-16 so we'll just reuse the ones for x86.
1448 pub fn dwarfRegisterBytes(ctx: *X86_16, register_num: u16) DwarfRegisterError![]u8 {1569 pub fn dwarfRegisterBytes(ctx: *X86_16, register_num: u16) DwarfRegisterError![]u8 {
1449 switch (register_num) {1570 switch (register_num) {
...@@ -1490,6 +1611,13 @@ const X86 = struct {...@@ -1490,6 +1611,13 @@ const X86 = struct {
1490 return ctx;1611 return ctx;
1491 }1612 }
14921613
1614 pub fn getFp(ctx: *const X86) u32 {
1615 return ctx.gprs.get(.ebp);
1616 }
1617 pub fn getPc(ctx: *const X86) u32 {
1618 return ctx.gprs.get(.eip);
1619 }
1620
1493 pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {1621 pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {
1494 // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.11622 // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1
1495 // § 2.4.2 "DWARF Register Number Mapping"1623 // § 2.4.2 "DWARF Register Number Mapping"
...@@ -1558,6 +1686,15 @@ const X86_64 = struct {...@@ -1558,6 +1686,15 @@ const X86_64 = struct {
1558 return ctx;1686 return ctx;
1559 }1687 }
15601688
1689 pub fn getFp(ctx: *const X86_64) usize {
1690 // On x32, registers are 64 bits but `usize` is 32 bits.
1691 return @intCast(ctx.gprs.get(.rbp));
1692 }
1693 pub fn getPc(ctx: *const X86_64) usize {
1694 // On x32, registers are 64 bits but `usize` is 32 bits.
1695 return @intCast(ctx.gprs.get(.rip));
1696 }
1697
1561 pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {1698 pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {
1562 // System V Application Binary Interface AMD64 Architecture Processor Supplement1699 // System V Application Binary Interface AMD64 Architecture Processor Supplement
1563 // § 3.6.2 "DWARF Register Number Mapping"1700 // § 3.6.2 "DWARF Register Number Mapping"