#[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 rdi, rsp", "push rbp", "jmp {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!("ud2", 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",
"lea {}, [rip + _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!(
"lea {}, [rip + __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!(
"mov {}, [{}]",
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!(
"mov [{}], {}",
in(reg) ptr,
in(reg) value,
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!(
"syscall",
inlateout("rax") __NR_mprotect as usize => r0,
in("rdi") ptr,
in("rsi") len,
in("rdx") PROT_READ,
lateout("rcx") _,
lateout("r11") _,
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!(
"syscall", "test eax, eax", "jnz 2f",
"mov rdi, r9", "mov rsi, rsp", "mov rdx, r12", "xor ebp, ebp", "push rax", "jmp {entry}",
"2:",
entry = sym super::thread::entry,
inlateout("rax") __NR_clone as usize => r0,
in("rdi") flags,
in("rsi") child_stack,
in("rdx") parent_tid,
in("r10") child_tid,
in("r8") newtls,
in("r9") fn_,
in("r12") num_args,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
r0
}
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
#[inline]
pub(super) unsafe fn set_thread_pointer(ptr: *mut c_void) {
unsafe {
rustix::runtime::set_fs(ptr);
debug_assert_eq!(*ptr.cast::<*const c_void>(), 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!("mov {}, fs:0", 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!(
"syscall",
"xor edi, edi",
"mov eax, {__NR_exit}",
"syscall",
"ud2",
__NR_exit = const __NR_exit,
in("rax") __NR_munmap,
in("rdi") map_addr,
in("rsi") 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 rax, {__NR_rt_sigreturn}",
"syscall",
"ud2";
__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;