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; 3] = [0x0F, 0x05, 0xC3];
#[inline(never)]
unsafe fn bootstrap_mmap(size: usize) -> *mut u8 {
let result: isize;
unsafe {
asm!(
".byte 0x0F", ".byte 0x05",
inlateout("rax") 9usize => result, in("rdi") 0usize,
in("rsi") size,
in("rdx") 3usize, in("r10") 0x22usize, in("r8") -1isize as usize,
in("r9") 0usize,
out("rcx") _, out("r11") _,
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 0x0F", ".byte 0x05",
inlateout("rax") 10usize => _rc, in("rdi") addr, in("rsi") len, in("rdx") prot,
out("rcx") _, out("r11") _,
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!(
"syscall",
inlateout("rax") nr => result,
in("rdi") a1, in("rsi") a2, in("rdx") a3,
in("r10") a4, in("r8") a5, in("r9") a6,
out("rcx") _, out("r11") _,
options(nostack),
);
}
result
}
#[inline(never)]
unsafe fn direct_syscall_noreturn(nr: usize, a1: usize) -> ! {
unsafe {
asm!("syscall", in("rax") nr, in("rdi") 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!(
"call {stub}",
stub = in(reg) stub,
inlateout("rax") nr => result,
in("rdi") a1, in("rsi") a2, in("rdx") a3,
in("r10") a4, in("r8") a5, in("r9") a6,
out("rcx") _, out("r11") _,
);
}
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!(
"jmp {stub}",
stub = in(reg) stub,
in("rax") nr, in("rdi") a1,
options(noreturn),
);
}
} else {
unsafe { direct_syscall_noreturn(nr, a1) }
}
}