aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/std/Progress.zig4
-rw-r--r--lib/std/c.zig53
-rw-r--r--lib/std/c/darwin.zig9
-rw-r--r--lib/std/debug.zig9
-rw-r--r--lib/std/os/emscripten.zig4
-rw-r--r--lib/std/os/linux.zig115
-rw-r--r--lib/std/os/linux/aarch64.zig2
-rw-r--r--lib/std/os/linux/arm.zig2
-rw-r--r--lib/std/os/linux/loongarch64.zig3
-rw-r--r--lib/std/os/linux/powerpc.zig2
-rw-r--r--lib/std/os/linux/powerpc64.zig2
-rw-r--r--lib/std/os/linux/s390x.zig2
-rw-r--r--lib/std/os/linux/sparc64.zig2
-rw-r--r--lib/std/os/linux/test.zig49
-rw-r--r--lib/std/os/linux/x86.zig2
-rw-r--r--lib/std/os/linux/x86_64.zig14
-rw-r--r--lib/std/os/plan9.zig5
-rw-r--r--lib/std/posix.zig87
-rw-r--r--lib/std/posix/test.zig173
-rw-r--r--lib/std/start.zig2
-rw-r--r--src/crash_report.zig5
-rw-r--r--test/standalone/sigpipe/build.zig2
22 files changed, 393 insertions, 155 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
index 24f58e1c68..2213bd9b11 100644
--- a/lib/std/Progress.zig
+++ b/lib/std/Progress.zig
@@ -410,9 +410,9 @@ pub fn start(options: Options) Node {
}
if (have_sigwinch) {
- var act: posix.Sigaction = .{
+ const act: posix.Sigaction = .{
.handler = .{ .sigaction = handleSigWinch },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART),
};
posix.sigaction(posix.SIG.WINCH, &act, null);
diff --git a/lib/std/c.zig b/lib/std/c.zig
index 35d175f0b9..d5560c3f6f 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -3115,6 +3115,21 @@ pub const SYS = switch (native_os) {
.linux => linux.SYS,
else => void,
};
+
+/// A common format for the Sigaction struct across a variety of Linux flavors.
+const common_linux_Sigaction = extern 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;
+
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
+ },
+ mask: sigset_t,
+ flags: c_uint,
+ restorer: ?*const fn () callconv(.c) void = null, // C library will fill this in
+};
+
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name.
pub const Sigaction = switch (native_os) {
.linux => switch (native_arch) {
@@ -3123,7 +3138,7 @@ pub const Sigaction = switch (native_os) {
.mips64,
.mips64el,
=> if (builtin.target.abi.isMusl())
- linux.Sigaction
+ common_linux_Sigaction
else if (builtin.target.ptrBitWidth() == 64) extern 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;
@@ -3160,8 +3175,8 @@ pub const Sigaction = switch (native_os) {
flags: c_uint,
restorer: ?*const fn () callconv(.c) void = null,
mask: sigset_t,
- } else linux.Sigaction,
- else => linux.Sigaction,
+ } else common_linux_Sigaction,
+ else => common_linux_Sigaction,
},
.emscripten => emscripten.Sigaction,
.netbsd, .macos, .ios, .tvos, .watchos, .visionos => extern struct {
@@ -4518,27 +4533,18 @@ pub const siginfo_t = switch (native_os) {
else => void,
};
pub const sigset_t = switch (native_os) {
- .linux => linux.sigset_t,
+ .linux => [1024 / @bitSizeOf(c_ulong)]c_ulong, // glibc and musl present a 1024-bit sigset_t, while kernel's is 128-bit or less.
.emscripten => emscripten.sigset_t,
// https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L19
- .openbsd, .macos, .ios, .tvos, .watchos, .visionos, .serenity => u32,
+ .openbsd, .serenity => u32,
+ .macos, .ios, .tvos, .watchos, .visionos => darwin.sigset_t,
.dragonfly, .netbsd, .solaris, .illumos, .freebsd => extern struct {
__bits: [SIG.WORDS]u32,
},
.haiku => u64,
else => u0,
};
-pub const empty_sigset: sigset_t = switch (native_os) {
- .linux => linux.empty_sigset,
- .emscripten => emscripten.empty_sigset,
- .dragonfly, .netbsd, .solaris, .illumos, .freebsd => .{ .__bits = [_]u32{0} ** SIG.WORDS },
- else => 0,
-};
-pub const filled_sigset = switch (native_os) {
- .linux => linux.filled_sigset,
- .haiku => ~@as(sigset_t, 0),
- else => 0,
-};
+
pub const sigval = switch (native_os) {
.linux => linux.sigval,
// https://github.com/SerenityOS/serenity/blob/ec492a1a0819e6239ea44156825c4ee7234ca3db/Kernel/API/POSIX/signal.h#L22-L25
@@ -6665,7 +6671,7 @@ pub const timezone = switch (native_os) {
};
pub const ucontext_t = switch (native_os) {
- .linux => linux.ucontext_t,
+ .linux => linux.ucontext_t, // std.os.linux.ucontext_t is currently glibc-compatible, but it should probably not be.
.emscripten => emscripten.ucontext_t,
.macos, .ios, .tvos, .watchos, .visionos => extern struct {
onstack: c_int,
@@ -9596,6 +9602,7 @@ pub const NSIG = switch (native_os) {
.windows => 23,
.haiku => 65,
.netbsd, .freebsd => 32,
+ .macos => darwin.NSIG,
.solaris, .illumos => 75,
// https://github.com/SerenityOS/serenity/blob/046c23f567a17758d762a33bdf04bacbfd088f9f/Kernel/API/POSIX/signal_numbers.h#L42
.openbsd, .serenity => 33,
@@ -10345,6 +10352,11 @@ pub const sigfillset = switch (native_os) {
else => private.sigfillset,
};
+pub const sigaddset = private.sigaddset;
+pub const sigemptyset = private.sigemptyset;
+pub const sigdelset = private.sigdelset;
+pub const sigismember = private.sigismember;
+
pub const sigprocmask = switch (native_os) {
.netbsd => private.__sigprocmask14,
else => private.sigprocmask,
@@ -11025,7 +11037,6 @@ pub const pthread_attr_set_qos_class_np = darwin.pthread_attr_set_qos_class_np;
pub const pthread_get_qos_class_np = darwin.pthread_get_qos_class_np;
pub const pthread_set_qos_class_self_np = darwin.pthread_set_qos_class_self_np;
pub const ptrace = darwin.ptrace;
-pub const sigaddset = darwin.sigaddset;
pub const task_for_pid = darwin.task_for_pid;
pub const task_get_exception_ports = darwin.task_get_exception_ports;
pub const task_info = darwin.task_info;
@@ -11148,7 +11159,11 @@ const private = struct {
extern "c" fn sched_yield() c_int;
extern "c" fn sendfile(out_fd: fd_t, in_fd: fd_t, offset: ?*off_t, count: usize) isize;
extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
- extern "c" fn sigfillset(set: ?*sigset_t) void;
+ extern "c" fn sigdelset(set: ?*sigset_t, signo: c_int) c_int;
+ extern "c" fn sigaddset(set: ?*sigset_t, signo: c_int) c_int;
+ extern "c" fn sigfillset(set: ?*sigset_t) c_int;
+ extern "c" fn sigemptyset(set: ?*sigset_t) c_int;
+ extern "c" fn sigismember(set: ?*const sigset_t, signo: c_int) c_int;
extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int;
diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig
index ac083ec9f2..74d79842be 100644
--- a/lib/std/c/darwin.zig
+++ b/lib/std/c/darwin.zig
@@ -10,7 +10,6 @@ const mode_t = std.c.mode_t;
const off_t = std.c.off_t;
const pid_t = std.c.pid_t;
const pthread_attr_t = std.c.pthread_attr_t;
-const sigset_t = std.c.sigset_t;
const timespec = std.c.timespec;
const sf_hdtr = std.c.sf_hdtr;
@@ -840,9 +839,11 @@ pub extern "c" fn sendfile(
flags: u32,
) c_int;
-pub fn sigaddset(set: *sigset_t, signo: u5) void {
- set.* |= @as(u32, 1) << (signo - 1);
-}
+// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/_types.h#L74
+pub const sigset_t = u32;
+
+// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/bsd/sys/signal.h#L76
+pub const NSIG = 32;
pub const qos_class_t = enum(c_uint) {
/// highest priority QOS class for critical tasks
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index d2b17ed30d..eb7cf793f2 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -1387,12 +1387,11 @@ pub fn attachSegfaultHandler() void {
windows_segfault_handle = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return;
}
- var act = posix.Sigaction{
+ const act = posix.Sigaction{
.handler = .{ .sigaction = handleSegfaultPosix },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
};
-
updateSegfaultHandler(&act);
}
@@ -1404,9 +1403,9 @@ fn resetSegfaultHandler() void {
}
return;
}
- var act = posix.Sigaction{
+ const act = posix.Sigaction{
.handler = .{ .handler = posix.SIG.DFL },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = 0,
};
updateSegfaultHandler(&act);
diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig
index ad7a3f65c1..0e07a8afb7 100644
--- a/lib/std/os/emscripten.zig
+++ b/lib/std/os/emscripten.zig
@@ -560,7 +560,9 @@ pub const Sigaction = extern struct {
};
pub const sigset_t = [1024 / 32]u32;
-pub const empty_sigset = [_]u32{0} ** @typeInfo(sigset_t).array.len;
+pub fn sigemptyset() sigset_t {
+ return [_]u32{0} ** @typeInfo(sigset_t).array.len;
+}
pub const siginfo_t = extern struct {
signo: i32,
errno: i32,
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index 58a2b196b3..794fccdf13 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,35 @@ 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;
-const sigset_len = @typeInfo(sigset_t).array.len;
+/// 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;
-/// Empty set to initialize sigset_t instances from.
-pub const empty_sigset: sigset_t = [_]u32{0} ** sigset_len;
+const SigsetElement = c_ulong;
-pub const filled_sigset: sigset_t = [_]u32{0x7fff_ffff} ++ [_]u32{0} ** (sigset_len - 1);
+const sigset_len = @typeInfo(sigset_t).array.len;
-pub const all_mask: sigset_t = [_]u32{0xffff_ffff} ** sigset_len;
+/// Zig's version of sigemptyset. Returns initialized sigset_t.
+pub fn sigemptyset() sigset_t {
+ return [_]SigsetElement{0} ** sigset_len;
+}
-fn sigset_bit_index(sig: usize) struct { word: usize, mask: u32 } {
+/// Zig's version of sigfillset. Returns initalized sigset_t.
+pub fn sigfillset() sigset_t {
+ return [_]SigsetElement{~@as(SigsetElement, 0)} ** sigset_len;
+}
+
+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)),
};
}
@@ -3487,6 +3499,7 @@ pub const SIG = if (is_mips) struct {
pub const UNBLOCK = 2;
pub const SETMASK = 3;
+ // https://github.com/torvalds/linux/blob/ca91b9500108d4cf083a635c2e11c884d5dd20ea/arch/mips/include/uapi/asm/signal.h#L25
pub const HUP = 1;
pub const INT = 2;
pub const QUIT = 3;
@@ -3494,33 +3507,32 @@ pub const SIG = if (is_mips) struct {
pub const TRAP = 5;
pub const ABRT = 6;
pub const IOT = ABRT;
- pub const BUS = 7;
+ pub const EMT = 7;
pub const FPE = 8;
pub const KILL = 9;
- pub const USR1 = 10;
+ pub const BUS = 10;
pub const SEGV = 11;
- pub const USR2 = 12;
+ pub const SYS = 12;
pub const PIPE = 13;
pub const ALRM = 14;
pub const TERM = 15;
- pub const STKFLT = 16;
- pub const CHLD = 17;
- pub const CONT = 18;
- pub const STOP = 19;
- pub const TSTP = 20;
- pub const TTIN = 21;
- pub const TTOU = 22;
- pub const URG = 23;
- pub const XCPU = 24;
- pub const XFSZ = 25;
- pub const VTALRM = 26;
- pub const PROF = 27;
- pub const WINCH = 28;
- pub const IO = 29;
- pub const POLL = 29;
- pub const PWR = 30;
- pub const SYS = 31;
- pub const UNUSED = SIG.SYS;
+ pub const USR1 = 16;
+ pub const USR2 = 17;
+ pub const CHLD = 18;
+ pub const PWR = 19;
+ pub const WINCH = 20;
+ pub const URG = 21;
+ pub const IO = 22;
+ pub const POLL = IO;
+ pub const STOP = 23;
+ pub const TSTP = 24;
+ pub const CONT = 25;
+ pub const TTIN = 26;
+ pub const TTOU = 27;
+ pub const VTALRM = 28;
+ pub const PROF = 29;
+ pub const XCPU = 30;
+ pub const XFZ = 31;
pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize));
pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0);
@@ -5479,38 +5491,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 {
- flags: c_uint,
- handler: k_sigaction_funcs.handler,
- mask: [4]c_ulong,
- restorer: k_sigaction_funcs.restorer,
- },
- .mips64, .mips64el => extern struct {
+ .mips, .mipsel, .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 +5526,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/aarch64.zig b/lib/std/os/linux/aarch64.zig
index 649e474310..ed40145e1b 100644
--- a/lib/std/os/linux/aarch64.zig
+++ b/lib/std/os/linux/aarch64.zig
@@ -289,7 +289,7 @@ pub const ucontext_t = extern struct {
flags: usize,
link: ?*ucontext_t,
stack: stack_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
mcontext: mcontext_t,
};
diff --git a/lib/std/os/linux/arm.zig b/lib/std/os/linux/arm.zig
index 8f83bb60a1..8fea30a5da 100644
--- a/lib/std/os/linux/arm.zig
+++ b/lib/std/os/linux/arm.zig
@@ -337,7 +337,7 @@ pub const ucontext_t = extern struct {
link: ?*ucontext_t,
stack: stack_t,
mcontext: mcontext_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
regspace: [64]u64,
};
diff --git a/lib/std/os/linux/loongarch64.zig b/lib/std/os/linux/loongarch64.zig
index 9ee8fe229c..50c4664ac8 100644
--- a/lib/std/os/linux/loongarch64.zig
+++ b/lib/std/os/linux/loongarch64.zig
@@ -264,8 +264,7 @@ pub const ucontext_t = extern struct {
flags: c_ulong,
link: ?*ucontext_t,
stack: stack_t,
- sigmask: sigset_t,
- _pad: [1024 / 8 - @sizeOf(sigset_t)]u8,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
mcontext: mcontext_t,
};
diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig
index 443a32769a..450cb27c7e 100644
--- a/lib/std/os/linux/powerpc.zig
+++ b/lib/std/os/linux/powerpc.zig
@@ -341,7 +341,7 @@ pub const ucontext_t = extern struct {
stack: stack_t,
pad: [7]i32,
regs: *mcontext_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
pad2: [3]i32,
mcontext: mcontext_t,
};
diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig
index bb1032fb21..b0a14ff3cb 100644
--- a/lib/std/os/linux/powerpc64.zig
+++ b/lib/std/os/linux/powerpc64.zig
@@ -337,7 +337,7 @@ pub const ucontext_t = extern struct {
flags: u32,
link: ?*ucontext_t,
stack: stack_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
mcontext: mcontext_t,
};
diff --git a/lib/std/os/linux/s390x.zig b/lib/std/os/linux/s390x.zig
index 70cf53f6fd..614c33d076 100644
--- a/lib/std/os/linux/s390x.zig
+++ b/lib/std/os/linux/s390x.zig
@@ -273,7 +273,7 @@ pub const ucontext_t = extern struct {
link: ?*ucontext_t,
stack: stack_t,
mcontext: mcontext_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
};
pub const mcontext_t = extern struct {
diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig
index 594b11b7ac..34df73fcb1 100644
--- a/lib/std/os/linux/sparc64.zig
+++ b/lib/std/os/linux/sparc64.zig
@@ -454,7 +454,7 @@ pub const ucontext_t = extern struct {
sigmask: u64,
mcontext: mcontext_t,
stack: stack_t,
- sigset: sigset_t,
+ sigset: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
};
/// TODO
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
index fa6f822329..7f5deff480 100644
--- a/lib/std/os/linux/test.zig
+++ b/lib/std/os/linux/test.zig
@@ -126,7 +126,9 @@ test "fadvise" {
}
test "sigset_t" {
- var sigset = linux.empty_sigset;
+ std.debug.assert(@sizeOf(linux.sigset_t) == (linux.NSIG / 8));
+
+ var sigset = linux.sigemptyset();
// See that none are set, then set each one, see that they're all set, then
// remove them all, and then see that none are set.
@@ -138,7 +140,6 @@ test "sigset_t" {
}
for (1..linux.NSIG) |i| {
try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
- try expectEqual(linux.sigismember(&linux.empty_sigset, @truncate(i)), false);
}
for (1..linux.NSIG) |i| {
linux.sigdelset(&sigset, @truncate(i));
@@ -147,22 +148,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 "sigfillset" {
+ // unlike the C library, all the signals are set in the kernel-level fillset
+ const sigset = linux.sigfillset();
+ for (1..linux.NSIG) |i| {
+ try expectEqual(linux.sigismember(&sigset, @truncate(i)), true);
+ }
+}
+
+test "sigemptyset" {
+ const sigset = linux.sigemptyset();
+ for (1..linux.NSIG) |i| {
+ try expectEqual(linux.sigismember(&sigset, @truncate(i)), false);
+ }
}
test "sysinfo" {
diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig
index cef0388206..41e1ec7d99 100644
--- a/lib/std/os/linux/x86.zig
+++ b/lib/std/os/linux/x86.zig
@@ -350,7 +350,7 @@ pub const ucontext_t = extern struct {
link: ?*ucontext_t,
stack: stack_t,
mcontext: mcontext_t,
- sigmask: sigset_t,
+ sigmask: [1024 / @bitSizeOf(c_ulong)]c_ulong, // Currently a libc-compatible (1024-bit) sigmask
regspace: [64]u64,
};
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"
);
}
diff --git a/lib/std/os/plan9.zig b/lib/std/os/plan9.zig
index 1882eda476..b910e26c71 100644
--- a/lib/std/os/plan9.zig
+++ b/lib/std/os/plan9.zig
@@ -182,7 +182,6 @@ pub const SIG = struct {
pub const TTOU = 20;
};
pub const sigset_t = c_long;
-pub const empty_sigset = 0;
pub const siginfo_t = c_long;
// TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we include it here to be compatible.
pub const Sigaction = extern struct {
@@ -199,6 +198,10 @@ pub const Sigaction = extern struct {
pub const AT = struct {
pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
};
+// Plan 9 doesn't do signals. This is just needed to get through start.zig.
+pub fn sigemptyset() sigset_t {
+ return 0;
+}
// TODO implement sigaction
// right now it is just a shim to allow using start.zig code
pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index ad65cf205d..f414aa801b 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -86,6 +86,7 @@ pub const MREMAP = system.MREMAP;
pub const MSF = system.MSF;
pub const MSG = system.MSG;
pub const NAME_MAX = system.NAME_MAX;
+pub const NSIG = system.NSIG;
pub const O = system.O;
pub const PATH_MAX = system.PATH_MAX;
pub const POLL = system.POLL;
@@ -126,10 +127,8 @@ pub const timerfd_clockid_t = system.timerfd_clockid_t;
pub const cpu_set_t = system.cpu_set_t;
pub const dev_t = system.dev_t;
pub const dl_phdr_info = system.dl_phdr_info;
-pub const empty_sigset = system.empty_sigset;
pub const fd_t = system.fd_t;
pub const file_obj = system.file_obj;
-pub const filled_sigset = system.filled_sigset;
pub const gid_t = system.gid_t;
pub const ifreq = system.ifreq;
pub const ino_t = system.ino_t;
@@ -678,7 +677,8 @@ pub fn abort() noreturn {
raise(SIG.ABRT) catch {};
// Disable all signal handlers.
- sigprocmask(SIG.BLOCK, &linux.all_mask, null);
+ const filledset = linux.sigfillset();
+ sigprocmask(SIG.BLOCK, &filledset, null);
// Only one thread may proceed to the rest of abort().
if (!builtin.single_threaded) {
@@ -691,14 +691,15 @@ pub fn abort() noreturn {
// Install default handler so that the tkill below will terminate.
const sigact = Sigaction{
.handler = .{ .handler = SIG.DFL },
- .mask = empty_sigset,
+ .mask = sigemptyset(),
.flags = 0,
};
sigaction(SIG.ABRT, &sigact, null);
_ = linux.tkill(linux.gettid(), SIG.ABRT);
- const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
+ var sigabrtmask = sigemptyset();
+ sigaddset(&sigabrtmask, SIG.ABRT);
sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
// Beyond this point should be unreachable.
@@ -723,18 +724,13 @@ pub fn raise(sig: u8) RaiseError!void {
}
if (native_os == .linux) {
- // https://git.musl-libc.org/cgit/musl/commit/?id=0bed7e0acfd34e3fb63ca0e4d99b7592571355a9
- //
- // Unlike musl, libc-less Zig std does not have any internal signals for implementation purposes, so we
- // need to block all signals on the assumption that any of them could potentially fork() in a handler.
- var set: sigset_t = undefined;
- sigprocmask(SIG.BLOCK, &linux.all_mask, &set);
-
- const tid = linux.gettid();
- const rc = linux.tkill(tid, sig);
-
- // restore signal mask
- sigprocmask(SIG.SETMASK, &set, null);
+ // Block all signals so a `fork` (from a signal handler) between the gettid() and kill() syscalls
+ // cannot trigger an extra, unexpected, inter-process signal. Signal paranoia inherited from Musl.
+ const filled = linux.sigfillset();
+ var orig: sigset_t = undefined;
+ sigprocmask(SIG.BLOCK, &filled, &orig);
+ const rc = linux.tkill(linux.gettid(), sig);
+ sigprocmask(SIG.SETMASK, &orig, null);
switch (errno(rc)) {
.SUCCESS => return,
@@ -5818,8 +5814,63 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
}
}
+/// Return a filled sigset_t.
+pub fn sigfillset() sigset_t {
+ if (builtin.link_libc) {
+ var set: sigset_t = undefined;
+ switch (errno(system.sigfillset(&set))) {
+ .SUCCESS => return set,
+ else => unreachable,
+ }
+ }
+ return system.sigfillset();
+}
+
+/// Return an empty sigset_t.
+pub fn sigemptyset() sigset_t {
+ if (builtin.link_libc) {
+ var set: sigset_t = undefined;
+ switch (errno(system.sigemptyset(&set))) {
+ .SUCCESS => return set,
+ else => unreachable,
+ }
+ }
+ return system.sigemptyset();
+}
+
+pub fn sigaddset(set: *sigset_t, sig: u8) void {
+ if (builtin.link_libc) {
+ switch (errno(system.sigaddset(set, sig))) {
+ .SUCCESS => return,
+ else => unreachable,
+ }
+ }
+ system.sigaddset(set, sig);
+}
+
+pub fn sigdelset(set: *sigset_t, sig: u8) void {
+ if (builtin.link_libc) {
+ switch (errno(system.sigdelset(set, sig))) {
+ .SUCCESS => return,
+ else => unreachable,
+ }
+ }
+ system.sigdelset(set, sig);
+}
+
+pub fn sigismember(set: *const sigset_t, sig: u8) bool {
+ if (builtin.link_libc) {
+ const rc = system.sigismember(set, sig);
+ switch (errno(rc)) {
+ .SUCCESS => return rc == 1,
+ else => unreachable,
+ }
+ }
+ return system.sigismember(set, sig);
+}
+
/// Examine and change a signal action.
-pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
+pub fn sigaction(sig: u8, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) void {
switch (errno(system.sigaction(sig, act, oact))) {
.SUCCESS => return,
// EINVAL means the signal is either invalid or some signal that cannot have its action
diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig
index e27f8971d7..c886ed6153 100644
--- a/lib/std/posix/test.zig
+++ b/lib/std/posix/test.zig
@@ -859,12 +859,61 @@ test "shutdown socket" {
std.net.Stream.close(.{ .handle = sock });
}
-test "sigaction" {
+test "sigset empty/full" {
+ if (native_os == .wasi or native_os == .windows)
+ return error.SkipZigTest;
+
+ var set: posix.sigset_t = posix.sigemptyset();
+ for (1..posix.NSIG) |i| {
+ try expectEqual(false, posix.sigismember(&set, @truncate(i)));
+ }
+
+ // The C library can reserve some (unnamed) signals, so can't check the full
+ // NSIG set is defined, but just test a couple:
+ set = posix.sigfillset();
+ try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.CHLD)));
+ try expectEqual(true, posix.sigismember(&set, @truncate(posix.SIG.INT)));
+}
+
+// Some signals (32 - 34 on glibc/musl) are not allowed to be added to a
+// sigset by the C library, so avoid testing them.
+fn reserved_signo(i: usize) bool {
+ return builtin.link_libc and (i >= 32 and i <= 34);
+}
+
+test "sigset add/del" {
if (native_os == .wasi or native_os == .windows)
return error.SkipZigTest;
- // https://github.com/ziglang/zig/issues/7427
- if (native_os == .linux and builtin.target.cpu.arch == .x86)
+ var sigset: posix.sigset_t = posix.sigemptyset();
+
+ // See that none are set, then set each one, see that they're all set, then
+ // remove them all, and then see that none are set.
+ for (1..posix.NSIG) |i| {
+ try expectEqual(false, posix.sigismember(&sigset, @truncate(i)));
+ }
+ for (1..posix.NSIG) |i| {
+ if (!reserved_signo(i)) {
+ posix.sigaddset(&sigset, @truncate(i));
+ }
+ }
+ for (1..posix.NSIG) |i| {
+ if (!reserved_signo(i)) {
+ try expectEqual(true, posix.sigismember(&sigset, @truncate(i)));
+ }
+ }
+ for (1..posix.NSIG) |i| {
+ if (!reserved_signo(i)) {
+ posix.sigdelset(&sigset, @truncate(i));
+ }
+ }
+ for (1..posix.NSIG) |i| {
+ try expectEqual(false, posix.sigismember(&sigset, @truncate(i)));
+ }
+}
+
+test "sigaction" {
+ if (native_os == .wasi or native_os == .windows)
return error.SkipZigTest;
// https://github.com/ziglang/zig/issues/15381
@@ -872,66 +921,138 @@ test "sigaction" {
return error.SkipZigTest;
}
+ const test_signo = posix.SIG.URG; // URG only because it is ignored by default in debuggers
+
const S = struct {
var handler_called_count: u32 = 0;
fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
_ = ctx_ptr;
// Check that we received the correct signal.
- switch (native_os) {
- .netbsd => {
- if (sig == posix.SIG.USR1 and sig == info.info.signo)
- handler_called_count += 1;
- },
- else => {
- if (sig == posix.SIG.USR1 and sig == info.signo)
- handler_called_count += 1;
- },
+ const info_sig = switch (native_os) {
+ .netbsd => info.info.signo,
+ else => info.signo,
+ };
+ if (sig == test_signo and sig == info_sig) {
+ handler_called_count += 1;
}
}
};
var sa: posix.Sigaction = .{
.handler = .{ .sigaction = &S.handler },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
};
+
var old_sa: posix.Sigaction = undefined;
// Install the new signal handler.
- posix.sigaction(posix.SIG.USR1, &sa, null);
+ posix.sigaction(test_signo, &sa, null);
// Check that we can read it back correctly.
- posix.sigaction(posix.SIG.USR1, null, &old_sa);
+ posix.sigaction(test_signo, null, &old_sa);
try testing.expectEqual(&S.handler, old_sa.handler.sigaction.?);
try testing.expect((old_sa.flags & posix.SA.SIGINFO) != 0);
// Invoke the handler.
- try posix.raise(posix.SIG.USR1);
- try testing.expect(S.handler_called_count == 1);
+ try posix.raise(test_signo);
+ try testing.expectEqual(1, S.handler_called_count);
// Check if passing RESETHAND correctly reset the handler to SIG_DFL
- posix.sigaction(posix.SIG.USR1, null, &old_sa);
+ posix.sigaction(test_signo, null, &old_sa);
try testing.expectEqual(posix.SIG.DFL, old_sa.handler.handler);
// Reinstall the signal w/o RESETHAND and re-raise
sa.flags = posix.SA.SIGINFO;
- posix.sigaction(posix.SIG.USR1, &sa, null);
- try posix.raise(posix.SIG.USR1);
- try testing.expect(S.handler_called_count == 2);
+ posix.sigaction(test_signo, &sa, null);
+ try posix.raise(test_signo);
+ try testing.expectEqual(2, S.handler_called_count);
// Now set the signal to ignored
sa.handler = .{ .handler = posix.SIG.IGN };
sa.flags = 0;
- posix.sigaction(posix.SIG.USR1, &sa, null);
+ posix.sigaction(test_signo, &sa, null);
// Re-raise to ensure handler is actually ignored
- try posix.raise(posix.SIG.USR1);
- try testing.expect(S.handler_called_count == 2);
+ try posix.raise(test_signo);
+ try testing.expectEqual(2, S.handler_called_count);
// Ensure that ignored state is returned when querying
- posix.sigaction(posix.SIG.USR1, null, &old_sa);
- try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler.?);
+ posix.sigaction(test_signo, null, &old_sa);
+ try testing.expectEqual(posix.SIG.IGN, old_sa.handler.handler);
+}
+
+test "sigset_t bits" {
+ if (native_os == .wasi or native_os == .windows)
+ return error.SkipZigTest;
+
+ const S = struct {
+ var expected_sig: i32 = undefined;
+ var handler_called_count: u32 = 0;
+
+ fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) void {
+ _ = ctx_ptr;
+
+ const info_sig = switch (native_os) {
+ .netbsd => info.info.signo,
+ else => info.signo,
+ };
+ if (sig == expected_sig and sig == info_sig) {
+ handler_called_count += 1;
+ }
+ }
+ };
+
+ const self_pid = posix.system.getpid();
+
+ // To check that sigset_t mapping matches kernel (think u32/u64 mismatches on
+ // big-endian), try sending a blocked signal to make sure the mask matches the
+ // signal. (Send URG and CHLD because they're ignored by default in the
+ // debugger, vs. USR1 or other named signals)
+ inline for ([_]usize{ posix.SIG.URG, posix.SIG.CHLD, 62, 94, 126 }) |test_signo| {
+ if (test_signo >= posix.NSIG) continue;
+
+ S.expected_sig = test_signo;
+ S.handler_called_count = 0;
+
+ const sa: posix.Sigaction = .{
+ .handler = .{ .sigaction = &S.handler },
+ .mask = posix.sigemptyset(),
+ .flags = posix.SA.SIGINFO | posix.SA.RESETHAND,
+ };
+
+ var old_sa: posix.Sigaction = undefined;
+
+ // Install the new signal handler.
+ posix.sigaction(test_signo, &sa, &old_sa);
+
+ // block the signal and see that its delayed until unblocked
+ var block_one: posix.sigset_t = posix.sigemptyset();
+ posix.sigaddset(&block_one, test_signo);
+ posix.sigprocmask(posix.SIG.BLOCK, &block_one, null);
+
+ // qemu maps target signals to host signals 1-to-1, so targets
+ // with more signals than the host will fail to send the signal.
+ const rc = posix.system.kill(self_pid, test_signo);
+ switch (posix.errno(rc)) {
+ .SUCCESS => {
+ // See that the signal is blocked, then unblocked
+ try testing.expectEqual(0, S.handler_called_count);
+ posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null);
+ try testing.expectEqual(1, S.handler_called_count);
+ },
+ .INVAL => {
+ // Signal won't get delviered. Just clean up.
+ posix.sigprocmask(posix.SIG.UNBLOCK, &block_one, null);
+ try testing.expectEqual(0, S.handler_called_count);
+ },
+ else => |errno| return posix.unexpectedErrno(errno),
+ }
+
+ // Restore original handler
+ posix.sigaction(test_signo, &old_sa, null);
+ }
}
test "dup & dup2" {
diff --git a/lib/std/start.zig b/lib/std/start.zig
index 9072c97c02..6495db1c51 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -749,7 +749,7 @@ fn maybeIgnoreSigpipe() void {
// Set handler to a noop function instead of `SIG.IGN` to prevent
// leaking signal disposition to a child process.
.handler = .{ .handler = noopSigHandler },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = 0,
};
posix.sigaction(posix.SIG.PIPE, &act, null);
diff --git a/src/crash_report.zig b/src/crash_report.zig
index 431381fce4..6f043c3bfb 100644
--- a/src/crash_report.zig
+++ b/src/crash_report.zig
@@ -175,12 +175,11 @@ pub fn attachSegfaultHandler() void {
_ = windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows);
return;
}
- var act: posix.Sigaction = .{
+ const act: posix.Sigaction = .{
.handler = .{ .sigaction = handleSegfaultPosix },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = (posix.SA.SIGINFO | posix.SA.RESTART | posix.SA.RESETHAND),
};
-
debug.updateSegfaultHandler(&act);
}
diff --git a/test/standalone/sigpipe/build.zig b/test/standalone/sigpipe/build.zig
index 8e1cb5e122..de06e1df15 100644
--- a/test/standalone/sigpipe/build.zig
+++ b/test/standalone/sigpipe/build.zig
@@ -18,7 +18,7 @@ pub fn build(b: *std.build.Builder) !void {
{
const act = posix.Sigaction{
.handler = .{ .handler = posix.SIG.DFL },
- .mask = posix.empty_sigset,
+ .mask = posix.sigemptyset(),
.flags = 0,
};
try posix.sigaction(posix.SIG.PIPE, &act, null);