#[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 = "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() -> !;
"mv a0, sp", "mv ra, zero", "mv fp, zero", "tail {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!("unimp", 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",
"lla {}, _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!(
"lla {}, __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!(
"ld {}, 0({})",
out(reg) r0,
in(reg) ptr,
options(nostack, preserves_flags),
);
r0
}
}
#[cfg(all(feature = "take-charge", not(feature = "optimize_for_size")))]
#[inline]
pub(super) unsafe fn oob_load(ptr: *const usize) -> usize {
unsafe {
let r0;
asm!(
"ld {}, 0({})",
out(reg) r0,
in(reg) ptr,
options(nostack, preserves_flags, readonly),
);
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!(
"sd {}, 0({})",
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!(
"ecall",
in("a7") __NR_mprotect,
inlateout("a0") ptr as usize => r0,
in("a1") len,
in("a2") 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!(
"ecall", "bnez a0, 0f",
"mv a0, {fn_}", "mv a1, sp", "mv a2, {num_args}", "mv fp, zero", "mv ra, zero", "tail {entry}",
"0:",
entry = sym super::thread::entry,
fn_ = in(reg) fn_,
num_args = in(reg) num_args,
in("a7") __NR_clone,
inlateout("a0") flags as usize => r0,
in("a1") child_stack,
in("a2") parent_tid,
in("a3") newtls,
in("a4") 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!("mv tp, {}", 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!("mv {}, tp", out(reg) ptr, options(nostack, preserves_flags, readonly));
}
ptr
}
#[cfg(feature = "take-charge")]
#[cfg(feature = "thread")]
pub(super) const TLS_OFFSET: usize = 0x800;
#[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!(
"ecall",
"mv a0, zero",
"li a7, {__NR_exit}",
"ecall",
"unimp",
__NR_exit = const __NR_exit,
in("a7") __NR_munmap,
in("a0") map_addr,
in("a1") map_len,
options(noreturn, nostack)
);
}
}