diff --git a/lib/clif-backend/src/lib.rs b/lib/clif-backend/src/lib.rs index 0d5df6d58..969348688 100644 --- a/lib/clif-backend/src/lib.rs +++ b/lib/clif-backend/src/lib.rs @@ -12,7 +12,7 @@ use cranelift_codegen::{ }; use target_lexicon::Triple; use wasmer_runtime::{ - backend::Compiler, + backend::{Compiler, Token}, error::{CompileError, CompileResult}, module::ModuleInner, }; @@ -28,7 +28,7 @@ impl CraneliftCompiler { impl Compiler for CraneliftCompiler { // Compiles wasm binary to a wasmer module. - fn compile(&self, wasm: &[u8]) -> CompileResult { + fn compile(&self, wasm: &[u8], _: Token) -> CompileResult { validate(wasm)?; let isa = get_isa(); diff --git a/lib/runtime/src/backend.rs b/lib/runtime/src/backend.rs index 4cc67a5fc..0ea709c3b 100644 --- a/lib/runtime/src/backend.rs +++ b/lib/runtime/src/backend.rs @@ -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; pub use crate::mmap::{Mmap, Protect}; 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 { - /// Compiles a `Module` from WebAssembly binary format - fn compile(&self, wasm: &[u8]) -> CompileResult; + /// Compiles a `Module` from WebAssembly binary format. + /// The `CompileToken` parameter ensures that this can only + /// be called from inside the runtime. + fn compile(&self, wasm: &[u8], _: Token) -> CompileResult; +} + +/// 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 { + /// 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( &self, module: &ModuleInner, local_func_index: LocalFuncIndex, + _: Token, ) -> Option>; } diff --git a/lib/runtime/src/backing.rs b/lib/runtime/src/backing.rs index 662b00858..b292a9637 100644 --- a/lib/runtime/src/backing.rs +++ b/lib/runtime/src/backing.rs @@ -1,4 +1,5 @@ use crate::{ + backend::Token, error::{LinkError, LinkResult}, export::{Context, Export}, import::Imports, @@ -178,14 +179,17 @@ impl LocalBacking { let sig_id = vm::SigId(sig_index.index() as u32); let func_data = match func_index.local_or_import(module) { - LocalOrImport::Local(local_func_index) => vm::ImportedFunc { - func: module - .func_resolver - .get(module, local_func_index) - .unwrap() - .as_ptr(), - vmctx, - }, + LocalOrImport::Local(local_func_index) => { + let token = Token::generate(); + vm::ImportedFunc { + func: module + .func_resolver + .get(module, local_func_index, token) + .unwrap() + .as_ptr(), + vmctx, + } + } LocalOrImport::Import(imported_func_index) => { imports.functions[imported_func_index].clone() } @@ -229,14 +233,17 @@ impl LocalBacking { let sig_id = vm::SigId(sig_index.index() as u32); let func_data = match func_index.local_or_import(module) { - LocalOrImport::Local(local_func_index) => vm::ImportedFunc { - func: module - .func_resolver - .get(module, local_func_index) - .unwrap() - .as_ptr(), - vmctx, - }, + LocalOrImport::Local(local_func_index) => { + let token = Token::generate(); + vm::ImportedFunc { + func: module + .func_resolver + .get(module, local_func_index, token) + .unwrap() + .as_ptr(), + vmctx, + } + } LocalOrImport::Import(imported_func_index) => { imports.functions[imported_func_index].clone() } diff --git a/lib/runtime/src/instance.rs b/lib/runtime/src/instance.rs index 075d4709b..ef57f2969 100644 --- a/lib/runtime/src/instance.rs +++ b/lib/runtime/src/instance.rs @@ -1,5 +1,6 @@ use crate::recovery::call_protected; use crate::{ + backend::Token, backing::{ImportBacking, LocalBacking}, error::{CallError, CallResult, Result}, export::{ @@ -67,11 +68,11 @@ impl Instance { /// Call an exported webassembly function given the export name. /// 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>, String>` in - /// order to support multi-value returns. - pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult> { + /// This returns `CallResult>` in order to support + /// the future multi-value returns webassembly feature. + pub fn call(&mut self, name: &str, args: &[Value]) -> CallResult> { let export_index = self.module .exports @@ -102,23 +103,13 @@ impl Instance { } impl Instance { - fn call_with_index( - &mut self, - func_index: FuncIndex, - args: &[Value], - ) -> CallResult> { - 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" - ); + fn call_with_index(&mut self, func_index: FuncIndex, args: &[Value]) -> CallResult> { + let sig_index = *self + .module + .func_assoc + .get(func_index) + .expect("broken invariant, incorrect func index"); + let signature = self.module.sig_registry.lookup_func_sig(sig_index); if !signature.check_sig(args) { Err(CallError::Signature { @@ -127,35 +118,78 @@ impl Instance { })? } - 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(); + // Create an output vector that's full of dummy values. + let mut returns = vec![Value::I32(0); signature.returns.len()]; - 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 - }) - })?) + let vmctx = match func_index.local_or_import(&self.module) { + LocalOrImport::Local(local_func_index) => &mut *self.inner.vmctx, + LocalOrImport::Import(imported_func_index) => { + self.inner.import_backing.functions[imported_func_index].vmctx + } + }; + + let token = Token::generate(); + + self.module.protected_caller.call( + &self.module, + func_index, + args, + &mut returns, + vmctx, + 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"); let (func_ptr, ctx) = match func_index.local_or_import(module) { - LocalOrImport::Local(local_func_index) => ( - module - .func_resolver - .get(&module, local_func_index) - .expect("broken invariant, func resolver not synced with module.exports") - .cast() - .as_ptr() as *const _, - Context::Internal, - ), + LocalOrImport::Local(local_func_index) => { + let token = Token::generate(); + ( + module + .func_resolver + .get(&module, local_func_index, token) + .expect("broken invariant, func resolver not synced with module.exports") + .cast() + .as_ptr() as *const _, + Context::Internal, + ) + } LocalOrImport::Import(imported_func_index) => { let imported_func = &self.import_backing.functions[imported_func_index]; ( diff --git a/lib/runtime/src/lib.rs b/lib/runtime/src/lib.rs index dd7f62253..f7517c593 100644 --- a/lib/runtime/src/lib.rs +++ b/lib/runtime/src/lib.rs @@ -32,7 +32,8 @@ use std::rc::Rc; /// Compile a webassembly module using the provided compiler. pub fn compile(wasm: &[u8], compiler: &dyn backend::Compiler) -> CompileResult { + let token = backend::Token::generate(); compiler - .compile(wasm) + .compile(wasm, token) .map(|inner| module::Module::new(Rc::new(inner))) } diff --git a/lib/runtime/src/module.rs b/lib/runtime/src/module.rs index 7ca3e30bf..2ccf5f7c0 100644 --- a/lib/runtime/src/module.rs +++ b/lib/runtime/src/module.rs @@ -1,5 +1,5 @@ use crate::{ - backend::FuncResolver, + backend::{FuncResolver, ProtectedCaller}, error::Result, import::Imports, sig_registry::SigRegistry, @@ -18,6 +18,7 @@ use std::rc::Rc; #[doc(hidden)] pub struct ModuleInner { pub func_resolver: Box, + pub protected_caller: Box, // This are strictly local and the typsystem ensures that. pub memories: Map, pub globals: Map,