From cf40171665dcf495bc9aae9edea5298bcb9762e6 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Tue, 15 Sep 2020 11:47:33 +0000 Subject: [PATCH] update wasmtime --- Cargo.lock | 1 + client/executor/common/src/error.rs | 13 +- client/executor/wasmtime/Cargo.toml | 1 + .../executor/wasmtime/src/instance_wrapper.rs | 120 +++++++++++++++--- client/executor/wasmtime/src/runtime.rs | 22 ++-- 5 files changed, 124 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edf9eef4d796f..faaf7c92d4534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6823,6 +6823,7 @@ dependencies = [ name = "sc-executor-wasmtime" version = "0.8.0-rc6" dependencies = [ + "anyhow", "assert_matches", "log", "parity-scale-codec", diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index ea4e57eb1d4ed..caed63c183e68 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -84,11 +84,22 @@ pub enum Error { /// No table is present. /// /// Call was requested that requires table but none was present in the instance. + #[display(fmt="No table exported by wasm blob")] NoTable, - /// No table is present. + /// No table entry is present. /// /// Call was requested that requires specific entry in the table to be present. + #[display(fmt="No table entry with index {} in wasm blob exported table", _0)] + #[from(ignore)] NoTableEntryWithIndex(u32), + /// Table entry is not a function. + #[display(fmt="Table element with index {} is not a function in wasm blob exported table", _0)] + #[from(ignore)] + TableElementIsNotAFunction(u32), + /// Function in table is null and thus cannot be called. + #[display(fmt="Table entry with index {} in wasm blob is null", _0)] + #[from(ignore)] + FunctionRefIsNull(u32), } impl std::error::Error for Error { diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 9618a659f5262..76eb4f516b2e7 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -12,6 +12,7 @@ description = "Defines a `WasmRuntime` that uses the Wasmtime JIT to execute." targets = ["x86_64-unknown-linux-gnu"] [dependencies] +anyhow = "1.0" log = "0.4.8" scoped-tls = "1.0" parity-wasm = "0.41.0" diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 9a4e44d3b106c..8924e5b220703 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -26,6 +26,7 @@ use std::{slice, marker}; use sc_executor_common::{ error::{Error, Result}, util::{WasmModuleInfo, DataSegmentsSnapshot}, + wasm_runtime::CallSite, }; use sp_wasm_interface::{Pointer, WordSize, Value}; use wasmtime::{Engine, Instance, Module, Memory, Table, Val, Func, Extern, Global, Store}; @@ -72,6 +73,40 @@ impl ModuleWrapper { } } +/// Format of the entrypoint. +pub enum EntryPointType { + /// Direct call. + Direct, + /// Indirect call. + Wrapped(u32), +} + +/// Wasm blob entry point. +pub struct EntryPoint { + call_type: EntryPointType, + func: wasmtime::Func, +} + +impl EntryPoint { + pub fn call(&self, data_ptr: i32, data_len: i32) -> anyhow::Result { + (match self.call_type { + EntryPointType::Direct => { + self.func.call(&[ + wasmtime::Val::I32(data_ptr), + wasmtime::Val::I32(data_len), + ]) + }, + EntryPointType::Wrapped(func) => { + self.func.call(&[ + wasmtime::Val::I32(func as _), + wasmtime::Val::I32(data_ptr), + wasmtime::Val::I32(data_len), + ]) + }, + }).map(|results| results[0].unwrap_i64() as u64) + } +} + /// Wrap the given WebAssembly Instance of a wasm module with Substrate-runtime. /// /// This struct is a handy wrapper around a wasmtime `Instance` that provides substrate specific @@ -150,24 +185,73 @@ impl InstanceWrapper { /// /// An entrypoint must have a signature `(i32, i32) -> i64`, otherwise this function will return /// an error. - pub fn resolve_entrypoint(&self, name: &str) -> Result { - // Resolve the requested method and verify that it has a proper signature. - let export = self - .instance - .get_export(name) - .ok_or_else(|| Error::from(format!("Exported method {} is not found", name)))?; - let entrypoint = extern_func(&export) - .ok_or_else(|| Error::from(format!("Export {} is not a function", name)))?; - match (entrypoint.ty().params(), entrypoint.ty().results()) { - (&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {} - _ => { - return Err(Error::from(format!( - "method {} have an unsupported signature", - name - ))) - } - } - Ok(entrypoint.clone()) + pub fn resolve_entrypoint(&self, site: CallSite) -> Result { + Ok(match site { + CallSite::Export(method) => { + // Resolve the requested method and verify that it has a proper signature. + let export = self + .instance + .get_export(method) + .ok_or_else(|| Error::from(format!("Exported method {} is not found", method)))?; + let func = extern_func(&export) + .ok_or_else(|| Error::from(format!("Export {} is not a function", method)))? + .clone(); + match (func.ty().params(), func.ty().results()) { + (&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {} + _ => { + return Err(Error::from(format!( + "method {} have an unsupported signature", + method, + ))) + } + } + EntryPoint { call_type: EntryPointType::Direct, func } + }, + CallSite::Table(func_ref) => { + let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?; + let val = table.get(func_ref) + .ok_or(Error::NoTableEntryWithIndex(func_ref))?; + let func = val + .funcref() + .ok_or(Error::TableElementIsNotAFunction(func_ref))? + .ok_or(Error::FunctionRefIsNull(func_ref))? + .clone(); + + match (func.ty().params(), func.ty().results()) { + (&[wasmtime::ValType::I32, wasmtime::ValType::I32], &[wasmtime::ValType::I64]) => {} + _ => { + return Err(Error::from(format!( + "Function @{} have an unsupported signature", + func_ref, + ))) + } + } + EntryPoint { call_type: EntryPointType::Direct, func } + }, + CallSite::TableWithWrapper { dispatcher_ref, func } => { + let table = self.instance.get_table("__indirect_function_table").ok_or(Error::NoTable)?; + let val = table.get(dispatcher_ref) + .ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?; + let dispatcher = val + .funcref() + .ok_or(Error::TableElementIsNotAFunction(dispatcher_ref))? + .ok_or(Error::FunctionRefIsNull(dispatcher_ref))?; + + match (dispatcher.ty().params(), dispatcher.ty().results()) { + ( + &[wasmtime::ValType::I32, wasmtime::ValType::I32, wasmtime::ValType::I32], + &[wasmtime::ValType::I64], + ) => {}, + _ => { + return Err(Error::from(format!( + "Function @{} have an unsupported signature", + dispatcher_ref, + ))) + } + } + EntryPoint { call_type: EntryPointType::Wrapped(func), func: dispatcher.clone() } + }, + }) } /// Returns an indirect function table of this instance. diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 365770b3fa866..9bc5c8635c7ee 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -18,14 +18,14 @@ use crate::host::HostState; use crate::imports::{Imports, resolve_imports}; -use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot}; +use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot, EntryPoint}; use crate::state_holder; use std::rc::Rc; use std::sync::Arc; use sc_executor_common::{ error::{Error, Result, WasmError}, - wasm_runtime::{WasmModule, WasmInstance}, + wasm_runtime::{WasmModule, WasmInstance, CallSite}, }; use sp_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; @@ -90,8 +90,8 @@ pub struct WasmtimeInstance { unsafe impl Send for WasmtimeInstance {} impl WasmInstance for WasmtimeInstance { - fn call(&self, method: &str, data: &[u8]) -> Result> { - let entrypoint = self.instance_wrapper.resolve_entrypoint(method)?; + fn call(&self, call_site: CallSite, data: &[u8]) -> Result> { + let entrypoint = self.instance_wrapper.resolve_entrypoint(call_site)?; let allocator = FreeingBumpHeapAllocator::new(self.heap_base); self.module_wrapper @@ -146,27 +146,21 @@ pub fn create_runtime( fn perform_call( data: &[u8], instance_wrapper: Rc, - entrypoint: wasmtime::Func, + entrypoint: EntryPoint, mut allocator: FreeingBumpHeapAllocator, ) -> Result> { let (data_ptr, data_len) = inject_input_data(&instance_wrapper, &mut allocator, data)?; let host_state = HostState::new(allocator, instance_wrapper.clone()); let ret = state_holder::with_initialized_state(&host_state, || { - match entrypoint.call(&[ - wasmtime::Val::I32(u32::from(data_ptr) as i32), - wasmtime::Val::I32(u32::from(data_len) as i32), - ]) { - Ok(results) => { - let retval = results[0].unwrap_i64() as u64; - Ok(unpack_ptr_and_len(retval)) - } + match entrypoint.call(u32::from(data_ptr) as i32, u32::from(data_len) as i32) { + Ok(retval) => Ok(unpack_ptr_and_len(retval)), Err(trap) => { return Err(Error::from(format!( "Wasm execution trapped: {}", trap ))); - } + }, } }); let (output_ptr, output_len) = ret?;