Code loader framework.

This commit is contained in:
losfair
2019-05-03 00:23:41 +08:00
parent 99c101f312
commit 14bfd75ac9
7 changed files with 242 additions and 1 deletions

View File

@ -80,6 +80,9 @@ pub trait RunnableModule: Send + Sync {
fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option<Wasm>;
unsafe fn do_early_trap(&self, data: Box<dyn Any>) -> !;
fn get_code(&self) -> Option<&[u8]> { None }
fn get_offsets(&self) -> Option<Vec<usize>> { None }
}
pub trait CacheGen: Send + Sync {

View File

@ -12,6 +12,8 @@ use crate::{
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
vm,
loader::{self, Loader, Instance as _},
structures::TypedIndex,
};
use smallvec::{smallvec, SmallVec};
use std::{mem, ptr::NonNull, sync::Arc};
@ -38,7 +40,7 @@ impl Drop for InstanceInner {
///
/// [`ImportObject`]: struct.ImportObject.html
pub struct Instance {
module: Arc<ModuleInner>,
pub module: Arc<ModuleInner>,
inner: Box<InstanceInner>,
#[allow(dead_code)]
import_object: ImportObject,
@ -127,6 +129,10 @@ impl Instance {
Ok(instance)
}
pub fn load<T: Loader>(&self, loader: T) -> ::std::result::Result<T::Instance, T::Error> {
loader.load(&*self.module.runnable_module, &self.module.info, unsafe { &(*self.inner.vmctx).internal })
}
/// Through generic magic and the awe-inspiring power of traits, we bring you...
///
/// # "Func"
@ -214,6 +220,32 @@ impl Instance {
}
}
pub fn resolve_local_func(&self, name: &str) -> ResolveResult<usize> {
let export_index =
self.module
.info
.exports
.get(name)
.ok_or_else(|| ResolveError::ExportNotFound {
name: name.to_string(),
})?;
if let ExportIndex::Func(func_index) = export_index {
match func_index.local_or_import(&self.module.info) {
LocalOrImport::Local(x) => Ok(x.index()),
LocalOrImport::Import(x) => Err(ResolveError::ExportWrongType {
name: name.to_string(),
}
.into())
}
} else {
Err(ResolveError::ExportWrongType {
name: name.to_string(),
}
.into())
}
}
/// This returns the representation of a function that can be called
/// safely.
///

View File

@ -33,6 +33,7 @@ pub mod units;
pub mod vm;
#[doc(hidden)]
pub mod vmcalls;
pub mod loader;
use self::error::CompileResult;
#[doc(inline)]

View File

@ -0,0 +1,130 @@
use std::{
fmt::Debug,
ops::{Deref, DerefMut},
};
use crate::{
backend::RunnableModule,
vm::InternalCtx,
module::ModuleInfo,
types::Value,
};
use libc::{
c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ,
PROT_WRITE,
};
pub trait Loader {
type Instance: Instance;
type Error: Debug;
fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &InternalCtx) -> Result<Self::Instance, Self::Error>;
}
pub trait Instance {
type Error: Debug;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error>;
}
pub struct LocalLoader;
impl Loader for LocalLoader {
type Instance = LocalInstance;
type Error = String;
fn load(&self, rm: &dyn RunnableModule, module: &ModuleInfo, ctx: &InternalCtx) -> Result<Self::Instance, Self::Error> {
let code = rm.get_code().unwrap();
let mut code_mem = CodeMemory::new(code.len());
code_mem[..code.len()].copy_from_slice(code);
code_mem.make_executable();
Ok(LocalInstance {
code: code_mem,
offsets: rm.get_offsets().unwrap(),
})
}
}
pub struct LocalInstance {
code: CodeMemory,
offsets: Vec<usize>,
}
impl Instance for LocalInstance {
type Error = String;
fn call(&mut self, id: usize, args: &[Value]) -> Result<u64, Self::Error> {
let offset = self.offsets[id];
let addr: *const u8 = unsafe { self.code.as_ptr().offset(offset as isize) };
use std::mem::transmute;
Ok(unsafe {
match args.len() {
0 => (transmute::<_, extern "C" fn() -> u64>(addr))(),
1 => (transmute::<_, extern "C" fn(u64) -> u64>(addr))(args[0].to_u64()),
2 => (transmute::<_, extern "C" fn(u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64()),
3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64()),
4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64(), args[3].to_u64()),
5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u64>(addr))(args[0].to_u64(), args[1].to_u64(), args[2].to_u64(), args[3].to_u64(), args[4].to_u64()),
_ => return Err("too many arguments".into())
}
})
}
}
pub struct CodeMemory {
ptr: *mut u8,
size: usize,
}
impl CodeMemory {
pub fn new(size: usize) -> CodeMemory {
fn round_up_to_page_size(size: usize) -> usize {
(size + (4096 - 1)) & !(4096 - 1)
}
let size = round_up_to_page_size(size);
let ptr = unsafe {
mmap(
::std::ptr::null_mut(),
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0,
)
};
if ptr as isize == -1 {
panic!("cannot allocate code memory");
}
CodeMemory {
ptr: ptr as _,
size: size,
}
}
pub fn make_executable(&mut self) {
if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
panic!("cannot set code memory to executable");
}
}
}
impl Drop for CodeMemory {
fn drop(&mut self) {
unsafe { munmap(self.ptr as _, self.size); }
}
}
impl Deref for CodeMemory {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
::std::slice::from_raw_parts(self.ptr, self.size)
}
}
}
impl DerefMut for CodeMemory {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
::std::slice::from_raw_parts_mut(self.ptr, self.size)
}
}
}

View File

@ -45,6 +45,15 @@ impl Value {
Value::F64(_) => Type::F64,
}
}
pub fn to_u64(&self) -> u64 {
match *self {
Value::I32(x) => x as u32 as u64,
Value::I64(x) => x as u64,
Value::F32(x) => f32::to_bits(x) as u64,
Value::F64(x) => f64::to_bits(x),
}
}
}
impl From<i32> for Value {