#[cfg(target_vendor = "mustang")]
use crate::sync::Mutex;
#[cfg(target_vendor = "mustang")]
#[cfg(feature = "threads")]
use crate::threads::initialize_main_thread;
use alloc::boxed::Box;
#[cfg(target_vendor = "mustang")]
use alloc::vec::Vec;
#[cfg(target_vendor = "mustang")]
use core::arch::asm;
use core::ffi::c_void;
#[cfg(not(target_vendor = "mustang"))]
use core::ptr::null_mut;
use linux_raw_sys::ctypes::c_int;
#[cfg(target_vendor = "mustang")]
pub(super) unsafe extern "C" fn entry(mem: *mut usize) -> ! {
extern "C" {
fn main(argc: c_int, argv: *mut *mut u8, envp: *mut *mut u8) -> c_int;
}
#[cfg(debug_assertions)]
{
extern "C" {
#[link_name = "llvm.frameaddress"]
fn builtin_frame_address(level: i32) -> *const u8;
#[link_name = "llvm.returnaddress"]
fn builtin_return_address(level: i32) -> *const u8;
#[cfg(target_arch = "aarch64")]
#[link_name = "llvm.sponentry"]
fn builtin_sponentry() -> *const u8;
}
debug_assert_ne!(mem, core::ptr::null_mut());
debug_assert_eq!(mem.addr() & 0xf, 0);
debug_assert!(builtin_frame_address(0).addr() <= mem.addr());
debug_assert_eq!(builtin_return_address(0), core::ptr::null());
debug_assert_ne!(builtin_frame_address(0), core::ptr::null());
#[cfg(not(any(target_arch = "arm", target_arch = "x86")))]
debug_assert_eq!(builtin_frame_address(0).addr() & 0xf, 0);
#[cfg(target_arch = "arm")]
debug_assert_eq!(builtin_frame_address(0).addr() & 0x7, 0);
#[cfg(target_arch = "x86")]
debug_assert_eq!(builtin_frame_address(0).addr() & 0xf, 8);
debug_assert_eq!(builtin_frame_address(1), core::ptr::null());
#[cfg(target_arch = "aarch64")]
debug_assert_ne!(builtin_sponentry(), core::ptr::null());
#[cfg(target_arch = "aarch64")]
debug_assert_eq!(builtin_sponentry().addr() & 0xf, 0);
}
let argc = *mem as c_int;
let argv = mem.add(1).cast::<*mut u8>();
let envp = argv.add(argc as usize + 1);
debug_assert!(argc >= 0);
debug_assert_eq!(*mem, argc as _);
debug_assert_eq!(*argv.add(argc as usize), core::ptr::null_mut());
rustix::param::init(envp);
#[cfg(feature = "threads")]
initialize_main_thread(mem.cast());
call_ctors(argc, argv, envp);
#[cfg(feature = "log")]
log::trace!("Calling `main({:?}, {:?}, {:?})`", argc, argv, envp);
let status = main(argc, argv, envp);
#[cfg(feature = "log")]
log::trace!("`main` returned `{:?}`", status);
exit(status)
}
#[cfg(target_vendor = "mustang")]
unsafe fn call_ctors(argc: c_int, argv: *mut *mut u8, envp: *mut *mut u8) {
extern "C" {
static __init_array_start: c_void;
static __init_array_end: c_void;
}
type InitFn = fn(c_int, *mut *mut u8, *mut *mut u8);
let mut init = &__init_array_start as *const _ as *const InitFn;
let init_end = &__init_array_end as *const _ as *const InitFn;
asm!("# {}", inout(reg) init);
while init != init_end {
#[cfg(feature = "log")]
log::trace!(
"Calling `.init_array`-registered function `{:?}({:?}, {:?}, {:?})`",
*init,
argc,
argv,
envp
);
(*init)(argc, argv, envp);
init = init.add(1);
}
}
#[cfg(target_vendor = "mustang")]
static DTORS: Mutex<Vec<Box<dyn FnOnce() + Send>>> = Mutex::new(Vec::new());
pub fn at_exit(func: Box<dyn FnOnce() + Send>) {
#[cfg(target_vendor = "mustang")]
{
let mut funcs = DTORS.lock();
funcs.push(func);
}
#[cfg(not(target_vendor = "mustang"))]
{
extern "C" {
fn __cxa_atexit(
func: unsafe extern "C" fn(*mut c_void),
arg: *mut c_void,
_dso: *mut c_void,
) -> c_int;
}
unsafe extern "C" fn at_exit_func(arg: *mut c_void) {
Box::from_raw(arg as *mut Box<dyn FnOnce() + Send>)();
}
let at_exit_arg = Box::into_raw(Box::new(func)).cast::<c_void>();
let r = unsafe { __cxa_atexit(at_exit_func, at_exit_arg, null_mut()) };
assert_eq!(r, 0);
}
}
pub fn exit(status: c_int) -> ! {
#[cfg(target_vendor = "mustang")]
#[cfg(feature = "threads")]
crate::threads::call_thread_dtors(crate::current_thread());
#[cfg(target_vendor = "mustang")]
loop {
let mut dtors = DTORS.lock();
if let Some(dtor) = dtors.pop() {
#[cfg(feature = "log")]
log::trace!("Calling `at_exit`-registered function",);
dtor();
} else {
break;
}
}
#[cfg(target_vendor = "mustang")]
unsafe {
extern "C" {
static __fini_array_start: c_void;
static __fini_array_end: c_void;
}
type InitFn = fn();
let mut fini: *const InitFn = &__fini_array_end as *const _ as *const InitFn;
let fini_start: *const InitFn = &__fini_array_start as *const _ as *const InitFn;
asm!("# {}", inout(reg) fini);
while fini != fini_start {
fini = fini.sub(1);
#[cfg(feature = "log")]
log::trace!("Calling `.fini_array`-registered function `{:?}()`", *fini);
(*fini)();
}
}
#[cfg(target_vendor = "mustang")]
{
exit_immediately(status)
}
#[cfg(not(target_vendor = "mustang"))]
unsafe {
libc::exit(status)
}
}
#[inline]
pub fn exit_immediately(status: c_int) -> ! {
#[cfg(feature = "log")]
log::trace!("Program exiting");
#[cfg(target_vendor = "mustang")]
{
rustix::runtime::exit_group(status)
}
#[cfg(not(target_vendor = "mustang"))]
unsafe {
libc::_exit(status)
}
}