use core::ffi::{c_int, c_void};
use core::ptr::{self, NonNull};
use crate::protection::Protection;
const PROT_NONE: c_int = 0;
const PROT_READ: c_int = 1;
const PROT_WRITE: c_int = 2;
const PROT_EXEC: c_int = 4;
const MAP_PRIVATE: c_int = 0x02;
#[cfg(any(target_os = "linux", target_os = "android"))]
const MAP_ANON: c_int = 0x0020;
#[cfg(not(any(target_os = "linux", target_os = "android")))]
const MAP_ANON: c_int = 0x1000;
unsafe extern "C" {
fn mmap(
addr: *mut c_void,
length: usize,
prot: c_int,
flags: c_int,
fd: c_int,
offset: i64,
) -> *mut c_void;
fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> c_int;
fn munmap(addr: *mut c_void, length: usize) -> c_int;
fn getpagesize() -> c_int;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe extern "C" {
fn __errno_location() -> *mut c_int;
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos"
))]
unsafe extern "C" {
fn __error() -> *mut c_int;
}
fn errno() -> i32 {
#[cfg(any(target_os = "linux", target_os = "android"))]
{
unsafe { *__errno_location() }
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos"
))]
{
unsafe { *__error() }
}
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "watchos"
)))]
{
0
}
}
fn prot_bits(protection: Protection) -> c_int {
match protection {
Protection::None => PROT_NONE,
Protection::Read => PROT_READ,
Protection::ReadWrite => PROT_READ | PROT_WRITE,
Protection::ReadExecute => PROT_READ | PROT_EXEC,
Protection::ReadWriteExecute => PROT_READ | PROT_WRITE | PROT_EXEC,
}
}
fn is_map_failed(ret: *mut c_void) -> bool {
ret.is_null() || ret.addr() == usize::MAX
}
pub(crate) fn page_size() -> usize {
let size = unsafe { getpagesize() };
if size > 0 { size as usize } else { 4096 }
}
pub(crate) fn map(length: usize) -> Result<NonNull<u8>, i32> {
let ret = unsafe {
mmap(
ptr::null_mut(),
length,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if is_map_failed(ret) {
return Err(errno());
}
Ok(unsafe { NonNull::new_unchecked(ret.cast::<u8>()) })
}
pub(crate) fn map_guarded(total: usize, guard: usize, data_len: usize) -> Result<NonNull<u8>, i32> {
let base = unsafe {
mmap(
ptr::null_mut(),
total,
PROT_NONE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if is_map_failed(base) {
return Err(errno());
}
let data = base.wrapping_byte_add(guard);
let rc = unsafe { mprotect(data, data_len, PROT_READ | PROT_WRITE) };
if rc != 0 {
let code = errno();
let _ = unsafe { munmap(base, total) };
return Err(code);
}
Ok(unsafe { NonNull::new_unchecked(base.cast::<u8>()) })
}
pub(crate) unsafe fn protect(ptr: *mut u8, len: usize, protection: Protection) -> Result<(), i32> {
let rc = unsafe { mprotect(ptr.cast::<c_void>(), len, prot_bits(protection)) };
if rc == 0 { Ok(()) } else { Err(errno()) }
}
pub(crate) unsafe fn unmap(base: *mut u8, total: usize) -> Result<(), i32> {
let rc = unsafe { munmap(base.cast::<c_void>(), total) };
if rc == 0 { Ok(()) } else { Err(errno()) }
}