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. Be sure to read the API documentation for Linker as well.

Wasm: 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")
)

Wasm: 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))
)

Host Source

//! Example of instantiating two modules which link to each other.

// You can execute this example with `cargo run --example linking`

use wasmtime::*;
use wasmtime_wasi::WasiCtx;

fn main() -> Result<()> {
    let engine = Engine::default();

    // First set up our linker which is going to be linking modules together. We
    // want our linker to have wasi available, so we set that up here as well.
    let mut linker = Linker::new(&engine);
    wasmtime_wasi::p1::add_to_linker_sync(&mut linker, |s| s)?;

    // Load and compile our two modules
    let linking1 = Module::from_file(&engine, "examples/linking1.wat")?;
    let linking2 = Module::from_file(&engine, "examples/linking2.wat")?;

    // Configure WASI and insert it into a `Store`
    let wasi = WasiCtx::builder().inherit_stdio().inherit_args().build_p1();
    let mut store = Store::new(&engine, wasi);

    // Instantiate our first module which only uses WASI, then register that
    // instance with the linker since the next linking will use it.
    let linking2 = linker.instantiate(&mut store, &linking2)?;
    linker.instance(&mut store, "linking2", linking2)?;

    // And with that we can perform the final link and the execute the module.
    let linking1 = linker.instantiate(&mut store, &linking1)?;
    let run = linking1.get_typed_func::<(), ()>(&mut store, "run")?;
    run.call(&mut store, ())?;
    Ok(())
}
/*
Example of compiling, instantiating, and linking two WebAssembly modules
together.

You can 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);
}
#include <fstream>
#include <iostream>
#include <sstream>
#include <wasmtime.hh>

using namespace wasmtime;

template <typename T, typename E> T unwrap(Result<T, E> result) {
  if (result) {
    return result.ok();
  }
  std::cerr << "error: " << result.err().message() << "\n";
  std::abort();
}

std::string readFile(const char *name) {
  std::ifstream watFile;
  watFile.open(name);
  std::stringstream strStream;
  strStream << watFile.rdbuf();
  return strStream.str();
}

int main() {
  Engine engine;
  Store store(engine);

  // Read our input `*.wat` files into `std::string`s
  std::string linking1_wat = readFile("examples/linking1.wat");
  std::string linking2_wat = readFile("examples/linking2.wat");

  // Compile our two modules
  Module linking1_module = Module::compile(engine, linking1_wat).unwrap();
  Module linking2_module = Module::compile(engine, linking2_wat).unwrap();

  // Configure WASI and store it within our `wasmtime_store_t`
  WasiConfig wasi;
  wasi.inherit_argv();
  wasi.inherit_env();
  wasi.inherit_stdin();
  wasi.inherit_stdout();
  wasi.inherit_stderr();
  store.context().set_wasi(std::move(wasi)).unwrap();

  // Create our linker which will be linking our modules together, and then add
  // our WASI instance to it.
  Linker linker(engine);
  linker.define_wasi().unwrap();

  // Instantiate our first module which only uses WASI, then register that
  // instance with the linker since the next linking will use it.
  Instance linking2 = linker.instantiate(store, linking2_module).unwrap();
  linker.define_instance(store, "linking2", linking2).unwrap();

  // And with that we can perform the final link and the execute the module.
  Instance linking1 = linker.instantiate(store, linking1_module).unwrap();
  Func f = std::get<Func>(*linking1.get(store, "run"));
  f.call(store, {}).unwrap();
}