authorgravatar for pat.github@tullmann.orgPat Tullmann <pat.github@tullmann.org> 2025-04-13 16:14:25-07:00
committergravatar for pat.github@tullmann.orgPat Tullmann <pat.github@tullmann.org> 2025-04-30 20:32:04-07:00
log298b1886b2b35b168589026d075d14206d6e3430
tree20316f3fc1a112b577dbc5ab825baffa574db2c0
parent51654aea877d1da92f7f2ad2c6c781c73e08d7f0

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.

3 files changed, 88 insertions(+), 42 deletions(-)

lib/std/os/linux.zig+37-32
...@@ -1745,8 +1745,9 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*...@@ -1745,8 +1745,9 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*
1745 return syscall4(.rt_sigprocmask, flags, @intFromPtr(set), @intFromPtr(oldset), NSIG / 8);1745 return syscall4(.rt_sigprocmask, flags, @intFromPtr(set), @intFromPtr(oldset), NSIG / 8);
1746}1746}
17471747
1748pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {1748pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
1749 assert(sig >= 1);1749 assert(sig > 0);
1750 assert(sig < NSIG);
1750 assert(sig != SIG.KILL);1751 assert(sig != SIG.KILL);
1751 assert(sig != SIG.STOP);1752 assert(sig != SIG.STOP);
17521753
...@@ -1755,14 +1756,15 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -1755,14 +1756,15 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
1755 const mask_size = @sizeOf(@TypeOf(ksa.mask));1756 const mask_size = @sizeOf(@TypeOf(ksa.mask));
17561757
1757 if (act) |new| {1758 if (act) |new| {
1759 // Zig needs to install our arch restorer function with any signal handler, so
1760 // must copy the Sigaction struct
1758 const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) &restore_rt else &restore;1761 const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) &restore_rt else &restore;
1759 ksa = k_sigaction{1762 ksa = k_sigaction{
1760 .handler = new.handler.handler,1763 .handler = new.handler.handler,
1761 .flags = new.flags | SA.RESTORER,1764 .flags = new.flags | SA.RESTORER,
1762 .mask = undefined,1765 .mask = new.mask,
1763 .restorer = @ptrCast(restorer_fn),1766 .restorer = @ptrCast(restorer_fn),
1764 };1767 };
1765 @memcpy(@as([*]u8, @ptrCast(&ksa.mask))[0..mask_size], @as([*]const u8, @ptrCast(&new.mask)));
1766 }1768 }
17671769
1768 const ksa_arg = if (act != null) @intFromPtr(&ksa) else 0;1770 const ksa_arg = if (act != null) @intFromPtr(&ksa) else 0;
...@@ -1777,8 +1779,8 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -1777,8 +1779,8 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
17771779
1778 if (oact) |old| {1780 if (oact) |old| {
1779 old.handler.handler = oldksa.handler;1781 old.handler.handler = oldksa.handler;
1780 old.flags = @as(c_uint, @truncate(oldksa.flags));1782 old.flags = oldksa.flags;
1781 @memcpy(@as([*]u8, @ptrCast(&old.mask))[0..mask_size], @as([*]const u8, @ptrCast(&oldksa.mask)));1783 old.mask = oldksa.mask;
1782 }1784 }
17831785
1784 return 0;1786 return 0;
...@@ -1786,25 +1788,31 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact...@@ -1786,25 +1788,31 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
17861788
1787const usize_bits = @typeInfo(usize).int.bits;1789const usize_bits = @typeInfo(usize).int.bits;
17881790
1789pub const sigset_t = [1024 / 32]u32;1791/// Defined as one greater than the largest defined signal number.
1792pub const NSIG = if (is_mips) 128 else 65;
17901793
1791const sigset_len = @typeInfo(sigset_t).array.len;1794/// Linux kernel's sigset_t. This is logically 64-bit on most
1795/// architectures, but 128-bit on MIPS. Contrast with the 1024-bit
1796/// sigset_t exported by the glibc and musl library ABIs.
1797pub const sigset_t = [(NSIG - 1 + 7) / @bitSizeOf(SigsetElement)]SigsetElement;
17921798
1793/// Empty set to initialize sigset_t instances from.1799const SigsetElement = c_ulong;
1794pub const empty_sigset: sigset_t = [_]u32{0} ** sigset_len;
17951800
1796pub const filled_sigset: sigset_t = [_]u32{0x7fff_ffff} ++ [_]u32{0} ** (sigset_len - 1);1801const sigset_len = @typeInfo(sigset_t).array.len;
1802
1803/// Empty set to initialize sigset_t instances from. No need for `sigemptyset`.
1804pub const empty_sigset: sigset_t = [_]SigsetElement{0} ** sigset_len;
17971805
1798pub const all_mask: sigset_t = [_]u32{0xffff_ffff} ** sigset_len;1806/// Filled set to initialize sigset_t instances from. No need for `sigfillset`.
1807pub const filled_sigset: sigset_t = [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
17991808
1800fn sigset_bit_index(sig: usize) struct { word: usize, mask: u32 } {1809fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } {
1801 assert(sig > 0);1810 assert(sig > 0);
1802 assert(sig < NSIG);1811 assert(sig < NSIG);
1803 const bit = sig - 1;1812 const bit = sig - 1;
1804 const shift = @as(u5, @truncate(bit % 32));
1805 return .{1813 return .{
1806 .word = bit / 32,1814 .word = bit / @bitSizeOf(SigsetElement),
1807 .mask = @as(u32, 1) << shift,1815 .mask = @as(SigsetElement, 1) << @truncate(bit % @bitSizeOf(SigsetElement)),
1808 };1816 };
1809}1817}
18101818
...@@ -5479,38 +5487,33 @@ pub const TFD = switch (native_arch) {...@@ -5479,38 +5487,33 @@ pub const TFD = switch (native_arch) {
5479 },5487 },
5480};5488};
54815489
5482/// NSIG is the total number of signals defined.
5483/// As signal numbers are sequential, NSIG is one greater than the largest defined signal number.
5484pub const NSIG = if (is_mips) 128 else 65;
5485
5486const k_sigaction_funcs = struct {5490const k_sigaction_funcs = struct {
5487 const handler = ?*align(1) const fn (i32) callconv(.c) void;5491 const handler = ?*align(1) const fn (i32) callconv(.c) void;
5488 const restorer = *const fn () callconv(.c) void;5492 const restorer = *const fn () callconv(.c) void;
5489};5493};
54905494
5495/// Kernel sigaction struct, as expected by the `rt_sigaction` syscall. Includes restorer.
5491pub const k_sigaction = switch (native_arch) {5496pub const k_sigaction = switch (native_arch) {
5492 .mips, .mipsel => extern struct {5497 .mips, .mipsel, .mips64, .mips64el => extern struct {
5493 flags: c_uint,5498 flags: c_uint,
5494 handler: k_sigaction_funcs.handler,5499 handler: k_sigaction_funcs.handler,
5495 mask: [4]c_ulong,5500 mask: sigset_t,
5496 restorer: k_sigaction_funcs.restorer,
5497 },
5498 .mips64, .mips64el => extern struct {
5499 flags: c_uint,
5500 handler: k_sigaction_funcs.handler,
5501 mask: [2]c_ulong,
5502 restorer: k_sigaction_funcs.restorer,5501 restorer: k_sigaction_funcs.restorer,
5503 },5502 },
5504 else => extern struct {5503 else => extern struct {
5505 handler: k_sigaction_funcs.handler,5504 handler: k_sigaction_funcs.handler,
5506 flags: c_ulong,5505 flags: c_ulong,
5507 restorer: k_sigaction_funcs.restorer,5506 restorer: k_sigaction_funcs.restorer,
5508 mask: [2]c_uint,5507 mask: sigset_t,
5509 },5508 },
5510};5509};
55115510
5511/// Kernel Sigaction wrapper for the actual ABI `k_sigaction`. The Zig
5512/// linux.zig wrapper library still does some pre-processing on
5513/// sigaction() calls (to add the `restorer` field).
5514///
5512/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.5515/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
5513pub const Sigaction = extern struct {5516pub const Sigaction = struct {
5514 pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;5517 pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;
5515 pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;5518 pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;
55165519
...@@ -5519,8 +5522,10 @@ pub const Sigaction = extern struct {...@@ -5519,8 +5522,10 @@ pub const Sigaction = extern struct {
5519 sigaction: ?sigaction_fn,5522 sigaction: ?sigaction_fn,
5520 },5523 },
5521 mask: sigset_t,5524 mask: sigset_t,
5522 flags: c_uint,5525 flags: switch (native_arch) {
5523 restorer: ?*const fn () callconv(.c) void = null,5526 .mips, .mipsel, .mips64, .mips64el => c_uint,
5527 else => c_ulong,
5528 },
5524};5529};
55255530
5526pub const SFD = struct {5531pub const SFD = struct {
lib/std/os/linux/test.zig+40-7
...@@ -126,6 +126,8 @@ test "fadvise" {...@@ -126,6 +126,8 @@ test "fadvise" {
126}126}
127127
128test "sigset_t" {128test "sigset_t" {
129 std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
130
129 var sigset = linux.empty_sigset;131 var sigset = linux.empty_sigset;
130132
131 // See that none are set, then set each one, see that they're all set, then133 // See that none are set, then set each one, see that they're all set, then
...@@ -138,6 +140,7 @@ test "sigset_t" {...@@ -138,6 +140,7 @@ test "sigset_t" {
138 }140 }
139 for (1..linux.NSIG) |i| {141 for (1..linux.NSIG) |i| {
140 try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);142 try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
143 try expectEqual(linux.sigismember(&linux.filled_sigset, @truncate(i)), true);
141 try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);144 try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);
142 }145 }
143 for (1..linux.NSIG) |i| {146 for (1..linux.NSIG) |i| {
...@@ -147,22 +150,52 @@ test "sigset_t" {...@@ -147,22 +150,52 @@ test "sigset_t" {
147 try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);150 try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
148 }151 }
149152
153 // Kernel sigset_t is either 2+ 32-bit values or 1+ 64-bit value(s).
154 const sigset_len = @typeInfo(linux.sigset_t).array.len;
155 const sigset_elemis64 = 64 == @bitSizeOf(@typeInfo(linux.sigset_t).array.child);
156
150 linux.sigaddset(&sigset, 1);157 linux.sigaddset(&sigset, 1);
151 try expectEqual(sigset[0], 1);158 try expectEqual(sigset[0], 1);
152 try expectEqual(sigset[1], 0);159 if (sigset_len > 1) {
160 try expectEqual(sigset[1], 0);
161 }
153162
154 linux.sigaddset(&sigset, 31);163 linux.sigaddset(&sigset, 31);
155 try expectEqual(sigset[0], 0x4000_0001);164 try expectEqual(sigset[0], 0x4000_0001);
156 try expectEqual(sigset[1], 0);165 if (sigset_len > 1) {
166 try expectEqual(sigset[1], 0);
167 }
157168
158 linux.sigaddset(&sigset, 36);169 linux.sigaddset(&sigset, 36);
159 try expectEqual(sigset[0], 0x4000_0001);170 if (sigset_elemis64) {
160 try expectEqual(sigset[1], 0x8);171 try expectEqual(sigset[0], 0x8_4000_0001);
172 } else {
173 try expectEqual(sigset[0], 0x4000_0001);
174 try expectEqual(sigset[1], 0x8);
175 }
161176
162 linux.sigaddset(&sigset, 64);177 linux.sigaddset(&sigset, 64);
163 try expectEqual(sigset[0], 0x4000_0001);178 if (sigset_elemis64) {
164 try expectEqual(sigset[1], 0x8000_0008);179 try expectEqual(sigset[0], 0x8000_0008_4000_0001);
165 try expectEqual(sigset[2], 0);180 } else {
181 try expectEqual(sigset[0], 0x4000_0001);
182 try expectEqual(sigset[1], 0x8000_0008);
183 }
184}
185
186test "filled_sigset" {
187 // unlike the C library, all the signals are set in the kernel-level fillset
188 const sigset = linux.filled_sigset;
189 for (1..linux.NSIG) |i| {
190 try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
191 }
192}
193
194test "empty_sigset" {
195 const sigset = linux.empty_sigset;
196 for (1..linux.NSIG) |i| {
197 try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
198 }
166}199}
167200
168test "sysinfo" {201test "sysinfo" {
lib/std/os/linux/x86_64.zig+11-3
...@@ -369,13 +369,21 @@ pub const mcontext_t = extern struct {...@@ -369,13 +369,21 @@ pub const mcontext_t = extern struct {
369 reserved1: [8]usize = undefined,369 reserved1: [8]usize = undefined,
370};370};
371371
372/// ucontext_t is part of the state pushed on the stack by the kernel for
373/// a signal handler. And also a subset of the state returned from the
374/// makecontext/getcontext/swapcontext POSIX APIs.
375///
376/// Currently this structure matches the glibc/musl layout. It contains a
377/// 1024-bit signal mask, and `fpregs_mem`. This structure should be
378/// split into one for the kernel ABI and c.zig should define a glibc/musl
379/// compatible structure.
372pub const ucontext_t = extern struct {380pub const ucontext_t = extern struct {
373 flags: usize,381 flags: usize,
374 link: ?*ucontext_t,382 link: ?*ucontext_t,
375 stack: stack_t,383 stack: stack_t,
376 mcontext: mcontext_t,384 mcontext: mcontext_t,
377 sigmask: sigset_t,385 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask.
378 fpregs_mem: [64]usize,386 fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t
379};387};
380388
381fn gpRegisterOffset(comptime reg_index: comptime_int) usize {389fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
...@@ -455,7 +463,7 @@ fn getContextInternal() callconv(.naked) usize {...@@ -455,7 +463,7 @@ fn getContextInternal() callconv(.naked) usize {
455 [stack_offset] "i" (@offsetOf(ucontext_t, "stack")),463 [stack_offset] "i" (@offsetOf(ucontext_t, "stack")),
456 [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),464 [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
457 [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),465 [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),
458 [sigset_size] "i" (linux.NSIG / 8),466 [sigset_size] "i" (@sizeOf(sigset_t)),
459 : "cc", "memory", "rax", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"467 : "cc", "memory", "rax", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
460 );468 );
461}469}