#[cfg(feature = "thread")]
use crate::thread;
#[cfg(feature = "program-at-exit")]
use alloc::boxed::Box;
#[cfg(all(feature = "program-at-exit", not(feature = "thread")))]
use core::cell::UnsafeCell;
use linux_raw_sys::ctypes::c_int;
#[cfg(all(feature = "program-at-exit", feature = "thread"))]
use rustix_futex_sync::Mutex;
#[cfg(not(any(feature = "origin-start", feature = "external-start")))]
compile_error!("\"origin-program\" depends on either \"origin-start\" or \"external-start\".");
pub(super) unsafe extern "C" fn entry(mem: *mut usize) -> ! {
unsafe {
#[cfg(debug_assertions)]
#[cfg(feature = "origin-start")]
{
debug_assert_ne!(mem, core::ptr::null_mut());
debug_assert_eq!(mem.addr() & 0xf, 0);
#[cfg(feature = "nightly")]
{
unsafe 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!(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, argv, envp) = compute_args(mem);
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
{
crate::relocate::relocate(envp);
}
init_runtime(mem, envp);
#[cfg(feature = "init-array")]
{
use core::arch::asm;
use core::ffi::c_void;
unsafe extern "C" {
static __init_array_start: c_void;
static __init_array_end: c_void;
}
type InitFn = unsafe extern "C" fn(c_int, *mut *mut u8, *mut *mut u8);
let mut init = core::ptr::addr_of!(__init_array_start).cast::<InitFn>();
let init_end = core::ptr::addr_of!(__init_array_end).cast::<InitFn>();
asm!("# {}", inout(reg) init, options(pure, nomem, nostack, preserves_flags));
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);
}
}
{
unsafe extern "Rust" {
fn origin_main(argc: usize, argv: *mut *mut u8, envp: *mut *mut u8) -> i32;
}
#[cfg(feature = "log")]
log::trace!("Calling `origin_main({:?}, {:?}, {:?})`", argc, argv, envp);
let status = origin_main(argc as usize, argv, envp);
#[cfg(feature = "log")]
log::trace!("`origin_main` returned `{:?}`", status);
exit(status)
}
}
}
#[cfg(feature = "external-start")]
pub unsafe fn start(mem: *mut usize) -> ! {
unsafe { entry(mem) }
}
unsafe fn compute_args(mem: *mut usize) -> (i32, *mut *mut u8, *mut *mut u8) {
unsafe {
use linux_raw_sys::ctypes::c_uint;
let kernel_argc = *mem;
let argc = kernel_argc as c_int;
let argv = mem.add(1).cast::<*mut u8>();
let envp = argv.add(argc as c_uint as usize + 1);
debug_assert!(argc >= 0);
debug_assert_eq!(kernel_argc, argc as _);
debug_assert_eq!(*argv.add(argc as usize), core::ptr::null_mut());
(argc, argv, envp)
}
}
#[allow(unused_variables)]
unsafe fn init_runtime(mem: *mut usize, envp: *mut *mut u8) {
#[cfg(feature = "param")]
unsafe {
rustix::param::init(envp);
}
#[cfg(feature = "thread")]
thread::initialize_startup_info();
#[cfg(feature = "thread")]
unsafe {
thread::initialize_main(mem.cast());
}
}
#[cfg(all(feature = "program-at-exit", feature = "thread"))]
static DTORS: Mutex<smallvec::SmallVec<[Box<dyn FnOnce() + Send>; 32]>> =
Mutex::new(smallvec::SmallVec::new_const());
#[cfg(all(feature = "program-at-exit", not(feature = "thread")))]
struct Dtors(UnsafeCell<smallvec::SmallVec<[Box<dyn FnOnce() + Send>; 32]>>);
#[cfg(all(feature = "program-at-exit", not(feature = "thread")))]
unsafe impl Sync for Dtors {}
#[cfg(all(feature = "program-at-exit", not(feature = "thread")))]
static DTORS: Dtors = Dtors(UnsafeCell::new(smallvec::SmallVec::new_const()));
#[cfg(feature = "program-at-exit")]
#[cfg_attr(docsrs, doc(cfg(feature = "program-at-exit")))]
pub fn at_exit(func: Box<dyn FnOnce() + Send>) {
#[cfg(feature = "thread")]
let mut dtors = DTORS.lock();
#[cfg(not(feature = "thread"))]
let dtors = unsafe { &mut *DTORS.0.get() };
dtors.push(func);
}
pub fn exit(status: c_int) -> ! {
#[cfg(feature = "thread-at-exit")]
crate::thread::call_dtors(crate::thread::current());
#[cfg(feature = "program-at-exit")]
loop {
#[cfg(feature = "thread")]
let mut dtors = DTORS.lock();
#[cfg(not(feature = "thread"))]
let dtors = unsafe { &mut *DTORS.0.get() };
if let Some(func) = dtors.pop() {
drop(dtors);
#[cfg(feature = "log")]
log::trace!("Calling `at_exit`-registered function");
func();
} else {
#[cfg(feature = "thread")]
core::mem::forget(dtors);
break;
}
}
#[cfg(feature = "fini-array")]
unsafe {
use core::arch::asm;
use core::ffi::c_void;
unsafe extern "C" {
static __fini_array_start: c_void;
static __fini_array_end: c_void;
}
type FiniFn = extern "C" fn();
let mut fini = core::ptr::addr_of!(__fini_array_end).cast::<FiniFn>();
let fini_start = core::ptr::addr_of!(__fini_array_start).cast::<FiniFn>();
asm!("# {}", inout(reg) fini, options(pure, nomem, nostack, preserves_flags));
while fini != fini_start {
fini = fini.sub(1);
#[cfg(feature = "log")]
log::trace!("Calling `.fini_array`-registered function `{:?}()`", *fini);
(*fini)();
}
}
immediate_exit(status)
}
#[inline]
pub fn immediate_exit(status: c_int) -> ! {
#[cfg(feature = "log")]
log::trace!("Program exiting with status `{:?}`", status);
rustix::runtime::exit_group(status)
}
#[inline]
#[cold]
pub fn trap() -> ! {
crate::arch::trap()
}