├── .github └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── README.md ├── wasm-sample-app ├── .gitignore ├── README.md ├── source.c └── target.wasm ├── wasm-wasi-sample-app ├── README.md ├── program.c └── program.wasm ├── wasmer-c-api-example.c └── wasmer-wasi-c-api-example.c /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: wasmer-c-api build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ubuntu-latest, macos-latest, windows-latest] 10 | runs-on: ${{ matrix.os }} 11 | 12 | steps: 13 | - name: checkout 14 | uses: actions/checkout@v1 15 | - name: install rust 16 | uses: actions-rs/toolchain@v1.0.0 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | - name: cmake 22 | if: matrix.os != 'windows-latest' 23 | run: cmake . 24 | - name: cmake windows 25 | if: matrix.os == 'windows-latest' 26 | run: cmake . -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" 27 | - name: make 28 | run: make 29 | - name: run the examples (Unix) 30 | if: matrix.os != 'windows-latest' 31 | run: | 32 | ./wasmer-c-api-example 33 | ./wasmer-wasi-c-api-example 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CTestTestfile.cmake 11 | _deps 12 | wasmer 13 | rust-build 14 | wasmer-c-api-example 15 | wasmer-wasi-c-api-example -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (WasmerCApiExample) 3 | add_executable(wasmer-c-api-example wasmer-c-api-example.c) 4 | add_executable(wasmer-wasi-c-api-example wasmer-wasi-c-api-example.c) 5 | 6 | include(ExternalProject) 7 | set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust-build) 8 | ExternalProject_Add( 9 | wasmer-runtime-c-api 10 | DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build 11 | GIT_REPOSITORY https://github.com/wasmerio/wasmer.git 12 | GIT_TAG origin/master 13 | CONFIGURE_COMMAND "" 14 | BUILD_COMMAND cargo build -p wasmer-runtime-c-api 15 | COMMAND cargo build -p wasmer-runtime-c-api 16 | BINARY_DIR "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/" 17 | INSTALL_COMMAND "" 18 | LOG_BUILD ON) 19 | add_dependencies(wasmer-c-api-example wasmer-runtime-c-api) 20 | 21 | if(WIN32) 22 | set(WASMER_LIB "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/target/debug/wasmer_runtime_c_api.dll") 23 | else() 24 | set(WASMER_LIB "${CMAKE_SOURCE_DIR}/rust-build/src/wasmer-runtime-c-api/target/debug/libwasmer_runtime_c_api${CMAKE_SHARED_LIBRARY_SUFFIX}") 25 | endif() 26 | 27 | if(NOT WASMER_LIB) 28 | message(FATAL_ERROR "wasmer library not found") 29 | endif() 30 | 31 | target_link_libraries(wasmer-c-api-example general ${WASMER_LIB}) 32 | target_link_libraries(wasmer-wasi-c-api-example general ${WASMER_LIB}) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Note: This repository is outdated and archived. It showcased the deprecated Wasmer C API. The new C API lands in [the Wasmer repository](https://github.com/wasmerio/wasmer/tree/master/lib/c-api) with [its examples](https://github.com/wasmerio/wasmer/tree/master/lib/c-api/examples) and [its online documentation](https://wasmerio.github.io/wasmer/crates/wasmer_c_api/index.html).** 2 | 3 |
4 | 5 | # C-API Embedder App Example 6 | 7 | [![Actions Status](https://github.com/wasmerio/wasmer-c-api/workflows/wasmer-c-api%20build/badge.svg)](https://github.com/wasmerio/wasmer-c-api/actions) 8 | 9 | This repo showcases how to use the [wasmer-runtime-c-api](https://crates.io/crates/wasmer-runtime-c-api/) from C. 10 | 11 | The documentation containing installation instructions, and additional examples can be found at the [Wasmer C/C++ Integration Documentation](https://docs.wasmer.io/runtime/c-integration/runtime-c-integration-installation). 12 | 13 | See [`wasmer-c-api-example.c`](./wasmer-c-api-example.c) and [`wasmer-wasi-c-api-example.c`](./wasmer-wasi-c-api-example.c) for the example implementations. 14 | 15 | The `wasm-sample-app` and `wasmer-wasi-c-api-example` directories contain example C wasm apps to run in the embedder app. 16 | 17 | ## Requirements 18 | - CMake 19 | - Emscripten to build the sample app 20 | 21 | ## Building & Running 22 | 23 | ```bash 24 | # Building the wasm-sample-app 25 | cd wasm-sample-app && emcc source.c -Os -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS="['_hello_wasm']" -o target.wasm 26 | 27 | # Building the wasm-wasi-sample-app 28 | cd wasmer-wasi-c-api-example && wasicc program.c -Oz -o program.wasm -Wl,--allow-undefined -Wl,--export-all 29 | 30 | # Build the embedder api 31 | cmake . 32 | make 33 | 34 | # Run the executables 35 | ./wasmer-c-api-example 36 | ./wasmer-wasi-c-api-example 37 | ``` 38 | 39 | ## Reference API Documentation 40 | 41 | The Reference API for the Wasmer C API can be found [here](https://wasmerio.github.io/wasmer/c/runtime-c-api/). 42 | -------------------------------------------------------------------------------- /wasm-sample-app/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /wasm-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | ## Requirements 4 | - Emscripten: https://emscripten.org/ 5 | 6 | ## Building 7 | `emcc source.c -Os -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS="['_hello_wasm']" -o target.wasm` 8 | 9 | ## Version 10 | The `target.wasm` file was build with the following version: 11 | ``` 12 | emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.38.21 13 | clang version 6.0.1 (emscripten 1.38.21 : 1.38.21) 14 | ``` -------------------------------------------------------------------------------- /wasm-sample-app/source.c: -------------------------------------------------------------------------------- 1 | extern void print_str(char *ptr, int len); 2 | 3 | int hello_wasm() 4 | { 5 | char *str = "Hello, World!"; 6 | print_str(str, 13); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /wasm-sample-app/target.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasmerio/wasmer-c-api/0fb412b502289e326fa11af4ec420c9d8836a484/wasm-sample-app/target.wasm -------------------------------------------------------------------------------- /wasm-wasi-sample-app/README.md: -------------------------------------------------------------------------------- 1 | # WASI Sample App 2 | 3 | ## Requirements 4 | - Install [wasienv] (instructions in its README.md) or manually set up Clang with a WASI sysroot 5 | 6 | ## Building 7 | ```sh 8 | # we must pass extra linker flags because we're extending the normal WASI ABI 9 | wasicc program.c -Oz -o program.wasm -Wl,--allow-undefined -Wl,--export-all 10 | ``` 11 | 12 | [wasienv]: https://github.com/wasienv/wasienv 13 | -------------------------------------------------------------------------------- /wasm-wasi-sample-app/program.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern void host_print(char *ptr, int len); 7 | 8 | __attribute__((used)) 9 | int print_random_number_of_length(int size) 10 | { 11 | while (size > 0) { 12 | int digit = random() % 10; 13 | putchar(digit + '0'); 14 | --size; 15 | } 16 | putchar('\n'); 17 | 18 | return 0; 19 | } 20 | 21 | int main(int argc, char* argv[]) 22 | { 23 | printf("The program name is %s\n", argv[0]); 24 | printf("Found %d arguments\n\n", argc); 25 | char* static_str = "Here's a string from the guest"; 26 | host_print(static_str, strlen(static_str)); 27 | 28 | srandom(time(NULL)); 29 | } 30 | -------------------------------------------------------------------------------- /wasm-wasi-sample-app/program.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wasmerio/wasmer-c-api/0fb412b502289e326fa11af4ec420c9d8836a484/wasm-wasi-sample-app/program.wasm -------------------------------------------------------------------------------- /wasmer-c-api-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rust-build/src/wasmer-runtime-c-api/lib/runtime-c-api/wasmer.h" 3 | #include 4 | #include 5 | #include 6 | 7 | static int print_str_called = false; 8 | 9 | // Host function that will be imported into the Web Assembly Instance 10 | void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) 11 | { 12 | print_str_called = true; 13 | const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); 14 | uint8_t *mem_bytes = wasmer_memory_data(memory); 15 | printf("%.*s", len, mem_bytes + ptr); 16 | } 17 | 18 | // Use the last_error API to retrieve error messages 19 | void print_wasmer_error() 20 | { 21 | int error_len = wasmer_last_error_length(); 22 | printf("Error len: `%d`\n", error_len); 23 | char *error_str = malloc(error_len); 24 | wasmer_last_error_message(error_str, error_len); 25 | printf("Error str: `%s`\n", error_str); 26 | } 27 | 28 | int main() 29 | { 30 | // Create a new func to hold the parameter and signature 31 | // of our `print_str` host function 32 | wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; 33 | wasmer_value_tag returns_sig[] = {}; 34 | wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); 35 | 36 | // Create module name for our imports 37 | // represented in bytes for UTF-8 compatability 38 | const char *module_name = "env"; 39 | wasmer_byte_array module_name_bytes = { .bytes = (const uint8_t *) module_name, 40 | .bytes_len = strlen(module_name) }; 41 | 42 | // Define a function import 43 | const char *import_name = "_print_str"; 44 | wasmer_byte_array import_name_bytes = { .bytes = (const uint8_t *) import_name, 45 | .bytes_len = strlen(import_name) }; 46 | wasmer_import_t func_import = { .module_name = module_name_bytes, 47 | .import_name = import_name_bytes, 48 | .tag = WASM_FUNCTION, 49 | .value.func = func }; 50 | 51 | // Define a memory import 52 | const char *import_memory_name = "memory"; 53 | wasmer_byte_array import_memory_name_bytes = { .bytes = (const uint8_t *) import_memory_name, 54 | .bytes_len = strlen(import_memory_name) }; 55 | wasmer_import_t memory_import = { .module_name = module_name_bytes, 56 | .import_name = import_memory_name_bytes, 57 | .tag = WASM_MEMORY }; 58 | wasmer_memory_t *memory = NULL; 59 | wasmer_limit_option_t max = { .has_some = true, 60 | .some = 256 }; 61 | wasmer_limits_t descriptor = { .min = 256, 62 | .max = max }; 63 | 64 | wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); 65 | if (memory_result != WASMER_OK) 66 | { 67 | print_wasmer_error(); 68 | } 69 | memory_import.value.memory = memory; 70 | 71 | // Define a global import 72 | const char *import_global_name = "__memory_base"; 73 | wasmer_byte_array import_global_name_bytes = { .bytes = (const uint8_t *) import_global_name, 74 | .bytes_len = strlen(import_global_name) }; 75 | wasmer_import_t global_import = { .module_name = module_name_bytes, 76 | .import_name = import_global_name_bytes, 77 | .tag = WASM_GLOBAL }; 78 | 79 | wasmer_value_t val = { .tag = WASM_I32, 80 | .value.I32 = 1024 }; 81 | wasmer_global_t *global = wasmer_global_new(val, false); 82 | global_import.value.global = global; 83 | 84 | // Define an array containing our imports 85 | wasmer_import_t imports[] = {func_import, global_import, memory_import}; 86 | 87 | // Read the wasm file bytes 88 | FILE *file = fopen("wasm-sample-app/target.wasm", "r"); 89 | fseek(file, 0, SEEK_END); 90 | long len = ftell(file); 91 | uint8_t *bytes = malloc(len); 92 | fseek(file, 0, SEEK_SET); 93 | fread(bytes, 1, len, file); 94 | fclose(file); 95 | 96 | // Creates a WebAssembly Instance from wasm bytes and imports 97 | wasmer_instance_t *instance = NULL; 98 | wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 3); 99 | printf("Compile result: %d\n", compile_result); 100 | 101 | if (compile_result != WASMER_OK) 102 | { 103 | print_wasmer_error(); 104 | } 105 | 106 | assert(compile_result == WASMER_OK); 107 | 108 | // Call the exported "hello_wasm" function of our instance 109 | wasmer_value_t params[] = {}; 110 | wasmer_value_t result_one; 111 | wasmer_value_t results[] = {result_one}; 112 | wasmer_result_t call_result = wasmer_instance_call(instance, "_hello_wasm", params, 0, results, 1); 113 | printf("\nCall result: %d\n", call_result); 114 | assert(call_result == WASMER_OK); 115 | assert(print_str_called); 116 | 117 | // Use *_destroy methods to cleanup as specified in the header documentation 118 | wasmer_import_func_destroy(func); 119 | wasmer_global_destroy(global); 120 | wasmer_memory_destroy(memory); 121 | wasmer_instance_destroy(instance); 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /wasmer-wasi-c-api-example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rust-build/src/wasmer-runtime-c-api/lib/runtime-c-api/wasmer.h" 3 | #include 4 | #include 5 | #include 6 | 7 | static int host_print_called = false; 8 | 9 | // Host function that will be imported into the Web Assembly Instance 10 | void host_print(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) 11 | { 12 | host_print_called = true; 13 | const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); 14 | uint8_t *mem_bytes = wasmer_memory_data(memory); 15 | printf("PRINTING FROM THE HOST: \"%.*s\"\n", len, mem_bytes + ptr); 16 | } 17 | 18 | // helper function to print byte array to stdout 19 | void print_byte_array(wasmer_byte_array *arr) { 20 | for (int i = 0; i < arr->bytes_len; ++i) { 21 | putchar(arr->bytes[i]); 22 | } 23 | } 24 | 25 | // Use the last_error API to retrieve error messages 26 | void print_wasmer_error() 27 | { 28 | int error_len = wasmer_last_error_length(); 29 | printf("Error len: `%d`\n", error_len); 30 | char *error_str = malloc(error_len); 31 | wasmer_last_error_message(error_str, error_len); 32 | printf("Error str: `%s`\n", error_str); 33 | } 34 | 35 | int main() 36 | { 37 | // Create a new func to hold the parameter and signature 38 | // of our `host_print` host function 39 | wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; 40 | wasmer_value_tag returns_sig[] = {}; 41 | wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) host_print, params_sig, 2, returns_sig, 0); 42 | 43 | // Create module name for our imports 44 | // represented in bytes for UTF-8 compatability 45 | const char *module_name = "env"; 46 | wasmer_byte_array module_name_bytes = { .bytes = (const uint8_t *) module_name, 47 | .bytes_len = strlen(module_name) }; 48 | 49 | // Define a function import 50 | const char *import_name = "host_print"; 51 | wasmer_byte_array import_name_bytes = { .bytes = (const uint8_t *) import_name, 52 | .bytes_len = strlen(import_name) }; 53 | wasmer_import_t func_import = { .module_name = module_name_bytes, 54 | .import_name = import_name_bytes, 55 | .tag = WASM_FUNCTION, 56 | .value.func = func }; 57 | 58 | // Define a memory import 59 | const char *import_memory_name = "memory"; 60 | wasmer_byte_array import_memory_name_bytes = { .bytes = (const uint8_t *) import_memory_name, 61 | .bytes_len = strlen(import_memory_name) }; 62 | wasmer_import_t memory_import = { .module_name = module_name_bytes, 63 | .import_name = import_memory_name_bytes, 64 | .tag = WASM_MEMORY }; 65 | wasmer_memory_t *memory = NULL; 66 | wasmer_limit_option_t max = { .has_some = true, 67 | .some = 256 }; 68 | wasmer_limits_t descriptor = { .min = 256, 69 | .max = max }; 70 | 71 | wasmer_result_t memory_result = wasmer_memory_new(&memory, descriptor); 72 | if (memory_result != WASMER_OK) 73 | { 74 | print_wasmer_error(); 75 | } 76 | memory_import.value.memory = memory; 77 | 78 | // Define a global import 79 | const char *import_global_name = "__memory_base"; 80 | wasmer_byte_array import_global_name_bytes = { .bytes = (const uint8_t *) import_global_name, 81 | .bytes_len = strlen(import_global_name) }; 82 | wasmer_import_t global_import = { .module_name = module_name_bytes, 83 | .import_name = import_global_name_bytes, 84 | .tag = WASM_GLOBAL }; 85 | 86 | wasmer_value_t val = { .tag = WASM_I32, 87 | .value.I32 = 1024 }; 88 | wasmer_global_t *global = wasmer_global_new(val, false); 89 | global_import.value.global = global; 90 | 91 | // Get the WASI base imports 92 | const char *arg1_val = "wasi-example-program-name"; 93 | const char *arg2_val = "--help"; 94 | wasmer_byte_array args[2] = { { .bytes = (const uint8_t *) arg1_val, 95 | .bytes_len = strlen(arg1_val) }, 96 | { .bytes = (const uint8_t *) arg2_val, 97 | .bytes_len = strlen(arg2_val) } }; 98 | const int num_args = (sizeof args) / (sizeof args[0]); 99 | 100 | const char *env1_val = "COLOR_OUTPUT=1"; 101 | wasmer_byte_array envs[1] = { { .bytes = (const uint8_t *) env1_val, 102 | .bytes_len = strlen(env1_val) } }; 103 | const int num_envs = (sizeof envs) / (sizeof envs[0]); 104 | 105 | wasmer_import_object_t *import_object = 106 | wasmer_wasi_generate_import_object(args, num_args, envs, num_envs, NULL, 0, NULL, 0); 107 | // Define an array containing our imports 108 | wasmer_import_t extra_imports[] = {func_import, global_import, memory_import}; 109 | 110 | // Add our custom imports to the WASI ImportObject 111 | wasmer_import_object_extend(import_object, extra_imports, 3); 112 | 113 | // Read the wasm file bytes 114 | FILE *file = fopen("wasm-wasi-sample-app/program.wasm", "r"); 115 | fseek(file, 0, SEEK_END); 116 | long len = ftell(file); 117 | uint8_t *bytes = malloc(len); 118 | fseek(file, 0, SEEK_SET); 119 | fread(bytes, 1, len, file); 120 | fclose(file); 121 | 122 | // iterate through and print out our imports 123 | wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object); 124 | 125 | puts("Functions in import object:"); 126 | while ( !wasmer_import_object_iter_at_end(func_iter) ) { 127 | wasmer_import_t import = { 0 }; 128 | wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import); 129 | assert(result == WASMER_OK); 130 | 131 | print_byte_array(&import.module_name); 132 | putchar(' '); 133 | print_byte_array(&import.import_name); 134 | putchar('\n'); 135 | 136 | assert(import.tag == WASM_FUNCTION); 137 | assert(import.value.func); 138 | wasmer_import_object_imports_destroy(&import, 1); 139 | } 140 | wasmer_import_object_iter_destroy(func_iter); 141 | func_iter = NULL; 142 | 143 | // Creates a WebAssembly Module from wasm bytes 144 | wasmer_module_t *module = NULL; 145 | wasmer_compile(&module, bytes, len); 146 | 147 | // Creates a WebAssembly Instance from a Module and ImportObject 148 | wasmer_instance_t *instance = NULL; 149 | wasmer_result_t compile_result = wasmer_module_import_instantiate(&instance, module, import_object); 150 | printf("Compile result: %d\n", compile_result); 151 | 152 | if (compile_result != WASMER_OK) 153 | { 154 | print_wasmer_error(); 155 | } 156 | 157 | assert(compile_result == WASMER_OK); 158 | 159 | // Call the pre-main function that invokes main, "_start". 160 | // This is necessary to use the WASI filesystem until WASI finishes support for 161 | // WASI Wasm modules as libraries 162 | wasmer_value_t params[] = { { .tag = WASM_I32, .value.I32 = 42 } }; 163 | wasmer_value_t result_one = { 0 }; 164 | wasmer_value_t results[] = {result_one}; 165 | wasmer_result_t call_result = wasmer_instance_call(instance, "_start", params, 0, results, 1); 166 | printf("Call result: %d (fn result %d %d)\n", call_result, results[0].tag, results[0].value.I32); 167 | assert(call_result == WASMER_OK); 168 | assert(host_print_called); 169 | 170 | // now call the extra function 171 | call_result = wasmer_instance_call(instance, "print_random_number_of_length", params, 1, results, 1); 172 | printf("Call result: %d (fn result %d %d)\n", call_result, results[0].tag, results[0].value.I32); 173 | assert(call_result == WASMER_OK); 174 | 175 | // Use *_destroy methods to cleanup as specified in the header documentation 176 | wasmer_import_func_destroy(func); 177 | wasmer_global_destroy(global); 178 | wasmer_memory_destroy(memory); 179 | wasmer_import_object_destroy(import_object); 180 | wasmer_instance_destroy(instance); 181 | 182 | return 0; 183 | } 184 | --------------------------------------------------------------------------------