#[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(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 = "signal")]
use linux_raw_sys::general::{__NR_rt_sigreturn, __NR_sigreturn};
#[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 r0, sp", "mov lr, #0", "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!(".inst 0xe7ffdefe", 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",
"ldr {0}, 1f",
"0:",
"add {0}, pc, {0}",
"b 2f",
".p2align 2",
"1:",
".word _DYNAMIC-(0b+8)",
"2:",
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;
unsafe {
asm!(
"ldr {0}, 1f",
"0:",
"add {0}, pc, {0}",
"b 2f",
".p2align 2",
"1:",
".word __ehdr_start-(0b+8)",
"2:",
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 {
{
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("r7") __NR_mprotect,
inlateout("r0") ptr as usize => r0,
in("r1") len,
in("r2") PROT_READ,
options(nostack, preserves_flags),
);
if r0 != 0 {
trap();
}
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const STACK_ALIGNMENT: usize = 4;
#[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", "tst r0, r0", "bne 0f",
"mov r0, {fn_}", "mov r1, sp", "mov r2, {num_args}", "mov fp, #0", "mov lr, #0", "b {entry}",
"0:",
entry = sym super::thread::entry,
fn_ = in(reg) fn_,
num_args = in(reg) num_args,
in("r7") __NR_clone,
inlateout("r0") flags as usize => r0,
in("r1") child_stack,
in("r2") parent_tid,
in("r3") newtls,
in("r4") 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 {
let res = rustix::runtime::arm_set_tls(ptr);
debug_assert_eq!(res, Ok(()));
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!("mrc p15, 0, {}, c13, c0, 3", out(reg) ptr);
}
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 r0, #0",
"mov r7, {__NR_exit}",
"svc 0",
"udf #16",
__NR_exit = const __NR_exit,
in("r7") __NR_munmap,
in("r0") map_addr,
in("r1") 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 r7, {__NR_rt_sigreturn}",
"swi 0",
"udf #16";
__NR_rt_sigreturn = const __NR_rt_sigreturn
);
#[cfg(feature = "take-charge")]
#[cfg(feature = "signal")]
naked_fn!(
"
Invoke the appropriate system call to return control from a signal
handler that does not use `SA_SIGINFO`.
# Safety
This function must never be called other than by the `sa_restorer`
mechanism.
";
pub(super) fn return_from_signal_handler_noinfo() -> ();
"mov r7, {__NR_sigreturn}",
"swi 0",
"udf #16";
__NR_sigreturn = const __NR_sigreturn
);