diff --git a/Cargo.lock b/Cargo.lock index 815f04419..f2ae14ceb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2900,6 +2900,7 @@ name = "wasmer-engine" version = "2.0.0" dependencies = [ "backtrace", + "enumset", "lazy_static", "loupe", "memmap2", @@ -2919,6 +2920,7 @@ name = "wasmer-engine-dummy" version = "2.0.0" dependencies = [ "bincode", + "enumset", "loupe", "serde", "serde_bytes", @@ -2933,6 +2935,7 @@ name = "wasmer-engine-dylib" version = "2.0.0" dependencies = [ "cfg-if 1.0.0", + "enumset", "leb128", "libloading", "loupe", @@ -2954,6 +2957,7 @@ version = "2.0.0" dependencies = [ "bincode", "cfg-if 1.0.0", + "enumset", "leb128", "libloading", "loupe", @@ -2972,6 +2976,7 @@ name = "wasmer-engine-universal" version = "2.0.0" dependencies = [ "cfg-if 1.0.0", + "enumset", "leb128", "loupe", "region", diff --git a/lib/api/src/sys/instance.rs b/lib/api/src/sys/instance.rs index c67518aef..7368c5549 100644 --- a/lib/api/src/sys/instance.rs +++ b/lib/api/src/sys/instance.rs @@ -58,6 +58,11 @@ pub enum InstantiationError { #[error(transparent)] Start(RuntimeError), + /// The module was compiled with a CPU feature that is not available on + /// the current host. + #[error("missing requires CPU features: {0:?}")] + CpuFeature(String), + /// Error occurred when initializing the host environment. #[error(transparent)] HostEnvInitialization(HostEnvInitError), @@ -68,6 +73,7 @@ impl From for InstantiationError { match other { wasmer_engine::InstantiationError::Link(e) => Self::Link(e), wasmer_engine::InstantiationError::Start(e) => Self::Start(e), + wasmer_engine::InstantiationError::CpuFeature(e) => Self::CpuFeature(e), } } } diff --git a/lib/c-api/src/wasm_c_api/instance.rs b/lib/c-api/src/wasm_c_api/instance.rs index 047dd18ee..1b95cbd12 100644 --- a/lib/c-api/src/wasm_c_api/instance.rs +++ b/lib/c-api/src/wasm_c_api/instance.rs @@ -73,6 +73,12 @@ pub unsafe extern "C" fn wasm_instance_new( return None; } + Err(e @ InstantiationError::CpuFeature(_)) => { + crate::error::update_last_error(e.to_string()); + + return None; + } + Err(InstantiationError::HostEnvInitialization(error)) => { crate::error::update_last_error(error); diff --git a/lib/engine-dylib/Cargo.toml b/lib/engine-dylib/Cargo.toml index 1b356e070..6df13e491 100644 --- a/lib/engine-dylib/Cargo.toml +++ b/lib/engine-dylib/Cargo.toml @@ -25,6 +25,7 @@ tempfile = "3.1" which = "4.0" rkyv = "0.6.1" loupe = "0.1" +enumset = "1.0" [features] # Enable the `compiler` feature if you want the engine to compile diff --git a/lib/engine-dylib/src/artifact.rs b/lib/engine-dylib/src/artifact.rs index 1469a3733..3a8d9a7f9 100644 --- a/lib/engine-dylib/src/artifact.rs +++ b/lib/engine-dylib/src/artifact.rs @@ -3,6 +3,7 @@ use crate::engine::{DylibEngine, DylibEngineInner}; use crate::serialize::{ArchivedModuleMetadata, ModuleMetadata}; +use enumset::EnumSet; use libloading::{Library, Symbol as LibrarySymbol}; use loupe::MemoryUsage; use std::error::Error; @@ -17,8 +18,8 @@ use tracing::log::error; #[cfg(feature = "compiler")] use tracing::trace; use wasmer_compiler::{ - Architecture, CompileError, CompiledFunctionFrameInfo, Features, FunctionAddressMap, - OperatingSystem, Symbol, SymbolRegistry, Triple, + Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features, + FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple, }; #[cfg(feature = "compiler")] use wasmer_compiler::{ @@ -211,6 +212,7 @@ impl DylibArtifact { prefix: engine_inner.get_prefix(&data), data_initializers, function_body_lengths, + cpu_features: target.cpu_features().as_u64(), }; let serialized_data = metadata.serialize()?; @@ -800,6 +802,10 @@ impl Artifact for DylibArtifact { &self.metadata.compile_info.features } + fn cpu_features(&self) -> enumset::EnumSet { + EnumSet::from_u64(self.metadata.cpu_features) + } + fn data_initializers(&self) -> &[OwnedDataInitializer] { &*self.metadata.data_initializers } diff --git a/lib/engine-dylib/src/serialize.rs b/lib/engine-dylib/src/serialize.rs index 7021834a8..33df08a6a 100644 --- a/lib/engine-dylib/src/serialize.rs +++ b/lib/engine-dylib/src/serialize.rs @@ -35,6 +35,7 @@ pub struct ModuleMetadata { pub data_initializers: Box<[OwnedDataInitializer]>, // The function body lengths (used to find function by address) pub function_body_lengths: PrimaryMap, + pub cpu_features: u64, } pub struct ModuleMetadataSymbolRegistry<'a> { diff --git a/lib/engine-staticlib/Cargo.toml b/lib/engine-staticlib/Cargo.toml index 860b8637a..cac5c2415 100644 --- a/lib/engine-staticlib/Cargo.toml +++ b/lib/engine-staticlib/Cargo.toml @@ -24,6 +24,7 @@ leb128 = "0.2" libloading = "0.7" tempfile = "3.1" loupe = "0.1" +enumset = "1.0" [features] # Enable the `compiler` feature if you want the engine to compile diff --git a/lib/engine-staticlib/src/artifact.rs b/lib/engine-staticlib/src/artifact.rs index 15b393c55..cf25fb480 100644 --- a/lib/engine-staticlib/src/artifact.rs +++ b/lib/engine-staticlib/src/artifact.rs @@ -3,12 +3,15 @@ use crate::engine::{StaticlibEngine, StaticlibEngineInner}; use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry}; +use enumset::EnumSet; use loupe::MemoryUsage; use std::collections::BTreeMap; use std::error::Error; use std::mem; use std::sync::Arc; -use wasmer_compiler::{CompileError, Features, OperatingSystem, SymbolRegistry, Triple}; +use wasmer_compiler::{ + CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple, +}; #[cfg(feature = "compiler")] use wasmer_compiler::{ CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain, @@ -182,6 +185,7 @@ impl StaticlibArtifact { prefix: engine_inner.get_prefix(&data), data_initializers, function_body_lengths, + cpu_features: target.cpu_features().as_u64(), }; /* @@ -453,6 +457,10 @@ impl Artifact for StaticlibArtifact { &self.metadata.compile_info.features } + fn cpu_features(&self) -> EnumSet { + EnumSet::from_u64(self.metadata.cpu_features) + } + fn data_initializers(&self) -> &[OwnedDataInitializer] { &*self.metadata.data_initializers } diff --git a/lib/engine-staticlib/src/serialize.rs b/lib/engine-staticlib/src/serialize.rs index cee443a45..3bd2fd799 100644 --- a/lib/engine-staticlib/src/serialize.rs +++ b/lib/engine-staticlib/src/serialize.rs @@ -12,6 +12,7 @@ pub struct ModuleMetadata { pub data_initializers: Box<[OwnedDataInitializer]>, // The function body lengths (used to find function by address) pub function_body_lengths: PrimaryMap, + pub cpu_features: u64, } #[derive(MemoryUsage)] diff --git a/lib/engine-universal/Cargo.toml b/lib/engine-universal/Cargo.toml index 683acc8fb..c76c4154c 100644 --- a/lib/engine-universal/Cargo.toml +++ b/lib/engine-universal/Cargo.toml @@ -11,8 +11,13 @@ readme = "README.md" edition = "2018" [dependencies] -wasmer-types = { path = "../types", version = "2.0.0", features = ["enable-rkyv"] } -wasmer-compiler = { path = "../compiler", version = "2.0.0", features = ["translator", "enable-rkyv"] } +wasmer-types = { path = "../types", version = "2.0.0", features = [ + "enable-rkyv", +] } +wasmer-compiler = { path = "../compiler", version = "2.0.0", features = [ + "translator", + "enable-rkyv", +] } wasmer-vm = { path = "../vm", version = "2.0.0", features = ["enable-rkyv"] } wasmer-engine = { path = "../engine", version = "2.0.0" } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } @@ -21,6 +26,7 @@ cfg-if = "1.0" leb128 = "0.2" rkyv = "0.6.1" loupe = "0.1" +enumset = "1.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 6e0d4bac9..61a7aeabe 100644 --- a/lib/engine-universal/src/artifact.rs +++ b/lib/engine-universal/src/artifact.rs @@ -6,9 +6,10 @@ use crate::link::link_module; #[cfg(feature = "compiler")] use crate::serialize::SerializableCompilation; use crate::serialize::SerializableModule; +use enumset::EnumSet; use loupe::MemoryUsage; use std::sync::{Arc, Mutex}; -use wasmer_compiler::{CompileError, Features, Triple}; +use wasmer_compiler::{CompileError, CpuFeature, Features, Triple}; #[cfg(feature = "compiler")] use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain}; use wasmer_engine::{ @@ -128,6 +129,7 @@ impl UniversalArtifact { compilation: serializable_compilation, compile_info, data_initializers, + cpu_features: engine.target().cpu_features().as_u64(), }; Self::from_parts(&mut inner_engine, serializable) } @@ -307,6 +309,10 @@ impl Artifact for UniversalArtifact { &self.serializable.compile_info.features } + fn cpu_features(&self) -> EnumSet { + EnumSet::from_u64(self.serializable.cpu_features) + } + fn data_initializers(&self) -> &[OwnedDataInitializer] { &*self.serializable.data_initializers } diff --git a/lib/engine-universal/src/serialize.rs b/lib/engine-universal/src/serialize.rs index 6e3c768d4..fa0cfc79f 100644 --- a/lib/engine-universal/src/serialize.rs +++ b/lib/engine-universal/src/serialize.rs @@ -38,6 +38,7 @@ pub struct SerializableModule { pub compilation: SerializableCompilation, pub compile_info: CompileModuleInfo, pub data_initializers: Box<[OwnedDataInitializer]>, + pub cpu_features: u64, } fn to_serialize_error(err: impl std::error::Error) -> SerializeError { diff --git a/lib/engine/Cargo.toml b/lib/engine/Cargo.toml index 39d058e5d..0e19974c4 100644 --- a/lib/engine/Cargo.toml +++ b/lib/engine/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive", "rc"] } serde_bytes = { version = "0.11" } lazy_static = "1.4" loupe = "0.1" +enumset = "1.0" [badges] maintenance = { status = "actively-developed" } diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index e8185d14a..21e53c90a 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -1,12 +1,13 @@ use crate::{ resolve_imports, InstantiationError, Resolver, RuntimeError, SerializeError, Tunables, }; +use enumset::EnumSet; use loupe::MemoryUsage; use std::any::Any; use std::fs; use std::path::Path; use std::sync::Arc; -use wasmer_compiler::Features; +use wasmer_compiler::{CpuFeature, Features}; use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::{ DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, @@ -43,6 +44,9 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { /// 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; @@ -96,6 +100,16 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { resolver: &dyn Resolver, host_state: Box, ) -> Result { + // Validate the CPU features this module was compiled with against the + // host CPU features. + let host_cpu_features = CpuFeature::for_host(); + if !host_cpu_features.is_superset(self.cpu_features()) { + Err(InstantiationError::CpuFeature(format!( + "{:?}", + self.cpu_features().difference(host_cpu_features) + )))?; + } + self.preinstantiate()?; let module = self.module(); diff --git a/lib/engine/src/error.rs b/lib/engine/src/error.rs index 87f771ebe..5874e5d8b 100644 --- a/lib/engine/src/error.rs +++ b/lib/engine/src/error.rs @@ -91,6 +91,11 @@ pub enum InstantiationError { #[error(transparent)] Link(LinkError), + /// 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), + /// A runtime error occured while invoking the start function #[error(transparent)] Start(RuntimeError), diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 145bc2529..493090c7e 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -257,7 +257,9 @@ fn trap_start_function_import(config: crate::Config) -> Result<()> { .err() .unwrap(); match err { - InstantiationError::Link(_) | InstantiationError::HostEnvInitialization(_) => { + InstantiationError::Link(_) + | InstantiationError::HostEnvInitialization(_) + | InstantiationError::CpuFeature(_) => { panic!("It should be a start error") } InstantiationError::Start(err) => { diff --git a/tests/lib/engine-dummy/Cargo.toml b/tests/lib/engine-dummy/Cargo.toml index ae30b2aef..5bb1369b2 100644 --- a/tests/lib/engine-dummy/Cargo.toml +++ b/tests/lib/engine-dummy/Cargo.toml @@ -16,19 +16,14 @@ serde = { version = "1.0", features = ["derive", "rc"], optional = true } serde_bytes = { version = "0.11", optional = true } bincode = { version = "1.2", optional = true } loupe = "0.1" +enumset = "1.0" [features] # Enable the `compiler` feature if you want the engine to compile # and not be only on headless mode. default = ["serialize", "compiler"] -compiler = [ - "wasmer-compiler/translator" -] -serialize = [ - "serde", - "serde_bytes", - "bincode" -] +compiler = ["wasmer-compiler/translator"] +serialize = ["serde", "serde_bytes", "bincode"] [badges] # TODO: publish this crate again and deprecate it diff --git a/tests/lib/engine-dummy/src/artifact.rs b/tests/lib/engine-dummy/src/artifact.rs index 72fc3d455..9b7523afb 100644 --- a/tests/lib/engine-dummy/src/artifact.rs +++ b/tests/lib/engine-dummy/src/artifact.rs @@ -2,13 +2,14 @@ //! done as separate steps. use crate::engine::DummyEngine; +use enumset::EnumSet; use loupe::MemoryUsage; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; use std::sync::Arc; -use wasmer_compiler::CompileError; #[cfg(feature = "compiler")] use wasmer_compiler::ModuleEnvironment; +use wasmer_compiler::{CompileError, CpuFeature}; use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables}; use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::{ @@ -30,6 +31,7 @@ pub struct DummyArtifactMetadata { // Plans for that module pub memory_styles: PrimaryMap, pub table_styles: PrimaryMap, + pub cpu_features: u64, } /// A Dummy artifact. @@ -104,6 +106,7 @@ impl DummyArtifact { data_initializers, memory_styles, table_styles, + cpu_features: engine.target().cpu_features().as_u64(), }; Self::from_parts(&engine, metadata) } @@ -211,6 +214,10 @@ impl Artifact for DummyArtifact { &self.metadata.features } + fn cpu_features(&self) -> EnumSet { + EnumSet::from_u64(self.metadata.cpu_features) + } + fn data_initializers(&self) -> &[OwnedDataInitializer] { &*self.metadata.data_initializers }