use core::{
arch::asm,
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
};
static TRAMPOLINE: AtomicUsize = AtomicUsize::new(0);
static USE_INDIRECT: AtomicBool = AtomicBool::new(true);
pub fn set_use_indirect(enabled: bool) {
USE_INDIRECT.store(enabled, Ordering::Relaxed);
}
const STUB: [u8; 8] = [0x01, 0x00, 0x00, 0xD4, 0xC0, 0x03, 0x5F, 0xD6];
#[inline(never)]
unsafe fn bootstrap_mmap(size: usize) -> *mut u8 {
let result: isize;
unsafe {
asm!(
".byte 0x01, 0x00, 0x00, 0xD4", in("x8") 222usize, in("x0") 0usize, in("x1") size, in("x2") 3usize, in("x3") 0x22usize, in("x4") -1isize as usize, in("x5") 0usize, lateout("x0") result,
options(nostack),
);
}
result as *mut u8
}
#[inline(never)]
unsafe fn bootstrap_mprotect(addr: *mut u8, len: usize, prot: usize) {
let _rc: isize;
unsafe {
asm!(
".byte 0x01, 0x00, 0x00, 0xD4", in("x8") 226usize, in("x0") addr,
in("x1") len,
in("x2") prot,
lateout("x0") _rc,
options(nostack),
);
}
}
pub fn init_trampoline() {
if TRAMPOLINE.load(Ordering::Acquire) != 0 {
return;
}
unsafe {
let page = bootstrap_mmap(4096);
assert!(
!page.is_null() && (page as isize) > 0,
"rustld: trampoline mmap failed"
);
core::ptr::copy_nonoverlapping(STUB.as_ptr(), page, STUB.len());
bootstrap_mprotect(page, 4096, 0x1 | 0x4); let _ = TRAMPOLINE.compare_exchange(0, page as usize, Ordering::Release, Ordering::Relaxed);
}
}
#[inline(always)]
pub fn trampoline() -> usize {
let addr = TRAMPOLINE.load(Ordering::Acquire);
if addr == 0 {
init_trampoline();
TRAMPOLINE.load(Ordering::Acquire)
} else {
addr
}
}
#[inline(never)]
unsafe fn direct_syscall6(
nr: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
) -> isize {
let result: isize;
unsafe {
asm!(
"svc 0",
in("x8") nr,
inlateout("x0") a1 => result,
in("x1") a2, in("x2") a3, in("x3") a4,
in("x4") a5, in("x5") a6,
options(nostack),
);
}
result
}
#[inline(never)]
unsafe fn direct_syscall_noreturn(nr: usize, a1: usize) -> ! {
unsafe {
asm!("svc 0", in("x8") nr, in("x0") a1, options(noreturn));
}
}
#[inline(always)]
pub unsafe fn indirect_syscall6(
nr: usize,
a1: usize,
a2: usize,
a3: usize,
a4: usize,
a5: usize,
a6: usize,
) -> isize {
if USE_INDIRECT.load(Ordering::Relaxed) {
let stub = trampoline();
let result: isize;
unsafe {
asm!(
"blr {stub}",
stub = in(reg) stub,
in("x8") nr,
inlateout("x0") a1 => result,
in("x1") a2, in("x2") a3, in("x3") a4,
in("x4") a5, in("x5") a6,
out("x30") _, options(nostack),
);
}
result
} else {
unsafe { direct_syscall6(nr, a1, a2, a3, a4, a5, a6) }
}
}
#[inline(always)]
pub unsafe fn indirect_syscall_noreturn(nr: usize, a1: usize) -> ! {
if USE_INDIRECT.load(Ordering::Relaxed) {
let stub = trampoline();
unsafe {
asm!(
"blr {stub}",
stub = in(reg) stub,
in("x8") nr,
in("x0") a1,
options(noreturn),
);
}
} else {
unsafe { direct_syscall_noreturn(nr, a1) }
}
}