| //! # wasmi |
| //! |
| //! This library allows to load WebAssembly modules in binary format and invoke functions on them. |
| //! |
| //! # Introduction |
| //! |
| //! WebAssembly (wasm) is a safe, portable, compact format that designed for efficient execution. |
| //! |
| //! Wasm code is distributed in a form of modules, that contains definitions of: |
| //! |
| //! - functions, |
| //! - global variables, |
| //! - linear memories, |
| //! - tables. |
| //! |
| //! and this definitions can be imported. Also, each definition can be exported. |
| //! |
| //! In addition to definitions, modules can define initialization data for their memories or tables that takes the |
| //! form of segments copied to given offsets. They can also define a `start` function that is automatically executed. |
| //! |
| //! ## Loading and Validation |
| //! |
| //! Before execution a module should be validated. This process checks that module is well-formed |
| //! and makes only allowed operations. |
| //! |
| //! Valid modules can't access memory out of it's sandbox, can't cause stack underflow |
| //! and can call functions only with correct signatures. |
| //! |
| //! ## Instantiatiation |
| //! |
| //! In order to execute code in wasm module it should be instatiated. |
| //! Instantiation includes the following steps: |
| //! |
| //! 1. Create an empty module instance, |
| //! 2. Resolve definition instances for each declared import in the module, |
| //! 3. Instantiate definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc), |
| //! 4. Initialize memory and table contents by copiying segments into them, |
| //! 5. Execute `start` function, if any. |
| //! |
| //! After these steps, module instance are ready to execute functions. |
| //! |
| //! ## Execution |
| //! |
| //! It is allowed to only execute functions which are exported by a module. |
| //! Functions can either return a result or trap (e.g. there can't be linking-error at the middle of execution). |
| //! This property is ensured by the validation process. |
| //! |
| //! # Examples |
| //! |
| //! ```rust |
| //! extern crate wasmi; |
| //! extern crate wabt; |
| //! |
| //! use wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue}; |
| //! |
| //! fn main() { |
| //! // Parse WAT (WebAssembly Text format) into wasm bytecode. |
| //! let wasm_binary: Vec<u8> = |
| //! wabt::wat2wasm( |
| //! r#" |
| //! (module |
| //! (func (export "test") (result i32) |
| //! i32.const 1337 |
| //! ) |
| //! ) |
| //! "#, |
| //! ) |
| //! .expect("failed to parse wat"); |
| //! |
| //! // Load wasm binary and prepare it for instantiation. |
| //! let module = wasmi::Module::from_buffer(&wasm_binary) |
| //! .expect("failed to load wasm"); |
| //! |
| //! // Instantiate a module with empty imports and |
| //! // asserting that there is no `start` function. |
| //! let instance = |
| //! ModuleInstance::new( |
| //! &module, |
| //! &ImportsBuilder::default() |
| //! ) |
| //! .expect("failed to instantiate wasm module") |
| //! .assert_no_start(); |
| //! |
| //! // Finally, invoke exported function "test" with no parameters |
| //! // and empty external function executor. |
| //! assert_eq!( |
| //! instance.invoke_export( |
| //! "test", |
| //! &[], |
| //! &mut NopExternals, |
| //! ).expect("failed to execute export"), |
| //! Some(RuntimeValue::I32(1337)), |
| //! ); |
| //! } |
| //! ``` |
| #![warn(missing_docs)] |
| |
| #![cfg_attr(not(target_env = "sgx"), no_std)] |
| #![cfg_attr(target_env = "sgx", feature(rustc_private))] |
| |
| #[cfg(not(target_env = "sgx"))] |
| #[macro_use] |
| extern crate sgx_tstd as std; |
| |
| extern crate parity_wasm; |
| extern crate byteorder; |
| extern crate memory_units as memory_units_crate; |
| |
| #[macro_use] |
| extern crate serde_derive; |
| extern crate serde; |
| |
| #[cfg(all(not(feature = "32bit_opt_in"), target_pointer_width = "32"))] |
| compile_error! { |
| "32-bit targets are not supported at the moment. |
| You can use '32bit_opt_in' feature. |
| See https://github.com/pepyakin/wasmi/issues/43" |
| } |
| |
| use std::prelude::v1::*; |
| use std::fmt; |
| use std::error; |
| use std::collections::HashMap; |
| |
| /// Error type which can thrown by wasm code or by host environment. |
| /// |
| /// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution. |
| /// Traps can't be handled by WebAssembly code, but are reported to the embedder. |
| #[derive(Debug, Serialize, Deserialize)] |
| pub struct Trap { |
| kind: TrapKind, |
| } |
| |
| impl Trap { |
| /// Create new trap. |
| pub fn new(kind: TrapKind) -> Trap { |
| Trap { kind } |
| } |
| |
| /// Returns kind of this trap. |
| pub fn kind(&self) -> &TrapKind { |
| &self.kind |
| } |
| } |
| |
| impl fmt::Display for Trap { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!(f, "Trap: {:?}", self.kind) |
| } |
| } |
| |
| impl error::Error for Trap { |
| fn description(&self) -> &str { |
| "runtime trap" |
| } |
| } |
| |
| /// Error type which can thrown by wasm code or by host environment. |
| /// |
| /// See [`Trap`] for details. |
| /// |
| /// [`Trap`]: struct.Trap.html |
| #[derive(Debug, Serialize, Deserialize)] |
| pub enum TrapKind { |
| /// Wasm code executed `unreachable` opcode. |
| /// |
| /// `unreachable` is a special opcode which always traps upon execution. |
| /// This opcode have a similar purpose as `ud2` in x86. |
| Unreachable, |
| |
| /// Attempt to load or store at the address which |
| /// lies outside of bounds of the memory. |
| /// |
| /// Since addresses are interpreted as unsigned integers, out of bounds access |
| /// can't happen with negative addresses (i.e. they will always wrap). |
| MemoryAccessOutOfBounds, |
| |
| /// Attempt to access table element at index which |
| /// lies outside of bounds. |
| /// |
| /// This typically can happen when `call_indirect` is executed |
| /// with index that lies out of bounds. |
| /// |
| /// Since indexes are interpreted as unsinged integers, out of bounds access |
| /// can't happen with negative indexes (i.e. they will always wrap). |
| TableAccessOutOfBounds, |
| |
| /// Attempt to access table element which is uninitialized (i.e. `None`). |
| /// |
| /// This typically can happen when `call_indirect` is executed. |
| ElemUninitialized, |
| |
| /// Attempt to divide by zero. |
| /// |
| /// This trap typically can happen if `div` or `rem` is executed with |
| /// zero as divider. |
| DivisionByZero, |
| |
| /// Attempt to make a conversion to an int failed. |
| /// |
| /// This can happen when: |
| /// |
| /// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is |
| /// because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer. |
| /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer. |
| InvalidConversionToInt, |
| |
| /// Stack overflow. |
| /// |
| /// This is likely caused by some infinite or very deep recursion. |
| /// Extensive inlining might also be the cause of stack overflow. |
| StackOverflow, |
| |
| /// Attempt to invoke a function with mismatching signature. |
| /// |
| /// This can happen if [`FuncInstance`] was invoked |
| /// with mismatching [signature][`Signature`]. |
| /// |
| /// This can always happen with indirect calls. `call_indirect` instruction always |
| /// specifies the expected signature of function. If `call_indirect` is executed |
| /// with index that points on function with signature different that is |
| /// expected by this `call_indirect`, this trap is raised. |
| /// |
| /// [`Signature`]: struct.Signature.html |
| UnexpectedSignature, |
| |
| // Error specified by the host. |
| // |
| // Typically returned from an implementation of [`Externals`]. |
| // |
| // [`Externals`]: trait.Externals.html |
| //Host(Box<host::HostError>), |
| } |
| |
| /// Internal interpreter error. |
| #[derive(Debug, Serialize, Deserialize)] |
| pub enum Error { |
| /// Module validation error. Might occur only at load time. |
| Validation(String), |
| /// Error while instantiating a module. Might occur when provided |
| /// with incorrect exports (i.e. linkage failure). |
| Instantiation(String), |
| /// Function-level error. |
| Function(String), |
| /// Table-level error. |
| Table(String), |
| /// Memory-level error. |
| Memory(String), |
| /// Global-level error. |
| Global(String), |
| /// Value-level error. |
| Value(String), |
| /// Trap. |
| Trap(Trap), |
| // Custom embedder error. |
| //Host(Box<host::HostError>), |
| } |
| |
| //impl Error { |
| // Returns [`HostError`] if this `Error` represents some host error. |
| // |
| // I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error. |
| // |
| // [`HostError`]: trait.HostError.html |
| // [`Host`]: enum.Error.html#variant.Host |
| // [`Trap`]: enum.Error.html#variant.Trap |
| // [`TrapKind::Host`]: enum.TrapKind.html#variant.Host |
| //pub fn as_host_error(&self) -> Option<&host::HostError> { |
| // match *self { |
| // Error::Host(ref host_err) => Some(&**host_err), |
| // Error::Trap(ref trap) => match *trap.kind() { |
| // TrapKind::Host(ref host_err) => Some(&**host_err), |
| // _ => None, |
| // } |
| // _ => None, |
| // } |
| //} |
| //} |
| |
| impl Into<String> for Error { |
| fn into(self) -> String { |
| match self { |
| Error::Validation(s) => s, |
| Error::Instantiation(s) => s, |
| Error::Function(s) => s, |
| Error::Table(s) => s, |
| Error::Memory(s) => s, |
| Error::Global(s) => s, |
| Error::Value(s) => s, |
| Error::Trap(s) => format!("trap: {:?}", s), |
| // Error::Host(e) => format!("user: {}", e), |
| } |
| } |
| } |
| |
| impl fmt::Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match *self { |
| Error::Validation(ref s) => write!(f, "Validation: {}", s), |
| Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s), |
| Error::Function(ref s) => write!(f, "Function: {}", s), |
| Error::Table(ref s) => write!(f, "Table: {}", s), |
| Error::Memory(ref s) => write!(f, "Memory: {}", s), |
| Error::Global(ref s) => write!(f, "Global: {}", s), |
| Error::Value(ref s) => write!(f, "Value: {}", s), |
| Error::Trap(ref s) => write!(f, "Trap: {:?}", s), |
| // Error::Host(ref e) => write!(f, "User: {}", e), |
| } |
| } |
| } |
| |
| impl error::Error for Error { |
| fn description(&self) -> &str { |
| match *self { |
| Error::Validation(ref s) => s, |
| Error::Instantiation(ref s) => s, |
| Error::Function(ref s) => s, |
| Error::Table(ref s) => s, |
| Error::Memory(ref s) => s, |
| Error::Global(ref s) => s, |
| Error::Value(ref s) => s, |
| Error::Trap(_) => "Trap", |
| // Error::Host(_) => "Host error", |
| } |
| } |
| } |
| |
| //impl<U> From<U> for Error where U: host::HostError + Sized { |
| // fn from(e: U) -> Self { |
| // Error::Host(Box::new(e)) |
| // } |
| //} |
| // |
| //impl<U> From<U> for Trap where U: host::HostError + Sized { |
| // fn from(e: U) -> Self { |
| // Trap::new(TrapKind::Host(Box::new(e))) |
| // } |
| //} |
| |
| impl From<Trap> for Error { |
| fn from(e: Trap) -> Error { |
| Error::Trap(e) |
| } |
| } |
| |
| impl From<TrapKind> for Trap { |
| fn from(e: TrapKind) -> Trap { |
| Trap::new(e) |
| } |
| } |
| |
| impl From<validation::Error> for Error { |
| fn from(e: validation::Error) -> Error { |
| Error::Validation(e.to_string()) |
| } |
| } |
| |
| mod validation; |
| mod common; |
| mod memory; |
| mod module; |
| mod runner; |
| mod table; |
| mod value; |
| mod host; |
| mod imports; |
| mod global; |
| mod func; |
| mod types; |
| // |
| ////#[cfg(test)] |
| ////mod tests; |
| // |
| pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE}; |
| pub use self::table::{TableInstance, TableRef}; |
| pub use self::value::{RuntimeValue, FromRuntimeValue}; |
| pub use self::host::{Externals, NopExternals, HostError, RuntimeArgs}; |
| pub use self::imports::{ModuleImportResolver, ImportResolver, ImportsBuilder}; |
| pub use self::module::{ModuleInstance, ModuleRef, ExternVal, NotStartedModuleRef}; |
| pub use self::global::{GlobalInstance, GlobalRef}; |
| pub use self::func::{FuncInstance, FuncRef}; |
| pub use self::types::{Signature, ValueType, GlobalDescriptor, TableDescriptor, MemoryDescriptor}; |
| |
| /// WebAssembly-specific sizes and units. |
| pub mod memory_units { |
| pub use memory_units_crate::wasm32::*; |
| pub use memory_units_crate::{Bytes, ByteSize, RoundUpTo, size_of}; |
| } |
| |
| /// Deserialized module prepared for instantiation. |
| pub struct Module { |
| labels: HashMap<usize, HashMap<usize, usize>>, |
| module: parity_wasm::elements::Module, |
| } |
| |
| impl Module { |
| |
| /// Create `Module` from `parity_wasm::elements::Module`. |
| /// |
| /// This function will load, validate and prepare a `parity_wasm`'s `Module`. |
| /// |
| /// # Errors |
| /// |
| /// Returns `Err` if provided `Module` is not valid. |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// extern crate parity_wasm; |
| /// extern crate wasmi; |
| /// |
| /// use parity_wasm::builder; |
| /// use parity_wasm::elements; |
| /// |
| /// fn main() { |
| /// let parity_module = |
| /// builder::module() |
| /// .function() |
| /// .signature().with_param(elements::ValueType::I32).build() |
| /// .body().build() |
| /// .build() |
| /// .build(); |
| /// |
| /// let module = wasmi::Module::from_parity_wasm_module(parity_module) |
| /// .expect("parity-wasm builder generated invalid module!"); |
| /// |
| /// // Instantiate `module`, etc... |
| /// } |
| /// ``` |
| pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> { |
| use validation::{validate_module, ValidatedModule}; |
| let ValidatedModule { |
| labels, |
| module, |
| } = validate_module(module)?; |
| |
| Ok(Module { |
| labels, |
| module, |
| }) |
| } |
| |
| /// Create `Module` from a given buffer. |
| /// |
| /// This function will deserialize wasm module from a given module, |
| /// validate and prepare it for instantiation. |
| /// |
| /// # Errors |
| /// |
| /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary. |
| /// |
| /// # Examples |
| /// |
| /// ```rust |
| /// extern crate wasmi; |
| /// |
| /// fn main() { |
| /// let module = |
| /// wasmi::Module::from_buffer( |
| /// // Minimal module: |
| /// // \0asm - magic |
| /// // 0x01 - version (in little-endian) |
| /// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00] |
| /// ).expect("Failed to load minimal module"); |
| /// |
| /// // Instantiate `module`, etc... |
| /// } |
| /// ``` |
| pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> { |
| let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref()) |
| .map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?; |
| Module::from_parity_wasm_module(module) |
| } |
| |
| pub(crate) fn module(&self) -> &parity_wasm::elements::Module { |
| &self.module |
| } |
| |
| pub(crate) fn labels(&self) -> &HashMap<usize, HashMap<usize, usize>> { |
| &self.labels |
| } |
| } |