From 9ae517714efba1017e5d0ee76f71c6beda015697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 28 Aug 2026 14:06:49 +0200 Subject: [PATCH 1/2] std.debug: remove SPARC workaround in handleSegfaultPosix() This no longer appears to be needed. --- lib/std/debug.zig | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 555330ccf87d86df0d82c3fdeca30e27cacca9b3..845ca4e66fcf1e4b433351691abc5923e0f06e4d 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1647,21 +1647,6 @@ fn handleSegfaultPosix(sig: posix.SIG, info: *const posix.siginfo_t, ctx_ptr: ?* }; const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr); - if (native_arch.isSPARC()) { - // It's unclear to me whether this is a QEMU bug or also real kernel behavior, but in the - // former, I observed that the most recent register window wasn't getting spilled on the - // stack as expected when a signal arrived. A `flushw` from the signal handler does not - // appear to be sufficient either. On the other hand, when doing a synchronous stack trace - // and using `flushw`, this all appears to work as expected. So, *probably* a QEMU bug, but - // someone with real SPARC hardware should verify. - // - // In any case, the register save area exists specifically so that register windows can be - // spilled asynchronously. This means that it should be perfectly fine for us to manually do - // so here. - const ctx = opt_cpu_context.?; - @as(*[16]usize, @ptrFromInt(ctx.o[6] + StackIterator.stack_bias)).* = ctx.l ++ ctx.i; - } - handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null); } -- 2.54.0 From fb1e767ff8a6f4c32865020e36df4038434ecc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 28 Aug 2026 14:07:24 +0200 Subject: [PATCH 2/2] std.debug: apply stack bias to the frame pointer fetched from the CPU context Unbreaks segfault stack tracing on SPARC. --- lib/std/debug.zig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 845ca4e66fcf1e4b433351691abc5923e0f06e4d..27d50955e8c4e966c961389951726283f927f073 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1086,10 +1086,12 @@ const StackIterator = union(enum) { switch (it.*) { .ctx_first => |context_ptr| { // After the first frame, start actually unwinding. - it.* = if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal) - .{ .di = .init(context_ptr) } - else - .{ .fp = context_ptr.getFp() }; + if (SelfInfo != void and SelfInfo.can_unwind and fp_usability != .ideal) { + it.* = .{ .di = .init(context_ptr) }; + } else { + const fp = applyOffset(context_ptr.getFp(), stack_bias) orelse return .end; + it.* = .{ .fp = fp }; + } // The caller expects *return* addresses, where they will subtract 1 to find the address of the call. // However, we have the actual current PC, which should not be adjusted. Compensate by adding 1. @@ -1099,7 +1101,7 @@ const StackIterator = union(enum) { const di = getSelfDebugInfo() catch unreachable; const ret_addr = di.unwindFrame(io, unwind_context) catch |err| { const pc = unwind_context.pc; - const fp = unwind_context.getFp(); + const fp = applyOffset(unwind_context.getFp(), stack_bias) orelse return .end; unwind_context.deinit(); it.* = .{ .fp = fp }; return .{ .switch_to_fp = .{ -- 2.54.0