diff --git a/Cargo.lock b/Cargo.lock index aa933fed4a..56a77539f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,15 +359,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "crc32fast" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" -dependencies = [ - "cfg-if", -] - [[package]] name = "criterion" version = "0.3.5" @@ -861,16 +852,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "libloading" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "llvm-sys" version = "120.2.1" @@ -1015,8 +996,6 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" dependencies = [ - "crc32fast", - "indexmap", "memchr", ] @@ -1930,7 +1909,6 @@ dependencies = [ "wasmer-compiler-cranelift", "wasmer-compiler-llvm", "wasmer-compiler-singlepass-near", - "wasmer-engine-dylib", "wasmer-engine-universal-near", "wasmer-near", "wasmprinter", @@ -1947,7 +1925,6 @@ dependencies = [ "tempfile", "thiserror", "wasmer-compiler-singlepass-near", - "wasmer-engine-dylib", "wasmer-engine-universal-near", "wasmer-near", ] @@ -1971,9 +1948,7 @@ dependencies = [ "wasmer-compiler-llvm", "wasmer-compiler-near", "wasmer-compiler-singlepass-near", - "wasmer-engine-dylib", "wasmer-engine-near", - "wasmer-engine-staticlib", "wasmer-engine-universal-near", "wasmer-near", "wasmer-types-near", @@ -2090,27 +2065,6 @@ dependencies = [ "wasmer-vm-near", ] -[[package]] -name = "wasmer-engine-dylib" -version = "2.1.0" -dependencies = [ - "cfg-if", - "enumset", - "leb128", - "libloading", - "loupe", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-compiler-near", - "wasmer-engine-near", - "wasmer-object", - "wasmer-types-near", - "wasmer-vm-near", - "which", -] - [[package]] name = "wasmer-engine-near" version = "2.1.2" @@ -2131,26 +2085,6 @@ dependencies = [ "wasmer-vm-near", ] -[[package]] -name = "wasmer-engine-staticlib" -version = "2.1.0" -dependencies = [ - "bincode", - "cfg-if", - "enumset", - "leb128", - "libloading", - "loupe", - "serde", - "tempfile", - "tracing", - "wasmer-compiler-near", - "wasmer-engine-near", - "wasmer-object", - "wasmer-types-near", - "wasmer-vm-near", -] - [[package]] name = "wasmer-engine-universal-near" version = "2.1.2" @@ -2197,7 +2131,6 @@ dependencies = [ "wasmer-compiler-near", "wasmer-compiler-singlepass-near", "wasmer-derive-near", - "wasmer-engine-dylib", "wasmer-engine-near", "wasmer-engine-universal-near", "wasmer-types-near", @@ -2206,16 +2139,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wasmer-object" -version = "2.1.0" -dependencies = [ - "object", - "thiserror", - "wasmer-compiler-near", - "wasmer-types-near", -] - [[package]] name = "wasmer-types-near" version = "2.1.2" @@ -2284,9 +2207,7 @@ dependencies = [ "wasmer-compiler-near", "wasmer-compiler-singlepass-near", "wasmer-engine-dummy", - "wasmer-engine-dylib", "wasmer-engine-near", - "wasmer-engine-staticlib", "wasmer-engine-universal-near", "wasmer-near", "wasmer-types-near", @@ -2344,17 +2265,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "which" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" -dependencies = [ - "either", - "lazy_static", - "libc", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index a4902cae15..29fbbc67a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,6 @@ wasmer-compiler-singlepass = { version = "=2.1.2", path = "lib/compiler-singlepa wasmer-compiler-llvm = { version = "2.0.0", path = "lib/compiler-llvm", optional = true } wasmer-engine = { version = "=2.1.2", path = "lib/engine", package = "wasmer-engine-near" } wasmer-engine-universal = { version = "=2.1.2", path = "lib/engine-universal", optional = true, package = "wasmer-engine-universal-near" } -wasmer-engine-dylib = { version = "2.0.0", path = "lib/engine-dylib", optional = true } -wasmer-engine-staticlib = { version = "2.0.0", path = "lib/engine-staticlib", optional = true } wasmer-wast = { version = "2.0.0", path = "tests/lib/wast", optional = true } wasmer-cache = { version = "2.0.0", path = "lib/cache", optional = true } wasmer-types = { version = "=2.1.2", path = "lib/types", package = "wasmer-types-near" } @@ -38,9 +36,6 @@ members = [ "lib/derive", "lib/engine", "lib/engine-universal", - "lib/engine-dylib", - "lib/engine-staticlib", - "lib/object", "lib/vm", "lib/types", "tests/lib/wast", @@ -80,7 +75,6 @@ default = [ "wast", "universal", "singlepass", - "staticlib", "cache", ] engine = [] @@ -88,14 +82,6 @@ universal = [ "wasmer-engine-universal", "engine", ] -dylib = [ - "wasmer-engine-dylib", - "engine", -] -staticlib = [ - "wasmer-engine-staticlib", - "engine", -] cache = ["wasmer-cache"] wast = ["wasmer-wast"] wat = ["wasmer/wat"] @@ -103,8 +89,6 @@ compiler = [ "wasmer/compiler", "wasmer-compiler/translator", "wasmer-engine-universal/compiler", - "wasmer-engine-dylib/compiler", - "wasmer-engine-staticlib/compiler", ] singlepass = [ "wasmer-compiler-singlepass", @@ -129,11 +113,6 @@ test-cranelift = [ test-llvm = [ "llvm", ] - -test-dylib = [ - "dylib", - "test-generator/test-dylib", -] test-universal = [ "universal", "test-generator/test-universal", @@ -160,11 +139,6 @@ name = "engine-universal" path = "examples/engine_universal.rs" required-features = ["cranelift"] -[[example]] -name = "engine-dylib" -path = "examples/engine_dylib.rs" -required-features = ["cranelift"] - [[example]] name = "engine-headless" path = "examples/engine_headless.rs" diff --git a/examples/engine_dylib.rs b/examples/engine_dylib.rs deleted file mode 100644 index 4381ea0872..0000000000 --- a/examples/engine_dylib.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Defining an engine in Wasmer is one of the fundamental steps. -//! -//! This example illustrates how to use the `wasmer_engine_dylib`, -//! aka the Dylib engine. An engine applies roughly 2 steps: -//! -//! 1. It compiles the Wasm module bytes to executable code, through -//! the intervention of a compiler, -//! 2. It stores the executable code somewhere. -//! -//! In the particular context of the Dylib engine, the executable code -//! is stored in a shared object (`.dylib`, `.so` or `.dll` file). -//! -//! You can run the example directly by executing in Wasmer root: -//! -//! ```shell -//! cargo run --example engine-dylib --release --features "cranelift" -//! ``` -//! -//! Ready? - -use wasmer::{imports, wat2wasm, Instance, Module, Store, Value}; -use wasmer_compiler_cranelift::Cranelift; -use wasmer_engine_dylib::Dylib; - -fn main() -> Result<(), Box> { - // Let's declare the Wasm module with the text representation. - let wasm_bytes = wat2wasm( - r#" -(module - (type $sum_t (func (param i32 i32) (result i32))) - (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) - local.get $x - local.get $y - i32.add) - (export "sum" (func $sum_f))) -"# - .as_bytes(), - )?; - - // Define a compiler configuration. - // - // In this situation, the compiler is - // `wasmer_compiler_cranelift`. The compiler is responsible to - // compile the Wasm module into executable code. - let compiler_config = Cranelift::default(); - - println!("Creating Dylib engine..."); - // Define the engine that will drive everything. - // - // In this case, the engine is `wasmer_engine_dylib` which means - // that a shared object is going to be generated. - let engine = Dylib::new(compiler_config).engine(); - - // Create a store, that holds the engine. - let store = Store::new(&engine); - - println!("Compiling module..."); - // Here we go. - // - // Let's compile the Wasm module. It is at this step that the Wasm - // text is transformed into Wasm bytes (if necessary), and then - // compiled to executable code by the compiler, which is then - // stored into a shared object by the engine. - let module = Module::new(&store, wasm_bytes)?; - - // Congrats, the Wasm module is compiled! Now let's execute it for - // the sake of having a complete example. - - // Create an import object. Since our Wasm module didn't declare - // any imports, it's an empty object. - let import_object = imports! {}; - - println!("Instantiating module..."); - // And here we go again. Let's instantiate the Wasm module. - let instance = Instance::new(&module, &import_object)?; - - println!("Calling `sum` function..."); - // The Wasm module exports a function called `sum`. - let sum = instance.exports.get_function("sum")?; - let results = sum.call(&[Value::I32(1), Value::I32(2)])?; - - println!("Results: {:?}", results); - assert_eq!(results.to_vec(), vec![Value::I32(3)]); - - Ok(()) -} - -#[test] -#[cfg(not(any(target_arch = "aarch64", target_env = "musl")))] -fn test_engine_dylib() -> Result<(), Box> { - main() -} diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index efbd50f15c..c5fd28a5bd 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -17,7 +17,6 @@ wasmer-compiler-cranelift = { path = "../lib/compiler-cranelift", optional = tru wasmer-compiler-llvm = { path = "../lib/compiler-llvm", optional = true } wasmer-compiler-singlepass = { path = "../lib/compiler-singlepass", package = "wasmer-compiler-singlepass-near", optional = true } wasmer-engine-universal = { path = "../lib/engine-universal", package = "wasmer-engine-universal-near", optional = true } -wasmer-engine-dylib = { path = "../lib/engine-dylib", optional = true } wasmprinter = "0.2" [features] @@ -25,7 +24,6 @@ cranelift = [ "wasmer-compiler-cranelift" ] llvm = [ "wasmer-compiler-llvm" ] singlepass = [ "wasmer-compiler-singlepass" ] universal = [ "wasmer-engine-universal" ] -dylib = [ "wasmer-engine-dylib" ] [[bin]] name = "equivalence_universal" @@ -52,12 +50,7 @@ name = "metering" path = "fuzz_targets/metering.rs" required-features = ["universal", "cranelift"] -[[bin]] -name = "dylib_cranelift" -path = "fuzz_targets/dylib_cranelift.rs" -required-features = ["dylib", "cranelift"] - [[bin]] name = "deterministic" path = "fuzz_targets/deterministic.rs" -required-features = ["universal", "dylib", "cranelift", "llvm", "singlepass"] +required-features = ["universal", "cranelift", "llvm", "singlepass"] diff --git a/fuzz/fuzz_targets/dylib_cranelift.rs b/fuzz/fuzz_targets/dylib_cranelift.rs deleted file mode 100644 index 66be05383d..0000000000 --- a/fuzz/fuzz_targets/dylib_cranelift.rs +++ /dev/null @@ -1,64 +0,0 @@ -#![no_main] - -use libfuzzer_sys::{arbitrary, arbitrary::Arbitrary, fuzz_target}; -use wasm_smith::{Config, ConfiguredModule}; -use wasmer::{imports, Instance, Module, Store}; -use wasmer_compiler_cranelift::Cranelift; -use wasmer_engine_dylib::Dylib; - -#[derive(Arbitrary, Debug, Default, Copy, Clone)] -struct NoImportsConfig; -impl Config for NoImportsConfig { - fn max_imports(&self) -> usize { - 0 - } - fn max_memory_pages(&self) -> u32 { - // https://github.com/wasmerio/wasmer/issues/2187 - 65535 - } - fn allow_start_export(&self) -> bool { - false - } -} -#[derive(Arbitrary)] -struct WasmSmithModule(ConfiguredModule); -impl std::fmt::Debug for WasmSmithModule { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&wasmprinter::print_bytes(self.0.to_bytes()).unwrap()) - } -} - -fuzz_target!(|module: WasmSmithModule| { - let serialized = { - let wasm_bytes = module.0.to_bytes(); - - if let Ok(path) = std::env::var("DUMP_TESTCASE") { - use std::fs::File; - use std::io::Write; - let mut file = File::create(path).unwrap(); - file.write_all(&wasm_bytes).unwrap(); - return; - } - - let compiler = Cranelift::default(); - let store = Store::new(&Dylib::new(compiler).engine()); - let module = Module::new(&store, &wasm_bytes).unwrap(); - module.serialize().unwrap() - }; - - let engine = Dylib::headless().engine(); - let store = Store::new(&engine); - let module = unsafe { Module::deserialize(&store, serialized.as_slice()) }.unwrap(); - match Instance::new(&module, &imports! {}) { - Ok(_) => {} - Err(e) => { - let error_message = format!("{}", e); - if error_message.starts_with("RuntimeError: ") - && error_message.contains("out of bounds") - { - return; - } - panic!("{}", e); - } - } -}); diff --git a/lib/README.md b/lib/README.md index 5d917800c7..1a946a7feb 100644 --- a/lib/README.md +++ b/lib/README.md @@ -3,50 +3,22 @@ The philosophy of Wasmer is to be very modular by design. It's composed of a set of crates. We can group them as follows: -* `api` — The public Rust or JS API exposes everything a user needs to use Wasmer - programatically through the `wasmer` crate, -* `c-api` — The public C API exposes everything a C user needs to use - Wasmer programatically, -* `cache` — The traits and types to cache compiled WebAssembly - modules, +* `api` — The public Rust API exposes everything a user needs to use Wasmer programatically through + the `wasmer` crate, +* `cache` — The traits and types to cache compiled WebAssembly modules, * `cli` — The Wasmer CLI itself, * `compiler` — The base for the compiler implementations, it defines the framework for the compilers and provides everything they need: - * `compiler-cranelift` — A WebAssembly compiler based on the - Cranelift compiler infrastructure, - * `compiler-llvm` — A WebAssembly compiler based on the LLVM - compiler infrastructure; recommended for runtime speed - performance, - * `compiler-singlepass` — A WebAssembly compiler based on our own - compilation infrastructure; recommended for compilation-time speed - performance. + * `compiler-cranelift` — A WebAssembly compiler based on the Cranelift compiler infrastructure, + * `compiler-llvm` — A WebAssembly compiler based on the LLVM compiler infrastructure; recommended + for runtime speed performance, + * `compiler-singlepass` — A WebAssembly compiler based on our own compilation infrastructure; + recommended for compilation-time speed performance. * `derive` — A set of procedural macros used inside Wasmer, -* ABI: - * `emscripten` — Emscripten ABI implementation inside Wasmer, - * `wasi` — WASI ABI implementation inside Wasmer: - * `wasi-types` — All the WASI types, - * `wasi-experimental-io-devices` — An experimental extension of - WASI for basic graphics. -* `engine` — The general abstraction for creating an engine, which is - responsible of leading the compiling and running flow. Using the - same compiler, the runtime performance will be approximately the - same, however the way it stores and loads the executable code will - differ: - * `engine-universal` — stores the code in a custom file format, and - loads it in memory, - * `engine-dylib` — stores Position-Independent Code in a native - shared object library (`.dylib`, `.so`, `.dll`) and loads it with - Operating System shared library loader (via `dlopen`), - * `engine-staticlib` — stores executable code in a native static - object library, in addition to emitting a C header file, which - both can be linked against a sandboxed WebAssembly runtime - environment for the compiled module with no need for runtime - compilation, - * `object` — A library to cross-generate native objects for various - platforms. -* `middlewares` — A collection of middlewares, like `metering` that - tracks how many operators are executed in total and putting a limit - on the total number of operators executed, +* `engine` — The general abstraction for creating an engine, which is responsible of leading the + compiling and running flow. Using the same compiler, the runtime performance will be + approximately the same, however the way it stores and loads the executable code will differ: + * `engine-universal` — stores the code in a custom file format, and loads it in memory, * `types` — The basic structures to use WebAssembly, * `vm` — The Wasmer VM runtime library, the low-level base of everything. diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 68168ea035..09e3d743e3 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -38,7 +38,6 @@ wasmer-compiler-singlepass = { path = "../compiler-singlepass", package = "wasme wasmer-compiler-cranelift = { path = "../compiler-cranelift", version = "2.1.0", optional = true } wasmer-compiler-llvm = { path = "../compiler-llvm", version = "2.1.0", optional = true } wasmer-engine-universal = { path = "../engine-universal", package = "wasmer-engine-universal-near", version = "=2.1.2", optional = true } -wasmer-engine-dylib = { path = "../engine-dylib", version = "2.1.0", optional = true } # - Mandatory dependencies for `sys` on Windows. [target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies] winapi = "0.3" @@ -64,7 +63,6 @@ compiler = [ "sys", "wasmer-compiler/translator", "wasmer-engine-universal/compiler", - "wasmer-engine-dylib/compiler", ] singlepass = [ "compiler", @@ -97,27 +95,16 @@ engine = ["sys"] "engine", "wasmer-engine-universal", ] - dylib = [ - "engine", - "wasmer-engine-dylib", - ] default-engine = [] default-universal = [ "default-engine", "universal", ] - default-dylib = [ - "default-engine", - "dylib", - ] # - Experimental / in-development features experimental-reference-types-extern-ref = [ "sys", "wasmer-types/experimental-reference-types-extern-ref", ] -# - Deprecated features. -jit = ["universal"] -native = ["dylib"] [package.metadata.docs.rs] -features = ["compiler", "core", "cranelift", "default-compiler", "default-dylib", "default-engine", "dylib", "engine", "jit", "llvm", "native", "singlepass", "sys", "sys-default", "universal"] +features = ["compiler", "core", "cranelift", "default-compiler", "default-engine", "engine", "jit", "llvm", "native", "singlepass", "sys", "sys-default", "universal"] diff --git a/lib/cache/Cargo.toml b/lib/cache/Cargo.toml index dd77022233..b8ed72cace 100644 --- a/lib/cache/Cargo.toml +++ b/lib/cache/Cargo.toml @@ -22,7 +22,6 @@ tempfile = "3" rand = "0.8.3" wasmer-compiler-singlepass = { path = "../compiler-singlepass", package = "wasmer-compiler-singlepass-near", version = "2.0.3" } wasmer-engine-universal = { path = "../engine-universal", package = "wasmer-engine-universal-near", version = "2.0.3" } -wasmer-engine-dylib = { path = "../engine-dylib", version = "2.0.0" } [features] blake3-pure = ["blake3/pure"] diff --git a/lib/cli/Cargo.toml b/lib/cli/Cargo.toml index eb3e5a1769..2447393d74 100644 --- a/lib/cli/Cargo.toml +++ b/lib/cli/Cargo.toml @@ -31,8 +31,6 @@ wasmer-compiler-singlepass = { version = "2.0.3", path = "../compiler-singlepass wasmer-compiler-llvm = { version = "2.0.0", path = "../compiler-llvm", optional = true } wasmer-engine = { version = "2.0.3", path = "../engine", package = "wasmer-engine-near" } wasmer-engine-universal = { version = "2.0.3", path = "../engine-universal", package = "wasmer-engine-universal-near", optional = true } -wasmer-engine-dylib = { version = "2.0.0", path = "../engine-dylib", optional = true } -wasmer-engine-staticlib = { version = "2.0.0", path = "../engine-staticlib", optional = true } wasmer-vm = { version = "2.0.3", path = "../vm", package = "wasmer-vm-near" } wasmer-wast = { version = "2.0.0", path = "../../tests/lib/wast", optional = true } wasmer-cache = { version = "2.0.0", path = "../cache", optional = true } @@ -58,8 +56,6 @@ default = [ "wat", "wast", "universal", - "dylib", - "staticlib", "cache", ] engine = [] @@ -67,14 +63,6 @@ universal = [ "wasmer-engine-universal", "engine", ] -dylib = [ - "wasmer-engine-dylib", - "engine", -] -staticlib = [ - "wasmer-engine-staticlib", - "engine", -] cache = ["wasmer-cache"] cache-blake3-pure = ["wasmer-cache/blake3-pure"] wast = ["wasmer-wast"] @@ -82,8 +70,6 @@ wat = ["wasmer/wat"] compiler = [ "wasmer-compiler/translator", "wasmer-engine-universal/compiler", - "wasmer-engine-dylib/compiler", - "wasmer-engine-staticlib/compiler", ] singlepass = [ "wasmer-compiler-singlepass", @@ -100,8 +86,7 @@ llvm = [ debug = ["fern", "log"] disable-all-logging = [] headless = [] -headless-minimal = ["headless", "disable-all-logging", "dylib", "universal"] +headless-minimal = ["headless", "disable-all-logging", "universal"] # Deprecated features. jit = ["universal"] -native = ["dylib"] diff --git a/lib/engine-dylib/Cargo.toml b/lib/engine-dylib/Cargo.toml deleted file mode 100644 index dc012fdff4..0000000000 --- a/lib/engine-dylib/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "wasmer-engine-dylib" -version = "2.1.0" -description = "Wasmer Dylib Engine" -categories = ["wasm"] -keywords = ["wasm", "webassembly", "engine", "dylib"] -authors = ["Wasmer Engineering Team "] -repository = "https://github.com/wasmerio/wasmer" -license = "MIT" -readme = "README.md" -edition = "2018" - -[dependencies] -wasmer-types = { path = "../types", package = "wasmer-types-near", version = "2.0.3" } -wasmer-compiler = { path = "../compiler", package = "wasmer-compiler-near", version = "2.0.3" } -wasmer-vm = { path = "../vm", version = "2.0.3", package = "wasmer-vm-near", features = ["enable-rkyv"] } -wasmer-engine = { path = "../engine", package = "wasmer-engine-near", version = "2.0.3" } -wasmer-object = { path = "../object", version = "2.0.0" } -serde = { version = "1.0", features = ["derive", "rc"] } -cfg-if = "1.0" -tracing = { version = "0.1", features = ["log"] } -leb128 = "0.2" -libloading = "0.7" -tempfile = "3.1" -which = "4.0" -rkyv = "0.7.20" -loupe = "0.1" -enumset = "1.0" - -[features] -# Enable the `compiler` feature if you want the engine to compile -# and not be only on headless mode. -compiler = ["wasmer-compiler/translator"] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/lib/engine-dylib/README.md b/lib/engine-dylib/README.md deleted file mode 100644 index 90987642a7..0000000000 --- a/lib/engine-dylib/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# `wasmer-engine-dylib` [![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) - -The Wasmer Dylib engine is usable with any compiler implementation -based on [`wasmer-compiler`] that is able to emit -[Position-Independent Code][PIC] (PIC). - -After the compiler generates the machine code for the functions, the -Dylib Engine generates a shared object file and links it via [`dlsym`] -so it can be usable by the [`wasmer`] API. - -This allows Wasmer to achieve *blazing fast* **native startup times**. - -*Note: you can find a [full working example using the Dylib engine -here][example].* - -### Difference with `wasmer-engine-universal` - -The Dylib Engine and Universal Engine mainly differ on how the Modules -are loaded/stored. Using the same compilers, both will have the same -runtime speed. - -However, the Dylib Engine uses the Operating System shared library -loader (via `dlopen`) and as such is able to achieve a much faster -startup time when deserializing a serialized `Module`. - -## Requirements - -The `wasmer-engine-dylib` crate requires a linker available on -your system to generate the shared object file. - -We recommend having [`gcc`] or [`clang`] installed. - -> Note: when **cross-compiling** to other targets, `clang` will be the -> default command used for compiling. - -You can install LLVM (that provides `clang`) easily on your -Debian-like system via this command: - -```bash -bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" -``` - -Or in macOS: - -```bash -brew install llvm -``` - -Or via any of the [pre-built binaries that LLVM -offers][llvm-pre-built]. - - -[`wasmer-compiler`]: https://github.com/wasmerio/wasmer/tree/master/lib/compiler -[PIC]: https://en.wikipedia.org/wiki/Position-independent_code -[`dlsym`]: https://www.freebsd.org/cgi/man.cgi?query=dlsym -[`wasmer`]: https://github.com/wasmerio/wasmer/tree/master/lib/api -[example]: https://github.com/wasmerio/wasmer/blob/master/examples/engine_dylib.rs -[`gcc`]: https://gcc.gnu.org/ -[`clang`]: https://clang.llvm.org/ -[llvm-pre-built]: https://releases.llvm.org/download.html diff --git a/lib/engine-dylib/src/artifact.rs b/lib/engine-dylib/src/artifact.rs deleted file mode 100644 index d1d1822766..0000000000 --- a/lib/engine-dylib/src/artifact.rs +++ /dev/null @@ -1,890 +0,0 @@ -//! Define `DylibArtifact` to allow compiling and instantiating -//! to be done as separate steps. - -use crate::engine::{DylibEngine, DylibEngineInner}; -use crate::serialize::{ArchivedModuleMetadata, ModuleMetadata}; -use enumset::EnumSet; -use libloading::{Library, Symbol as LibrarySymbol}; -use loupe::MemoryUsage; -use std::error::Error; -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -#[cfg(feature = "compiler")] -use std::process::Command; -use std::sync::{Arc, Mutex}; -use tempfile::NamedTempFile; -use tracing::log::error; -#[cfg(feature = "compiler")] -use tracing::trace; -use wasmer_compiler::{ - Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features, - FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple, -}; -#[cfg(feature = "compiler")] -use wasmer_compiler::{ - CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain, - ModuleTranslationState, -}; -use wasmer_engine::{ - register_frame_info, Artifact, DeserializeError, FunctionExtent, GlobalFrameInfoRegistration, - InstantiationError, SerializeError, -}; -#[cfg(feature = "compiler")] -use wasmer_engine::{Engine, Tunables}; -#[cfg(feature = "compiler")] -use wasmer_object::{emit_compilation, emit_data, get_object_for_target}; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; -#[cfg(feature = "compiler")] -use wasmer_types::DataInitializer; -use wasmer_types::{ - FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, - SignatureIndex, TableIndex, -}; -use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMFunctionBody, - VMSharedSignatureIndex, VMTrampoline, -}; - -/// A compiled Wasm module, ready to be instantiated. -#[derive(MemoryUsage)] -pub struct DylibArtifact { - dylib_path: PathBuf, - is_temporary: bool, - metadata: ModuleMetadata, - finished_functions: BoxedSlice, - #[loupe(skip)] - finished_function_call_trampolines: BoxedSlice, - finished_dynamic_function_trampolines: BoxedSlice, - func_data_registry: Arc, - signatures: BoxedSlice, - frame_info_registration: Mutex>, -} - -impl Drop for DylibArtifact { - fn drop(&mut self) { - if self.is_temporary { - if let Err(err) = std::fs::remove_file(&self.dylib_path) { - error!("cannot delete the temporary dylib artifact: {}", err); - } - } - } -} - -fn to_compile_error(err: impl Error) -> CompileError { - CompileError::Codegen(err.to_string()) -} - -const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA"; - -impl DylibArtifact { - // Mach-O header in iOS/Mac - #[allow(dead_code)] - const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254]; - - // ELF Magic header for Linux (32 bit) - #[allow(dead_code)] - const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1]; - - // ELF Magic header for Linux (64 bit) - #[allow(dead_code)] - const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2]; - - // COFF Magic header for Windows (64 bit) - #[allow(dead_code)] - const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z']; - - /// Check if the provided bytes look like `DylibArtifact`. - /// - /// This means, if the bytes look like a shared object file in the target - /// system. - pub fn is_deserializable(bytes: &[u8]) -> bool { - cfg_if::cfg_if! { - if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] { - bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64) - } - else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] { - bytes.starts_with(Self::MAGIC_HEADER_ELF_64) - } - else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] { - bytes.starts_with(Self::MAGIC_HEADER_ELF_32) - } - else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] { - bytes.starts_with(Self::MAGIC_HEADER_COFF_64) - } - else { - false - } - } - } - - #[cfg(feature = "compiler")] - /// Generate a compilation - fn generate_metadata<'data>( - data: &'data [u8], - features: &Features, - compiler: &dyn Compiler, - tunables: &dyn Tunables, - ) -> Result< - ( - CompileModuleInfo, - PrimaryMap>, - Vec>, - Option, - ), - CompileError, - > { - let environ = ModuleEnvironment::new(); - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - - // 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 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 compile_info = CompileModuleInfo { - module: Arc::new(module), - features: features.clone(), - memory_styles, - table_styles, - }; - Ok(( - compile_info, - translation.function_body_inputs, - translation.data_initializers, - translation.module_translation_state, - )) - } - - /// Compile a data buffer into a `DylibArtifact`, which may - /// then be instantiated. - #[cfg(feature = "compiler")] - pub fn new( - engine: &DylibEngine, - data: &[u8], - tunables: &dyn Tunables, - ) -> Result { - let mut engine_inner = engine.inner_mut(); - let target = engine.target(); - let compiler = engine_inner.compiler()?; - let (compile_info, function_body_inputs, data_initializers, module_translation) = - Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?; - - let data_initializers = data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(); - - let target_triple = target.triple(); - - /* - // We construct the function body lengths - let function_body_lengths = compilation - .get_function_bodies() - .values() - .map(|function_body| function_body.body.len() as u64) - .map(|_function_body| 0u64) - .collect::>(); - */ - - // TODO: we currently supply all-zero function body lengths. - // We don't know the lengths until they're compiled, yet we have to - // supply the metadata as an input to the compile. - let function_body_lengths = function_body_inputs - .keys() - .map(|_function_body| 0u64) - .collect::>(); - - let mut metadata = ModuleMetadata { - compile_info, - prefix: engine_inner.get_prefix(&data), - data_initializers, - function_body_lengths, - cpu_features: target.cpu_features().as_u64(), - }; - - let serialized_data = metadata.serialize()?; - - let mut metadata_binary = vec![0; 12]; - let mut writable = &mut metadata_binary[..]; - leb128::write::unsigned(&mut writable, serialized_data.len() as u64) - .expect("Should write number"); - metadata_binary.extend(serialized_data); - - let (compile_info, symbol_registry) = metadata.split(); - - // Ensure that we pass information about signals in module metadata. - compile_info.features.signal_less(!compiler.use_signals()); - - let maybe_obj_bytes = compiler.experimental_native_compile_module( - &target, - &compile_info, - module_translation.as_ref().unwrap(), - &function_body_inputs, - &symbol_registry, - &metadata_binary, - ); - - let filepath = match maybe_obj_bytes { - Some(obj_bytes) => { - let obj_bytes = obj_bytes?; - let file = tempfile::Builder::new() - .prefix("wasmer_dylib_") - .suffix(".o") - .tempfile() - .map_err(to_compile_error)?; - - // Re-open it. - let (mut file, filepath) = file.keep().map_err(to_compile_error)?; - file.write_all(&obj_bytes).map_err(to_compile_error)?; - filepath - } - None => { - let compilation = compiler.compile_module( - &target, - &compile_info, - module_translation.as_ref().unwrap(), - function_body_inputs, - )?; - let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?; - emit_data( - &mut obj, - WASMER_METADATA_SYMBOL, - &metadata_binary, - std::mem::align_of::() as u64, - ) - .map_err(to_compile_error)?; - emit_compilation(&mut obj, compilation, &symbol_registry, &target_triple) - .map_err(to_compile_error)?; - let file = tempfile::Builder::new() - .prefix("wasmer_dylib_") - .suffix(".o") - .tempfile() - .map_err(to_compile_error)?; - - // Re-open it. - let (mut file, filepath) = file.keep().map_err(to_compile_error)?; - let obj_bytes = obj.write().map_err(to_compile_error)?; - - file.write_all(&obj_bytes).map_err(to_compile_error)?; - filepath - } - }; - - let output_filepath = { - let suffix = format!(".{}", Self::get_default_extension(&target_triple)); - let shared_file = tempfile::Builder::new() - .prefix("wasmer_dylib_") - .suffix(&suffix) - .tempfile() - .map_err(to_compile_error)?; - shared_file - .into_temp_path() - .keep() - .map_err(to_compile_error)? - }; - - let is_cross_compiling = engine_inner.is_cross_compiling(); - let target_triple_str = { - let into_str = target_triple.to_string(); - // We have to adapt the target triple string, because otherwise - // Apple's clang will not recognize it. - if into_str == "aarch64-apple-darwin" { - "arm64-apple-darwin".to_string() - } else { - into_str - } - }; - - // Set 'isysroot' clang flag if compiling to iOS target - let ios_compile_target = target_triple.operating_system == OperatingSystem::Ios; - let ios_sdk_flag = { - if ios_compile_target { - if target_triple.architecture == Architecture::X86_64 { - "-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk" - } else { - "-isysroot/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk" - } - } else { - "" - } - }; - let ios_sdk_lib = { - if ios_compile_target { - "-lSystem" - } else { - "" - } - }; - - // Get the location of the 'ld' linker for clang - let fuse_linker = { - let ld_install = which::which("ld"); - if ios_compile_target && ld_install.is_ok() { - ld_install.unwrap().into_os_string().into_string().unwrap() - } else { - "lld".to_string() - } - }; - - let cross_compiling_args: Vec = if is_cross_compiling { - vec![ - format!("--target={}", target_triple_str), - format!("-fuse-ld={}", fuse_linker), - "-nodefaultlibs".to_string(), - "-nostdlib".to_string(), - ios_sdk_flag.to_string(), - ios_sdk_lib.to_string(), - ] - } else { - // We are explicit on the target when the host system is - // Apple Silicon, otherwise compilation fails. - if target_triple_str == "arm64-apple-darwin" { - vec![format!("--target={}", target_triple_str)] - } else { - vec![] - } - }; - let target_args = match (target_triple.operating_system, is_cross_compiling) { - (OperatingSystem::Windows, true) => vec!["-Wl,/force:unresolved,/noentry"], - (OperatingSystem::Windows, false) => vec!["-Wl,-undefined,dynamic_lookup"], - _ => vec!["-nostartfiles", "-Wl,-undefined,dynamic_lookup"], - }; - trace!( - "Compiling for target {} from host {}", - target_triple_str, - Triple::host().to_string(), - ); - - let notext = match (target_triple.operating_system, target_triple.architecture) { - (OperatingSystem::Linux, Architecture::X86_64) => vec!["-Wl,-z,notext"], - _ => vec![], - }; - - let linker = engine_inner.linker().executable(); - let output = Command::new(linker) - .arg(&filepath) - .arg("-o") - .arg(&output_filepath) - .args(&target_args) - // .args(&wasmer_symbols) - .arg("-shared") - .args(¬ext) - .args(&cross_compiling_args) - .arg("-v") - .output() - .map_err(to_compile_error); - - if fs::metadata(&filepath).is_ok() { - fs::remove_file(filepath).map_err(to_compile_error)?; - } - - let output = output?; - - if !output.status.success() { - return Err(CompileError::Codegen(format!( - "Shared object file generator failed with:\nstderr:{}\nstdout:{}", - String::from_utf8_lossy(&output.stderr).trim_end(), - String::from_utf8_lossy(&output.stdout).trim_end() - ))); - } - - trace!("gcc command result {:?}", output); - - let mut artifact = if is_cross_compiling { - Self::from_parts_crosscompiled(metadata, output_filepath) - } else { - let lib = unsafe { Library::new(&output_filepath).map_err(to_compile_error)? }; - Self::from_parts(&mut engine_inner, metadata, output_filepath, lib) - }?; - artifact.is_temporary = true; - - Ok(artifact) - } - - /// Get the default extension when serializing this artifact - pub fn get_default_extension(triple: &Triple) -> &'static str { - match triple.operating_system { - OperatingSystem::Windows => "dll", - OperatingSystem::Darwin | OperatingSystem::Ios | OperatingSystem::MacOSX { .. } => { - "dylib" - } - _ => "so", - } - } - - /// Construct a `DylibArtifact` from component parts. - pub fn from_parts_crosscompiled( - metadata: ModuleMetadata, - dylib_path: PathBuf, - ) -> Result { - let finished_functions: PrimaryMap = PrimaryMap::new(); - let finished_function_call_trampolines: PrimaryMap = - PrimaryMap::new(); - let finished_dynamic_function_trampolines: PrimaryMap = - PrimaryMap::new(); - let signatures: PrimaryMap = PrimaryMap::new(); - Ok(Self { - dylib_path, - is_temporary: false, - metadata, - finished_functions: finished_functions.into_boxed_slice(), - finished_function_call_trampolines: finished_function_call_trampolines - .into_boxed_slice(), - finished_dynamic_function_trampolines: finished_dynamic_function_trampolines - .into_boxed_slice(), - func_data_registry: Arc::new(FuncDataRegistry::new()), - signatures: signatures.into_boxed_slice(), - frame_info_registration: Mutex::new(None), - }) - } - - /// Construct a `DylibArtifact` from component parts. - pub fn from_parts( - engine_inner: &mut DylibEngineInner, - metadata: ModuleMetadata, - dylib_path: PathBuf, - lib: Library, - ) -> Result { - let mut finished_functions: PrimaryMap = - PrimaryMap::new(); - for (function_local_index, _function_len) in metadata.function_body_lengths.iter() { - let function_name = metadata - .get_symbol_registry() - .symbol_to_name(Symbol::LocalFunction(function_local_index)); - unsafe { - // We use a fake function signature `fn()` because we just - // want to get the function address. - let func: LibrarySymbol = lib - .get(function_name.as_bytes()) - .map_err(to_compile_error)?; - finished_functions.push(FunctionBodyPtr( - func.into_raw().into_raw() as *const VMFunctionBody - )); - } - } - - // Retrieve function call trampolines - let mut finished_function_call_trampolines: PrimaryMap = - PrimaryMap::with_capacity(metadata.compile_info.module.signatures.len()); - for sig_index in metadata.compile_info.module.signatures.keys() { - let function_name = metadata - .get_symbol_registry() - .symbol_to_name(Symbol::FunctionCallTrampoline(sig_index)); - unsafe { - let trampoline: LibrarySymbol = lib - .get(function_name.as_bytes()) - .map_err(to_compile_error)?; - let raw = *trampoline.into_raw(); - finished_function_call_trampolines.push(raw); - } - } - - // Retrieve dynamic function trampolines (only for imported functions) - let mut finished_dynamic_function_trampolines: PrimaryMap = - PrimaryMap::with_capacity(metadata.compile_info.module.num_imported_functions); - for func_index in metadata - .compile_info - .module - .functions - .keys() - .take(metadata.compile_info.module.num_imported_functions) - { - let function_name = metadata - .get_symbol_registry() - .symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index)); - unsafe { - let trampoline: LibrarySymbol = lib - .get(function_name.as_bytes()) - .map_err(to_compile_error)?; - finished_dynamic_function_trampolines.push(FunctionBodyPtr( - trampoline.into_raw().into_raw() as *const VMFunctionBody, - )); - } - } - - // Leaving frame infos from now, as they are not yet used - // however they might be useful for the future. - // let frame_infos = compilation - // .get_frame_info() - // .values() - // .map(|frame_info| SerializableFunctionFrameInfo::Processed(frame_info.clone())) - // .collect::>(); - // Self::from_parts(&mut engine_inner, lib, metadata, ) - // let frame_info_registration = register_frame_info( - // serializable.module.clone(), - // &finished_functions, - // serializable.compilation.function_frame_info.clone(), - // ); - - // Compute indices into the shared signature table. - let signatures = { - metadata - .compile_info - .module - .signatures - .values() - .map(|sig| engine_inner.signatures().register(sig)) - .collect::>() - }; - - engine_inner.add_library(lib); - - Ok(Self { - dylib_path, - is_temporary: false, - metadata, - finished_functions: finished_functions.into_boxed_slice(), - finished_function_call_trampolines: finished_function_call_trampolines - .into_boxed_slice(), - finished_dynamic_function_trampolines: finished_dynamic_function_trampolines - .into_boxed_slice(), - func_data_registry: engine_inner.func_data().clone(), - signatures: signatures.into_boxed_slice(), - frame_info_registration: Mutex::new(None), - }) - } - - /// Compile a data buffer into a `DylibArtifact`, which may - /// then be instantiated. - #[cfg(not(feature = "compiler"))] - pub fn new(_engine: &DylibEngine, _data: &[u8]) -> Result { - Err(CompileError::Codegen( - "Compilation is not enabled in the engine".to_string(), - )) - } - - /// Deserialize a `DylibArtifact` from bytes. - /// - /// # Safety - /// - /// The bytes must represent a serialized WebAssembly module. - pub unsafe fn deserialize( - engine: &DylibEngine, - bytes: &[u8], - ) -> Result { - if !Self::is_deserializable(&bytes) { - return Err(DeserializeError::Incompatible( - "The provided bytes are not in any native format Wasmer can understand".to_string(), - )); - } - // Dump the bytes into a file, so we can read it with our `dlopen` - let named_file = NamedTempFile::new()?; - let (mut file, path) = named_file.keep().map_err(|e| e.error)?; - file.write_all(&bytes)?; - // We already checked for the header, so we don't need - // to check again. - let mut artifact = Self::deserialize_from_file_unchecked(&engine, &path)?; - artifact.is_temporary = true; - - Ok(artifact) - } - - /// Deserialize a `DylibArtifact` from a file path. - /// - /// # Safety - /// - /// The file's content must represent a serialized WebAssembly module. - pub unsafe fn deserialize_from_file( - engine: &DylibEngine, - path: &Path, - ) -> Result { - let mut file = File::open(&path)?; - let mut buffer = [0; 5]; - // read up to 5 bytes - file.read_exact(&mut buffer)?; - if !Self::is_deserializable(&buffer) { - return Err(DeserializeError::Incompatible( - "The provided bytes are not in any native format Wasmer can understand".to_string(), - )); - } - Self::deserialize_from_file_unchecked(&engine, &path) - } - - /// Deserialize a `DylibArtifact` from a file path (unchecked). - /// - /// # Safety - /// - /// The file's content must represent a serialized WebAssembly module. - pub unsafe fn deserialize_from_file_unchecked( - engine: &DylibEngine, - path: &Path, - ) -> Result { - let lib = Library::new(&path).map_err(|e| { - DeserializeError::CorruptedBinary(format!("Library loading failed: {}", e)) - })?; - let shared_path: PathBuf = PathBuf::from(path); - // We use 12 + 1, as the length of the module will take 12 bytes - // (we construct it like that in `metadata_length`) and we also want - // to take the first element of the data to construct the slice from - // it. - let symbol: LibrarySymbol<*mut [u8; 12 + 1]> = - lib.get(WASMER_METADATA_SYMBOL).map_err(|e| { - DeserializeError::CorruptedBinary(format!( - "The provided object file doesn't seem to be generated by Wasmer: {}", - e - )) - })?; - use std::ops::Deref; - use std::slice; - - let size = &mut **symbol.deref(); - let mut readable = &size[..]; - let metadata_len = leb128::read::unsigned(&mut readable).map_err(|_e| { - DeserializeError::CorruptedBinary("Can't read metadata size".to_string()) - })?; - let metadata_slice: &'static [u8] = - slice::from_raw_parts(&size[12] as *const u8, metadata_len as usize); - - let metadata = ModuleMetadata::deserialize(metadata_slice)?; - - let mut engine_inner = engine.inner_mut(); - - Self::from_parts(&mut engine_inner, metadata, shared_path, lib) - .map_err(DeserializeError::Compiler) - } - - /// Used in test deserialize metadata is correct - pub fn metadata(&self) -> &ModuleMetadata { - &self.metadata - } -} - -impl Artifact for DylibArtifact { - fn module(&self) -> Arc { - self.metadata.compile_info.module.clone() - } - - fn module_ref(&self) -> &ModuleInfo { - &self.metadata.compile_info.module - } - - fn module_mut(&mut self) -> Option<&mut ModuleInfo> { - Arc::get_mut(&mut self.metadata.compile_info.module) - } - - fn register_frame_info(&self) { - let mut info = self.frame_info_registration.lock().unwrap(); - - if info.is_some() { - return; - } - - // We (reverse) order all the functions by their pointer location. - // [f9, f6, f7, f8...] and calculate their potential function body size by - // getting the diff in pointers between functions (since they are all located - // in the same __text section). - - let min_call_trampolines_pointer = self - .finished_function_call_trampolines - .values() - .map(|t| *t as usize) - .min() - .unwrap_or(0); - let min_dynamic_trampolines_pointer = self - .finished_dynamic_function_trampolines - .values() - .map(|t| **t as usize) - .min() - .unwrap_or(0); - - let fp = self.finished_functions.clone(); - let mut function_pointers = fp.into_iter().collect::>(); - - // Sort the keys by the funciton pointer values in reverse order. - // This way we can get the maximum function lengths (since functions can't overlap in memory) - function_pointers.sort_by(|(_k1, v1), (_k2, v2)| v2.cmp(v1)); - let mut iter = function_pointers.into_iter(); - let mut function_pointers = iter - .next() - .map(|(index, function_pointer)| { - let fp = **function_pointer as usize; - // In case we are in the first function pointer (the one with the highest pointer) - // we try to determine it's bounds (function size) by using the other function trampoline - // locations. - let current_size_by_ptr = if fp < min_call_trampolines_pointer { - if min_call_trampolines_pointer < min_dynamic_trampolines_pointer - || min_dynamic_trampolines_pointer == 0 - { - min_call_trampolines_pointer - fp - } else { - min_dynamic_trampolines_pointer - fp - } - } else if fp < min_dynamic_trampolines_pointer { - min_dynamic_trampolines_pointer - fp - } else { - // The trampoline pointers are before the function. - // We can safely assume the function will be at least 16 bits of length. - // This is a very hacky assumption, but it makes collisions work perfectly - // Since DLOpen simlinks will always be > 16 bits of difference between - // two different libraries for the same symbol. - // Note: the minmum Mach-O file is 0x1000 bytes and the minimum ELF is 0x0060 bytes - 16 - }; - let mut prev_pointer = fp; - // We choose the minimum between the function size given the pointer diff - // and the emitted size by the address map - let ptr = function_pointer; - let length = current_size_by_ptr; - let first = ( - index, - FunctionExtent { - ptr: *ptr, - length: length, - }, - ); - std::iter::once(first) - .chain(iter.map(|(index, function_pointer)| { - let fp = **function_pointer as usize; - // This assumes we never lay any functions bodies across the usize::MAX..nullptr - // wrapping point. - // Which is generally true on most OSes, but certainly doesn't have to be true. - // - // Further reading: https://lwn.net/Articles/342330/ \ - // "There is one little problem with that reasoning, though: NULL (zero) can - // actually be a valid pointer address." - let current_size_by_ptr = prev_pointer - fp; - - prev_pointer = fp; - // We choose the minimum between the function size given the pointer diff - // and the emitted size by the address map - let ptr = function_pointer; - let length = current_size_by_ptr; - ( - index, - FunctionExtent { - ptr: *ptr, - length: length, - }, - ) - })) - .collect::>() - }) - .unwrap_or_default(); - - // We sort them again, by key this time - function_pointers.sort_by(|(k1, _v1), (k2, _v2)| k1.cmp(k2)); - - let frame_infos = function_pointers - .iter() - .map(|(_, extent)| CompiledFunctionFrameInfo { - traps: vec![], - address_map: FunctionAddressMap { - body_len: extent.length, - ..Default::default() - }, - }) - .collect::>(); - - let finished_function_extents = function_pointers - .into_iter() - .map(|(_, function_extent)| function_extent) - .collect::>() - .into_boxed_slice(); - - *info = register_frame_info( - self.metadata.compile_info.module.clone(), - &finished_function_extents, - frame_infos, - ); - } - - 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 - } - - 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)?) - } - - /// 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(()) - } -} diff --git a/lib/engine-dylib/src/builder.rs b/lib/engine-dylib/src/builder.rs deleted file mode 100644 index b751f33e00..0000000000 --- a/lib/engine-dylib/src/builder.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::DylibEngine; -use wasmer_compiler::{CompilerConfig, Features, Target}; - -/// The Dylib builder -pub struct Dylib { - compiler_config: Option>, - target: Option, - features: Option, -} - -impl Dylib { - #[cfg(feature = "compiler")] - /// Create a new Dylib builder. - pub fn new(compiler_config: T) -> Self - where - T: Into>, - { - let mut compiler_config = compiler_config.into(); - compiler_config.enable_pic(); - - Self { - compiler_config: Some(compiler_config), - target: None, - features: None, - } - } - - /// Create a new headless Dylib builder. - pub fn headless() -> Self { - Self { - compiler_config: None, - target: None, - features: None, - } - } - - /// Set the target - pub fn target(mut self, target: Target) -> Self { - self.target = Some(target); - self - } - - /// Set the features - pub fn features(mut self, features: Features) -> Self { - self.features = Some(features); - self - } - - /// Build the `DylibEngine` for this configuration - pub fn engine(self) -> DylibEngine { - if let Some(_compiler_config) = self.compiler_config { - #[cfg(feature = "compiler")] - { - let compiler_config = _compiler_config; - let target = self.target.unwrap_or_default(); - let features = self - .features - .unwrap_or_else(|| compiler_config.default_features_for_target(&target)); - let compiler = compiler_config.compiler(); - DylibEngine::new(compiler, target, features) - } - - #[cfg(not(feature = "compiler"))] - { - unreachable!("Cannot call `DylibEngine::new` without the `compiler` feature") - } - } else { - DylibEngine::headless() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(feature = "compiler")] - use std::sync::Arc; - #[cfg(feature = "compiler")] - use wasmer_compiler::{Compiler, ModuleMiddleware}; - - #[cfg(feature = "compiler")] - #[derive(Default)] - pub struct TestCompilerConfig { - pub enabled_pic: bool, - pub middlewares: Vec>, - } - - #[cfg(feature = "compiler")] - impl CompilerConfig for TestCompilerConfig { - fn enable_pic(&mut self) { - self.enabled_pic = true; - } - - fn compiler(self: Box) -> Box { - unimplemented!("compiler not implemented"); - } - - fn push_middleware(&mut self, middleware: Arc) { - self.middlewares.push(middleware); - } - } - - #[cfg(feature = "compiler")] - #[test] - #[should_panic(expected = "compiler not implemented")] - fn build_engine() { - let compiler_config = TestCompilerConfig::default(); - let dylib = Dylib::new(compiler_config); - let _engine = dylib.engine(); - } - - #[test] - fn build_headless_engine() { - let dylib = Dylib::headless(); - let _engine = dylib.engine(); - } -} diff --git a/lib/engine-dylib/src/engine.rs b/lib/engine-dylib/src/engine.rs deleted file mode 100644 index eeaffb647d..0000000000 --- a/lib/engine-dylib/src/engine.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! Dylib Engine. - -use crate::DylibArtifact; -use libloading::Library; -use loupe::MemoryUsage; -use std::path::Path; -use std::sync::Arc; -use std::sync::Mutex; -use wasmer_compiler::{CompileError, Target}; -#[cfg(feature = "compiler")] -use wasmer_compiler::{Compiler, Triple}; -use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; -#[cfg(feature = "compiler")] -use wasmer_types::Features; -use wasmer_types::FunctionType; -use wasmer_vm::{ - FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex, -}; - -/// A WebAssembly `Dylib` Engine. -#[derive(Clone, MemoryUsage)] -pub struct DylibEngine { - inner: Arc>, - /// The target for the compiler - target: Arc, - engine_id: EngineId, -} - -impl DylibEngine { - /// Create a new `DylibEngine` with the given config - #[cfg(feature = "compiler")] - pub fn new(compiler: Box, target: Target, features: Features) -> Self { - let is_cross_compiling = *target.triple() != Triple::host(); - let linker = Linker::find_linker(is_cross_compiling); - - Self { - inner: Arc::new(Mutex::new(DylibEngineInner { - compiler: Some(compiler), - signatures: SignatureRegistry::new(), - func_data: Arc::new(FuncDataRegistry::new()), - prefixer: None, - features, - is_cross_compiling, - linker, - libraries: vec![], - })), - target: Arc::new(target), - engine_id: EngineId::default(), - } - } - - /// Create a headless `DylibEngine` - /// - /// A headless engine is an engine without any compiler attached. - /// This is useful for assuring a minimal runtime for running - /// WebAssembly modules. - /// - /// For example, for running in IoT devices where compilers are very - /// expensive, or also to optimize startup speed. - /// - /// # Important - /// - /// Headless engines can't compile or validate any modules, - /// they just take already processed Modules (via `Module::serialize`). - pub fn headless() -> Self { - Self { - inner: Arc::new(Mutex::new(DylibEngineInner { - #[cfg(feature = "compiler")] - compiler: None, - #[cfg(feature = "compiler")] - features: Features::default(), - signatures: SignatureRegistry::new(), - func_data: Arc::new(FuncDataRegistry::new()), - prefixer: None, - is_cross_compiling: false, - linker: Linker::None, - libraries: vec![], - })), - target: Arc::new(Target::default()), - engine_id: EngineId::default(), - } - } - - /// Sets a prefixer for the wasm module, so we can avoid any collisions - /// in the exported function names on the generated shared object. - /// - /// This, allows us to rather than have functions named `wasmer_function_1` - /// to be named `wasmer_function_PREFIX_1`. - /// - /// # Important - /// - /// This prefixer function should be deterministic, so the compilation - /// remains deterministic. - pub fn set_deterministic_prefixer(&mut self, prefixer: F) - where - F: Fn(&[u8]) -> String + Send + 'static, - { - let mut inner = self.inner_mut(); - inner.prefixer = Some(Box::new(prefixer)); - } - - pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, DylibEngineInner> { - self.inner.lock().unwrap() - } - - pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, DylibEngineInner> { - self.inner.lock().unwrap() - } -} - -impl Engine for DylibEngine { - fn use_signals(&self) -> bool { - true - } - - /// The target - fn target(&self) -> &Target { - &self.target - } - - /// Register a signature - fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex { - let compiler = self.inner(); - compiler.signatures().register(func_type) - } - - fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { - let compiler = self.inner(); - compiler.func_data().register(func_data) - } - - /// Lookup a signature - fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { - let compiler = self.inner(); - compiler.signatures().lookup(sig) - } - - /// Validates a WebAssembly module - fn validate(&self, binary: &[u8]) -> Result<(), CompileError> { - self.inner().validate(binary) - } - - /// Compile a WebAssembly binary - #[cfg(feature = "compiler")] - fn compile( - &self, - binary: &[u8], - tunables: &dyn Tunables, - ) -> Result, CompileError> { - Ok(Arc::new(DylibArtifact::new(&self, binary, tunables)?)) - } - - /// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled). - #[cfg(not(feature = "compiler"))] - fn compile( - &self, - _binary: &[u8], - _tunables: &dyn Tunables, - ) -> Result, CompileError> { - Err(CompileError::Codegen( - "The `DylibEngine` is operating in headless mode, so it cannot compile a module." - .to_string(), - )) - } - - /// Deserializes a WebAssembly module (binary content of a shared object file) - unsafe fn deserialize(&self, bytes: &[u8]) -> Result, DeserializeError> { - Ok(Arc::new(DylibArtifact::deserialize(&self, &bytes)?)) - } - - /// Deserializes a WebAssembly module from a path - /// It should point to a shared object file generated by this engine. - unsafe fn deserialize_from_file( - &self, - file_ref: &Path, - ) -> Result, DeserializeError> { - Ok(Arc::new(DylibArtifact::deserialize_from_file( - &self, &file_ref, - )?)) - } - - fn id(&self) -> &EngineId { - &self.engine_id - } - - fn cloned(&self) -> Arc { - Arc::new(self.clone()) - } -} - -#[derive(Clone, Copy, MemoryUsage)] -pub(crate) enum Linker { - None, - Clang11, - Clang10, - Clang, - Gcc, -} - -impl Linker { - #[cfg(feature = "compiler")] - fn find_linker(is_cross_compiling: bool) -> Self { - let (possibilities, requirements): (&[_], _) = if is_cross_compiling { - ( - &[Linker::Clang11, Linker::Clang10, Linker::Clang], - "at least one of `clang-11`, `clang-10`, or `clang`", - ) - } else { - (&[Linker::Gcc], "`gcc`") - }; - *possibilities - .iter() - .filter(|linker| which::which(linker.executable()).is_ok()) - .next() - .unwrap_or_else(|| { - panic!( - "Need {} installed in order to use `DylibEngine` when {}cross-compiling", - requirements, - if is_cross_compiling { "" } else { "not " } - ) - }) - } - - pub(crate) fn executable(self) -> &'static str { - match self { - Self::None => "", - Self::Clang11 => "clang-11", - Self::Clang10 => "clang-10", - Self::Clang => "clang", - Self::Gcc => "gcc", - } - } -} - -/// The inner contents of `DylibEngine` -#[derive(MemoryUsage)] -pub struct DylibEngineInner { - /// The compiler - #[cfg(feature = "compiler")] - compiler: Option>, - - /// The WebAssembly features to use - #[cfg(feature = "compiler")] - features: Features, - - /// The signature registry is used mainly to operate with trampolines - /// performantly. - signatures: SignatureRegistry, - - /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 - /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. - /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. - func_data: Arc, - - /// The prefixer returns the a String to prefix each of - /// the functions in the shared object generated by the `DylibEngine`, - /// so we can assure no collisions. - #[loupe(skip)] - prefixer: Option String + Send>>, - - /// Whether the Dylib engine will cross-compile. - is_cross_compiling: bool, - - /// The linker to use. - linker: Linker, - - /// List of libraries loaded by this engine. - #[loupe(skip)] - libraries: Vec, -} - -impl DylibEngineInner { - /// 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 `DylibEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string())); - } - Ok(&**self - .compiler - .as_ref() - .expect("Can't get compiler reference")) - } - - #[cfg(feature = "compiler")] - pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String { - if let Some(prefixer) = &self.prefixer { - prefixer(&bytes) - } else { - "".to_string() - } - } - - #[cfg(feature = "compiler")] - pub(crate) fn features(&self) -> &Features { - &self.features - } - - /// 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 `DylibEngine` is not compiled with compiler support, which is required for validating".to_string(), - )) - } - - /// Shared signature registry. - pub fn signatures(&self) -> &SignatureRegistry { - &self.signatures - } - - /// Shared func metadata registry. - pub(crate) fn func_data(&self) -> &Arc { - &self.func_data - } - - pub(crate) fn is_cross_compiling(&self) -> bool { - self.is_cross_compiling - } - - pub(crate) fn linker(&self) -> Linker { - self.linker - } - - pub(crate) fn add_library(&mut self, library: Library) { - self.libraries.push(library); - } -} diff --git a/lib/engine-dylib/src/lib.rs b/lib/engine-dylib/src/lib.rs deleted file mode 100644 index 7aad4a628a..0000000000 --- a/lib/engine-dylib/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Dylib Engine for Wasmer compilers. -//! -//! Given a compiler (such as `CraneliftCompiler` or `LLVMCompiler`) -//! it generates a dylib/shared object file (`.so` or `.dylib` -//! depending on the target), saves it temporarily to disk and uses it -//! natively via `dlopen` and `dlsym` (using the `libloading` -//! library). - -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "cargo-clippy", allow(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 builder; -mod engine; -mod serialize; - -pub use crate::artifact::DylibArtifact; -pub use crate::builder::Dylib; -pub use crate::engine::DylibEngine; - -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/engine-dylib/src/serialize.rs b/lib/engine-dylib/src/serialize.rs deleted file mode 100644 index 4306de734b..0000000000 --- a/lib/engine-dylib/src/serialize.rs +++ /dev/null @@ -1,148 +0,0 @@ -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 serde::{Deserialize, Serialize}; -use std::error::Error; -use wasmer_compiler::{CompileError, CompileModuleInfo, SectionIndex, Symbol, SymbolRegistry}; -use wasmer_engine::DeserializeError; -use wasmer_types::entity::{EntityRef, PrimaryMap}; -use wasmer_types::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex}; - -fn to_compile_error(err: impl Error) -> CompileError { - CompileError::Codegen(format!("{}", err)) -} - -/// Serializable struct that represents the compiled metadata. -#[derive( - Serialize, - Deserialize, - Debug, - MemoryUsage, - RkyvSerialize, - RkyvDeserialize, - Archive, - PartialEq, - Eq, -)] -pub struct ModuleMetadata { - pub compile_info: CompileModuleInfo, - pub prefix: String, - pub data_initializers: Box<[OwnedDataInitializer]>, - // The function body lengths (used to find function by address) - pub function_body_lengths: PrimaryMap, - pub cpu_features: u64, -} - -pub struct ModuleMetadataSymbolRegistry<'a> { - pub prefix: &'a String, -} - -impl ModuleMetadata { - pub fn split<'a>( - &'a mut self, - ) -> (&'a mut CompileModuleInfo, ModuleMetadataSymbolRegistry<'a>) { - let compile_info = &mut self.compile_info; - let symbol_registry = ModuleMetadataSymbolRegistry { - prefix: &self.prefix, - }; - (compile_info, symbol_registry) - } - - pub fn get_symbol_registry<'a>(&'a self) -> ModuleMetadataSymbolRegistry<'a> { - ModuleMetadataSymbolRegistry { - prefix: &self.prefix, - } - } - - pub fn serialize(&mut self) -> Result, CompileError> { - let mut serializer = AllocSerializer::<4096>::default(); - let pos = serializer.serialize_value(self).map_err(to_compile_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()) - } - - pub unsafe fn deserialize(metadata_slice: &[u8]) -> Result { - let archived = Self::archive_from_slice(metadata_slice)?; - Self::deserialize_from_archive(archived) - } - - unsafe fn archive_from_slice<'a>( - metadata_slice: &'a [u8], - ) -> Result<&'a ArchivedModuleMetadata, DeserializeError> { - 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, - )) - } - - pub fn deserialize_from_archive( - archived: &ArchivedModuleMetadata, - ) -> Result { - let mut deserializer = SharedDeserializeMap::new(); - RkyvDeserialize::deserialize(archived, &mut deserializer) - .map_err(|e| DeserializeError::CorruptedBinary(format!("{:?}", e))) - } -} - -impl<'a> SymbolRegistry for ModuleMetadataSymbolRegistry<'a> { - fn symbol_to_name(&self, symbol: Symbol) -> String { - match symbol { - Symbol::LocalFunction(index) => { - format!("wasmer_function_{}_{}", self.prefix, index.index()) - } - Symbol::Section(index) => format!("wasmer_section_{}_{}", self.prefix, index.index()), - Symbol::FunctionCallTrampoline(index) => { - format!( - "wasmer_trampoline_function_call_{}_{}", - self.prefix, - index.index() - ) - } - Symbol::DynamicFunctionTrampoline(index) => { - format!( - "wasmer_trampoline_dynamic_function_{}_{}", - self.prefix, - index.index() - ) - } - } - } - - fn name_to_symbol(&self, name: &str) -> Option { - if let Some(index) = name.strip_prefix(&format!("wasmer_function_{}_", self.prefix)) { - index - .parse::() - .ok() - .map(|index| Symbol::LocalFunction(LocalFunctionIndex::from_u32(index))) - } else if let Some(index) = name.strip_prefix(&format!("wasmer_section_{}_", self.prefix)) { - index - .parse::() - .ok() - .map(|index| Symbol::Section(SectionIndex::from_u32(index))) - } else if let Some(index) = - name.strip_prefix(&format!("wasmer_trampoline_function_call_{}_", self.prefix)) - { - index - .parse::() - .ok() - .map(|index| Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(index))) - } else if let Some(index) = name.strip_prefix(&format!( - "wasmer_trampoline_dynamic_function_{}_", - self.prefix - )) { - index - .parse::() - .ok() - .map(|index| Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(index))) - } else { - None - } - } -} diff --git a/lib/engine-staticlib/Cargo.toml b/lib/engine-staticlib/Cargo.toml deleted file mode 100644 index d637d0ccb4..0000000000 --- a/lib/engine-staticlib/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "wasmer-engine-staticlib" -version = "2.1.0" -authors = ["Wasmer Engineering Team "] -description = "Wasmer Staticlib Engine" -categories = ["wasm"] -keywords = ["webassembly", "wasm"] -repository = "https://github.com/wasmerio/wasmer" -license = "MIT" -readme = "README.md" -edition = "2018" - -[dependencies] -wasmer-types = { path = "../types", version = "2.0.3", package = "wasmer-types-near" } -wasmer-compiler = { path = "../compiler", package = "wasmer-compiler-near", version = "2.0.3" } -wasmer-vm = { path = "../vm", version = "2.0.3", package = "wasmer-vm-near" } -wasmer-engine = { path = "../engine", version = "2.0.3", package = "wasmer-engine-near" } -wasmer-object = { path = "../object", version = "2.0.0" } -serde = { version = "1.0", features = ["derive", "rc"] } -cfg-if = "1.0" -tracing = "0.1" -bincode = "1.3" -leb128 = "0.2" -libloading = "0.7" -tempfile = "3.1" -loupe = "0.1" -enumset = "1.0" - -[features] -# Enable the `compiler` feature if you want the engine to compile -# and not be only on headless mode. -compiler = ["wasmer-compiler/translator"] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/lib/engine-staticlib/README.md b/lib/engine-staticlib/README.md deleted file mode 100644 index 9c1cc0dbd7..0000000000 --- a/lib/engine-staticlib/README.md +++ /dev/null @@ -1,137 +0,0 @@ -# Wasmer Engine Staticlib - -This is an [engine](https://crates.io/crates/wasmer-engine) for the -[wasmer](https://crates.io/crates/wasmer) WebAssembly VM. - -This engine is used to produce a native static object library that can -be linked against providing a sandboxed WebAssembly runtime -environment for the compiled module with no need for runtime -compilation. - -## Example of use - -First we compile our WebAssembly file with Wasmer - -```sh -wasmer compile path/to/wasm/file.wasm --llvm --staticlib -o my_wasm.o --header my_wasm.h -``` - -You will then see output like: - -``` -Engine: staticlib -Compiler: llvm -Target: x86_64-apple-darwin -✔ File compiled successfully to `my_wasm.o`. -✔ Header file generated successfully at `my_wasm.h`. -``` - -Now let's create a program to link with this static object file. - -```c -#include "wasmer.h" -#include "my_wasm.h" - -#include -#include - -#define own - -static void print_wasmer_error() -{ - int error_len = wasmer_last_error_length(); - printf("Error len: `%d`\n", error_len); - char* error_str = (char*) malloc(error_len); - wasmer_last_error_message(error_str, error_len); - printf("Error str: `%s`\n", error_str); - free(error_str); -} - -int main() { - printf("Initializing...\n"); - wasm_config_t* config = wasm_config_new(); - wasm_config_set_engine(config, STATICLIB); - wasm_engine_t* engine = wasm_engine_new_with_config(config); - wasm_store_t* store = wasm_store_new(engine); - - wasm_module_t* module = wasmer_staticlib_engine_new(store, "qjs.wasm"); - if (!module) { - printf("Failed to create module\n"); - print_wasmer_error(); - return -1; - } - - // We have now finished the memory buffer book keeping and we have a valid Module. - - // In this example we're passing some JavaScript source code as a command line argument - // to a WASI module that can evaluate JavaScript. - wasi_config_t* wasi_config = wasi_config_new("constant_value_here"); - const char* js_string = "function greet(name) { return JSON.stringify('Hello, ' + name); }; print(greet('World'));"; - wasi_config_arg(wasi_config, "--eval"); - wasi_config_arg(wasi_config, js_string); - wasi_env_t* wasi_env = wasi_env_new(wasi_config); - if (!wasi_env) { - printf("> Error building WASI env!\n"); - print_wasmer_error(); - return 1; - } - - wasm_importtype_vec_t import_types; - wasm_module_imports(module, &import_types); - int num_imports = import_types.size; - wasm_extern_t** imports = (wasm_extern_t**) malloc(num_imports * sizeof(wasm_extern_t*)); - wasm_importtype_vec_delete(&import_types); - - bool get_imports_result = wasi_get_imports(store, module, wasi_env, imports); - wasi_env_delete(wasi_env); - if (!get_imports_result) { - printf("> Error getting WASI imports!\n"); - print_wasmer_error(); - return 1; - } - - wasm_instance_t* instance = wasm_instance_new(store, module, (const wasm_extern_t* const*) imports, NULL); - if (! instance) { - printf("Failed to create instance\n"); - print_wasmer_error(); - return -1; - } - - // WASI is now set up. - own wasm_func_t* start_function = wasi_get_start_function(instance); - if (!start_function) { - fprintf(stderr, "`_start` function not found\n"); - print_wasmer_error(); - return -1; - } - - fflush(stdout); - own wasm_trap_t* trap = wasm_func_call(start_function, NULL, NULL); - if (trap) { - fprintf(stderr, "Trap is not NULL: TODO:\n"); - return -1; - } - - wasm_instance_delete(instance); - wasm_module_delete(module); - wasm_store_delete(store); - wasm_engine_delete(engine); - return 0; -} -``` - -We save that source code into `test.c` and run: - -```sh -clang -O2 -c test.c -o test.o -``` - -Now we just need to link everything together: - -```sh -clang -O2 test.o my_wasm.o libwasmer.a -``` - -We link the static object file we created with our C code, the object -file we generated with Wasmer, and `libwasmer` together and produce an -executable that can call into our compiled WebAssembly! diff --git a/lib/engine-staticlib/src/artifact.rs b/lib/engine-staticlib/src/artifact.rs deleted file mode 100644 index cf25fb4808..0000000000 --- a/lib/engine-staticlib/src/artifact.rs +++ /dev/null @@ -1,510 +0,0 @@ -//! Define `StaticlibArtifact` to allow compiling and instantiating to be -//! done as separate steps. - -use crate::engine::{StaticlibEngine, StaticlibEngineInner}; -use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry}; -use enumset::EnumSet; -use loupe::MemoryUsage; -use std::collections::BTreeMap; -use std::error::Error; -use std::mem; -use std::sync::Arc; -use wasmer_compiler::{ - CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple, -}; -#[cfg(feature = "compiler")] -use wasmer_compiler::{ - CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain, - ModuleTranslationState, -}; -use wasmer_engine::{Artifact, DeserializeError, InstantiationError, SerializeError}; -#[cfg(feature = "compiler")] -use wasmer_engine::{Engine, Tunables}; -#[cfg(feature = "compiler")] -use wasmer_object::{emit_compilation, emit_data, get_object_for_target}; -use wasmer_types::entity::EntityRef; -use wasmer_types::entity::{BoxedSlice, PrimaryMap}; -#[cfg(feature = "compiler")] -use wasmer_types::DataInitializer; -use wasmer_types::{ - FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, OwnedDataInitializer, - SignatureIndex, TableIndex, -}; -use wasmer_vm::{ - FuncDataRegistry, FunctionBodyPtr, MemoryStyle, TableStyle, VMSharedSignatureIndex, - VMTrampoline, -}; - -/// A compiled wasm module, ready to be instantiated. -#[derive(MemoryUsage)] -pub struct StaticlibArtifact { - metadata: ModuleMetadata, - module_bytes: Vec, - finished_functions: BoxedSlice, - #[loupe(skip)] - finished_function_call_trampolines: BoxedSlice, - finished_dynamic_function_trampolines: BoxedSlice, - signatures: BoxedSlice, - func_data_registry: Arc, - /// Length of the serialized metadata - metadata_length: usize, - symbol_registry: ModuleMetadataSymbolRegistry, - is_compiled: bool, -} - -#[allow(dead_code)] -fn to_compile_error(err: impl Error) -> CompileError { - CompileError::Codegen(format!("{}", err)) -} - -#[allow(dead_code)] -const WASMER_METADATA_SYMBOL: &[u8] = b"WASMER_METADATA"; - -impl StaticlibArtifact { - // Mach-O header in Mac - #[allow(dead_code)] - const MAGIC_HEADER_MH_CIGAM_64: &'static [u8] = &[207, 250, 237, 254]; - - // ELF Magic header for Linux (32 bit) - #[allow(dead_code)] - const MAGIC_HEADER_ELF_32: &'static [u8] = &[0x7f, b'E', b'L', b'F', 1]; - - // ELF Magic header for Linux (64 bit) - #[allow(dead_code)] - const MAGIC_HEADER_ELF_64: &'static [u8] = &[0x7f, b'E', b'L', b'F', 2]; - - // COFF Magic header for Windows (64 bit) - #[allow(dead_code)] - const MAGIC_HEADER_COFF_64: &'static [u8] = &[b'M', b'Z']; - - /// Check if the provided bytes look like `StaticlibArtifact`. - /// - /// This means, if the bytes look like a static object file in the - /// target system. - pub fn is_deserializable(bytes: &[u8]) -> bool { - cfg_if::cfg_if! { - if #[cfg(all(target_pointer_width = "64", target_vendor="apple"))] { - bytes.starts_with(Self::MAGIC_HEADER_MH_CIGAM_64) - } - else if #[cfg(all(target_pointer_width = "64", target_os="linux"))] { - bytes.starts_with(Self::MAGIC_HEADER_ELF_64) - } - else if #[cfg(all(target_pointer_width = "32", target_os="linux"))] { - bytes.starts_with(Self::MAGIC_HEADER_ELF_32) - } - else if #[cfg(all(target_pointer_width = "64", target_os="windows"))] { - bytes.starts_with(Self::MAGIC_HEADER_COFF_64) - } - else { - false - } - } - } - - #[cfg(feature = "compiler")] - /// Generate a compilation - fn generate_metadata<'data>( - data: &'data [u8], - features: &Features, - compiler: &dyn Compiler, - tunables: &dyn Tunables, - ) -> Result< - ( - CompileModuleInfo, - PrimaryMap>, - Vec>, - Option, - ), - CompileError, - > { - let environ = ModuleEnvironment::new(); - let translation = environ.translate(data).map_err(CompileError::Wasm)?; - - // 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 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 compile_info = CompileModuleInfo { - module: Arc::new(module), - features: features.clone(), - memory_styles, - table_styles, - }; - Ok(( - compile_info, - translation.function_body_inputs, - translation.data_initializers, - translation.module_translation_state, - )) - } - - /// Compile a data buffer into a `StaticlibArtifact`, which can be statically linked against - /// and run later. - #[cfg(feature = "compiler")] - pub fn new( - engine: &StaticlibEngine, - data: &[u8], - tunables: &dyn Tunables, - ) -> Result { - let mut engine_inner = engine.inner_mut(); - let target = engine.target(); - let compiler = engine_inner.compiler()?; - let (compile_info, function_body_inputs, data_initializers, module_translation) = - Self::generate_metadata(data, engine_inner.features(), compiler, tunables)?; - - let data_initializers = data_initializers - .iter() - .map(OwnedDataInitializer::new) - .collect::>() - .into_boxed_slice(); - - let target_triple = target.triple(); - - // TODO: we currently supply all-zero function body lengths. - // We don't know the lengths until they're compiled, yet we have to - // supply the metadata as an input to the compile. - let function_body_lengths = function_body_inputs - .keys() - .map(|_function_body| 0u64) - .collect::>(); - - let mut metadata = ModuleMetadata { - compile_info, - prefix: engine_inner.get_prefix(&data), - data_initializers, - function_body_lengths, - cpu_features: target.cpu_features().as_u64(), - }; - - /* - In the C file we need: - - imports - - exports - - to construct an api::Module which is a Store (can be passed in via argument) and an - Arc which means this struct which includes: - - CompileModuleInfo - - Features - - ModuleInfo - - MemoryIndex -> MemoryStyle - - TableIndex -> TableStyle - - LocalFunctionIndex -> FunctionBodyPtr // finished functions - - FunctionIndex -> FunctionBodyPtr // finished dynamic function trampolines - - SignatureIndex -> VMSharedSignatureindextureIndex // signatures - */ - - let serialized_data = bincode::serialize(&metadata).map_err(to_compile_error)?; - let mut metadata_binary = vec![0; 10]; - let mut writable = &mut metadata_binary[..]; - leb128::write::unsigned(&mut writable, serialized_data.len() as u64) - .expect("Should write number"); - metadata_binary.extend(serialized_data); - let metadata_length = metadata_binary.len(); - - let (compile_info, symbol_registry) = metadata.split(); - - let mut module = (*compile_info.module).clone(); - let middlewares = compiler.get_middlewares(); - middlewares.apply_on_module_info(&mut module); - compile_info.module = Arc::new(module); - - let maybe_obj_bytes = compiler.experimental_native_compile_module( - &target, - &compile_info, - module_translation.as_ref().unwrap(), - &function_body_inputs, - &symbol_registry, - &metadata_binary, - ); - - let obj_bytes = if let Some(obj_bytes) = maybe_obj_bytes { - obj_bytes? - } else { - let compilation = compiler.compile_module( - &target, - &metadata.compile_info, - module_translation.as_ref().unwrap(), - function_body_inputs, - )?; - // there's an ordering issue, but we can update function_body_lengths here. - /* - // We construct the function body lengths - let function_body_lengths = compilation - .get_function_bodies() - .values() - .map(|function_body| function_body.body.len() as u64) - .collect::>(); - */ - let mut obj = get_object_for_target(&target_triple).map_err(to_compile_error)?; - emit_data(&mut obj, WASMER_METADATA_SYMBOL, &metadata_binary, 1) - .map_err(to_compile_error)?; - emit_compilation(&mut obj, compilation, &symbol_registry, &target_triple) - .map_err(to_compile_error)?; - obj.write().map_err(to_compile_error)? - }; - - Self::from_parts_crosscompiled(&mut *engine_inner, metadata, obj_bytes, metadata_length) - } - - /// Get the default extension when serializing this artifact - pub fn get_default_extension(triple: &Triple) -> &'static str { - match triple.operating_system { - OperatingSystem::Windows => "obj", - _ => "o", - } - } - - /// Construct a `StaticlibArtifact` from component parts. - pub fn from_parts_crosscompiled( - engine_inner: &mut StaticlibEngineInner, - metadata: ModuleMetadata, - module_bytes: Vec, - metadata_length: usize, - ) -> Result { - let finished_functions: PrimaryMap = PrimaryMap::new(); - let finished_function_call_trampolines: PrimaryMap = - PrimaryMap::new(); - let finished_dynamic_function_trampolines: PrimaryMap = - PrimaryMap::new(); - let signature_registry = engine_inner.signatures(); - let signatures = metadata - .compile_info - .module - .signatures - .values() - .map(|sig| signature_registry.register(sig)) - .collect::>(); - - let symbol_registry = metadata.get_symbol_registry(); - Ok(Self { - metadata, - module_bytes, - finished_functions: finished_functions.into_boxed_slice(), - finished_function_call_trampolines: finished_function_call_trampolines - .into_boxed_slice(), - finished_dynamic_function_trampolines: finished_dynamic_function_trampolines - .into_boxed_slice(), - signatures: signatures.into_boxed_slice(), - func_data_registry: engine_inner.func_data().clone(), - metadata_length, - symbol_registry, - is_compiled: true, - }) - } - - /// Compile a data buffer into a `StaticlibArtifact`, which may then be instantiated. - #[cfg(not(feature = "compiler"))] - pub fn new(_engine: &StaticlibEngine, _data: &[u8]) -> Result { - Err(CompileError::Codegen( - "Compilation is not enabled in the engine".to_string(), - )) - } - - /// Deserialize a `StaticlibArtifact` from bytes. - /// - /// # Safety - /// - /// The bytes must represent a serialized WebAssembly module. - pub unsafe fn deserialize( - engine: &StaticlibEngine, - bytes: &[u8], - ) -> Result { - let mut reader = bytes; - let data_len = leb128::read::unsigned(&mut reader).unwrap() as usize; - - let metadata: ModuleMetadata = bincode::deserialize(&bytes[10..(data_len + 10)]).unwrap(); - - const WORD_SIZE: usize = mem::size_of::(); - let mut byte_buffer = [0u8; WORD_SIZE]; - - let mut cur_offset = data_len + 10; - byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - cur_offset += WORD_SIZE; - - let num_finished_functions = usize::from_ne_bytes(byte_buffer); - let mut finished_functions = PrimaryMap::new(); - - let engine_inner = engine.inner(); - let signature_registry = engine_inner.signatures(); - let func_data_registry = engine_inner.func_data().clone(); - let mut sig_map: BTreeMap = BTreeMap::new(); - - let num_imported_functions = metadata.compile_info.module.num_imported_functions; - // set up the imported functions first... - for i in 0..num_imported_functions { - let sig_idx = metadata.compile_info.module.functions[FunctionIndex::new(i)]; - let func_type = &metadata.compile_info.module.signatures[sig_idx]; - let vm_shared_idx = signature_registry.register(&func_type); - sig_map.insert(sig_idx, vm_shared_idx); - } - // read finished functions in order now... - for i in 0..num_finished_functions { - let local_func_idx = LocalFunctionIndex::new(i); - let func_idx = metadata.compile_info.module.func_index(local_func_idx); - let sig_idx = metadata.compile_info.module.functions[func_idx]; - let func_type = &metadata.compile_info.module.signatures[sig_idx]; - let vm_shared_idx = signature_registry.register(&func_type); - sig_map.insert(sig_idx, vm_shared_idx); - - byte_buffer[0..WORD_SIZE] - .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _); - cur_offset += WORD_SIZE; - - // TODO: we can read back the length here if we serialize it. This will improve debug output. - - finished_functions.push(fp); - } - - let mut signatures: PrimaryMap<_, VMSharedSignatureIndex> = PrimaryMap::new(); - for i in 0..(sig_map.len()) { - if let Some(shared_idx) = sig_map.get(&SignatureIndex::new(i)) { - signatures.push(*shared_idx); - } else { - panic!("Invalid data, missing sig idx; TODO: handle this error"); - } - } - - // read trampolines in order - let mut finished_function_call_trampolines = PrimaryMap::new(); - byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - cur_offset += WORD_SIZE; - let num_function_trampolines = usize::from_ne_bytes(byte_buffer); - for _ in 0..num_function_trampolines { - byte_buffer[0..WORD_SIZE] - .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - cur_offset += WORD_SIZE; - let trampoline_ptr_bytes = usize::from_ne_bytes(byte_buffer); - let trampoline = mem::transmute::(trampoline_ptr_bytes); - finished_function_call_trampolines.push(trampoline); - // TODO: we can read back the length here if we serialize it. This will improve debug output. - } - - // read dynamic function trampolines in order now... - let mut finished_dynamic_function_trampolines = PrimaryMap::new(); - byte_buffer[0..WORD_SIZE].clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - cur_offset += WORD_SIZE; - let num_dynamic_trampoline_functions = usize::from_ne_bytes(byte_buffer); - for _i in 0..num_dynamic_trampoline_functions { - byte_buffer[0..WORD_SIZE] - .clone_from_slice(&bytes[cur_offset..(cur_offset + WORD_SIZE)]); - let fp = FunctionBodyPtr(usize::from_ne_bytes(byte_buffer) as _); - cur_offset += WORD_SIZE; - - // TODO: we can read back the length here if we serialize it. This will improve debug output. - - finished_dynamic_function_trampolines.push(fp); - } - - let symbol_registry = metadata.get_symbol_registry(); - Ok(Self { - metadata, - module_bytes: bytes.to_owned(), - finished_functions: finished_functions.into_boxed_slice(), - finished_function_call_trampolines: finished_function_call_trampolines - .into_boxed_slice(), - finished_dynamic_function_trampolines: finished_dynamic_function_trampolines - .into_boxed_slice(), - signatures: signatures.into_boxed_slice(), - func_data_registry, - metadata_length: 0, - symbol_registry, - is_compiled: false, - }) - } - - /// Get the `SymbolRegistry` used to generate the names used in the Artifact. - pub fn symbol_registry(&self) -> &dyn SymbolRegistry { - &self.symbol_registry - } - - /// The length in bytes of the metadata in the serialized output. - pub fn metadata_length(&self) -> usize { - self.metadata_length - } -} - -impl Artifact for StaticlibArtifact { - fn module(&self) -> Arc { - self.metadata.compile_info.module.clone() - } - - fn module_ref(&self) -> &ModuleInfo { - &self.metadata.compile_info.module - } - - fn module_mut(&mut self) -> Option<&mut ModuleInfo> { - 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 - } - - fn cpu_features(&self) -> 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 - } - - 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> { - if self.is_compiled { - panic!( - "a module built with the staticlib engine must be linked \ - into the current executable" - ); - } - Ok(()) - } - - /// Serialize a StaticlibArtifact - fn serialize(&self) -> Result, SerializeError> { - Ok(self.module_bytes.clone()) - } -} diff --git a/lib/engine-staticlib/src/builder.rs b/lib/engine-staticlib/src/builder.rs deleted file mode 100644 index 1f0a4f7eb6..0000000000 --- a/lib/engine-staticlib/src/builder.rs +++ /dev/null @@ -1,117 +0,0 @@ -use crate::StaticlibEngine; -use wasmer_compiler::{CompilerConfig, Features, Target}; - -/// The Staticlib builder -pub struct Staticlib { - compiler_config: Option>, - target: Option, - features: Option, -} - -impl Staticlib { - #[cfg(feature = "compiler")] - /// Create a new Staticlib - pub fn new(compiler_config: T) -> Self - where - T: Into>, - { - let mut compiler_config = compiler_config.into(); - compiler_config.enable_pic(); - - Self { - compiler_config: Some(compiler_config), - target: None, - features: None, - } - } - - /// Create a new headless Staticlib - pub fn headless() -> Self { - Self { - compiler_config: None, - target: None, - features: None, - } - } - - /// Set the target - pub fn target(mut self, target: Target) -> Self { - self.target = Some(target); - self - } - - /// Set the features - pub fn features(mut self, features: Features) -> Self { - self.features = Some(features); - self - } - - /// Build the `StaticlibEngine` for this configuration - pub fn engine(self) -> StaticlibEngine { - if let Some(_compiler_config) = self.compiler_config { - #[cfg(feature = "compiler")] - { - let compiler_config = _compiler_config; - let target = self.target.unwrap_or_default(); - let features = self - .features - .unwrap_or_else(|| compiler_config.default_features_for_target(&target)); - let compiler = compiler_config.compiler(); - StaticlibEngine::new(compiler, target, features) - } - - #[cfg(not(feature = "compiler"))] - { - unreachable!("Cannot call `StaticlibEngine::new` without the `compiler` feature") - } - } else { - StaticlibEngine::headless() - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(feature = "compiler")] - use std::sync::Arc; - #[cfg(feature = "compiler")] - use wasmer_compiler::{Compiler, ModuleMiddleware}; - - #[cfg(feature = "compiler")] - #[derive(Default)] - pub struct TestCompilerConfig { - pub enabled_pic: bool, - pub middlewares: Vec>, - } - - #[cfg(feature = "compiler")] - impl CompilerConfig for TestCompilerConfig { - fn enable_pic(&mut self) { - self.enabled_pic = true; - } - - fn compiler(self: Box) -> Box { - unimplemented!("compiler not implemented"); - } - - fn push_middleware(&mut self, middleware: Arc) { - self.middlewares.push(middleware); - } - } - - #[cfg(feature = "compiler")] - #[test] - #[should_panic(expected = "compiler not implemented")] - fn build_engine() { - let compiler_config = TestCompilerConfig::default(); - let staticlib = Staticlib::new(compiler_config); - let _engine = staticlib.engine(); - } - - #[test] - fn build_headless_engine() { - let staticlib = Staticlib::headless(); - let _engine = staticlib.engine(); - } -} diff --git a/lib/engine-staticlib/src/engine.rs b/lib/engine-staticlib/src/engine.rs deleted file mode 100644 index e3e7ea852c..0000000000 --- a/lib/engine-staticlib/src/engine.rs +++ /dev/null @@ -1,262 +0,0 @@ -use crate::StaticlibArtifact; -use loupe::MemoryUsage; -use std::io::Read; -use std::path::Path; -use std::sync::{Arc, Mutex}; -#[cfg(feature = "compiler")] -use wasmer_compiler::Compiler; -use wasmer_compiler::{CompileError, Target}; -use wasmer_engine::{Artifact, DeserializeError, Engine, EngineId, Tunables}; -#[cfg(feature = "compiler")] -use wasmer_types::Features; -use wasmer_types::FunctionType; -use wasmer_vm::{ - FuncDataRegistry, SignatureRegistry, VMCallerCheckedAnyfunc, VMFuncRef, VMSharedSignatureIndex, -}; - -/// A WebAssembly `Staticlib` Engine. -#[derive(Clone, MemoryUsage)] -pub struct StaticlibEngine { - inner: Arc>, - /// The target for the compiler - target: Arc, - engine_id: EngineId, -} - -impl StaticlibEngine { - /// Create a new `StaticlibEngine` with the given config - #[cfg(feature = "compiler")] - pub fn new(compiler: Box, target: Target, features: Features) -> Self { - Self { - inner: Arc::new(Mutex::new(StaticlibEngineInner { - compiler: Some(compiler), - signatures: SignatureRegistry::new(), - func_data: Arc::new(FuncDataRegistry::new()), - prefixer: None, - features, - })), - target: Arc::new(target), - engine_id: EngineId::default(), - } - } - - /// Create a headless `StaticlibEngine` - /// - /// A headless engine is an engine without any compiler attached. - /// This is useful for assuring a minimal runtime for running - /// WebAssembly modules. - /// - /// For example, for running in IoT devices where compilers are very - /// expensive, or also to optimize startup speed. - /// - /// # Important - /// - /// Headless engines can't compile or validate any modules, - /// they just take already processed Modules (via `Module::serialize`). - pub fn headless() -> Self { - Self { - inner: Arc::new(Mutex::new(StaticlibEngineInner { - #[cfg(feature = "compiler")] - compiler: None, - #[cfg(feature = "compiler")] - features: Features::default(), - signatures: SignatureRegistry::new(), - func_data: Arc::new(FuncDataRegistry::new()), - prefixer: None, - })), - target: Arc::new(Target::default()), - engine_id: EngineId::default(), - } - } - - /// Sets a prefixer for the Wasm module, so we can avoid any - /// collisions in the exported function names on the generated - /// object. - /// - /// This, allows us to rather than have functions named - /// `wasmer_function_1` to be named `wasmer_function_PREFIX_1`. - /// - /// # Important - /// - /// This prefixer function should be deterministic, so the - /// compilation remains deterministic. - pub fn set_deterministic_prefixer(&mut self, prefixer: F) - where - F: Fn(&[u8]) -> String + Send + 'static, - { - let mut inner = self.inner_mut(); - inner.prefixer = Some(Box::new(prefixer)); - } - - pub(crate) fn inner(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> { - self.inner.lock().unwrap() - } - - pub(crate) fn inner_mut(&self) -> std::sync::MutexGuard<'_, StaticlibEngineInner> { - self.inner.lock().unwrap() - } -} - -impl Engine for StaticlibEngine { - /// If signal usage required. - fn use_signals(&self) -> bool { - true - } - - /// The target - fn target(&self) -> &Target { - &self.target - } - - /// Register a signature - fn register_signature(&self, func_type: &FunctionType) -> VMSharedSignatureIndex { - let compiler = self.inner(); - compiler.signatures().register(func_type) - } - - fn register_function_metadata(&self, func_data: VMCallerCheckedAnyfunc) -> VMFuncRef { - let compiler = self.inner(); - compiler.func_data().register(func_data) - } - - /// Lookup a signature - fn lookup_signature(&self, sig: VMSharedSignatureIndex) -> Option { - let compiler = self.inner(); - compiler.signatures().lookup(sig) - } - - /// Validates a WebAssembly module - fn validate(&self, binary: &[u8]) -> Result<(), CompileError> { - self.inner().validate(binary) - } - - /// Compile a WebAssembly binary - #[cfg(feature = "compiler")] - fn compile( - &self, - binary: &[u8], - tunables: &dyn Tunables, - ) -> Result, CompileError> { - Ok(Arc::new(StaticlibArtifact::new(&self, binary, tunables)?)) - } - - /// Compile a WebAssembly binary (it will fail because the `compiler` flag is disabled). - #[cfg(not(feature = "compiler"))] - fn compile( - &self, - _binary: &[u8], - _tunables: &dyn Tunables, - ) -> Result, CompileError> { - Err(CompileError::Codegen( - "The `StaticlibEngine` is operating in headless mode, so it cannot compile a module." - .to_string(), - )) - } - - /// Deserializes a WebAssembly module (binary content of a static object file) - unsafe fn deserialize(&self, bytes: &[u8]) -> Result, DeserializeError> { - Ok(Arc::new(StaticlibArtifact::deserialize(&self, &bytes)?)) - } - - /// Deserializes a WebAssembly module from a path - /// - /// It should point to a static object file generated by this - /// engine. - unsafe fn deserialize_from_file( - &self, - file_ref: &Path, - ) -> Result, DeserializeError> { - let mut f = std::fs::File::open(file_ref)?; - let mut vec = vec![]; - f.read_to_end(&mut vec)?; - - self.deserialize(&vec[..]) - } - - fn id(&self) -> &EngineId { - &self.engine_id - } - - fn cloned(&self) -> Arc { - Arc::new(self.clone()) - } -} - -/// The inner contents of `StaticlibEngine` -#[derive(MemoryUsage)] -pub struct StaticlibEngineInner { - /// The compiler - #[cfg(feature = "compiler")] - compiler: Option>, - - /// The WebAssembly features to use - #[cfg(feature = "compiler")] - features: Features, - - /// The signature registry is used mainly to operate with trampolines - /// performantly. - signatures: SignatureRegistry, - - /// The backing storage of `VMFuncRef`s. This centralized store ensures that 2 - /// functions with the same `VMCallerCheckedAnyfunc` will have the same `VMFuncRef`. - /// It also guarantees that the `VMFuncRef`s stay valid until the engine is dropped. - func_data: Arc, - - /// The prefixer returns the a String to prefix each of the - /// functions in the static object generated by the - /// `StaticlibEngine`, so we can assure no collisions. - #[loupe(skip)] - prefixer: Option String + Send>>, -} - -impl StaticlibEngineInner { - /// 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 `StaticlibEngine` is operating in headless mode, so it can only execute already compiled Modules.".to_string())); - } - Ok(&**self - .compiler - .as_ref() - .expect("Can't get compiler reference")) - } - - #[cfg(feature = "compiler")] - pub(crate) fn get_prefix(&self, bytes: &[u8]) -> String { - if let Some(prefixer) = &self.prefixer { - prefixer(&bytes) - } else { - "".to_string() - } - } - - #[cfg(feature = "compiler")] - pub(crate) fn features(&self) -> &Features { - &self.features - } - - /// 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 `StaticlibEngine` is not compiled with compiler support, which is required for validating".to_string(), - )) - } - - /// Shared signature registry. - pub fn signatures(&self) -> &SignatureRegistry { - &self.signatures - } - - /// Shared func metadata registry. - pub(crate) fn func_data(&self) -> &Arc { - &self.func_data - } -} diff --git a/lib/engine-staticlib/src/lib.rs b/lib/engine-staticlib/src/lib.rs deleted file mode 100644 index db416de750..0000000000 --- a/lib/engine-staticlib/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -//! Staticlib engine for Wasmer compilers. -//! -//! Given a compiler (such as `CraneliftCompiler` or `LLVMCompiler`) -//! it generates a static object file (`.o` file) and metadata which -//! can be used to access it from other programming languages static. - -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "cargo-clippy", allow(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 builder; -mod engine; -mod serialize; - -pub use crate::artifact::StaticlibArtifact; -pub use crate::builder::Staticlib; -pub use crate::engine::StaticlibEngine; - -/// Version number of this crate. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); diff --git a/lib/engine-staticlib/src/serialize.rs b/lib/engine-staticlib/src/serialize.rs deleted file mode 100644 index 3bd2fd799e..0000000000 --- a/lib/engine-staticlib/src/serialize.rs +++ /dev/null @@ -1,93 +0,0 @@ -use loupe::MemoryUsage; -use serde::{Deserialize, Serialize}; -use wasmer_compiler::{CompileModuleInfo, SectionIndex, Symbol, SymbolRegistry}; -use wasmer_types::entity::{EntityRef, PrimaryMap}; -use wasmer_types::{FunctionIndex, LocalFunctionIndex, OwnedDataInitializer, SignatureIndex}; - -/// Serializable struct that represents the compiled metadata. -#[derive(Serialize, Deserialize, Debug, MemoryUsage)] -pub struct ModuleMetadata { - pub compile_info: CompileModuleInfo, - pub prefix: String, - pub data_initializers: Box<[OwnedDataInitializer]>, - // The function body lengths (used to find function by address) - pub function_body_lengths: PrimaryMap, - pub cpu_features: u64, -} - -#[derive(MemoryUsage)] -pub struct ModuleMetadataSymbolRegistry { - pub prefix: String, -} - -impl ModuleMetadata { - pub fn split(&mut self) -> (&mut CompileModuleInfo, ModuleMetadataSymbolRegistry) { - let compile_info = &mut self.compile_info; - let symbol_registry = ModuleMetadataSymbolRegistry { - prefix: self.prefix.clone(), - }; - (compile_info, symbol_registry) - } - - pub fn get_symbol_registry(&self) -> ModuleMetadataSymbolRegistry { - ModuleMetadataSymbolRegistry { - prefix: self.prefix.clone(), - } - } -} - -impl SymbolRegistry for ModuleMetadataSymbolRegistry { - fn symbol_to_name(&self, symbol: Symbol) -> String { - match symbol { - Symbol::LocalFunction(index) => { - format!("wasmer_function_{}_{}", self.prefix, index.index()) - } - Symbol::Section(index) => format!("wasmer_section_{}_{}", self.prefix, index.index()), - Symbol::FunctionCallTrampoline(index) => { - format!( - "wasmer_trampoline_function_call_{}_{}", - self.prefix, - index.index() - ) - } - Symbol::DynamicFunctionTrampoline(index) => { - format!( - "wasmer_trampoline_dynamic_function_{}_{}", - self.prefix, - index.index() - ) - } - } - } - - fn name_to_symbol(&self, name: &str) -> Option { - if let Some(index) = name.strip_prefix(&format!("wasmer_function_{}_", self.prefix)) { - index - .parse::() - .ok() - .map(|index| Symbol::LocalFunction(LocalFunctionIndex::from_u32(index))) - } else if let Some(index) = name.strip_prefix(&format!("wasmer_section_{}_", self.prefix)) { - index - .parse::() - .ok() - .map(|index| Symbol::Section(SectionIndex::from_u32(index))) - } else if let Some(index) = - name.strip_prefix(&format!("wasmer_trampoline_function_call_{}_", self.prefix)) - { - index - .parse::() - .ok() - .map(|index| Symbol::FunctionCallTrampoline(SignatureIndex::from_u32(index))) - } else if let Some(index) = name.strip_prefix(&format!( - "wasmer_trampoline_dynamic_function_{}_", - self.prefix - )) { - index - .parse::() - .ok() - .map(|index| Symbol::DynamicFunctionTrampoline(FunctionIndex::from_u32(index))) - } else { - None - } - } -} diff --git a/lib/object/Cargo.toml b/lib/object/Cargo.toml deleted file mode 100644 index ff9ac4f3bf..0000000000 --- a/lib/object/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "wasmer-object" -version = "2.1.0" -description = "Wasmer Native Object generator" -categories = ["wasm"] -keywords = ["wasm", "webassembly"] -authors = ["Wasmer Engineering Team "] -repository = "https://github.com/wasmerio/wasmer" -license = "MIT" -readme = "README.md" -edition = "2018" - -[dependencies] -wasmer-types = { path = "../types", package = "wasmer-types-near", version = "2.0.3" } -wasmer-compiler = { path = "../compiler", package = "wasmer-compiler-near", version = "2.0.3", default-features = false, features = [ - "std", - "translator" -] } -object = { version = "0.27", default-features = false, features = ["write"] } -thiserror = "1.0" diff --git a/lib/object/README.md b/lib/object/README.md deleted file mode 100644 index 99e9c86204..0000000000 --- a/lib/object/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# `wasmer-object` [![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) - -The Wasmer Native Object crate aims at cross-generating native objects -for various platforms. - -This crate is the foundation of [the `wasmer-engine-dylib` -crate](../engine-dylib/). Given a compilation result, i.e. the result -of `wasmer_compiler::Compiler::compile_module`, this crate exposes -functions to create an `Object` file for a given target. It is a -useful thin layer on top of [the `object` -crate](https://github.com/gimli-rs/object). diff --git a/lib/object/src/error.rs b/lib/object/src/error.rs deleted file mode 100644 index 78fa3c6654..0000000000 --- a/lib/object/src/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use object::write::Error as ObjectWriteError; -use thiserror::Error; - -/// The Object error can occur when creating an object file -/// from a `Compilation`. -#[derive(Error, Debug)] -pub enum ObjectError { - /// The object was provided a not-supported binary format - #[error("Binary format {0} not supported")] - UnsupportedBinaryFormat(String), - /// The object was provided a not-supported architecture - #[error("Architecture {0} not supported")] - UnsupportedArchitecture(String), - /// The object was provided an unknown endianness - #[error("Unknown Endianness")] - UnknownEndianness, - /// The object was provided a not-supported architecture - #[error("Error when writing the object: {0}")] - Write(#[from] ObjectWriteError), -} diff --git a/lib/object/src/lib.rs b/lib/object/src/lib.rs deleted file mode 100644 index a7adc126d5..0000000000 --- a/lib/object/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Object creator for Wasm Compilations. -//! -//! Given a compilation result (this is, the result when calling `Compiler::compile_module`) -//! this exposes functions to create an Object file for a given target. - -#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates)] -#![warn(unused_import_braces)] -#![cfg_attr(feature = "cargo-clippy", allow(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 error; -mod module; - -pub use crate::error::ObjectError; -pub use crate::module::{emit_compilation, emit_data, get_object_for_target}; diff --git a/lib/object/src/module.rs b/lib/object/src/module.rs deleted file mode 100644 index 4094ca0875..0000000000 --- a/lib/object/src/module.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::error::ObjectError; -use object::write::{ - Object, Relocation, StandardSection, StandardSegment, Symbol as ObjSymbol, SymbolSection, -}; -use object::{ - elf, RelocationEncoding, RelocationKind, SectionKind, SymbolFlags, SymbolKind, SymbolScope, -}; -use wasmer_compiler::{ - Architecture, BinaryFormat, Compilation, CustomSectionProtection, Endianness, - RelocationKind as Reloc, RelocationTarget, SectionIndex, Symbol, SymbolRegistry, Triple, -}; -use wasmer_types::entity::PrimaryMap; -use wasmer_types::LocalFunctionIndex; - -const DWARF_SECTION_NAME: &[u8] = b".eh_frame"; - -/// Create an object for a given target `Triple`. -/// -/// # Usage -/// -/// ```rust -/// # use wasmer_compiler::Triple; -/// # use wasmer_object::ObjectError; -/// use wasmer_object::get_object_for_target; -/// -/// # fn generate_object_for_target(triple: &Triple) -> Result<(), ObjectError> { -/// let mut object = get_object_for_target(&triple)?; -/// -/// # Ok(()) -/// # } -/// ``` -pub fn get_object_for_target(triple: &Triple) -> Result { - let obj_binary_format = match triple.binary_format { - BinaryFormat::Elf => object::BinaryFormat::Elf, - BinaryFormat::Macho => object::BinaryFormat::MachO, - BinaryFormat::Coff => object::BinaryFormat::Coff, - binary_format => { - return Err(ObjectError::UnsupportedBinaryFormat(format!( - "{}", - binary_format - ))); - } - }; - let obj_architecture = match triple.architecture { - Architecture::X86_64 => object::Architecture::X86_64, - Architecture::Aarch64(_) => object::Architecture::Aarch64, - architecture => { - return Err(ObjectError::UnsupportedArchitecture(format!( - "{}", - architecture - ))); - } - }; - let obj_endianness = match triple - .endianness() - .map_err(|_| ObjectError::UnknownEndianness)? - { - Endianness::Little => object::Endianness::Little, - Endianness::Big => object::Endianness::Big, - }; - - Ok(Object::new( - obj_binary_format, - obj_architecture, - obj_endianness, - )) -} - -/// Write data into an existing object. -/// -/// # Usage -/// -/// ```rust -/// # use wasmer_compiler::Triple; -/// # use wasmer_object::ObjectError; -/// use wasmer_object::{get_object_for_target, emit_data}; -/// -/// # fn emit_data_into_object(triple: &Triple) -> Result<(), ObjectError> { -/// let mut object = get_object_for_target(&triple)?; -/// emit_data(&mut object, b"WASMER_METADATA", &b"Hello, World!"[..], 1)?; -/// -/// # Ok(()) -/// # } -/// ``` -pub fn emit_data( - obj: &mut Object, - name: &[u8], - data: &[u8], - align: u64, -) -> Result<(), ObjectError> { - let symbol_id = obj.add_symbol(ObjSymbol { - name: name.to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Data, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }); - let section_id = obj.section_id(StandardSection::Data); - obj.add_symbol_data(symbol_id, section_id, &data, align); - - Ok(()) -} - -/// Emit the compilation result into an existing object. -/// -/// # Usage -/// -/// ```rust -/// # use wasmer_compiler::{Compilation, SymbolRegistry, Triple}; -/// # use wasmer_object::ObjectError; -/// use wasmer_object::{get_object_for_target, emit_compilation}; -/// -/// # fn emit_compilation_into_object( -/// # triple: &Triple, -/// # compilation: Compilation, -/// # symbol_registry: impl SymbolRegistry, -/// # ) -> Result<(), ObjectError> { -/// let mut object = get_object_for_target(&triple)?; -/// emit_compilation(&mut object, compilation, &symbol_registry, &triple)?; -/// # Ok(()) -/// # } -/// ``` -pub fn emit_compilation( - obj: &mut Object, - compilation: Compilation, - symbol_registry: &impl SymbolRegistry, - triple: &Triple, -) -> Result<(), ObjectError> { - let function_bodies = compilation.get_function_bodies(); - let function_relocations = compilation.get_relocations(); - let custom_sections = compilation.get_custom_sections(); - let custom_section_relocations = compilation.get_custom_section_relocations(); - let function_call_trampolines = compilation.get_function_call_trampolines(); - let dynamic_function_trampolines = compilation.get_dynamic_function_trampolines(); - - let debug_index = compilation.get_debug().map(|d| d.eh_frame); - - let align = match triple.architecture { - Architecture::X86_64 => 1, - // In Arm64 is recommended a 4-byte alignment - Architecture::Aarch64(_) => 4, - _ => 1, - }; - - // Add sections - let custom_section_ids = custom_sections - .into_iter() - .map(|(section_index, custom_section)| { - if debug_index.map(|d| d == section_index).unwrap_or(false) { - // If this is the debug section - let segment = obj.segment_name(StandardSegment::Debug).to_vec(); - let section_id = - obj.add_section(segment, DWARF_SECTION_NAME.to_vec(), SectionKind::Debug); - obj.append_section_data(section_id, custom_section.bytes.as_slice(), align); - let section_name = symbol_registry.symbol_to_name(Symbol::Section(section_index)); - let symbol_id = obj.add_symbol(ObjSymbol { - name: section_name.into_bytes(), - value: 0, - size: custom_section.bytes.len() as _, - kind: SymbolKind::Data, - scope: SymbolScope::Compilation, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - (section_id, symbol_id) - } else { - let section_name = symbol_registry.symbol_to_name(Symbol::Section(section_index)); - let (section_kind, standard_section) = match custom_section.protection { - CustomSectionProtection::ReadExecute => { - (SymbolKind::Text, StandardSection::Text) - } - CustomSectionProtection::Read => (SymbolKind::Data, StandardSection::Data), - }; - let section_id = obj.section_id(standard_section); - let symbol_id = obj.add_symbol(ObjSymbol { - name: section_name.into_bytes(), - value: 0, - size: custom_section.bytes.len() as _, - kind: section_kind, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - obj.add_symbol_data( - symbol_id, - section_id, - custom_section.bytes.as_slice(), - align, - ); - (section_id, symbol_id) - } - }) - .collect::>(); - - // Add functions - let function_symbol_ids = function_bodies - .into_iter() - .map(|(function_local_index, function)| { - let function_name = - symbol_registry.symbol_to_name(Symbol::LocalFunction(function_local_index)); - let section_id = obj.section_id(StandardSection::Text); - let symbol_id = obj.add_symbol(ObjSymbol { - name: function_name.into_bytes(), - value: 0, - size: function.body.len() as _, - kind: SymbolKind::Text, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - obj.add_symbol_data(symbol_id, section_id, &function.body, align); - (section_id, symbol_id) - }) - .collect::>(); - - // Add function call trampolines - for (signature_index, function) in function_call_trampolines.into_iter() { - let function_name = - symbol_registry.symbol_to_name(Symbol::FunctionCallTrampoline(signature_index)); - let section_id = obj.section_id(StandardSection::Text); - let symbol_id = obj.add_symbol(ObjSymbol { - name: function_name.into_bytes(), - value: 0, - size: function.body.len() as _, - kind: SymbolKind::Text, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - obj.add_symbol_data(symbol_id, section_id, &function.body, align); - } - - // Add dynamic function trampolines - for (func_index, function) in dynamic_function_trampolines.into_iter() { - let function_name = - symbol_registry.symbol_to_name(Symbol::DynamicFunctionTrampoline(func_index)); - let section_id = obj.section_id(StandardSection::Text); - let symbol_id = obj.add_symbol(ObjSymbol { - name: function_name.into_bytes(), - value: 0, - size: function.body.len() as _, - kind: SymbolKind::Text, - scope: SymbolScope::Dynamic, - weak: false, - section: SymbolSection::Section(section_id), - flags: SymbolFlags::None, - }); - obj.add_symbol_data(symbol_id, section_id, &function.body, align); - } - - let mut all_relocations = Vec::new(); - - for (function_local_index, relocations) in function_relocations.into_iter() { - let (section_id, symbol_id) = function_symbol_ids.get(function_local_index).unwrap(); - all_relocations.push((*section_id, *symbol_id, relocations)) - } - - for (section_index, relocations) in custom_section_relocations.into_iter() { - if !debug_index.map(|d| d == section_index).unwrap_or(false) { - // Skip DWARF relocations just yet - let (section_id, symbol_id) = custom_section_ids.get(section_index).unwrap(); - all_relocations.push((*section_id, *symbol_id, relocations)); - } - } - - for (section_id, symbol_id, relocations) in all_relocations.into_iter() { - let (_symbol_id, section_offset) = obj.symbol_section_and_offset(symbol_id).unwrap(); - - for r in relocations { - let (relocation_kind, relocation_encoding, relocation_size) = match r.kind { - Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), - Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), - Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), - Reloc::X86CallPCRel4 => { - (RelocationKind::Relative, RelocationEncoding::X86Branch, 32) - } - Reloc::X86CallPLTRel4 => ( - RelocationKind::PltRelative, - RelocationEncoding::X86Branch, - 32, - ), - Reloc::X86GOTPCRel4 => { - (RelocationKind::GotRelative, RelocationEncoding::Generic, 32) - } - // Reloc::X86PCRelRodata4 => { - // } - Reloc::Arm64Call => ( - RelocationKind::Elf(elf::R_AARCH64_CALL26), - RelocationEncoding::Generic, - 32, - ), - Reloc::ElfX86_64TlsGd => ( - RelocationKind::Elf(elf::R_X86_64_TLSGD), - RelocationEncoding::Generic, - 32, - ), - other => { - return Err(ObjectError::UnsupportedArchitecture(format!( - "{} (relocation: {}", - triple.architecture, other - ))) - } - }; - - let relocation_address = section_offset + r.offset as u64; - - match r.reloc_target { - RelocationTarget::LocalFunc(index) => { - let (_, target_symbol) = function_symbol_ids.get(index).unwrap(); - obj.add_relocation( - section_id, - Relocation { - offset: relocation_address, - size: relocation_size, - kind: relocation_kind, - encoding: relocation_encoding, - symbol: *target_symbol, - addend: r.addend, - }, - ) - .map_err(ObjectError::Write)?; - } - RelocationTarget::LibCall(libcall) => { - let libcall_fn_name = libcall.to_function_name().as_bytes(); - // We add the symols lazily as we see them - let target_symbol = obj.symbol_id(libcall_fn_name).unwrap_or_else(|| { - obj.add_symbol(ObjSymbol { - name: libcall_fn_name.to_vec(), - value: 0, - size: 0, - kind: SymbolKind::Unknown, - scope: SymbolScope::Unknown, - weak: false, - section: SymbolSection::Undefined, - flags: SymbolFlags::None, - }) - }); - obj.add_relocation( - section_id, - Relocation { - offset: relocation_address, - size: relocation_size, - kind: relocation_kind, - encoding: relocation_encoding, - symbol: target_symbol, - addend: r.addend, - }, - ) - .map_err(ObjectError::Write)?; - } - RelocationTarget::CustomSection(section_index) => { - let (_, target_symbol) = custom_section_ids.get(section_index).unwrap(); - obj.add_relocation( - section_id, - Relocation { - offset: relocation_address, - size: relocation_size, - kind: relocation_kind, - encoding: relocation_encoding, - symbol: *target_symbol, - addend: r.addend, - }, - ) - .map_err(ObjectError::Write)?; - } - RelocationTarget::JumpTable(_func_index, _jt) => { - // do nothing - } - }; - } - } - - Ok(()) -} diff --git a/lib/vm/src/vmoffsets.rs b/lib/vm/src/vmoffsets.rs index 82f063f451..110140c05c 100644 --- a/lib/vm/src/vmoffsets.rs +++ b/lib/vm/src/vmoffsets.rs @@ -4,7 +4,7 @@ //! Offsets and sizes of various structs in wasmer-vm's vmcontext //! module. -#![deny(broken_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links)] use crate::VMBuiltinFunctionIndex; use loupe::MemoryUsage;