diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 00390c0800..3e5c808f56 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1745,8 +1745,9 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?* return syscall4(.rt_sigprocmask, flags, @intFromPtr(set), @intFromPtr(oldset), NSIG / 8); } -pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize { - assert(sig >= 1); +pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize { + assert(sig > 0); + assert(sig < NSIG); assert(sig != SIG.KILL); assert(sig != SIG.STOP); @@ -1755,14 +1756,15 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact const mask_size = @sizeOf(@TypeOf(ksa.mask)); if (act) |new| { + // Zig needs to install our arch restorer function with any signal handler, so + // must copy the Sigaction struct const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) &restore_rt else &restore; ksa = k_sigaction{ .handler = new.handler.handler, .flags = new.flags | SA.RESTORER, - .mask = undefined, + .mask = new.mask, .restorer = @ptrCast(restorer_fn), }; - @memcpy(@as([*]u8, @ptrCast(&ksa.mask))[0..mask_size], @as([*]const u8, @ptrCast(&new.mask))); } 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 if (oact) |old| { old.handler.handler = oldksa.handler; - old.flags = @as(c_uint, @truncate(oldksa.flags)); - @memcpy(@as([*]u8, @ptrCast(&old.mask))[0..mask_size], @as([*]const u8, @ptrCast(&oldksa.mask))); + old.flags = oldksa.flags; + old.mask = oldksa.mask; } return 0; @@ -1786,25 +1788,31 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact const usize_bits = @typeInfo(usize).int.bits; -pub const sigset_t = [1024 / 32]u32; +/// Defined as one greater than the largest defined signal number. +pub const NSIG = if (is_mips) 128 else 65; + +/// Linux kernel's sigset_t. This is logically 64-bit on most +/// architectures, but 128-bit on MIPS. Contrast with the 1024-bit +/// sigset_t exported by the glibc and musl library ABIs. +pub const sigset_t = [(NSIG - 1 + 7) / @bitSizeOf(SigsetElement)]SigsetElement; + +const SigsetElement = c_ulong; const sigset_len = @typeInfo(sigset_t).array.len; -/// Empty set to initialize sigset_t instances from. -pub const empty_sigset: sigset_t = [_]u32{0} ** sigset_len; +/// Empty set to initialize sigset_t instances from. No need for `sigemptyset`. +pub const empty_sigset: sigset_t = [_]SigsetElement{0} ** sigset_len; -pub const filled_sigset: sigset_t = [_]u32{0x7fff_ffff} ++ [_]u32{0} ** (sigset_len - 1); +/// Filled set to initialize sigset_t instances from. No need for `sigfillset`. +pub const filled_sigset: sigset_t = [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len; -pub const all_mask: sigset_t = [_]u32{0xffff_ffff} ** sigset_len; - -fn sigset_bit_index(sig: usize) struct { word: usize, mask: u32 } { +fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } { assert(sig > 0); assert(sig < NSIG); const bit = sig - 1; - const shift = @as(u5, @truncate(bit % 32)); return .{ - .word = bit / 32, - .mask = @as(u32, 1) << shift, + .word = bit / @bitSizeOf(SigsetElement), + .mask = @as(SigsetElement, 1) << @truncate(bit % @bitSizeOf(SigsetElement)), }; } @@ -5479,38 +5487,33 @@ pub const TFD = switch (native_arch) { }, }; -/// NSIG is the total number of signals defined. -/// As signal numbers are sequential, NSIG is one greater than the largest defined signal number. -pub const NSIG = if (is_mips) 128 else 65; - const k_sigaction_funcs = struct { const handler = ?*align(1) const fn (i32) callconv(.c) void; const restorer = *const fn () callconv(.c) void; }; +/// Kernel sigaction struct, as expected by the `rt_sigaction` syscall. Includes restorer. pub const k_sigaction = switch (native_arch) { - .mips, .mipsel => extern struct { + .mips, .mipsel, .mips64, .mips64el => extern struct { flags: c_uint, handler: k_sigaction_funcs.handler, - mask: [4]c_ulong, - restorer: k_sigaction_funcs.restorer, - }, - .mips64, .mips64el => extern struct { - flags: c_uint, - handler: k_sigaction_funcs.handler, - mask: [2]c_ulong, + mask: sigset_t, restorer: k_sigaction_funcs.restorer, }, else => extern struct { handler: k_sigaction_funcs.handler, flags: c_ulong, restorer: k_sigaction_funcs.restorer, - mask: [2]c_uint, + mask: sigset_t, }, }; +/// Kernel Sigaction wrapper for the actual ABI `k_sigaction`. The Zig +/// linux.zig wrapper library still does some pre-processing on +/// sigaction() calls (to add the `restorer` field). +/// /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { +pub const Sigaction = struct { pub const handler_fn = *align(1) const fn (i32) callconv(.c) void; pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void; @@ -5519,8 +5522,10 @@ pub const Sigaction = extern struct { sigaction: ?sigaction_fn, }, mask: sigset_t, - flags: c_uint, - restorer: ?*const fn () callconv(.c) void = null, + flags: switch (native_arch) { + .mips, .mipsel, .mips64, .mips64el => c_uint, + else => c_ulong, + }, }; pub const SFD = struct { 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" ); }