use std::ffi::c_void;
use std::ptr::NonNull;
use super::super::*;
use libc;
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct LinuxMemoryHandle(pub NonNull<c_void>);
impl MemoryHandle for LinuxMemoryHandle {
fn from_ptr(ptr: NonNull<c_void>) -> Self {
Self(ptr)
}
fn as_ptr(&self) -> NonNull<c_void> {
self.0
}
}
pub struct LinuxMemoryAllocationInfo {
page_size: usize,
allocation_size: usize,
allocation_start: LinuxMemoryHandle,
}
impl AllocationInfo<LinuxMemoryController> for LinuxMemoryAllocationInfo {
fn page_size(&self) -> usize {
self.page_size
}
fn allocation_size(&self) -> usize {
self.allocation_size
}
fn allocation_start(&self) -> LinuxMemoryHandle {
self.allocation_start
}
}
#[derive(Debug)]
pub struct LinuxMemoryController;
impl LinuxMemoryController {
pub const fn new() -> Self {
Self
}
unsafe fn sys_get_page_size(&self) -> usize {
unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
}
unsafe fn allocate_system_memory(
&self,
_page_size: usize,
size: usize,
) -> Result<LinuxMemoryHandle> {
let handle = unsafe {
libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
-1,
0,
)
};
NonNull::new(handle)
.map(LinuxMemoryHandle)
.ok_or(MemoryError::CantAllocate)
}
fn allign_up(&self, page_size: usize, address: usize) -> usize {
(address + page_size - 1) & !(page_size - 1)
}
fn allign_down(&self, page_size: usize, address: usize) -> usize {
address & !(page_size - 1)
}
fn get_native_protection_flags(&self, protection: MemoryProtection) -> i32 {
match protection {
MemoryProtection::NoAccess => 0,
MemoryProtection::ReadWrite => libc::PROT_READ | libc::PROT_WRITE,
MemoryProtection::ReadExecute => libc::PROT_READ | libc::PROT_EXEC,
MemoryProtection::Other(proc) => proc as i32,
}
}
unsafe fn native_set_page_protection(
&self,
page: NonNull<c_void>,
allocation_size: usize,
protection: i32,
) -> Result<()> {
let success = unsafe { libc::mprotect(page.as_ptr(), allocation_size, protection) == 0 };
if !success {
Err(MemoryError::CantSetMemoryProtection(page.as_ptr() as usize))
} else {
Ok(())
}
}
}
impl MemoryController for LinuxMemoryController {
type Handle = LinuxMemoryHandle;
type AllocationInfoType = LinuxMemoryAllocationInfo;
unsafe fn allocate_memory(&self, min_size: Option<usize>) -> Result<Self::AllocationInfoType> {
let page_size = unsafe { self.sys_get_page_size() } as usize;
let allocation_size = if let Some(min_size) = min_size {
self.allign_up(page_size, min_size)
} else {
page_size
};
let allocation_start = unsafe { self.allocate_system_memory(page_size, allocation_size)? };
Ok(Self::AllocationInfoType {
page_size,
allocation_size,
allocation_start,
})
}
unsafe fn set_page_protection(
&self,
handle: Self::Handle,
size: usize,
protection: MemoryProtection,
) -> Result<()> {
let protection = self.get_native_protection_flags(protection);
unsafe {
self.native_set_page_protection(handle.as_ptr(), size, protection)?;
}
Ok(())
}
unsafe fn get_symbol_address(
&self,
module: Option<&CStr>,
symbol: &CStr,
) -> Result<NonNull<c_void>> {
let module_handle = if let Some(module) = module {
let module_addr = unsafe { libc::dlopen(module.as_ptr(), libc::RTLD_LAZY) };
if module_addr.is_null() {
return Err(MemoryError::CantFindModule(
module.to_str().unwrap_or("<invalid-module-name>").into(),
));
}
module_addr
} else {
libc::RTLD_DEFAULT
};
let proc_address = {
let proc_address = unsafe { libc::dlsym(module_handle, symbol.as_ptr()) };
NonNull::new(proc_address).ok_or_else(|| {
MemoryError::CantFindModule(
symbol.to_str().unwrap_or("<invalid-module-name>").into(),
)
})?
};
Ok(proc_address)
}
fn protection_guard_for_page<'a>(
&'a self,
ptr: NonNull<c_void>,
on_enter: MemoryProtection,
on_exit: Option<MemoryProtection>,
) -> Result<MemoryProtectionGuard<'a, Self>>
where
Self: Sized,
{
let on_exit = if let Some(on_exit) = on_exit {
on_exit
} else {
MemoryProtection::ReadExecute
};
let page_size = unsafe { self.sys_get_page_size() };
let alligned_address = {
let address =
self.allign_down(page_size as usize, ptr.as_ptr() as usize) as *mut c_void;
NonNull::new(address).ok_or(MemoryError::BadAdress(address))?
};
self.protection_guard(
Self::Handle::from_ptr(alligned_address),
page_size,
on_enter,
on_exit,
)
}
}