authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-16 10:14:05+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-10-16 10:14:05+02:00
log48f8133beaef31121caea82e39190291bdfdc633
treebdef017a179e3ef0450b2cb6dd71e15543ffd8e9
parent493ad58ff72ac79528cbd20e9506daca964ec37b
parente0f10da2703fa4a1390c9daf91c0997270920c4a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25569 from alexrp/std-debug-sparc

`std.debug`: implement `sparc*-linux` unwinding

5 files changed, 235 insertions(+), 57 deletions(-)

lib/std/debug.zig+71-26
...@@ -728,7 +728,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri...@@ -728,7 +728,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri
728 }728 }
729 // `ret_addr` is the return address, which is *after* the function call.729 // `ret_addr` is the return address, which is *after* the function call.
730 // Subtract 1 to get an address *in* the function call for a better source location.730 // Subtract 1 to get an address *in* the function call for a better source location.
731 try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config);731 try printSourceAtAddress(di_gpa, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config);
732 printed_any_frame = true;732 printed_any_frame = true;
733 },733 },
734 };734 };
...@@ -777,7 +777,7 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c...@@ -777,7 +777,7 @@ pub fn writeStackTrace(st: *const std.builtin.StackTrace, writer: *Writer, tty_c
777 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {777 for (st.instruction_addresses[0..captured_frames]) |ret_addr| {
778 // `ret_addr` is the return address, which is *after* the function call.778 // `ret_addr` is the return address, which is *after* the function call.
779 // Subtract 1 to get an address *in* the function call for a better source location.779 // Subtract 1 to get an address *in* the function call for a better source location.
780 try printSourceAtAddress(di_gpa, di, writer, ret_addr -| 1, tty_config);780 try printSourceAtAddress(di_gpa, di, writer, ret_addr -| StackIterator.ra_call_offset, tty_config);
781 }781 }
782 if (n_frames > captured_frames) {782 if (n_frames > captured_frames) {
783 tty_config.setColor(writer, .bold) catch {};783 tty_config.setColor(writer, .bold) catch {};
...@@ -807,14 +807,6 @@ const StackIterator = union(enum) {...@@ -807,14 +807,6 @@ const StackIterator = union(enum) {
807 /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and807 /// `@frameAddress` and `cpu_context.Native.current` as the caller's stack frame and
808 /// our own are one and the same.808 /// our own are one and the same.
809 inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator {809 inline fn init(opt_context_ptr: ?CpuContextPtr) error{CannotUnwindFromContext}!StackIterator {
810 if (builtin.cpu.arch.isSPARC()) {
811 // Flush all the register windows on stack.
812 if (builtin.cpu.has(.sparc, .v9)) {
813 asm volatile ("flushw" ::: .{ .memory = true });
814 } else {
815 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
816 }
817 }
818 if (opt_context_ptr) |context_ptr| {810 if (opt_context_ptr) |context_ptr| {
819 if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext;811 if (SelfInfo == void or !SelfInfo.can_unwind) return error.CannotUnwindFromContext;
820 // Use `di_first` here so we report the PC in the context before unwinding any further.812 // Use `di_first` here so we report the PC in the context before unwinding any further.
...@@ -833,7 +825,19 @@ const StackIterator = union(enum) {...@@ -833,7 +825,19 @@ const StackIterator = union(enum) {
833 // in our caller's frame and above.825 // in our caller's frame and above.
834 return .{ .di = .init(&.current()) };826 return .{ .di = .init(&.current()) };
835 }827 }
836 return .{ .fp = @frameAddress() };828 return .{
829 // On SPARC, the frame pointer will point to the previous frame's save area,
830 // meaning we will read the previous return address and thus miss a frame.
831 // Instead, start at the stack pointer so we get the return address from the
832 // current frame's save area. The addition of the stack bias cannot fail here
833 // since we know we have a valid stack pointer.
834 .fp = if (native_arch.isSPARC()) sp: {
835 flushSparcWindows();
836 break :sp asm (""
837 : [_] "={o6}" (-> usize),
838 ) + stack_bias;
839 } else @frameAddress(),
840 };
837 }841 }
838 fn deinit(si: *StackIterator) void {842 fn deinit(si: *StackIterator) void {
839 switch (si.*) {843 switch (si.*) {
...@@ -842,6 +846,15 @@ const StackIterator = union(enum) {...@@ -842,6 +846,15 @@ const StackIterator = union(enum) {
842 }846 }
843 }847 }
844848
849 noinline fn flushSparcWindows() void {
850 // Flush all register windows except the current one (hence `noinline`). This ensures that
851 // we actually see meaningful data on the stack when we walk the frame chain.
852 if (comptime builtin.target.cpu.has(.sparc, .v9))
853 asm volatile ("flushw" ::: .{ .memory = true })
854 else
855 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
856 }
857
845 const FpUsability = enum {858 const FpUsability = enum {
846 /// FP unwinding is impractical on this target. For example, due to its very silly ABI859 /// FP unwinding is impractical on this target. For example, due to its very silly ABI
847 /// design decisions, it's not possible to do generic FP unwinding on MIPS without a860 /// design decisions, it's not possible to do generic FP unwinding on MIPS without a
...@@ -873,6 +886,8 @@ const StackIterator = union(enum) {...@@ -873,6 +886,8 @@ const StackIterator = union(enum) {
873 .powerpcle,886 .powerpcle,
874 .powerpc64,887 .powerpc64,
875 .powerpc64le,888 .powerpc64le,
889 .sparc,
890 .sparc64,
876 => .ideal,891 => .ideal,
877 // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers892 // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-purpose-of-specific-CPU-registers
878 .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe,893 .aarch64 => if (builtin.target.os.tag.isDarwin()) .safe else .unsafe,
...@@ -923,7 +938,8 @@ const StackIterator = union(enum) {...@@ -923,7 +938,8 @@ const StackIterator = union(enum) {
923 const di_gpa = getDebugInfoAllocator();938 const di_gpa = getDebugInfoAllocator();
924 const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| {939 const ret_addr = di.unwindFrame(di_gpa, unwind_context) catch |err| {
925 const pc = unwind_context.pc;940 const pc = unwind_context.pc;
926 it.* = .{ .fp = unwind_context.getFp() };941 const fp = unwind_context.getFp();
942 it.* = .{ .fp = fp };
927 return .{ .switch_to_fp = .{943 return .{ .switch_to_fp = .{
928 .address = pc,944 .address = pc,
929 .err = err,945 .err = err,
...@@ -935,8 +951,8 @@ const StackIterator = union(enum) {...@@ -935,8 +951,8 @@ const StackIterator = union(enum) {
935 .fp => |fp| {951 .fp => |fp| {
936 if (fp == 0) return .end; // we reached the "sentinel" base pointer952 if (fp == 0) return .end; // we reached the "sentinel" base pointer
937953
938 const bp_addr = applyOffset(fp, bp_offset) orelse return .end;954 const bp_addr = applyOffset(fp, fp_to_bp_offset) orelse return .end;
939 const ra_addr = applyOffset(fp, ra_offset) orelse return .end;955 const ra_addr = applyOffset(fp, fp_to_ra_offset) orelse return .end;
940956
941 if (bp_addr == 0 or !mem.isAligned(bp_addr, @alignOf(usize)) or957 if (bp_addr == 0 or !mem.isAligned(bp_addr, @alignOf(usize)) or
942 ra_addr == 0 or !mem.isAligned(ra_addr, @alignOf(usize)))958 ra_addr == 0 or !mem.isAligned(ra_addr, @alignOf(usize)))
...@@ -947,7 +963,7 @@ const StackIterator = union(enum) {...@@ -947,7 +963,7 @@ const StackIterator = union(enum) {
947963
948 const bp_ptr: *const usize = @ptrFromInt(bp_addr);964 const bp_ptr: *const usize = @ptrFromInt(bp_addr);
949 const ra_ptr: *const usize = @ptrFromInt(ra_addr);965 const ra_ptr: *const usize = @ptrFromInt(ra_addr);
950 const bp = applyOffset(bp_ptr.*, bp_bias) orelse return .end;966 const bp = applyOffset(bp_ptr.*, stack_bias) orelse return .end;
951967
952 // The stack grows downards, so `bp > fp` should always hold. If it doesn't, this968 // The stack grows downards, so `bp > fp` should always hold. If it doesn't, this
953 // frame is invalid, so we'll treat it as though it we reached end of stack. The969 // frame is invalid, so we'll treat it as though it we reached end of stack. The
...@@ -964,33 +980,46 @@ const StackIterator = union(enum) {...@@ -964,33 +980,46 @@ const StackIterator = union(enum) {
964 }980 }
965981
966 /// Offset of the saved base pointer (previous frame pointer) wrt the frame pointer.982 /// Offset of the saved base pointer (previous frame pointer) wrt the frame pointer.
967 const bp_offset = off: {983 const fp_to_bp_offset = off: {
968 // On RISC-V the frame pointer points to the top of the saved register984 // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area,
969 // area, on pretty much every other architecture it points to the stack985 // in which the base pointer is the first word.
970 // slot where the previous frame pointer is saved.
971 if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -2 * @sizeOf(usize);986 if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -2 * @sizeOf(usize);
972 // On SPARC the previous frame pointer is stored at 14 slots past %fp+BIAS.987 // On SPARC, the frame pointer points to the save area which holds 16 slots for the local
988 // and incoming registers. The base pointer (i6) is stored in its customary save slot.
973 if (native_arch.isSPARC()) break :off 14 * @sizeOf(usize);989 if (native_arch.isSPARC()) break :off 14 * @sizeOf(usize);
990 // Everywhere else, the frame pointer points directly to the location of the base pointer.
974 break :off 0;991 break :off 0;
975 };992 };
976993
977 /// Offset of the saved return address wrt the frame pointer.994 /// Offset of the saved return address wrt the frame pointer.
978 const ra_offset = off: {995 const fp_to_ra_offset = off: {
979 if (native_arch.isLoongArch() or native_arch.isRISCV()) break :off -1 * @sizeOf(usize);996 // On LoongArch and RISC-V, the frame pointer points to the top of the saved register area,
980 if (native_arch.isSPARC()) break :off 15 * @sizeOf(usize);997 // in which the return address is the second word.
998 if (native_arch.isRISCV() or native_arch.isLoongArch()) break :off -1 * @sizeOf(usize);
981 if (native_arch.isPowerPC64()) break :off 2 * @sizeOf(usize);999 if (native_arch.isPowerPC64()) break :off 2 * @sizeOf(usize);
982 // On s390x, r14 is the link register and we need to grab it from its customary slot in the1000 // On s390x, r14 is the link register and we need to grab it from its customary slot in the
983 // register save area (ELF ABI s390x Supplement §1.2.2.2).1001 // register save area (ELF ABI s390x Supplement §1.2.2.2).
984 if (native_arch == .s390x) break :off 14 * @sizeOf(usize);1002 if (native_arch == .s390x) break :off 14 * @sizeOf(usize);
1003 // On SPARC, the frame pointer points to the save area which holds 16 slots for the local
1004 // and incoming registers. The return address (i7) is stored in its customary save slot.
1005 if (native_arch.isSPARC()) break :off 15 * @sizeOf(usize);
985 break :off @sizeOf(usize);1006 break :off @sizeOf(usize);
986 };1007 };
9871008
988 /// Value to add to a base pointer after loading it from the stack. Yes, SPARC really does this.1009 /// Value to add to the stack pointer and frame/base pointers to get the real location being
989 const bp_bias = bias: {1010 /// pointed to. Yes, SPARC really does this.
990 if (native_arch.isSPARC()) break :bias 2047;1011 const stack_bias = bias: {
1012 if (native_arch == .sparc64) break :bias 2047;
991 break :bias 0;1013 break :bias 0;
992 };1014 };
9931015
1016 /// On some oddball architectures, a return address points to the call instruction rather than
1017 /// the instruction following it.
1018 const ra_call_offset = off: {
1019 if (native_arch.isSPARC()) break :off 0;
1020 break :off 1;
1021 };
1022
994 fn applyOffset(addr: usize, comptime off: comptime_int) ?usize {1023 fn applyOffset(addr: usize, comptime off: comptime_int) ?usize {
995 if (off >= 0) return math.add(usize, addr, off) catch return null;1024 if (off >= 0) return math.add(usize, addr, off) catch return null;
996 return math.sub(usize, addr, -off) catch return null;1025 return math.sub(usize, addr, -off) catch return null;
...@@ -1429,6 +1458,22 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa...@@ -1429,6 +1458,22 @@ fn handleSegfaultPosix(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopa
1429 break :info .{ addr, name };1458 break :info .{ addr, name };
1430 };1459 };
1431 const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr);1460 const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr);
1461
1462 if (native_arch.isSPARC()) {
1463 // It's unclear to me whether this is a QEMU bug or also real kernel behavior, but in the
1464 // former, I observed that the most recent register window wasn't getting spilled on the
1465 // stack as expected when a signal arrived. A `flushw` from the signal handler does not
1466 // appear to be sufficient either. On the other hand, when doing a synchronous stack trace
1467 // and using `flushw`, this all appears to work as expected. So, *probably* a QEMU bug, but
1468 // someone with real SPARC hardware should verify.
1469 //
1470 // In any case, the register save area exists specifically so that register windows can be
1471 // spilled asynchronously. This means that it should be perfectly fine for us to manually do
1472 // so here.
1473 const ctx = opt_cpu_context.?;
1474 @as(*[16]usize, @ptrFromInt(ctx.o[6] + StackIterator.stack_bias)).* = ctx.l ++ ctx.i;
1475 }
1476
1432 handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null);1477 handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null);
1433}1478}
14341479
lib/std/debug/Dwarf.zig+3
...@@ -1437,6 +1437,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {...@@ -1437,6 +1437,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
1437 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67,1437 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67,
1438 .riscv32, .riscv32be, .riscv64, .riscv64be => 65,1438 .riscv32, .riscv32be, .riscv64, .riscv64be => 65,
1439 .s390x => 65,1439 .s390x => 65,
1440 .sparc, .sparc64 => 32,
1440 .x86 => 8,1441 .x86 => 8,
1441 .x86_64 => 16,1442 .x86_64 => 16,
1442 else => null,1443 else => null,
...@@ -1453,6 +1454,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {...@@ -1453,6 +1454,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
1453 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,1454 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
1454 .riscv32, .riscv32be, .riscv64, .riscv64be => 8,1455 .riscv32, .riscv32be, .riscv64, .riscv64be => 8,
1455 .s390x => 11,1456 .s390x => 11,
1457 .sparc, .sparc64 => 30,
1456 .x86 => 5,1458 .x86 => 5,
1457 .x86_64 => 6,1459 .x86_64 => 6,
1458 else => unreachable,1460 else => unreachable,
...@@ -1469,6 +1471,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {...@@ -1469,6 +1471,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
1469 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,1471 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
1470 .riscv32, .riscv32be, .riscv64, .riscv64be => 2,1472 .riscv32, .riscv32be, .riscv64, .riscv64be => 2,
1471 .s390x => 15,1473 .s390x => 15,
1474 .sparc, .sparc64 => 14,
1472 .x86 => 4,1475 .x86 => 4,
1473 .x86_64 => 7,1476 .x86_64 => 7,
1474 else => unreachable,1477 else => unreachable,
lib/std/debug/SelfInfo/Elf.zig+4-16
...@@ -94,28 +94,22 @@ pub const can_unwind: bool = s: {...@@ -94,28 +94,22 @@ pub const can_unwind: bool = s: {
94 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through94 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through
95 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.95 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.
96 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {96 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
97 // Not supported yet: arm, m68k, sparc6497 // Not supported yet: arm, m68k
98 .haiku => &.{98 .haiku => &.{
99 .aarch64,99 .aarch64,
100 .powerpc,
101 .riscv64,100 .riscv64,
102 .x86,101 .x86,
103 .x86_64,102 .x86_64,
104 },103 },
105 // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, sparc/sparc64, xtensa104 // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, xtensa
106 .linux => &.{105 .linux => &.{
107 .aarch64,106 .aarch64,
108 .aarch64_be,107 .aarch64_be,
109 .hexagon,
110 .loongarch64,108 .loongarch64,
111 .mips,109 .mips,
112 .mipsel,110 .mipsel,
113 .mips64,111 .mips64,
114 .mips64el,112 .mips64el,
115 .powerpc,
116 .powerpcle,
117 .powerpc64,
118 .powerpc64le,
119 .riscv32,113 .riscv32,
120 .riscv64,114 .riscv64,
121 .s390x,115 .s390x,
...@@ -134,28 +128,23 @@ pub const can_unwind: bool = s: {...@@ -134,28 +128,23 @@ pub const can_unwind: bool = s: {
134 // Not supported yet: arm128 // Not supported yet: arm
135 .freebsd => &.{129 .freebsd => &.{
136 .aarch64,130 .aarch64,
137 .powerpc64,
138 .powerpc64le,
139 .riscv64,131 .riscv64,
140 .x86_64,132 .x86_64,
141 },133 },
142 // Not supported yet: arm/armeb, m68k, mips64/mips64el, sparc/sparc64134 // Not supported yet: arm/armeb, m68k, mips64/mips64el
143 .netbsd => &.{135 .netbsd => &.{
144 .aarch64,136 .aarch64,
145 .aarch64_be,137 .aarch64_be,
146 .mips,138 .mips,
147 .mipsel,139 .mipsel,
148 .powerpc,
149 .x86,140 .x86,
150 .x86_64,141 .x86_64,
151 },142 },
152 // Not supported yet: arm, sparc64143 // Not supported yet: arm
153 .openbsd => &.{144 .openbsd => &.{
154 .aarch64,145 .aarch64,
155 .mips64,146 .mips64,
156 .mips64el,147 .mips64el,
157 .powerpc,
158 .powerpc64,
159 .riscv64,148 .riscv64,
160 .x86,149 .x86,
161 .x86_64,150 .x86_64,
...@@ -165,7 +154,6 @@ pub const can_unwind: bool = s: {...@@ -165,7 +154,6 @@ pub const can_unwind: bool = s: {
165 .x86,154 .x86,
166 .x86_64,155 .x86_64,
167 },156 },
168 // Not supported yet: sparc64
169 .solaris => &.{157 .solaris => &.{
170 .x86_64,158 .x86_64,
171 },159 },
lib/std/debug/cpu_context.zig+155-13
...@@ -10,6 +10,7 @@ else switch (native_arch) {...@@ -10,6 +10,7 @@ else switch (native_arch) {
10 .loongarch32, .loongarch64 => LoongArch,10 .loongarch32, .loongarch64 => LoongArch,
11 .mips, .mipsel, .mips64, .mips64el => Mips,11 .mips, .mipsel, .mips64, .mips64el => Mips,
12 .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc,12 .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc,
13 .sparc, .sparc64 => Sparc,
13 .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,14 .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,
14 .s390x => S390x,15 .s390x => S390x,
15 .x86 => X86,16 .x86 => X86,
...@@ -39,6 +40,26 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {...@@ -39,6 +40,26 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
39 },40 },
40 .pc = @truncate(uc.mcontext.pc),41 .pc = @truncate(uc.mcontext.pc),
41 };42 };
43 } else if (native_arch.isSPARC() and native_os == .linux) {
44 const SparcStackFrame = extern struct {
45 l: [8]usize,
46 i: [8]usize,
47 _x: [8]usize,
48 };
49
50 // When invoking a signal handler, the kernel builds an `rt_signal_frame` structure on the
51 // stack and passes a pointer to its `info` field to the signal handler. This implies that
52 // prior to said `info` field, we will find the `ss` field which, among other things,
53 // contains the incoming and local registers of the interrupted code.
54 const frame = @as(*const SparcStackFrame, @ptrFromInt(@as(usize, @intFromPtr(ctx_ptr)) - @sizeOf(SparcStackFrame)));
55
56 return .{
57 .g = uc.mcontext.g,
58 .o = uc.mcontext.o,
59 .l = frame.l,
60 .i = frame.i,
61 .pc = uc.mcontext.pc,
62 };
42 }63 }
4364
44 // Only unified conversions from here.65 // Only unified conversions from here.
...@@ -858,6 +879,104 @@ const Powerpc = extern struct {...@@ -858,6 +879,104 @@ const Powerpc = extern struct {
858 }879 }
859};880};
860881
882/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
883const Sparc = extern struct {
884 g: [8]Gpr,
885 o: [8]Gpr,
886 l: [8]Gpr,
887 i: [8]Gpr,
888 pc: Gpr,
889
890 pub const Gpr = if (native_arch == .sparc64) u64 else u32;
891
892 pub inline fn current() Sparc {
893 flushWindows();
894
895 var ctx: Sparc = undefined;
896 asm volatile (if (Gpr == u64)
897 \\ stx %g0, [%l0 + 0]
898 \\ stx %g1, [%l0 + 8]
899 \\ stx %g2, [%l0 + 16]
900 \\ stx %g3, [%l0 + 24]
901 \\ stx %g4, [%l0 + 32]
902 \\ stx %g5, [%l0 + 40]
903 \\ stx %g6, [%l0 + 48]
904 \\ stx %g7, [%l0 + 56]
905 \\ stx %o0, [%l0 + 64]
906 \\ stx %o1, [%l0 + 72]
907 \\ stx %o2, [%l0 + 80]
908 \\ stx %o3, [%l0 + 88]
909 \\ stx %o4, [%l0 + 96]
910 \\ stx %o5, [%l0 + 104]
911 \\ stx %o6, [%l0 + 112]
912 \\ stx %o7, [%l0 + 120]
913 \\ stx %l0, [%l0 + 128]
914 \\ stx %l1, [%l0 + 136]
915 \\ stx %l2, [%l0 + 144]
916 \\ stx %l3, [%l0 + 152]
917 \\ stx %l4, [%l0 + 160]
918 \\ stx %l5, [%l0 + 168]
919 \\ stx %l6, [%l0 + 176]
920 \\ stx %l7, [%l0 + 184]
921 \\ stx %i0, [%l0 + 192]
922 \\ stx %i1, [%l0 + 200]
923 \\ stx %i2, [%l0 + 208]
924 \\ stx %i3, [%l0 + 216]
925 \\ stx %i4, [%l0 + 224]
926 \\ stx %i5, [%l0 + 232]
927 \\ stx %i6, [%l0 + 240]
928 \\ stx %i7, [%l0 + 248]
929 \\ call 1f
930 \\ stx %o7, [%l0 + 256]
931 \\1:
932 else
933 \\ std %g0, [%l0 + 0]
934 \\ std %g2, [%l0 + 8]
935 \\ std %g4, [%l0 + 16]
936 \\ std %g6, [%l0 + 24]
937 \\ std %o0, [%l0 + 32]
938 \\ std %o2, [%l0 + 40]
939 \\ std %o4, [%l0 + 48]
940 \\ std %o6, [%l0 + 56]
941 \\ std %l0, [%l0 + 64]
942 \\ std %l2, [%l0 + 72]
943 \\ std %l4, [%l0 + 80]
944 \\ std %l6, [%l0 + 88]
945 \\ std %i0, [%l0 + 96]
946 \\ std %i2, [%l0 + 104]
947 \\ std %i4, [%l0 + 112]
948 \\ std %i6, [%l0 + 120]
949 \\ call 1f
950 \\ st %o7, [%l0 + 128]
951 \\1:
952 :
953 : [gprs] "{l0}" (&ctx),
954 : .{ .o7 = true, .memory = true });
955 return ctx;
956 }
957
958 noinline fn flushWindows() void {
959 // Flush all register windows except the current one (hence `noinline`). This ensures that
960 // we actually see meaningful data on the stack when we walk the frame chain.
961 if (comptime builtin.target.cpu.has(.sparc, .v9))
962 asm volatile ("flushw" ::: .{ .memory = true })
963 else
964 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
965 }
966
967 pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
968 switch (register_num) {
969 0...7 => return @ptrCast(&ctx.g[register_num]),
970 8...15 => return @ptrCast(&ctx.o[register_num - 8]),
971 16...23 => return @ptrCast(&ctx.l[register_num - 16]),
972 24...31 => return @ptrCast(&ctx.i[register_num - 24]),
973 32 => return @ptrCast(&ctx.pc),
974
975 else => return error.InvalidRegister,
976 }
977 }
978};
979
861/// This is an `extern struct` so that inline assembly in `current` can use field offsets.980/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
862const Riscv = extern struct {981const Riscv = extern struct {
863 /// The numbered general-purpose registers r0 - r31. r0 must be zero.982 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
...@@ -1271,9 +1390,32 @@ const signal_ucontext_t = switch (native_os) {...@@ -1271,9 +1390,32 @@ const signal_ucontext_t = switch (native_os) {
1271 lr: u32,1390 lr: u32,
1272 },1391 },
1273 },1392 },
1274 // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/include/uapi/asm/uctx.h1393 // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/kernel/signal_32.c#L48-L49
1275 .sparc => @compileError("sparc-linux ucontext_t missing"),1394 .sparc => extern struct {
1276 .sparc64 => @compileError("sparc64-linux ucontext_t missing"),1395 // Not actually a `ucontext_t` at all because, uh, reasons?
1396
1397 _info: std.os.linux.siginfo_t,
1398 mcontext: extern struct {
1399 _psr: u32,
1400 pc: u32,
1401 _npc: u32,
1402 _y: u32,
1403 g: [8]u32,
1404 o: [8]u32,
1405 },
1406 },
1407 // https://github.com/torvalds/linux/blob/cd5a0afbdf8033dc83786315d63f8b325bdba2fd/arch/sparc/kernel/signal_64.c#L247-L248
1408 .sparc64 => extern struct {
1409 // Ditto...
1410
1411 _info: std.os.linux.siginfo_t,
1412 mcontext: extern struct {
1413 g: [8]u64,
1414 o: [8]u64,
1415 _tstate: u64,
1416 pc: u64,
1417 },
1418 },
1277 else => unreachable,1419 else => unreachable,
1278 },1420 },
1279 // https://github.com/freebsd/freebsd-src/blob/55c28005f544282b984ae0e15dacd0c108d8ab12/sys/sys/_ucontext.h1421 // https://github.com/freebsd/freebsd-src/blob/55c28005f544282b984ae0e15dacd0c108d8ab12/sys/sys/_ucontext.h
...@@ -1398,14 +1540,14 @@ const signal_ucontext_t = switch (native_os) {...@@ -1398,14 +1540,14 @@ const signal_ucontext_t = switch (native_os) {
1398 },1540 },
1399 },1541 },
1400 // This needs to be audited by someone with access to the Solaris headers.1542 // This needs to be audited by someone with access to the Solaris headers.
1401 .solaris => extern struct {1543 .solaris => switch (native_arch) {
1402 _flags: u64,1544 .sparc64 => @compileError("sparc64-solaris ucontext_t missing"),
1403 _link: ?*signal_ucontext_t,1545 .x86_64 => extern struct {
1404 _sigmask: std.c.sigset_t,1546 _flags: u64,
1405 _stack: std.c.stack_t,1547 _link: ?*signal_ucontext_t,
1406 mcontext: switch (native_arch) {1548 _sigmask: std.c.sigset_t,
1407 .sparc64 => @compileError("sparc64-solaris mcontext_t missing"),1549 _stack: std.c.stack_t,
1408 .x86_64 => extern struct {1550 mcontext: extern struct {
1409 r15: u64,1551 r15: u64,
1410 r14: u64,1552 r14: u64,
1411 r13: u64,1553 r13: u64,
...@@ -1425,8 +1567,8 @@ const signal_ucontext_t = switch (native_os) {...@@ -1425,8 +1567,8 @@ const signal_ucontext_t = switch (native_os) {
1425 _err: i64,1567 _err: i64,
1426 rip: u64,1568 rip: u64,
1427 },1569 },
1428 else => unreachable,
1429 },1570 },
1571 else => unreachable,
1430 },1572 },
1431 // https://github.com/illumos/illumos-gate/blob/d4ce137bba3bd16823db6374d9e9a643264ce245/usr/src/uts/intel/sys/ucontext.h1573 // https://github.com/illumos/illumos-gate/blob/d4ce137bba3bd16823db6374d9e9a643264ce245/usr/src/uts/intel/sys/ucontext.h
1432 .illumos => extern struct {1574 .illumos => extern struct {
...@@ -1538,7 +1680,7 @@ const signal_ucontext_t = switch (native_os) {...@@ -1538,7 +1680,7 @@ const signal_ucontext_t = switch (native_os) {
1538 },1680 },
1539 },1681 },
1540 // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/sparc64/include/signal.h1682 // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/sparc64/include/signal.h
1541 .sparc64 => @compileError("sparc64-openbsd mcontext_t missing"),1683 .sparc64 => @compileError("sparc64-openbsd ucontext_t missing"),
1542 // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/i386/include/signal.h1684 // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/i386/include/signal.h
1543 .x86 => extern struct {1685 .x86 => extern struct {
1544 mcontext: extern struct {1686 mcontext: extern struct {
lib/std/os/windows.zig+2-2
...@@ -4229,8 +4229,8 @@ pub const CONTEXT = switch (native_arch) {...@@ -4229,8 +4229,8 @@ pub const CONTEXT = switch (native_arch) {
4229 SegSs: DWORD,4229 SegSs: DWORD,
4230 ExtendedRegisters: [512]BYTE,4230 ExtendedRegisters: [512]BYTE,
42314231
4232 pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize } {4232 pub fn getRegs(ctx: *const CONTEXT) struct { bp: usize, ip: usize, sp: usize } {
4233 return .{ .bp = ctx.Ebp, .ip = ctx.Eip };4233 return .{ .bp = ctx.Ebp, .ip = ctx.Eip, .sp = ctx.Esp };
4234 }4234 }
4235 },4235 },
4236 .x86_64 => extern struct {4236 .x86_64 => extern struct {