std: rework/remove ucontext_t

Our usage of `ucontext_t` in the standard library was kind of
problematic. We unnecessarily mimiced libc-specific structures, and our
`getcontext` implementation was overkill for our use case of stack
tracing.

This commit introduces a new namespace, `std.debug.cpu_context`, which
contains "context" types for various architectures (currently x86,
x86_64, ARM, and AARCH64) containing the general-purpose CPU registers;
the ones needed in practice for stack unwinding. Each implementation has
a function `current` which populates the structure using inline
assembly. The structure is user-overrideable, though that should only be
necessary if the standard library does not have an implementation for
the *architecture*: that is to say, none of this is OS-dependent.

Of course, in POSIX signal handlers, we get a `ucontext_t` from the
kernel. The function `std.debug.cpu_context.fromPosixSignalContext`
converts this to a `std.debug.cpu_context.Native` with a big ol' target
switch.

This functionality is not exposed from `std.c` or `std.posix`, and
neither are `ucontext_t`, `mcontext_t`, or `getcontext`. The rationale
is that these types and functions do not conform to a specific ABI, and
in fact tend to get updated over time based on CPU features and
extensions; in addition, different libcs use different structures which
are "partially compatible" with the kernel structure. Overall, it's a
mess, but all we need is the kernel context, so we can just define a
kernel-compatible structure as long as we don't claim C compatibility by
putting it in `std.c` or `std.posix`.

This change resulted in a few nice `std.debug` simplifications, but
nothing too noteworthy. However, the main benefit of this change is that
DWARF unwinding---sometimes necessary for collecting stack traces
reliably---now requires far less target-specific integration.

Also fix a bug I noticed in `PageAllocator` (I found this due to a bug
in my distro's QEMU distribution; thanks, broken QEMU patch!) and I
think a couple of minor bugs in `std.debug`.

Resolves: #23801
Resolves: #23802
This commit is contained in:
mlugg
2025-09-17 18:38:11 +01:00
parent b578cca022
commit a18fd41064
33 changed files with 1409 additions and 1516 deletions
+37 -3
View File
@@ -295,11 +295,45 @@ pub const UnwindContext = struct {
pc: usize,
cur: windows.CONTEXT,
history_table: windows.UNWIND_HISTORY_TABLE,
pub fn init(ctx: *const windows.CONTEXT, gpa: Allocator) Allocator.Error!UnwindContext {
_ = gpa;
pub fn init(ctx: *const std.debug.cpu_context.Native) UnwindContext {
return .{
.pc = @returnAddress(),
.cur = ctx.*,
.cur = switch (builtin.cpu.arch) {
.x86_64 => std.mem.zeroInit(windows.CONTEXT, .{
.Rax = ctx.gprs.get(.rax),
.Rcx = ctx.gprs.get(.rcx),
.Rdx = ctx.gprs.get(.rdx),
.Rbx = ctx.gprs.get(.rbx),
.Rsp = ctx.gprs.get(.rsp),
.Rbp = ctx.gprs.get(.rbp),
.Rsi = ctx.gprs.get(.rsi),
.Rdi = ctx.gprs.get(.rdi),
.R8 = ctx.gprs.get(.r8),
.R9 = ctx.gprs.get(.r9),
.R10 = ctx.gprs.get(.r10),
.R11 = ctx.gprs.get(.r11),
.R12 = ctx.gprs.get(.r12),
.R13 = ctx.gprs.get(.r13),
.R14 = ctx.gprs.get(.r14),
.R15 = ctx.gprs.get(.r15),
.Rip = ctx.gprs.get(.rip),
}),
.aarch64, .aarch64_be => .{
.ContextFlags = 0,
.Cpsr = 0,
.DUMMYUNIONNAME = .{ .X = ctx.x },
.Sp = ctx.sp,
.Pc = ctx.pc,
.V = @splat(.{ .B = @splat(0) }),
.Fpcr = 0,
.Fpsr = 0,
.Bcr = @splat(0),
.Bvr = @splat(0),
.Wcr = @splat(0),
.Wvr = @splat(0),
},
else => comptime unreachable,
},
.history_table = std.mem.zeroes(windows.UNWIND_HISTORY_TABLE),
};
}