1 /* 2 Example of compiling, instantiating, and linking two WebAssembly modules 3 together. 4 5 You can compile and run this example on Linux with: 6 7 cargo build --release -p wasmtime-c-api 8 cc examples/linking.c \ 9 -I crates/c-api/include \ 10 -I crates/c-api/wasm-c-api/include \ 11 target/release/libwasmtime.a \ 12 -lpthread -ldl -lm \ 13 -o linking 14 ./linking 15 16 Note that on Windows and macOS the command will be similar, but you'll need 17 to tweak the `-lpthread` and such annotations. 18 19 You can also build using cmake: 20 21 mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-linking 22 */ 23 24 #include <assert.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <wasi.h> 28 #include <wasm.h> 29 #include <wasmtime.h> 30 31 #define MIN(a, b) ((a) < (b) ? (a) : (b)) 32 33 static void exit_with_error(const char *message, wasmtime_error_t *error, 34 wasm_trap_t *trap); 35 static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, 36 const char *file); 37 38 int main() { 39 // Set up our context 40 wasm_engine_t *engine = wasm_engine_new(); 41 assert(engine != NULL); 42 wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); 43 assert(store != NULL); 44 wasmtime_context_t *context = wasmtime_store_context(store); 45 46 wasm_byte_vec_t linking1_wasm, linking2_wasm; 47 read_wat_file(engine, &linking1_wasm, "examples/linking1.wat"); 48 read_wat_file(engine, &linking2_wasm, "examples/linking2.wat"); 49 50 // Compile our two modules 51 wasmtime_error_t *error; 52 wasmtime_module_t *linking1_module = NULL; 53 wasmtime_module_t *linking2_module = NULL; 54 error = wasmtime_module_new(engine, (uint8_t *)linking1_wasm.data, 55 linking1_wasm.size, &linking1_module); 56 if (error != NULL) 57 exit_with_error("failed to compile linking1", error, NULL); 58 error = wasmtime_module_new(engine, (uint8_t *)linking2_wasm.data, 59 linking2_wasm.size, &linking2_module); 60 if (error != NULL) 61 exit_with_error("failed to compile linking2", error, NULL); 62 wasm_byte_vec_delete(&linking1_wasm); 63 wasm_byte_vec_delete(&linking2_wasm); 64 65 // Configure WASI and store it within our `wasmtime_store_t` 66 wasi_config_t *wasi_config = wasi_config_new(); 67 assert(wasi_config); 68 wasi_config_inherit_argv(wasi_config); 69 wasi_config_inherit_env(wasi_config); 70 wasi_config_inherit_stdin(wasi_config); 71 wasi_config_inherit_stdout(wasi_config); 72 wasi_config_inherit_stderr(wasi_config); 73 wasm_trap_t *trap = NULL; 74 error = wasmtime_context_set_wasi(context, wasi_config); 75 if (error != NULL) 76 exit_with_error("failed to instantiate wasi", NULL, trap); 77 78 // Create our linker which will be linking our modules together, and then add 79 // our WASI instance to it. 80 wasmtime_linker_t *linker = wasmtime_linker_new(engine); 81 error = wasmtime_linker_define_wasi(linker); 82 if (error != NULL) 83 exit_with_error("failed to link wasi", error, NULL); 84 85 // Instantiate `linking2` with our linker. 86 wasmtime_instance_t linking2; 87 error = wasmtime_linker_instantiate(linker, context, linking2_module, 88 &linking2, &trap); 89 if (error != NULL || trap != NULL) 90 exit_with_error("failed to instantiate linking2", error, trap); 91 92 // Register our new `linking2` instance with the linker 93 error = wasmtime_linker_define_instance(linker, context, "linking2", 94 strlen("linking2"), &linking2); 95 if (error != NULL) 96 exit_with_error("failed to link linking2", error, NULL); 97 98 // Instantiate `linking1` with the linker now that `linking2` is defined 99 wasmtime_instance_t linking1; 100 error = wasmtime_linker_instantiate(linker, context, linking1_module, 101 &linking1, &trap); 102 if (error != NULL || trap != NULL) 103 exit_with_error("failed to instantiate linking1", error, trap); 104 105 // Lookup our `run` export function 106 wasmtime_extern_t run; 107 bool ok = wasmtime_instance_export_get(context, &linking1, "run", 3, &run); 108 assert(ok); 109 assert(run.kind == WASMTIME_EXTERN_FUNC); 110 error = wasmtime_func_call(context, &run.of.func, NULL, 0, NULL, 0, &trap); 111 if (error != NULL || trap != NULL) 112 exit_with_error("failed to call run", error, trap); 113 114 // Clean up after ourselves at this point 115 wasmtime_linker_delete(linker); 116 wasmtime_module_delete(linking1_module); 117 wasmtime_module_delete(linking2_module); 118 wasmtime_store_delete(store); 119 wasm_engine_delete(engine); 120 return 0; 121 } 122 123 static void read_wat_file(wasm_engine_t *engine, wasm_byte_vec_t *bytes, 124 const char *filename) { 125 wasm_byte_vec_t wat; 126 // Load our input file to parse it next 127 FILE *file = fopen(filename, "r"); 128 if (!file) { 129 printf("> Error loading file!\n"); 130 exit(1); 131 } 132 fseek(file, 0L, SEEK_END); 133 size_t file_size = ftell(file); 134 wasm_byte_vec_new_uninitialized(&wat, file_size); 135 fseek(file, 0L, SEEK_SET); 136 if (fread(wat.data, file_size, 1, file) != 1) { 137 printf("> Error loading module!\n"); 138 exit(1); 139 } 140 fclose(file); 141 142 // Parse the wat into the binary wasm format 143 wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, bytes); 144 if (error != NULL) 145 exit_with_error("failed to parse wat", error, NULL); 146 wasm_byte_vec_delete(&wat); 147 } 148 149 static void exit_with_error(const char *message, wasmtime_error_t *error, 150 wasm_trap_t *trap) { 151 fprintf(stderr, "error: %s\n", message); 152 wasm_byte_vec_t error_message; 153 if (error != NULL) { 154 wasmtime_error_message(error, &error_message); 155 wasmtime_error_delete(error); 156 } else { 157 wasm_trap_message(trap, &error_message); 158 wasm_trap_delete(trap); 159 } 160 fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data); 161 wasm_byte_vec_delete(&error_message); 162 exit(1); 163 } 164