diff --git a/Cargo.lock b/Cargo.lock index 9bddeda50cc5..369dffaaa1e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -848,6 +848,10 @@ dependencies = [ name = "example-fib-debug-wasm" version = "0.1.0" +[[package]] +name = "example-wasi-wasm" +version = "0.1.0" + [[package]] name = "faerie" version = "0.15.0" diff --git a/Cargo.toml b/Cargo.toml index f7f690398b36..d17ff8c94a77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ members = [ "crates/misc/rust", "crates/wiggle", "examples/fib-debug/wasm", + "examples/wasi/wasm", "fuzz", ] diff --git a/crates/misc/run-examples/src/main.rs b/crates/misc/run-examples/src/main.rs index 6eec10f21230..f782b929cb66 100644 --- a/crates/misc/run-examples/src/main.rs +++ b/crates/misc/run-examples/src/main.rs @@ -2,13 +2,18 @@ use std::collections::BTreeSet; use std::process::Command; fn main() { + let example_to_run = std::env::args().nth(1); let examples = std::fs::read_dir("examples").unwrap(); let examples = examples - .map(|e| { + .filter_map(|e| { let e = e.unwrap(); let path = e.path(); let dir = e.metadata().unwrap().is_dir(); - (path.file_stem().unwrap().to_str().unwrap().to_owned(), dir) + if let Some("wat") = path.extension().and_then(|s| s.to_str()) { + return None; + } + + Some((path.file_stem().unwrap().to_str().unwrap().to_owned(), dir)) }) .collect::>(); @@ -21,14 +26,24 @@ fn main() { if example == "README" { continue; } + if let Some(example_to_run) = &example_to_run { + if !example.contains(&example_to_run[..]) { + continue; + } + } if is_dir { println!("======== Rust wasm file `{}` ============", example); + let target = if example == "fib-debug" { + "wasm32-unknown-unknown" + } else { + "wasm32-wasi" + }; run(Command::new("cargo") .arg("build") .arg("-p") .arg(format!("example-{}-wasm", example)) .arg("--target") - .arg("wasm32-unknown-unknown")); + .arg(target)); } println!("======== Rust example `{}` ============", example); run(Command::new("cargo") diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 9db228cf27a5..cb89521abd15 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -7,6 +7,22 @@ - [Examples](./examples.md) - [Markdown parser](./examples-markdown.md) - [Profiling WebAssembly](./examples-profiling.md) + - [Embedding in Rust](./examples-rust-embed.md) + - [Hello, world!](./examples-rust-hello-world.md) + - [Calculating the GCD](./examples-rust-gcd.md) + - [Using linear memory](./examples-rust-memory.md) + - [WASI](./examples-rust-wasi.md) + - [Linking modules](./examples-rust-linking.md) + - [Debugging](./examples-rust-debugging.md) + - [Using multi-value](./examples-rust-multi-value.md) + - [Embedding in C](./examples-c-embed.md) + - [Hello, world!](./examples-c-hello-world.md) + - [Calculating the GCD](./examples-c-gcd.md) + - [Using linear memory](./examples-c-memory.md) + - [WASI](./examples-c-wasi.md) + - [Linking modules](./examples-c-linking.md) + - [Debugging](./examples-c-debugging.md) + - [Using multi-value](./examples-c-multi-value.md) - [Using WebAssembly from your lanugage](./lang.md) - [Python](./lang-python.md) - [.NET](./lang-dotnet.md) diff --git a/docs/embed-rust.md b/docs/embed-rust.md index 36ab0bc7ea73..1d194b25b567 100644 --- a/docs/embed-rust.md +++ b/docs/embed-rust.md @@ -3,7 +3,8 @@ This document shows an example of how to embed Wasmtime using the [Rust API][apidoc] to execute a simple wasm program. Be sure to also check out the [full API documentation][apidoc] for a full listing of what the [`wasmtime` -crate][crate] has to offer. +crate][wasmtime] has to offer and the [book examples for +Rust](./examples-rust-embed.md) for more information. [apidoc]: https://bytecodealliance.github.io/wasmtime/api/wasmtime/ [wasmtime]: https://crates.io/crates/wasmtime diff --git a/docs/examples-c-debugging.md b/docs/examples-c-debugging.md new file mode 100644 index 000000000000..5fc2068ac9d7 --- /dev/null +++ b/docs/examples-c-debugging.md @@ -0,0 +1,15 @@ +# Debugging + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/fib-debug/main.c + +This example shows off how to set up a module for dynamic runtime debugging via +a native debugger like GDB or LLDB. + +## `main.c` + +```c +{{#include ../examples/fib-debug/main.c}} +``` diff --git a/docs/examples-c-embed.md b/docs/examples-c-embed.md new file mode 100644 index 000000000000..13c7999c0bb9 --- /dev/null +++ b/docs/examples-c-embed.md @@ -0,0 +1,12 @@ +# Embedding in C + +This section is intended to showcase the C embedding API for Wasmtime. The C +embedding API is based on the [proposed wasm C embedding API][proposal] (namely +[`wasm.h`]) and has a few extension headers (like [`wasi.h`] and +[`wasmtime.h`]) which are intended to eventually become part of the standard +themselves one day. + +[proposal]: https://github.com/webassembly/wasm-c-api +[`wasm.h`]: https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h +[`wasi.h`]: https://github.com/bytecodealliance/wasmtime/blob/master/crates/c-api/include/wasi.h +[`wasmtime.h`]: https://github.com/bytecodealliance/wasmtime/blob/master/crates/c-api/include/wasmtime.h diff --git a/docs/examples-c-gcd.md b/docs/examples-c-gcd.md new file mode 100644 index 000000000000..bb668f970919 --- /dev/null +++ b/docs/examples-c-gcd.md @@ -0,0 +1,22 @@ +# Calculating the GCD + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/gcd.c + +This example shows off how run a wasm program which calculates the GCD of two +numbers. + +## `gcd.wat` + +```wat +{{#include ../examples/gcd.wat}} +``` + + +## `gcd.c` + +```c +{{#include ../examples/gcd.c}} +``` diff --git a/docs/examples-c-hello-world.md b/docs/examples-c-hello-world.md new file mode 100644 index 000000000000..3416259e72d9 --- /dev/null +++ b/docs/examples-c-hello-world.md @@ -0,0 +1,22 @@ +# Hello, world! + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/hello.c + +This example shows off how to instantiate a simple wasm module and interact with +it. + +## `hello.wat` + +```wat +{{#include ../examples/hello.wat}} +``` + + +## `hello.c` + +```c +{{#include ../examples/hello.c}} +``` diff --git a/docs/examples-c-linking.md b/docs/examples-c-linking.md new file mode 100644 index 000000000000..dd3cb9b399c6 --- /dev/null +++ b/docs/examples-c-linking.md @@ -0,0 +1,27 @@ +# Linking modules + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/linking.c + +This example shows off how to compile and instantiate modules which link +together. + +## `linking1.wat` + +```wat +{{#include ../examples/linking1.wat}} +``` + +## `linking2.wat` + +```wat +{{#include ../examples/linking2.wat}} +``` + +## `linking.c` + +```c +{{#include ../examples/linking.c}} +``` diff --git a/docs/examples-c-memory.md b/docs/examples-c-memory.md new file mode 100644 index 000000000000..19157d4307c4 --- /dev/null +++ b/docs/examples-c-memory.md @@ -0,0 +1,24 @@ +# Using linear memory + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/memory.c + +This example shows off how to interact with wasm memory in a module. Be sure to +read the documentation for [`Memory`] as well. + +[`Memory`]: https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Memory.html + +## `memory.wat` + +```wat +{{#include ../examples/memory.wat}} +``` + + +## `memory.c` + +```c +{{#include ../examples/memory.c}} +``` diff --git a/docs/examples-c-multi-value.md b/docs/examples-c-multi-value.md new file mode 100644 index 000000000000..cd9640b0e076 --- /dev/null +++ b/docs/examples-c-multi-value.md @@ -0,0 +1,22 @@ +# Using multi-value + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/multi.c + +This example shows off how to interact with a wasm module that uses multi-value +exports and imports. + +## `multi.wat` + +```wat +{{#include ../examples/multi.wat}} +``` + + +## `multi.c` + +```c +{{#include ../examples/multi.c}} +``` diff --git a/docs/examples-c-wasi.md b/docs/examples-c-wasi.md new file mode 100644 index 000000000000..bcf38345275a --- /dev/null +++ b/docs/examples-c-wasi.md @@ -0,0 +1,21 @@ +# WASI + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/wasi/main.c + +This example shows off how to instantiate a wasm module using WASI imports. + +## Wasm Source code + +```rust,ignore +{{#include ../examples/wasi/wasm/wasi.c}} +``` + + +## `wasi.c` + +```c +{{#include ../examples/wasi/main.c}} +``` diff --git a/docs/examples-rust-debugging.md b/docs/examples-rust-debugging.md new file mode 100644 index 000000000000..acd86e1a8d84 --- /dev/null +++ b/docs/examples-rust-debugging.md @@ -0,0 +1,15 @@ +# Debugging + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/fib-debug/main.rs + +This example shows off how to set up a module for dynamic runtime debugging via +a native debugger like GDB or LLDB. + +## `main.rs` + +```rust,ignore +{{#include ../examples/fib-debug/main.rs}} +``` diff --git a/docs/examples-rust-embed.md b/docs/examples-rust-embed.md new file mode 100644 index 000000000000..576ea99ac495 --- /dev/null +++ b/docs/examples-rust-embed.md @@ -0,0 +1,7 @@ +# Embedding in Rust + +This section is intended to showcase the Rust embedding API for Wasmtime. This +is done through the [`wasmtime` crate](https://crates.io/crates/wasmtime). In +addition to browsing the following examples you can also browse the [specific +section on Rust embedding](./embed-rust.md) or the [full API +documentation](https://docs.rs/wasmtime). diff --git a/docs/examples-rust-gcd.md b/docs/examples-rust-gcd.md new file mode 100644 index 000000000000..061b5ebe66c7 --- /dev/null +++ b/docs/examples-rust-gcd.md @@ -0,0 +1,22 @@ +# Calculating the GCD + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/gcd.rs + +This example shows off how run a wasm program which calculates the GCD of two +numbers. + +## `gcd.wat` + +```wat +{{#include ../examples/gcd.wat}} +``` + + +## `gcd.rs` + +```rust,ignore +{{#include ../examples/gcd.rs}} +``` diff --git a/docs/examples-rust-hello-world.md b/docs/examples-rust-hello-world.md new file mode 100644 index 000000000000..b61084c49b25 --- /dev/null +++ b/docs/examples-rust-hello-world.md @@ -0,0 +1,22 @@ +# Hello, world! + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/hello.rs + +This example shows off how to instantiate a simple wasm module and interact with +it. + +## `hello.wat` + +```wat +{{#include ../examples/hello.wat}} +``` + + +## `hello.rs` + +```rust,ignore +{{#include ../examples/hello.rs}} +``` diff --git a/docs/examples-rust-linking.md b/docs/examples-rust-linking.md new file mode 100644 index 000000000000..bb2f9467a7cb --- /dev/null +++ b/docs/examples-rust-linking.md @@ -0,0 +1,27 @@ +# Linking modules + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/linking.rs + +This example shows off how to compile and instantiate modules which link +together. + +## `linking1.wat` + +```wat +{{#include ../examples/linking1.wat}} +``` + +## `linking2.wat` + +```wat +{{#include ../examples/linking2.wat}} +``` + +## `linking.rs` + +```rust,ignore +{{#include ../examples/linking.rs}} +``` diff --git a/docs/examples-rust-memory.md b/docs/examples-rust-memory.md new file mode 100644 index 000000000000..de52a8be3927 --- /dev/null +++ b/docs/examples-rust-memory.md @@ -0,0 +1,24 @@ +# Using linear memory + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/memory.rs + +This example shows off how to interact with wasm memory in a module. Be sure to +read the documentation for [`Memory`] as well. + +[`Memory`]: https://bytecodealliance.github.io/wasmtime/api/wasmtime/struct.Memory.html + +## `memory.wat` + +```wat +{{#include ../examples/memory.wat}} +``` + + +## `memory.rs` + +```rust,ignore +{{#include ../examples/memory.rs}} +``` diff --git a/docs/examples-rust-multi-value.md b/docs/examples-rust-multi-value.md new file mode 100644 index 000000000000..b119502446a8 --- /dev/null +++ b/docs/examples-rust-multi-value.md @@ -0,0 +1,22 @@ +# Using multi-value + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/multi.rs + +This example shows off how to interact with a wasm module that uses multi-value +exports and imports. + +## `multi.wat` + +```wat +{{#include ../examples/multi.wat}} +``` + + +## `multi.rs` + +```rust,ignore +{{#include ../examples/multi.rs}} +``` diff --git a/docs/examples-rust-wasi.md b/docs/examples-rust-wasi.md new file mode 100644 index 000000000000..59a7aa861634 --- /dev/null +++ b/docs/examples-rust-wasi.md @@ -0,0 +1,21 @@ +# WASI + +You can also [browse this source code online][code] and clone the wasmtime +repository to run the example locally. + +[code]: https://github.com/bytecodealliance/wasmtime/blob/master/examples/wasi/main.rs + +This example shows off how to instantiate a wasm module using WASI imports. + +## Wasm Source code + +```rust +{{#include ../examples/wasi/wasm/wasi.rs}} +``` + + +## `wasi.rs` + +```rust,ignore +{{#include ../examples/wasi/main.rs}} +``` diff --git a/examples/linking.c b/examples/linking.c new file mode 100644 index 000000000000..8f3d73af8aeb --- /dev/null +++ b/examples/linking.c @@ -0,0 +1,194 @@ +/* +Example of compiling, instantiating, and linking two WebAssembly modules +together. + +You can compile and run this example on Linux with: + + cargo build --release -p wasmtime + cc examples/linking.c \ + -I crates/c-api/include \ + -I crates/c-api/wasm-c-api/include \ + target/release/libwasmtime.a \ + -lpthread -ldl -lm \ + -o linking + ./linking + +Note that on Windows and macOS the command will be similar, but you'll need +to tweak the `-lpthread` and such annotations. +*/ + +#include +#include +#include +#include +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static void print_trap(wasm_trap_t *trap); +static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, const char *file); + +int main() { + int ret = 0; + // Set up our context + wasm_engine_t *engine = wasm_engine_new(); + assert(engine != NULL); + wasm_store_t *store = wasm_store_new(engine); + assert(store != NULL); + + wasm_byte_vec_t linking1_wasm, linking2_wasm; + read_wat_file(engine, &linking1_wasm, "examples/linking1.wat"); + read_wat_file(engine, &linking2_wasm, "examples/linking2.wat"); + + // Compile our two modules + wasm_module_t *linking1_module = wasm_module_new(store, &linking1_wasm); + assert(linking1_module != NULL); + wasm_module_t *linking2_module = wasm_module_new(store, &linking2_wasm); + assert(linking2_module != NULL); + wasm_byte_vec_delete(&linking1_wasm); + wasm_byte_vec_delete(&linking2_wasm); + + // Instantiate wasi + wasi_config_t *wasi_config = wasi_config_new(); + assert(wasi_config); + wasi_config_inherit_argv(wasi_config); + wasi_config_inherit_env(wasi_config); + wasi_config_inherit_stdin(wasi_config); + wasi_config_inherit_stdout(wasi_config); + wasi_config_inherit_stderr(wasi_config); + wasm_trap_t *trap = NULL; + wasi_instance_t *wasi = wasi_instance_new(store, wasi_config, &trap); + if (wasi == NULL) { + print_trap(trap); + exit(1); + } + + // Create imports for `linking2` + wasm_importtype_vec_t linking2_import_types; + wasm_module_imports(linking2_module, &linking2_import_types); + const wasm_extern_t **linking2_imports = calloc(linking2_import_types.size, sizeof(void*)); + assert(linking2_imports); + for (int i = 0; i < linking2_import_types.size; i++) { + const wasm_extern_t *binding = wasi_instance_bind_import(wasi, linking2_import_types.data[i]); + if (binding != NULL) { + linking2_imports[i] = binding; + } else { + printf("> Failed to satisfy import\n"); + exit(1); + } + } + wasm_importtype_vec_delete(&linking2_import_types); + + // Instantiate `linking2` + wasm_instance_t *linking2 = wasm_instance_new(store, linking2_module, linking2_imports, &trap); + if (linking2 == NULL) { + print_trap(trap); + exit(1); + } + free(linking2_imports); + wasm_extern_vec_t linking2_externs; + wasm_instance_exports(linking2, &linking2_externs); + wasm_exporttype_vec_t linking2_exports; + wasm_module_exports(linking2_module, &linking2_exports); + + // Create imports for `linking1` + wasm_importtype_vec_t linking1_import_types; + wasm_module_imports(linking1_module, &linking1_import_types); + const wasm_extern_t **linking1_imports = calloc(linking1_import_types.size, sizeof(void*)); + assert(linking1_imports); + for (int i = 0; i < linking1_import_types.size; i++) { + const wasm_importtype_t *import = linking1_import_types.data[i]; + const wasm_name_t *module = wasm_importtype_module(import); + const wasm_name_t *name = wasm_importtype_name(import); + if (strncmp(module->data, "linking2", module->size) == 0) { + const wasm_extern_t *e = NULL; + for (int j = 0; j < linking2_exports.size; j++) { + const wasm_name_t *export_name = wasm_exporttype_name(linking2_exports.data[j]); + if (name->size == export_name->size && + strncmp(name->data, export_name->data, name->size) == 0) { + e = linking2_externs.data[j]; + break; + } + } + if (e) { + linking1_imports[i] = e; + continue; + } + } + + printf("> Failed to satisfy import\n"); + exit(1); + } + wasm_importtype_vec_delete(&linking1_import_types); + + // Instantiate `linking1` + wasm_instance_t *linking1 = wasm_instance_new(store, linking1_module, linking1_imports, &trap); + if (linking1 == NULL) { + print_trap(trap); + exit(1); + } + + // Lookup our `run` export function + wasm_extern_vec_t linking1_externs; + wasm_instance_exports(linking1, &linking1_externs); + assert(linking1_externs.size == 1); + wasm_func_t *run = wasm_extern_as_func(linking1_externs.data[0]); + assert(run != NULL); + trap = wasm_func_call(run, NULL, NULL); + if (trap != NULL) { + print_trap(trap); + exit(1); + } + + // Clean up after ourselves at this point + wasm_extern_vec_delete(&linking1_externs); + wasm_extern_vec_delete(&linking2_externs); + wasm_instance_delete(linking1); + wasm_instance_delete(linking2); + wasm_module_delete(linking1_module); + wasm_module_delete(linking2_module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; +} + +static void read_wat_file( + wasm_engine_t *engine, + wasm_byte_vec_t *bytes, + const char *filename +) { + wasm_byte_vec_t wat; + // Load our input file to parse it next + FILE* file = fopen(filename, "r"); + if (!file) { + printf("> Error loading file!\n"); + exit(1); + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + wasm_byte_vec_new_uninitialized(&wat, file_size); + fseek(file, 0L, SEEK_SET); + if (fread(wat.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + exit(1); + } + fclose(file); + + // Parse the wat into the binary wasm format + wasm_byte_vec_t error; + if (wasmtime_wat2wasm(engine, &wat, bytes, &error) == 0) { + fprintf(stderr, "failed to parse wat %.*s\n", (int) error.size, error.data); + exit(1); + } + wasm_byte_vec_delete(&wat); +} + +static void print_trap(wasm_trap_t *trap) { + assert(trap != NULL); + wasm_message_t message; + wasm_trap_message(trap, &message); + fprintf(stderr, "failed to instantiate module %.*s\n", (int) message.size, message.data); + wasm_byte_vec_delete(&message); + wasm_trap_delete(trap); +} diff --git a/examples/linking.rs b/examples/linking.rs new file mode 100644 index 000000000000..bee24e72754f --- /dev/null +++ b/examples/linking.rs @@ -0,0 +1,57 @@ +//! Example of instantiating two modules which link to each other. + +// You can execute this example with `cargo run --example linking` + +use anyhow::Result; +use wasmtime::*; +use wasmtime_wasi::{Wasi, WasiCtx}; + +fn main() -> Result<()> { + let store = Store::default(); + + // Load and compile our two modules + let linking1 = Module::from_file(&store, "examples/linking1.wat")?; + let linking2 = Module::from_file(&store, "examples/linking2.wat")?; + + // Instantiate the first, `linking2`, which uses WASI imports + let wasi = Wasi::new(&store, WasiCtx::new(std::env::args())?); + let mut imports = Vec::new(); + for import in linking2.imports() { + if import.module() == "wasi_snapshot_preview1" { + if let Some(export) = wasi.get_export(import.name()) { + imports.push(Extern::from(export.clone())); + continue; + } + } + panic!( + "couldn't find import for `{}::{}`", + import.module(), + import.name() + ); + } + let linking2 = Instance::new(&linking2, &imports)?; + + // And using the previous instance we can create the imports for `linking1`, + // using the previous exports. + let mut imports = Vec::new(); + for import in linking1.imports() { + if import.module() == "linking2" { + if let Some(export) = linking2.get_export(import.name()) { + imports.push(export.clone()); + continue; + } + } + panic!( + "couldn't find import for `{}::{}`", + import.module(), + import.name() + ); + } + let linking1 = Instance::new(&linking1, &imports)?; + + // And once everything is instantiated we can run! + let run = linking1.get_export("run").and_then(|e| e.func()).unwrap(); + let run = run.get0::<()>()?; + run()?; + Ok(()) +} diff --git a/examples/linking1.wat b/examples/linking1.wat new file mode 100644 index 000000000000..24cac6df0e0f --- /dev/null +++ b/examples/linking1.wat @@ -0,0 +1,23 @@ +(module + (import "linking2" "double" (func $double (param i32) (result i32))) + (import "linking2" "log" (func $log (param i32 i32))) + (import "linking2" "memory" (memory 1)) + (import "linking2" "memory_offset" (global $offset i32)) + + (func (export "run") + ;; Call into the other module to double our number, and we could print it + ;; here but for now we just drop it + i32.const 2 + call $double + drop + + ;; Our `data` segment initialized our imported memory, so let's print the + ;; string there now. + global.get $offset + i32.const 14 + call $log + ) + + (data (global.get $offset) "Hello, world!\n") +) + diff --git a/examples/linking2.wat b/examples/linking2.wat new file mode 100644 index 000000000000..8011f0422433 --- /dev/null +++ b/examples/linking2.wat @@ -0,0 +1,33 @@ +(module + (type $fd_write_ty (func (param i32 i32 i32 i32) (result i32))) + (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (type $fd_write_ty))) + + (func (export "double") (param i32) (result i32) + local.get 0 + i32.const 2 + i32.mul + ) + + (func (export "log") (param i32 i32) + ;; store the pointer in the first iovec field + i32.const 4 + local.get 0 + i32.store + + ;; store the length in the first iovec field + i32.const 4 + local.get 1 + i32.store offset=4 + + ;; call the `fd_write` import + i32.const 1 ;; stdout fd + i32.const 4 ;; iovs start + i32.const 1 ;; number of iovs + i32.const 0 ;; where to write nwritten bytes + call $fd_write + drop + ) + + (memory (export "memory") 2) + (global (export "memory_offset") i32 (i32.const 65536)) +) diff --git a/examples/wasi/main.c b/examples/wasi/main.c new file mode 100644 index 000000000000..513dc362eddb --- /dev/null +++ b/examples/wasi/main.c @@ -0,0 +1,138 @@ +/* +Example of instantiating a WebAssembly which uses WASI imports. + +You can compile and run this example on Linux with: + + cargo build --release -p wasmtime + cc examples/wasi.c \ + -I crates/c-api/include \ + -I crates/c-api/wasm-c-api/include \ + target/release/libwasmtime.a \ + -lpthread -ldl -lm \ + -o wasi + ./wasi + +Note that on Windows and macOS the command will be similar, but you'll need +to tweak the `-lpthread` and such annotations. +*/ + +#include +#include +#include +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static void print_trap(wasm_trap_t *trap); +static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, const char *file); + +int main() { + int ret = 0; + // Set up our context + wasm_engine_t *engine = wasm_engine_new(); + assert(engine != NULL); + wasm_store_t *store = wasm_store_new(engine); + assert(store != NULL); + + + wasm_byte_vec_t wasm; + // Load our input file to parse it next + FILE* file = fopen("target/wasm32-wasi/debug/wasi.wasm", "rb"); + if (!file) { + printf("> Error loading file!\n"); + exit(1); + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + wasm_byte_vec_new_uninitialized(&wasm, file_size); + fseek(file, 0L, SEEK_SET); + if (fread(wasm.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + exit(1); + } + fclose(file); + + // Compile our modules + wasm_module_t *module = wasm_module_new(store, &wasm); + assert(module != NULL); + wasm_byte_vec_delete(&wasm); + + // Instantiate wasi + wasi_config_t *wasi_config = wasi_config_new(); + assert(wasi_config); + wasi_config_inherit_argv(wasi_config); + wasi_config_inherit_env(wasi_config); + wasi_config_inherit_stdin(wasi_config); + wasi_config_inherit_stdout(wasi_config); + wasi_config_inherit_stderr(wasi_config); + wasm_trap_t *trap = NULL; + wasi_instance_t *wasi = wasi_instance_new(store, wasi_config, &trap); + if (wasi == NULL) { + print_trap(trap); + exit(1); + } + + // Create import list for our module using wasi + wasm_importtype_vec_t import_types; + wasm_module_imports(module, &import_types); + const wasm_extern_t **imports = calloc(import_types.size, sizeof(void*)); + assert(imports); + for (int i = 0; i < import_types.size; i++) { + const wasm_extern_t *binding = wasi_instance_bind_import(wasi, import_types.data[i]); + if (binding != NULL) { + imports[i] = binding; + } else { + printf("> Failed to satisfy import\n"); + exit(1); + } + } + wasm_importtype_vec_delete(&import_types); + + // Instantiate the module + wasm_instance_t *instance = wasm_instance_new(store, module, imports, &trap); + if (instance == NULL) { + print_trap(trap); + exit(1); + } + free(imports); + + // Lookup our `_start` export function + wasm_extern_vec_t externs; + wasm_instance_exports(instance, &externs); + wasm_exporttype_vec_t exports; + wasm_module_exports(module, &exports); + wasm_extern_t *start_extern = NULL; + for (int i = 0; i < exports.size; i++) { + const wasm_name_t *name = wasm_exporttype_name(exports.data[i]); + if (strncmp(name->data, "_start", name->size) == 0) + start_extern = externs.data[i]; + } + assert(start_extern); + wasm_func_t *start = wasm_extern_as_func(start_extern); + assert(start != NULL); + trap = wasm_func_call(start, NULL, NULL); + if (trap != NULL) { + print_trap(trap); + exit(1); + } + + // Clean up after ourselves at this point + wasm_exporttype_vec_delete(&exports); + wasm_extern_vec_delete(&externs); + wasm_instance_delete(instance); + wasm_module_delete(module); + wasm_store_delete(store); + wasm_engine_delete(engine); + return 0; +} + +static void print_trap(wasm_trap_t *trap) { + assert(trap != NULL); + wasm_message_t message; + wasm_trap_message(trap, &message); + fprintf(stderr, "failed to instantiate module %.*s\n", (int) message.size, message.data); + wasm_byte_vec_delete(&message); + wasm_trap_delete(trap); +} + diff --git a/examples/wasi/main.rs b/examples/wasi/main.rs new file mode 100644 index 000000000000..9216bce52fac --- /dev/null +++ b/examples/wasi/main.rs @@ -0,0 +1,43 @@ +//! Example of instantiating of instantiating a wasm module which uses WASI +//! imports. + +// You can execute this example with `cargo run --example wasi` + +use anyhow::Result; +use wasmtime::*; +use wasmtime_wasi::{Wasi, WasiCtx}; + +fn main() -> Result<()> { + let store = Store::default(); + let module = Module::from_file(&store, "target/wasm32-wasi/debug/wasi.wasm")?; + + // Create an instance of `Wasi` which contains a `WasiCtx`. Note that + // `WasiCtx` provides a number of ways to configure what the target program + // will have access to. + let wasi = Wasi::new(&store, WasiCtx::new(std::env::args())?); + let mut imports = Vec::new(); + for import in module.imports() { + if import.module() == "wasi_snapshot_preview1" { + if let Some(export) = wasi.get_export(import.name()) { + imports.push(Extern::from(export.clone())); + continue; + } + } + panic!( + "couldn't find import for `{}::{}`", + import.module(), + import.name() + ); + } + + // Instance our module with the imports we've created, then we can run the + // standard wasi `_start` function. + let instance = Instance::new(&module, &imports)?; + let start = instance + .get_export("_start") + .and_then(|e| e.func()) + .unwrap(); + let start = start.get0::<()>()?; + start()?; + Ok(()) +} diff --git a/examples/wasi/wasm/Cargo.toml b/examples/wasi/wasm/Cargo.toml new file mode 100644 index 000000000000..507a9f7047d1 --- /dev/null +++ b/examples/wasi/wasm/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "example-wasi-wasm" +version = "0.1.0" +authors = ["The Wasmtime Project Developers"] +edition = "2018" +publish = false + +[[bin]] +path = "wasi.rs" +name = "wasi" diff --git a/examples/wasi/wasm/wasi.rs b/examples/wasi/wasm/wasi.rs new file mode 100644 index 000000000000..e7a11a969c03 --- /dev/null +++ b/examples/wasi/wasm/wasi.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}