aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os/linux
diff options
context:
space:
mode:
authorPat Tullmann <pat.github@tullmann.org>2025-04-13 16:14:25 -0700
committerPat Tullmann <pat.github@tullmann.org>2025-04-30 20:32:04 -0700
commit298b1886b2b35b168589026d075d14206d6e3430 (patch)
tree20316f3fc1a112b577dbc5ab825baffa574db2c0 /lib/std/os/linux
parent51654aea877d1da92f7f2ad2c6c781c73e08d7f0 (diff)
downloadzig-298b1886b2b35b168589026d075d14206d6e3430.tar.gz
zig-298b1886b2b35b168589026d075d14206d6e3430.zip
std.os.linux: export kernel-sized sigset_t and operations
The kernel ABI sigset_t is smaller than the glibc one. Define the right-sized sigset_t and fixup the sigaction() wrapper to leverage it. The Sigaction wrapper here is not an ABI, so relax it (drop the "extern" and the "restorer" fields), the existing `k_sigaction` is the ABI sigaction struct. Linux defines `sigset_t` with a c_ulong, so it can be 32-bit or 64-bit, depending on the platform. This can make a difference on big-endian systems. Patch up `ucontext_t` so that this change doesn't impact its layout. AFAICT, its currently the glibc layout.
Diffstat (limited to 'lib/std/os/linux')
-rw-r--r--lib/std/os/linux/test.zig47
-rw-r--r--lib/std/os/linux/x86_64.zig14
2 files changed, 51 insertions, 10 deletions
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
index fa6f822329..097c5a388c 100644
--- a/lib/std/os/linux/test.zig
+++ b/lib/std/os/linux/test.zig
@@ -126,6 +126,8 @@ test "fadvise" {
}
test "sigset_t" {
+ std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
+
var sigset = linux.empty_sigset;
// See that none are set, then set each one, see that they're all set, then
@@ -138,6 +140,7 @@ test "sigset_t" {
}
for (1..linux.NSIG) |i| {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
+ try expectEqual(linux.sigismember(&linux.filled_sigset, @truncate(i)), true);
try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);
}
for (1..linux.NSIG) |i| {
@@ -147,22 +150,52 @@ test "sigset_t" {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
}
+ // Kernel sigset_t is either 2+ 32-bit values or 1+ 64-bit value(s).
+ const sigset_len = @typeInfo(linux.sigset_t).array.len;
+ const sigset_elemis64 = 64 == @bitSizeOf(@typeInfo(linux.sigset_t).array.child);
+
linux.sigaddset(&sigset, 1);
try expectEqual(sigset[0], 1);
- try expectEqual(sigset[1], 0);
+ if (sigset_len > 1) {
+ try expectEqual(sigset[1], 0);
+ }
linux.sigaddset(&sigset, 31);
try expectEqual(sigset[0], 0x4000_0001);
- try expectEqual(sigset[1], 0);
+ if (sigset_len > 1) {
+ try expectEqual(sigset[1], 0);
+ }
linux.sigaddset(&sigset, 36);
- try expectEqual(sigset[0], 0x4000_0001);
- try expectEqual(sigset[1], 0x8);
+ if (sigset_elemis64) {
+ try expectEqual(sigset[0], 0x8_4000_0001);
+ } else {
+ try expectEqual(sigset[0], 0x4000_0001);
+ try expectEqual(sigset[1], 0x8);
+ }
linux.sigaddset(&sigset, 64);
- try expectEqual(sigset[0], 0x4000_0001);
- try expectEqual(sigset[1], 0x8000_0008);
- try expectEqual(sigset[2], 0);
+ if (sigset_elemis64) {
+ try expectEqual(sigset[0], 0x8000_0008_4000_0001);
+ } else {
+ try expectEqual(sigset[0], 0x4000_0001);
+ try expectEqual(sigset[1], 0x8000_0008);
+ }
+}
+
+test "filled_sigset" {
+ // unlike the C library, all the signals are set in the kernel-level fillset
+ const sigset = linux.filled_sigset;
+ for (1..linux.NSIG) |i| {
+ try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
+ }
+}
+
+test "empty_sigset" {
+ const sigset = linux.empty_sigset;
+ for (1..linux.NSIG) |i| {
+ try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
+ }
}
test "sysinfo" {
diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig
index 90147876c8..7ef86fdcf8 100644
--- a/lib/std/os/linux/x86_64.zig
+++ b/lib/std/os/linux/x86_64.zig
@@ -369,13 +369,21 @@ pub const mcontext_t = extern struct {
reserved1: [8]usize = undefined,
};
+/// ucontext_t is part of the state pushed on the stack by the kernel for
+/// a signal handler. And also a subset of the state returned from the
+/// makecontext/getcontext/swapcontext POSIX APIs.
+///
+/// Currently this structure matches the glibc/musl layout. It contains a
+/// 1024-bit signal mask, and `fpregs_mem`. This structure should be
+/// split into one for the kernel ABI and c.zig should define a glibc/musl
+/// compatible structure.
pub const ucontext_t = extern struct {
flags: usize,
link: ?*ucontext_t,
stack: stack_t,
mcontext: mcontext_t,
- sigmask: sigset_t,
- fpregs_mem: [64]usize,
+ 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 {
@@ -455,7 +463,7 @@ fn getContextInternal() callconv(.naked) usize {
[stack_offset] "i" (@offsetOf(ucontext_t, "stack")),
[sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
[sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),
- [sigset_size] "i" (linux.NSIG / 8),
+ [sigset_size] "i" (@sizeOf(sigset_t)),
: "cc", "memory", "rax", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
);
}