rt: signal handling in yggdrasil-rt
This commit is contained in:
parent
cbd823e17b
commit
ed7f6c2f46
kernel
lib/runtime/src/process
test.cuserspace/lib/ygglibc
@ -1,5 +1,5 @@
|
|||||||
use core::{
|
use core::{
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -82,6 +82,7 @@ pub struct Process {
|
|||||||
parent: Option<Weak<Process>>,
|
parent: Option<Weak<Process>>,
|
||||||
|
|
||||||
inner: IrqSafeRwLock<ProcessInner>,
|
inner: IrqSafeRwLock<ProcessInner>,
|
||||||
|
signal_entry: AtomicUsize,
|
||||||
|
|
||||||
pub(crate) exit: OneTimeEvent<ExitCode>,
|
pub(crate) exit: OneTimeEvent<ExitCode>,
|
||||||
|
|
||||||
@ -107,6 +108,7 @@ impl Process {
|
|||||||
id,
|
id,
|
||||||
parent,
|
parent,
|
||||||
|
|
||||||
|
signal_entry: AtomicUsize::new(0),
|
||||||
inner: IrqSafeRwLock::new(ProcessInner::new(id, group_id, Some(space.clone()), image)),
|
inner: IrqSafeRwLock::new(ProcessInner::new(id, group_id, Some(space.clone()), image)),
|
||||||
|
|
||||||
exit: OneTimeEvent::new(),
|
exit: OneTimeEvent::new(),
|
||||||
@ -323,6 +325,16 @@ impl Process {
|
|||||||
inner.space = None;
|
inner.space = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the signal entry point
|
||||||
|
pub fn set_signal_entry(&self, entry: usize) {
|
||||||
|
self.signal_entry.store(entry, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves current signal entry of the process
|
||||||
|
pub fn signal_entry(&self) -> usize {
|
||||||
|
self.signal_entry.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
/// Raises a signal for the specified process
|
/// Raises a signal for the specified process
|
||||||
///
|
///
|
||||||
/// If sender is Some(id) - signal was sent by some thread in some (possibly this) process.
|
/// If sender is Some(id) - signal was sent by some thread in some (possibly this) process.
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
use core::{cell::Cell, mem::size_of, ops::Deref};
|
use core::{
|
||||||
|
cell::Cell,
|
||||||
|
mem::size_of,
|
||||||
|
ops::Deref,
|
||||||
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
collections::{btree_map, BTreeMap},
|
collections::{btree_map, BTreeMap},
|
||||||
@ -59,10 +64,6 @@ struct SignalEntry {
|
|||||||
stack: usize,
|
stack: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ThreadInner {
|
|
||||||
signal_entry: Option<SignalEntry>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes a single thread within the system
|
/// Describes a single thread within the system
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
/// Unique thread ID
|
/// Unique thread ID
|
||||||
@ -77,7 +78,8 @@ pub struct Thread {
|
|||||||
space: Option<Arc<ProcessAddressSpace>>,
|
space: Option<Arc<ProcessAddressSpace>>,
|
||||||
debug: IrqSafeSpinlock<ThreadDebuggingInfo>,
|
debug: IrqSafeSpinlock<ThreadDebuggingInfo>,
|
||||||
|
|
||||||
inner: IrqSafeSpinlock<ThreadInner>,
|
// inner: IrqSafeSpinlock<ThreadInner>,
|
||||||
|
signal_stack: AtomicUsize,
|
||||||
signal_queue: SegQueue<Signal>,
|
signal_queue: SegQueue<Signal>,
|
||||||
|
|
||||||
pub exit: Arc<BoolEvent>,
|
pub exit: Arc<BoolEvent>,
|
||||||
@ -133,7 +135,7 @@ impl Thread {
|
|||||||
process,
|
process,
|
||||||
space,
|
space,
|
||||||
|
|
||||||
inner: IrqSafeSpinlock::new(ThreadInner { signal_entry: None }),
|
signal_stack: AtomicUsize::new(0),
|
||||||
signal_queue: SegQueue::new(),
|
signal_queue: SegQueue::new(),
|
||||||
exit: Arc::new(BoolEvent::new()),
|
exit: Arc::new(BoolEvent::new()),
|
||||||
kill: BoolEvent::new(),
|
kill: BoolEvent::new(),
|
||||||
@ -221,10 +223,9 @@ impl Thread {
|
|||||||
self.affinity.set(affinity);
|
self.affinity.set(affinity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the thread signal entry/stack information
|
/// Updates the thread signal stack information
|
||||||
pub fn set_signal_entry(&self, entry: usize, stack: usize) {
|
pub fn set_signal_stack(&self, stack: usize) {
|
||||||
let mut inner = self.inner.lock();
|
self.signal_stack.store(stack, Ordering::Release);
|
||||||
inner.signal_entry.replace(SignalEntry { entry, stack });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the thread address space (usually provided by its parent process). If none exists,
|
/// Returns the thread address space (usually provided by its parent process). If none exists,
|
||||||
@ -266,6 +267,7 @@ impl Thread {
|
|||||||
|
|
||||||
/// Pushes a signal to the thread's signal queue
|
/// Pushes a signal to the thread's signal queue
|
||||||
pub fn raise_signal(&self, signal: Signal) {
|
pub fn raise_signal(&self, signal: Signal) {
|
||||||
|
log::debug!("{}: raise signal {signal:?}", self.id);
|
||||||
self.signal_queue.push(signal);
|
self.signal_queue.push(signal);
|
||||||
self.enqueue();
|
self.enqueue();
|
||||||
}
|
}
|
||||||
@ -622,6 +624,8 @@ impl CurrentThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(signal) = self.signal_queue.pop() {
|
if let Some(signal) = self.signal_queue.pop() {
|
||||||
|
log::debug!("{}: handle signal {signal:?}", self.id);
|
||||||
|
|
||||||
if signal == Signal::Debug {
|
if signal == Signal::Debug {
|
||||||
frame.set_single_step(true);
|
frame.set_single_step(true);
|
||||||
|
|
||||||
@ -638,19 +642,22 @@ impl CurrentThread {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner = self.inner.lock();
|
let ip = self.process().signal_entry();
|
||||||
|
let sp = self.signal_stack.load(Ordering::Acquire);
|
||||||
|
|
||||||
let Some(entry) = inner.signal_entry.as_ref() else {
|
// Check if ip and sp are legible for signal entry
|
||||||
drop(inner);
|
if ip == 0 || sp < 4096 {
|
||||||
|
log::warn!("No legible signal handler for {}", self.id);
|
||||||
self.exit_process(ExitCode::BySignal(signal));
|
self.exit_process(ExitCode::BySignal(signal));
|
||||||
};
|
}
|
||||||
|
|
||||||
// TODO check if really in a syscall, lol
|
// TODO check if really in a syscall, lol
|
||||||
let syscall_return = -(u32::from(Error::Interrupted) as isize);
|
let syscall_return = -(u32::from(Error::Interrupted) as isize);
|
||||||
frame.set_return_value(syscall_return as u64);
|
frame.set_return_value(syscall_return as u64);
|
||||||
|
|
||||||
// Setup signal frame
|
// Setup signal frame
|
||||||
let usp = ((entry.stack - size_of::<SignalEntryData>()) & !0xF)
|
// FIXME use write_foreign_*** for this
|
||||||
|
let usp = ((sp - size_of::<SignalEntryData>()) & !0xF)
|
||||||
- TaskContextImpl::SIGNAL_STACK_EXTRA_ALIGN;
|
- TaskContextImpl::SIGNAL_STACK_EXTRA_ALIGN;
|
||||||
let frame_ptr = usp as *mut SignalEntryData;
|
let frame_ptr = usp as *mut SignalEntryData;
|
||||||
|
|
||||||
@ -666,13 +673,13 @@ impl CurrentThread {
|
|||||||
// Setup return to signal handler
|
// Setup return to signal handler
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Signal entry @ pc={:#x}, sp={:#x} (top = {:#x})",
|
"Signal entry @ pc={:#x}, sp={:#x} (top = {:#x})",
|
||||||
entry.entry,
|
ip,
|
||||||
usp,
|
usp,
|
||||||
entry.stack
|
sp,
|
||||||
);
|
);
|
||||||
|
|
||||||
frame.set_user_sp(usp);
|
frame.set_user_sp(usp);
|
||||||
frame.set_user_ip(entry.entry);
|
frame.set_user_ip(ip);
|
||||||
|
|
||||||
// Pass the frame pointer as an argument to signal handler entry
|
// Pass the frame pointer as an argument to signal handler entry
|
||||||
frame.set_argument(usp as _);
|
frame.set_argument(usp as _);
|
||||||
|
@ -201,7 +201,14 @@ pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
|
|||||||
|
|
||||||
pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
|
pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
|
||||||
let thread = Thread::current();
|
let thread = Thread::current();
|
||||||
thread.set_signal_entry(ip, sp);
|
let process = thread.process();
|
||||||
|
log::debug!("{}: set_signal_entry({:#x}, {:#x})", thread.id, ip, sp);
|
||||||
|
if ip != usize::MAX {
|
||||||
|
process.set_signal_entry(ip);
|
||||||
|
}
|
||||||
|
if sp != usize::MAX {
|
||||||
|
thread.set_signal_stack(sp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
||||||
|
@ -6,4 +6,5 @@ pub use abi::process::{
|
|||||||
SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions,
|
SpawnOption, SpawnOptions, StringArgIter, ThreadId, ThreadSpawnOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod signal;
|
||||||
pub mod thread_local;
|
pub mod thread_local;
|
||||||
|
148
lib/runtime/src/process/signal.rs
Normal file
148
lib/runtime/src/process/signal.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
//! Runtime utilities for signal handling
|
||||||
|
use core::ffi::c_int;
|
||||||
|
|
||||||
|
use abi::{
|
||||||
|
error::Error,
|
||||||
|
mem::{MappingFlags, MappingSource},
|
||||||
|
process::{ExitCode, Signal, SignalEntryData},
|
||||||
|
};
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
|
/// Describes how a signal should be handled
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum SignalHandler {
|
||||||
|
/// Terminate when received
|
||||||
|
Terminate,
|
||||||
|
/// Silently ignore the signal
|
||||||
|
Ignore,
|
||||||
|
/// Pass the signal to a custom function (Rust version)
|
||||||
|
Rust(fn(Signal)),
|
||||||
|
/// Pass the signal to a custom function (C compat version)
|
||||||
|
C(unsafe extern "C" fn(c_int)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO RwLock here
|
||||||
|
const MAX_SIGNALS: usize = 16;
|
||||||
|
static mut TABLE: [SignalHandler; MAX_SIGNALS] = [const { SignalHandler::Terminate }; MAX_SIGNALS];
|
||||||
|
|
||||||
|
unsafe extern "C" fn common_signal_entry(data: &SignalEntryData) -> ! {
|
||||||
|
let index = data.signal.into_raw() as usize;
|
||||||
|
|
||||||
|
if index >= MAX_SIGNALS {
|
||||||
|
terminate_by_signal(data.signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
match TABLE[index] {
|
||||||
|
SignalHandler::Rust(function) => function(data.signal),
|
||||||
|
SignalHandler::C(function) => {
|
||||||
|
let signum = data.signal.into_raw() as i32;
|
||||||
|
function(signum);
|
||||||
|
}
|
||||||
|
SignalHandler::Terminate => terminate_by_signal(data.signal),
|
||||||
|
SignalHandler::Ignore => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::sys::exit_signal(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add signal print hook function to dump signal info here
|
||||||
|
fn terminate_by_signal(signal: Signal) -> ! {
|
||||||
|
crate::debug_trace!("runtime: terminated by signal: {signal:?}");
|
||||||
|
unsafe { crate::sys::exit_process(ExitCode::BySignal(signal)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the handler for a particular signal. Returns the old handler used for that signal.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Marked as unsafe due to being thread-unsafe. Will be lifted once I port RwLock into the runtime
|
||||||
|
/// crate.
|
||||||
|
pub unsafe fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler {
|
||||||
|
let entry = &mut TABLE[signal.into_raw() as usize];
|
||||||
|
let old_handler = core::mem::replace(entry, handler);
|
||||||
|
old_handler
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the stack that will be used to handle signals **on this thread**.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// To be legible for signal handling, `sp` must be non-NULL and have at least 4K of space
|
||||||
|
/// below it. It is up to the caller to make sure `sp` points to a proper stack's top.
|
||||||
|
///
|
||||||
|
/// TLDR: just use [setup_signal_stack].
|
||||||
|
pub unsafe fn set_signal_stack(sp: usize) {
|
||||||
|
crate::sys::set_signal_entry(usize::MAX, sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate_signal_stack(mut size: usize) -> Result<usize, Error> {
|
||||||
|
if size == 0 {
|
||||||
|
size = 4096;
|
||||||
|
}
|
||||||
|
size = (size + 0xFFF) & !0xFFF;
|
||||||
|
let base = unsafe {
|
||||||
|
crate::sys::map_memory(None, size, MappingFlags::WRITE, &MappingSource::Anonymous)
|
||||||
|
}?;
|
||||||
|
let top = base + size;
|
||||||
|
Ok(top)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates a signal stack and sets it **for this thread**. Returns an error if allocation fails.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This function may allocate more memory than requested in the `size` parameter. It will allocate
|
||||||
|
/// at least 4KiB of memory, rounding the size up to a page boundary. This is required for the
|
||||||
|
/// stack to be legible for entry from the kernel.
|
||||||
|
///
|
||||||
|
/// The allocated stack is expected to live forever or at least until it is replaced by another
|
||||||
|
/// one, so current implementation just leaks the stack. The implementation also provides no
|
||||||
|
/// guarantees on what will happen if a raw syscall to [crate::sys::set_signal_entry] is done
|
||||||
|
/// after calling this function.
|
||||||
|
pub fn setup_signal_stack(size: usize) -> Result<(), Error> {
|
||||||
|
let sp = allocate_signal_stack(size)?;
|
||||||
|
unsafe { set_signal_stack(sp) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs full signal setup for the process:
|
||||||
|
///
|
||||||
|
/// * Allocates a signal stack for **the current thread**.
|
||||||
|
/// * Sets up a signal entry into the runtime-managed handler for the whole process.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This function is intended to be called from the main thread, while [setup_signal_stack] is
|
||||||
|
/// intended for setting up thread signal stacks. A caller *may* call this function elsewhere,
|
||||||
|
/// and this will be safe, but likely will not make any sense.
|
||||||
|
///
|
||||||
|
/// Also see notes for [setup_signal_stack].
|
||||||
|
pub fn setup_signal_full(size: usize) -> Result<(), Error> {
|
||||||
|
let sp = allocate_signal_stack(size)?;
|
||||||
|
unsafe { crate::sys::set_signal_entry(imp::signal_entry as usize, sp) };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// implementation details
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||||
|
mod imp {
|
||||||
|
// i686 is a bit tricky: cdecl ABI requires the arguments to be passed on the stack, while
|
||||||
|
// the kernel does the easy thing and just puts it into %eax, so a fixup needs to be done
|
||||||
|
// before entering a proper handler. The wrapper also aligns the stack nicely.
|
||||||
|
#[naked]
|
||||||
|
pub(super) unsafe extern "C" fn signal_entry() -> ! {
|
||||||
|
// %eax - SignalEntryData pointer
|
||||||
|
core::arch::naked_asm!(
|
||||||
|
r#"
|
||||||
|
jmp .
|
||||||
|
"#,
|
||||||
|
options(att_syntax)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(not(target_arch = "x86"), rust_analyzer))]
|
||||||
|
mod imp {
|
||||||
|
pub(super) use super::common_signal_entry as signal_entry;
|
||||||
|
}
|
17
test.c
17
test.c
@ -1,20 +1,3 @@
|
|||||||
#include <pthread.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/yggdrasil.h>
|
|
||||||
|
|
||||||
static void *function(void *arg) {
|
|
||||||
abort();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
assert(pthread_create(&thread, NULL, function, (void *) NULL) == 0);
|
|
||||||
pthread_join(thread, NULL);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#ifndef _YGGDRASIL_SETJMP_H
|
#ifndef _YGGDRASIL_SETJMP_H
|
||||||
#define _YGGDRASIL_SETJMP_H 1
|
#define _YGGDRASIL_SETJMP_H 1
|
||||||
|
|
||||||
|
int setjmp(jmp_buf buf);
|
||||||
|
int _setjmp(jmp_buf buf);
|
||||||
|
|
||||||
|
[[noreturn]] void longjmp(jmp_buf buf, int val);
|
||||||
|
[[noreturn]] void _longjmp(jmp_buf buf, int val);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -11,7 +11,7 @@ typedef _Atomic int sig_atomic_t;
|
|||||||
|
|
||||||
#define SIG_IGN __sig_ignore
|
#define SIG_IGN __sig_ignore
|
||||||
// TODO
|
// TODO
|
||||||
#define SIG_ERR __sig_terminate
|
#define SIG_ERR ((sig_handler_t) 1)
|
||||||
// TODO
|
// TODO
|
||||||
#define SIG_HOLD __sig_terminate
|
#define SIG_HOLD __sig_terminate
|
||||||
|
|
||||||
|
@ -102,10 +102,13 @@ impl<T> EResult<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn expect(self, message: &str) -> T {
|
pub fn expect(self, message: &str) -> T {
|
||||||
match self {
|
match self {
|
||||||
Self::Ok(value) => value,
|
Self::Ok(value) => value,
|
||||||
Self::Err(err) => panic!("expect() failed: {}, errno={:?}", message, err)
|
Self::Err(err) => {
|
||||||
|
panic!("expect() failed: {}, errno={:?}", message, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,4 @@ exclude = [
|
|||||||
"longjmp",
|
"longjmp",
|
||||||
"_setjmp",
|
"_setjmp",
|
||||||
"_longjmp",
|
"_longjmp",
|
||||||
"__x86_64_setjmp",
|
|
||||||
"__x86_64_longjmp"
|
|
||||||
]
|
]
|
||||||
|
@ -2,11 +2,10 @@ use core::ffi::{c_char, c_int, c_long, c_void};
|
|||||||
|
|
||||||
use yggdrasil_rt::process::Signal;
|
use yggdrasil_rt::process::Signal;
|
||||||
|
|
||||||
use crate::{error::CIntZeroResult, util::PointerExt};
|
use crate::{error::{self, CIntZeroResult, EResult, TryFromExt}, signal, util::PointerExt};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
sys_time::__ygg_timespec_t,
|
errno, sys_time::__ygg_timespec_t, sys_types::{pid_t, uid_t}
|
||||||
sys_types::{pid_t, uid_t},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
|
pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
|
||||||
@ -104,16 +103,15 @@ impl SigNumber {
|
|||||||
pub const INVALID: Self = Self(-65536);
|
pub const INVALID: Self = Self(-65536);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<SigNumber> for Signal {
|
impl TryFromExt<SigNumber> for Signal {
|
||||||
type Error = ();
|
fn e_try_from(value: SigNumber) -> EResult<Self> {
|
||||||
|
|
||||||
fn try_from(value: SigNumber) -> Result<Self, Self::Error> {
|
|
||||||
match value {
|
match value {
|
||||||
SIGSEGV => Ok(Signal::MemoryAccessViolation),
|
SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation),
|
||||||
SIGABRT => Ok(Signal::Aborted),
|
SIGABRT => EResult::Ok(Signal::Aborted),
|
||||||
SIGKILL => Ok(Signal::Killed),
|
SIGKILL => EResult::Ok(Signal::Killed),
|
||||||
SIGINT => Ok(Signal::Interrupted),
|
SIGINT => EResult::Ok(Signal::Interrupted),
|
||||||
_ => Err(()),
|
SIGTRAP => EResult::Ok(Signal::Debug),
|
||||||
|
_ => EResult::Err(errno::EINVAL),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,8 +227,25 @@ unsafe extern "C" fn sigismember(_mask: *const sigset_t, _signum: c_int) -> c_in
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn signal(_handler: sig_handler_t, _signum: c_int) -> sig_handler_t {
|
unsafe extern "C" fn signal(handler: sig_handler_t, signum: c_int) -> sig_handler_t {
|
||||||
todo!()
|
// Validate handler
|
||||||
|
let address: usize = core::mem::transmute(handler);
|
||||||
|
// NULL or SIG_ERR
|
||||||
|
if address == 0 || address == 1 {
|
||||||
|
yggdrasil_rt::debug_trace!("libc: signal() was passed an invalid handler");
|
||||||
|
error::errno = errno::EINVAL;
|
||||||
|
// SIG_ERR
|
||||||
|
return core::mem::transmute(1usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
match signal::set(SigNumber(signum), handler) {
|
||||||
|
EResult::Ok(handler) => handler,
|
||||||
|
EResult::Err(err) => {
|
||||||
|
error::errno = err;
|
||||||
|
// SIG_ERR
|
||||||
|
core::mem::transmute(1usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -18,7 +18,7 @@ unsafe extern "C" fn nanosleep(
|
|||||||
|
|
||||||
let amount = Duration::from(*rqtp);
|
let amount = Duration::from(*rqtp);
|
||||||
|
|
||||||
process::sleep(amount)?;
|
process::sleep(amount).expect("TODO interruptible sleep in nanosleep()");
|
||||||
|
|
||||||
CIntZeroResult::SUCCESS
|
CIntZeroResult::SUCCESS
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use core::ffi::c_int;
|
||||||
|
|
||||||
mod exec;
|
mod exec;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod getopt;
|
mod getopt;
|
||||||
mod io;
|
mod io;
|
||||||
mod process;
|
mod process;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
pub const STDIN_FILENO: c_int = 0;
|
||||||
|
pub const STDOUT_FILENO: c_int = 1;
|
||||||
|
pub const STDERR_FILENO: c_int = 2;
|
||||||
|
@ -6,8 +6,8 @@ use core::{
|
|||||||
use yggdrasil_rt::debug_trace;
|
use yggdrasil_rt::debug_trace;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::{CIntCountResult, CIntZeroResult},
|
error::{self, CIntCountResult, CIntZeroResult},
|
||||||
headers::sys_types::{gid_t, pid_t, uid_t},
|
headers::{errno, sys_types::{gid_t, pid_t, uid_t}},
|
||||||
process,
|
process,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -121,6 +121,11 @@ unsafe extern "C" fn setuid(uid: uid_t) -> c_int {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
unsafe extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
||||||
let duration = Duration::from_secs(seconds.try_into().unwrap());
|
let duration = Duration::from_secs(seconds.try_into().unwrap());
|
||||||
process::sleep(duration).expect("TODO: sleep failed");
|
match process::sleep(duration) {
|
||||||
0
|
Ok(()) => 0,
|
||||||
|
Err(remaining) => {
|
||||||
|
error::errno = errno::EINTR;
|
||||||
|
remaining.as_secs() as _
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ mod sync;
|
|||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
mod thread;
|
mod thread;
|
||||||
|
mod signal;
|
||||||
|
|
||||||
pub mod headers;
|
pub mod headers;
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ unsafe extern "C" fn __ygglibc_entry(
|
|||||||
main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int,
|
main: extern "C" fn(c_int, *const *const c_char, *const *const c_char) -> c_int,
|
||||||
arg: usize,
|
arg: usize,
|
||||||
) {
|
) {
|
||||||
|
signal::init(true);
|
||||||
init::init();
|
init::init();
|
||||||
|
|
||||||
// Setup args
|
// Setup args
|
||||||
|
@ -24,9 +24,9 @@ pub fn getpgrp() -> pid_t {
|
|||||||
pgid.into_raw().try_into().unwrap()
|
pgid.into_raw().try_into().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sleep(duration: Duration) -> EResult<()> {
|
pub fn sleep(duration: Duration) -> Result<(), Duration> {
|
||||||
unsafe { syscall::nanosleep(&duration) }?;
|
// TODO make nanosleep return duration
|
||||||
EResult::Ok(())
|
unsafe { syscall::nanosleep(&duration) }.map_err(|_| Duration::from_secs(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn abort() -> ! {
|
pub fn abort() -> ! {
|
||||||
|
56
userspace/lib/ygglibc/src/signal.rs
Normal file
56
userspace/lib/ygglibc/src/signal.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use yggdrasil_rt::process::{
|
||||||
|
signal::{self, SignalHandler},
|
||||||
|
Signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{EResult, ResultExt, TryFromExt},
|
||||||
|
headers::{
|
||||||
|
self, errno, signal::{sig_handler_t, SigNumber}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SIGNAL_STACK_SIZE: usize = 4096 * 8;
|
||||||
|
|
||||||
|
// These are just stubs for addresses, which get converted into Rust handlers
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn __sig_ignore(signum: SigNumber) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
unsafe extern "C" fn __sig_terminate(signum: SigNumber) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn set(signum: SigNumber, handler: sig_handler_t) -> EResult<sig_handler_t> {
|
||||||
|
let signal = Signal::e_try_from(signum)?;
|
||||||
|
let handler = match handler {
|
||||||
|
// Transform special cases into Rust signal handlers instead
|
||||||
|
_ if handler == __sig_terminate => todo!(),
|
||||||
|
_ if handler == __sig_ignore => todo!(),
|
||||||
|
// This is safe: handler essentially has the same type, just in a wrapper
|
||||||
|
_ => core::mem::transmute(handler)
|
||||||
|
};
|
||||||
|
let old = signal::set_handler(signal, SignalHandler::C(handler));
|
||||||
|
let old = match old {
|
||||||
|
// Transform Rust special cases into C "handlers"
|
||||||
|
SignalHandler::Ignore => __sig_ignore,
|
||||||
|
SignalHandler::Terminate => __sig_terminate,
|
||||||
|
// libc doesn't set Rust signal handlers, return terminate just in case
|
||||||
|
SignalHandler::Rust(_) => __sig_terminate,
|
||||||
|
SignalHandler::C(handler) => core::mem::transmute(handler),
|
||||||
|
};
|
||||||
|
EResult::Ok(old)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(main: bool) {
|
||||||
|
// TODO reset any non-default handlers possibly set by whatever libc might've been entered from
|
||||||
|
if main {
|
||||||
|
signal::setup_signal_full(SIGNAL_STACK_SIZE)
|
||||||
|
.expect("Couldn't setup signal handler for the main thread");
|
||||||
|
} else {
|
||||||
|
signal::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack");
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ use crate::{
|
|||||||
error::{EResult, OptionExt}, headers::{
|
error::{EResult, OptionExt}, headers::{
|
||||||
errno::{self, Errno},
|
errno::{self, Errno},
|
||||||
sys_types::{pthread_attr_t, pthread_t},
|
sys_types::{pthread_attr_t, pthread_t},
|
||||||
}, process, sync::Mutex
|
}, process, signal, sync::Mutex
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod tls;
|
pub mod tls;
|
||||||
@ -183,7 +183,7 @@ impl Thread {
|
|||||||
core::hint::spin_loop();
|
core::hint::spin_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { Self::set_this(argument.thread.clone()) };
|
unsafe { Self::setup_self(&argument.thread); }
|
||||||
|
|
||||||
let result = (argument.entry)(argument.argument);
|
let result = (argument.entry)(argument.argument);
|
||||||
argument.thread.result.store(result, Ordering::Release);
|
argument.thread.result.store(result, Ordering::Release);
|
||||||
@ -194,6 +194,12 @@ impl Thread {
|
|||||||
// TODO call thread-local destructors
|
// TODO call thread-local destructors
|
||||||
unsafe { yggdrasil_rt::sys::exit_thread() }
|
unsafe { yggdrasil_rt::sys::exit_thread() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn setup_self(self: &Arc<Self>) {
|
||||||
|
Self::set_this(self.clone());
|
||||||
|
// Setup signal stack
|
||||||
|
signal::init(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Thread {
|
impl Drop for Thread {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user