diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-09-17 18:38:11 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-09-30 13:44:54 +0100 |
| commit | a18fd41064493e742eacebc88e2afeadd54ff6f0 (patch) | |
| tree | 1081fbd6d3c64cf1f583ae3188ab05e0320f03d9 /lib/std/os/linux | |
| parent | b578cca022f4c9ce94439e2ee795639b3a23c8f5 (diff) | |
| download | zig-a18fd41064493e742eacebc88e2afeadd54ff6f0.tar.gz zig-a18fd41064493e742eacebc88e2afeadd54ff6f0.zip | |
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
Diffstat (limited to 'lib/std/os/linux')
| -rw-r--r-- | lib/std/os/linux/aarch64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/arm.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/hexagon.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/loongarch64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/m68k.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/mips.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/mips64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/powerpc.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/powerpc64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/riscv32.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/riscv64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/s390x.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/sparc64.zig | 3 | ||||
| -rw-r--r-- | lib/std/os/linux/x86.zig | 14 | ||||
| -rw-r--r-- | lib/std/os/linux/x86_64.zig | 95 |
15 files changed, 0 insertions, 148 deletions
diff --git a/lib/std/os/linux/aarch64.zig b/lib/std/os/linux/aarch64.zig index 5fcf04c58b..6538e1f175 100644 --- a/lib/std/os/linux/aarch64.zig +++ b/lib/std/os/linux/aarch64.zig @@ -260,7 +260,4 @@ pub const ucontext_t = extern struct { mcontext: mcontext_t, }; -/// TODO -pub const getcontext = {}; - pub const Elf_Symndx = u32; diff --git a/lib/std/os/linux/arm.zig b/lib/std/os/linux/arm.zig index 5f41607efe..693fe5aafd 100644 --- a/lib/std/os/linux/arm.zig +++ b/lib/std/os/linux/arm.zig @@ -310,7 +310,4 @@ pub const ucontext_t = extern struct { regspace: [64]u64, }; -/// TODO -pub const getcontext = {}; - pub const Elf_Symndx = u32; diff --git a/lib/std/os/linux/hexagon.zig b/lib/std/os/linux/hexagon.zig index 255faba20f..ea54829e65 100644 --- a/lib/std/os/linux/hexagon.zig +++ b/lib/std/os/linux/hexagon.zig @@ -237,6 +237,3 @@ pub const VDSO = void; /// TODO pub const ucontext_t = void; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/loongarch64.zig b/lib/std/os/linux/loongarch64.zig index b6ff38fccd..f57050ddb7 100644 --- a/lib/std/os/linux/loongarch64.zig +++ b/lib/std/os/linux/loongarch64.zig @@ -250,6 +250,3 @@ pub const ucontext_t = extern struct { }; pub const Elf_Symndx = u32; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/m68k.zig b/lib/std/os/linux/m68k.zig index 78851c3928..82d854e498 100644 --- a/lib/std/os/linux/m68k.zig +++ b/lib/std/os/linux/m68k.zig @@ -258,6 +258,3 @@ pub const VDSO = void; /// TODO pub const ucontext_t = void; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index c971bbd739..f0a235a598 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -349,6 +349,3 @@ pub const Elf_Symndx = u32; /// TODO pub const ucontext_t = void; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 91fcacef38..11ad102914 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -328,6 +328,3 @@ pub const Elf_Symndx = u32; /// TODO pub const ucontext_t = void; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index 3e876bb3f0..32e66d8c78 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -381,6 +381,3 @@ pub const ucontext_t = extern struct { }; pub const Elf_Symndx = u32; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 70b11a86bb..fb4686c7b2 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -376,6 +376,3 @@ pub const ucontext_t = extern struct { }; pub const Elf_Symndx = u32; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/riscv32.zig b/lib/std/os/linux/riscv32.zig index 01b6002135..f70fcefd14 100644 --- a/lib/std/os/linux/riscv32.zig +++ b/lib/std/os/linux/riscv32.zig @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask mcontext: mcontext_t, }; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 577cf3ec48..d2eb4f9b4d 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -255,6 +255,3 @@ pub const ucontext_t = extern struct { sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask mcontext: mcontext_t, }; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/s390x.zig b/lib/std/os/linux/s390x.zig index 620aedfb69..71594d4a65 100644 --- a/lib/std/os/linux/s390x.zig +++ b/lib/std/os/linux/s390x.zig @@ -273,6 +273,3 @@ pub const mcontext_t = extern struct { __regs2: [18]u32, __regs3: [16]f64, }; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 1377343888..1859ba5c05 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -426,6 +426,3 @@ pub const ucontext_t = extern struct { stack: stack_t, sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask }; - -/// TODO -pub const getcontext = {}; diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig index eed85a2ad0..41b11a35e3 100644 --- a/lib/std/os/linux/x86.zig +++ b/lib/std/os/linux/x86.zig @@ -436,17 +436,3 @@ pub fn getContextInternal() callconv(.naked) usize { [sigset_size] "i" (linux.NSIG / 8), : .{ .cc = true, .memory = true, .eax = true, .ecx = true, .edx = true }); } - -pub inline fn getcontext(context: *ucontext_t) usize { - // This method is used so that getContextInternal can control - // its prologue in order to read ESP from a constant offset. - // An aligned stack is not needed for getContextInternal. - var clobber_edx: usize = undefined; - return asm volatile ( - \\ calll %[getContextInternal:P] - : [_] "={eax}" (-> usize), - [_] "={edx}" (clobber_edx), - : [_] "{edx}" (context), - [getContextInternal] "X" (&getContextInternal), - : .{ .cc = true, .memory = true, .ecx = true }); -} diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index 583fad872d..72944c37ac 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -352,98 +352,3 @@ pub const ucontext_t = extern struct { sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask. fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t }; - -fn gpRegisterOffset(comptime reg_index: comptime_int) usize { - return @offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "gregs") + @sizeOf(usize) * reg_index; -} - -fn getContextInternal() callconv(.naked) usize { - // TODO: Read GS/FS registers? - asm volatile ( - \\ movq $0, %[flags_offset:c](%%rdi) - \\ movq $0, %[link_offset:c](%%rdi) - \\ movq %%r8, %[r8_offset:c](%%rdi) - \\ movq %%r9, %[r9_offset:c](%%rdi) - \\ movq %%r10, %[r10_offset:c](%%rdi) - \\ movq %%r11, %[r11_offset:c](%%rdi) - \\ movq %%r12, %[r12_offset:c](%%rdi) - \\ movq %%r13, %[r13_offset:c](%%rdi) - \\ movq %%r14, %[r14_offset:c](%%rdi) - \\ movq %%r15, %[r15_offset:c](%%rdi) - \\ movq %%rdi, %[rdi_offset:c](%%rdi) - \\ movq %%rsi, %[rsi_offset:c](%%rdi) - \\ movq %%rbp, %[rbp_offset:c](%%rdi) - \\ movq %%rbx, %[rbx_offset:c](%%rdi) - \\ movq %%rdx, %[rdx_offset:c](%%rdi) - \\ movq %%rax, %[rax_offset:c](%%rdi) - \\ movq %%rcx, %[rcx_offset:c](%%rdi) - \\ movq (%%rsp), %%rcx - \\ movq %%rcx, %[rip_offset:c](%%rdi) - \\ leaq 8(%%rsp), %%rcx - \\ movq %%rcx, %[rsp_offset:c](%%rdi) - \\ pushfq - \\ popq %[efl_offset:c](%%rdi) - \\ leaq %[fpmem_offset:c](%%rdi), %%rcx - \\ movq %%rcx, %[fpstate_offset:c](%%rdi) - \\ fnstenv (%%rcx) - \\ fldenv (%%rcx) - \\ stmxcsr %[mxcsr_offset:c](%%rdi) - \\ leaq %[stack_offset:c](%%rdi), %%rsi - \\ movq %%rdi, %%r8 - \\ xorl %%edi, %%edi - \\ movl %[sigaltstack], %%eax - \\ syscall - \\ testq %%rax, %%rax - \\ jnz 0f - \\ movl %[sigprocmask], %%eax - \\ xorl %%esi, %%esi - \\ leaq %[sigmask_offset:c](%%r8), %%rdx - \\ movl %[sigset_size], %%r10d - \\ syscall - \\0: - \\ retq - : - : [flags_offset] "i" (@offsetOf(ucontext_t, "flags")), - [link_offset] "i" (@offsetOf(ucontext_t, "link")), - [r8_offset] "i" (comptime gpRegisterOffset(REG.R8)), - [r9_offset] "i" (comptime gpRegisterOffset(REG.R9)), - [r10_offset] "i" (comptime gpRegisterOffset(REG.R10)), - [r11_offset] "i" (comptime gpRegisterOffset(REG.R11)), - [r12_offset] "i" (comptime gpRegisterOffset(REG.R12)), - [r13_offset] "i" (comptime gpRegisterOffset(REG.R13)), - [r14_offset] "i" (comptime gpRegisterOffset(REG.R14)), - [r15_offset] "i" (comptime gpRegisterOffset(REG.R15)), - [rdi_offset] "i" (comptime gpRegisterOffset(REG.RDI)), - [rsi_offset] "i" (comptime gpRegisterOffset(REG.RSI)), - [rbp_offset] "i" (comptime gpRegisterOffset(REG.RBP)), - [rbx_offset] "i" (comptime gpRegisterOffset(REG.RBX)), - [rdx_offset] "i" (comptime gpRegisterOffset(REG.RDX)), - [rax_offset] "i" (comptime gpRegisterOffset(REG.RAX)), - [rcx_offset] "i" (comptime gpRegisterOffset(REG.RCX)), - [rsp_offset] "i" (comptime gpRegisterOffset(REG.RSP)), - [rip_offset] "i" (comptime gpRegisterOffset(REG.RIP)), - [efl_offset] "i" (comptime gpRegisterOffset(REG.EFL)), - [fpstate_offset] "i" (@offsetOf(ucontext_t, "mcontext") + @offsetOf(mcontext_t, "fpregs")), - [fpmem_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem")), - [mxcsr_offset] "i" (@offsetOf(ucontext_t, "fpregs_mem") + @offsetOf(fpstate, "mxcsr")), - [sigaltstack] "i" (@intFromEnum(linux.SYS.sigaltstack)), - [stack_offset] "i" (@offsetOf(ucontext_t, "stack")), - [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)), - [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")), - [sigset_size] "i" (@sizeOf(sigset_t)), - : .{ .cc = true, .memory = true, .rax = true, .rcx = true, .rdx = true, .rdi = true, .rsi = true, .r8 = true, .r10 = true, .r11 = true }); -} - -pub inline fn getcontext(context: *ucontext_t) usize { - // This method is used so that getContextInternal can control - // its prologue in order to read RSP from a constant offset - // An aligned stack is not needed for getContextInternal. - var clobber_rdi: usize = undefined; - return asm volatile ( - \\ callq %[getContextInternal:P] - : [_] "={rax}" (-> usize), - [_] "={rdi}" (clobber_rdi), - : [_] "{rdi}" (context), - [getContextInternal] "X" (&getContextInternal), - : .{ .cc = true, .memory = true, .rcx = true, .rdx = true, .rsi = true, .r8 = true, .r10 = true, .r11 = true }); -} |
