use core::ffi::c_void;
use core::mem::MaybeUninit;
use core::ptr::{self, NonNull};
use crate::protection::Protection;
const MEM_COMMIT: u32 = 0x0000_1000;
const MEM_RESERVE: u32 = 0x0000_2000;
const MEM_RELEASE: u32 = 0x0000_8000;
const PAGE_NOACCESS: u32 = 0x01;
const PAGE_READONLY: u32 = 0x02;
const PAGE_READWRITE: u32 = 0x04;
const PAGE_EXECUTE_READ: u32 = 0x20;
const PAGE_EXECUTE_READWRITE: u32 = 0x40;
#[repr(C)]
#[allow(
dead_code,
reason = "every field must be present for the struct's size and field offsets to match \
the C layout, even though only dw_page_size is read"
)]
struct SystemInfo {
w_processor_architecture: u16,
w_reserved: u16,
dw_page_size: u32,
lp_minimum_application_address: *mut c_void,
lp_maximum_application_address: *mut c_void,
dw_active_processor_mask: usize,
dw_number_of_processors: u32,
dw_processor_type: u32,
dw_allocation_granularity: u32,
w_processor_level: u16,
w_processor_revision: u16,
}
#[allow(
non_snake_case,
reason = "these are foreign Win32 symbols and must keep their exported names"
)]
unsafe extern "system" {
fn VirtualAlloc(
address: *mut c_void,
size: usize,
allocation_type: u32,
protect: u32,
) -> *mut c_void;
fn VirtualProtect(
address: *mut c_void,
size: usize,
new_protect: u32,
old_protect: *mut u32,
) -> i32;
fn VirtualFree(address: *mut c_void, size: usize, free_type: u32) -> i32;
fn GetSystemInfo(system_info: *mut SystemInfo);
fn GetLastError() -> u32;
}
fn last_error() -> i32 {
unsafe { GetLastError() as i32 }
}
fn page_protect(protection: Protection) -> u32 {
match protection {
Protection::None => PAGE_NOACCESS,
Protection::Read => PAGE_READONLY,
Protection::ReadWrite => PAGE_READWRITE,
Protection::ReadExecute => PAGE_EXECUTE_READ,
Protection::ReadWriteExecute => PAGE_EXECUTE_READWRITE,
}
}
pub(crate) fn page_size() -> usize {
let mut info = MaybeUninit::<SystemInfo>::uninit();
unsafe { GetSystemInfo(info.as_mut_ptr()) };
let size = unsafe { info.assume_init_ref().dw_page_size } as usize;
if size != 0 { size } else { 4096 }
}
pub(crate) fn map(length: usize) -> Result<NonNull<u8>, i32> {
let ret = unsafe {
VirtualAlloc(
ptr::null_mut(),
length,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
)
};
NonNull::new(ret.cast::<u8>()).ok_or_else(last_error)
}
pub(crate) fn map_guarded(total: usize, guard: usize, data_len: usize) -> Result<NonNull<u8>, i32> {
let base = unsafe { VirtualAlloc(ptr::null_mut(), total, MEM_RESERVE, PAGE_NOACCESS) };
if base.is_null() {
return Err(last_error());
}
let data = base.wrapping_byte_add(guard);
let committed = unsafe { VirtualAlloc(data, data_len, MEM_COMMIT, PAGE_READWRITE) };
if committed.is_null() {
let code = last_error();
let _ = unsafe { VirtualFree(base, 0, MEM_RELEASE) };
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 mut previous = 0u32;
let rc = unsafe {
VirtualProtect(
ptr.cast::<c_void>(),
len,
page_protect(protection),
&mut previous,
)
};
if rc != 0 { Ok(()) } else { Err(last_error()) }
}
pub(crate) unsafe fn unmap(base: *mut u8, _total: usize) -> Result<(), i32> {
let rc = unsafe { VirtualFree(base.cast::<c_void>(), 0, MEM_RELEASE) };
if rc != 0 { Ok(()) } else { Err(last_error()) }
}