Implement basic support for (kernel) rt signals.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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" {
|
||||
|
||||
+36
-20
@@ -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<impl FnMut(u32, bool) -> 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 {
|
||||
|
||||
+1
-1
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)?)
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ NAMES=\
|
||||
sa_restart \
|
||||
sigchld \
|
||||
stdio/ctermid \
|
||||
sigqueue \
|
||||
stdio/tempnam \
|
||||
stdio/tmpnam \
|
||||
stdlib/bsearch \
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user