#[cfg(any(
feature = "take-charge",
all(not(feature = "unwinding"), feature = "panic-handler-trap")
))]
use core::arch::asm;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::elf::Elf_Ehdr;
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
use linux_raw_sys::general::__NR_rt_sigreturn;
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
use linux_raw_sys::general::{__NR_mprotect, PROT_READ};
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
use {
core::ffi::c_void,
linux_raw_sys::general::{__NR_clone, __NR_exit, __NR_munmap},
rustix::thread::RawPid,
};
#[cfg(feature = "origin-start")]
naked_fn!(
"
The program entry point.
# Safety
This function must never be called explicitly. It is the first thing
executed in the program, and it assumes that memory is laid out according
to the operating system convention for starting a new program.
";
pub(super) fn _start() -> !;
"mov x0, sp", "mov x30, xzr", "b {entry}"; entry = sym super::program::entry
);
#[cfg(any(
feature = "take-charge",
all(not(feature = "unwinding"), feature = "panic-handler-trap")
))]
pub(super) fn trap() -> ! {
unsafe {
asm!("brk #0x1", options(noreturn, nostack));
}
}
#[cfg(any(
all(feature = "thread", feature = "take-charge"),
all(feature = "experimental-relocate", feature = "origin-start")
))]
#[allow(dead_code)]
pub(super) fn dynamic_table_addr() -> *const linux_raw_sys::elf::Elf_Dyn {
let addr;
unsafe {
asm!(
".weak _DYNAMIC",
".hidden _DYNAMIC",
"adrp {0}, _DYNAMIC",
"add {0}, {0}, :lo12:_DYNAMIC",
out(reg) addr
);
}
addr
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
pub(super) fn ehdr_addr() -> *const Elf_Ehdr {
let addr: *const Elf_Ehdr;
unsafe {
asm!(
"adrp {0}, __ehdr_start",
"add {0}, {0}, :lo12:__ehdr_start",
out(reg) addr
);
}
addr
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_load(ptr: usize) -> usize {
unsafe {
let r0;
asm!(
"ldr {}, [{}]",
out(reg) r0,
in(reg) ptr,
options(nostack, preserves_flags),
);
r0
}
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_store(ptr: usize, value: usize) {
unsafe {
asm!(
"str {}, [{}]",
in(reg) value,
in(reg) ptr,
options(nostack, preserves_flags),
);
}
}
#[cfg(all(feature = "experimental-relocate", feature = "origin-start"))]
#[cfg(relocation_model = "pic")]
#[inline]
pub(super) unsafe fn relocation_mprotect_readonly(ptr: usize, len: usize) {
unsafe {
let r0: usize;
asm!(
"svc 0",
in("x8") __NR_mprotect,
inlateout("x0") ptr as usize => r0,
in("x1") len,
in("x2") PROT_READ,
options(nostack, preserves_flags),
);
if r0 != 0 {
trap();
}
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const STACK_ALIGNMENT: usize = 16;
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn clone(
flags: u32,
child_stack: *mut c_void,
parent_tid: *mut RawPid,
child_tid: *mut RawPid,
newtls: *mut c_void,
fn_: extern "C" fn(),
num_args: usize,
) -> isize {
unsafe {
let r0;
asm!(
"svc 0", "cbnz x0, 0f",
"mov x0, {fn_}", "mov x1, sp", "mov x2, {num_args}", "mov x29, xzr", "mov x30, xzr", "b {entry}",
"0:",
entry = sym super::thread::entry,
fn_ = in(reg) fn_,
num_args = in(reg) num_args,
in("x8") __NR_clone,
inlateout("x0") flags as usize => r0,
in("x1") child_stack,
in("x2") parent_tid,
in("x3") newtls,
in("x4") child_tid,
options(nostack)
);
r0
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn set_thread_pointer(ptr: *mut c_void) {
unsafe {
asm!("msr tpidr_el0, {}", in(reg) ptr);
debug_assert_eq!(thread_pointer(), ptr);
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) fn thread_pointer() -> *mut c_void {
let ptr;
unsafe {
asm!("mrs {}, tpidr_el0", out(reg) ptr, options(nostack, preserves_flags, readonly));
}
ptr
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const TLS_OFFSET: usize = 0;
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn munmap_and_exit_thread(map_addr: *mut c_void, map_len: usize) -> ! {
unsafe {
asm!(
"svc 0",
"mov x0, xzr",
"mov x8, {__NR_exit}",
"svc 0",
"udf #16",
__NR_exit = const __NR_exit,
in("x8") __NR_munmap,
in("x0") map_addr,
in("x1") map_len,
options(noreturn, nostack)
);
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
naked_fn!(
"
Invoke the `__NR_rt_sigreturn` system call to return control from a signal
handler.
# Safety
This function must never be called other than by the `sa_restorer`
mechanism.
";
pub(super) fn return_from_signal_handler() -> ();
"mov x8, {__NR_rt_sigreturn}",
"svc 0",
"udf #16";
__NR_rt_sigreturn = const __NR_rt_sigreturn
);
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
pub(super) use return_from_signal_handler as return_from_signal_handler_noinfo;