add 'ProtectedCaller' to runtime

This commit is contained in:
Lachlan Sneff 2019-01-18 12:13:01 -08:00
parent ed87168c48
commit 705708cafe
6 changed files with 183 additions and 81 deletions

View File

@ -12,7 +12,7 @@ use cranelift_codegen::{
}; };
use target_lexicon::Triple; use target_lexicon::Triple;
use wasmer_runtime::{ use wasmer_runtime::{
backend::Compiler, backend::{Compiler, Token},
error::{CompileError, CompileResult}, error::{CompileError, CompileResult},
module::ModuleInner, module::ModuleInner,
}; };
@ -28,7 +28,7 @@ impl CraneliftCompiler {
impl Compiler for CraneliftCompiler { impl Compiler for CraneliftCompiler {
// Compiles wasm binary to a wasmer module. // Compiles wasm binary to a wasmer module.
fn compile(&self, wasm: &[u8]) -> CompileResult<ModuleInner> { fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner> {
validate(wasm)?; validate(wasm)?;
let isa = get_isa(); let isa = get_isa();

View File

@ -1,18 +1,74 @@
use crate::{error::CompileResult, module::ModuleInner, types::LocalFuncIndex, vm}; use crate::{
error::CompileResult,
error::RuntimeResult,
module::ModuleInner,
types::{FuncIndex, LocalFuncIndex, Value},
vm,
};
use std::ptr::NonNull; use std::ptr::NonNull;
pub use crate::mmap::{Mmap, Protect}; pub use crate::mmap::{Mmap, Protect};
pub use crate::sig_registry::SigRegistry; pub use crate::sig_registry::SigRegistry;
/// This type cannot be constructed from
/// outside the runtime crate.
pub struct Token {
_private: (),
}
impl Token {
pub(crate) fn generate() -> Self {
Self { _private: () }
}
}
pub trait Compiler { pub trait Compiler {
/// Compiles a `Module` from WebAssembly binary format /// Compiles a `Module` from WebAssembly binary format.
fn compile(&self, wasm: &[u8]) -> CompileResult<ModuleInner>; /// The `CompileToken` parameter ensures that this can only
/// be called from inside the runtime.
fn compile(&self, wasm: &[u8], _: Token) -> CompileResult<ModuleInner>;
}
/// The functionality exposed by this trait is expected to be used
/// for calling functions exported by a webassembly module from
/// host code only.
pub trait ProtectedCaller {
/// This calls the exported function designated by `local_func_index`.
/// Important to note, this supports calling imported functions that are
/// then exported.
///
/// It's invalid to attempt to call a local function that isn't exported and
/// the implementation is expected to check for that. The implementation
/// is also expected to check for correct parameter types and correct
/// parameter number.
///
/// The `returns` parameter is filled with dummy values when passed in and upon function
/// return, will be filled with the return values of the wasm function, as long as the
/// call completed successfully.
///
/// The existance of the Token parameter ensures that this can only be called from
/// within the runtime crate.
fn call(
&self,
module: &ModuleInner,
func_index: FuncIndex,
params: &[Value],
returns: &mut [Value],
vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<()>;
} }
pub trait FuncResolver { pub trait FuncResolver {
/// This returns a pointer to the function designated by the `local_func_index`
/// parameter.
///
/// The existance of the Token parameter ensures that this can only be called from
/// within the runtime crate.
fn get( fn get(
&self, &self,
module: &ModuleInner, module: &ModuleInner,
local_func_index: LocalFuncIndex, local_func_index: LocalFuncIndex,
_: Token,
) -> Option<NonNull<vm::Func>>; ) -> Option<NonNull<vm::Func>>;
} }

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
backend::Token,
error::{LinkError, LinkResult}, error::{LinkError, LinkResult},
export::{Context, Export}, export::{Context, Export},
import::Imports, import::Imports,
@ -178,14 +179,17 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32); let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) { let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc { LocalOrImport::Local(local_func_index) => {
func: module let token = Token::generate();
.func_resolver vm::ImportedFunc {
.get(module, local_func_index) func: module
.unwrap() .func_resolver
.as_ptr(), .get(module, local_func_index, token)
vmctx, .unwrap()
}, .as_ptr(),
vmctx,
}
}
LocalOrImport::Import(imported_func_index) => { LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone() imports.functions[imported_func_index].clone()
} }
@ -229,14 +233,17 @@ impl LocalBacking {
let sig_id = vm::SigId(sig_index.index() as u32); let sig_id = vm::SigId(sig_index.index() as u32);
let func_data = match func_index.local_or_import(module) { let func_data = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => vm::ImportedFunc { LocalOrImport::Local(local_func_index) => {
func: module let token = Token::generate();
.func_resolver vm::ImportedFunc {
.get(module, local_func_index) func: module
.unwrap() .func_resolver
.as_ptr(), .get(module, local_func_index, token)
vmctx, .unwrap()
}, .as_ptr(),
vmctx,
}
}
LocalOrImport::Import(imported_func_index) => { LocalOrImport::Import(imported_func_index) => {
imports.functions[imported_func_index].clone() imports.functions[imported_func_index].clone()
} }

View File

@ -1,5 +1,6 @@
use crate::recovery::call_protected; use crate::recovery::call_protected;
use crate::{ use crate::{
backend::Token,
backing::{ImportBacking, LocalBacking}, backing::{ImportBacking, LocalBacking},
error::{CallError, CallResult, Result}, error::{CallError, CallResult, Result},
export::{ export::{
@ -67,11 +68,11 @@ impl Instance {
/// Call an exported webassembly function given the export name. /// Call an exported webassembly function given the export name.
/// Pass arguments by wrapping each one in the `Value` enum. /// Pass arguments by wrapping each one in the `Value` enum.
/// The returned value is also returned in a `Value`. /// The returned values are also each wrapped in a `Value`.
/// ///
/// This will eventually return `Result<Option<Vec<Value>>, String>` in /// This returns `CallResult<Vec<Value>>` in order to support
/// order to support multi-value returns. /// the future multi-value returns webassembly feature.
pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult<Option<Value>> { pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult<Vec<Value>> {
let export_index = let export_index =
self.module self.module
.exports .exports
@ -102,23 +103,13 @@ impl Instance {
} }
impl Instance { impl Instance {
fn call_with_index( fn call_with_index(&mut self, func_index: FuncIndex, args: &[Value]) -> CallResult<Vec<Value>> {
&mut self, let sig_index = *self
func_index: FuncIndex, .module
args: &[Value], .func_assoc
) -> CallResult<Option<Value>> { .get(func_index)
let (func_ref, ctx, signature) = self.inner.get_func_from_index(&self.module, func_index); .expect("broken invariant, incorrect func index");
let signature = self.module.sig_registry.lookup_func_sig(sig_index);
let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
let vmctx_ptr = match ctx {
Context::External(vmctx) => vmctx,
Context::Internal => &mut *self.inner.vmctx,
};
assert!(
signature.returns.len() <= 1,
"multi-value returns not yet supported"
);
if !signature.check_sig(args) { if !signature.check_sig(args) {
Err(CallError::Signature { Err(CallError::Signature {
@ -127,35 +118,78 @@ impl Instance {
})? })?
} }
let libffi_args: Vec<_> = args // Create an output vector that's full of dummy values.
.iter() let mut returns = vec![Value::I32(0); signature.returns.len()];
.map(|val| match val {
Value::I32(ref x) => libffi_arg(x),
Value::I64(ref x) => libffi_arg(x),
Value::F32(ref x) => libffi_arg(x),
Value::F64(ref x) => libffi_arg(x),
})
.chain(iter::once(libffi_arg(&vmctx_ptr)))
.collect();
Ok(call_protected(|| { let vmctx = match func_index.local_or_import(&self.module) {
signature LocalOrImport::Local(local_func_index) => &mut *self.inner.vmctx,
.returns LocalOrImport::Import(imported_func_index) => {
.first() self.inner.import_backing.functions[imported_func_index].vmctx
.map(|ty| match ty { }
Type::I32 => Value::I32(unsafe { libffi_call(func_ptr, &libffi_args) }), };
Type::I64 => Value::I64(unsafe { libffi_call(func_ptr, &libffi_args) }),
Type::F32 => Value::F32(unsafe { libffi_call(func_ptr, &libffi_args) }), let token = Token::generate();
Type::F64 => Value::F64(unsafe { libffi_call(func_ptr, &libffi_args) }),
}) self.module.protected_caller.call(
.or_else(|| { &self.module,
// call with no returns func_index,
unsafe { args,
libffi_call::<()>(func_ptr, &libffi_args); &mut returns,
} vmctx,
None token,
}) )?;
})?)
Ok(returns)
// let (func_ref, ctx, signature) = self.inner.get_func_from_index(&self.module, func_index);
// let func_ptr = CodePtr::from_ptr(func_ref.inner() as _);
// let vmctx_ptr = match ctx {
// Context::External(vmctx) => vmctx,
// Context::Internal => &mut *self.inner.vmctx,
// };
// assert!(
// signature.returns.len() <= 1,
// "multi-value returns not yet supported"
// );
// if !signature.check_sig(args) {
// Err(CallError::Signature {
// expected: signature.clone(),
// found: args.iter().map(|val| val.ty()).collect(),
// })?
// }
// let libffi_args: Vec<_> = args
// .iter()
// .map(|val| match val {
// Value::I32(ref x) => libffi_arg(x),
// Value::I64(ref x) => libffi_arg(x),
// Value::F32(ref x) => libffi_arg(x),
// Value::F64(ref x) => libffi_arg(x),
// })
// .chain(iter::once(libffi_arg(&vmctx_ptr)))
// .collect();
// Ok(call_protected(|| {
// signature
// .returns
// .first()
// .map(|ty| match ty {
// Type::I32 => Value::I32(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::I64 => Value::I64(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::F32 => Value::F32(unsafe { libffi_call(func_ptr, &libffi_args) }),
// Type::F64 => Value::F64(unsafe { libffi_call(func_ptr, &libffi_args) }),
// })
// .or_else(|| {
// // call with no returns
// unsafe {
// libffi_call::<()>(func_ptr, &libffi_args);
// }
// None
// })
// })?)
} }
} }
@ -218,15 +252,18 @@ impl InstanceInner {
.expect("broken invariant, incorrect func index"); .expect("broken invariant, incorrect func index");
let (func_ptr, ctx) = match func_index.local_or_import(module) { let (func_ptr, ctx) = match func_index.local_or_import(module) {
LocalOrImport::Local(local_func_index) => ( LocalOrImport::Local(local_func_index) => {
module let token = Token::generate();
.func_resolver (
.get(&module, local_func_index) module
.expect("broken invariant, func resolver not synced with module.exports") .func_resolver
.cast() .get(&module, local_func_index, token)
.as_ptr() as *const _, .expect("broken invariant, func resolver not synced with module.exports")
Context::Internal, .cast()
), .as_ptr() as *const _,
Context::Internal,
)
}
LocalOrImport::Import(imported_func_index) => { LocalOrImport::Import(imported_func_index) => {
let imported_func = &self.import_backing.functions[imported_func_index]; let imported_func = &self.import_backing.functions[imported_func_index];
( (

View File

@ -32,7 +32,8 @@ use std::rc::Rc;
/// Compile a webassembly module using the provided compiler. /// Compile a webassembly module using the provided compiler.
pub fn compile(wasm: &[u8], compiler: &dyn backend::Compiler) -> CompileResult<module::Module> { pub fn compile(wasm: &[u8], compiler: &dyn backend::Compiler) -> CompileResult<module::Module> {
let token = backend::Token::generate();
compiler compiler
.compile(wasm) .compile(wasm, token)
.map(|inner| module::Module::new(Rc::new(inner))) .map(|inner| module::Module::new(Rc::new(inner)))
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
backend::FuncResolver, backend::{FuncResolver, ProtectedCaller},
error::Result, error::Result,
import::Imports, import::Imports,
sig_registry::SigRegistry, sig_registry::SigRegistry,
@ -18,6 +18,7 @@ use std::rc::Rc;
#[doc(hidden)] #[doc(hidden)]
pub struct ModuleInner { pub struct ModuleInner {
pub func_resolver: Box<dyn FuncResolver>, pub func_resolver: Box<dyn FuncResolver>,
pub protected_caller: Box<dyn ProtectedCaller>,
// This are strictly local and the typsystem ensures that. // This are strictly local and the typsystem ensures that.
pub memories: Map<LocalMemoryIndex, Memory>, pub memories: Map<LocalMemoryIndex, Memory>,
pub globals: Map<LocalGlobalIndex, Global>, pub globals: Map<LocalGlobalIndex, Global>,