Till now, I have the following:
#[cfg(any(unix))]
use libc::{
mmap,
munmap,
PROT_EXEC,
PROT_READ,
PROT_WRITE,
MAP_ANONYMOUS,
MAP_PRIVATE,
};
use std::{ptr, io, slice};
/// The assembler of the x86 and x86-64 JIT assembly.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Assembler {
bytes: Vec<u8>,
}
impl Assembler {
/// Creates a new JIT assembler.
pub fn new() -> Self {
Self {
bytes: Vec::with_capacity(64)
}
}
/// Puts some bytecode into the assembler.
pub fn put(&mut self, bytes: &[u8]) {
self.bytes.extend_from_slice(bytes);
}
/// Returns the bytecode inside.
pub fn inside(&self) -> &[u8] {&self.bytes}
/// Prepares a new compiled executable code.
#[cfg(any(unix))]
pub unsafe fn prepare<T>(&self) -> Result<Compiled<T>, io::Error> {
let ptr = mmap(
ptr::null_mut(),
self.bytes.len(),
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE,
-1,
0
);
if ptr == ptr::null_mut() {
Err(io::Error::last_os_error())
} else {
ptr::copy(self.bytes.as_ptr(), ptr as *mut u8, self.bytes.len());
Ok(Compiled {
func: ptr as *mut T,
size: self.bytes.len()
})
}
}
}
impl From<Vec<u8>> for Assembler {
fn from(bytes: Vec<u8>) -> Self {
Self {
bytes
}
}
}
/// A compiled executable code.
/// The function lifetime depends on this struct.
/// Should be only used with `fn`s (not `Fn`s).
#[derive(Debug, PartialEq, Eq)]
pub struct Compiled<T> {
func: *mut T,
size: usize,
}
impl<T> Compiled<T> {
/// Returns a pointer to the compiled function.
/// Do not call the function when the struct
/// is dropped.
pub unsafe fn func(&self) -> T
where
T: Copy
{
*(&self.func as *const *mut T as *const T)
}
/// Returns the length of the function.
pub fn len(&self) -> usize {self.size}
/// Returns the function bytecode as a vector.
pub fn bytecode(&self) -> Vec<u8> {
Vec::from(unsafe {
slice::from_raw_parts(self.func as *const u8, self.size)
})
}
}
#[cfg(any(unix))]
impl<T> Drop for Compiled<T> {
fn drop(&mut self) {
unsafe {
let result = munmap(self.func as *mut _, self.size);
debug_assert!(result >= 0);
}
}
}
Note that Compiled.func should actually be T, but I can’t cast *mut c_void as T. I also cannot transmute it, because transmute needs types with same size, but I can’t constraint T’s size.