diff --git a/include/bits/signal.h b/include/bits/signal.h index 57832b5e36..013540fd5f 100644 --- a/include/bits/signal.h +++ b/include/bits/signal.h @@ -5,7 +5,6 @@ #define SIG_IGN ((void (*)(int))1) #define SIG_ERR ((void (*)(int))-1) -struct siginfo; typedef struct siginfo siginfo_t; typedef unsigned long long sigset_t; diff --git a/redox-rt/src/arch/x86_64.rs b/redox-rt/src/arch/x86_64.rs index d0d0f9bf04..a0756c1282 100644 --- a/redox-rt/src/arch/x86_64.rs +++ b/redox-rt/src/arch/x86_64.rs @@ -25,6 +25,9 @@ pub struct SigArea { pub tmp_rsp: usize, pub tmp_rax: usize, pub tmp_rdx: usize, + pub tmp_rdi: usize, + pub tmp_rsi: usize, + pub tmp_ptr: usize, pub altstack_top: usize, pub altstack_bottom: usize, @@ -170,6 +173,8 @@ asmfunction!(__relibc_internal_sigentry: [" mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx + mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi + mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi // First, select signal, always pick first available bit 1: @@ -201,11 +206,18 @@ asmfunction!(__relibc_internal_sigentry: [" jz 7f bt edx, eax // check if signal was sent to thread specifically - jc 2f // then continue as usual + jc 2f // if so, continue as usual - // otherwise, try clearing pending - lock btr [rip + {pctl} + {pctl_off_pending}], eax - jnc 1b + // otherwise, try (competitively) dequeueing realtime signal + mov esi, eax + mov eax, {SYS_SIGDEQUEUE} + mov rdi, fs:[0] + add rdi, {tcb_sa_off} + {sa_tmp_ptr} // out pointer of dequeued realtime sig + syscall + test eax, eax + jnz 1b // assumes error can only be EAGAIN + lea eax, [esi + 32] + jmp 9f 2: mov edx, eax shr edx, 5 @@ -220,7 +232,7 @@ asmfunction!(__relibc_internal_sigentry: [" // skip the sigaltstack logic. lea rdx, [rip + {pctl} + {pctl_off_actions}] - // LEA doesn't support x16, so just do two x8s. + // LEA doesn't support 16x, so just do two x8s. lea rdx, [rdx + 8 * rax] lea rdx, [rdx + 8 * rax] @@ -247,8 +259,8 @@ asmfunction!(__relibc_internal_sigentry: [" push fs:[{tcb_sc_off} + {sc_saved_rip}] push fs:[{tcb_sc_off} + {sc_saved_rflags}] - push rdi - push rsi + push fs:[{tcb_sa_off} + {sa_tmp_rdi}] + push fs:[{tcb_sa_off} + {sa_tmp_rsi}] push fs:[{tcb_sa_off} + {sa_tmp_rdx}] push rcx push fs:[{tcb_sa_off} + {sa_tmp_rax}] @@ -288,7 +300,7 @@ asmfunction!(__relibc_internal_sigentry: [" vextractf128 [rsp], ymm15, 1 5: push rax // selected signal - sub rsp, 8 + push fs:[{tcb_sa_off} + {sa_tmp_ptr}] mov rdi, rsp call {inner} @@ -383,6 +395,9 @@ __relibc_internal_sigentry_crit_third: sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp), sa_tmp_rax = const offset_of!(SigArea, tmp_rax), sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx), + sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi), + sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi), + sa_tmp_ptr = const offset_of!(SigArea, tmp_ptr), sa_altstack_top = const offset_of!(SigArea, altstack_top), sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom), sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg), @@ -398,6 +413,7 @@ __relibc_internal_sigentry_crit_third: REDZONE_SIZE = const 128, STACK_ALIGN = const 16, SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000 + SYS_SIGDEQUEUE = const syscall::SYS_SIGDEQUEUE, ]); extern "C" { diff --git a/redox-rt/src/signal.rs b/redox-rt/src/signal.rs index 95443b01c4..529d1b3564 100644 --- a/redox-rt/src/signal.rs +++ b/redox-rt/src/signal.rs @@ -1,14 +1,14 @@ use core::{ cell::{Cell, UnsafeCell}, - ffi::c_int, + ffi::{c_int, c_void}, sync::atomic::{AtomicU8, AtomicUsize, Ordering}, }; use syscall::{ - data::AtomicU64, Error, NonatomicUsize, RawAction, RealtimeSig, Result, SetSighandlerData, - SigProcControl, Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, - SIGCONT, SIGFPE, SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, - SIGTTOU, SIGURG, SIGWINCH, SIGXCPU, SIGXFSZ, + data::AtomicU64, Error, NonatomicUsize, RawAction, Result, SetSighandlerData, SigProcControl, + Sigcontrol, SigcontrolFlags, EINVAL, ENOMEM, EPERM, SIGABRT, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, + SIGILL, SIGKILL, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, + SIGWINCH, SIGXCPU, SIGXFSZ, }; use crate::{arch::*, proc::FdGuard, sync::Mutex, RtTcb, Tcb}; @@ -25,11 +25,12 @@ pub fn sighandler_function() -> usize { #[repr(C)] pub struct SigStack { #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] - _pad: [usize; 1], // pad to 16 bytes alignment + _pad: [usize; 0], // pad to 16 bytes alignment #[cfg(target_arch = "x86")] - _pad: [usize; 3], // pad to 16 bytes alignment + _pad: [usize; 2], // pad to 16 bytes alignment + sival: usize, sig_num: usize, // x86_64: 864 bytes @@ -119,10 +120,33 @@ unsafe fn inner(stack: &mut SigStack) { && let Some(sigaction) = handler.sigaction { //let _ = syscall::write(1, alloc::format!("SIGACTION {:p}\n", sigaction).as_bytes()); + // TODO: This struct is for practical reasons locked to Linux's ABI, but avoid redefining + // it here. Alternatively, check at compile time that the structs are equivalent. + #[repr(C)] + struct siginfo { + si_signo: c_int, + si_errno: c_int, + si_code: c_int, + si_pid: c_int, // TODO pid_t + si_uid: c_int, // TODO uid_t + si_addr: *mut c_void, + si_status: c_int, + si_value: usize, // TODO union + } + let info = siginfo { + si_signo: stack.sig_num as c_int, + si_addr: core::ptr::null_mut(), + si_code: 0, // TODO: SIG_QUEUE/SIG_USER/etc + si_errno: 0, + si_pid: 0, + si_status: 0, + si_uid: 0, + si_value: stack.sival, + }; sigaction( stack.sig_num as c_int, - core::ptr::null_mut(), - core::ptr::null_mut(), + core::ptr::addr_of!(info).cast(), + core::ptr::null_mut(), // TODO ); } else if let Some(handler) = handler.handler { //let _ = syscall::write(1, alloc::format!("HANDLER {:p}\n", handler).as_bytes()); @@ -232,9 +256,9 @@ fn modify_sigmask(old: Option<&mut u64>, op: Option u32 // POSIX requires that at least one pending unblocked signal be delivered before // pthread_sigmask returns, if there is one. Deliver the lowest-numbered one. if can_raise != 0 { - let signal = can_raise.trailing_zeros() + 1; - // TODO - crate::sys::posix_kill_thread(**RtTcb::current().thread_fd(), signal); + unsafe { + manually_enter_trampoline(); + } } Ok(()) @@ -443,14 +467,6 @@ pub(crate) static PROC_CONTROL_STRUCT: SigProcControl = SigProcControl { user_data: AtomicU64::new(0), } }; 64], - qhead: AtomicU8::new(0), - _rsvd: [0; 7], - queue: [const { - RealtimeSig { - arg: NonatomicUsize::new(0), - } - }; 32], - qtail: AtomicU8::new(0), }; fn combine_allowset([lo, hi]: [u64; 2]) -> u64 { diff --git a/redox-rt/src/sys.rs b/redox-rt/src/sys.rs index 97e7ce7385..e3a1db8892 100644 --- a/redox-rt/src/sys.rs +++ b/redox-rt/src/sys.rs @@ -46,7 +46,7 @@ pub fn posix_kill(pid: usize, sig: usize) -> Result<()> { #[inline] pub fn posix_sigqueue(pid: usize, sig: usize, val: usize) -> Result<()> { match wrapper(false, || unsafe { - syscall::syscall3(syscall::SYS_SIGQUEUE, pid, sig, val) + syscall::syscall3(syscall::SYS_SIGENQUEUE, pid, sig, val) }) { Ok(_) | Err(Error { errno: EINTR }) => Ok(()), Err(error) => Err(error), diff --git a/src/header/signal/linux.rs b/src/header/signal/linux.rs index fb23a0e99b..f17a55c6c3 100644 --- a/src/header/signal/linux.rs +++ b/src/header/signal/linux.rs @@ -76,3 +76,5 @@ pub const SS_DISABLE: usize = 2; // Those two should be updated from kernel headers pub const MINSIGSTKSZ: usize = 2048; pub const SIGSTKSZ: usize = 8096; + +pub const SI_QUEUE: i32 = -1; diff --git a/src/header/signal/mod.rs b/src/header/signal/mod.rs index f3c58111a5..6fe93ed266 100644 --- a/src/header/signal/mod.rs +++ b/src/header/signal/mod.rs @@ -55,21 +55,26 @@ pub struct sigaltstack { } #[repr(C)] -#[derive(Clone, Debug)] +#[derive(Clone, Copy)] pub struct siginfo { pub si_signo: c_int, pub si_errno: c_int, pub si_code: c_int, - _padding: [c_int; 29], - _si_align: [usize; 0], + pub si_pid: pid_t, + pub si_uid: uid_t, + pub si_addr: *mut c_void, + pub si_status: c_int, + pub si_value: sigval, } #[no_mangle] pub extern "C" fn _cbindgen_export_siginfo(a: siginfo) {} +#[derive(Clone, Copy)] +#[repr(C)] pub union sigval { - pub si_int: c_int, - pub si_ptr: *mut c_void, + pub sival_int: c_int, + pub sival_ptr: *mut c_void, } /// cbindgen:ignore @@ -371,7 +376,7 @@ pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int #[no_mangle] pub unsafe extern "C" fn sigtimedwait( set: *const sigset_t, - sig: *mut siginfo_t, + sig: *mut siginfo, // https://github.com/mozilla/cbindgen/issues/621 tp: *const timespec, ) -> c_int { Sys::sigtimedwait(set, sig, tp) diff --git a/src/platform/linux/signal.rs b/src/platform/linux/signal.rs index d25a6139ef..b1d4d2e068 100644 --- a/src/platform/linux/signal.rs +++ b/src/platform/linux/signal.rs @@ -1,4 +1,5 @@ -use core::mem; +use crate::header::signal::sigval; +use core::{mem, ptr::addr_of}; use super::{ super::{types::*, PalSignal}, @@ -7,7 +8,7 @@ use super::{ use crate::{ error::Errno, header::{ - signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG, SA_RESTORER}, + signal::{sigaction, siginfo_t, sigset_t, stack_t, NSIG, SA_RESTORER, SI_QUEUE}, sys_time::itimerval, time::timespec, }, @@ -21,6 +22,19 @@ impl PalSignal for Sys { fn kill(pid: pid_t, sig: c_int) -> c_int { e(unsafe { syscall!(KILL, pid, sig) }) as c_int } + fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<(), Errno> { + let info = siginfo_t { + si_addr: core::ptr::null_mut(), + si_code: SI_QUEUE, + si_errno: 0, + si_pid: 0, // TODO: GETPID? + si_signo: sig, + si_status: 0, + si_uid: 0, // TODO: GETUID? + si_value: val, + }; + e_raw(unsafe { syscall!(RT_SIGQUEUEINFO, pid, sig, addr_of!(info)) }).map(|_| ()) + } fn killpg(pgrp: pid_t, sig: c_int) -> c_int { e(unsafe { syscall!(KILL, -(pgrp as isize) as pid_t, sig) }) as c_int diff --git a/src/platform/redox/signal.rs b/src/platform/redox/signal.rs index 1f71784f86..6f343754c5 100644 --- a/src/platform/redox/signal.rs +++ b/src/platform/redox/signal.rs @@ -59,7 +59,7 @@ impl PalSignal for Sys { Ok(redox_rt::sys::posix_sigqueue( pid as usize, sig as usize, - unsafe { val.si_ptr } as usize, + unsafe { val.sival_ptr } as usize, )?) } diff --git a/tests/Makefile b/tests/Makefile index 774a03e86a..b2ce84cbf7 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -163,6 +163,7 @@ NAMES=\ sa_restart \ sigchld \ stdio/ctermid \ + sigqueue \ stdio/tempnam \ stdio/tmpnam \ stdlib/bsearch \ diff --git a/tests/sigqueue.c b/tests/sigqueue.c new file mode 100644 index 0000000000..a69df9645b --- /dev/null +++ b/tests/sigqueue.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.h" + +#define THE_SIG SIGRTMIN + +volatile sig_atomic_t num = 1; + +void action(int sig, siginfo_t *info, void *context) { + (void)context; + assert(sig == THE_SIG); + assert(info->si_signo == THE_SIG); + assert(info->si_value.sival_int == num); + num++; + write(1, "action\n", 7); +} + +int main(void) { + int status, fds[2]; + + status = pipe(fds); + ERROR_IF(pipe, status, == -1); + + int child = fork(); + ERROR_IF(fork, child, == -1); + + status = close(fds[child == 0 ? 0 : 1]); + ERROR_IF(close, status, == -1); + + sigset_t set; + status = sigfillset(&set); + ERROR_IF(sigfillset, status, == -1); + status = sigdelset(&set, SIGSEGV); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGBUS); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGILL); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGFPE); + ERROR_IF(sigdelset, status, == -1); + status = sigdelset(&set, SIGINT); + ERROR_IF(sigdelset, status, == -1); + status = sigprocmask(SIG_SETMASK, &set, NULL); + ERROR_IF(sigprocmask, status, == -1); + + struct sigaction sa; + memcpy(&sa.sa_mask, &set, sizeof (sigset_t)); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = action; + + status = sigaction(THE_SIG, &sa, NULL); + ERROR_IF(sigaction, status, == -1); + + if (child == 0) { + status = sigemptyset(&set); + ERROR_IF(sigemptyset, status, == -1); + while (num != 32) { + } + status = write(fds[1], "A", 1); + ERROR_IF(write, status, == -1); + } else { + for (int n = 1; n <= 32; n++) { + status = sigqueue(child, THE_SIG, (union sigval){ .sival_int = n }); + ERROR_IF(sigqueue, status, == -1); + } + char buf[1]; + status = read(fds[0], buf, 1); + ERROR_IF(read, status, == -1); + } + + return 0; +}