Last active
August 17, 2022 06:19
-
-
Save f3lixding/45e208b5dcccd1a29a23e6a66402238f to your computer and use it in GitHub Desktop.
embed_wasm_in_c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
wit_bindgen_rust::export!("../guesttwo.wit"); | |
struct Guesttwo {} | |
impl guesttwo::Guesttwo for Guesttwo { | |
fn hellotwo(name: String) -> String { | |
let res = format!("Helloooooooo {}!", name); | |
res | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
hellotwo: func(name: string) -> string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <wasm.h> | |
#include <wasmtime/config.h> | |
#include <wasmtime.h> | |
static void exit_with_error(const char* message, wasmtime_error_t* error, wasm_trap_t* trap); | |
static void call_with_complex_param_type(wasmtime_linker_t* linker, wasmtime_context_t* context, char* arg0, int32_t length); | |
int main() { | |
int ret = 0; | |
// Set up our compilation context. Note that we could also work with a | |
// `wasm_config_t` here to configure what feature are enabled and various | |
// compilation settings. | |
printf("Initializing...\n"); | |
wasmtime_error_t* config_error = NULL; | |
wasm_config_t* config = wasm_config_new(); | |
config_error = wasmtime_config_cache_config_load(config, NULL); | |
assert(config_error == NULL); | |
wasm_engine_t* engine = wasm_engine_new_with_config(config); | |
assert(engine != NULL); | |
wasmtime_linker_t* linker = wasmtime_linker_new(engine); | |
wasmtime_error_t* error = wasmtime_linker_define_wasi(linker); | |
// With an engine we can create a *store* which is a long-lived group of wasm | |
// modules. Note that we allocate some custom data here to live in the store, | |
// but here we skip that and specify NULL. | |
wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL); | |
assert(store != NULL); | |
wasmtime_context_t* context = wasmtime_store_context(store); | |
// Read our input file, which in this case is a wasm text file. | |
const char* module_path = "/Volumes/workplace/wasm_poc/src/Embed-wasm-to-c/guest/target/wasm32-wasi/release/guest.wasm"; | |
const char* module_path_two = "/Users/dingfeli/workplace/wasm_poc/src/Embed-wasm-to-c/guest_two/target/wasm32-wasi/release/guest_two.wasm"; | |
FILE* file = fopen(module_path_two, "rb"); | |
assert(file != NULL); | |
fseek(file, 0L, SEEK_END); | |
size_t file_size = ftell(file); | |
fseek(file, 0L, SEEK_SET); | |
wasm_byte_vec_t wasm; | |
wasm_byte_vec_new_uninitialized(&wasm, file_size); | |
assert(fread(wasm.data, file_size, 1, file) == 1); | |
fclose(file); | |
// Now that we've got our binary webassembly we can compile our module. | |
printf("Compiling module...\n"); | |
wasmtime_module_t* module = NULL; | |
error = wasmtime_module_new(engine, (uint8_t*)wasm.data, wasm.size, &module); | |
wasm_byte_vec_delete(&wasm); | |
if (error != NULL) | |
exit_with_error("failed to compile module", error, NULL); | |
printf("Instantiating module...\n"); | |
wasm_trap_t* trap = NULL; | |
// wasi stuff | |
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); | |
error = wasmtime_context_set_wasi(context, wasi_config); | |
if (error != NULL) | |
exit_with_error("failed to set wasi to context", error, NULL); | |
// actually instantiating | |
error = wasmtime_linker_module(linker, context, "", 0, module); | |
if (error != NULL || trap != NULL) | |
exit_with_error("failed to instantiate", error, trap); | |
char* arg0 = "Felix"; | |
call_with_complex_param_type(linker, context, arg0, 5); | |
// Clean up after ourselves at this point | |
printf("All finished!\n"); | |
ret = 0; | |
// wasmtime_module_delete(module); | |
// wasmtime_store_delete(store); | |
// wasm_engine_delete(engine); | |
return ret; | |
} | |
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); | |
} | |
static void call_with_complex_param_type( | |
wasmtime_linker_t* linker, | |
wasmtime_context_t* context, | |
char* arg0, | |
int32_t length | |
) { | |
// obtain realloc and free memory functions | |
wasmtime_extern_t func_canonical_abi_realloc; | |
bool ok = wasmtime_linker_get(linker, context, "", 0, "canonical_abi_realloc", 21, &func_canonical_abi_realloc); | |
assert(ok); | |
wasmtime_extern_t func_canonical_abi_free; | |
ok = wasmtime_linker_get(linker, context, "", 0, "canonical_abi_free", 18, &func_canonical_abi_free); | |
assert(ok); | |
// allocate argument onto memory | |
// for value type: https://docs.wasmtime.dev/c-api/val_8h.html#a97038492e2cc547de6c95cf597453353 | |
// realloc takes four args: start_ptr, alloc_len, value_type, total_length (bytes?) | |
wasmtime_val_t realloc_arg_list[4] = { 0, 0, 0, 20 }; | |
wasmtime_val_t realloc_results[1]; | |
wasmtime_error_t* error; | |
wasm_trap_t* trap = NULL; | |
error = wasmtime_func_call(context, &func_canonical_abi_realloc.of.func, realloc_arg_list, 4, realloc_results, 1, &trap); | |
wasmtime_val_t realloc_result = realloc_results[0]; | |
if (error != NULL || trap != NULL) | |
exit_with_error("failed to allocate memory for arugments", error, trap); | |
// get memory from the instance, likely using a wasmtime api | |
wasmtime_extern_t item; | |
ok = wasmtime_linker_get(linker, context, "", 0, "memory", strlen("memory"), &item); | |
assert(ok && item.kind == WASM_EXTERN_MEMORY); | |
wasmtime_memory_t memory = item.of.memory; | |
// mutate memory | |
// need to do it like this: https://github.com/bytecodealliance/wasmtime/blob/d89c262657e98caf0195c07ac3fd72126e1f8ec3/examples/memory.c#L73 | |
char* name = "FelixFelix"; | |
uint8_t* ptr0 = wasmtime_memory_data(context, &memory); | |
printf("base ptr first returned in c as: %p\n", ptr0); | |
for (int i = 0; i < strlen(name); i++) { | |
ptr0[realloc_result.of.i32 + i] = name[i]; | |
printf("%c, %d\n", name[i], name[i]); | |
} | |
//prep func call arg list | |
wasmtime_val_t hellotwo_arg_list[2]; | |
hellotwo_arg_list[0].kind = WASMTIME_I32; | |
hellotwo_arg_list[0].of.i32 = realloc_result.of.i32; | |
hellotwo_arg_list[1].kind = WASMTIME_I32; | |
hellotwo_arg_list[1].of.i32 = strlen(name); | |
// calling func | |
wasmtime_extern_t hellotwo; | |
ok = wasmtime_linker_get(linker, context, "", 0, "hellotwo", strlen("hellotwo"), &hellotwo); | |
assert(ok); | |
wasmtime_val_t ret[1]; | |
error = wasmtime_func_call(context, &hellotwo.of.func, hellotwo_arg_list, 2, ret, 1, &trap); | |
if (error != NULL || trap != NULL) | |
exit_with_error("failed to invoke hellotwo", error, trap); | |
// decoding the return: | |
uint32_t ret_ptr0_loc = ret[0].of.i32; | |
uint32_t ret_ptr0 = ptr0[ret_ptr0_loc + 0]; | |
uint32_t ret_len = ptr0[ret_ptr0_loc + 4]; | |
typedef char placeholder_t; | |
placeholder_t ret_char[ret_len]; | |
printf("realloc_result: %d\n", realloc_result.of.i32); | |
printf("ret_ptr0_loc: %d\n", ret_ptr0_loc); | |
printf("ret_ptr0: %d\n", ret_ptr0); | |
for (int i = 0; i < ret_len; i++) { | |
ret_char[i] = (placeholder_t)ptr0[ret_ptr0 + i]; | |
} | |
// for (int i = 0; i < (int)ret_ptr0_loc * 2; i++) { | |
// printf("%d: %c: %d\n", i, ptr0[i], ptr0[i]); | |
// } | |
// for (int i = 1061072, j = 0; i < 1061072 + ret_len; i++, j++) { | |
// ret_char[j] = (char)ptr0[i]; | |
// } | |
printf("%s\n", ret_char); | |
// freeing the return area memory | |
wasmtime_val_t free_args[3]; | |
free_args[0].kind = WASMTIME_I32; | |
free_args[0].of.i32 = ret_ptr0; | |
free_args[1].kind = WASMTIME_I32; | |
free_args[1].of.i32 = ret_len; | |
free_args[2].kind = WASMTIME_I32; | |
free_args[2].of.i32 = 1; | |
error = wasmtime_func_call(context, &func_canonical_abi_free.of.func, free_args, 3, NULL, 0, &trap); | |
if (error != NULL || trap != NULL) | |
exit_with_error("failed to free memory", error, trap); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment