executable_memory 0.1.2

executable memory for windows and unix
Documentation
use core::{slice, mem, fmt};
use core::ops::{Deref, DerefMut};

#[cfg(any(target_os = "linux", target_os = "macos"))]
use libc;

#[cfg(target_os = "windows")]
use winapi;


pub const PAGE_SIZE: usize = 4096;


#[derive(PartialEq, Eq)]
pub struct ExecutableMemory {
    ptr: *mut u8,
    len: usize,
}

impl Default for ExecutableMemory {
    #[inline(always)]
    fn default() -> Self {
        ExecutableMemory::new(1)
    }
}

impl ExecutableMemory {
    #[inline]
    pub fn new(num_pages: usize) -> Self {
        ExecutableMemory {
            ptr: unsafe {
                alloc_executable_memory(PAGE_SIZE, num_pages)
            },
            len: num_pages * PAGE_SIZE,
        }
    }

    #[inline(always)]
    pub fn as_ptr(&self) -> *mut u8 {
        self.ptr
    }
    #[inline(always)]
    pub fn len(&self) -> usize {
        self.len
    }

    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        unsafe {
            slice::from_raw_parts(self.ptr, self.len)
        }
    }
    #[inline]
    pub fn as_slice_mut(&mut self) -> &mut [u8] {
        unsafe {
            slice::from_raw_parts_mut(self.ptr, self.len)
        }
    }
}

impl Deref for ExecutableMemory {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_slice()
    }
}
impl DerefMut for ExecutableMemory {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_slice_mut()
    }
}

impl fmt::Debug for ExecutableMemory {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Drop for ExecutableMemory {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            dealloc_executable_memory(self.ptr, PAGE_SIZE);
        }
    }
}


#[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe fn alloc_executable_memory(page_size: usize, num_pages: usize) -> *mut u8 {
    let size = page_size * num_pages;
    let mut raw_addr: *mut libc::c_void = mem::uninitialized();

    libc::posix_memalign(&mut raw_addr, page_size, size);
    libc::mprotect(raw_addr, size, libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE);

    mem::transmute(raw_addr)
}
#[cfg(target_os = "windows")]
unsafe fn alloc_executable_memory(page_size: usize, num_pages: usize) -> *mut u8 {
    let size = page_size * num_pages;
    let raw_addr: *mut winapi::ctypes::c_void;

    raw_addr = winapi::um::memoryapi::VirtualAlloc(
        ::core::ptr::null_mut(),
        size,
        winapi::um::winnt::MEM_RESERVE | winapi::um::winnt::MEM_COMMIT,
        winapi::um::winnt::PAGE_EXECUTE_READWRITE
    );

    assert_ne!(
        raw_addr, 0 as *mut winapi::ctypes::c_void,
        "Could not allocate memory. Error Code: {:?}",
        winapi::um::errhandlingapi::GetLastError()
    );

    mem::transmute(raw_addr)
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe fn dealloc_executable_memory(ptr: *mut u8, page_size: usize) {
    libc::munmap(ptr as *mut _, page_size);
}
#[cfg(target_os = "windows")]
unsafe fn dealloc_executable_memory(ptr: *mut u8, _: usize) {
	winapi::um::memoryapi::VirtualFree(ptr as *mut _, 0, winapi::um::winnt::MEM_RELEASE);
}


#[cfg(test)]
mod test {
    use super::*;


    #[test]
    fn test_call_function() {
        let mut memory = ExecutableMemory::default();

        memory[0] = 0xb8;
        memory[1] = 0xff;
        memory[2] = 0xff;
        memory[3] = 0xff;
        memory[4] = 0xff;
        memory[5] = 0xc3;

        let f: fn() -> u32 = unsafe {
            mem::transmute((&memory[0..6]).as_ptr())
        };

        assert_eq!(f(), 4294967295);
    }
}