aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os/linux
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-09-17 18:38:11 +0100
committermlugg <mlugg@mlugg.co.uk>2025-09-30 13:44:54 +0100
commita18fd41064493e742eacebc88e2afeadd54ff6f0 (patch)
tree1081fbd6d3c64cf1f583ae3188ab05e0320f03d9 /lib/std/os/linux
parentb578cca022f4c9ce94439e2ee795639b3a23c8f5 (diff)
downloadzig-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.zig3
-rw-r--r--lib/std/os/linux/arm.zig3
-rw-r--r--lib/std/os/linux/hexagon.zig3
-rw-r--r--lib/std/os/linux/loongarch64.zig3
-rw-r--r--lib/std/os/linux/m68k.zig3
-rw-r--r--lib/std/os/linux/mips.zig3
-rw-r--r--lib/std/os/linux/mips64.zig3
-rw-r--r--lib/std/os/linux/powerpc.zig3
-rw-r--r--lib/std/os/linux/powerpc64.zig3
-rw-r--r--lib/std/os/linux/riscv32.zig3
-rw-r--r--lib/std/os/linux/riscv64.zig3
-rw-r--r--lib/std/os/linux/s390x.zig3
-rw-r--r--lib/std/os/linux/sparc64.zig3
-rw-r--r--lib/std/os/linux/x86.zig14
-rw-r--r--lib/std/os/linux/x86_64.zig95
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 });
-}