authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-05-01 21:29:23+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-05-01 21:29:23+02:00
logad9cb401124e4b2d59e49bed2736aad7813e4f1d
tree7bafd4a5dac886e67bb8f63ecfa22ae3bb2084c1
parent971d19a3b2ca97b9acd00d74556a57a335898ed8
parenta55ecd7532e7ae015afe5cb5bc16b89376a4bf56
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23601 from rootbeer/sig-split

Split glibc and linux sigset_t ABIs and the accessor functions

22 files changed, 393 insertions(+), 155 deletions(-)

lib/std/Progress.zig+2-2
......@@ -410,9 +410,9 @@ pub fn start(options: Options) Node {
410410 }
411411
412412 if (have_sigwinch) {
413 var act: posix.Sigaction = .{
413 const act: posix.Sigaction = .{
414414 .handler = .{ .sigaction = handleSigWinch },
415 .mask = posix.empty_sigset,
415 .mask = posix.sigemptyset(),
416416 .flags = (posix.SA.SIGINFO | posix.SA.RESTART),
417417 };
418418 posix.sigaction(posix.SIG.WINCH, &act, null);
lib/std/c.zig+34-19
......@@ -3115,6 +3115,21 @@ pub const SYS = switch (native_os) {
31153115 .linux => linux.SYS,
31163116 else => void,
31173117};
3118
3119/// A common format for the Sigaction struct across a variety of Linux flavors.
3120const common_linux_Sigaction = extern struct {
3121 pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;
3122 pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;
3123
3124 handler: extern union {
3125 handler: ?handler_fn,
3126 sigaction: ?sigaction_fn,
3127 },
3128 mask: sigset_t,
3129 flags: c_uint,
3130 restorer: ?*const fn () callconv(.c) void = null, // C library will fill this in
3131};
3132
31183133/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name.
31193134pub const Sigaction = switch (native_os) {
31203135 .linux => switch (native_arch) {
......@@ -3123,7 +3138,7 @@ pub const Sigaction = switch (native_os) {
31233138 .mips64,
31243139 .mips64el,
31253140 => if (builtin.target.abi.isMusl())
3126 linux.Sigaction
3141 common_linux_Sigaction
31273142 else if (builtin.target.ptrBitWidth() == 64) extern struct {
31283143 pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;
31293144 pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;
......@@ -3160,8 +3175,8 @@ pub const Sigaction = switch (native_os) {
31603175 flags: c_uint,
31613176 restorer: ?*const fn () callconv(.c) void = null,
31623177 mask: sigset_t,
3163 } else linux.Sigaction,
3164 else => linux.Sigaction,
3178 } else common_linux_Sigaction,
3179 else => common_linux_Sigaction,
31653180 },
31663181 .emscripten => emscripten.Sigaction,
31673182 .netbsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {
......@@ -4518,27 +4533,18 @@ pub const siginfo_t = switch (native_os) {
45184533 else => void,
45194534};
45204535pub const sigset_t = switch (native_os) {
4521 .linux => linux.sigset_t,
4536 .linux => [1024 / @bitSizeOf(c_ulong)]c_ulong, // glibc and musl present a 1024-bit sigset_t, while kernel's is 128-bit or less.
45224537 .emscripten => emscripten.sigset_t,
45234538 // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L19
4524 .openbsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => u32,
4539 .openbsd, .serenity => u32,
4540 .macos, .ios, .tvos, .watchos, .visionos => darwin.sigset_t,
45254541 .dragonfly, .netbsd, .solaris, .illumos, .freebsd => extern struct {
45264542 __bits: [SIG.WORDS]u32,
45274543 },
45284544 .haiku => u64,
45294545 else => u0,
45304546};
4531pub const empty_sigset: sigset_t = switch (native_os) {
4532 .linux => linux.empty_sigset,
4533 .emscripten => emscripten.empty_sigset,
4534 .dragonfly, .netbsd, .solaris, .illumos, .freebsd => .{ .__bits = [_]u32{0} ** SIG.WORDS },
4535 else => 0,
4536};
4537pub const filled_sigset = switch (native_os) {
4538 .linux => linux.filled_sigset,
4539 .haiku => ~@as(sigset_t, 0),
4540 else => 0,
4541};
4547
45424548pub const sigval = switch (native_os) {
45434549 .linux => linux.sigval,
45444550 // https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L22-L25
......@@ -6665,7 +6671,7 @@ pub const timezone = switch (native_os) {
66656671};
66666672
66676673pub const ucontext_t = switch (native_os) {
6668 .linux => linux.ucontext_t,
6674 .linux => linux.ucontext_t, // std.os.linux.ucontext_t is currently glibc-compatible, but it should probably not be.
66696675 .emscripten => emscripten.ucontext_t,
66706676 .macos, .ios, .tvos, .watchos, .visionos => extern struct {
66716677 onstack: c_int,
......@@ -9596,6 +9602,7 @@ pub const NSIG = switch (native_os) {
95969602 .windows => 23,
95979603 .haiku => 65,
95989604 .netbsd, .freebsd => 32,
9605 .macos => darwin.NSIG,
95999606 .solaris, .illumos => 75,
96009607 // https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h#L42
96019608 .openbsd, .serenity => 33,
......@@ -10345,6 +10352,11 @@ pub const sigfillset = switch (native_os) {
1034510352 else => private.sigfillset,
1034610353};
1034710354
10355pub const sigaddset = private.sigaddset;
10356pub const sigemptyset = private.sigemptyset;
10357pub const sigdelset = private.sigdelset;
10358pub const sigismember = private.sigismember;
10359
1034810360pub const sigprocmask = switch (native_os) {
1034910361 .netbsd => private.__sigprocmask14,
1035010362 else => private.sigprocmask,
......@@ -11025,7 +11037,6 @@ pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np;
1102511037pub const pthread_get_qos_class_np = darwin.pthread_get_qos_class_np;
1102611038pub const pthread_set_qos_class_self_np = darwin.pthread_set_qos_class_self_np;
1102711039pub const ptrace = darwin.ptrace;
11028pub const sigaddset = darwin.sigaddset;
1102911040pub const task_for_pid = darwin.task_for_pid;
1103011041pub const task_get_exception_ports = darwin.task_get_exception_ports;
1103111042pub const task_info = darwin.task_info;
......@@ -11148,7 +11159,11 @@ const private = struct {
1114811159 extern "c" fn sched_yield() c_int;
1114911160 extern "c" fn sendfile(out_fd: fd_t, in_fd: fd_t, offset: ?*off_t, count: usize) isize;
1115011161 extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
11151 extern "c" fn sigfillset(set: ?*sigset_t) void;
11162 extern "c" fn sigdelset(set: ?*sigset_t, signo: c_int) c_int;
11163 extern "c" fn sigaddset(set: ?*sigset_t, signo: c_int) c_int;
11164 extern "c" fn sigfillset(set: ?*sigset_t) c_int;
11165 extern "c" fn sigemptyset(set: ?*sigset_t) c_int;
11166 extern "c" fn sigismember(set: ?*const sigset_t, signo: c_int) c_int;
1115211167 extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
1115311168 extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
1115411169 extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
lib/std/c/darwin.zig+5-4
......@@ -10,7 +10,6 @@ const mode_t = std.c.mode_t;
1010const off_t = std.c.off_t;
1111const pid_t = std.c.pid_t;
1212const pthread_attr_t = std.c.pthread_attr_t;
13const sigset_t = std.c.sigset_t;
1413const timespec = std.c.timespec;
1514const sf_hdtr = std.c.sf_hdtr;
1615
......@@ -840,9 +839,11 @@ pub extern "c" fn sendfile(
840839 flags: u32,
841840) c_int;
842841
843pub fn sigaddset(set: *sigset_t, signo: u5) void {
844 set.* |= @as(u32, 1) << (signo - 1);
845}
842// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/_types.h#L74
843pub const sigset_t = u32;
844
845// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/signal.h#L76
846pub const NSIG = 32;
846847
847848pub const qos_class_t = enum(c_uint) {
848849 /// highest priority QOS class for critical tasks
lib/std/debug.zig+4-5
......@@ -1387,12 +1387,11 @@ pub fn attachSegfaultHandler() void {
13871387 windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
13881388 return;
13891389 }
1390 var act = posix.Sigaction{
1390 const act = posix.Sigaction{
13911391 .handler = .{ .sigaction = handleSegfaultPosix },
1392 .mask = posix.empty_sigset,
1392 .mask = posix.sigemptyset(),
13931393 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
13941394 };
1395
13961395 updateSegfaultHandler(&act);
13971396}
13981397
......@@ -1404,9 +1403,9 @@ fn resetSegfaultHandler() void {
14041403 }
14051404 return;
14061405 }
1407 var act = posix.Sigaction{
1406 const act = posix.Sigaction{
14081407 .handler = .{ .handler = posix.SIG.DFL },
1409 .mask = posix.empty_sigset,
1408 .mask = posix.sigemptyset(),
14101409 .flags = 0,
14111410 };
14121411 updateSegfaultHandler(&act);
lib/std/os/emscripten.zig+3-1
......@@ -560,7 +560,9 @@ pub const Sigaction = extern struct {
560560};
561561
562562pub const sigset_t = [1024 / 32]u32;
563pub const empty_sigset = [_]u32{0} ** @typeInfo(sigset_t).array.len;
563pub fn sigemptyset() sigset_t {
564 return [_]u32{0} ** @typeInfo(sigset_t).array.len;
565}
564566pub const siginfo_t = extern struct {
565567 signo: i32,
566568 errno: i32,
lib/std/os/linux.zig+62-53
......@@ -1745,8 +1745,9 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*
17451745 return syscall4(.rt_sigprocmask, flags, @intFromPtr(set), @intFromPtr(oldset), NSIG / 8);
17461746}
17471747
1748pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
1749 assert(sig >= 1);
1748pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
1749 assert(sig > 0);
1750 assert(sig < NSIG);
17501751 assert(sig != SIG.KILL);
17511752 assert(sig != SIG.STOP);
17521753
......@@ -1755,14 +1756,15 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
17551756 const mask_size = @sizeOf(@TypeOf(ksa.mask));
17561757
17571758 if (act) |new| {
1759 // Zig needs to install our arch restorer function with any signal handler, so
1760 // must copy the Sigaction struct
17581761 const restorer_fn = if ((new.flags & SA.SIGINFO) != 0) &restore_rt else &restore;
17591762 ksa = k_sigaction{
17601763 .handler = new.handler.handler,
17611764 .flags = new.flags | SA.RESTORER,
1762 .mask = undefined,
1765 .mask = new.mask,
17631766 .restorer = @ptrCast(restorer_fn),
17641767 };
1765 @memcpy(@as([*]u8, @ptrCast(&ksa.mask))[0..mask_size], @as([*]const u8, @ptrCast(&new.mask)));
17661768 }
17671769
17681770 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
17771779
17781780 if (oact) |old| {
17791781 old.handler.handler = oldksa.handler;
1780 old.flags = @as(c_uint, @truncate(oldksa.flags));
1781 @memcpy(@as([*]u8, @ptrCast(&old.mask))[0..mask_size], @as([*]const u8, @ptrCast(&oldksa.mask)));
1782 old.flags = oldksa.flags;
1783 old.mask = oldksa.mask;
17821784 }
17831785
17841786 return 0;
......@@ -1786,25 +1788,35 @@ pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigact
17861788
17871789const 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.
1794pub const empty_sigset: sigset_t = [_]u32{0} ** sigset_len;
1799const SigsetElement = c_ulong;
17951800
1796pub const filled_sigset: sigset_t = [_]u32{0x7fff_ffff} ++ [_]u32{0} ** (sigset_len - 1);
1801const sigset_len = @typeInfo(sigset_t).array.len;
17971802
1798pub const all_mask: sigset_t = [_]u32{0xffff_ffff} ** sigset_len;
1803/// Zig's version of sigemptyset. Returns initialized sigset_t.
1804pub fn sigemptyset() sigset_t {
1805 return [_]SigsetElement{0} ** sigset_len;
1806}
17991807
1800fn sigset_bit_index(sig: usize) struct { word: usize, mask: u32 } {
1808/// Zig's version of sigfillset. Returns initalized sigset_t.
1809pub fn sigfillset() sigset_t {
1810 return [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
1811}
1812
1813fn sigset_bit_index(sig: usize) struct { word: usize, mask: SigsetElement } {
18011814 assert(sig > 0);
18021815 assert(sig < NSIG);
18031816 const bit = sig - 1;
1804 const shift = @as(u5, @truncate(bit % 32));
18051817 return .{
1806 .word = bit / 32,
1807 .mask = @as(u32, 1) << shift,
1818 .word = bit / @bitSizeOf(SigsetElement),
1819 .mask = @as(SigsetElement, 1) << @truncate(bit % @bitSizeOf(SigsetElement)),
18081820 };
18091821}
18101822
......@@ -3487,6 +3499,7 @@ pub const SIG = if (is_mips) struct {
34873499 pub const UNBLOCK = 2;
34883500 pub const SETMASK = 3;
34893501
3502 // https://github.com/torvalds/linux/blob/ca91b9500108d4cf083a635c2e11c884d5dd20ea/arch/mips/include/uapi/asm/signal.h#L25
34903503 pub const HUP = 1;
34913504 pub const INT = 2;
34923505 pub const QUIT = 3;
......@@ -3494,33 +3507,32 @@ pub const SIG = if (is_mips) struct {
34943507 pub const TRAP = 5;
34953508 pub const ABRT = 6;
34963509 pub const IOT = ABRT;
3497 pub const BUS = 7;
3510 pub const EMT = 7;
34983511 pub const FPE = 8;
34993512 pub const KILL = 9;
3500 pub const USR1 = 10;
3513 pub const BUS = 10;
35013514 pub const SEGV = 11;
3502 pub const USR2 = 12;
3515 pub const SYS = 12;
35033516 pub const PIPE = 13;
35043517 pub const ALRM = 14;
35053518 pub const TERM = 15;
3506 pub const STKFLT = 16;
3507 pub const CHLD = 17;
3508 pub const CONT = 18;
3509 pub const STOP = 19;
3510 pub const TSTP = 20;
3511 pub const TTIN = 21;
3512 pub const TTOU = 22;
3513 pub const URG = 23;
3514 pub const XCPU = 24;
3515 pub const XFSZ = 25;
3516 pub const VTALRM = 26;
3517 pub const PROF = 27;
3518 pub const WINCH = 28;
3519 pub const IO = 29;
3520 pub const POLL = 29;
3521 pub const PWR = 30;
3522 pub const SYS = 31;
3523 pub const UNUSED = SIG.SYS;
3519 pub const USR1 = 16;
3520 pub const USR2 = 17;
3521 pub const CHLD = 18;
3522 pub const PWR = 19;
3523 pub const WINCH = 20;
3524 pub const URG = 21;
3525 pub const IO = 22;
3526 pub const POLL = IO;
3527 pub const STOP = 23;
3528 pub const TSTP = 24;
3529 pub const CONT = 25;
3530 pub const TTIN = 26;
3531 pub const TTOU = 27;
3532 pub const VTALRM = 28;
3533 pub const PROF = 29;
3534 pub const XCPU = 30;
3535 pub const XFZ = 31;
35243536
35253537 pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize));
35263538 pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0);
......@@ -5479,38 +5491,33 @@ pub const TFD = switch (native_arch) {
54795491 },
54805492};
54815493
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
54865494const k_sigaction_funcs = struct {
54875495 const handler = ?*align(1) const fn (i32) callconv(.c) void;
54885496 const restorer = *const fn () callconv(.c) void;
54895497};
54905498
5499/// Kernel sigaction struct, as expected by the `rt_sigaction` syscall. Includes restorer.
54915500pub const k_sigaction = switch (native_arch) {
5492 .mips, .mipsel => extern struct {
5493 flags: c_uint,
5494 handler: k_sigaction_funcs.handler,
5495 mask: [4]c_ulong,
5496 restorer: k_sigaction_funcs.restorer,
5497 },
5498 .mips64, .mips64el => extern struct {
5501 .mips, .mipsel, .mips64, .mips64el => extern struct {
54995502 flags: c_uint,
55005503 handler: k_sigaction_funcs.handler,
5501 mask: [2]c_ulong,
5504 mask: sigset_t,
55025505 restorer: k_sigaction_funcs.restorer,
55035506 },
55045507 else => extern struct {
55055508 handler: k_sigaction_funcs.handler,
55065509 flags: c_ulong,
55075510 restorer: k_sigaction_funcs.restorer,
5508 mask: [2]c_uint,
5511 mask: sigset_t,
55095512 },
55105513};
55115514
5515/// Kernel Sigaction wrapper for the actual ABI `k_sigaction`. The Zig
5516/// linux.zig wrapper library still does some pre-processing on
5517/// sigaction() calls (to add the `restorer` field).
5518///
55125519/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
5513pub const Sigaction = extern struct {
5520pub const Sigaction = struct {
55145521 pub const handler_fn = *align(1) const fn (i32) callconv(.c) void;
55155522 pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.c) void;
55165523
......@@ -5519,8 +5526,10 @@ pub const Sigaction = extern struct {
55195526 sigaction: ?sigaction_fn,
55205527 },
55215528 mask: sigset_t,
5522 flags: c_uint,
5523 restorer: ?*const fn () callconv(.c) void = null,
5529 flags: switch (native_arch) {
5530 .mips, .mipsel, .mips64, .mips64el => c_uint,
5531 else => c_ulong,
5532 },
55245533};
55255534
55265535pub const SFD = struct {
lib/std/os/linux/aarch64.zig+1-1
......@@ -289,7 +289,7 @@ pub const ucontext_t = extern struct {
289289 flags: usize,
290290 link: ?*ucontext_t,
291291 stack: stack_t,
292 sigmask: sigset_t,
292 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
293293 mcontext: mcontext_t,
294294};
295295
lib/std/os/linux/arm.zig+1-1
......@@ -337,7 +337,7 @@ pub const ucontext_t = extern struct {
337337 link: ?*ucontext_t,
338338 stack: stack_t,
339339 mcontext: mcontext_t,
340 sigmask: sigset_t,
340 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
341341 regspace: [64]u64,
342342};
343343
lib/std/os/linux/loongarch64.zig+1-2
......@@ -264,8 +264,7 @@ pub const ucontext_t = extern struct {
264264 flags: c_ulong,
265265 link: ?*ucontext_t,
266266 stack: stack_t,
267 sigmask: sigset_t,
268 _pad: [1024 / 8 - @sizeOf(sigset_t)]u8,
267 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
269268 mcontext: mcontext_t,
270269};
271270
lib/std/os/linux/powerpc.zig+1-1
......@@ -341,7 +341,7 @@ pub const ucontext_t = extern struct {
341341 stack: stack_t,
342342 pad: [7]i32,
343343 regs: *mcontext_t,
344 sigmask: sigset_t,
344 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
345345 pad2: [3]i32,
346346 mcontext: mcontext_t,
347347};
lib/std/os/linux/powerpc64.zig+1-1
......@@ -337,7 +337,7 @@ pub const ucontext_t = extern struct {
337337 flags: u32,
338338 link: ?*ucontext_t,
339339 stack: stack_t,
340 sigmask: sigset_t,
340 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
341341 mcontext: mcontext_t,
342342};
343343
lib/std/os/linux/s390x.zig+1-1
......@@ -273,7 +273,7 @@ pub const ucontext_t = extern struct {
273273 link: ?*ucontext_t,
274274 stack: stack_t,
275275 mcontext: mcontext_t,
276 sigmask: sigset_t,
276 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
277277};
278278
279279pub const mcontext_t = extern struct {
lib/std/os/linux/sparc64.zig+1-1
......@@ -454,7 +454,7 @@ pub const ucontext_t = extern struct {
454454 sigmask: u64,
455455 mcontext: mcontext_t,
456456 stack: stack_t,
457 sigset: sigset_t,
457 sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
458458};
459459
460460/// TODO
lib/std/os/linux/test.zig+40-9
......@@ -126,7 +126,9 @@ test "fadvise" {
126126}
127127
128128test "sigset_t" {
129 var sigset = linux.empty_sigset;
129 std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
130
131 var sigset = linux.sigemptyset();
130132
131133 // See that none are set, then set each one, see that they're all set, then
132134 // remove them all, and then see that none are set.
......@@ -138,7 +140,6 @@ test "sigset_t" {
138140 }
139141 for (1..linux.NSIG) |i| {
140142 try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
141 try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);
142143 }
143144 for (1..linux.NSIG) |i| {
144145 linux.sigdelset(&sigset, @truncate(i));
......@@ -147,22 +148,52 @@ test "sigset_t" {
147148 try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
148149 }
149150
151 // Kernel sigset_t is either 2+ 32-bit values or 1+ 64-bit value(s).
152 const sigset_len = @typeInfo(linux.sigset_t).array.len;
153 const sigset_elemis64 = 64 == @bitSizeOf(@typeInfo(linux.sigset_t).array.child);
154
150155 linux.sigaddset(&sigset, 1);
151156 try expectEqual(sigset[0], 1);
152 try expectEqual(sigset[1], 0);
157 if (sigset_len > 1) {
158 try expectEqual(sigset[1], 0);
159 }
153160
154161 linux.sigaddset(&sigset, 31);
155162 try expectEqual(sigset[0], 0x4000_0001);
156 try expectEqual(sigset[1], 0);
163 if (sigset_len > 1) {
164 try expectEqual(sigset[1], 0);
165 }
157166
158167 linux.sigaddset(&sigset, 36);
159 try expectEqual(sigset[0], 0x4000_0001);
160 try expectEqual(sigset[1], 0x8);
168 if (sigset_elemis64) {
169 try expectEqual(sigset[0], 0x8_4000_0001);
170 } else {
171 try expectEqual(sigset[0], 0x4000_0001);
172 try expectEqual(sigset[1], 0x8);
173 }
161174
162175 linux.sigaddset(&sigset, 64);
163 try expectEqual(sigset[0], 0x4000_0001);
164 try expectEqual(sigset[1], 0x8000_0008);
165 try expectEqual(sigset[2], 0);
176 if (sigset_elemis64) {
177 try expectEqual(sigset[0], 0x8000_0008_4000_0001);
178 } else {
179 try expectEqual(sigset[0], 0x4000_0001);
180 try expectEqual(sigset[1], 0x8000_0008);
181 }
182}
183
184test "sigfillset" {
185 // unlike the C library, all the signals are set in the kernel-level fillset
186 const sigset = linux.sigfillset();
187 for (1..linux.NSIG) |i| {
188 try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
189 }
190}
191
192test "sigemptyset" {
193 const sigset = linux.sigemptyset();
194 for (1..linux.NSIG) |i| {
195 try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
196 }
166197}
167198
168199test "sysinfo" {
lib/std/os/linux/x86.zig+1-1
......@@ -350,7 +350,7 @@ pub const ucontext_t = extern struct {
350350 link: ?*ucontext_t,
351351 stack: stack_t,
352352 mcontext: mcontext_t,
353 sigmask: sigset_t,
353 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
354354 regspace: [64]u64,
355355};
356356
lib/std/os/linux/x86_64.zig+11-3
......@@ -369,13 +369,21 @@ pub const mcontext_t = extern struct {
369369 reserved1: [8]usize = undefined,
370370};
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.
372380pub const ucontext_t = extern struct {
373381 flags: usize,
374382 link: ?*ucontext_t,
375383 stack: stack_t,
376384 mcontext: mcontext_t,
377 sigmask: sigset_t,
378 fpregs_mem: [64]usize,
385 sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a glibc-compatible (1024-bit) sigmask.
386 fpregs_mem: [64]usize, // Not part of kernel ABI, only part of glibc ucontext_t
379387};
380388
381389fn gpRegisterOffset(comptime reg_index: comptime_int) usize {
......@@ -455,7 +463,7 @@ fn getContextInternal() callconv(.naked) usize {
455463 [stack_offset] "i" (@offsetOf(ucontext_t, "stack")),
456464 [sigprocmask] "i" (@intFromEnum(linux.SYS.rt_sigprocmask)),
457465 [sigmask_offset] "i" (@offsetOf(ucontext_t, "sigmask")),
458 [sigset_size] "i" (linux.NSIG / 8),
466 [sigset_size] "i" (@sizeOf(sigset_t)),
459467 : "cc", "memory", "rax", "rcx", "rdx", "rdi", "rsi", "r8", "r10", "r11"
460468 );
461469}
lib/std/os/plan9.zig+4-1
......@@ -182,7 +182,6 @@ pub const SIG = struct {
182182 pub const TTOU = 20;
183183};
184184pub const sigset_t = c_long;
185pub const empty_sigset = 0;
186185pub const siginfo_t = c_long;
187186// TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we include it here to be compatible.
188187pub const Sigaction = extern struct {
......@@ -199,6 +198,10 @@ pub const Sigaction = extern struct {
199198pub const AT = struct {
200199 pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
201200};
201// Plan 9 doesn't do signals. This is just needed to get through start.zig.
202pub fn sigemptyset() sigset_t {
203 return 0;
204}
202205// TODO implement sigaction
203206// right now it is just a shim to allow using start.zig code
204207pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
lib/std/posix.zig+69-18
......@@ -86,6 +86,7 @@ pub const MREMAP = system.MREMAP;
8686pub const MSF = system.MSF;
8787pub const MSG = system.MSG;
8888pub const NAME_MAX = system.NAME_MAX;
89pub const NSIG = system.NSIG;
8990pub const O = system.O;
9091pub const PATH_MAX = system.PATH_MAX;
9192pub const POLL = system.POLL;
......@@ -126,10 +127,8 @@ pub const timerfd_clockid_t = system.timerfd_clockid_t;
126127pub const cpu_set_t = system.cpu_set_t;
127128pub const dev_t = system.dev_t;
128129pub const dl_phdr_info = system.dl_phdr_info;
129pub const empty_sigset = system.empty_sigset;
130130pub const fd_t = system.fd_t;
131131pub const file_obj = system.file_obj;
132pub const filled_sigset = system.filled_sigset;
133132pub const gid_t = system.gid_t;
134133pub const ifreq = system.ifreq;
135134pub const ino_t = system.ino_t;
......@@ -678,7 +677,8 @@ pub fn abort() noreturn {
678677 raise(SIG.ABRT) catch {};
679678
680679 // Disable all signal handlers.
681 sigprocmask(SIG.BLOCK, &linux.all_mask, null);
680 const filledset = linux.sigfillset();
681 sigprocmask(SIG.BLOCK, &filledset, null);
682682
683683 // Only one thread may proceed to the rest of abort().
684684 if (!builtin.single_threaded) {
......@@ -691,14 +691,15 @@ pub fn abort() noreturn {
691691 // Install default handler so that the tkill below will terminate.
692692 const sigact = Sigaction{
693693 .handler = .{ .handler = SIG.DFL },
694 .mask = empty_sigset,
694 .mask = sigemptyset(),
695695 .flags = 0,
696696 };
697697 sigaction(SIG.ABRT, &sigact, null);
698698
699699 _ = linux.tkill(linux.gettid(), SIG.ABRT);
700700
701 const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
701 var sigabrtmask = sigemptyset();
702 sigaddset(&sigabrtmask, SIG.ABRT);
702703 sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
703704
704705 // Beyond this point should be unreachable.
......@@ -723,18 +724,13 @@ pub fn raise(sig: u8) RaiseError!void {
723724 }
724725
725726 if (native_os == .linux) {
726 // https://git.musl-libc.org/cgit/musl/commit/?id=0bed7e0acfd34e3fb63ca0e4d99b7592571355a9
727 //
728 // Unlike musl, libc-less Zig std does not have any internal signals for implementation purposes, so we
729 // need to block all signals on the assumption that any of them could potentially fork() in a handler.
730 var set: sigset_t = undefined;
731 sigprocmask(SIG.BLOCK, &linux.all_mask, &set);
732
733 const tid = linux.gettid();
734 const rc = linux.tkill(tid, sig);
735
736 // restore signal mask
737 sigprocmask(SIG.SETMASK, &set, null);
727 // Block all signals so a `fork` (from a signal handler) between the gettid() and kill() syscalls
728 // cannot trigger an extra, unexpected, inter-process signal. Signal paranoia inherited from Musl.
729 const filled = linux.sigfillset();
730 var orig: sigset_t = undefined;
731 sigprocmask(SIG.BLOCK, &filled, &orig);
732 const rc = linux.tkill(linux.gettid(), sig);
733 sigprocmask(SIG.SETMASK, &orig, null);
738734
739735 switch (errno(rc)) {
740736 .SUCCESS => return,
......@@ -5818,8 +5814,63 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
58185814 }
58195815}
58205816
5817/// Return a filled sigset_t.
5818pub fn sigfillset() sigset_t {
5819 if (builtin.link_libc) {
5820 var set: sigset_t = undefined;
5821 switch (errno(system.sigfillset(&set))) {
5822 .SUCCESS => return set,
5823 else => unreachable,
5824 }
5825 }
5826 return system.sigfillset();
5827}
5828
5829/// Return an empty sigset_t.
5830pub fn sigemptyset() sigset_t {
5831 if (builtin.link_libc) {
5832 var set: sigset_t = undefined;
5833 switch (errno(system.sigemptyset(&set))) {
5834 .SUCCESS => return set,
5835 else => unreachable,
5836 }
5837 }
5838 return system.sigemptyset();
5839}
5840
5841pub fn sigaddset(set: *sigset_t, sig: u8) void {
5842 if (builtin.link_libc) {
5843 switch (errno(system.sigaddset(set, sig))) {
5844 .SUCCESS => return,
5845 else => unreachable,
5846 }
5847 }
5848 system.sigaddset(set, sig);
5849}
5850
5851pub fn sigdelset(set: *sigset_t, sig: u8) void {
5852 if (builtin.link_libc) {
5853 switch (errno(system.sigdelset(set, sig))) {
5854 .SUCCESS => return,
5855 else => unreachable,
5856 }
5857 }
5858 system.sigdelset(set, sig);
5859}
5860
5861pub fn sigismember(set: *const sigset_t, sig: u8) bool {
5862 if (builtin.link_libc) {
5863 const rc = system.sigismember(set, sig);
5864 switch (errno(rc)) {
5865 .SUCCESS => return rc == 1,
5866 else => unreachable,
5867 }
5868 }
5869 return system.sigismember(set, sig);
5870}
5871
58215872/// Examine and change a signal action.
5822pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
5873pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
58235874 switch (errno(system.sigaction(sig, act, oact))) {
58245875 .SUCCESS => return,
58255876 // EINVAL means the signal is either invalid or some signal that cannot have its action
lib/std/posix/test.zig+147-26
......@@ -859,12 +859,61 @@ test "shutdown socket" {
859859 std.net.Stream.close(.{ .handle = sock });
860860}
861861
862test "sigaction" {
862test "sigset empty/full" {
863 if (native_os == .wasi or native_os == .windows)
864 return error.SkipZigTest;
865
866 var set: posix.sigset_t = posix.sigemptyset();
867 for (1..posix.NSIG) |i| {
868 try expectEqual(false, posix.sigismember(&set, @truncate(i)));
869 }
870
871 // The C library can reserve some (unnamed) signals, so can't check the full
872 // NSIG set is defined, but just test a couple:
873 set = posix.sigfillset();
874 try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.CHLD)));
875 try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.INT)));
876}
877
878// Some signals (32 - 34 on glibc/musl) are not allowed to be added to a
879// sigset by the C library, so avoid testing them.
880fn reserved_signo(i: usize) bool {
881 return builtin.link_libc and (i >= 32 and i <= 34);
882}
883
884test "sigset add/del" {
863885 if (native_os == .wasi or native_os == .windows)
864886 return error.SkipZigTest;
865887
866 // https://github.com/ziglang/zig/issues/7427
867 if (native_os == .linux and builtin.target.cpu.arch == .x86)
888 var sigset: posix.sigset_t = posix.sigemptyset();
889
890 // See that none are set, then set each one, see that they're all set, then
891 // remove them all, and then see that none are set.
892 for (1..posix.NSIG) |i| {
893 try expectEqual(false, posix.sigismember(&sigset, @truncate(i)));
894 }
895 for (1..posix.NSIG) |i| {
896 if (!reserved_signo(i)) {
897 posix.sigaddset(&sigset, @truncate(i));
898 }
899 }
900 for (1..posix.NSIG) |i| {
901 if (!reserved_signo(i)) {
902 try expectEqual(true, posix.sigismember(&sigset, @truncate(i)));
903 }
904 }
905 for (1..posix.NSIG) |i| {
906 if (!reserved_signo(i)) {
907 posix.sigdelset(&sigset, @truncate(i));
908 }
909 }
910 for (1..posix.NSIG) |i| {
911 try expectEqual(false, posix.sigismember(&sigset, @truncate(i)));
912 }
913}
914
915test "sigaction" {
916 if (native_os == .wasi or native_os == .windows)
868917 return error.SkipZigTest;
869918
870919 // https://github.com/ziglang/zig/issues/15381
......@@ -872,66 +921,138 @@ test "sigaction" {
872921 return error.SkipZigTest;
873922 }
874923
924 const test_signo = posix.SIG.URG; // URG only because it is ignored by default in debuggers
925
875926 const S = struct {
876927 var handler_called_count: u32 = 0;
877928
878929 fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
879930 _ = ctx_ptr;
880931 // Check that we received the correct signal.
881 switch (native_os) {
882 .netbsd => {
883 if (sig == posix.SIG.USR1 and sig == info.info.signo)
884 handler_called_count += 1;
885 },
886 else => {
887 if (sig == posix.SIG.USR1 and sig == info.signo)
888 handler_called_count += 1;
889 },
932 const info_sig = switch (native_os) {
933 .netbsd => info.info.signo,
934 else => info.signo,
935 };
936 if (sig == test_signo and sig == info_sig) {
937 handler_called_count += 1;
890938 }
891939 }
892940 };
893941
894942 var sa: posix.Sigaction = .{
895943 .handler = .{ .sigaction = &S.handler },
896 .mask = posix.empty_sigset,
944 .mask = posix.sigemptyset(),
897945 .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
898946 };
947
899948 var old_sa: posix.Sigaction = undefined;
900949
901950 // Install the new signal handler.
902 posix.sigaction(posix.SIG.USR1, &sa, null);
951 posix.sigaction(test_signo, &sa, null);
903952
904953 // Check that we can read it back correctly.
905 posix.sigaction(posix.SIG.USR1, null, &old_sa);
954 posix.sigaction(test_signo, null, &old_sa);
906955 try testing.expectEqual(&S.handler, old_sa.handler.sigaction.?);
907956 try testing.expect((old_sa.flags & posix.SA.SIGINFO) != 0);
908957
909958 // Invoke the handler.
910 try posix.raise(posix.SIG.USR1);
911 try testing.expect(S.handler_called_count == 1);
959 try posix.raise(test_signo);
960 try testing.expectEqual(1, S.handler_called_count);
912961
913962 // Check if passing RESETHAND correctly reset the handler to SIG_DFL
914 posix.sigaction(posix.SIG.USR1, null, &old_sa);
963 posix.sigaction(test_signo, null, &old_sa);
915964 try testing.expectEqual(posix.SIG.DFL, old_sa.handler.handler);
916965
917966 // Reinstall the signal w/o RESETHAND and re-raise
918967 sa.flags = posix.SA.SIGINFO;
919 posix.sigaction(posix.SIG.USR1, &sa, null);
920 try posix.raise(posix.SIG.USR1);
921 try testing.expect(S.handler_called_count == 2);
968 posix.sigaction(test_signo, &sa, null);
969 try posix.raise(test_signo);
970 try testing.expectEqual(2, S.handler_called_count);
922971
923972 // Now set the signal to ignored
924973 sa.handler = .{ .handler = posix.SIG.IGN };
925974 sa.flags = 0;
926 posix.sigaction(posix.SIG.USR1, &sa, null);
975 posix.sigaction(test_signo, &sa, null);
927976
928977 // Re-raise to ensure handler is actually ignored
929 try posix.raise(posix.SIG.USR1);
930 try testing.expect(S.handler_called_count == 2);
978 try posix.raise(test_signo);
979 try testing.expectEqual(2, S.handler_called_count);
931980
932981 // Ensure that ignored state is returned when querying
933 posix.sigaction(posix.SIG.USR1, null, &old_sa);
934 try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler.?);
982 posix.sigaction(test_signo, null, &old_sa);
983 try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler);
984}
985
986test "sigset_t bits" {
987 if (native_os == .wasi or native_os == .windows)
988 return error.SkipZigTest;
989
990 const S = struct {
991 var expected_sig: i32 = undefined;
992 var handler_called_count: u32 = 0;
993
994 fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
995 _ = ctx_ptr;
996
997 const info_sig = switch (native_os) {
998 .netbsd => info.info.signo,
999 else => info.signo,
1000 };
1001 if (sig == expected_sig and sig == info_sig) {
1002 handler_called_count += 1;
1003 }
1004 }
1005 };
1006
1007 const self_pid = posix.system.getpid();
1008
1009 // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on
1010 // big-endian), try sending a blocked signal to make sure the mask matches the
1011 // signal. (Send URG and CHLD because they're ignored by default in the
1012 // debugger, vs. USR1 or other named signals)
1013 inline for ([_]usize{ posix.SIG.URG, posix.SIG.CHLD, 62, 94, 126 }) |test_signo| {
1014 if (test_signo >= posix.NSIG) continue;
1015
1016 S.expected_sig = test_signo;
1017 S.handler_called_count = 0;
1018
1019 const sa: posix.Sigaction = .{
1020 .handler = .{ .sigaction = &S.handler },
1021 .mask = posix.sigemptyset(),
1022 .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
1023 };
1024
1025 var old_sa: posix.Sigaction = undefined;
1026
1027 // Install the new signal handler.
1028 posix.sigaction(test_signo, &sa, &old_sa);
1029
1030 // block the signal and see that its delayed until unblocked
1031 var block_one: posix.sigset_t = posix.sigemptyset();
1032 posix.sigaddset(&block_one, test_signo);
1033 posix.sigprocmask(posix.SIG.BLOCK, &block_one, null);
1034
1035 // qemu maps target signals to host signals 1-to-1, so targets
1036 // with more signals than the host will fail to send the signal.
1037 const rc = posix.system.kill(self_pid, test_signo);
1038 switch (posix.errno(rc)) {
1039 .SUCCESS => {
1040 // See that the signal is blocked, then unblocked
1041 try testing.expectEqual(0, S.handler_called_count);
1042 posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null);
1043 try testing.expectEqual(1, S.handler_called_count);
1044 },
1045 .INVAL => {
1046 // Signal won't get delviered. Just clean up.
1047 posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null);
1048 try testing.expectEqual(0, S.handler_called_count);
1049 },
1050 else => |errno| return posix.unexpectedErrno(errno),
1051 }
1052
1053 // Restore original handler
1054 posix.sigaction(test_signo, &old_sa, null);
1055 }
9351056}
9361057
9371058test "dup & dup2" {
lib/std/start.zig+1-1
......@@ -749,7 +749,7 @@ fn maybeIgnoreSigpipe() void {
749749 // Set handler to a noop function instead of `SIG.IGN` to prevent
750750 // leaking signal disposition to a child process.
751751 .handler = .{ .handler = noopSigHandler },
752 .mask = posix.empty_sigset,
752 .mask = posix.sigemptyset(),
753753 .flags = 0,
754754 };
755755 posix.sigaction(posix.SIG.PIPE, &act, null);
src/crash_report.zig+2-3
......@@ -175,12 +175,11 @@ pub fn attachSegfaultHandler() void {
175175 _ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
176176 return;
177177 }
178 var act: posix.Sigaction = .{
178 const act: posix.Sigaction = .{
179179 .handler = .{ .sigaction = handleSegfaultPosix },
180 .mask = posix.empty_sigset,
180 .mask = posix.sigemptyset(),
181181 .flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
182182 };
183
184183 debug.updateSegfaultHandler(&act);
185184}
186185
test/standalone/sigpipe/build.zig+1-1
......@@ -18,7 +18,7 @@ pub fn build(b: *std.build.Builder) !void {
1818 {
1919 const act = posix.Sigaction{
2020 .handler = .{ .handler = posix.SIG.DFL },
21 .mask = posix.empty_sigset,
21 .mask = posix.sigemptyset(),
2222 .flags = 0,
2323 };
2424 try posix.sigaction(posix.SIG.PIPE, &act, null);