From d5bbfd0768a8788c256f4f21e699aa53a52e11dd Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 22 Mar 2022 11:24:07 +0100 Subject: [PATCH 01/13] Moved TrapCode enum to wasmer_types, and adapted compiler and wompiler-singlepass --- lib/cli/Cargo.toml | 21 ++++++++++++-------- lib/cli/src/bin/wasmer_compiler.rs | 13 ++++++++++++ lib/compiler-singlepass/src/codegen.rs | 4 ++-- lib/compiler-singlepass/src/compiler.rs | 4 ++-- lib/compiler-singlepass/src/machine.rs | 4 ++-- lib/compiler-singlepass/src/machine_arm64.rs | 4 ++-- lib/compiler-singlepass/src/machine_x64.rs | 4 ++-- lib/compiler/Cargo.toml | 4 +++- lib/compiler/src/trap.rs | 2 +- lib/engine-universal/Cargo.toml | 2 +- lib/engine/Cargo.toml | 4 +++- lib/types/src/lib.rs | 3 +++ lib/{vm/src/trap => types/src}/trapcode.rs | 0 lib/vm/src/trap/mod.rs | 3 +-- lib/vm/src/trap/traphandlers.rs | 2 +- 15 files changed, 49 insertions(+), 25 deletions(-) create mode 100644 lib/cli/src/bin/wasmer_compiler.rs rename lib/{vm/src/trap => types/src}/trapcode.rs (100%) diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index a0b5cbdae5b..f149aaa6430 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -24,19 +24,11 @@ doc = false required-features = ["headless"] [dependencies] -wasmer = { version = "=2.2.1", path = "../api", default-features = false } wasmer-compiler = { version = "=2.2.1", path = "../compiler" } -wasmer-compiler-cranelift = { version = "=2.2.1", path = "../compiler-cranelift", optional = true } wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true } -wasmer-compiler-llvm = { version = "=2.2.1", path = "../compiler-llvm", optional = true } -wasmer-emscripten = { version = "=2.2.1", path = "../emscripten", optional = true } wasmer-engine = { version = "=2.2.1", path = "../engine" } wasmer-engine-universal = { version = "=2.2.1", path = "../engine-universal", optional = true } -wasmer-engine-dylib = { version = "=2.2.1", path = "../engine-dylib", optional = true } -wasmer-engine-staticlib = { version = "=2.2.1", path = "../engine-staticlib", optional = true } -wasmer-vm = { version = "=2.2.1", path = "../vm" } wasmer-wasi = { version = "=2.2.1", path = "../wasi", optional = true } -wasmer-wasi-experimental-io-devices = { version = "=2.2.1", path = "../wasi-experimental-io-devices", optional = true } wasmer-wast = { version = "=2.2.1", path = "../../tests/lib/wast", optional = true } wasmer-cache = { version = "=2.2.1", path = "../cache", optional = true } wasmer-types = { version = "=2.2.1", path = "../types" } @@ -55,6 +47,19 @@ fern = { version = "0.6", features = ["colored"], optional = true } log = { version = "0.4", optional = true } tempfile = "3" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasmer = { version = "=2.2.1", path = "../api", default-features = false, features = ["js-default"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasmer = { version = "=2.2.1", path = "../api", default-features = false } +wasmer-vm = { version = "=2.2.1", path = "../vm" } +wasmer-compiler-cranelift = { version = "=2.2.1", path = "../compiler-cranelift", optional = true } +wasmer-compiler-llvm = { version = "=2.2.1", path = "../compiler-llvm", optional = true } +wasmer-wasi-experimental-io-devices = { version = "=2.2.1", path = "../wasi-experimental-io-devices", optional = true } +wasmer-emscripten = { version = "=2.2.1", path = "../emscripten", optional = true } +wasmer-engine-dylib = { version = "=2.2.1", path = "../engine-dylib", optional = true } +wasmer-engine-staticlib = { version = "=2.2.1", path = "../engine-staticlib", optional = true } + [target.'cfg(target_os = "linux")'.dependencies] unix_mode = "0.1.3" diff --git a/lib/cli/src/bin/wasmer_compiler.rs b/lib/cli/src/bin/wasmer_compiler.rs new file mode 100644 index 00000000000..13c4ff72e7c --- /dev/null +++ b/lib/cli/src/bin/wasmer_compiler.rs @@ -0,0 +1,13 @@ +use wasmer_cli::cli::wasmer_main; + +#[cfg(not(any(feature = "cranelift", feature = "singlepass", feature = "llvm")))] +compile_error!( + "Either enable at least one compiler, or compile the wasmer-headless binary instead" +); + +#[cfg(featue = "run")] +compile_error!("Cannot enable run with the compile-only build"); + +fn main() { + wasmer_main(); +} diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index 93debeb2a06..4bb1a304521 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -23,9 +23,9 @@ use wasmer_types::{ }; use wasmer_types::{ FunctionIndex, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, ModuleInfo, - SignatureIndex, TableIndex, Type, + SignatureIndex, TableIndex, TrapCode, Type, }; -use wasmer_vm::{MemoryStyle, TableStyle, TrapCode, VMBuiltinFunctionIndex, VMOffsets}; +use wasmer_vm::{MemoryStyle, TableStyle, VMBuiltinFunctionIndex, VMOffsets}; /// The singlepass per-function code generator. pub struct FuncGen<'a, M: Machine> { diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index 958a6b3fe7c..e3751a9d71f 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -29,9 +29,9 @@ use wasmer_compiler::{ }; use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{ - FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo, TableIndex, + FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo, TableIndex, TrapCode, }; -use wasmer_vm::{TrapCode, VMOffsets}; +use wasmer_vm::VMOffsets; /// A compiler that compiles a WebAssembly module with Singlepass. /// It does the compilation in one pass diff --git a/lib/compiler-singlepass/src/machine.rs b/lib/compiler-singlepass/src/machine.rs index 753640b209c..9dd54fb382a 100644 --- a/lib/compiler-singlepass/src/machine.rs +++ b/lib/compiler-singlepass/src/machine.rs @@ -12,8 +12,8 @@ use wasmer_compiler::{ Architecture, CallingConvention, CpuFeature, CustomSection, FunctionBody, InstructionAddressMap, Relocation, RelocationTarget, Target, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType}; -use wasmer_vm::{TrapCode, VMOffsets}; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode}; +use wasmer_vm::VMOffsets; pub type Label = DynamicLabel; pub type Offset = AssemblyOffset; diff --git a/lib/compiler-singlepass/src/machine_arm64.rs b/lib/compiler-singlepass/src/machine_arm64.rs index 90c27323952..9990e0a2028 100644 --- a/lib/compiler-singlepass/src/machine_arm64.rs +++ b/lib/compiler-singlepass/src/machine_arm64.rs @@ -14,8 +14,8 @@ use wasmer_compiler::{ CallingConvention, CustomSection, FunctionBody, InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SourceLoc, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType}; -use wasmer_vm::{TrapCode, VMOffsets}; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode}; +use wasmer_vm::VMOffsets; type Assembler = VecAssembler; type Location = AbstractLocation; diff --git a/lib/compiler-singlepass/src/machine_x64.rs b/lib/compiler-singlepass/src/machine_x64.rs index 36be802904a..67fd6f73444 100644 --- a/lib/compiler-singlepass/src/machine_x64.rs +++ b/lib/compiler-singlepass/src/machine_x64.rs @@ -18,8 +18,8 @@ use wasmer_compiler::{ InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SectionBody, SourceLoc, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType, Type}; -use wasmer_vm::{TrapCode, VMOffsets}; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode, Type}; +use wasmer_vm::VMOffsets; type Assembler = VecAssembler; diff --git a/lib/compiler/Cargo.toml b/lib/compiler/Cargo.toml index 3579b58fe0e..50cdbdcd114 100644 --- a/lib/compiler/Cargo.toml +++ b/lib/compiler/Cargo.toml @@ -11,7 +11,6 @@ readme = "README.md" edition = "2018" [dependencies] -wasmer-vm = { path = "../vm", version = "=2.2.1" } wasmer-types = { path = "../types", version = "=2.2.1", default-features = false } wasmparser = { version = "0.83", optional = true, default-features = false } target-lexicon = { version = "0.12.2", default-features = false } @@ -24,6 +23,9 @@ smallvec = "1.6" rkyv = { version = "0.7.20", optional = true } loupe = "0.1" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasmer-vm = { path = "../vm", version = "=2.2.1" } + [features] default = ["std", "enable-serde", "enable-rkyv"] # This feature is for compiler implementors, it enables using `Compiler` and diff --git a/lib/compiler/src/trap.rs b/lib/compiler/src/trap.rs index 2d016c13122..d4119110df1 100644 --- a/lib/compiler/src/trap.rs +++ b/lib/compiler/src/trap.rs @@ -4,7 +4,7 @@ use loupe::MemoryUsage; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -use wasmer_vm::TrapCode; +use wasmer_types::TrapCode; /// Information about trap. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] diff --git a/lib/engine-universal/Cargo.toml b/lib/engine-universal/Cargo.toml index fbf106dc1b7..2b413410447 100644 --- a/lib/engine-universal/Cargo.toml +++ b/lib/engine-universal/Cargo.toml @@ -21,7 +21,7 @@ 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 = "3.0" +region = { version = "3.0" } cfg-if = "1.0" leb128 = "0.2" rkyv = "0.7.20" diff --git a/lib/engine/Cargo.toml b/lib/engine/Cargo.toml index 79fdc2b8ede..e7015e9111b 100644 --- a/lib/engine/Cargo.toml +++ b/lib/engine/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" [dependencies] wasmer-types = { path = "../types", version = "=2.2.1" } wasmer-compiler = { path = "../compiler", version = "=2.2.1" } -wasmer-vm = { path = "../vm", 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" @@ -27,5 +26,8 @@ lazy_static = "1.4" loupe = "0.1" enumset = "1.0" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasmer-vm = { path = "../vm", version = "=2.2.1" } + [badges] maintenance = { status = "actively-developed" } diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 4337b902725..b952890b9ef 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -62,6 +62,7 @@ mod initializers; mod memory_view; mod module; mod native; +mod trapcode; mod types; mod units; mod values; @@ -93,5 +94,7 @@ pub use types::{ #[cfg(feature = "enable-rkyv")] pub use archives::ArchivableIndexMap; +pub use crate::trapcode::TrapCode; + /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/vm/src/trap/trapcode.rs b/lib/types/src/trapcode.rs similarity index 100% rename from lib/vm/src/trap/trapcode.rs rename to lib/types/src/trapcode.rs diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index 6d406f56a79..18e065afead 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -3,12 +3,11 @@ //! This is the module that facilitates the usage of Traps //! in Wasmer Runtime -mod trapcode; mod traphandlers; -pub use trapcode::TrapCode; pub use traphandlers::{ catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, Trap, TrapHandler, TrapHandlerFn, }; pub use traphandlers::{init_traps, resume_panic}; +pub use wasmer_types::TrapCode; diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index ce19d0da625..2de040e518e 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -4,7 +4,6 @@ //! WebAssembly trap handling, which is built on top of the lower-level //! signalhandling mechanisms. -use super::trapcode::TrapCode; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline}; use backtrace::Backtrace; use corosensei::stack::DefaultStack; @@ -21,6 +20,7 @@ use std::mem::MaybeUninit; use std::ptr::{self, NonNull}; use std::sync::atomic::{compiler_fence, AtomicPtr, Ordering}; use std::sync::{Mutex, Once}; +use wasmer_types::TrapCode; cfg_if::cfg_if! { if #[cfg(unix)] { From eb7ef525210656dd89b662d1e3e45c6d12d13bb7 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 22 Mar 2022 14:59:26 +0100 Subject: [PATCH 02/13] Move VMOffset (and a few friends) to wasmer_types, and adapted compiler-singlepass to use it --- lib/compiler-singlepass/src/codegen.rs | 11 +- lib/compiler-singlepass/src/compiler.rs | 2 +- lib/compiler-singlepass/src/emitter_arm64.rs | 3 +- lib/compiler-singlepass/src/machine.rs | 3 +- lib/compiler-singlepass/src/machine_arm64.rs | 3 +- lib/compiler-singlepass/src/machine_x64.rs | 3 +- lib/types/Cargo.toml | 1 + lib/types/src/lib.rs | 3 + lib/{vm => types}/src/vmoffsets.rs | 130 ++++++++++++++++++- lib/vm/Cargo.toml | 6 +- lib/vm/src/lib.rs | 14 +- lib/vm/src/vmcontext.rs | 122 +---------------- 12 files changed, 150 insertions(+), 151 deletions(-) rename lib/{vm => types}/src/vmoffsets.rs (84%) diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index 4bb1a304521..85f5b349f1a 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -18,14 +18,11 @@ use wasmer_compiler::{ Relocation, RelocationTarget, SectionIndex, }; use wasmer_types::{ - entity::{EntityRef, PrimaryMap}, - FunctionType, + entity::{EntityRef, PrimaryMap, SecondaryMap}, + FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, + ModuleInfo, SignatureIndex, TableIndex, TrapCode, Type, VMBuiltinFunctionIndex, VMOffsets, }; -use wasmer_types::{ - FunctionIndex, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, ModuleInfo, - SignatureIndex, TableIndex, TrapCode, Type, -}; -use wasmer_vm::{MemoryStyle, TableStyle, VMBuiltinFunctionIndex, VMOffsets}; +use wasmer_vm::{MemoryStyle, TableStyle}; /// The singlepass per-function code generator. pub struct FuncGen<'a, M: Machine> { diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index e3751a9d71f..8994ec4690e 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -30,8 +30,8 @@ use wasmer_compiler::{ use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{ FunctionIndex, FunctionType, LocalFunctionIndex, MemoryIndex, ModuleInfo, TableIndex, TrapCode, + VMOffsets, }; -use wasmer_vm::VMOffsets; /// A compiler that compiles a WebAssembly module with Singlepass. /// It does the compilation in one pass diff --git a/lib/compiler-singlepass/src/emitter_arm64.rs b/lib/compiler-singlepass/src/emitter_arm64.rs index 33588d7d148..5a7b0c74da6 100644 --- a/lib/compiler-singlepass/src/emitter_arm64.rs +++ b/lib/compiler-singlepass/src/emitter_arm64.rs @@ -12,8 +12,7 @@ use dynasmrt::{ use wasmer_compiler::{ CallingConvention, CustomSection, CustomSectionProtection, FunctionBody, SectionBody, }; -use wasmer_types::{FunctionIndex, FunctionType, Type}; -use wasmer_vm::VMOffsets; +use wasmer_types::{FunctionIndex, FunctionType, Type, VMOffsets}; type Assembler = VecAssembler; diff --git a/lib/compiler-singlepass/src/machine.rs b/lib/compiler-singlepass/src/machine.rs index 9dd54fb382a..b55ff38efd3 100644 --- a/lib/compiler-singlepass/src/machine.rs +++ b/lib/compiler-singlepass/src/machine.rs @@ -12,8 +12,7 @@ use wasmer_compiler::{ Architecture, CallingConvention, CpuFeature, CustomSection, FunctionBody, InstructionAddressMap, Relocation, RelocationTarget, Target, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType, TrapCode}; -use wasmer_vm::VMOffsets; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode, VMOffsets}; pub type Label = DynamicLabel; pub type Offset = AssemblyOffset; diff --git a/lib/compiler-singlepass/src/machine_arm64.rs b/lib/compiler-singlepass/src/machine_arm64.rs index 9990e0a2028..3e98b36b235 100644 --- a/lib/compiler-singlepass/src/machine_arm64.rs +++ b/lib/compiler-singlepass/src/machine_arm64.rs @@ -14,8 +14,7 @@ use wasmer_compiler::{ CallingConvention, CustomSection, FunctionBody, InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SourceLoc, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType, TrapCode}; -use wasmer_vm::VMOffsets; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode, VMOffsets}; type Assembler = VecAssembler; type Location = AbstractLocation; diff --git a/lib/compiler-singlepass/src/machine_x64.rs b/lib/compiler-singlepass/src/machine_x64.rs index 67fd6f73444..c1eb953bae1 100644 --- a/lib/compiler-singlepass/src/machine_x64.rs +++ b/lib/compiler-singlepass/src/machine_x64.rs @@ -18,8 +18,7 @@ use wasmer_compiler::{ InstructionAddressMap, Relocation, RelocationKind, RelocationTarget, SectionBody, SourceLoc, TrapInformation, }; -use wasmer_types::{FunctionIndex, FunctionType, TrapCode, Type}; -use wasmer_vm::VMOffsets; +use wasmer_types::{FunctionIndex, FunctionType, TrapCode, Type, VMOffsets}; type Assembler = VecAssembler; diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index 3348db675a1..3ead06c249a 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -13,6 +13,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", features = ["derive", "rc"], optional = true, default-features = false } thiserror = "1.0" +more-asserts = "0.2" indexmap = { version = "1.6", features = ["serde-1"] } rkyv = { version = "0.7.20", optional = true } loupe = { version = "0.1", features = ["enable-indexmap"] } diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index b952890b9ef..12d707d2517 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -66,6 +66,7 @@ mod trapcode; mod types; mod units; mod values; +mod vmoffsets; /// The entity module, with common helpers for Rust structures pub mod entity; @@ -96,5 +97,7 @@ pub use archives::ArchivableIndexMap; pub use crate::trapcode::TrapCode; +pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; + /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/vm/src/vmoffsets.rs b/lib/types/src/vmoffsets.rs similarity index 84% rename from lib/vm/src/vmoffsets.rs rename to lib/types/src/vmoffsets.rs index 2a1df9cf66f..211c4575358 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/types/src/vmoffsets.rs @@ -6,14 +6,134 @@ #![deny(broken_intra_doc_links)] -use crate::VMBuiltinFunctionIndex; -use loupe::MemoryUsage; -use more_asserts::assert_lt; -use std::convert::TryFrom; -use wasmer_types::{ +use crate::{ FunctionIndex, GlobalIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, ModuleInfo, SignatureIndex, TableIndex, }; +use loupe::MemoryUsage; +use more_asserts::assert_lt; +use std::convert::TryFrom; + +/// An index type for builtin functions. +#[derive(Copy, Clone, Debug)] +pub struct VMBuiltinFunctionIndex(u32); + +impl VMBuiltinFunctionIndex { + /// Returns an index for wasm's `memory.grow` builtin function. + pub const fn get_memory32_grow_index() -> Self { + Self(0) + } + /// Returns an index for wasm's imported `memory.grow` builtin function. + pub const fn get_imported_memory32_grow_index() -> Self { + Self(1) + } + /// Returns an index for wasm's `memory.size` builtin function. + pub const fn get_memory32_size_index() -> Self { + Self(2) + } + /// Returns an index for wasm's imported `memory.size` builtin function. + pub const fn get_imported_memory32_size_index() -> Self { + Self(3) + } + /// Returns an index for wasm's `table.copy` when both tables are locally + /// defined. + pub const fn get_table_copy_index() -> Self { + Self(4) + } + /// Returns an index for wasm's `table.init`. + pub const fn get_table_init_index() -> Self { + Self(5) + } + /// Returns an index for wasm's `elem.drop`. + pub const fn get_elem_drop_index() -> Self { + Self(6) + } + /// Returns an index for wasm's `memory.copy` for locally defined memories. + pub const fn get_memory_copy_index() -> Self { + Self(7) + } + /// Returns an index for wasm's `memory.copy` for imported memories. + pub const fn get_imported_memory_copy_index() -> Self { + Self(8) + } + /// Returns an index for wasm's `memory.fill` for locally defined memories. + pub const fn get_memory_fill_index() -> Self { + Self(9) + } + /// Returns an index for wasm's `memory.fill` for imported memories. + pub const fn get_imported_memory_fill_index() -> Self { + Self(10) + } + /// Returns an index for wasm's `memory.init` instruction. + pub const fn get_memory_init_index() -> Self { + Self(11) + } + /// Returns an index for wasm's `data.drop` instruction. + pub const fn get_data_drop_index() -> Self { + Self(12) + } + /// Returns an index for wasm's `raise_trap` instruction. + pub const fn get_raise_trap_index() -> Self { + Self(13) + } + /// Returns an index for wasm's `table.size` instruction for local tables. + pub const fn get_table_size_index() -> Self { + Self(14) + } + /// Returns an index for wasm's `table.size` instruction for imported tables. + pub const fn get_imported_table_size_index() -> Self { + Self(15) + } + /// Returns an index for wasm's `table.grow` instruction for local tables. + pub const fn get_table_grow_index() -> Self { + Self(16) + } + /// Returns an index for wasm's `table.grow` instruction for imported tables. + pub const fn get_imported_table_grow_index() -> Self { + Self(17) + } + /// Returns an index for wasm's `table.get` instruction for local tables. + pub const fn get_table_get_index() -> Self { + Self(18) + } + /// Returns an index for wasm's `table.get` instruction for imported tables. + pub const fn get_imported_table_get_index() -> Self { + Self(19) + } + /// Returns an index for wasm's `table.set` instruction for local tables. + pub const fn get_table_set_index() -> Self { + Self(20) + } + /// Returns an index for wasm's `table.set` instruction for imported tables. + pub const fn get_imported_table_set_index() -> Self { + Self(21) + } + /// Returns an index for wasm's `func.ref` instruction. + pub const fn get_func_ref_index() -> Self { + Self(22) + } + /// Returns an index for wasm's `table.fill` instruction for local tables. + pub const fn get_table_fill_index() -> Self { + Self(23) + } + /// Returns an index for a function to increment the externref count. + pub const fn get_externref_inc_index() -> Self { + Self(24) + } + /// Returns an index for a function to decrement the externref count. + pub const fn get_externref_dec_index() -> Self { + Self(25) + } + /// Returns the total number of builtin functions. + pub const fn builtin_functions_total_number() -> u32 { + 26 + } + + /// Return the index as an u32 number. + pub const fn index(self) -> u32 { + self.0 + } +} #[cfg(target_pointer_width = "32")] fn cast_to_u32(sz: usize) -> u32 { diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index ed92a6b90ab..93bc58558fc 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -12,7 +12,6 @@ edition = "2018" [dependencies] wasmer-types = { path = "../types", version = "=2.2.1" } -region = "3.0" libc = { version = "^0.2", default-features = false } memoffset = "0.6" indexmap = { version = "1.6", features = ["serde-1"] } @@ -24,10 +23,13 @@ serde = { version = "1.0", features = ["derive", "rc"] } rkyv = { version = "0.7.20", optional = true } loupe = { version = "0.1", features = ["enable-indexmap"] } enum-iterator = "0.7.0" -corosensei = { version = "0.1.2" } scopeguard = "1.1.0" lazy_static = "1.4.0" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +region = { version = "3.0" } +corosensei = { version = "0.1.2" } + [target.'cfg(target_vendor = "apple")'.dependencies] mach = "0.3.2" diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 1214b921d84..8c1e594c449 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -32,7 +32,6 @@ mod sig_registry; mod table; mod trap; mod vmcontext; -mod vmoffsets; pub mod libcalls; @@ -51,19 +50,20 @@ pub use crate::sig_registry::SignatureRegistry; pub use crate::table::{LinearTable, Table, TableElement, TableStyle}; pub use crate::trap::*; pub use crate::vmcontext::{ - VMBuiltinFunctionIndex, VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, - VMFunctionBody, VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, - VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, - VMTableImport, VMTrampoline, + VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, + VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, + VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, + VMTrampoline, }; -pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMOffsets}; use loupe::MemoryUsage; -pub use wasmer_types::VMExternRef; #[deprecated( since = "2.1.0", note = "ModuleInfo, ExportsIterator, ImportsIterator should be imported from wasmer_types." )] pub use wasmer_types::{ExportsIterator, ImportsIterator, ModuleInfo}; +pub use wasmer_types::{ + TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMExternRef, VMOffsets, +}; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/vm/src/vmcontext.rs b/lib/vm/src/vmcontext.rs index eae9d02d614..c7e39e979b9 100644 --- a/lib/vm/src/vmcontext.rs +++ b/lib/vm/src/vmcontext.rs @@ -10,6 +10,7 @@ use crate::instance::Instance; use crate::memory::Memory; use crate::table::Table; use crate::trap::{Trap, TrapCode}; +use crate::VMBuiltinFunctionIndex; use crate::VMExternRef; use loupe::{MemoryUsage, MemoryUsageTracker, POINTER_BYTE_SIZE}; use std::any::Any; @@ -892,127 +893,6 @@ impl Default for VMCallerCheckedAnyfunc { } } -/// An index type for builtin functions. -#[derive(Copy, Clone, Debug)] -pub struct VMBuiltinFunctionIndex(u32); - -impl VMBuiltinFunctionIndex { - /// Returns an index for wasm's `memory.grow` builtin function. - pub const fn get_memory32_grow_index() -> Self { - Self(0) - } - /// Returns an index for wasm's imported `memory.grow` builtin function. - pub const fn get_imported_memory32_grow_index() -> Self { - Self(1) - } - /// Returns an index for wasm's `memory.size` builtin function. - pub const fn get_memory32_size_index() -> Self { - Self(2) - } - /// Returns an index for wasm's imported `memory.size` builtin function. - pub const fn get_imported_memory32_size_index() -> Self { - Self(3) - } - /// Returns an index for wasm's `table.copy` when both tables are locally - /// defined. - pub const fn get_table_copy_index() -> Self { - Self(4) - } - /// Returns an index for wasm's `table.init`. - pub const fn get_table_init_index() -> Self { - Self(5) - } - /// Returns an index for wasm's `elem.drop`. - pub const fn get_elem_drop_index() -> Self { - Self(6) - } - /// Returns an index for wasm's `memory.copy` for locally defined memories. - pub const fn get_memory_copy_index() -> Self { - Self(7) - } - /// Returns an index for wasm's `memory.copy` for imported memories. - pub const fn get_imported_memory_copy_index() -> Self { - Self(8) - } - /// Returns an index for wasm's `memory.fill` for locally defined memories. - pub const fn get_memory_fill_index() -> Self { - Self(9) - } - /// Returns an index for wasm's `memory.fill` for imported memories. - pub const fn get_imported_memory_fill_index() -> Self { - Self(10) - } - /// Returns an index for wasm's `memory.init` instruction. - pub const fn get_memory_init_index() -> Self { - Self(11) - } - /// Returns an index for wasm's `data.drop` instruction. - pub const fn get_data_drop_index() -> Self { - Self(12) - } - /// Returns an index for wasm's `raise_trap` instruction. - pub const fn get_raise_trap_index() -> Self { - Self(13) - } - /// Returns an index for wasm's `table.size` instruction for local tables. - pub const fn get_table_size_index() -> Self { - Self(14) - } - /// Returns an index for wasm's `table.size` instruction for imported tables. - pub const fn get_imported_table_size_index() -> Self { - Self(15) - } - /// Returns an index for wasm's `table.grow` instruction for local tables. - pub const fn get_table_grow_index() -> Self { - Self(16) - } - /// Returns an index for wasm's `table.grow` instruction for imported tables. - pub const fn get_imported_table_grow_index() -> Self { - Self(17) - } - /// Returns an index for wasm's `table.get` instruction for local tables. - pub const fn get_table_get_index() -> Self { - Self(18) - } - /// Returns an index for wasm's `table.get` instruction for imported tables. - pub const fn get_imported_table_get_index() -> Self { - Self(19) - } - /// Returns an index for wasm's `table.set` instruction for local tables. - pub const fn get_table_set_index() -> Self { - Self(20) - } - /// Returns an index for wasm's `table.set` instruction for imported tables. - pub const fn get_imported_table_set_index() -> Self { - Self(21) - } - /// Returns an index for wasm's `func.ref` instruction. - pub const fn get_func_ref_index() -> Self { - Self(22) - } - /// Returns an index for wasm's `table.fill` instruction for local tables. - pub const fn get_table_fill_index() -> Self { - Self(23) - } - /// Returns an index for a function to increment the externref count. - pub const fn get_externref_inc_index() -> Self { - Self(24) - } - /// Returns an index for a function to decrement the externref count. - pub const fn get_externref_dec_index() -> Self { - Self(25) - } - /// Returns the total number of builtin functions. - pub const fn builtin_functions_total_number() -> u32 { - 26 - } - - /// Return the index as an u32 number. - pub const fn index(self) -> u32 { - self.0 - } -} - /// An array that stores addresses of builtin functions. We translate code /// to use indirect calls. This way, we don't have to patch the code. #[repr(C)] From e745ceab7a7d155855fa1fcc5d5ad617fa186c42 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 23 Mar 2022 09:55:08 +0100 Subject: [PATCH 03/13] Moved TableStyle and MemoryStyle to wasmer-types (and compiler-single pass do not depend from vm anymore) --- Cargo.lock | 2 +- lib/compiler-singlepass/Cargo.toml | 1 - lib/compiler-singlepass/src/codegen.rs | 4 +- lib/compiler-singlepass/src/compiler.rs | 2 +- lib/compiler/src/module.rs | 3 +- lib/types/src/lib.rs | 5 +- lib/types/src/memory.rs | 87 +++++++++++++++++++++++++ lib/types/src/table.rs | 15 +++++ lib/vm/src/export.rs | 6 +- lib/vm/src/instance/mod.rs | 6 +- lib/vm/src/lib.rs | 6 +- lib/vm/src/memory.rs | 87 +------------------------ lib/vm/src/table.rs | 16 +---- 13 files changed, 123 insertions(+), 117 deletions(-) create mode 100644 lib/types/src/memory.rs create mode 100644 lib/types/src/table.rs diff --git a/Cargo.lock b/Cargo.lock index 1e5742b1ce9..44f38e0fc98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2955,7 +2955,6 @@ dependencies = [ "target-lexicon 0.12.3", "wasmer-compiler", "wasmer-types", - "wasmer-vm", ] [[package]] @@ -3117,6 +3116,7 @@ version = "2.2.1" dependencies = [ "indexmap", "loupe", + "more-asserts", "rkyv", "serde", "thiserror", diff --git a/lib/compiler-singlepass/Cargo.toml b/lib/compiler-singlepass/Cargo.toml index 8227ec4c963..eb6a71b3716 100644 --- a/lib/compiler-singlepass/Cargo.toml +++ b/lib/compiler-singlepass/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" [dependencies] wasmer-compiler = { path = "../compiler", version = "=2.2.1", features = ["translator"], default-features = false } -wasmer-vm = { path = "../vm", version = "=2.2.1" } wasmer-types = { path = "../types", version = "=2.2.1", default-features = false, features = ["std"] } rayon = { version = "1.5", optional = true } hashbrown = { version = "0.11", optional = true } diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index 85f5b349f1a..d7087a7b8b1 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -20,9 +20,9 @@ use wasmer_compiler::{ use wasmer_types::{ entity::{EntityRef, PrimaryMap, SecondaryMap}, FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, - ModuleInfo, SignatureIndex, TableIndex, TrapCode, Type, VMBuiltinFunctionIndex, VMOffsets, + MemoryStyle, ModuleInfo, SignatureIndex, TableIndex, TableStyle, TrapCode, Type, + VMBuiltinFunctionIndex, VMOffsets, }; -use wasmer_vm::{MemoryStyle, TableStyle}; /// The singlepass per-function code generator. pub struct FuncGen<'a, M: Machine> { diff --git a/lib/compiler-singlepass/src/compiler.rs b/lib/compiler-singlepass/src/compiler.rs index 8994ec4690e..776ccc943ca 100644 --- a/lib/compiler-singlepass/src/compiler.rs +++ b/lib/compiler-singlepass/src/compiler.rs @@ -315,7 +315,7 @@ mod tests { use std::str::FromStr; use target_lexicon::triple; use wasmer_compiler::{CpuFeature, Features, Triple}; - use wasmer_vm::{MemoryStyle, TableStyle}; + use wasmer_types::{MemoryStyle, TableStyle}; fn dummy_compilation_ingredients<'a>() -> ( CompileModuleInfo, diff --git a/lib/compiler/src/module.rs b/lib/compiler/src/module.rs index 6e9c5ce279c..73b88775125 100644 --- a/lib/compiler/src/module.rs +++ b/lib/compiler/src/module.rs @@ -5,8 +5,7 @@ use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use wasmer_types::entity::PrimaryMap; -use wasmer_types::{Features, MemoryIndex, ModuleInfo, TableIndex}; -use wasmer_vm::{MemoryStyle, TableStyle}; +use wasmer_types::{Features, MemoryIndex, MemoryStyle, ModuleInfo, TableIndex, TableStyle}; /// The required info for compiling a module. /// diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 12d707d2517..af21cc21554 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -59,9 +59,11 @@ mod extern_ref; mod features; mod indexes; mod initializers; +mod memory; mod memory_view; mod module; mod native; +mod table; mod trapcode; mod types; mod units; @@ -95,8 +97,9 @@ pub use types::{ #[cfg(feature = "enable-rkyv")] pub use archives::ArchivableIndexMap; +pub use crate::memory::{MemoryError, MemoryStyle}; +pub use crate::table::TableStyle; pub use crate::trapcode::TrapCode; - pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; /// Version number of this crate. diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs new file mode 100644 index 00000000000..3384492c4c4 --- /dev/null +++ b/lib/types/src/memory.rs @@ -0,0 +1,87 @@ +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, + } + } +} diff --git a/lib/types/src/table.rs b/lib/types/src/table.rs new file mode 100644 index 00000000000..c15a9aca002 --- /dev/null +++ b/lib/types/src/table.rs @@ -0,0 +1,15 @@ +use loupe::MemoryUsage; +#[cfg(feature = "enable-rkyv")] +use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +use serde::{Deserialize, Serialize}; + +/// Implementation styles for WebAssembly tables. +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, MemoryUsage)] +#[cfg_attr( + feature = "enable-rkyv", + derive(RkyvSerialize, RkyvDeserialize, Archive) +)] +pub enum TableStyle { + /// Signatures are stored in the table and checked in the caller. + CallerChecksSignature, +} diff --git a/lib/vm/src/export.rs b/lib/vm/src/export.rs index f9567962109..473e8727892 100644 --- a/lib/vm/src/export.rs +++ b/lib/vm/src/export.rs @@ -3,12 +3,12 @@ use crate::global::Global; use crate::instance::WeakOrStrongInstanceRef; -use crate::memory::{Memory, MemoryStyle}; -use crate::table::{Table, TableStyle}; +use crate::memory::Memory; +use crate::table::Table; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline}; use loupe::MemoryUsage; use std::sync::Arc; -use wasmer_types::{FunctionType, MemoryType, TableType}; +use wasmer_types::{FunctionType, MemoryStyle, MemoryType, TableStyle, TableType}; /// The value of an export passed from one instance to another. #[derive(Debug)] diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index bed09bbbfc6..a42eb0b9533 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -17,7 +17,7 @@ use crate::export::VMExtern; use crate::func_data_registry::VMFuncRef; use crate::global::Global; use crate::imports::Imports; -use crate::memory::{Memory, MemoryError}; +use crate::memory::Memory; use crate::table::{Table, TableElement}; use crate::trap::{catch_traps, Trap, TrapCode, TrapHandler}; use crate::vmcontext::{ @@ -44,8 +44,8 @@ use std::sync::Arc; use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, - LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, - ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, + LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryError, + MemoryIndex, ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, }; /// The function pointer to call with data and an [`Instance`] pointer to diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 8c1e594c449..0b671ce46b4 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -43,11 +43,11 @@ pub use crate::instance::{ ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle, WeakOrStrongInstanceRef, }; -pub use crate::memory::{LinearMemory, Memory, MemoryError, MemoryStyle}; +pub use crate::memory::{LinearMemory, Memory}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; -pub use crate::table::{LinearTable, Table, TableElement, TableStyle}; +pub use crate::table::{LinearTable, Table, TableElement}; pub use crate::trap::*; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMDynamicFunctionContext, VMFunctionBody, @@ -56,11 +56,13 @@ pub use crate::vmcontext::{ VMTrampoline, }; use loupe::MemoryUsage; +pub use wasmer_types::TableStyle; #[deprecated( since = "2.1.0", note = "ModuleInfo, ExportsIterator, ImportsIterator should be imported from wasmer_types." )] pub use wasmer_types::{ExportsIterator, ImportsIterator, ModuleInfo}; +pub use wasmer_types::{MemoryError, MemoryStyle}; pub use wasmer_types::{ TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMExternRef, VMOffsets, }; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 0465d3dbf1f..10768ebd1f4 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -9,98 +9,13 @@ use crate::mmap::Mmap; use crate::vmcontext::VMMemoryDefinition; use loupe::MemoryUsage; use more_asserts::assert_ge; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use serde::{Deserialize, Serialize}; use std::borrow::BorrowMut; use std::cell::UnsafeCell; use std::convert::TryInto; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use thiserror::Error; -use wasmer_types::{Bytes, MemoryType, Pages}; - -/// 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 wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages}; /// Trait for implementing Wasm Memory used by Wasmer. pub trait Memory: fmt::Debug + Send + Sync + MemoryUsage { diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index 6f8abe4fdd1..6a1731ea5dd 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -10,27 +10,13 @@ use crate::trap::{Trap, TrapCode}; use crate::vmcontext::VMTableDefinition; use crate::VMExternRef; use loupe::{MemoryUsage, MemoryUsageTracker}; -#[cfg(feature = "enable-rkyv")] -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; -use serde::{Deserialize, Serialize}; use std::borrow::{Borrow, BorrowMut}; use std::cell::UnsafeCell; use std::convert::TryFrom; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use wasmer_types::{ExternRef, TableType, Type as ValType}; - -/// Implementation styles for WebAssembly tables. -#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, MemoryUsage)] -#[cfg_attr( - feature = "enable-rkyv", - derive(RkyvSerialize, RkyvDeserialize, Archive) -)] -pub enum TableStyle { - /// Signatures are stored in the table and checked in the caller. - CallerChecksSignature, -} +use wasmer_types::{ExternRef, TableStyle, TableType, Type as ValType}; /// Trait for implementing the interface of a Wasm table. pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { From 3d7ae9e21f380aec3667a2d6e5a73b1db46ace92 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 23 Mar 2022 14:00:53 +0100 Subject: [PATCH 04/13] Moved LibCall enum to wasmer_types, and compiler is no longer dependant of wasmer_vm --- Cargo.lock | 4 +- .../src/translator/translation_utils.rs | 3 +- lib/compiler/Cargo.toml | 5 +- lib/compiler/src/relocation.rs | 3 +- lib/engine-dylib/src/trampoline.rs | 4 +- lib/engine-universal/src/link.rs | 3 +- lib/types/Cargo.toml | 1 + lib/types/src/lib.rs | 2 + lib/types/src/libcalls.rs | 162 ++++++++++++ lib/vm/src/lib.rs | 1 + lib/vm/src/libcalls.rs | 231 +++--------------- 11 files changed, 210 insertions(+), 209 deletions(-) create mode 100644 lib/types/src/libcalls.rs diff --git a/Cargo.lock b/Cargo.lock index 44f38e0fc98..8d7f9da10f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2890,8 +2890,7 @@ dependencies = [ "target-lexicon 0.12.3", "thiserror", "wasmer-types", - "wasmer-vm", - "wasmparser 0.83.0", + "wasmparser 0.78.2", ] [[package]] @@ -3114,6 +3113,7 @@ dependencies = [ name = "wasmer-types" version = "2.2.1" dependencies = [ + "enum-iterator", "indexmap", "loupe", "more-asserts", diff --git a/lib/compiler-cranelift/src/translator/translation_utils.rs b/lib/compiler-cranelift/src/translator/translation_utils.rs index a1b825a07c0..a0277c8b866 100644 --- a/lib/compiler-cranelift/src/translator/translation_utils.rs +++ b/lib/compiler-cranelift/src/translator/translation_utils.rs @@ -11,8 +11,7 @@ use wasmer_compiler::wasm_unsupported; use wasmer_compiler::wasmparser; use wasmer_compiler::RelocationKind; use wasmer_compiler::{WasmError, WasmResult}; -use wasmer_types::{FunctionType, Type}; -use wasmer_vm::libcalls::LibCall; +use wasmer_types::{FunctionType, LibCall, Type}; /// Helper function translate a Function signature into Cranelift Ir pub fn signature_to_cranelift_ir( diff --git a/lib/compiler/Cargo.toml b/lib/compiler/Cargo.toml index 50cdbdcd114..ba1ddbc40e5 100644 --- a/lib/compiler/Cargo.toml +++ b/lib/compiler/Cargo.toml @@ -23,9 +23,6 @@ smallvec = "1.6" rkyv = { version = "0.7.20", optional = true } loupe = "0.1" -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -wasmer-vm = { path = "../vm", version = "=2.2.1" } - [features] default = ["std", "enable-serde", "enable-rkyv"] # This feature is for compiler implementors, it enables using `Compiler` and @@ -35,7 +32,7 @@ translator = ["wasmparser"] std = ["wasmer-types/std"] core = ["hashbrown", "wasmer-types/core"] enable-serde = ["serde", "serde_bytes", "wasmer-types/enable-serde"] -enable-rkyv = ["rkyv", "wasmer-vm/enable-rkyv", "wasmer-types/enable-rkyv"] +enable-rkyv = ["rkyv", "wasmer-types/enable-rkyv"] [badges] maintenance = { status = "experimental" } diff --git a/lib/compiler/src/relocation.rs b/lib/compiler/src/relocation.rs index 63971fffe91..3246f09c5da 100644 --- a/lib/compiler/src/relocation.rs +++ b/lib/compiler/src/relocation.rs @@ -19,8 +19,7 @@ use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; use wasmer_types::entity::PrimaryMap; -use wasmer_types::LocalFunctionIndex; -use wasmer_vm::libcalls::LibCall; +use wasmer_types::{LibCall, LocalFunctionIndex}; /// Relocation kinds for every ISA. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] diff --git a/lib/engine-dylib/src/trampoline.rs b/lib/engine-dylib/src/trampoline.rs index 12d60e2b6c3..3254d270bbf 100644 --- a/lib/engine-dylib/src/trampoline.rs +++ b/lib/engine-dylib/src/trampoline.rs @@ -17,7 +17,7 @@ use object::{ BinaryFormat, RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope, }; use wasmer_compiler::{Architecture, Target}; -use wasmer_vm::libcalls::LibCall; +use wasmer_vm::libcalls::{function_pointer, LibCall}; /// Symbol exported from the dynamic library which points to the trampoline table. pub const WASMER_TRAMPOLINES_SYMBOL: &[u8] = b"WASMER_TRAMPOLINES"; @@ -161,6 +161,6 @@ pub fn emit_trampolines(obj: &mut Object, target: &Target) { /// Fills in the libcall trampoline table at the given address. pub unsafe fn fill_trampoline_table(table: *mut usize) { for libcall in LibCall::into_enum_iter() { - *table.add(libcall as usize) = libcall.function_pointer(); + *table.add(libcall as usize) = function_pointer(libcall); } } diff --git a/lib/engine-universal/src/link.rs b/lib/engine-universal/src/link.rs index f427bce57e1..051f1922714 100644 --- a/lib/engine-universal/src/link.rs +++ b/lib/engine-universal/src/link.rs @@ -6,6 +6,7 @@ use wasmer_compiler::{Relocation, RelocationKind, RelocationTarget, Relocations, use wasmer_engine::FunctionExtent; use wasmer_types::entity::PrimaryMap; use wasmer_types::{LocalFunctionIndex, ModuleInfo}; +use wasmer_vm::libcalls::function_pointer; use wasmer_vm::SectionBodyPtr; fn apply_relocation( @@ -22,7 +23,7 @@ fn apply_relocation( // Use the direct target of the libcall if the relocation supports // a full 64-bit address. Otherwise use a trampoline. if r.kind == RelocationKind::Abs8 || r.kind == RelocationKind::X86PCRel8 { - libcall.function_pointer() + function_pointer(libcall) } else { get_libcall_trampoline( libcall, diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index 3ead06c249a..1e85f099aae 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -17,6 +17,7 @@ more-asserts = "0.2" indexmap = { version = "1.6", features = ["serde-1"] } rkyv = { version = "0.7.20", optional = true } loupe = { version = "0.1", features = ["enable-indexmap"] } +enum-iterator = "0.7.0" [features] default = ["std", "enable-serde", "enable-rkyv"] diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index af21cc21554..27e8edf47c7 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -59,6 +59,7 @@ mod extern_ref; mod features; mod indexes; mod initializers; +mod libcalls; mod memory; mod memory_view; mod module; @@ -97,6 +98,7 @@ pub use types::{ #[cfg(feature = "enable-rkyv")] pub use archives::ArchivableIndexMap; +pub use crate::libcalls::LibCall; pub use crate::memory::{MemoryError, MemoryStyle}; pub use crate::table::TableStyle; pub use crate::trapcode::TrapCode; diff --git a/lib/types/src/libcalls.rs b/lib/types/src/libcalls.rs new file mode 100644 index 00000000000..40be5dfd6f9 --- /dev/null +++ b/lib/types/src/libcalls.rs @@ -0,0 +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) + } +} diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 0b671ce46b4..fe9bd00324c 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -56,6 +56,7 @@ pub use crate::vmcontext::{ VMTrampoline, }; use loupe::MemoryUsage; +pub use wasmer_types::LibCall; pub use wasmer_types::TableStyle; #[deprecated( since = "2.1.0", diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index 83149f6ac12..198e311ccff 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -43,12 +43,7 @@ use crate::table::{RawTableElement, TableElement}; use crate::trap::{raise_lib_trap, Trap, TrapCode}; use crate::vmcontext::VMContext; use crate::{on_host_stack, VMExternRef}; -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; +pub use wasmer_types::LibCall; use wasmer_types::{ DataIndex, ElemIndex, FunctionIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, TableIndex, Type, @@ -694,195 +689,39 @@ pub unsafe extern "C" fn wasmer_vm_raise_trap(trap_code: TrapCode) -> ! { #[no_mangle] pub static wasmer_vm_probestack: unsafe extern "C" fn() = PROBESTACK; -/// 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 { - /// The function pointer to a libcall - pub fn function_pointer(self) -> usize { - match self { - Self::CeilF32 => wasmer_vm_f32_ceil as usize, - Self::CeilF64 => wasmer_vm_f64_ceil as usize, - Self::FloorF32 => wasmer_vm_f32_floor as usize, - Self::FloorF64 => wasmer_vm_f64_floor as usize, - Self::NearestF32 => wasmer_vm_f32_nearest as usize, - Self::NearestF64 => wasmer_vm_f64_nearest as usize, - Self::TruncF32 => wasmer_vm_f32_trunc as usize, - Self::TruncF64 => wasmer_vm_f64_trunc as usize, - Self::Memory32Size => wasmer_vm_memory32_size as usize, - Self::ImportedMemory32Size => wasmer_vm_imported_memory32_size as usize, - Self::TableCopy => wasmer_vm_table_copy as usize, - Self::TableInit => wasmer_vm_table_init as usize, - Self::TableFill => wasmer_vm_table_fill as usize, - Self::TableSize => wasmer_vm_table_size as usize, - Self::ImportedTableSize => wasmer_vm_imported_table_size as usize, - Self::TableGet => wasmer_vm_table_get as usize, - Self::ImportedTableGet => wasmer_vm_imported_table_get as usize, - Self::TableSet => wasmer_vm_table_set as usize, - Self::ImportedTableSet => wasmer_vm_imported_table_set as usize, - Self::TableGrow => wasmer_vm_table_grow as usize, - Self::ImportedTableGrow => wasmer_vm_imported_table_grow as usize, - Self::FuncRef => wasmer_vm_func_ref as usize, - Self::ElemDrop => wasmer_vm_elem_drop as usize, - Self::Memory32Copy => wasmer_vm_memory32_copy as usize, - Self::ImportedMemory32Copy => wasmer_vm_imported_memory32_copy as usize, - Self::Memory32Fill => wasmer_vm_memory32_fill as usize, - Self::ImportedMemory32Fill => wasmer_vm_memory32_fill as usize, - Self::Memory32Init => wasmer_vm_memory32_init as usize, - Self::DataDrop => wasmer_vm_data_drop as usize, - Self::Probestack => wasmer_vm_probestack as usize, - Self::RaiseTrap => wasmer_vm_raise_trap as usize, - } - } - - /// 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) +/// The function pointer to a libcall +pub fn function_pointer(libcall: LibCall) -> usize { + match libcall { + LibCall::CeilF32 => wasmer_vm_f32_ceil as usize, + LibCall::CeilF64 => wasmer_vm_f64_ceil as usize, + LibCall::FloorF32 => wasmer_vm_f32_floor as usize, + LibCall::FloorF64 => wasmer_vm_f64_floor as usize, + LibCall::NearestF32 => wasmer_vm_f32_nearest as usize, + LibCall::NearestF64 => wasmer_vm_f64_nearest as usize, + LibCall::TruncF32 => wasmer_vm_f32_trunc as usize, + LibCall::TruncF64 => wasmer_vm_f64_trunc as usize, + LibCall::Memory32Size => wasmer_vm_memory32_size as usize, + LibCall::ImportedMemory32Size => wasmer_vm_imported_memory32_size as usize, + LibCall::TableCopy => wasmer_vm_table_copy as usize, + LibCall::TableInit => wasmer_vm_table_init as usize, + LibCall::TableFill => wasmer_vm_table_fill as usize, + LibCall::TableSize => wasmer_vm_table_size as usize, + LibCall::ImportedTableSize => wasmer_vm_imported_table_size as usize, + LibCall::TableGet => wasmer_vm_table_get as usize, + LibCall::ImportedTableGet => wasmer_vm_imported_table_get as usize, + LibCall::TableSet => wasmer_vm_table_set as usize, + LibCall::ImportedTableSet => wasmer_vm_imported_table_set as usize, + LibCall::TableGrow => wasmer_vm_table_grow as usize, + LibCall::ImportedTableGrow => wasmer_vm_imported_table_grow as usize, + LibCall::FuncRef => wasmer_vm_func_ref as usize, + LibCall::ElemDrop => wasmer_vm_elem_drop as usize, + LibCall::Memory32Copy => wasmer_vm_memory32_copy as usize, + LibCall::ImportedMemory32Copy => wasmer_vm_imported_memory32_copy as usize, + LibCall::Memory32Fill => wasmer_vm_memory32_fill as usize, + LibCall::ImportedMemory32Fill => wasmer_vm_memory32_fill as usize, + LibCall::Memory32Init => wasmer_vm_memory32_init as usize, + LibCall::DataDrop => wasmer_vm_data_drop as usize, + LibCall::Probestack => wasmer_vm_probestack as usize, + LibCall::RaiseTrap => wasmer_vm_raise_trap as usize, } } From 1783aa50f1fa6bcc2a966e21e19eed3aff995e12 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 24 Mar 2022 16:12:51 +0100 Subject: [PATCH 05/13] Also moved enum Trap out of vm --- Cargo.lock | 1 + lib/types/Cargo.toml | 1 + lib/types/src/lib.rs | 2 +- lib/types/src/trapcode.rs | 71 +++++++++++++++++++++++++++++++++ lib/vm/src/table.rs | 3 +- lib/vm/src/trap/mod.rs | 4 +- lib/vm/src/trap/traphandlers.rs | 71 +-------------------------------- 7 files changed, 78 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d7f9da10f8..3f0fcfaff76 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3113,6 +3113,7 @@ dependencies = [ name = "wasmer-types" version = "2.2.1" dependencies = [ + "backtrace", "enum-iterator", "indexmap", "loupe", diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index 1e85f099aae..99d67ddc076 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -18,6 +18,7 @@ indexmap = { version = "1.6", features = ["serde-1"] } rkyv = { version = "0.7.20", optional = true } loupe = { version = "0.1", features = ["enable-indexmap"] } enum-iterator = "0.7.0" +backtrace = "0.3" [features] default = ["std", "enable-serde", "enable-rkyv"] diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 27e8edf47c7..1ddb541581d 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -101,7 +101,7 @@ pub use archives::ArchivableIndexMap; pub use crate::libcalls::LibCall; pub use crate::memory::{MemoryError, MemoryStyle}; pub use crate::table::TableStyle; -pub use crate::trapcode::TrapCode; +pub use crate::trapcode::{Trap, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; /// Version number of this crate. diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 1fd02176401..c5dd6bd642b 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -3,12 +3,14 @@ //! Trap codes describing the reason for a trap. +use backtrace::Backtrace; use core::fmt::{self, Display, Formatter}; use core::str::FromStr; use loupe::MemoryUsage; #[cfg(feature = "enable-rkyv")] use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; use serde::{Deserialize, Serialize}; +use std::error::Error; use thiserror::Error; /// A trap code describing the reason for a trap. @@ -162,3 +164,72 @@ mod tests { assert_eq!("users".parse::(), Err(())); } } + +/// Stores trace message with backtrace. +#[derive(Debug)] +pub enum Trap { + /// A user-raised trap through `raise_user_trap`. + User(Box), + + /// A trap raised from the Wasm generated code + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Wasm { + /// The program counter in generated code where this trap happened. + pc: usize, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + /// Optional trapcode associated to the signal that caused the trap + signal_trap: Option, + }, + + /// A trap raised from a wasm libcall + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Lib { + /// Code of the trap. + trap_code: TrapCode, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + }, + + /// A trap indicating that the runtime was unable to allocate sufficient memory. + /// + /// Note: this trap is nondeterministic, since it depends on the host system. + OOM { + /// Native stack backtrace at the time the OOM occurred + backtrace: Backtrace, + }, +} + +impl Trap { + /// Construct a new Wasm trap with the given source location and backtrace. + /// + /// Internally saves a backtrace when constructed. + pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { + Trap::Wasm { + pc, + backtrace, + signal_trap, + } + } + + /// Construct a new Wasm trap with the given trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn lib(trap_code: TrapCode) -> Self { + let backtrace = Backtrace::new_unresolved(); + Trap::Lib { + trap_code, + backtrace, + } + } + + /// Construct a new OOM trap with the given source location and trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn oom() -> Self { + let backtrace = Backtrace::new_unresolved(); + Trap::OOM { backtrace } + } +} diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index 6a1731ea5dd..217be53aba0 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -6,7 +6,6 @@ //! `Table` is to WebAssembly tables what `LinearMemory` is to WebAssembly linear memories. use crate::func_data_registry::VMFuncRef; -use crate::trap::{Trap, TrapCode}; use crate::vmcontext::VMTableDefinition; use crate::VMExternRef; use loupe::{MemoryUsage, MemoryUsageTracker}; @@ -16,7 +15,7 @@ use std::convert::TryFrom; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use wasmer_types::{ExternRef, TableStyle, TableType, Type as ValType}; +use wasmer_types::{ExternRef, TableStyle, TableType, Trap, TrapCode, Type as ValType}; /// Trait for implementing the interface of a Wasm table. pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index 18e065afead..6473c2d1c33 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -6,8 +6,8 @@ mod traphandlers; pub use traphandlers::{ - catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, Trap, + catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, TrapHandler, TrapHandlerFn, }; pub use traphandlers::{init_traps, resume_panic}; -pub use wasmer_types::TrapCode; +pub use wasmer_types::{Trap, TrapCode}; diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index 2de040e518e..5d83ec93541 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -20,7 +20,7 @@ use std::mem::MaybeUninit; use std::ptr::{self, NonNull}; use std::sync::atomic::{compiler_fence, AtomicPtr, Ordering}; use std::sync::{Mutex, Once}; -use wasmer_types::TrapCode; +use wasmer_types::{Trap, TrapCode}; cfg_if::cfg_if! { if #[cfg(unix)] { @@ -523,75 +523,6 @@ pub unsafe fn resume_panic(payload: Box) -> ! { unwind_with(UnwindReason::Panic(payload)) } -/// Stores trace message with backtrace. -#[derive(Debug)] -pub enum Trap { - /// A user-raised trap through `raise_user_trap`. - User(Box), - - /// A trap raised from the Wasm generated code - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Wasm { - /// The program counter in generated code where this trap happened. - pc: usize, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - /// Optional trapcode associated to the signal that caused the trap - signal_trap: Option, - }, - - /// A trap raised from a wasm libcall - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Lib { - /// Code of the trap. - trap_code: TrapCode, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - }, - - /// A trap indicating that the runtime was unable to allocate sufficient memory. - /// - /// Note: this trap is nondeterministic, since it depends on the host system. - OOM { - /// Native stack backtrace at the time the OOM occurred - backtrace: Backtrace, - }, -} - -impl Trap { - /// Construct a new Wasm trap with the given source location and backtrace. - /// - /// Internally saves a backtrace when constructed. - pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { - Trap::Wasm { - pc, - backtrace, - signal_trap, - } - } - - /// Construct a new Wasm trap with the given trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn lib(trap_code: TrapCode) -> Self { - let backtrace = Backtrace::new_unresolved(); - Trap::Lib { - trap_code, - backtrace, - } - } - - /// Construct a new OOM trap with the given source location and trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn oom() -> Self { - let backtrace = Backtrace::new_unresolved(); - Trap::OOM { backtrace } - } -} - /// Call the wasm function pointed to by `callee`. /// /// * `vmctx` - the callee vmctx argument From 1f3613c881a68facee388e79fad0dcf86b6c79b5 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Fri, 25 Mar 2022 20:00:29 +0100 Subject: [PATCH 06/13] Split Artifact to have ArtifactCreate that handles only the creation part, not the run part --- Cargo.lock | 19 ++ lib/api/Cargo.toml | 1 + lib/artifact/Cargo.toml | 22 ++ lib/artifact/README.md | 20 ++ lib/artifact/src/artifact.rs | 166 +++++++++++++ lib/artifact/src/error.rs | 66 +++++ lib/artifact/src/funcbody.rs | 17 ++ lib/artifact/src/lib.rs | 55 +++++ lib/cli/Cargo.toml | 1 + lib/engine-dylib/Cargo.toml | 1 + lib/engine-dylib/src/artifact.rs | 52 ++-- lib/engine-staticlib/Cargo.toml | 1 + lib/engine-staticlib/src/artifact.rs | 15 +- lib/engine-universal/Cargo.toml | 5 +- lib/engine-universal/src/artifact.rs | 26 +- lib/engine/Cargo.toml | 1 + lib/engine/src/artifact.rs | 164 ++----------- lib/engine/src/error.rs | 55 +---- lib/engine/src/lib.rs | 8 +- lib/types/src/libcalls.rs | 324 ++++++++++++------------- lib/types/src/memory.rs | 176 +++++++------- lib/types/src/table.rs | 4 +- lib/types/src/trapcode.rs | 4 +- lib/vm/Cargo.toml | 1 + lib/vm/src/lib.rs | 31 +-- lib/vm/src/vmcontext.rs | 22 +- tests/lib/engine-dummy/Cargo.toml | 1 + tests/lib/engine-dummy/src/artifact.rs | 39 +-- 28 files changed, 728 insertions(+), 569 deletions(-) create mode 100644 lib/artifact/Cargo.toml create mode 100644 lib/artifact/README.md create mode 100644 lib/artifact/src/artifact.rs create mode 100644 lib/artifact/src/error.rs create mode 100644 lib/artifact/src/funcbody.rs create mode 100644 lib/artifact/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 3f0fcfaff76..12308a3367a 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 2427d99c3e4..10730f5548b 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 00000000000..a57fcc8a312 --- /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 00000000000..515aea657a9 --- /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 00000000000..ffa3933b7fa --- /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 00000000000..7fd6d7b6e17 --- /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 00000000000..662623baa83 --- /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 00000000000..66f91a75b41 --- /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 f149aaa6430..efd72cd48a6 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 da1219406eb..826c5d72271 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 45ae04191ac..ad4977f1bbc 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 531e143fd40..b9c4182aca4 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 400a4c91693..887e0cd74c5 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 2b413410447..b9ee48139b2 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 672eb1a76e7..64a69d360ee 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 e7015e9111b..f2683758516 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 9b29f43fb40..cee7be34085 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 -/// 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]; +/// 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 + 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 5874e5d8bea..fc377849c01 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 f364077f50a..35737b8d0ef 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 40be5dfd6f9..15fc8c9db80 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 3384492c4c4..4ad12c05afd 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 c15a9aca002..345649eed31 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 c5dd6bd642b..46812ce3576 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 93bc58558fc..7c99658350f 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 fe9bd00324c..1f3877e8bbc 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 c7e39e979b9..a4d1b3a43a9 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 61b42112454..a88e48c3f53 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 9b7523afbb6..0658c17c94c 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", - )) - } } From 0d6e13af41b114e93a2cf6ab906d73afe7a883b7 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 30 Mar 2022 13:38:57 +0200 Subject: [PATCH 07/13] Added wasm32 part for vfs file access --- lib/vfs/src/host_fs.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 961f6685be0..c2eb4908693 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -9,6 +9,8 @@ use std::fs; use std::io::{self, Read, Seek, Write}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; +#[cfg(target_arch = "wasm32")] +use std::os::wasi::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawHandle, RawHandle}; use std::path::{Path, PathBuf}; @@ -67,6 +69,31 @@ impl TryInto for FileDescriptor { } } +#[cfg(target_arch = "wasm32")] +impl TryIntoFileDescriptor for T +where + T: AsRawFd, +{ + type Error = FsError; + + fn try_into_filedescriptor(&self) -> std::result::Result { + Ok(FileDescriptor( + self.as_raw_fd() + .try_into() + .map_err(|_| FsError::InvalidFd)?, + )) + } +} + +#[cfg(target_arch = "wasm32")] +impl TryInto for FileDescriptor { + type Error = FsError; + + fn try_into(self) -> std::result::Result { + self.0.try_into().map_err(|_| FsError::InvalidFd) + } +} + #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct FileSystem; From fd2db370148fc8e89f5dde326a7fa6abc1f68526 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 12 Apr 2022 14:44:16 +0200 Subject: [PATCH 08/13] Removed cfg conditionnal compilation in link, to allow cross-link between different bitness (wasm32 vs 64bits) --- Cargo.lock | 2 +- lib/compiler-singlepass/src/codegen.rs | 2 +- lib/engine-universal/src/link.rs | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 12308a3367a..a5c039d0cb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2903,7 +2903,7 @@ dependencies = [ "target-lexicon 0.12.3", "thiserror", "wasmer-types", - "wasmparser 0.78.2", + "wasmparser 0.83.0", ] [[package]] diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index d7087a7b8b1..580eaec52b3 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -18,7 +18,7 @@ use wasmer_compiler::{ Relocation, RelocationTarget, SectionIndex, }; use wasmer_types::{ - entity::{EntityRef, PrimaryMap, SecondaryMap}, + entity::{EntityRef, PrimaryMap}, FunctionIndex, FunctionType, GlobalIndex, LocalFunctionIndex, LocalMemoryIndex, MemoryIndex, MemoryStyle, ModuleInfo, SignatureIndex, TableIndex, TableStyle, TrapCode, Type, VMBuiltinFunctionIndex, VMOffsets, diff --git a/lib/engine-universal/src/link.rs b/lib/engine-universal/src/link.rs index 051f1922714..1c8d43cdba4 100644 --- a/lib/engine-universal/src/link.rs +++ b/lib/engine-universal/src/link.rs @@ -38,17 +38,14 @@ fn apply_relocation( }; match r.kind { - #[cfg(target_pointer_width = "64")] RelocationKind::Abs8 => unsafe { let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); write_unaligned(reloc_address as *mut u64, reloc_delta); }, - #[cfg(target_pointer_width = "32")] RelocationKind::X86PCRel4 => unsafe { let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); write_unaligned(reloc_address as *mut u32, reloc_delta as _); }, - #[cfg(target_pointer_width = "64")] RelocationKind::X86PCRel8 => unsafe { let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64); write_unaligned(reloc_address as *mut u64, reloc_delta); From 5c609c34f415ee35296d8b6264c18fc1086e0c2b Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Tue, 19 Apr 2022 15:44:02 +0200 Subject: [PATCH 09/13] Splitted UniversalEngine in two, to have the Build (compilation) part separated from the Run part --- Cargo.lock | 17 +- lib/artifact/src/artifact.rs | 5 - lib/engine-dylib/src/artifact.rs | 128 +++++----- lib/engine-staticlib/src/artifact.rs | 8 +- lib/engine-universal/Cargo.toml | 5 +- lib/engine-universal/src/artifact.rs | 238 ++++++------------ lib/engine-universal/src/engine.rs | 42 ++-- lib/engine-universal/src/lib.rs | 2 - lib/engine-universal/src/link.rs | 2 +- lib/engine/src/artifact.rs | 5 + lib/universal-artifact/Cargo.toml | 28 +++ lib/universal-artifact/README.md | 20 ++ lib/universal-artifact/src/artifact.rs | 233 +++++++++++++++++ lib/universal-artifact/src/engine.rs | 52 ++++ lib/universal-artifact/src/lib.rs | 35 +++ lib/universal-artifact/src/serialize.rs | 110 ++++++++ .../src/trampoline.rs | 2 +- tests/lib/engine-dummy/src/artifact.rs | 8 +- 18 files changed, 671 insertions(+), 269 deletions(-) create mode 100644 lib/universal-artifact/Cargo.toml create mode 100644 lib/universal-artifact/README.md create mode 100644 lib/universal-artifact/src/artifact.rs create mode 100644 lib/universal-artifact/src/engine.rs create mode 100644 lib/universal-artifact/src/lib.rs create mode 100644 lib/universal-artifact/src/serialize.rs rename lib/{engine-universal => universal-artifact}/src/trampoline.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index a5c039d0cb0..588f5cddb27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3081,20 +3081,33 @@ name = "wasmer-engine-universal" version = "2.2.1" dependencies = [ "cfg-if 1.0.0", - "enum-iterator", "enumset", "leb128", "loupe", "region", "rkyv", - "wasmer-artifact", "wasmer-compiler", "wasmer-engine", + "wasmer-engine-universal-artifact", "wasmer-types", "wasmer-vm", "winapi", ] +[[package]] +name = "wasmer-engine-universal-artifact" +version = "2.2.1" +dependencies = [ + "enum-iterator", + "enumset", + "loupe", + "rkyv", + "thiserror", + "wasmer-artifact", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-integration-tests-cli" version = "2.2.1" diff --git a/lib/artifact/src/artifact.rs b/lib/artifact/src/artifact.rs index ffa3933b7fa..5b5561aec63 100644 --- a/lib/artifact/src/artifact.rs +++ b/lib/artifact/src/artifact.rs @@ -30,11 +30,6 @@ pub trait ArtifactCreate: Send + Sync + Upcastable + MemoryUsage { /// 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; diff --git a/lib/engine-dylib/src/artifact.rs b/lib/engine-dylib/src/artifact.rs index ad4977f1bbc..7ea5b848586 100644 --- a/lib/engine-dylib/src/artifact.rs +++ b/lib/engine-dylib/src/artifact.rs @@ -692,6 +692,70 @@ impl ArtifactCreate for DylibArtifact { Arc::get_mut(&mut self.metadata.compile_info.module) } + fn features(&self) -> &Features { + &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 + } + + fn memory_styles(&self) -> &PrimaryMap { + &self.metadata.compile_info.memory_styles + } + + fn table_styles(&self) -> &PrimaryMap { + &self.metadata.compile_info.table_styles + } + + /// Serialize a `DylibArtifact`. + fn serialize(&self) -> Result, SerializeError> { + Ok(std::fs::read(&self.dylib_path)?) + } + + /// Serialize a `DylibArtifact` to a portable file + #[cfg(feature = "compiler")] + fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { + let serialized = self.serialize()?; + std::fs::write(&path, serialized)?; + + /* + When you write the artifact to a new file it still has the 'Mach-O Identifier' + of the original file, and so this can causes linker issues when adding + the new file to an XCode project. + + The below code renames the ID of the file so that it references itself through + an @executable_path prefix. Basically it tells XCode to find this file + inside of the projects' list of 'linked executables'. + + You need to be running MacOS for the following to actually work though. + */ + let has_extension = path.extension().is_some(); + if has_extension && path.extension().unwrap() == "dylib" { + let filename = path.file_name().unwrap().to_str().unwrap(); + let parent_dir = path.parent().unwrap(); + let absolute_path = std::fs::canonicalize(&parent_dir) + .unwrap() + .into_os_string() + .into_string() + .unwrap(); + + Command::new("install_name_tool") + .arg("-id") + .arg(format!("@executable_path/{}", &filename)) + .arg(&filename) + .current_dir(&absolute_path) + .output()?; + } + + Ok(()) + } +} +impl Artifact for DylibArtifact { fn register_frame_info(&self) { let mut info = self.frame_info_registration.lock().unwrap(); @@ -822,70 +886,6 @@ impl ArtifactCreate for DylibArtifact { ); } - fn features(&self) -> &Features { - &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 - } - - fn memory_styles(&self) -> &PrimaryMap { - &self.metadata.compile_info.memory_styles - } - - fn table_styles(&self) -> &PrimaryMap { - &self.metadata.compile_info.table_styles - } - - /// Serialize a `DylibArtifact`. - fn serialize(&self) -> Result, SerializeError> { - Ok(std::fs::read(&self.dylib_path)?) - } - - /// Serialize a `DylibArtifact` to a portable file - #[cfg(feature = "compiler")] - fn serialize_to_file(&self, path: &Path) -> Result<(), SerializeError> { - let serialized = self.serialize()?; - std::fs::write(&path, serialized)?; - - /* - When you write the artifact to a new file it still has the 'Mach-O Identifier' - of the original file, and so this can causes linker issues when adding - the new file to an XCode project. - - The below code renames the ID of the file so that it references itself through - an @executable_path prefix. Basically it tells XCode to find this file - inside of the projects' list of 'linked executables'. - - You need to be running MacOS for the following to actually work though. - */ - let has_extension = path.extension().is_some(); - if has_extension && path.extension().unwrap() == "dylib" { - let filename = path.file_name().unwrap().to_str().unwrap(); - let parent_dir = path.parent().unwrap(); - let absolute_path = std::fs::canonicalize(&parent_dir) - .unwrap() - .into_os_string() - .into_string() - .unwrap(); - - Command::new("install_name_tool") - .arg("-id") - .arg(format!("@executable_path/{}", &filename)) - .arg(&filename) - .current_dir(&absolute_path) - .output()?; - } - - Ok(()) - } -} -impl Artifact for DylibArtifact { fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } diff --git a/lib/engine-staticlib/src/artifact.rs b/lib/engine-staticlib/src/artifact.rs index 887e0cd74c5..c27e711fb8d 100644 --- a/lib/engine-staticlib/src/artifact.rs +++ b/lib/engine-staticlib/src/artifact.rs @@ -450,10 +450,6 @@ impl ArtifactCreate for StaticlibArtifact { Arc::get_mut(&mut self.metadata.compile_info.module) } - fn register_frame_info(&self) { - // Do nothing for now - } - fn features(&self) -> &Features { &self.metadata.compile_info.features } @@ -480,6 +476,10 @@ impl ArtifactCreate for StaticlibArtifact { } } impl Artifact for StaticlibArtifact { + fn register_frame_info(&self) { + // Do nothing for now + } + fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } diff --git a/lib/engine-universal/Cargo.toml b/lib/engine-universal/Cargo.toml index b9ee48139b2..ea151ca946e 100644 --- a/lib/engine-universal/Cargo.toml +++ b/lib/engine-universal/Cargo.toml @@ -11,7 +11,9 @@ readme = "README.md" edition = "2018" [dependencies] -wasmer-artifact = { path = "../artifact", version = "=2.2.1" } +wasmer-engine-universal-artifact = { path = "../universal-artifact", version = "=2.2.1", features = [ + "compiler", +] } wasmer-types = { path = "../types", version = "=2.2.1", features = [ "enable-rkyv", ] } @@ -27,7 +29,6 @@ leb128 = "0.2" rkyv = "0.7.20" loupe = "0.1" enumset = "1.0" -enum-iterator = "0.7.0" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] region = { version = "3.0" } diff --git a/lib/engine-universal/src/artifact.rs b/lib/engine-universal/src/artifact.rs index 64a69d360ee..e8abc5e0bdb 100644 --- a/lib/engine-universal/src/artifact.rs +++ b/lib/engine-universal/src/artifact.rs @@ -1,26 +1,22 @@ -//! Define `UniversalArtifact` to allow compiling and instantiating to be -//! done as separate steps. +//! Define `UniversalArtifact`, based on `UniversalArtifactBuild` +//! to allow compiling and instantiating to be done as separate steps. use crate::engine::{UniversalEngine, UniversalEngineInner}; use crate::link::link_module; -#[cfg(feature = "compiler")] -use crate::serialize::SerializableCompilation; -use crate::serialize::SerializableModule; -use crate::trampoline::{libcall_trampoline_len, make_libcall_trampolines}; 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}; +use wasmer_compiler::ModuleEnvironment; +use wasmer_compiler::{CompileError, CpuFeature, Features, Triple}; use wasmer_engine::{ register_frame_info, Artifact, DeserializeError, FunctionExtent, GlobalFrameInfoRegistration, MetadataHeader, SerializeError, }; #[cfg(feature = "compiler")] use wasmer_engine::{Engine, Tunables}; +use wasmer_engine_universal_artifact::ArtifactCreate; +use wasmer_engine_universal_artifact::{SerializableModule, UniversalArtifactBuild}; use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::{ FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, @@ -34,7 +30,7 @@ use wasmer_vm::{ /// A compiled wasm module, ready to be instantiated. #[derive(MemoryUsage)] pub struct UniversalArtifact { - serializable: SerializableModule, + artifact: UniversalArtifactBuild, finished_functions: BoxedSlice, #[loupe(skip)] finished_function_call_trampolines: BoxedSlice, @@ -46,14 +42,7 @@ pub struct UniversalArtifact { } impl UniversalArtifact { - const MAGIC_HEADER: &'static [u8; 16] = b"wasmer-universal"; - - /// Check if the provided bytes look like a serialized `UniversalArtifact`. - pub fn is_deserializable(bytes: &[u8]) -> bool { - bytes.starts_with(Self::MAGIC_HEADER) - } - - /// Compile a data buffer into a `UniversalArtifact`, which may then be instantiated. + /// Compile a data buffer into a `UniversalArtifactBuild`, which may then be instantiated. #[cfg(feature = "compiler")] pub fn new( engine: &UniversalEngine, @@ -62,17 +51,8 @@ impl UniversalArtifact { ) -> Result { let environ = ModuleEnvironment::new(); let mut inner_engine = engine.inner_mut(); - let features = inner_engine.features(); - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - - let compiler = inner_engine.compiler()?; - - // We try to apply the middleware first - let mut module = translation.module; - let middlewares = compiler.get_middlewares(); - middlewares.apply_on_module_info(&mut module); - + let module = translation.module; let memory_styles: PrimaryMap = module .memories .values() @@ -84,65 +64,18 @@ impl UniversalArtifact { .map(|table_type| tunables.table_style(table_type)) .collect(); - let compile_info = CompileModuleInfo { - module: Arc::new(module), - features: features.clone(), + let artifact = UniversalArtifactBuild::new( + inner_engine.builder_mut(), + data, + engine.target(), memory_styles, table_styles, - }; - - // Compile the Module - let compilation = compiler.compile_module( - &engine.target(), - &compile_info, - // SAFETY: Calling `unwrap` is correct since - // `environ.translate()` above will write some data into - // `module_translation_state`. - translation.module_translation_state.as_ref().unwrap(), - translation.function_body_inputs, )?; - let function_call_trampolines = compilation.get_function_call_trampolines(); - let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); - - let data_initializers = translation - .data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(); - let frame_infos = compilation.get_frame_info(); - - // Synthesize a custom section to hold the libcall trampolines. - let mut custom_sections = compilation.get_custom_sections(); - let mut custom_section_relocations = compilation.get_custom_section_relocations(); - let libcall_trampolines_section = make_libcall_trampolines(engine.target()); - custom_section_relocations.push(libcall_trampolines_section.relocations.clone()); - let libcall_trampolines = custom_sections.push(libcall_trampolines_section); - let libcall_trampoline_len = libcall_trampoline_len(engine.target()) as u32; - - let serializable_compilation = SerializableCompilation { - function_bodies: compilation.get_function_bodies(), - function_relocations: compilation.get_relocations(), - function_frame_info: frame_infos, - function_call_trampolines, - dynamic_function_trampolines, - custom_sections, - custom_section_relocations, - debug: compilation.get_debug(), - libcall_trampolines, - libcall_trampoline_len, - }; - let serializable = SerializableModule { - compilation: serializable_compilation, - compile_info, - data_initializers, - cpu_features: engine.target().cpu_features().as_u64(), - }; - Self::from_parts(&mut inner_engine, serializable) + Self::from_parts(&mut inner_engine, artifact) } - /// Compile a data buffer into a `UniversalArtifact`, which may then be instantiated. + /// Compile a data buffer into a `UniversalArtifactBuild`, which may then be instantiated. #[cfg(not(feature = "compiler"))] pub fn new(_engine: &UniversalEngine, _data: &[u8]) -> Result { Err(CompileError::Codegen( @@ -150,72 +83,71 @@ impl UniversalArtifact { )) } - /// Deserialize a UniversalArtifact + /// Deserialize a UniversalArtifactBuild /// /// # Safety /// This function is unsafe because rkyv reads directly without validating /// the data. pub unsafe fn deserialize( - universal: &UniversalEngine, + engine: &UniversalEngine, bytes: &[u8], ) -> Result { - if !Self::is_deserializable(bytes) { + if !UniversalArtifactBuild::is_deserializable(bytes) { return Err(DeserializeError::Incompatible( "The provided bytes are not wasmer-universal".to_string(), )); } - let bytes = &bytes[Self::MAGIC_HEADER.len()..]; + let bytes = &bytes[UniversalArtifactBuild::MAGIC_HEADER.len()..]; let metadata_len = MetadataHeader::parse(bytes)?; let metadata_slice: &[u8] = &bytes[MetadataHeader::LEN..][..metadata_len]; let serializable = SerializableModule::deserialize(metadata_slice)?; - Self::from_parts(&mut universal.inner_mut(), serializable) - .map_err(DeserializeError::Compiler) + let artifact = UniversalArtifactBuild::from_serializable(serializable); + let mut inner_engine = engine.inner_mut(); + Self::from_parts(&mut inner_engine, artifact).map_err(DeserializeError::Compiler) } - /// Construct a `UniversalArtifact` from component parts. + /// Construct a `UniversalArtifactBuild` from component parts. pub fn from_parts( - inner_engine: &mut UniversalEngineInner, - serializable: SerializableModule, + engine_inner: &mut UniversalEngineInner, + artifact: UniversalArtifactBuild, ) -> Result { let ( finished_functions, finished_function_call_trampolines, finished_dynamic_function_trampolines, custom_sections, - ) = inner_engine.allocate( - &serializable.compile_info.module, - &serializable.compilation.function_bodies, - &serializable.compilation.function_call_trampolines, - &serializable.compilation.dynamic_function_trampolines, - &serializable.compilation.custom_sections, + ) = engine_inner.allocate( + artifact.module_ref(), + artifact.get_function_bodies_ref(), + artifact.get_function_call_trampolines_ref(), + artifact.get_dynamic_function_trampolines_ref(), + artifact.get_custom_sections_ref(), )?; link_module( - &serializable.compile_info.module, + artifact.module_ref(), &finished_functions, - serializable.compilation.function_relocations.clone(), + artifact.get_function_relocations().clone(), &custom_sections, - &serializable.compilation.custom_section_relocations, - serializable.compilation.libcall_trampolines, - serializable.compilation.libcall_trampoline_len as usize, + artifact.get_custom_section_relocations_ref(), + artifact.get_libcall_trampolines(), + artifact.get_libcall_trampoline_len(), ); // Compute indices into the shared signature table. let signatures = { - let signature_registry = inner_engine.signatures(); - serializable - .compile_info - .module + let signature_registry = engine_inner.signatures(); + artifact + .module() .signatures .values() .map(|sig| signature_registry.register(sig)) .collect::>() }; - let eh_frame = match &serializable.compilation.debug { + let eh_frame = match artifact.get_debug_ref() { Some(debug) => { - let eh_frame_section_size = serializable.compilation.custom_sections - [debug.eh_frame] + let eh_frame_section_size = artifact.get_custom_sections_ref()[debug.eh_frame] .bytes .len(); let eh_frame_section_pointer = custom_sections[debug.eh_frame]; @@ -227,9 +159,9 @@ impl UniversalArtifact { }; // Make all code compiled thus far executable. - inner_engine.publish_compiled_code(); + engine_inner.publish_compiled_code(); - inner_engine.publish_eh_frame(eh_frame)?; + engine_inner.publish_eh_frame(eh_frame)?; let finished_function_lengths = finished_functions .values() @@ -246,10 +178,10 @@ impl UniversalArtifact { let finished_dynamic_function_trampolines = finished_dynamic_function_trampolines.into_boxed_slice(); let signatures = signatures.into_boxed_slice(); - let func_data_registry = inner_engine.func_data().clone(); + let func_data_registry = engine_inner.func_data().clone(); Ok(Self { - serializable, + artifact, finished_functions, finished_function_call_trampolines, finished_dynamic_function_trampolines, @@ -259,28 +191,55 @@ impl UniversalArtifact { func_data_registry, }) } - /// Get the default extension when serializing this artifact - pub fn get_default_extension(_triple: &Triple) -> &'static str { - // `.wasmu` is the default extension for all the triples. It - // stands for “Wasm Universal”. - "wasmu" + pub fn get_default_extension(triple: &Triple) -> &'static str { + UniversalArtifactBuild::get_default_extension(triple) + } + /// Check if the provided bytes look like a serialized `UniversalArtifactBuild`. + pub fn is_deserializable(bytes: &[u8]) -> bool { + UniversalArtifactBuild::is_deserializable(bytes) } } impl ArtifactCreate for UniversalArtifact { fn module(&self) -> Arc { - self.serializable.compile_info.module.clone() + self.artifact.module() } fn module_ref(&self) -> &ModuleInfo { - &self.serializable.compile_info.module + self.artifact.module_ref() } fn module_mut(&mut self) -> Option<&mut ModuleInfo> { - Arc::get_mut(&mut self.serializable.compile_info.module) + self.artifact.module_mut() + } + + fn features(&self) -> &Features { + self.artifact.features() + } + + fn cpu_features(&self) -> EnumSet { + self.artifact.cpu_features() + } + + fn data_initializers(&self) -> &[OwnedDataInitializer] { + self.artifact.data_initializers() } + fn memory_styles(&self) -> &PrimaryMap { + self.artifact.memory_styles() + } + + fn table_styles(&self) -> &PrimaryMap { + self.artifact.table_styles() + } + + fn serialize(&self) -> Result, SerializeError> { + self.artifact.serialize() + } +} + +impl Artifact for UniversalArtifact { fn register_frame_info(&self) { let mut info = self.frame_info_registration.lock().unwrap(); @@ -297,47 +256,14 @@ impl ArtifactCreate for UniversalArtifact { .collect::>() .into_boxed_slice(); - let frame_infos = &self.serializable.compilation.function_frame_info; + let frame_infos = self.artifact.get_frame_info_ref(); *info = register_frame_info( - self.serializable.compile_info.module.clone(), + self.artifact.module(), &finished_function_extents, frame_infos.clone(), ); } - fn features(&self) -> &Features { - &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 - } - - fn memory_styles(&self) -> &PrimaryMap { - &self.serializable.compile_info.memory_styles - } - - fn table_styles(&self) -> &PrimaryMap { - &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 } diff --git a/lib/engine-universal/src/engine.rs b/lib/engine-universal/src/engine.rs index ecc34931212..72e5b18f22a 100644 --- a/lib/engine-universal/src/engine.rs +++ b/lib/engine-universal/src/engine.rs @@ -1,6 +1,7 @@ //! Universal compilation. -use crate::{CodeMemory, UniversalArtifact}; +use crate::CodeMemory; +use crate::UniversalArtifact; use loupe::MemoryUsage; use std::sync::{Arc, Mutex}; #[cfg(feature = "compiler")] @@ -9,6 +10,7 @@ use wasmer_compiler::{ CompileError, CustomSection, CustomSectionProtection, FunctionBody, SectionIndex, Target, }; use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, FunctionExtent, Tunables}; +use wasmer_engine_universal_artifact::UniversalEngineBuilder; use wasmer_types::entity::PrimaryMap; use wasmer_types::{ Features, FunctionIndex, FunctionType, LocalFunctionIndex, ModuleInfo, SignatureIndex, @@ -33,11 +35,10 @@ impl UniversalEngine { pub fn new(compiler: Box, target: Target, features: Features) -> Self { Self { inner: Arc::new(Mutex::new(UniversalEngineInner { - compiler: Some(compiler), + builder: UniversalEngineBuilder::new(Some(compiler), features), code_memory: vec![], signatures: SignatureRegistry::new(), func_data: Arc::new(FuncDataRegistry::new()), - features, })), target: Arc::new(target), engine_id: EngineId::default(), @@ -60,12 +61,10 @@ impl UniversalEngine { pub fn headless() -> Self { Self { inner: Arc::new(Mutex::new(UniversalEngineInner { - #[cfg(feature = "compiler")] - compiler: None, + builder: UniversalEngineBuilder::new(None, Features::default()), code_memory: vec![], signatures: SignatureRegistry::new(), func_data: Arc::new(FuncDataRegistry::new()), - features: Features::default(), })), target: Arc::new(Target::default()), engine_id: EngineId::default(), @@ -149,11 +148,8 @@ impl Engine for UniversalEngine { /// The inner contents of `UniversalEngine` #[derive(MemoryUsage)] pub struct UniversalEngineInner { - /// The compiler - #[cfg(feature = "compiler")] - compiler: Option>, - /// The features to compile the Wasm module with - features: Features, + /// The builder (include compiler and cpu features) + builder: UniversalEngineBuilder, /// The code memory is responsible of publishing the compiled /// functions to memory. code_memory: Vec, @@ -168,32 +164,22 @@ pub struct UniversalEngineInner { impl UniversalEngineInner { /// Gets the compiler associated to this engine. - #[cfg(feature = "compiler")] pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> { - if self.compiler.is_none() { - return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string())); - } - Ok(&**self.compiler.as_ref().unwrap()) + self.builder.compiler() } /// Validate the module - #[cfg(feature = "compiler")] pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> { - self.compiler()?.validate_module(self.features(), data) - } - - /// Validate the module - #[cfg(not(feature = "compiler"))] - pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> { - Err(CompileError::Validate( - "The UniversalEngine is not compiled with compiler support, which is required for validating" - .to_string(), - )) + self.builder.validate(data) } /// The Wasm features pub fn features(&self) -> &Features { - &self.features + self.builder.features() + } + + pub fn builder_mut(&mut self) -> &mut UniversalEngineBuilder { + &mut self.builder } /// Allocate compiled functions into memory diff --git a/lib/engine-universal/src/lib.rs b/lib/engine-universal/src/lib.rs index b552dae2a2d..68afde89054 100644 --- a/lib/engine-universal/src/lib.rs +++ b/lib/engine-universal/src/lib.rs @@ -28,8 +28,6 @@ mod builder; mod code_memory; mod engine; mod link; -mod serialize; -mod trampoline; mod unwind; pub use crate::artifact::UniversalArtifact; diff --git a/lib/engine-universal/src/link.rs b/lib/engine-universal/src/link.rs index 1c8d43cdba4..757c8c1867e 100644 --- a/lib/engine-universal/src/link.rs +++ b/lib/engine-universal/src/link.rs @@ -1,9 +1,9 @@ //! Linking for Universal-compiled code. -use crate::trampoline::get_libcall_trampoline; use std::ptr::{read_unaligned, write_unaligned}; use wasmer_compiler::{Relocation, RelocationKind, RelocationTarget, Relocations, SectionIndex}; use wasmer_engine::FunctionExtent; +use wasmer_engine_universal_artifact::get_libcall_trampoline; use wasmer_types::entity::PrimaryMap; use wasmer_types::{LocalFunctionIndex, ModuleInfo}; use wasmer_vm::libcalls::function_pointer; diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index cee7be34085..4754990d801 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -21,6 +21,11 @@ use wasmer_vm::{ /// 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 + ArtifactCreate { + /// 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 functions allocated in memory or this `Artifact` /// ready to be run. fn finished_functions(&self) -> &BoxedSlice; diff --git a/lib/universal-artifact/Cargo.toml b/lib/universal-artifact/Cargo.toml new file mode 100644 index 00000000000..fe3ec1846e0 --- /dev/null +++ b/lib/universal-artifact/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "wasmer-engine-universal-artifact" +version = "2.2.1" +description = "Wasmer Engine Universal 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-artifact = { path = "../artifact", version = "=2.2.1" } +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" +rkyv = "0.7.20" +enum-iterator = "0.7.0" + +[features] +compiler = [] + +[badges] +maintenance = { status = "actively-developed" } + diff --git a/lib/universal-artifact/README.md b/lib/universal-artifact/README.md new file mode 100644 index 00000000000..c02f1c11bc3 --- /dev/null +++ b/lib/universal-artifact/README.md @@ -0,0 +1,20 @@ +# `wasmer-engine-universal-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 generating 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/universal-artifact/src/artifact.rs b/lib/universal-artifact/src/artifact.rs new file mode 100644 index 00000000000..a28e0836513 --- /dev/null +++ b/lib/universal-artifact/src/artifact.rs @@ -0,0 +1,233 @@ +//! Define `UniversalArtifactBuild` to allow compiling and instantiating to be +//! done as separate steps. + +use crate::serialize::SerializableCompilation; +use crate::serialize::SerializableModule; +#[cfg(feature = "compiler")] +use crate::trampoline::{libcall_trampoline_len, make_libcall_trampolines}; +use crate::{ArtifactCreate, UniversalEngineBuilder}; +use enumset::EnumSet; +use loupe::MemoryUsage; +use std::mem; +use std::sync::Arc; +use wasmer_artifact::{MetadataHeader, SerializeError}; +use wasmer_compiler::{ + CompileError, CompileModuleInfo, CompiledFunctionFrameInfo, CpuFeature, CustomSection, Dwarf, + Features, FunctionBody, ModuleEnvironment, ModuleMiddlewareChain, Relocation, SectionIndex, + Target, Triple, +}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{ + FunctionIndex, LocalFunctionIndex, MemoryIndex, MemoryStyle, ModuleInfo, OwnedDataInitializer, + SignatureIndex, TableIndex, TableStyle, +}; + +/// A compiled wasm module, ready to be instantiated. +#[derive(MemoryUsage)] +pub struct UniversalArtifactBuild { + serializable: SerializableModule, +} + +impl UniversalArtifactBuild { + /// Header signature for wasmu binary + pub const MAGIC_HEADER: &'static [u8; 16] = b"wasmer-universal"; + + /// Check if the provided bytes look like a serialized `UniversalArtifactBuild`. + pub fn is_deserializable(bytes: &[u8]) -> bool { + bytes.starts_with(Self::MAGIC_HEADER) + } + + /// Compile a data buffer into a `UniversalArtifactBuild`, which may then be instantiated. + #[cfg(feature = "compiler")] + pub fn new( + inner_engine: &mut UniversalEngineBuilder, + data: &[u8], + target: &Target, + memory_styles: PrimaryMap, + table_styles: PrimaryMap, + ) -> Result { + let environ = ModuleEnvironment::new(); + let features = inner_engine.features(); + + let translation = environ.translate(data).map_err(CompileError::Wasm)?; + + let compiler = inner_engine.compiler()?; + + // We try to apply the middleware first + let mut module = translation.module; + let middlewares = compiler.get_middlewares(); + middlewares.apply_on_module_info(&mut module); + + let compile_info = CompileModuleInfo { + module: Arc::new(module), + features: features.clone(), + memory_styles, + table_styles, + }; + + // Compile the Module + let compilation = compiler.compile_module( + target, + &compile_info, + // SAFETY: Calling `unwrap` is correct since + // `environ.translate()` above will write some data into + // `module_translation_state`. + translation.module_translation_state.as_ref().unwrap(), + translation.function_body_inputs, + )?; + let function_call_trampolines = compilation.get_function_call_trampolines(); + let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); + + let data_initializers = translation + .data_initializers + .iter() + .map(OwnedDataInitializer::new) + .collect::>() + .into_boxed_slice(); + + let frame_infos = compilation.get_frame_info(); + + // Synthesize a custom section to hold the libcall trampolines. + let mut custom_sections = compilation.get_custom_sections(); + let mut custom_section_relocations = compilation.get_custom_section_relocations(); + let libcall_trampolines_section = make_libcall_trampolines(target); + custom_section_relocations.push(libcall_trampolines_section.relocations.clone()); + let libcall_trampolines = custom_sections.push(libcall_trampolines_section); + let libcall_trampoline_len = libcall_trampoline_len(target) as u32; + + let serializable_compilation = SerializableCompilation { + function_bodies: compilation.get_function_bodies(), + function_relocations: compilation.get_relocations(), + function_frame_info: frame_infos, + function_call_trampolines, + dynamic_function_trampolines, + custom_sections, + custom_section_relocations, + debug: compilation.get_debug(), + libcall_trampolines, + libcall_trampoline_len, + }; + let serializable = SerializableModule { + compilation: serializable_compilation, + compile_info, + data_initializers, + cpu_features: target.cpu_features().as_u64(), + }; + Ok(Self { serializable }) + } + + /// Compile a data buffer into a `UniversalArtifactBuild`, which may then be instantiated. + #[cfg(not(feature = "compiler"))] + pub fn new(_engine: &UniversalEngineBuilder, _data: &[u8]) -> Result { + Err(CompileError::Codegen( + "Compilation is not enabled in the engine".to_string(), + )) + } + + /// Create a new UniversalArtifactBuild from a SerializableModule + pub fn from_serializable(serializable: SerializableModule) -> Self { + Self { serializable } + } + + /// Get the default extension when serializing this artifact + pub fn get_default_extension(_triple: &Triple) -> &'static str { + // `.wasmu` is the default extension for all the triples. It + // stands for “Wasm Universal”. + "wasmu" + } + + /// Get Functions Bodies ref + pub fn get_function_bodies_ref(&self) -> &PrimaryMap { + &self.serializable.compilation.function_bodies + } + + /// Get Functions Call Trampolines ref + pub fn get_function_call_trampolines_ref(&self) -> &PrimaryMap { + &self.serializable.compilation.function_call_trampolines + } + + /// Get Dynamic Functions Call Trampolines ref + pub fn get_dynamic_function_trampolines_ref(&self) -> &PrimaryMap { + &self.serializable.compilation.dynamic_function_trampolines + } + + /// Get Custom Sections ref + pub fn get_custom_sections_ref(&self) -> &PrimaryMap { + &self.serializable.compilation.custom_sections + } + + /// Get Function Relocations + pub fn get_function_relocations(&self) -> PrimaryMap> { + self.serializable.compilation.function_relocations.clone() + } + + /// Get Function Relocations ref + pub fn get_custom_section_relocations_ref(&self) -> &PrimaryMap> { + &self.serializable.compilation.custom_section_relocations + } + + /// Get LibCall Trampoline Section Index + pub fn get_libcall_trampolines(&self) -> SectionIndex { + self.serializable.compilation.libcall_trampolines + } + + /// Get LibCall Trampoline Length + pub fn get_libcall_trampoline_len(&self) -> usize { + self.serializable.compilation.libcall_trampoline_len as usize + } + + /// Get Debug optional Dwarf ref + pub fn get_debug_ref(&self) -> &Option { + &self.serializable.compilation.debug + } + + /// Get Function Relocations ref + pub fn get_frame_info_ref(&self) -> &PrimaryMap { + &self.serializable.compilation.function_frame_info + } +} + +impl ArtifactCreate for UniversalArtifactBuild { + fn module(&self) -> Arc { + self.serializable.compile_info.module.clone() + } + + fn module_ref(&self) -> &ModuleInfo { + &self.serializable.compile_info.module + } + + fn module_mut(&mut self) -> Option<&mut ModuleInfo> { + Arc::get_mut(&mut self.serializable.compile_info.module) + } + + fn features(&self) -> &Features { + &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 + } + + fn memory_styles(&self) -> &PrimaryMap { + &self.serializable.compile_info.memory_styles + } + + fn table_styles(&self) -> &PrimaryMap { + &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) + } +} diff --git a/lib/universal-artifact/src/engine.rs b/lib/universal-artifact/src/engine.rs new file mode 100644 index 00000000000..f2225e0ddaf --- /dev/null +++ b/lib/universal-artifact/src/engine.rs @@ -0,0 +1,52 @@ +//! Universal compilation. + +use loupe::MemoryUsage; +use wasmer_compiler::CompileError; +#[cfg(feature = "compiler")] +use wasmer_compiler::Compiler; +use wasmer_types::Features; + +/// The Builder contents of `UniversalEngine` +#[derive(MemoryUsage)] +pub struct UniversalEngineBuilder { + /// The compiler + #[cfg(feature = "compiler")] + compiler: Option>, + /// The features to compile the Wasm module with + features: Features, +} + +impl UniversalEngineBuilder { + /// Create a new builder with pre-made components + pub fn new(compiler: Option>, features: Features) -> Self { + UniversalEngineBuilder { compiler, features } + } + /// Gets the compiler associated to this engine. + #[cfg(feature = "compiler")] + pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> { + if self.compiler.is_none() { + return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string())); + } + Ok(&**self.compiler.as_ref().unwrap()) + } + + /// Validate the module + #[cfg(feature = "compiler")] + pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> { + self.compiler()?.validate_module(self.features(), data) + } + + /// Validate the module + #[cfg(not(feature = "compiler"))] + pub fn validate<'data>(&self, _data: &'data [u8]) -> Result<(), CompileError> { + Err(CompileError::Validate( + "The UniversalEngine is not compiled with compiler support, which is required for validating" + .to_string(), + )) + } + + /// The Wasm features + pub fn features(&self) -> &Features { + &self.features + } +} diff --git a/lib/universal-artifact/src/lib.rs b/lib/universal-artifact/src/lib.rs new file mode 100644 index 00000000000..952d6d3bd84 --- /dev/null +++ b/lib/universal-artifact/src/lib.rs @@ -0,0 +1,35 @@ +//! 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 engine; +mod serialize; +mod trampoline; + +pub use crate::artifact::UniversalArtifactBuild; +pub use crate::engine::UniversalEngineBuilder; +pub use crate::serialize::SerializableModule; +pub use crate::trampoline::*; +pub use wasmer_artifact::{ArtifactCreate, MetadataHeader, Upcastable}; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/universal-artifact/src/serialize.rs b/lib/universal-artifact/src/serialize.rs new file mode 100644 index 00000000000..2840dd76e4c --- /dev/null +++ b/lib/universal-artifact/src/serialize.rs @@ -0,0 +1,110 @@ +use loupe::MemoryUsage; +use rkyv::{ + archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer, + ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize, + Serialize as RkyvSerialize, +}; +use wasmer_artifact::{DeserializeError, SerializeError}; +use wasmer_compiler::{ + CompileModuleInfo, CompiledFunctionFrameInfo, CustomSection, Dwarf, FunctionBody, Relocation, + SectionIndex, +}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex}; + +/// The compilation related data for a serialized modules +#[derive(MemoryUsage, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct SerializableCompilation { + pub function_bodies: PrimaryMap, + pub function_relocations: PrimaryMap>, + pub function_frame_info: PrimaryMap, + pub function_call_trampolines: PrimaryMap, + pub dynamic_function_trampolines: PrimaryMap, + pub custom_sections: PrimaryMap, + pub custom_section_relocations: PrimaryMap>, + // The section indices corresponding to the Dwarf debug info + pub debug: Option, + // Custom section containing libcall trampolines. + pub libcall_trampolines: SectionIndex, + // Length of each libcall trampoline. + pub libcall_trampoline_len: u32, +} + +/// Serializable struct that is able to serialize from and to +/// a `UniversalArtifactInfo`. +#[derive(MemoryUsage, Archive, RkyvDeserialize, RkyvSerialize)] +pub struct SerializableModule { + /// The main serializable compilation object + pub compilation: SerializableCompilation, + /// Compilation informations + pub compile_info: CompileModuleInfo, + /// Datas initializers + pub data_initializers: Box<[OwnedDataInitializer]>, + /// CPU Feature flags for this compilation + pub cpu_features: u64, +} + +fn to_serialize_error(err: impl std::error::Error) -> SerializeError { + SerializeError::Generic(format!("{}", err)) +} + +impl SerializableModule { + /// Serialize a Module into bytes + /// The bytes will have the following format: + /// RKYV serialization (any length) + POS (8 bytes) + pub fn serialize(&self) -> Result, SerializeError> { + let mut serializer = AllocSerializer::<4096>::default(); + let pos = serializer + .serialize_value(self) + .map_err(to_serialize_error)? as u64; + let mut serialized_data = serializer.into_serializer().into_inner(); + serialized_data.extend_from_slice(&pos.to_le_bytes()); + Ok(serialized_data.to_vec()) + } + + /// Deserialize a Module from a slice. + /// The slice must have the following format: + /// RKYV serialization (any length) + POS (8 bytes) + /// + /// # Safety + /// + /// This method is unsafe since it deserializes data directly + /// from memory. + /// Right now we are not doing any extra work for validation, but + /// `rkyv` has an option to do bytecheck on the serialized data before + /// serializing (via `rkyv::check_archived_value`). + pub unsafe fn deserialize(metadata_slice: &[u8]) -> Result { + let archived = Self::archive_from_slice(metadata_slice)?; + Self::deserialize_from_archive(archived) + } + + /// # Safety + /// + /// This method is unsafe. + /// Please check `SerializableModule::deserialize` for more details. + unsafe fn archive_from_slice<'a>( + metadata_slice: &'a [u8], + ) -> Result<&'a ArchivedSerializableModule, DeserializeError> { + if metadata_slice.len() < 8 { + return Err(DeserializeError::Incompatible( + "invalid serialized data".into(), + )); + } + let mut pos: [u8; 8] = Default::default(); + pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]); + let pos: u64 = u64::from_le_bytes(pos); + Ok(archived_value::( + &metadata_slice[..metadata_slice.len() - 8], + pos as usize, + )) + } + + /// Deserialize a compilation module from an archive + pub fn deserialize_from_archive( + archived: &ArchivedSerializableModule, + ) -> Result { + let mut deserializer = SharedDeserializeMap::new(); + RkyvDeserialize::deserialize(archived, &mut deserializer) + .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e))) + } +} diff --git a/lib/engine-universal/src/trampoline.rs b/lib/universal-artifact/src/trampoline.rs similarity index 98% rename from lib/engine-universal/src/trampoline.rs rename to lib/universal-artifact/src/trampoline.rs index 7c4c62b9729..65932be48ae 100644 --- a/lib/engine-universal/src/trampoline.rs +++ b/lib/universal-artifact/src/trampoline.rs @@ -8,7 +8,7 @@ use wasmer_compiler::{ Architecture, CustomSection, CustomSectionProtection, Relocation, RelocationKind, RelocationTarget, SectionBody, Target, }; -use wasmer_vm::libcalls::LibCall; +use wasmer_types::LibCall; // SystemV says that both x16 and x17 are available as intra-procedural scratch // registers but Apple's ABI restricts us to use x17. diff --git a/tests/lib/engine-dummy/src/artifact.rs b/tests/lib/engine-dummy/src/artifact.rs index 0658c17c94c..d6a061c95a3 100644 --- a/tests/lib/engine-dummy/src/artifact.rs +++ b/tests/lib/engine-dummy/src/artifact.rs @@ -207,10 +207,6 @@ impl ArtifactCreate for DummyArtifact { Arc::get_mut(&mut self.metadata.module) } - fn register_frame_info(&self) { - // Do nothing, since functions are not generated for the dummy engine - } - fn features(&self) -> &Features { &self.metadata.features } @@ -249,6 +245,10 @@ impl ArtifactCreate for DummyArtifact { } } impl Artifact for DummyArtifact { + fn register_frame_info(&self) { + // Do nothing, since functions are not generated for the dummy engine + } + fn finished_functions(&self) -> &BoxedSlice { &self.finished_functions } From 63538477f25fcbd58b7f4dbd950cac7bbbbad611 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Wed, 20 Apr 2022 11:15:56 +0200 Subject: [PATCH 10/13] Moved is_wasm function from api to types crate --- lib/api/src/js/mod.rs | 3 +-- lib/api/src/sys/mod.rs | 3 +-- lib/api/src/sys/utils.rs | 4 ---- lib/types/src/lib.rs | 3 +++ lib/{api/src/js => types/src}/utils.rs | 0 5 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 lib/api/src/sys/utils.rs rename lib/{api/src/js => types/src}/utils.rs (100%) diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index 95e7d15ccec..66c107c8cc5 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -41,7 +41,6 @@ mod resolver; mod store; mod trap; mod types; -mod utils; mod wasm_bindgen_polyfill; /// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`. @@ -75,8 +74,8 @@ pub use crate::js::types::{ TableType, Val, ValType, }; pub use crate::js::types::{Val as Value, ValType as Type}; -pub use crate::js::utils::is_wasm; +pub use wasmer_types::is_wasm; pub use wasmer_types::{ Atomically, Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, MemoryView, Pages, ValueType, WASM_MAX_PAGES, WASM_MIN_PAGES, WASM_PAGE_SIZE, diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index 157a26a4a64..ab7d82e3f7d 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -10,7 +10,6 @@ mod ptr; mod store; mod tunables; mod types; -mod utils; /// Implement [`WasmerEnv`] for your type with `#[derive(WasmerEnv)]`. /// @@ -45,7 +44,6 @@ pub use crate::sys::types::{ TableType, Val, ValType, }; pub use crate::sys::types::{Val as Value, ValType as Type}; -pub use crate::sys::utils::is_wasm; pub use target_lexicon::{Architecture, CallingConvention, OperatingSystem, Triple, HOST}; #[cfg(feature = "compiler")] pub use wasmer_compiler::{ @@ -59,6 +57,7 @@ pub use wasmer_engine::{ ChainableNamedResolver, DeserializeError, Engine, Export, FrameInfo, LinkError, NamedResolver, NamedResolverChain, Resolver, RuntimeError, SerializeError, Tunables, }; +pub use wasmer_types::is_wasm; #[cfg(feature = "experimental-reference-types-extern-ref")] pub use wasmer_types::ExternRef; pub use wasmer_types::{ diff --git a/lib/api/src/sys/utils.rs b/lib/api/src/sys/utils.rs deleted file mode 100644 index b3f067f3334..00000000000 --- a/lib/api/src/sys/utils.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Check if the provided bytes are wasm-like -pub fn is_wasm(bytes: impl AsRef<[u8]>) -> bool { - bytes.as_ref().starts_with(b"\0asm") -} diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 1ddb541581d..9f443e708ed 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -68,6 +68,7 @@ mod table; mod trapcode; mod types; mod units; +mod utils; mod values; mod vmoffsets; @@ -104,5 +105,7 @@ pub use crate::table::TableStyle; pub use crate::trapcode::{Trap, TrapCode}; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; +pub use crate::utils::is_wasm; + /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/api/src/js/utils.rs b/lib/types/src/utils.rs similarity index 100% rename from lib/api/src/js/utils.rs rename to lib/types/src/utils.rs From d8a7d9f987468316f2981b5e54118386a23d26e6 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 21 Apr 2022 16:34:34 +0200 Subject: [PATCH 11/13] Small change on UniversalEngine for consistency --- lib/universal-artifact/src/engine.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/universal-artifact/src/engine.rs b/lib/universal-artifact/src/engine.rs index f2225e0ddaf..fe57b3156c8 100644 --- a/lib/universal-artifact/src/engine.rs +++ b/lib/universal-artifact/src/engine.rs @@ -21,15 +21,26 @@ impl UniversalEngineBuilder { pub fn new(compiler: Option>, features: Features) -> Self { UniversalEngineBuilder { compiler, features } } + /// Gets the compiler associated to this engine. #[cfg(feature = "compiler")] pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> { if self.compiler.is_none() { - return Err(CompileError::Codegen("The UniversalEngine is operating in headless mode, so it can only execute already compiled Modules.".to_string())); + return Err(CompileError::Codegen( + "The UniversalEngine is not compiled in.".to_string(), + )); } Ok(&**self.compiler.as_ref().unwrap()) } + /// Gets the compiler associated to this engine. + #[cfg(not(feature = "compiler"))] + pub fn compiler(&self) -> Result<&dyn Compiler, CompileError> { + return Err(CompileError::Codegen( + "The UniversalEngine is not compiled in.".to_string(), + )); + } + /// Validate the module #[cfg(feature = "compiler")] pub fn validate<'data>(&self, data: &'data [u8]) -> Result<(), CompileError> { From 8010cb88e7e48fc48c50fbff451b5854e5415e43 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Thu, 21 Apr 2022 16:35:20 +0200 Subject: [PATCH 12/13] Created stand-alone wasmer-compiler, builded only as a .wasm target for now Removed leftover trace of compiler feature in compiler-cli Excluded the new wasmer-compiler-cli from lint test, like wasmer-cli it's based on --- Cargo.lock | 22 ++ Cargo.toml | 1 + Makefile | 5 +- lib/cli-compiler/Cargo.toml | 65 ++++ lib/cli-compiler/README.md | 34 ++ lib/cli-compiler/build.rs | 4 + lib/cli-compiler/src/bin/wasmer_compiler.rs | 13 + lib/cli-compiler/src/cli.rs | 75 ++++ lib/cli-compiler/src/commands.rs | 7 + lib/cli-compiler/src/commands/compile.rs | 124 ++++++ lib/cli-compiler/src/commands/config.rs | 102 +++++ lib/cli-compiler/src/commands/validate.rs | 40 ++ lib/cli-compiler/src/common.rs | 53 +++ lib/cli-compiler/src/error.rs | 114 ++++++ lib/cli-compiler/src/lib.rs | 30 ++ lib/cli-compiler/src/logging.rs | 68 ++++ lib/cli-compiler/src/store.rs | 410 ++++++++++++++++++++ lib/cli-compiler/src/utils.rs | 92 +++++ lib/compiler-singlepass/Cargo.toml | 5 +- 19 files changed, 1262 insertions(+), 2 deletions(-) create mode 100644 lib/cli-compiler/Cargo.toml create mode 100644 lib/cli-compiler/README.md create mode 100644 lib/cli-compiler/build.rs create mode 100644 lib/cli-compiler/src/bin/wasmer_compiler.rs create mode 100644 lib/cli-compiler/src/cli.rs create mode 100644 lib/cli-compiler/src/commands.rs create mode 100644 lib/cli-compiler/src/commands/compile.rs create mode 100644 lib/cli-compiler/src/commands/config.rs create mode 100644 lib/cli-compiler/src/commands/validate.rs create mode 100644 lib/cli-compiler/src/common.rs create mode 100644 lib/cli-compiler/src/error.rs create mode 100644 lib/cli-compiler/src/lib.rs create mode 100644 lib/cli-compiler/src/logging.rs create mode 100644 lib/cli-compiler/src/store.rs create mode 100644 lib/cli-compiler/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 588f5cddb27..4d46ea97115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2906,6 +2906,28 @@ dependencies = [ "wasmparser 0.83.0", ] +[[package]] +name = "wasmer-compiler-cli" +version = "2.2.1" +dependencies = [ + "anyhow", + "atty", + "bytesize", + "cfg-if 1.0.0", + "colored 2.0.0", + "distance", + "fern", + "log", + "structopt", + "tempfile", + "unix_mode", + "wasmer-compiler", + "wasmer-compiler-singlepass", + "wasmer-engine-universal-artifact", + "wasmer-types", + "wasmer-vfs", +] + [[package]] name = "wasmer-compiler-cranelift" version = "2.2.1" diff --git a/Cargo.toml b/Cargo.toml index f1273bf1226..eb2d96bf65a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ members = [ "lib/cache", "lib/c-api", "lib/cli", + "lib/cli-compiler", "lib/compiler", "lib/compiler-cranelift", "lib/compiler-singlepass", diff --git a/Makefile b/Makefile index 00b2a1c1c1d..6fba03fab3e 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,7 @@ ifneq ($(ENABLE_LLVM), 0) endif endif -exclude_tests := --exclude wasmer-c-api --exclude wasmer-cli +exclude_tests := --exclude wasmer-c-api --exclude wasmer-cli --exclude wasmer-compiler-cli # Is failing to compile in Linux for some reason exclude_tests += --exclude wasmer-wasi-experimental-io-devices # We run integration tests separately (it requires building the c-api) @@ -379,6 +379,9 @@ build-wasmer-debug: bench: cargo bench $(compiler_features) +build-wasmer-wasm: + cargo build --release --manifest-path lib/cli-compiler/Cargo.toml --target wasm32-wasi --features singlepass,universal --bin wasmer-compiler + # For best results ensure the release profile looks like the following # in Cargo.toml: # [profile.release] diff --git a/lib/cli-compiler/Cargo.toml b/lib/cli-compiler/Cargo.toml new file mode 100644 index 00000000000..ba9257d93f3 --- /dev/null +++ b/lib/cli-compiler/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "wasmer-compiler-cli" +version = "2.2.1" +description = "Wasmer Compiler CLI" +categories = ["wasm", "command-line-interface"] +keywords = ["wasm", "webassembly", "cli"] +authors = ["Wasmer Engineering Team "] +repository = "https://github.com/wasmerio/wasmer" +license = "MIT" +readme = "README.md" +edition = "2018" +default-run = "wasmer-compiler" +build = "build.rs" + +[[bin]] +name = "wasmer-compiler" +path = "src/bin/wasmer_compiler.rs" +doc = false + +[dependencies] +wasmer-engine-universal-artifact = { version = "=2.2.1", path = "../universal-artifact", features = ["compiler"] } +wasmer-compiler = { version = "=2.2.1", path = "../compiler" } +wasmer-types = { version = "=2.2.1", path = "../types" } +wasmer-vfs = { version = "=2.2.1", path = "../vfs", default-features = false, features = ["host-fs"] } +atty = "0.2" +colored = "2.0" +anyhow = "1.0" +structopt = { version = "0.3", features = ["suggestions"] } +# For the function names autosuggestion +distance = "0.4" +# For the inspect subcommand +bytesize = "1.0" +cfg-if = "1.0" +# For debug feature +fern = { version = "0.6", features = ["colored"], optional = true } +log = { version = "0.4", optional = true } +tempfile = "3" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true, default-features = false, features = ["wasm"] } + +[target.'cfg(target_os = "linux")'.dependencies] +unix_mode = "0.1.3" + +[features] +# Don't add the compiler features in default, please add them on the Makefile +# since we might want to autoconfigure them depending on the availability on the host. +default = [ + "universal", +] +engine = [] +universal = [] +compiler = [ + "wasmer-compiler/translator", +] +singlepass = [ + "wasmer-compiler-singlepass", + "compiler", +] +debug = ["fern", "log"] +disable-all-logging = [] +jit = ["universal"] diff --git a/lib/cli-compiler/README.md b/lib/cli-compiler/README.md new file mode 100644 index 00000000000..512321d19bf --- /dev/null +++ b/lib/cli-compiler/README.md @@ -0,0 +1,34 @@ +# `wasmer-cli-compiler` [![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 Wasmer Compiler only CLI. + + +## Features + +The Compiler only Wasmer supports the following features: +* `universal` (default): support for the [Universal engine]. +* `wasi` (default): support for [WASI]. +* `experimental-io-devices`: support for experimental IO devices in WASI. +* `emscripten` (default): support for [Emscripten]. +* `singlepass`: support for the [Singlepass compiler]. + +[Universal engine]: https://github.com/wasmerio/wasmer/tree/master/lib/engine-universal/ +[WASI]: https://github.com/wasmerio/wasmer/tree/master/lib/wasi/ +[Emscripten]: https://github.com/wasmerio/wasmer/tree/master/lib/emscripten/ +[Singlepass compiler]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler-singlepass/ + +## CLI commands + +Once you have Wasmer installed, you can start executing WebAssembly files easily: + +Get the current Wasmer version: + +```bash +wasmer-compiler -V +``` + +Compile a WebAssembly file: + +```bash +wasmer-compiler compile myfile.wasm -o myfile.wasmu --singlepass --universal +``` diff --git a/lib/cli-compiler/build.rs b/lib/cli-compiler/build.rs new file mode 100644 index 00000000000..36fe64763ed --- /dev/null +++ b/lib/cli-compiler/build.rs @@ -0,0 +1,4 @@ +pub fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=WASMER_INSTALL_PREFIX"); +} diff --git a/lib/cli-compiler/src/bin/wasmer_compiler.rs b/lib/cli-compiler/src/bin/wasmer_compiler.rs new file mode 100644 index 00000000000..372ee665eb3 --- /dev/null +++ b/lib/cli-compiler/src/bin/wasmer_compiler.rs @@ -0,0 +1,13 @@ +use wasmer_compiler_cli::cli::wasmer_main; + +#[cfg(not(any(feature = "cranelift", feature = "singlepass", feature = "llvm")))] +compile_error!( + "Either enable at least one compiler, or compile the wasmer-headless binary instead" +); + +#[cfg(featue = "run")] +compile_error!("Cannot enable run with the compile-only build"); + +fn main() { + wasmer_main(); +} diff --git a/lib/cli-compiler/src/cli.rs b/lib/cli-compiler/src/cli.rs new file mode 100644 index 00000000000..177edea1c93 --- /dev/null +++ b/lib/cli-compiler/src/cli.rs @@ -0,0 +1,75 @@ +//! The logic for the Wasmer CLI tool. + +use crate::commands::Compile; +use crate::commands::{Config, Validate}; +use crate::error::PrettyError; +use anyhow::Result; + +use structopt::{clap::ErrorKind, StructOpt}; + +#[derive(StructOpt)] +#[structopt( + name = "wasmer-compiler", + about = "WebAssembly standalone Compiler.", + author +)] +/// The options for the wasmer Command Line Interface +enum WasmerCLIOptions { + /// Validate a WebAssembly binary + #[structopt(name = "validate")] + Validate(Validate), + + /// Compile a WebAssembly binary + #[structopt(name = "compile")] + Compile(Compile), + + /// Get various configuration information needed + /// to compile programs which use Wasmer + #[structopt(name = "config")] + Config(Config), +} + +impl WasmerCLIOptions { + fn execute(&self) -> Result<()> { + match self { + Self::Validate(validate) => validate.execute(), + Self::Compile(compile) => compile.execute(), + Self::Config(config) => config.execute(), + } + } +} + +/// The main function for the Wasmer CLI tool. +pub fn wasmer_main() { + // We allow windows to print properly colors + #[cfg(windows)] + colored::control::set_virtual_terminal(true).unwrap(); + + // We try to run wasmer with the normal arguments. + // Eg. `wasmer ` + // In case that fails, we fallback trying the Run subcommand directly. + // Eg. `wasmer myfile.wasm --dir=.` + // + // In case we've been run as wasmer-binfmt-interpreter myfile.wasm args, + // we assume that we're registered via binfmt_misc + let args = std::env::args().collect::>(); + let command = args.get(1); + let options = { + match command.unwrap_or(&"".to_string()).as_ref() { + "compile" | "config" | "help" | "inspect" | "validate" => WasmerCLIOptions::from_args(), + _ => { + WasmerCLIOptions::from_iter_safe(args.iter()).unwrap_or_else(|e| { + match e.kind { + // This fixes a issue that: + // 1. Shows the version twice when doing `wasmer -V` + // 2. Shows the run help (instead of normal help) when doing `wasmer --help` + ErrorKind::VersionDisplayed | ErrorKind::HelpDisplayed => e.exit(), + _ => WasmerCLIOptions::Compile(Compile::from_args()), + } + }) + } + } + }; + + PrettyError::report(options.execute()); +} diff --git a/lib/cli-compiler/src/commands.rs b/lib/cli-compiler/src/commands.rs new file mode 100644 index 00000000000..74418834fa4 --- /dev/null +++ b/lib/cli-compiler/src/commands.rs @@ -0,0 +1,7 @@ +//! The commands available in the Wasmer binary. +mod compile; +mod config; +mod validate; + +pub use compile::*; +pub use {config::*, validate::*}; diff --git a/lib/cli-compiler/src/commands/compile.rs b/lib/cli-compiler/src/commands/compile.rs new file mode 100644 index 00000000000..b6570b51a25 --- /dev/null +++ b/lib/cli-compiler/src/commands/compile.rs @@ -0,0 +1,124 @@ +use crate::store::{EngineType, StoreOptions}; +use crate::warning; +use anyhow::{Context, Result}; +use std::path::{Path, PathBuf}; +use structopt::StructOpt; +use wasmer_compiler::{CompileError, CpuFeature, ModuleEnvironment, Target, Triple}; +use wasmer_engine_universal_artifact::{ArtifactCreate, UniversalArtifactBuild}; +use wasmer_types::entity::PrimaryMap; +use wasmer_types::{MemoryIndex, MemoryStyle, TableIndex, TableStyle}; + +#[derive(Debug, StructOpt)] +/// The options for the `wasmer compile` subcommand +pub struct Compile { + /// Input file + #[structopt(name = "FILE", parse(from_os_str))] + path: PathBuf, + + /// Output file + #[structopt(name = "OUTPUT PATH", short = "o", parse(from_os_str))] + output: PathBuf, + + /// Compilation Target triple + #[structopt(long = "target")] + target_triple: Option, + + #[structopt(flatten)] + store: StoreOptions, + + #[structopt(short = "m", multiple = true, number_of_values = 1)] + cpu_features: Vec, +} + +impl Compile { + /// Runs logic for the `compile` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context(format!("failed to compile `{}`", self.path.display())) + } + + pub(crate) fn get_recommend_extension( + engine_type: &EngineType, + target_triple: &Triple, + ) -> Result<&'static str> { + Ok(match engine_type { + EngineType::Universal => { + wasmer_engine_universal_artifact::UniversalArtifactBuild::get_default_extension( + target_triple, + ) + } + }) + } + + fn inner_execute(&self) -> Result<()> { + let target = self + .target_triple + .as_ref() + .map(|target_triple| { + let mut features = self + .cpu_features + .clone() + .into_iter() + .fold(CpuFeature::set(), |a, b| a | b); + // Cranelift requires SSE2, so we have this "hack" for now to facilitate + // usage + features |= CpuFeature::SSE2; + Target::new(target_triple.clone(), features) + }) + .unwrap_or_default(); + let (mut engine, engine_type, compiler_type) = + self.store.get_engine_for_target(target.clone())?; + let output_filename = self + .output + .file_stem() + .map(|osstr| osstr.to_string_lossy().to_string()) + .unwrap_or_default(); + let recommended_extension = Self::get_recommend_extension(&engine_type, target.triple())?; + match self.output.extension() { + Some(ext) => { + if ext != recommended_extension { + warning!("the output file has a wrong extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + None => { + warning!("the output file has no extension. We recommend using `{}.{}` for the chosen target", &output_filename, &recommended_extension) + } + } + let tunables = self.store.get_tunables_for_target(&target)?; + + println!("Engine: {}", engine_type.to_string()); + println!("Compiler: {}", compiler_type.to_string()); + println!("Target: {}", target.triple()); + + // compile and save the artifact (without using module from api) + let path: &Path = self.path.as_ref(); + let wasm_bytes = std::fs::read(path)?; + let environ = ModuleEnvironment::new(); + let translation = environ.translate(&wasm_bytes).map_err(CompileError::Wasm)?; + let module = translation.module; + let memory_styles: PrimaryMap = module + .memories + .values() + .map(|memory_type| tunables.memory_style(memory_type)) + .collect(); + let table_styles: PrimaryMap = module + .tables + .values() + .map(|table_type| tunables.table_style(table_type)) + .collect(); + let artifact = UniversalArtifactBuild::new( + &mut engine, + &wasm_bytes, + &target, + memory_styles, + table_styles, + )?; + artifact.serialize_to_file(self.output.as_ref())?; + eprintln!( + "✔ File compiled successfully to `{}`.", + self.output.display(), + ); + + Ok(()) + } +} diff --git a/lib/cli-compiler/src/commands/config.rs b/lib/cli-compiler/src/commands/config.rs new file mode 100644 index 00000000000..0ded2bf4540 --- /dev/null +++ b/lib/cli-compiler/src/commands/config.rs @@ -0,0 +1,102 @@ +use crate::VERSION; +use anyhow::{Context, Result}; +use std::env; +use std::path::PathBuf; +use structopt::StructOpt; + +#[derive(Debug, StructOpt)] +/// The options for the `wasmer config` subcommand +pub struct Config { + /// Print the installation prefix. + #[structopt(long, conflicts_with = "pkg-config")] + prefix: bool, + + /// Directory containing Wasmer executables. + #[structopt(long, conflicts_with = "pkg-config")] + bindir: bool, + + /// Directory containing Wasmer headers. + #[structopt(long, conflicts_with = "pkg-config")] + includedir: bool, + + /// Directory containing Wasmer libraries. + #[structopt(long, conflicts_with = "pkg-config")] + libdir: bool, + + /// Libraries needed to link against Wasmer components. + #[structopt(long, conflicts_with = "pkg-config")] + libs: bool, + + /// C compiler flags for files that include Wasmer headers. + #[structopt(long, conflicts_with = "pkg-config")] + cflags: bool, + + /// It outputs the necessary details for compiling + /// and linking a program to Wasmer, using the `pkg-config` format. + #[structopt(long)] + pkg_config: bool, +} + +impl Config { + /// Runs logic for the `config` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context("failed to retrieve the wasmer config".to_string()) + } + fn inner_execute(&self) -> Result<()> { + let key = "WASMER_DIR"; + let wasmer_dir = env::var(key) + .or_else(|e| { + option_env!("WASMER_INSTALL_PREFIX") + .map(str::to_string) + .ok_or(e) + }) + .context(format!( + "failed to retrieve the {} environment variables", + key + ))?; + + let prefix = PathBuf::from(wasmer_dir); + + let prefixdir = prefix.display().to_string(); + let bindir = prefix.join("bin").display().to_string(); + let includedir = prefix.join("include").display().to_string(); + let libdir = prefix.join("lib").display().to_string(); + let cflags = format!("-I{}", includedir); + let libs = format!("-L{} -lwasmer", libdir); + + if self.pkg_config { + println!("prefix={}", prefixdir); + println!("exec_prefix={}", bindir); + println!("includedir={}", includedir); + println!("libdir={}", libdir); + println!(); + println!("Name: wasmer"); + println!("Description: The Wasmer library for running WebAssembly"); + println!("Version: {}", VERSION); + println!("Cflags: {}", cflags); + println!("Libs: {}", libs); + return Ok(()); + } + + if self.prefix { + println!("{}", prefixdir); + } + if self.bindir { + println!("{}", bindir); + } + if self.includedir { + println!("{}", includedir); + } + if self.libdir { + println!("{}", libdir); + } + if self.libs { + println!("{}", libs); + } + if self.cflags { + println!("{}", cflags); + } + Ok(()) + } +} diff --git a/lib/cli-compiler/src/commands/validate.rs b/lib/cli-compiler/src/commands/validate.rs new file mode 100644 index 00000000000..497912f3ef6 --- /dev/null +++ b/lib/cli-compiler/src/commands/validate.rs @@ -0,0 +1,40 @@ +use crate::store::StoreOptions; +use anyhow::{bail, Context, Result}; +use std::path::PathBuf; +use std::str::FromStr; +use structopt::StructOpt; +use wasmer_compiler::{CpuFeature, Target, Triple}; +use wasmer_types::is_wasm; + +#[derive(Debug, StructOpt)] +/// The options for the `wasmer validate` subcommand +pub struct Validate { + /// File to validate as WebAssembly + #[structopt(name = "FILE", parse(from_os_str))] + path: PathBuf, + + #[structopt(flatten)] + store: StoreOptions, +} + +impl Validate { + /// Runs logic for the `validate` subcommand + pub fn execute(&self) -> Result<()> { + self.inner_execute() + .context(format!("failed to validate `{}`", self.path.display())) + } + fn inner_execute(&self) -> Result<()> { + let target = Target::new( + Triple::from_str("x86_64-linux-gnu").unwrap(), + CpuFeature::SSE2 | CpuFeature::AVX, + ); + let (engine, _engine_type, _compiler_type) = self.store.get_engine_for_target(target)?; + let module_contents = std::fs::read(&self.path)?; + if !is_wasm(&module_contents) { + bail!("`wasmer validate` only validates WebAssembly files"); + } + engine.validate(&module_contents)?; + eprintln!("Validation passed for `{}`.", self.path.display()); + Ok(()) + } +} diff --git a/lib/cli-compiler/src/common.rs b/lib/cli-compiler/src/common.rs new file mode 100644 index 00000000000..863e154103f --- /dev/null +++ b/lib/cli-compiler/src/common.rs @@ -0,0 +1,53 @@ +//! Common module with common used structures across different +//! commands. +use crate::VERSION; +use std::env; +use std::path::PathBuf; +use structopt::StructOpt; + +#[derive(Debug, StructOpt, Clone, Default)] +/// The WebAssembly features that can be passed through the +/// Command Line args. +pub struct WasmFeatures { + /// Enable support for the SIMD proposal. + #[structopt(long = "enable-simd")] + pub simd: bool, + + /// Enable support for the threads proposal. + #[structopt(long = "enable-threads")] + pub threads: bool, + + /// Enable support for the reference types proposal. + #[structopt(long = "enable-reference-types")] + pub reference_types: bool, + + /// Enable support for the multi value proposal. + #[structopt(long = "enable-multi-value")] + pub multi_value: bool, + + /// Enable support for the bulk memory proposal. + #[structopt(long = "enable-bulk-memory")] + pub bulk_memory: bool, + + /// Enable support for all pre-standard proposals. + #[structopt(long = "enable-all")] + pub all: bool, +} + +/// Get the cache dir +pub fn get_cache_dir() -> PathBuf { + match env::var("WASMER_CACHE_DIR") { + Ok(dir) => { + let mut path = PathBuf::from(dir); + path.push(VERSION); + path + } + Err(_) => { + // We use a temporal directory for saving cache files + let mut temp_dir = env::temp_dir(); + temp_dir.push("wasmer"); + temp_dir.push(VERSION); + temp_dir + } + } +} diff --git a/lib/cli-compiler/src/error.rs b/lib/cli-compiler/src/error.rs new file mode 100644 index 00000000000..f23b50339c0 --- /dev/null +++ b/lib/cli-compiler/src/error.rs @@ -0,0 +1,114 @@ +//! Implements `PretyError` to print pretty errors in the CLI (when they happen) + +use anyhow::{Chain, Error}; +use colored::*; +use std::fmt::{self, Debug, Write}; + +/// A `PrettyError` for printing `anyhow::Error` nicely. +pub struct PrettyError { + error: Error, +} + +/// A macro that prints a warning with nice colors +#[macro_export] +macro_rules! warning { + ($($arg:tt)*) => ({ + use colored::*; + eprintln!("{}: {}", "warning".yellow().bold(), format!($($arg)*)); + }) +} + +impl PrettyError { + /// Process a `Result` printing any errors and exiting + /// the process after + pub fn report(result: Result) -> ! { + std::process::exit(match result { + Ok(_t) => 0, + Err(error) => { + eprintln!("{:?}", PrettyError { error }); + 1 + } + }); + } +} + +impl Debug for PrettyError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let error = &self.error; + + if f.alternate() { + return Debug::fmt(&error, f); + } + + write!(f, "{}", format!("{}: {}", "error".red(), error).bold())?; + // write!(f, "{}", error)?; + + if let Some(cause) = error.source() { + // write!(f, "\n{}:", "caused by".bold().blue())?; + let chain = Chain::new(cause); + let (total_errors, _) = chain.size_hint(); + for (n, error) in chain.enumerate() { + writeln!(f)?; + let mut indented = Indented { + inner: f, + number: Some(n + 1), + is_last: n == total_errors - 1, + started: false, + }; + write!(indented, "{}", error)?; + } + } + Ok(()) + } +} + +struct Indented<'a, D> { + inner: &'a mut D, + number: Option, + started: bool, + is_last: bool, +} + +impl Write for Indented<'_, T> +where + T: Write, +{ + fn write_str(&mut self, s: &str) -> fmt::Result { + for (i, line) in s.split('\n').enumerate() { + if !self.started { + self.started = true; + match self.number { + Some(number) => { + if !self.is_last { + write!( + self.inner, + "{} {: >4} ", + "│".bold().blue(), + format!("{}:", number).dimmed() + )? + } else { + write!( + self.inner, + "{}{: >2}: ", + "╰─▶".bold().blue(), + format!("{}", number).bold().blue() + )? + } + } + None => self.inner.write_str(" ")?, + } + } else if i > 0 { + self.inner.write_char('\n')?; + if self.number.is_some() { + self.inner.write_str(" ")?; + } else { + self.inner.write_str(" ")?; + } + } + + self.inner.write_str(line)?; + } + + Ok(()) + } +} diff --git a/lib/cli-compiler/src/lib.rs b/lib/cli-compiler/src/lib.rs new file mode 100644 index 00000000000..17b1d4ca248 --- /dev/null +++ b/lib/cli-compiler/src/lib.rs @@ -0,0 +1,30 @@ +//! The Wasmer binary lib + +#![deny( + missing_docs, + dead_code, + nonstandard_style, + unused_mut, + unused_variables, + unused_unsafe, + unreachable_patterns, + unstable_features +)] +#![doc(html_favicon_url = "https://wasmer.io/images/icons/favicon-32x32.png")] +#![doc(html_logo_url = "https://github.com/wasmerio.png?size=200")] + +#[macro_use] +extern crate anyhow; + +pub mod commands; +pub mod common; +#[macro_use] +pub mod error; +pub mod cli; +#[cfg(feature = "debug")] +pub mod logging; +pub mod store; +pub mod utils; + +/// Version number of this crate. +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/cli-compiler/src/logging.rs b/lib/cli-compiler/src/logging.rs new file mode 100644 index 00000000000..d60a45fbf33 --- /dev/null +++ b/lib/cli-compiler/src/logging.rs @@ -0,0 +1,68 @@ +//! Logging functions for the debug feature. +use crate::utils::wasmer_should_print_color; +use anyhow::Result; +use fern::colors::{Color, ColoredLevelConfig}; +use std::time; + +/// The debug level +pub type DebugLevel = log::LevelFilter; + +/// Subroutine to instantiate the loggers +pub fn set_up_logging(verbose: u8) -> Result<(), String> { + let colors_line = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Yellow) + .trace(Color::BrightBlack); + let should_color = wasmer_should_print_color(); + + let colors_level = colors_line.info(Color::Green); + let level = match verbose { + 1 => DebugLevel::Debug, + _ => DebugLevel::Trace, + }; + let dispatch = fern::Dispatch::new() + .level(level) + .chain({ + let base = if should_color { + fern::Dispatch::new().format(move |out, message, record| { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time"); + out.finish(format_args!( + "{color_line}[{seconds}.{millis} {level} {target}{color_line}]{ansi_close} {message}", + color_line = format_args!( + "\x1B[{}m", + colors_line.get_color(&record.level()).to_fg_str() + ), + seconds = time.as_secs(), + millis = time.subsec_millis(), + level = colors_level.color(record.level()), + target = record.target(), + ansi_close = "\x1B[0m", + message = message, + )); + }) + } else { + // default formatter without color + fern::Dispatch::new().format(move |out, message, record| { + let time = time::SystemTime::now().duration_since(time::UNIX_EPOCH).expect("Can't get time"); + out.finish(format_args!( + "[{seconds}.{millis} {level} {target}] {message}", + seconds = time.as_secs(), + millis = time.subsec_millis(), + level = record.level(), + target = record.target(), + message = message, + )); + }) + }; + + base + .filter(|metadata| { + metadata.target().starts_with("wasmer") + }) + .chain(std::io::stdout()) + }); + + dispatch.apply().map_err(|e| format!("{}", e))?; + + Ok(()) +} diff --git a/lib/cli-compiler/src/store.rs b/lib/cli-compiler/src/store.rs new file mode 100644 index 00000000000..a9d108dc9e7 --- /dev/null +++ b/lib/cli-compiler/src/store.rs @@ -0,0 +1,410 @@ +//! Common module with common used structures across different +//! commands. + +use crate::common::WasmFeatures; +use anyhow::Result; +use std::string::ToString; +#[allow(unused_imports)] +use std::sync::Arc; +use structopt::StructOpt; +use wasmer_compiler::{CompilerConfig, Features, PointerWidth, Target}; +use wasmer_engine_universal_artifact::UniversalEngineBuilder; +use wasmer_types::{MemoryStyle, MemoryType, Pages, TableStyle, TableType}; + +/// Minimul Subset of Tunable parameters for WebAssembly compilation. +#[derive(Clone)] +pub struct SubsetTunables { + /// For static heaps, the size in wasm pages of the heap protected by bounds checking. + pub static_memory_bound: Pages, + + /// The size in bytes of the offset guard for static heaps. + pub static_memory_offset_guard_size: u64, + + /// The size in bytes of the offset guard for dynamic heaps. + pub dynamic_memory_offset_guard_size: u64, +} + +impl SubsetTunables { + /// Get the `BaseTunables` for a specific Target + pub fn for_target(target: &Target) -> Self { + let triple = target.triple(); + let pointer_width: PointerWidth = triple.pointer_width().unwrap(); + let (static_memory_bound, static_memory_offset_guard_size): (Pages, u64) = + match pointer_width { + PointerWidth::U16 => (0x400.into(), 0x1000), + PointerWidth::U32 => (0x4000.into(), 0x1_0000), + // Static Memory Bound: + // Allocating 4 GiB of address space let us avoid the + // need for explicit bounds checks. + // Static Memory Guard size: + // Allocating 2 GiB of address space lets us translate wasm + // offsets into x86 offsets as aggressively as we can. + PointerWidth::U64 => (0x1_0000.into(), 0x8000_0000), + }; + + // Allocate a small guard to optimize common cases but without + // wasting too much memory. + // The Windows memory manager seems more laxed than the other ones + // And a guard of just 1 page may not be enough is some borderline cases + // So using 2 pages for guard on this platform + #[cfg(target_os = "windows")] + let dynamic_memory_offset_guard_size: u64 = 0x2_0000; + #[cfg(not(target_os = "windows"))] + let dynamic_memory_offset_guard_size: u64 = 0x1_0000; + + Self { + static_memory_bound, + static_memory_offset_guard_size, + dynamic_memory_offset_guard_size, + } + } + /// Get a `MemoryStyle` for the provided `MemoryType` + pub fn memory_style(&self, memory: &MemoryType) -> MemoryStyle { + // A heap with a maximum that doesn't exceed the static memory bound specified by the + // tunables make it static. + // + // If the module doesn't declare an explicit maximum treat it as 4GiB. + let maximum = memory.maximum.unwrap_or_else(Pages::max_value); + if maximum <= self.static_memory_bound { + MemoryStyle::Static { + // Bound can be larger than the maximum for performance reasons + bound: self.static_memory_bound, + offset_guard_size: self.static_memory_offset_guard_size, + } + } else { + MemoryStyle::Dynamic { + offset_guard_size: self.dynamic_memory_offset_guard_size, + } + } + } + + /// Get a [`TableStyle`] for the provided [`TableType`]. + pub fn table_style(&self, _table: &TableType) -> TableStyle { + TableStyle::CallerChecksSignature + } +} + +#[derive(Debug, Clone, StructOpt, Default)] +/// The compiler and engine options +pub struct StoreOptions { + #[structopt(flatten)] + compiler: CompilerOptions, +} + +#[derive(Debug, Clone, StructOpt, Default)] +/// The compiler options +pub struct CompilerOptions { + /// Use Singlepass compiler. + #[structopt(long, conflicts_with_all = &["cranelift", "llvm"])] + singlepass: bool, + + /// Use Cranelift compiler. + #[structopt(long, conflicts_with_all = &["singlepass", "llvm"])] + cranelift: bool, + + /// Use LLVM compiler. + #[structopt(long, conflicts_with_all = &["singlepass", "cranelift"])] + llvm: bool, + + /// Enable compiler internal verification. + #[structopt(long)] + enable_verifier: bool, + + /// LLVM debug directory, where IR and object files will be written to. + #[cfg(feature = "llvm")] + #[structopt(long, parse(from_os_str))] + llvm_debug_dir: Option, + + #[structopt(flatten)] + features: WasmFeatures, +} + +impl CompilerOptions { + fn get_compiler(&self) -> Result { + if self.cranelift { + Ok(CompilerType::Cranelift) + } else if self.llvm { + Ok(CompilerType::LLVM) + } else if self.singlepass { + Ok(CompilerType::Singlepass) + } else { + // Auto mode, we choose the best compiler for that platform + cfg_if::cfg_if! { + if #[cfg(all(feature = "cranelift", any(target_arch = "x86_64", target_arch = "aarch64")))] { + Ok(CompilerType::Cranelift) + } + else if #[cfg(all(feature = "singlepass", target_arch = "x86_64"))] { + Ok(CompilerType::Singlepass) + } + else if #[cfg(feature = "llvm")] { + Ok(CompilerType::LLVM) + } else { + bail!("There are no available compilers for your architecture"); + } + } + } + } + + /// Get the enaled Wasm features. + pub fn get_features(&self, mut features: Features) -> Result { + if self.features.threads || self.features.all { + features.threads(true); + } + if self.features.multi_value || self.features.all { + features.multi_value(true); + } + if self.features.simd || self.features.all { + features.simd(true); + } + if self.features.bulk_memory || self.features.all { + features.bulk_memory(true); + } + if self.features.reference_types || self.features.all { + features.reference_types(true); + } + Ok(features) + } + + fn get_engine_by_type( + &self, + target: Target, + compiler_config: Box, + engine_type: EngineType, + ) -> Result { + let features = self.get_features(compiler_config.default_features_for_target(&target))?; + let engine: UniversalEngineBuilder = match engine_type { + EngineType::Universal => { + UniversalEngineBuilder::new(Some(compiler_config.compiler()), features) + } + }; + + Ok(engine) + } + + /// Get the Compiler Config for the current options + #[allow(unused_variables)] + pub(crate) fn get_compiler_config(&self) -> Result<(Box, CompilerType)> { + let compiler = self.get_compiler()?; + let compiler_config: Box = match compiler { + CompilerType::Headless => bail!("The headless engine can't be chosen"), + #[cfg(feature = "singlepass")] + CompilerType::Singlepass => { + let mut config = wasmer_compiler_singlepass::Singlepass::new(); + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(feature = "cranelift")] + CompilerType::Cranelift => { + let mut config = wasmer_compiler_cranelift::Cranelift::new(); + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(feature = "llvm")] + CompilerType::LLVM => { + use std::fmt; + use std::fs::File; + use std::io::Write; + use wasmer_compiler_llvm::{ + CompiledKind, InkwellMemoryBuffer, InkwellModule, LLVMCallbacks, LLVM, + }; + use wasmer_types::entity::EntityRef; + let mut config = LLVM::new(); + struct Callbacks { + debug_dir: PathBuf, + } + impl Callbacks { + fn new(debug_dir: PathBuf) -> Result { + // Create the debug dir in case it doesn't exist + std::fs::create_dir_all(&debug_dir)?; + Ok(Self { debug_dir }) + } + } + // Converts a kind into a filename, that we will use to dump + // the contents of the IR object file to. + fn types_to_signature(types: &[Type]) -> String { + types + .iter() + .map(|ty| match ty { + Type::I32 => "i".to_string(), + Type::I64 => "I".to_string(), + Type::F32 => "f".to_string(), + Type::F64 => "F".to_string(), + Type::V128 => "v".to_string(), + Type::ExternRef => "e".to_string(), + Type::FuncRef => "r".to_string(), + }) + .collect::>() + .join("") + } + // Converts a kind into a filename, that we will use to dump + // the contents of the IR object file to. + fn function_kind_to_filename(kind: &CompiledKind) -> String { + match kind { + CompiledKind::Local(local_index) => { + format!("function_{}", local_index.index()) + } + CompiledKind::FunctionCallTrampoline(func_type) => format!( + "trampoline_call_{}_{}", + types_to_signature(&func_type.params()), + types_to_signature(&func_type.results()) + ), + CompiledKind::DynamicFunctionTrampoline(func_type) => format!( + "trampoline_dynamic_{}_{}", + types_to_signature(&func_type.params()), + types_to_signature(&func_type.results()) + ), + CompiledKind::Module => "module".into(), + } + } + impl LLVMCallbacks for Callbacks { + fn preopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.preopt.ll", function_kind_to_filename(kind))); + module + .print_to_file(&path) + .expect("Error while dumping pre optimized LLVM IR"); + } + fn postopt_ir(&self, kind: &CompiledKind, module: &InkwellModule) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.postopt.ll", function_kind_to_filename(kind))); + module + .print_to_file(&path) + .expect("Error while dumping post optimized LLVM IR"); + } + fn obj_memory_buffer( + &self, + kind: &CompiledKind, + memory_buffer: &InkwellMemoryBuffer, + ) { + let mut path = self.debug_dir.clone(); + path.push(format!("{}.o", function_kind_to_filename(kind))); + let mem_buf_slice = memory_buffer.as_slice(); + let mut file = File::create(path) + .expect("Error while creating debug object file from LLVM IR"); + let mut pos = 0; + while pos < mem_buf_slice.len() { + pos += file.write(&mem_buf_slice[pos..]).unwrap(); + } + } + } + + impl fmt::Debug for Callbacks { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "LLVMCallbacks") + } + } + + if let Some(ref llvm_debug_dir) = self.llvm_debug_dir { + config.callbacks(Some(Arc::new(Callbacks::new(llvm_debug_dir.clone())?))); + } + if self.enable_verifier { + config.enable_verifier(); + } + Box::new(config) + } + #[cfg(not(all(feature = "singlepass", feature = "cranelift", feature = "llvm",)))] + compiler => { + bail!( + "The `{}` compiler is not included in this binary.", + compiler.to_string() + ) + } + }; + + #[allow(unreachable_code)] + Ok((compiler_config, compiler)) + } +} + +/// The compiler used for the store +#[derive(Debug, PartialEq, Eq)] +pub enum CompilerType { + /// Singlepass compiler + Singlepass, + /// Cranelift compiler + Cranelift, + /// LLVM compiler + LLVM, + /// Headless compiler + Headless, +} + +impl CompilerType { + /// Return all enabled compilers + pub fn enabled() -> Vec { + vec![ + #[cfg(feature = "singlepass")] + Self::Singlepass, + #[cfg(feature = "cranelift")] + Self::Cranelift, + #[cfg(feature = "llvm")] + Self::LLVM, + ] + } +} + +impl ToString for CompilerType { + fn to_string(&self) -> String { + match self { + Self::Singlepass => "singlepass".to_string(), + Self::Cranelift => "cranelift".to_string(), + Self::LLVM => "llvm".to_string(), + Self::Headless => "headless".to_string(), + } + } +} + +/// The engine used for the store +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum EngineType { + /// Universal Engine + Universal, +} + +impl ToString for EngineType { + fn to_string(&self) -> String { + match self { + Self::Universal => "universal".to_string(), + } + } +} + +impl StoreOptions { + /// Get a UniversalEngineBulder for the Target + pub fn get_engine_for_target( + &self, + target: Target, + ) -> Result<(UniversalEngineBuilder, EngineType, CompilerType)> { + let (compiler_config, compiler_type) = self.compiler.get_compiler_config()?; + let (engine, engine_type) = self.get_engine_with_compiler(target, compiler_config)?; + Ok((engine, engine_type, compiler_type)) + } + + /// Get default EngineType + pub fn get_engine(&self) -> Result { + Ok(EngineType::Universal) + } + + fn get_engine_with_compiler( + &self, + target: Target, + compiler_config: Box, + ) -> Result<(UniversalEngineBuilder, EngineType)> { + let engine_type = self.get_engine()?; + let engine = self + .compiler + .get_engine_by_type(target, compiler_config, engine_type)?; + + Ok((engine, engine_type)) + } + + /// Get (Subset)Tunables for the Target + pub fn get_tunables_for_target(&self, target: &Target) -> Result { + let tunables = SubsetTunables::for_target(target); + Ok(tunables) + } +} diff --git a/lib/cli-compiler/src/utils.rs b/lib/cli-compiler/src/utils.rs new file mode 100644 index 00000000000..a032f26f40b --- /dev/null +++ b/lib/cli-compiler/src/utils.rs @@ -0,0 +1,92 @@ +//! Utility functions for the WebAssembly module +use anyhow::{bail, Result}; +use std::env; +use std::path::PathBuf; + +/// Whether or not Wasmer should print with color +pub fn wasmer_should_print_color() -> bool { + env::var("WASMER_COLOR") + .ok() + .and_then(|inner| inner.parse::().ok()) + .unwrap_or_else(|| atty::is(atty::Stream::Stdout)) +} + +fn retrieve_alias_pathbuf(alias: &str, real_dir: &str) -> Result<(String, PathBuf)> { + let pb = PathBuf::from(&real_dir); + if let Ok(pb_metadata) = pb.metadata() { + if !pb_metadata.is_dir() { + bail!("\"{}\" exists, but it is not a directory", &real_dir); + } + } else { + bail!("Directory \"{}\" does not exist", &real_dir); + } + Ok((alias.to_string(), pb)) +} + +/// Parses a mapdir from a string +pub fn parse_mapdir(entry: &str) -> Result<(String, PathBuf)> { + // We try first splitting by `::` + if let [alias, real_dir] = entry.split("::").collect::>()[..] { + retrieve_alias_pathbuf(alias, real_dir) + } + // And then we try splitting by `:` (for compatibility with previous API) + else if let [alias, real_dir] = entry.split(':').collect::>()[..] { + retrieve_alias_pathbuf(alias, real_dir) + } else { + bail!( + "Directory mappings must consist of two paths separate by a `::` or `:`. Found {}", + &entry + ) + } +} + +/// Parses an environment variable. +pub fn parse_envvar(entry: &str) -> Result<(String, String)> { + let entry = entry.trim(); + + match entry.find('=') { + None => bail!( + "Environment variable must be of the form `=`; found `{}`", + &entry + ), + + Some(0) => bail!( + "Environment variable is not well formed, the `name` is missing in `=`; got `{}`", + &entry + ), + + Some(position) if position == entry.len() - 1 => bail!( + "Environment variable is not well formed, the `value` is missing in `=`; got `{}`", + &entry + ), + + Some(position) => Ok((entry[..position].into(), entry[position + 1..].into())), + } +} + +#[cfg(test)] +mod tests { + use super::parse_envvar; + + #[test] + fn test_parse_envvar() { + assert_eq!( + parse_envvar("A").unwrap_err().to_string(), + "Environment variable must be of the form `=`; found `A`" + ); + assert_eq!( + parse_envvar("=A").unwrap_err().to_string(), + "Environment variable is not well formed, the `name` is missing in `=`; got `=A`" + ); + assert_eq!( + parse_envvar("A=").unwrap_err().to_string(), + "Environment variable is not well formed, the `value` is missing in `=`; got `A=`" + ); + assert_eq!(parse_envvar("A=B").unwrap(), ("A".into(), "B".into())); + assert_eq!(parse_envvar(" A=B\t").unwrap(), ("A".into(), "B".into())); + assert_eq!( + parse_envvar("A=B=C=D").unwrap(), + ("A".into(), "B=C=D".into()) + ); + } +} diff --git a/lib/compiler-singlepass/Cargo.toml b/lib/compiler-singlepass/Cargo.toml index eb6a71b3716..f9dee2065bb 100644 --- a/lib/compiler-singlepass/Cargo.toml +++ b/lib/compiler-singlepass/Cargo.toml @@ -14,7 +14,6 @@ edition = "2018" [dependencies] wasmer-compiler = { path = "../compiler", version = "=2.2.1", features = ["translator"], default-features = false } wasmer-types = { path = "../types", version = "=2.2.1", default-features = false, features = ["std"] } -rayon = { version = "1.5", optional = true } hashbrown = { version = "0.11", optional = true } gimli = { version = "0.26", optional = true } more-asserts = "0.2" @@ -25,6 +24,9 @@ byteorder = "1.3" smallvec = "1.6" loupe = "0.1" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +rayon = { version = "1.5", optional = true } + [dev-dependencies] target-lexicon = { version = "0.12.2", default-features = false } @@ -33,6 +35,7 @@ maintenance = { status = "actively-developed" } [features] default = ["std", "rayon", "unwind", "avx"] +wasm = ["std", "avx"] std = ["wasmer-compiler/std", "wasmer-types/std"] core = ["hashbrown", "wasmer-types/core"] unwind = ["gimli"] From 0c7ea79bfc82ddf44dbfa9660c02c54cc111fff4 Mon Sep 17 00:00:00 2001 From: ptitSeb Date: Mon, 2 May 2022 10:12:35 +0200 Subject: [PATCH 13/13] Cleanup: Removed conditionnal toml on wasm for VM as it's not builded in that configuration Added unwind to wasm build Moved Trap back to vm (from types) as it's used only on Runtime Move MemoryError back to vm (from types) as it's used on Runtime only scenario Some cleanup and lint fixes Removed wasm build case from vfs/host_fs as it's not used --- Cargo.lock | 2 - lib/cli-compiler/Cargo.toml | 1 - lib/cli/Cargo.toml | 24 ++++------ lib/cli/src/bin/wasmer_compiler.rs | 13 ------ lib/compiler-singlepass/Cargo.toml | 2 +- lib/types/src/lib.rs | 4 +- lib/types/src/memory.rs | 43 ------------------ lib/types/src/trapcode.rs | 71 ----------------------------- lib/vfs/src/host_fs.rs | 27 ----------- lib/vm/Cargo.toml | 2 - lib/vm/src/instance/mod.rs | 6 +-- lib/vm/src/lib.rs | 4 +- lib/vm/src/memory.rs | 45 ++++++++++++++++++- lib/vm/src/table.rs | 3 +- lib/vm/src/trap/mod.rs | 4 +- lib/vm/src/trap/trap.rs | 72 ++++++++++++++++++++++++++++++ lib/vm/src/trap/traphandlers.rs | 3 +- 17 files changed, 140 insertions(+), 186 deletions(-) delete mode 100644 lib/cli/src/bin/wasmer_compiler.rs create mode 100644 lib/vm/src/trap/trap.rs diff --git a/Cargo.lock b/Cargo.lock index 4d46ea97115..68eeeda0d1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2870,7 +2870,6 @@ dependencies = [ "tempfile", "unix_mode", "wasmer", - "wasmer-artifact", "wasmer-cache", "wasmer-compiler", "wasmer-compiler-cranelift", @@ -2925,7 +2924,6 @@ dependencies = [ "wasmer-compiler-singlepass", "wasmer-engine-universal-artifact", "wasmer-types", - "wasmer-vfs", ] [[package]] diff --git a/lib/cli-compiler/Cargo.toml b/lib/cli-compiler/Cargo.toml index ba9257d93f3..fcc01acd650 100644 --- a/lib/cli-compiler/Cargo.toml +++ b/lib/cli-compiler/Cargo.toml @@ -21,7 +21,6 @@ doc = false wasmer-engine-universal-artifact = { version = "=2.2.1", path = "../universal-artifact", features = ["compiler"] } wasmer-compiler = { version = "=2.2.1", path = "../compiler" } wasmer-types = { version = "=2.2.1", path = "../types" } -wasmer-vfs = { version = "=2.2.1", path = "../vfs", default-features = false, features = ["host-fs"] } atty = "0.2" colored = "2.0" anyhow = "1.0" diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index efd72cd48a6..2303da10dd6 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -24,12 +24,19 @@ doc = false required-features = ["headless"] [dependencies] -wasmer-artifact = { version = "=2.2.1", path = "../artifact" } +wasmer = { version = "=2.2.1", path = "../api", default-features = false } wasmer-compiler = { version = "=2.2.1", path = "../compiler" } +wasmer-compiler-cranelift = { version = "=2.2.1", path = "../compiler-cranelift", optional = true } wasmer-compiler-singlepass = { version = "=2.2.1", path = "../compiler-singlepass", optional = true } +wasmer-compiler-llvm = { version = "=2.2.1", path = "../compiler-llvm", optional = true } +wasmer-emscripten = { version = "=2.2.1", path = "../emscripten", optional = true } wasmer-engine = { version = "=2.2.1", path = "../engine" } wasmer-engine-universal = { version = "=2.2.1", path = "../engine-universal", optional = true } +wasmer-engine-dylib = { version = "=2.2.1", path = "../engine-dylib", optional = true } +wasmer-engine-staticlib = { version = "=2.2.1", path = "../engine-staticlib", optional = true } +wasmer-vm = { version = "=2.2.1", path = "../vm" } wasmer-wasi = { version = "=2.2.1", path = "../wasi", optional = true } +wasmer-wasi-experimental-io-devices = { version = "=2.2.1", path = "../wasi-experimental-io-devices", optional = true } wasmer-wast = { version = "=2.2.1", path = "../../tests/lib/wast", optional = true } wasmer-cache = { version = "=2.2.1", path = "../cache", optional = true } wasmer-types = { version = "=2.2.1", path = "../types" } @@ -48,19 +55,6 @@ fern = { version = "0.6", features = ["colored"], optional = true } log = { version = "0.4", optional = true } tempfile = "3" -[target.'cfg(target_arch = "wasm32")'.dependencies] -wasmer = { version = "=2.2.1", path = "../api", default-features = false, features = ["js-default"] } - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -wasmer = { version = "=2.2.1", path = "../api", default-features = false } -wasmer-vm = { version = "=2.2.1", path = "../vm" } -wasmer-compiler-cranelift = { version = "=2.2.1", path = "../compiler-cranelift", optional = true } -wasmer-compiler-llvm = { version = "=2.2.1", path = "../compiler-llvm", optional = true } -wasmer-wasi-experimental-io-devices = { version = "=2.2.1", path = "../wasi-experimental-io-devices", optional = true } -wasmer-emscripten = { version = "=2.2.1", path = "../emscripten", optional = true } -wasmer-engine-dylib = { version = "=2.2.1", path = "../engine-dylib", optional = true } -wasmer-engine-staticlib = { version = "=2.2.1", path = "../engine-staticlib", optional = true } - [target.'cfg(target_os = "linux")'.dependencies] unix_mode = "0.1.3" @@ -125,4 +119,4 @@ headless-minimal = ["headless", "disable-all-logging", "wasi", "dylib", "univers # Deprecated features. jit = ["universal"] -native = ["dylib"] +native = ["dylib"] \ No newline at end of file diff --git a/lib/cli/src/bin/wasmer_compiler.rs b/lib/cli/src/bin/wasmer_compiler.rs deleted file mode 100644 index 13c4ff72e7c..00000000000 --- a/lib/cli/src/bin/wasmer_compiler.rs +++ /dev/null @@ -1,13 +0,0 @@ -use wasmer_cli::cli::wasmer_main; - -#[cfg(not(any(feature = "cranelift", feature = "singlepass", feature = "llvm")))] -compile_error!( - "Either enable at least one compiler, or compile the wasmer-headless binary instead" -); - -#[cfg(featue = "run")] -compile_error!("Cannot enable run with the compile-only build"); - -fn main() { - wasmer_main(); -} diff --git a/lib/compiler-singlepass/Cargo.toml b/lib/compiler-singlepass/Cargo.toml index f9dee2065bb..1fbfc0c2f24 100644 --- a/lib/compiler-singlepass/Cargo.toml +++ b/lib/compiler-singlepass/Cargo.toml @@ -35,7 +35,7 @@ maintenance = { status = "actively-developed" } [features] default = ["std", "rayon", "unwind", "avx"] -wasm = ["std", "avx"] +wasm = ["std", "unwind", "avx"] std = ["wasmer-compiler/std", "wasmer-types/std"] core = ["hashbrown", "wasmer-types/core"] unwind = ["gimli"] diff --git a/lib/types/src/lib.rs b/lib/types/src/lib.rs index 9f443e708ed..439dafe10d7 100644 --- a/lib/types/src/lib.rs +++ b/lib/types/src/lib.rs @@ -100,9 +100,9 @@ pub use types::{ pub use archives::ArchivableIndexMap; pub use crate::libcalls::LibCall; -pub use crate::memory::{MemoryError, MemoryStyle}; +pub use crate::memory::MemoryStyle; pub use crate::table::TableStyle; -pub use crate::trapcode::{Trap, TrapCode}; +pub use crate::trapcode::TrapCode; pub use crate::vmoffsets::{TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMOffsets}; pub use crate::utils::is_wasm; diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 4ad12c05afd..920abc06129 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -4,49 +4,6 @@ use loupe::MemoryUsage; 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)] diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 46812ce3576..648898fd6e5 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -3,7 +3,6 @@ //! Trap codes describing the reason for a trap. -use backtrace::Backtrace; use core::fmt::{self, Display, Formatter}; use core::str::FromStr; use loupe::MemoryUsage; @@ -11,7 +10,6 @@ use loupe::MemoryUsage; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; -use std::error::Error; use thiserror::Error; /// A trap code describing the reason for a trap. @@ -166,72 +164,3 @@ mod tests { assert_eq!("users".parse::(), Err(())); } } - -/// Stores trace message with backtrace. -#[derive(Debug)] -pub enum Trap { - /// A user-raised trap through `raise_user_trap`. - User(Box), - - /// A trap raised from the Wasm generated code - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Wasm { - /// The program counter in generated code where this trap happened. - pc: usize, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - /// Optional trapcode associated to the signal that caused the trap - signal_trap: Option, - }, - - /// A trap raised from a wasm libcall - /// - /// Note: this trap is deterministic (assuming a deterministic host implementation) - Lib { - /// Code of the trap. - trap_code: TrapCode, - /// Native stack backtrace at the time the trap occurred - backtrace: Backtrace, - }, - - /// A trap indicating that the runtime was unable to allocate sufficient memory. - /// - /// Note: this trap is nondeterministic, since it depends on the host system. - OOM { - /// Native stack backtrace at the time the OOM occurred - backtrace: Backtrace, - }, -} - -impl Trap { - /// Construct a new Wasm trap with the given source location and backtrace. - /// - /// Internally saves a backtrace when constructed. - pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { - Trap::Wasm { - pc, - backtrace, - signal_trap, - } - } - - /// Construct a new Wasm trap with the given trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn lib(trap_code: TrapCode) -> Self { - let backtrace = Backtrace::new_unresolved(); - Trap::Lib { - trap_code, - backtrace, - } - } - - /// Construct a new OOM trap with the given source location and trap code. - /// - /// Internally saves a backtrace when constructed. - pub fn oom() -> Self { - let backtrace = Backtrace::new_unresolved(); - Trap::OOM { backtrace } - } -} diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index c2eb4908693..961f6685be0 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -9,8 +9,6 @@ use std::fs; use std::io::{self, Read, Seek, Write}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, RawFd}; -#[cfg(target_arch = "wasm32")] -use std::os::wasi::io::{AsRawFd, RawFd}; #[cfg(windows)] use std::os::windows::io::{AsRawHandle, RawHandle}; use std::path::{Path, PathBuf}; @@ -69,31 +67,6 @@ impl TryInto for FileDescriptor { } } -#[cfg(target_arch = "wasm32")] -impl TryIntoFileDescriptor for T -where - T: AsRawFd, -{ - type Error = FsError; - - fn try_into_filedescriptor(&self) -> std::result::Result { - Ok(FileDescriptor( - self.as_raw_fd() - .try_into() - .map_err(|_| FsError::InvalidFd)?, - )) - } -} - -#[cfg(target_arch = "wasm32")] -impl TryInto for FileDescriptor { - type Error = FsError; - - fn try_into(self) -> std::result::Result { - self.0.try_into().map_err(|_| FsError::InvalidFd) - } -} - #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct FileSystem; diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 7c99658350f..21359bbbd09 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -26,8 +26,6 @@ loupe = { version = "0.1", features = ["enable-indexmap"] } enum-iterator = "0.7.0" scopeguard = "1.1.0" lazy_static = "1.4.0" - -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] region = { version = "3.0" } corosensei = { version = "0.1.2" } diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index a42eb0b9533..bed09bbbfc6 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -17,7 +17,7 @@ use crate::export::VMExtern; use crate::func_data_registry::VMFuncRef; use crate::global::Global; use crate::imports::Imports; -use crate::memory::Memory; +use crate::memory::{Memory, MemoryError}; use crate::table::{Table, TableElement}; use crate::trap::{catch_traps, Trap, TrapCode, TrapHandler}; use crate::vmcontext::{ @@ -44,8 +44,8 @@ use std::sync::Arc; use wasmer_types::entity::{packed_option::ReservedValue, BoxedSlice, EntityRef, PrimaryMap}; use wasmer_types::{ DataIndex, DataInitializer, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, - LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryError, - MemoryIndex, ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, + LocalFunctionIndex, LocalGlobalIndex, LocalMemoryIndex, LocalTableIndex, MemoryIndex, + ModuleInfo, Pages, SignatureIndex, TableIndex, TableInitializer, }; /// The function pointer to call with data and an [`Instance`] pointer to diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 1f3877e8bbc..8056736a28a 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -43,7 +43,7 @@ pub use crate::instance::{ ImportFunctionEnv, ImportInitializerFuncPtr, InstanceAllocator, InstanceHandle, WeakOrStrongInstanceRef, }; -pub use crate::memory::{LinearMemory, Memory}; +pub use crate::memory::{LinearMemory, Memory, MemoryError}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; @@ -56,13 +56,13 @@ pub use crate::vmcontext::{ }; pub use wasmer_artifact::{FunctionBodyPtr, VMFunctionBody}; pub use wasmer_types::LibCall; +pub use wasmer_types::MemoryStyle; pub use wasmer_types::TableStyle; #[deprecated( since = "2.1.0", note = "ModuleInfo, ExportsIterator, ImportsIterator should be imported from wasmer_types." )] pub use wasmer_types::{ExportsIterator, ImportsIterator, ModuleInfo}; -pub use wasmer_types::{MemoryError, MemoryStyle}; pub use wasmer_types::{ TargetSharedSignatureIndex, VMBuiltinFunctionIndex, VMExternRef, VMOffsets, }; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 10768ebd1f4..75919bb7f8b 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -15,7 +15,50 @@ use std::convert::TryInto; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages}; +use thiserror::Error; +use wasmer_types::{Bytes, MemoryStyle, MemoryType, Pages}; + +/// 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), +} /// Trait for implementing Wasm Memory used by Wasmer. pub trait Memory: fmt::Debug + Send + Sync + MemoryUsage { diff --git a/lib/vm/src/table.rs b/lib/vm/src/table.rs index 217be53aba0..3f7c8187a5b 100644 --- a/lib/vm/src/table.rs +++ b/lib/vm/src/table.rs @@ -7,6 +7,7 @@ use crate::func_data_registry::VMFuncRef; use crate::vmcontext::VMTableDefinition; +use crate::Trap; use crate::VMExternRef; use loupe::{MemoryUsage, MemoryUsageTracker}; use std::borrow::{Borrow, BorrowMut}; @@ -15,7 +16,7 @@ use std::convert::TryFrom; use std::fmt; use std::ptr::NonNull; use std::sync::Mutex; -use wasmer_types::{ExternRef, TableStyle, TableType, Trap, TrapCode, Type as ValType}; +use wasmer_types::{ExternRef, TableStyle, TableType, TrapCode, Type as ValType}; /// Trait for implementing the interface of a Wasm table. pub trait Table: fmt::Debug + Send + Sync + MemoryUsage { diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index 6473c2d1c33..ca764576ba4 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -3,11 +3,13 @@ //! This is the module that facilitates the usage of Traps //! in Wasmer Runtime +mod trap; mod traphandlers; +pub use trap::Trap; pub use traphandlers::{ catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, TrapHandler, TrapHandlerFn, }; pub use traphandlers::{init_traps, resume_panic}; -pub use wasmer_types::{Trap, TrapCode}; +pub use wasmer_types::TrapCode; diff --git a/lib/vm/src/trap/trap.rs b/lib/vm/src/trap/trap.rs new file mode 100644 index 00000000000..fdb20d3f590 --- /dev/null +++ b/lib/vm/src/trap/trap.rs @@ -0,0 +1,72 @@ +use backtrace::Backtrace; +use std::error::Error; +use wasmer_types::TrapCode; + +/// Stores trace message with backtrace. +#[derive(Debug)] +pub enum Trap { + /// A user-raised trap through `raise_user_trap`. + User(Box), + + /// A trap raised from the Wasm generated code + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Wasm { + /// The program counter in generated code where this trap happened. + pc: usize, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + /// Optional trapcode associated to the signal that caused the trap + signal_trap: Option, + }, + + /// A trap raised from a wasm libcall + /// + /// Note: this trap is deterministic (assuming a deterministic host implementation) + Lib { + /// Code of the trap. + trap_code: TrapCode, + /// Native stack backtrace at the time the trap occurred + backtrace: Backtrace, + }, + + /// A trap indicating that the runtime was unable to allocate sufficient memory. + /// + /// Note: this trap is nondeterministic, since it depends on the host system. + OOM { + /// Native stack backtrace at the time the OOM occurred + backtrace: Backtrace, + }, +} + +impl Trap { + /// Construct a new Wasm trap with the given source location and backtrace. + /// + /// Internally saves a backtrace when constructed. + pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option) -> Self { + Trap::Wasm { + pc, + backtrace, + signal_trap, + } + } + + /// Construct a new Wasm trap with the given trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn lib(trap_code: TrapCode) -> Self { + let backtrace = Backtrace::new_unresolved(); + Trap::Lib { + trap_code, + backtrace, + } + } + + /// Construct a new OOM trap with the given source location and trap code. + /// + /// Internally saves a backtrace when constructed. + pub fn oom() -> Self { + let backtrace = Backtrace::new_unresolved(); + Trap::OOM { backtrace } + } +} diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index 5d83ec93541..baecaf387f3 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -5,6 +5,7 @@ //! signalhandling mechanisms. use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline}; +use crate::Trap; use backtrace::Backtrace; use corosensei::stack::DefaultStack; use corosensei::trap::{CoroutineTrapHandler, TrapHandlerRegs}; @@ -20,7 +21,7 @@ use std::mem::MaybeUninit; use std::ptr::{self, NonNull}; use std::sync::atomic::{compiler_fence, AtomicPtr, Ordering}; use std::sync::{Mutex, Once}; -use wasmer_types::{Trap, TrapCode}; +use wasmer_types::TrapCode; cfg_if::cfg_if! { if #[cfg(unix)] {