Linking modules
You can also browse this source code online and clone the wasmtime repository to run the example locally.
This example shows off how to compile and instantiate modules which link together.
linking1.wat
(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")
)
linking2.wat
(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))
)
linking.c
/*
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-c-api
cc examples/linking.c \
-I crates/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.
You can also build using cmake:
mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-linking
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <wasi.h>
#include <wasm.h>
#include <wasmtime.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static void exit_with_error(const char *message, wasmtime_error_t *error,
wasm_trap_t *trap);
static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes,
const char *file);
int main() {
// Set up our context
wasm_engine_t *engine = wasm_engine_new();
assert(engine != NULL);
wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL);
assert(store != NULL);
wasmtime_context_t *context = wasmtime_store_context(store);
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
wasmtime_error_t *error;
wasmtime_module_t *linking1_module = NULL;
wasmtime_module_t *linking2_module = NULL;
error = wasmtime_module_new(engine, (uint8_t *)linking1_wasm.data,
linking1_wasm.size, &linking1_module);
if (error != NULL)
exit_with_error("failed to compile linking1", error, NULL);
error = wasmtime_module_new(engine, (uint8_t *)linking2_wasm.data,
linking2_wasm.size, &linking2_module);
if (error != NULL)
exit_with_error("failed to compile linking2", error, NULL);
wasm_byte_vec_delete(&linking1_wasm);
wasm_byte_vec_delete(&linking2_wasm);
// Configure WASI and store it within our `wasmtime_store_t`
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;
error = wasmtime_context_set_wasi(context, wasi_config);
if (error != NULL)
exit_with_error("failed to instantiate wasi", NULL, trap);
// Create our linker which will be linking our modules together, and then add
// our WASI instance to it.
wasmtime_linker_t *linker = wasmtime_linker_new(engine);
error = wasmtime_linker_define_wasi(linker);
if (error != NULL)
exit_with_error("failed to link wasi", error, NULL);
// Instantiate `linking2` with our linker.
wasmtime_instance_t linking2;
error = wasmtime_linker_instantiate(linker, context, linking2_module,
&linking2, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate linking2", error, trap);
// Register our new `linking2` instance with the linker
error = wasmtime_linker_define_instance(linker, context, "linking2",
strlen("linking2"), &linking2);
if (error != NULL)
exit_with_error("failed to link linking2", error, NULL);
// Instantiate `linking1` with the linker now that `linking2` is defined
wasmtime_instance_t linking1;
error = wasmtime_linker_instantiate(linker, context, linking1_module,
&linking1, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to instantiate linking1", error, trap);
// Lookup our `run` export function
wasmtime_extern_t run;
bool ok = wasmtime_instance_export_get(context, &linking1, "run", 3, &run);
assert(ok);
assert(run.kind == WASMTIME_EXTERN_FUNC);
error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap);
if (error != NULL || trap != NULL)
exit_with_error("failed to call run", error, trap);
// Clean up after ourselves at this point
wasmtime_linker_delete(linker);
wasmtime_module_delete(linking1_module);
wasmtime_module_delete(linking2_module);
wasmtime_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
wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, bytes);
if (error != NULL)
exit_with_error("failed to parse wat", error, NULL);
wasm_byte_vec_delete(&wat);
}
static void exit_with_error(const char *message, wasmtime_error_t *error,
wasm_trap_t *trap) {
fprintf(stderr, "error: %s\n", message);
wasm_byte_vec_t error_message;
if (error != NULL) {
wasmtime_error_message(error, &error_message);
wasmtime_error_delete(error);
} else {
wasm_trap_message(trap, &error_message);
wasm_trap_delete(trap);
}
fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data);
wasm_byte_vec_delete(&error_message);
exit(1);
}