diff --git a/CHANGELOG.md b/CHANGELOG.md index a6940da6065..37b1390ec3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,12 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#399](https://github.com/wasmerio/wasmer/pull/399) Add example of using a plugin extended from WASI +- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows +- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule ## 0.4.0 - 2018-04-23 -- [#397](https://github.com/wasmerio/wasmer/pull/397) Fix WASI fs abstraction to work on Windows -- [#390](https://github.com/wasmerio/wasmer/pull/390) Pin released wapm version and add it as a git submodule - [#383](https://github.com/wasmerio/wasmer/pull/383) Hook up wasi exit code to wasmer cli. - [#382](https://github.com/wasmerio/wasmer/pull/382) Improve error message on `--backend` flag to only suggest currently enabled backends - [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors. diff --git a/Cargo.lock b/Cargo.lock index 2bac41a8191..44e77e7df31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1317,6 +1317,10 @@ name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "plugin-for-example" +version = "0.1.0" + [[package]] name = "podio" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index e71b907184b..2391db8b370 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ wasmer-llvm-backend = { path = "lib/llvm-backend", optional = true } wasmer-wasi = { path = "lib/wasi", optional = true } [workspace] -members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi"] +members = ["lib/clif-backend", "lib/singlepass-backend", "lib/runtime", "lib/runtime-abi", "lib/runtime-core", "lib/emscripten", "lib/spectests", "lib/win-exception-handler", "lib/runtime-c-api", "lib/llvm-backend", "lib/wasi", "examples/plugin-for-example"] [build-dependencies] wabt = "0.7.2" @@ -49,3 +49,7 @@ fast-tests = [] "backend:singlepass" = ["wasmer-singlepass-backend"] wasi = ["wasmer-wasi"] vfs = ["wasmer-runtime-abi"] + +[[example]] +name = "plugin" +crate-type = ["bin"] diff --git a/examples/plugin-for-example.wasm b/examples/plugin-for-example.wasm new file mode 100755 index 00000000000..4754287abdb Binary files /dev/null and b/examples/plugin-for-example.wasm differ diff --git a/examples/plugin-for-example/Cargo.toml b/examples/plugin-for-example/Cargo.toml new file mode 100644 index 00000000000..28d23491031 --- /dev/null +++ b/examples/plugin-for-example/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +authors = ["The Wasmer Engineering Team "] +edition = "2018" + +[dependencies] diff --git a/examples/plugin-for-example/README.md b/examples/plugin-for-example/README.md new file mode 100644 index 00000000000..42bc3ac269d --- /dev/null +++ b/examples/plugin-for-example/README.md @@ -0,0 +1,43 @@ +# WASI plugin example + +In this example we extend the imports of Wasmer's WASI ABI to demonstrate how custom plugins work. + +See the `wasmer/examples/plugin.rs` file for the source code of the host system. + +## Compiling +_Attention Windows users: WASI target only works with the `nightly-x86_64-pc-windows-gnu` toolchain._ +``` +# Install an up to date version of Rust nightly +# Add the target +rustup target add wasm32-unknown-wasi +# build it +cargo +nightly build --release --target=wasm32-unknown-wasi +# copy it to examples folder +cp ../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm ../ +``` + +## Running +``` +# Go back to top level Wasmer dir +cd .. +# Run the example +cargo run --example plugin +``` + +## Inspecting the plugin +``` +# Install wabt via wapm; installed globally with the `g` flag +wapm install -g wabt +# Turn the binary WASM file in to a readable WAT text file +wapm run wasm2wat examples/plugin-for-example.wasm +``` + +At the top of the file we can see which functions this plugin expects. Most are covered by WASI, but we handle the rest. + +## Explanation + +In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allowing our program to rely on Wasmer's implementation of the syscalls defined by WASI as well as our own that we made. This allows us to use the full power of an existing ABI, like WASI, and give it super-powers for our specific use case. + +Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24). + +[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/ diff --git a/examples/plugin-for-example/src/main.rs b/examples/plugin-for-example/src/main.rs new file mode 100644 index 00000000000..9ee284aab58 --- /dev/null +++ b/examples/plugin-for-example/src/main.rs @@ -0,0 +1,12 @@ +extern "C" { + fn it_works() -> i32; +} + +#[no_mangle] +pub fn plugin_entrypoint(n: i32) -> i32 { + println!("Hello from inside WASI"); + let result = unsafe { it_works() }; + result + n +} + +pub fn main() {} diff --git a/examples/plugin-for-example/wapm.toml b/examples/plugin-for-example/wapm.toml new file mode 100644 index 00000000000..7e2e261a766 --- /dev/null +++ b/examples/plugin-for-example/wapm.toml @@ -0,0 +1,12 @@ +[package] +name = "plugin-for-example" +version = "0.1.0" +description = "A plugin for our example system" +readme = "README.md" +repository = "https://github.com/wasmerio/wasmer/examples/plugin-for-example" +license = "MIT" + +[[module]] +name = "plugin-for-example" +source = "../../target/wasm32-unknown-wasi/release/plugin-for-example.wasm" +abi = "none" \ No newline at end of file diff --git a/examples/plugin.rs b/examples/plugin.rs new file mode 100644 index 00000000000..dbb06fe6165 --- /dev/null +++ b/examples/plugin.rs @@ -0,0 +1,38 @@ +use wasmer_runtime::{func, imports, instantiate}; +use wasmer_runtime_core::vm::Ctx; +use wasmer_wasi::generate_import_object; + +static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm"; + +fn it_works(_ctx: &mut Ctx) -> i32 { + println!("Hello from outside WASI"); + 5 +} + +fn main() { + // Load the plugin data + let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!( + "Could not read in WASM plugin at {}", + PLUGIN_LOCATION + )); + + // WASI imports + let mut base_imports = generate_import_object(vec![], vec![], vec![]); + // env is the default namespace for extern functions + let custom_imports = imports! { + "env" => { + "it_works" => func!(it_works), + }, + }; + // The WASI imports object contains all required import functions for a WASI module to run. + // Extend this imports with our custom imports containing "it_works" function so that our custom wasm code may run. + base_imports.extend(custom_imports); + let instance = + instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module"); + + // get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32 + let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap(); + // call the "entry_point" function in WebAssembly with the number "2" as the i32 argument + let result = entry_point.call(2).expect("failed to execute plugin"); + println!("result: {}", result); +}