Implement basic support for (kernel) rt signals.

This commit is contained in:
4lDO2
2024-07-28 23:20:51 +02:00
parent 47e07654cb
commit 13e7b77643
10 changed files with 170 additions and 39 deletions
-1
View File
@@ -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;
+24 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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),
+2
View File
@@ -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;
+11 -6
View File
@@ -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)
+16 -2
View File
@@ -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
+1 -1
View File
@@ -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,
)?)
}
+1
View File
@@ -163,6 +163,7 @@ NAMES=\
sa_restart \
sigchld \
stdio/ctermid \
sigqueue \
stdio/tempnam \
stdio/tmpnam \
stdlib/bsearch \
+78
View File
@@ -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;
}