diff --git a/Cargo.lock b/Cargo.lock index 3f0fcfaff..12308a336 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2765,6 +2765,7 @@ dependencies = [ "thiserror", "wasm-bindgen", "wasm-bindgen-test", + "wasmer-artifact", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-compiler-llvm", @@ -2780,6 +2781,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasmer-artifact" +version = "2.2.1" +dependencies = [ + "enumset", + "loupe", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-bin-fuzz" version = "0.0.0" @@ -2858,6 +2870,7 @@ dependencies = [ "tempfile", "unix_mode", "wasmer", + "wasmer-artifact", "wasmer-cache", "wasmer-compiler", "wasmer-compiler-cranelift", @@ -2996,6 +3009,7 @@ dependencies = [ "serde_bytes", "target-lexicon 0.12.3", "thiserror", + "wasmer-artifact", "wasmer-compiler", "wasmer-types", "wasmer-vm", @@ -3010,6 +3024,7 @@ dependencies = [ "loupe", "serde", "serde_bytes", + "wasmer-artifact", "wasmer-compiler", "wasmer-engine", "wasmer-types", @@ -3031,6 +3046,7 @@ dependencies = [ "serde", "tempfile", "tracing", + "wasmer-artifact", "wasmer-compiler", "wasmer-engine", "wasmer-object", @@ -3052,6 +3068,7 @@ dependencies = [ "serde", "tempfile", "tracing", + "wasmer-artifact", "wasmer-compiler", "wasmer-engine", "wasmer-object", @@ -3070,6 +3087,7 @@ dependencies = [ "loupe", "region", "rkyv", + "wasmer-artifact", "wasmer-compiler", "wasmer-engine", "wasmer-types", @@ -3156,6 +3174,7 @@ dependencies = [ "scopeguard", "serde", "thiserror", + "wasmer-artifact", "wasmer-types", "winapi", ] diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 2427d99c3..10730f554 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -22,6 +22,7 @@ edition = "2018" # Shared dependencies. [dependencies] # - Mandatory shared dependencies. +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } indexmap = { version = "1.6", features = ["serde-1"] } cfg-if = "1.0" thiserror = "1.0" diff --git a/lib/artifact/Cargo.toml b/lib/artifact/Cargo.toml new file mode 100644 index 000000000..a57fcc8a3 --- /dev/null +++ b/lib/artifact/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "wasmer-artifact" +version = "2.2.1" +description = "Wasmer Artifact abstraction" +categories = ["wasm"] +keywords = ["wasm", "webassembly", "engine"] +authors = ["Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +license = "MIT OR Apache-2.0 WITH LLVM-exception " +readme = "README.md" +edition = "2018" + +[dependencies] +wasmer-types = { path = "../types", version = "=2.2.1" } +wasmer-compiler = { path = "../compiler", version = "=2.2.1" } +loupe = "0.1" +thiserror = "1.0" +enumset = "1.0" + +[badges] +maintenance = { status = "actively-developed" } + diff --git a/lib/artifact/README.md b/lib/artifact/README.md new file mode 100644 index 000000000..515aea657 --- /dev/null +++ b/lib/artifact/README.md @@ -0,0 +1,20 @@ +# `wasmer-artifact` [![Build Status](https://github.com/wasmerio/wasmer/workflows/build/badge.svg?style=flat-square)](https://github.com/wasmerio/wasmer/actions?query=workflow%3Abuild) [![Join Wasmer Slack](https://img.shields.io/static/v1?label=Slack&message=join%20chat&color=brighgreen&style=flat-square)](https://slack.wasmer.io) [![MIT License](https://img.shields.io/github/license/wasmerio/wasmer.svg?style=flat-square)](https://github.com/wasmerio/wasmer/blob/master/LICENSE) + + +This crate is the general abstraction for creating Artifacts in Wasmer. + +### Acknowledgments + +This project borrowed some of the code of the trap implementation from +the [`wasmtime-api`], the code since then has evolved significantly. + +Please check [Wasmer `ATTRIBUTIONS`] to further see licenses and other +attributions of the project. + + +[`wasmer-engine-universal`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal +[`wasmer-engine-dylib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-dylib +[`wasmer-engine-staticlib`]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-staticlib +[`wasmer-engine-dummy`]: https://github.com/wasmerio/wasmer/tree/master/tests/lib/engine-dummy +[`wasmtime-api`]: https://crates.io/crates/wasmtime +[Wasmer `ATTRIBUTIONS`]: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md diff --git a/lib/artifact/src/artifact.rs b/lib/artifact/src/artifact.rs new file mode 100644 index 000000000..ffa3933b7 --- /dev/null +++ b/lib/artifact/src/artifact.rs @@ -0,0 +1,166 @@ +use crate::{DeserializeError, SerializeError}; +use enumset::EnumSet; +use loupe::MemoryUsage; +use std::any::Any; +use std::convert::TryInto; +use std::path::Path; +use std::sync::Arc; +use std::{fs, mem}; +use wasmer_compiler::{CpuFeature, Features}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{ + MemoryIndex, MemoryStyle, ModuleInfo, OwnedDataInitializer, TableIndex, TableStyle, +}; + +/// An `Artifact` is the product that the `Engine` +/// implementation produce and use. +/// +/// The `Artifact` contains the compiled data for a given +/// module as well as extra information needed to run the +/// module at runtime, such as [`ModuleInfo`] and [`Features`]. +pub trait ArtifactCreate: Send + Sync + Upcastable + MemoryUsage { + /// Return a reference-counted pointer to the module + fn module(&self) -> Arc; + + /// Return a pointer to a module. + fn module_ref(&self) -> &ModuleInfo; + + /// Gets a mutable reference to the info. + /// + /// Note: this will return `None` if the module is already instantiated. + fn module_mut(&mut self) -> Option<&mut ModuleInfo>; + + /// Register thie `Artifact` stack frame information into the global scope. + /// + /// This is required to ensure that any traps can be properly symbolicated. + fn register_frame_info(&self); + + /// Returns the features for this Artifact + fn features(&self) -> &Features; + + /// Returns the CPU features for this Artifact + fn cpu_features(&self) -> EnumSet; + + /// Returns the memory styles associated with this `Artifact`. + fn memory_styles(&self) -> &PrimaryMap; + + /// Returns the table plans associated with this `Artifact`. + fn table_styles(&self) -> &PrimaryMap; + + /// Returns data initializers to pass to `InstanceHandle::initialize` + fn data_initializers(&self) -> &[OwnedDataInitializer]; + + /// Serializes an artifact into bytes + fn serialize(&self) -> Result, SerializeError>; + + /// Serializes an artifact into a file path + fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { + let serialized = self.serialize()?; + fs::write(&path, serialized)?; + Ok(()) + } +} + +// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 . +/// Trait needed to get downcasting of `Engine`s to work. +pub trait Upcastable { + /// upcast ref + fn upcast_any_ref(&'_ self) -> &'_ dyn Any; + /// upcast mut ref + fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any; + /// upcast boxed dyn + fn upcast_any_box(self: Box) -> Box; +} + +impl Upcastable for T { + #[inline] + fn upcast_any_ref(&'_ self) -> &'_ dyn Any { + self + } + #[inline] + fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any { + self + } + #[inline] + fn upcast_any_box(self: Box) -> Box { + self + } +} + +impl dyn ArtifactCreate + 'static { + /// Try to downcast the artifact into a given type. + #[inline] + pub fn downcast_ref(&'_ self) -> Option<&'_ T> { + self.upcast_any_ref().downcast_ref::() + } + + /// Try to downcast the artifact into a given type mutably. + #[inline] + pub fn downcast_mut(&'_ mut self) -> Option<&'_ mut T> { + self.upcast_any_mut().downcast_mut::() + } +} + +/// Metadata header which holds an ABI version and the length of the remaining +/// metadata. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct MetadataHeader { + magic: [u8; 8], + version: u32, + len: u32, +} + +impl MetadataHeader { + /// Current ABI version. Increment this any time breaking changes are made + /// to the format of the serialized data. + const CURRENT_VERSION: u32 = 1; + + /// Magic number to identify wasmer metadata. + const MAGIC: [u8; 8] = *b"WASMER\0\0"; + + /// Length of the metadata header. + pub const LEN: usize = 16; + + /// Alignment of the metadata. + pub const ALIGN: usize = 16; + + /// Creates a new header for metadata of the given length. + pub fn new(len: usize) -> [u8; 16] { + let header = MetadataHeader { + magic: Self::MAGIC, + version: Self::CURRENT_VERSION, + len: len.try_into().expect("metadata exceeds maximum length"), + }; + unsafe { mem::transmute(header) } + } + + /// Parses the header and returns the length of the metadata following it. + pub fn parse(bytes: &[u8]) -> Result { + if bytes.as_ptr() as usize % 16 != 0 { + return Err(DeserializeError::CorruptedBinary( + "misaligned metadata".to_string(), + )); + } + let bytes: [u8; 16] = bytes + .get(..16) + .ok_or_else(|| { + DeserializeError::CorruptedBinary("invalid metadata header".to_string()) + })? + .try_into() + .unwrap(); + let header: MetadataHeader = unsafe { mem::transmute(bytes) }; + if header.magic != Self::MAGIC { + return Err(DeserializeError::Incompatible( + "The provided bytes were not serialized by Wasmer".to_string(), + )); + } + if header.version != Self::CURRENT_VERSION { + return Err(DeserializeError::Incompatible( + "The provided bytes were serialized by an incompatible version of Wasmer" + .to_string(), + )); + } + Ok(header.len as usize) + } +} diff --git a/lib/artifact/src/error.rs b/lib/artifact/src/error.rs new file mode 100644 index 000000000..7fd6d7b6e --- /dev/null +++ b/lib/artifact/src/error.rs @@ -0,0 +1,66 @@ +//! The WebAssembly possible errors +use std::io; +use thiserror::Error; +use wasmer_compiler::CompileError; +use wasmer_types::ExternType; + +/// The Serialize error can occur when serializing a +/// compiled Module into a binary. +#[derive(Error, Debug)] +pub enum SerializeError { + /// An IO error + #[error(transparent)] + Io(#[from] io::Error), + /// A generic serialization error + #[error("{0}")] + Generic(String), +} + +/// The Deserialize error can occur when loading a +/// compiled Module from a binary. +#[derive(Error, Debug)] +pub enum DeserializeError { + /// An IO error + #[error(transparent)] + Io(#[from] io::Error), + /// A generic deserialization error + #[error("{0}")] + Generic(String), + /// Incompatible serialized binary + #[error("incompatible binary: {0}")] + Incompatible(String), + /// The provided binary is corrupted + #[error("corrupted binary: {0}")] + CorruptedBinary(String), + /// The binary was valid, but we got an error when + /// trying to allocate the required resources. + #[error(transparent)] + Compiler(CompileError), +} + +/// An ImportError. +/// +/// Note: this error is not standard to WebAssembly, but it's +/// useful to determine the import issue on the API side. +#[derive(Error, Debug)] +pub enum ImportError { + /// Incompatible Import Type. + /// This error occurs when the import types mismatch. + #[error("incompatible import type. Expected {0:?} but received {1:?}")] + IncompatibleType(ExternType, ExternType), + + /// Unknown Import. + /// This error occurs when an import was expected but not provided. + #[error("unknown import. Expected {0:?}")] + UnknownImport(ExternType), +} + +/// An error while preinstantiating a module. +/// +#[derive(Error, Debug)] +pub enum PreInstantiationError { + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[error("module compiled with CPU feature that is missing from host")] + CpuFeature(String), +} diff --git a/lib/artifact/src/funcbody.rs b/lib/artifact/src/funcbody.rs new file mode 100644 index 000000000..662623baa --- /dev/null +++ b/lib/artifact/src/funcbody.rs @@ -0,0 +1,17 @@ +/// A placeholder byte-sized type which is just used to provide some amount of type +/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's +/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes +/// around. +#[repr(C)] +pub struct VMFunctionBody(u8); + +#[cfg(test)] +mod test_vmfunction_body { + use super::VMFunctionBody; + use std::mem::size_of; + + #[test] + fn check_vmfunction_body_offsets() { + assert_eq!(size_of::(), 1); + } +} diff --git a/lib/artifact/src/lib.rs b/lib/artifact/src/lib.rs new file mode 100644 index 000000000..66f91a75b --- /dev/null +++ b/lib/artifact/src/lib.rs @@ -0,0 +1,55 @@ +//! Generic Artifact abstraction for Wasmer Engines. + +#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] +#![warn(unused_import_braces)] +#![cfg_attr( + feature = "cargo-clippy", + allow(clippy::new_without_default, clippy::new_without_default) +)] +#![cfg_attr( + feature = "cargo-clippy", + warn( + clippy::float_arithmetic, + clippy::mut_mut, + clippy::nonminimal_bool, + clippy::option_map_unwrap_or, + clippy::option_map_unwrap_or_else, + clippy::print_stdout, + clippy::unicode_not_nfc, + clippy::use_self + ) +)] + +mod artifact; +mod error; +mod funcbody; + +pub use crate::artifact::{ArtifactCreate, MetadataHeader, Upcastable}; +pub use crate::error::{DeserializeError, ImportError, PreInstantiationError, SerializeError}; +pub use crate::funcbody::VMFunctionBody; +use loupe::MemoryUsage; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + +/// A safe wrapper around `VMFunctionBody`. +#[derive(Clone, Copy, Debug, MemoryUsage)] +#[repr(transparent)] +pub struct FunctionBodyPtr(pub *const VMFunctionBody); + +impl std::ops::Deref for FunctionBodyPtr { + type Target = *const VMFunctionBody; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// # Safety +/// The VMFunctionBody that this points to is opaque, so there's no data to +/// read or write through this pointer. This is essentially a usize. +unsafe impl Send for FunctionBodyPtr {} +/// # Safety +/// The VMFunctionBody that this points to is opaque, so there's no data to +/// read or write through this pointer. This is essentially a usize. +unsafe impl Sync for FunctionBodyPtr {} diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index f149aaa64..efd72cd48 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -24,6 +24,7 @@ doc = false required-features = ["headless"] [dependencies] +wasmer-artifact = { version = "=2.2.1", path = "../artifact" } wasmer-compiler = { version = "=2.2.1", path = "../compiler" } wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true } wasmer-engine = { version = "=2.2.1", path = "../engine" } diff --git a/lib/engine-dylib/Cargo.toml b/lib/engine-dylib/Cargo.toml index da1219406..826c5d722 100644 --- a/lib/engine-dylib/Cargo.toml +++ b/lib/engine-dylib/Cargo.toml @@ -11,6 +11,7 @@ readme = "README.md" edition = "2018" [dependencies] +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } wasmer-types = { path = "../types", version = "=2.2.1" } wasmer-compiler = { path = "../compiler", version = "=2.2.1" } wasmer-vm = { path = "../vm", version = "=2.2.1", features = ["enable-rkyv"] } diff --git a/lib/engine-dylib/src/artifact.rs b/lib/engine-dylib/src/artifact.rs index 45ae04191..ad4977f1b 100644 --- a/lib/engine-dylib/src/artifact.rs +++ b/lib/engine-dylib/src/artifact.rs @@ -19,6 +19,7 @@ use tempfile::NamedTempFile; use tracing::log::error; #[cfg(feature = "compiler")] use tracing::trace; +use wasmer_artifact::ArtifactCreate; use wasmer_compiler::{ Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features, FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple, @@ -678,7 +679,7 @@ impl DylibArtifact { } } -impl Artifact for DylibArtifact { +impl ArtifactCreate for DylibArtifact { fn module(&self) -> Arc { self.metadata.compile_info.module.clone() } @@ -841,30 +842,6 @@ impl Artifact for DylibArtifact { &self.metadata.compile_info.table_styles } - fn finished_functions(&self) -> &BoxedSlice { - &self.finished_functions - } - - fn finished_function_call_trampolines(&self) -> &BoxedSlice { - &self.finished_function_call_trampolines - } - - fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice { - &self.finished_dynamic_function_trampolines - } - - fn signatures(&self) -> &BoxedSlice { - &self.signatures - } - - fn func_data_registry(&self) -> &FuncDataRegistry { - &self.func_data_registry - } - - fn preinstantiate(&self) -> Result<(), InstantiationError> { - Ok(()) - } - /// Serialize a `DylibArtifact`. fn serialize(&self) -> Result, SerializeError> { Ok(std::fs::read(&self.dylib_path)?) @@ -908,3 +885,28 @@ impl Artifact for DylibArtifact { Ok(()) } } +impl Artifact for DylibArtifact { + fn finished_functions(&self) -> &BoxedSlice { + &self.finished_functions + } + + fn finished_function_call_trampolines(&self) -> &BoxedSlice { + &self.finished_function_call_trampolines + } + + fn finished_dynamic_function_trampolines(&self) -> &BoxedSlice { + &self.finished_dynamic_function_trampolines + } + + fn signatures(&self) -> &BoxedSlice { + &self.signatures + } + + fn func_data_registry(&self) -> &FuncDataRegistry { + &self.func_data_registry + } + + fn preinstantiate(&self) -> Result<(), InstantiationError> { + Ok(()) + } +} diff --git a/lib/engine-staticlib/Cargo.toml b/lib/engine-staticlib/Cargo.toml index 531e143fd..b9c4182ac 100644 --- a/lib/engine-staticlib/Cargo.toml +++ b/lib/engine-staticlib/Cargo.toml @@ -11,6 +11,7 @@ readme = "README.md" edition = "2018" [dependencies] +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } wasmer-types = { path = "../types", version = "=2.2.1" } wasmer-compiler = { path = "../compiler", version = "=2.2.1" } wasmer-vm = { path = "../vm", version = "=2.2.1" } diff --git a/lib/engine-staticlib/src/artifact.rs b/lib/engine-staticlib/src/artifact.rs index 400a4c916..887e0cd74 100644 --- a/lib/engine-staticlib/src/artifact.rs +++ b/lib/engine-staticlib/src/artifact.rs @@ -9,6 +9,7 @@ use std::collections::BTreeMap; use std::error::Error; use std::mem; use std::sync::Arc; +use wasmer_artifact::ArtifactCreate; use wasmer_compiler::{ CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple, }; @@ -436,7 +437,7 @@ impl StaticlibArtifact { } } -impl Artifact for StaticlibArtifact { +impl ArtifactCreate for StaticlibArtifact { fn module(&self) -> Arc { self.metadata.compile_info.module.clone() } @@ -473,6 +474,12 @@ impl Artifact for StaticlibArtifact { &self.metadata.compile_info.table_styles } + /// Serialize a StaticlibArtifact + fn serialize(&self) -> Result, SerializeError> { + Ok(self.module_bytes.clone()) + } +} +impl Artifact for StaticlibArtifact { fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } @@ -492,7 +499,6 @@ impl Artifact for StaticlibArtifact { fn func_data_registry(&self) -> &FuncDataRegistry { &self.func_data_registry } - fn preinstantiate(&self) -> Result<(), InstantiationError> { if self.is_compiled { panic!( @@ -502,9 +508,4 @@ impl Artifact for StaticlibArtifact { } Ok(()) } - - /// Serialize a StaticlibArtifact - fn serialize(&self) -> Result, SerializeError> { - Ok(self.module_bytes.clone()) - } } diff --git a/lib/engine-universal/Cargo.toml b/lib/engine-universal/Cargo.toml index 2b4134104..b9ee48139 100644 --- a/lib/engine-universal/Cargo.toml +++ b/lib/engine-universal/Cargo.toml @@ -11,6 +11,7 @@ readme = "README.md" edition = "2018" [dependencies] +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } wasmer-types = { path = "../types", version = "=2.2.1", features = [ "enable-rkyv", ] } @@ -21,7 +22,6 @@ wasmer-compiler = { path = "../compiler", version = "=2.2.1", features = [ wasmer-vm = { path = "../vm", version = "=2.2.1", features = ["enable-rkyv"] } wasmer-engine = { path = "../engine", version = "=2.2.1" } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } -region = { version = "3.0" } cfg-if = "1.0" leb128 = "0.2" rkyv = "0.7.20" @@ -29,6 +29,9 @@ loupe = "0.1" enumset = "1.0" enum-iterator = "0.7.0" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +region = { version = "3.0" } + [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winnt", "impl-default"] } diff --git a/lib/engine-universal/src/artifact.rs b/lib/engine-universal/src/artifact.rs index 672eb1a76..64a69d360 100644 --- a/lib/engine-universal/src/artifact.rs +++ b/lib/engine-universal/src/artifact.rs @@ -11,6 +11,7 @@ use enumset::EnumSet; use loupe::MemoryUsage; use std::mem; use std::sync::{Arc, Mutex}; +use wasmer_artifact::ArtifactCreate; use wasmer_compiler::{CompileError, CpuFeature, Features, Triple}; #[cfg(feature = "compiler")] use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain}; @@ -267,7 +268,7 @@ impl UniversalArtifact { } } -impl Artifact for UniversalArtifact { +impl ArtifactCreate for UniversalArtifact { fn module(&self) -> Arc { self.serializable.compile_info.module.clone() } @@ -324,6 +325,19 @@ impl Artifact for UniversalArtifact { &self.serializable.compile_info.table_styles } + fn serialize(&self) -> Result, SerializeError> { + let serialized_data = self.serializable.serialize()?; + assert!(mem::align_of::() <= MetadataHeader::ALIGN); + + let mut metadata_binary = vec![]; + metadata_binary.extend(Self::MAGIC_HEADER); + metadata_binary.extend(MetadataHeader::new(serialized_data.len())); + metadata_binary.extend(serialized_data); + Ok(metadata_binary) + } +} + +impl Artifact for UniversalArtifact { fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } @@ -343,14 +357,4 @@ impl Artifact for UniversalArtifact { fn func_data_registry(&self) -> &FuncDataRegistry { &self.func_data_registry } - fn serialize(&self) -> Result, SerializeError> { - let serialized_data = self.serializable.serialize()?; - assert!(mem::align_of::() <= MetadataHeader::ALIGN); - - let mut metadata_binary = vec![]; - metadata_binary.extend(Self::MAGIC_HEADER); - metadata_binary.extend(MetadataHeader::new(serialized_data.len())); - metadata_binary.extend(serialized_data); - Ok(metadata_binary) - } } diff --git a/lib/engine/Cargo.toml b/lib/engine/Cargo.toml index e7015e911..f26837585 100644 --- a/lib/engine/Cargo.toml +++ b/lib/engine/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] wasmer-types = { path = "../types", version = "=2.2.1" } wasmer-compiler = { path = "../compiler", version = "=2.2.1" } +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } target-lexicon = { version = "0.12.2", default-features = false } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } backtrace = "0.3" diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index 9b29f43fb..cee7be340 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -1,63 +1,26 @@ -use crate::{ - resolve_imports, DeserializeError, InstantiationError, Resolver, RuntimeError, SerializeError, - Tunables, -}; -use enumset::EnumSet; +use crate::{resolve_imports, InstantiationError, Resolver, RuntimeError, Tunables}; use loupe::MemoryUsage; use std::any::Any; -use std::convert::TryInto; -use std::path::Path; -use std::sync::Arc; -use std::{fs, mem}; -use wasmer_compiler::{CpuFeature, Features}; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; -use wasmer_types::{ - DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, - OwnedDataInitializer, SignatureIndex, TableIndex, -}; +pub use wasmer_artifact::MetadataHeader; +use wasmer_artifact::{ArtifactCreate, Upcastable}; +use wasmer_compiler::CpuFeature; +use wasmer_types::entity::BoxedSlice; +use wasmer_types::{DataInitializer, FunctionIndex, LocalFunctionIndex, SignatureIndex}; use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, TableStyle, - TrapHandler, VMSharedSignatureIndex, VMTrampoline, + FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, TrapHandler, + VMSharedSignatureIndex, VMTrampoline, }; /// An `Artifact` is the product that the `Engine` /// implementation produce and use. /// -/// The `Artifact` contains the compiled data for a given -/// module as well as extra information needed to run the + +/// An `Artifact` is the product that the `Engine` +/// implementation produce and use. +/// +/// The `ArtifactRun` contains the extra information needed to run the /// module at runtime, such as [`ModuleInfo`] and [`Features`]. -pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { - /// Return a reference-counted pointer to the module - fn module(&self) -> Arc; - - /// Return a pointer to a module. - fn module_ref(&self) -> &ModuleInfo; - - /// Gets a mutable reference to the info. - /// - /// Note: this will return `None` if the module is already instantiated. - fn module_mut(&mut self) -> Option<&mut ModuleInfo>; - - /// Register thie `Artifact` stack frame information into the global scope. - /// - /// This is required to ensure that any traps can be properly symbolicated. - fn register_frame_info(&self); - - /// Returns the features for this Artifact - fn features(&self) -> &Features; - - /// Returns the CPU features for this Artifact - fn cpu_features(&self) -> EnumSet; - - /// Returns the memory styles associated with this `Artifact`. - fn memory_styles(&self) -> &PrimaryMap; - - /// Returns the table plans associated with this `Artifact`. - fn table_styles(&self) -> &PrimaryMap; - - /// Returns data initializers to pass to `InstanceHandle::initialize` - fn data_initializers(&self) -> &[OwnedDataInitializer]; - +pub trait Artifact: Send + Sync + Upcastable + MemoryUsage + ArtifactCreate { /// Returns the functions allocated in memory or this `Artifact` /// ready to be run. fn finished_functions(&self) -> &BoxedSlice; @@ -76,21 +39,10 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { /// Get the func data registry fn func_data_registry(&self) -> &FuncDataRegistry; - /// Serializes an artifact into bytes - fn serialize(&self) -> Result, SerializeError>; - - /// Serializes an artifact into a file path - fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { - let serialized = self.serialize()?; - fs::write(&path, serialized)?; - Ok(()) - } - /// Do preinstantiation logic that is executed before instantiating fn preinstantiate(&self) -> Result<(), InstantiationError> { Ok(()) } - /// Crate an `Instance` from this `Artifact`. /// /// # Safety @@ -168,7 +120,6 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { .map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))?; Ok(handle) } - /// Finishes the instantiation of a just created `InstanceHandle`. /// /// # Safety @@ -193,29 +144,6 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { } } -// Implementation of `Upcastable` taken from https://users.rust-lang.org/t/why-does-downcasting-not-work-for-subtraits/33286/7 . -/// Trait needed to get downcasting of `Engine`s to work. -pub trait Upcastable { - fn upcast_any_ref(&'_ self) -> &'_ dyn Any; - fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any; - fn upcast_any_box(self: Box) -> Box; -} - -impl Upcastable for T { - #[inline] - fn upcast_any_ref(&'_ self) -> &'_ dyn Any { - self - } - #[inline] - fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any { - self - } - #[inline] - fn upcast_any_box(self: Box) -> Box { - self - } -} - impl dyn Artifact + 'static { /// Try to downcast the artifact into a given type. #[inline] @@ -229,67 +157,3 @@ impl dyn Artifact + 'static { self.upcast_any_mut().downcast_mut::() } } - -/// Metadata header which holds an ABI version and the length of the remaining -/// metadata. -#[repr(C)] -#[derive(Clone, Copy)] -pub struct MetadataHeader { - magic: [u8; 8], - version: u32, - len: u32, -} - -impl MetadataHeader { - /// Current ABI version. Increment this any time breaking changes are made - /// to the format of the serialized data. - const CURRENT_VERSION: u32 = 1; - - /// Magic number to identify wasmer metadata. - const MAGIC: [u8; 8] = *b"WASMER\0\0"; - - /// Length of the metadata header. - pub const LEN: usize = 16; - - /// Alignment of the metadata. - pub const ALIGN: usize = 16; - - /// Creates a new header for metadata of the given length. - pub fn new(len: usize) -> [u8; 16] { - let header = MetadataHeader { - magic: Self::MAGIC, - version: Self::CURRENT_VERSION, - len: len.try_into().expect("metadata exceeds maximum length"), - }; - unsafe { mem::transmute(header) } - } - - /// Parses the header and returns the length of the metadata following it. - pub fn parse(bytes: &[u8]) -> Result { - if bytes.as_ptr() as usize % 16 != 0 { - return Err(DeserializeError::CorruptedBinary( - "misaligned metadata".to_string(), - )); - } - let bytes: [u8; 16] = bytes - .get(..16) - .ok_or_else(|| { - DeserializeError::CorruptedBinary("invalid metadata header".to_string()) - })? - .try_into() - .unwrap(); - let header: MetadataHeader = unsafe { mem::transmute(bytes) }; - if header.magic != Self::MAGIC { - return Err(DeserializeError::Incompatible( - "The provided bytes were not serialized by Wasmer".to_string(), - )); - } - if header.version != Self::CURRENT_VERSION { - return Err(DeserializeError::Incompatible( - "The provided bytes were serialized by an incompatible version of Wasmer" - .to_string(), - )); - } - Ok(header.len as usize) - } -} diff --git a/lib/engine/src/error.rs b/lib/engine/src/error.rs index 5874e5d8b..fc377849c 100644 --- a/lib/engine/src/error.rs +++ b/lib/engine/src/error.rs @@ -1,60 +1,7 @@ //! The WebAssembly possible errors use crate::trap::RuntimeError; -use std::io; use thiserror::Error; -use wasmer_compiler::CompileError; -use wasmer_types::ExternType; - -/// The Serialize error can occur when serializing a -/// compiled Module into a binary. -#[derive(Error, Debug)] -pub enum SerializeError { - /// An IO error - #[error(transparent)] - Io(#[from] io::Error), - /// A generic serialization error - #[error("{0}")] - Generic(String), -} - -/// The Deserialize error can occur when loading a -/// compiled Module from a binary. -#[derive(Error, Debug)] -pub enum DeserializeError { - /// An IO error - #[error(transparent)] - Io(#[from] io::Error), - /// A generic deserialization error - #[error("{0}")] - Generic(String), - /// Incompatible serialized binary - #[error("incompatible binary: {0}")] - Incompatible(String), - /// The provided binary is corrupted - #[error("corrupted binary: {0}")] - CorruptedBinary(String), - /// The binary was valid, but we got an error when - /// trying to allocate the required resources. - #[error(transparent)] - Compiler(CompileError), -} - -/// An ImportError. -/// -/// Note: this error is not standard to WebAssembly, but it's -/// useful to determine the import issue on the API side. -#[derive(Error, Debug)] -pub enum ImportError { - /// Incompatible Import Type. - /// This error occurs when the import types mismatch. - #[error("incompatible import type. Expected {0:?} but received {1:?}")] - IncompatibleType(ExternType, ExternType), - - /// Unknown Import. - /// This error occurs when an import was expected but not provided. - #[error("unknown import. Expected {0:?}")] - UnknownImport(ExternType), -} +pub use wasmer_artifact::{DeserializeError, ImportError, SerializeError}; /// The WebAssembly.LinkError object indicates an error during /// module instantiation (besides traps from the start function). diff --git a/lib/engine/src/lib.rs b/lib/engine/src/lib.rs index f364077f5..35737b8d0 100644 --- a/lib/engine/src/lib.rs +++ b/lib/engine/src/lib.rs @@ -31,11 +31,9 @@ mod resolver; mod trap; mod tunables; -pub use crate::artifact::{Artifact, MetadataHeader}; +pub use crate::artifact::Artifact; pub use crate::engine::{Engine, EngineId}; -pub use crate::error::{ - DeserializeError, ImportError, InstantiationError, LinkError, SerializeError, -}; +pub use crate::error::{InstantiationError, LinkError}; pub use crate::export::{Export, ExportFunction, ExportFunctionMetadata}; pub use crate::resolver::{ resolve_imports, ChainableNamedResolver, NamedResolver, NamedResolverChain, NullResolver, @@ -43,6 +41,8 @@ pub use crate::resolver::{ }; pub use crate::trap::*; pub use crate::tunables::Tunables; +pub use wasmer_artifact::{ArtifactCreate, MetadataHeader}; +pub use wasmer_artifact::{DeserializeError, ImportError, SerializeError}; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/types/src/libcalls.rs b/lib/types/src/libcalls.rs index 40be5dfd6..15fc8c9db 100644 --- a/lib/types/src/libcalls.rs +++ b/lib/types/src/libcalls.rs @@ -1,162 +1,162 @@ -use enum_iterator::IntoEnumIterator; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use serde::{Deserialize, Serialize}; -use std::fmt; - -/// The name of a runtime library routine. -/// -/// This list is likely to grow over time. -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -#[derive( - Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, MemoryUsage, IntoEnumIterator, -)] -pub enum LibCall { - /// ceil.f32 - CeilF32, - - /// ceil.f64 - CeilF64, - - /// floor.f32 - FloorF32, - - /// floor.f64 - FloorF64, - - /// nearest.f32 - NearestF32, - - /// nearest.f64 - NearestF64, - - /// trunc.f32 - TruncF32, - - /// trunc.f64 - TruncF64, - - /// memory.size for local functions - Memory32Size, - - /// memory.size for imported functions - ImportedMemory32Size, - - /// table.copy - TableCopy, - - /// table.init - TableInit, - - /// table.fill - TableFill, - - /// table.size for local tables - TableSize, - - /// table.size for imported tables - ImportedTableSize, - - /// table.get for local tables - TableGet, - - /// table.get for imported tables - ImportedTableGet, - - /// table.set for local tables - TableSet, - - /// table.set for imported tables - ImportedTableSet, - - /// table.grow for local tables - TableGrow, - - /// table.grow for imported tables - ImportedTableGrow, - - /// ref.func - FuncRef, - - /// elem.drop - ElemDrop, - - /// memory.copy for local memories - Memory32Copy, - - /// memory.copy for imported memories - ImportedMemory32Copy, - - /// memory.fill for local memories - Memory32Fill, - - /// memory.fill for imported memories - ImportedMemory32Fill, - - /// memory.init - Memory32Init, - - /// data.drop - DataDrop, - - /// A custom trap - RaiseTrap, - - /// probe for stack overflow. These are emitted for functions which need - /// when the `enable_probestack` setting is true. - Probestack, -} - -impl LibCall { - /// Return the function name associated to the libcall. - pub fn to_function_name(&self) -> &str { - match self { - Self::CeilF32 => "wasmer_vm_f32_ceil", - Self::CeilF64 => "wasmer_vm_f64_ceil", - Self::FloorF32 => "wasmer_vm_f32_floor", - Self::FloorF64 => "wasmer_vm_f64_floor", - Self::NearestF32 => "wasmer_vm_f32_nearest", - Self::NearestF64 => "wasmer_vm_f64_nearest", - Self::TruncF32 => "wasmer_vm_f32_trunc", - Self::TruncF64 => "wasmer_vm_f64_trunc", - Self::Memory32Size => "wasmer_vm_memory32_size", - Self::ImportedMemory32Size => "wasmer_vm_imported_memory32_size", - Self::TableCopy => "wasmer_vm_table_copy", - Self::TableInit => "wasmer_vm_table_init", - Self::TableFill => "wasmer_vm_table_fill", - Self::TableSize => "wasmer_vm_table_size", - Self::ImportedTableSize => "wasmer_vm_imported_table_size", - Self::TableGet => "wasmer_vm_table_get", - Self::ImportedTableGet => "wasmer_vm_imported_table_get", - Self::TableSet => "wasmer_vm_table_set", - Self::ImportedTableSet => "wasmer_vm_imported_table_set", - Self::TableGrow => "wasmer_vm_table_grow", - Self::ImportedTableGrow => "wasmer_vm_imported_table_grow", - Self::FuncRef => "wasmer_vm_func_ref", - Self::ElemDrop => "wasmer_vm_elem_drop", - Self::Memory32Copy => "wasmer_vm_memory32_copy", - Self::ImportedMemory32Copy => "wasmer_vm_imported_memory32_copy", - Self::Memory32Fill => "wasmer_vm_memory32_fill", - Self::ImportedMemory32Fill => "wasmer_vm_imported_memory32_fill", - Self::Memory32Init => "wasmer_vm_memory32_init", - Self::DataDrop => "wasmer_vm_data_drop", - Self::RaiseTrap => "wasmer_vm_raise_trap", - // We have to do this because macOS requires a leading `_` and it's not - // a normal function, it's a static variable, so we have to do it manually. - #[cfg(target_vendor = "apple")] - Self::Probestack => "_wasmer_vm_probestack", - #[cfg(not(target_vendor = "apple"))] - Self::Probestack => "wasmer_vm_probestack", - } - } -} - -impl fmt::Display for LibCall { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} +use enum_iterator::IntoEnumIterator; +use loupe::MemoryUsage; +#[cfg(feature = "enable-rkyv")] +use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +#[cfg(feature = "enable-serde")] +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// The name of a runtime library routine. +/// +/// This list is likely to grow over time. +#[cfg_attr( + feature = "enable-rkyv", + derive(RkyvSerialize, RkyvDeserialize, Archive) +)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, MemoryUsage, IntoEnumIterator)] +pub enum LibCall { + /// ceil.f32 + CeilF32, + + /// ceil.f64 + CeilF64, + + /// floor.f32 + FloorF32, + + /// floor.f64 + FloorF64, + + /// nearest.f32 + NearestF32, + + /// nearest.f64 + NearestF64, + + /// trunc.f32 + TruncF32, + + /// trunc.f64 + TruncF64, + + /// memory.size for local functions + Memory32Size, + + /// memory.size for imported functions + ImportedMemory32Size, + + /// table.copy + TableCopy, + + /// table.init + TableInit, + + /// table.fill + TableFill, + + /// table.size for local tables + TableSize, + + /// table.size for imported tables + ImportedTableSize, + + /// table.get for local tables + TableGet, + + /// table.get for imported tables + ImportedTableGet, + + /// table.set for local tables + TableSet, + + /// table.set for imported tables + ImportedTableSet, + + /// table.grow for local tables + TableGrow, + + /// table.grow for imported tables + ImportedTableGrow, + + /// ref.func + FuncRef, + + /// elem.drop + ElemDrop, + + /// memory.copy for local memories + Memory32Copy, + + /// memory.copy for imported memories + ImportedMemory32Copy, + + /// memory.fill for local memories + Memory32Fill, + + /// memory.fill for imported memories + ImportedMemory32Fill, + + /// memory.init + Memory32Init, + + /// data.drop + DataDrop, + + /// A custom trap + RaiseTrap, + + /// probe for stack overflow. These are emitted for functions which need + /// when the `enable_probestack` setting is true. + Probestack, +} + +impl LibCall { + /// Return the function name associated to the libcall. + pub fn to_function_name(&self) -> &str { + match self { + Self::CeilF32 => "wasmer_vm_f32_ceil", + Self::CeilF64 => "wasmer_vm_f64_ceil", + Self::FloorF32 => "wasmer_vm_f32_floor", + Self::FloorF64 => "wasmer_vm_f64_floor", + Self::NearestF32 => "wasmer_vm_f32_nearest", + Self::NearestF64 => "wasmer_vm_f64_nearest", + Self::TruncF32 => "wasmer_vm_f32_trunc", + Self::TruncF64 => "wasmer_vm_f64_trunc", + Self::Memory32Size => "wasmer_vm_memory32_size", + Self::ImportedMemory32Size => "wasmer_vm_imported_memory32_size", + Self::TableCopy => "wasmer_vm_table_copy", + Self::TableInit => "wasmer_vm_table_init", + Self::TableFill => "wasmer_vm_table_fill", + Self::TableSize => "wasmer_vm_table_size", + Self::ImportedTableSize => "wasmer_vm_imported_table_size", + Self::TableGet => "wasmer_vm_table_get", + Self::ImportedTableGet => "wasmer_vm_imported_table_get", + Self::TableSet => "wasmer_vm_table_set", + Self::ImportedTableSet => "wasmer_vm_imported_table_set", + Self::TableGrow => "wasmer_vm_table_grow", + Self::ImportedTableGrow => "wasmer_vm_imported_table_grow", + Self::FuncRef => "wasmer_vm_func_ref", + Self::ElemDrop => "wasmer_vm_elem_drop", + Self::Memory32Copy => "wasmer_vm_memory32_copy", + Self::ImportedMemory32Copy => "wasmer_vm_imported_memory32_copy", + Self::Memory32Fill => "wasmer_vm_memory32_fill", + Self::ImportedMemory32Fill => "wasmer_vm_imported_memory32_fill", + Self::Memory32Init => "wasmer_vm_memory32_init", + Self::DataDrop => "wasmer_vm_data_drop", + Self::RaiseTrap => "wasmer_vm_raise_trap", + // We have to do this because macOS requires a leading `_` and it's not + // a normal function, it's a static variable, so we have to do it manually. + #[cfg(target_vendor = "apple")] + Self::Probestack => "_wasmer_vm_probestack", + #[cfg(not(target_vendor = "apple"))] + Self::Probestack => "wasmer_vm_probestack", + } + } +} + +impl fmt::Display for LibCall { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 3384492c4..4ad12c05a 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -1,87 +1,89 @@ -use crate::Pages; -use loupe::MemoryUsage; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -/// Error type describing things that can go wrong when operating on Wasm Memories. -#[derive(Error, Debug, Clone, PartialEq, Hash)] -pub enum MemoryError { - /// Low level error with mmap. - #[error("Error when allocating memory: {0}")] - Region(String), - /// The operation would cause the size of the memory to exceed the maximum or would cause - /// an overflow leading to unindexable memory. - #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] - CouldNotGrow { - /// The current size in pages. - current: Pages, - /// The attempted amount to grow by in pages. - attempted_delta: Pages, - }, - /// The operation would cause the size of the memory size exceed the maximum. - #[error("The memory is invalid because {}", reason)] - InvalidMemory { - /// The reason why the provided memory is invalid. - reason: String, - }, - /// Caller asked for more minimum memory than we can give them. - #[error("The minimum requested ({} pages) memory is greater than the maximum allowed memory ({} pages)", min_requested.0, max_allowed.0)] - MinimumMemoryTooLarge { - /// The number of pages requested as the minimum amount of memory. - min_requested: Pages, - /// The maximum amount of memory we can allocate. - max_allowed: Pages, - }, - /// Caller asked for a maximum memory greater than we can give them. - #[error("The maximum requested memory ({} pages) is greater than the maximum allowed memory ({} pages)", max_requested.0, max_allowed.0)] - MaximumMemoryTooLarge { - /// The number of pages requested as the maximum amount of memory. - max_requested: Pages, - /// The number of pages requested as the maximum amount of memory. - max_allowed: Pages, - }, - /// A user defined error value, used for error cases not listed above. - #[error("A user-defined error occurred: {0}")] - Generic(String), -} - -/// Implementation styles for WebAssembly linear memory. -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -pub enum MemoryStyle { - /// The actual memory can be resized and moved. - Dynamic { - /// Our chosen offset-guard size. - /// - /// It represents the size in bytes of extra guard pages after the end - /// to optimize loads and stores with constant offsets. - offset_guard_size: u64, - }, - /// Address space is allocated up front. - Static { - /// The number of mapped and unmapped pages. - bound: Pages, - /// Our chosen offset-guard size. - /// - /// It represents the size in bytes of extra guard pages after the end - /// to optimize loads and stores with constant offsets. - offset_guard_size: u64, - }, -} - -impl MemoryStyle { - /// Returns the offset-guard size - pub fn offset_guard_size(&self) -> u64 { - match self { - Self::Dynamic { offset_guard_size } => *offset_guard_size, - Self::Static { - offset_guard_size, .. - } => *offset_guard_size, - } - } -} +use crate::Pages; +use loupe::MemoryUsage; +#[cfg(feature = "enable-rkyv")] +use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +#[cfg(feature = "enable-serde")] +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +/// Error type describing things that can go wrong when operating on Wasm Memories. +#[derive(Error, Debug, Clone, PartialEq, Hash)] +pub enum MemoryError { + /// Low level error with mmap. + #[error("Error when allocating memory: {0}")] + Region(String), + /// The operation would cause the size of the memory to exceed the maximum or would cause + /// an overflow leading to unindexable memory. + #[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)] + CouldNotGrow { + /// The current size in pages. + current: Pages, + /// The attempted amount to grow by in pages. + attempted_delta: Pages, + }, + /// The operation would cause the size of the memory size exceed the maximum. + #[error("The memory is invalid because {}", reason)] + InvalidMemory { + /// The reason why the provided memory is invalid. + reason: String, + }, + /// Caller asked for more minimum memory than we can give them. + #[error("The minimum requested ({} pages) memory is greater than the maximum allowed memory ({} pages)", min_requested.0, max_allowed.0)] + MinimumMemoryTooLarge { + /// The number of pages requested as the minimum amount of memory. + min_requested: Pages, + /// The maximum amount of memory we can allocate. + max_allowed: Pages, + }, + /// Caller asked for a maximum memory greater than we can give them. + #[error("The maximum requested memory ({} pages) is greater than the maximum allowed memory ({} pages)", max_requested.0, max_allowed.0)] + MaximumMemoryTooLarge { + /// The number of pages requested as the maximum amount of memory. + max_requested: Pages, + /// The number of pages requested as the maximum amount of memory. + max_allowed: Pages, + }, + /// A user defined error value, used for error cases not listed above. + #[error("A user-defined error occurred: {0}")] + Generic(String), +} + +/// Implementation styles for WebAssembly linear memory. +#[derive(Debug, Clone, PartialEq, Eq, Hash, MemoryUsage)] +#[cfg_attr( + feature = "enable-rkyv", + derive(RkyvSerialize, RkyvDeserialize, Archive) +)] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +pub enum MemoryStyle { + /// The actual memory can be resized and moved. + Dynamic { + /// Our chosen offset-guard size. + /// + /// It represents the size in bytes of extra guard pages after the end + /// to optimize loads and stores with constant offsets. + offset_guard_size: u64, + }, + /// Address space is allocated up front. + Static { + /// The number of mapped and unmapped pages. + bound: Pages, + /// Our chosen offset-guard size. + /// + /// It represents the size in bytes of extra guard pages after the end + /// to optimize loads and stores with constant offsets. + offset_guard_size: u64, + }, +} + +impl MemoryStyle { + /// Returns the offset-guard size + pub fn offset_guard_size(&self) -> u64 { + match self { + Self::Dynamic { offset_guard_size } => *offset_guard_size, + Self::Static { + offset_guard_size, .. + } => *offset_guard_size, + } + } +} diff --git a/lib/types/src/table.rs b/lib/types/src/table.rs index c15a9aca0..345649eed 100644 --- a/lib/types/src/table.rs +++ b/lib/types/src/table.rs @@ -1,14 +1,16 @@ use loupe::MemoryUsage; #[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +#[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; /// Implementation styles for WebAssembly tables. -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, MemoryUsage)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, MemoryUsage)] #[cfg_attr( feature = "enable-rkyv", derive(RkyvSerialize, RkyvDeserialize, Archive) )] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub enum TableStyle { /// Signatures are stored in the table and checked in the caller. CallerChecksSignature, diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index c5dd6bd64..46812ce35 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -9,6 +9,7 @@ use core::str::FromStr; use loupe::MemoryUsage; #[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +#[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use std::error::Error; use thiserror::Error; @@ -16,11 +17,12 @@ use thiserror::Error; /// A trap code describing the reason for a trap. /// /// All trap instructions have an explicit trap code. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Error, MemoryUsage)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Error, MemoryUsage)] #[cfg_attr( feature = "enable-rkyv", derive(RkyvSerialize, RkyvDeserialize, Archive) )] +#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[repr(u32)] pub enum TrapCode { /// The current stack space was exhausted. diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 93bc58558..7c9965835 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" [dependencies] wasmer-types = { path = "../types", version = "=2.2.1" } +wasmer-artifact = { path = "../artifact", version = "=2.2.1" } libc = { version = "^0.2", default-features = false } memoffset = "0.6" indexmap = { version = "1.6", features = ["serde-1"] } diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index fe9bd0032..1f3877e8b 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -50,12 +50,11 @@ pub use crate::sig_registry::SignatureRegistry; pub use crate::table::{LinearTable, Table, TableElement}; pub use crate::trap::*; pub use crate::vmcontext::{ - VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, - VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, - VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, - VMTrampoline, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionEnvironment, + VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, + VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, }; -use loupe::MemoryUsage; +pub use wasmer_artifact::{FunctionBodyPtr, VMFunctionBody}; pub use wasmer_types::LibCall; pub use wasmer_types::TableStyle; #[deprecated( @@ -71,28 +70,6 @@ pub use wasmer_types::{ /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); -/// A safe wrapper around `VMFunctionBody`. -#[derive(Clone, Copy, Debug, MemoryUsage)] -#[repr(transparent)] -pub struct FunctionBodyPtr(pub *const VMFunctionBody); - -impl std::ops::Deref for FunctionBodyPtr { - type Target = *const VMFunctionBody; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// # Safety -/// The VMFunctionBody that this points to is opaque, so there's no data to -/// read or write through this pointer. This is essentially a usize. -unsafe impl Send for FunctionBodyPtr {} -/// # Safety -/// The VMFunctionBody that this points to is opaque, so there's no data to -/// read or write through this pointer. This is essentially a usize. -unsafe impl Sync for FunctionBodyPtr {} - /// Pointers to section data. #[derive(Clone, Copy, Debug)] #[repr(transparent)] diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index c7e39e979..a4d1b3a43 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -20,6 +20,7 @@ use std::mem; use std::ptr::{self, NonNull}; use std::sync::Arc; use std::u32; +pub use wasmer_artifact::VMFunctionBody; /// Union representing the first parameter passed when calling a function. /// @@ -169,24 +170,6 @@ mod test_vmdynamicfunction_import_context { } } -/// A placeholder byte-sized type which is just used to provide some amount of type -/// safety when dealing with pointers to JIT-compiled function bodies. Note that it's -/// deliberately not Copy, as we shouldn't be carelessly copying function body bytes -/// around. -#[repr(C)] -pub struct VMFunctionBody(u8); - -#[cfg(test)] -mod test_vmfunction_body { - use super::VMFunctionBody; - use std::mem::size_of; - - #[test] - fn check_vmfunction_body_offsets() { - assert_eq!(size_of::(), 1); - } -} - /// A function kind is a calling convention into and out of wasm code. #[derive(Debug, Copy, Clone, PartialEq, MemoryUsage)] #[repr(C)] @@ -799,9 +782,8 @@ pub struct VMSharedSignatureIndex(u32); #[cfg(test)] mod test_vmshared_signature_index { use super::VMSharedSignatureIndex; - use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; use std::mem::size_of; - use wasmer_types::ModuleInfo; + use wasmer_types::{ModuleInfo, TargetSharedSignatureIndex, VMOffsets}; #[test] fn check_vmshared_signature_index() { diff --git a/tests/lib/engine-dummy/Cargo.toml b/tests/lib/engine-dummy/Cargo.toml index 61b421124..a88e48c3f 100644 --- a/tests/lib/engine-dummy/Cargo.toml +++ b/tests/lib/engine-dummy/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" publish = false [dependencies] +wasmer-artifact = { path = "../../../lib/artifact", version = "=2.2.1" } wasmer-types = { path = "../../../lib/types", version = "=2.2.1" } wasmer-compiler = { path = "../../../lib/compiler", version = "=2.2.1" } wasmer-vm = { path = "../../../lib/vm", version = "=2.2.1" } diff --git a/tests/lib/engine-dummy/src/artifact.rs b/tests/lib/engine-dummy/src/artifact.rs index 9b7523afb..0658c17c9 100644 --- a/tests/lib/engine-dummy/src/artifact.rs +++ b/tests/lib/engine-dummy/src/artifact.rs @@ -7,6 +7,7 @@ use loupe::MemoryUsage; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use std::sync::Arc; +use wasmer_artifact::ArtifactCreate; #[cfg(feature = "compiler")] use wasmer_compiler::ModuleEnvironment; use wasmer_compiler::{CompileError, CpuFeature}; @@ -193,7 +194,7 @@ impl DummyArtifact { } } -impl Artifact for DummyArtifact { +impl ArtifactCreate for DummyArtifact { fn module(&self) -> Arc { self.metadata.module.clone() } @@ -229,7 +230,25 @@ impl Artifact for DummyArtifact { fn table_styles(&self) -> &PrimaryMap { &self.metadata.table_styles } + #[cfg(feature = "serialize")] + fn serialize(&self) -> Result, SerializeError> { + let bytes = bincode::serialize(&self.metadata) + .map_err(|e| SerializeError::Generic(format!("{:?}", e)))?; + // Prepend the header. + let mut serialized = Self::MAGIC_HEADER.to_vec(); + serialized.extend(bytes); + Ok(serialized) + } + + #[cfg(not(feature = "serialize"))] + fn serialize(&self) -> Result, SerializeError> { + Err(SerializeError::Generic( + "The serializer feature is not enabled in the DummyEngine", + )) + } +} +impl Artifact for DummyArtifact { fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } @@ -249,22 +268,4 @@ impl Artifact for DummyArtifact { fn func_data_registry(&self) -> &FuncDataRegistry { &self.func_data_registry } - - #[cfg(feature = "serialize")] - fn serialize(&self) -> Result, SerializeError> { - let bytes = bincode::serialize(&self.metadata) - .map_err(|e| SerializeError::Generic(format!("{:?}", e)))?; - - // Prepend the header. - let mut serialized = Self::MAGIC_HEADER.to_vec(); - serialized.extend(bytes); - Ok(serialized) - } - - #[cfg(not(feature = "serialize"))] - fn serialize(&self) -> Result, SerializeError> { - Err(SerializeError::Generic( - "The serializer feature is not enabled in the DummyEngine", - )) - } }