#![allow(dead_code)]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
const RET10: &[u8] = &[0xB8, 0x0A, 0x00, 0x00, 0x00, 0xC3]; #[cfg(target_arch = "aarch64")]
const RET10: &[u8] = &[0x40, 0x01, 0x80, 0x52, 0xC0, 0x03, 0x5F, 0xD6];
pub fn far_ret10(near: usize, distance: usize) -> usize {
let size = region::page::size();
let mut hint = (near + distance).next_multiple_of(0x10000);
for _ in 0..4096 {
if let Some(address) = map_code(hint, size) {
if address.abs_diff(near) >= distance {
return address;
}
}
hint += 0x10_0000;
}
panic!("could not map a distant function");
}
#[cfg(unix)]
fn map_code(hint: usize, size: usize) -> Option<usize> {
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
let flags = libc::MAP_JIT;
#[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))]
let flags = 0;
let address = unsafe {
libc::mmap(
hint as *mut libc::c_void,
size,
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
libc::MAP_PRIVATE | libc::MAP_ANON | flags,
-1,
0,
)
};
if address == libc::MAP_FAILED {
return None;
}
unsafe {
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
libc::pthread_jit_write_protect_np(0);
std::ptr::copy_nonoverlapping(RET10.as_ptr(), address.cast(), RET10.len());
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
libc::pthread_jit_write_protect_np(1);
}
clear_cache(address as usize, RET10.len());
Some(address as usize)
}
#[cfg(windows)]
fn map_code(hint: usize, size: usize) -> Option<usize> {
let mut memory = region::alloc_at(
hint as *const u8,
size,
region::Protection::READ_WRITE_EXECUTE,
)
.ok()?;
let address = memory.as_mut_ptr::<u8>();
unsafe { std::ptr::copy_nonoverlapping(RET10.as_ptr(), address, RET10.len()) };
std::mem::forget(memory);
Some(address as usize)
}
fn clear_cache(address: usize, len: usize) {
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
{
unsafe extern "C" {
fn sys_icache_invalidate(start: *mut core::ffi::c_void, len: usize);
}
unsafe { sys_icache_invalidate(address as *mut _, len) };
}
#[cfg(all(target_arch = "aarch64", not(target_vendor = "apple")))]
{
unsafe extern "C" {
fn __clear_cache(start: *mut core::ffi::c_char, end: *mut core::ffi::c_char);
}
unsafe { __clear_cache(address as *mut _, (address + len) as *mut _) };
}
let _ = (address, len);
}
pub fn is_translated() -> bool {
#[cfg(all(target_vendor = "apple", target_arch = "x86_64"))]
{
let mut translated: libc::c_int = 0;
let mut size = size_of::<libc::c_int>();
let result = unsafe {
libc::sysctlbyname(
c"sysctl.proc_translated".as_ptr(),
(&raw mut translated).cast(),
&mut size,
std::ptr::null_mut(),
0,
)
};
result == 0 && translated == 1
}
#[cfg(not(all(target_vendor = "apple", target_arch = "x86_64")))]
false
}