115 lines
2.6 KiB
Rust

use core::{
ffi::{c_char, c_int, c_void, CStr}, mem::MaybeUninit, time::Duration
};
use yggdrasil_rt::{
process::{ExitCode, Signal}, sync::mutex::Mutex, sys as syscall
};
use alloc::vec::Vec;
use crate::{
headers::sys_types::pid_t,
io::{self, managed::stderr},
};
struct DsoDestructor {
f: extern "C" fn (*mut c_void),
arg: *mut c_void,
}
unsafe impl Sync for DsoDestructor {}
impl DsoDestructor {
unsafe fn invoke(self) {
(self.f)(self.arg)
}
}
static AT_EXIT: Mutex<Vec<extern "C" fn()>> = Mutex::new(Vec::new());
// TODO this will be in the linker instead
static AT_DSO_EXIT: Mutex<Vec<DsoDestructor>> = Mutex::new(Vec::new());
pub fn getpid() -> pid_t {
let pid = unsafe { syscall::get_pid() };
pid.into_raw().try_into().unwrap()
}
pub fn getpgrp() -> pid_t {
let pgid = unsafe { syscall::get_process_group_id() };
pgid.into_raw().try_into().unwrap()
}
pub fn sleep(duration: Duration) -> Result<(), Duration> {
let mut remaining = MaybeUninit::uninit();
if unsafe { syscall::nanosleep(&duration, &mut remaining) }.is_ok() {
Ok(())
} else {
Err(unsafe { remaining.assume_init() })
}
}
pub fn abort() -> ! {
let pid = unsafe { syscall::get_pid() };
unsafe { syscall::send_signal(pid, Signal::Aborted).ok() };
unreachable!()
}
pub fn c_exit_immediately(status: c_int) -> ! {
let code = ExitCode::Exited(status);
unsafe { syscall::exit_process(code) };
}
pub fn c_exit(status: c_int) -> ! {
unsafe {
pre_exit();
c_exit_immediately(status)
}
}
pub fn at_exit(f: extern "C" fn()) {
AT_EXIT.lock().push(f);
}
pub fn at_dso_exit(f: extern "C" fn(*mut c_void), arg: *mut c_void, _dso_handle: *mut c_void) {
// TODO no support for dlopen/dlclose, so at_dso_exit == at_exit right now
AT_DSO_EXIT.lock().push(DsoDestructor {
f,
arg
});
}
unsafe fn call_atexit() {
for d in AT_DSO_EXIT.lock().drain(..) {
d.invoke();
}
for f in AT_EXIT.lock().drain(..) {
f();
}
}
unsafe fn pre_exit() {
call_atexit();
io::cleanup();
}
#[no_mangle]
unsafe extern "C" fn __assert_fail(file: *const c_char, line: c_int, message: *const c_char) -> ! {
use core::fmt::Write;
let err = stderr.as_mut().unwrap();
let file = match file.is_null() {
false => CStr::from_ptr(file).to_str().ok(),
true => None,
}
.unwrap_or("???");
let expr = match message.is_null() {
false => CStr::from_ptr(message).to_str().ok(),
true => None,
}
.unwrap_or("???");
writeln!(err, "Assertion failed: '{}' at {}:{}", expr, file, line).ok();
abort()
}