Skip to content

Instantly share code, notes, and snippets.

@f3lixding
Last active August 17, 2022 06:19
Show Gist options
  • Save f3lixding/45e208b5dcccd1a29a23e6a66402238f to your computer and use it in GitHub Desktop.
Save f3lixding/45e208b5dcccd1a29a23e6a66402238f to your computer and use it in GitHub Desktop.
embed_wasm_in_c
wit_bindgen_rust::export!("../guesttwo.wit");
struct Guesttwo {}
impl guesttwo::Guesttwo for Guesttwo {
fn hellotwo(name: String) -> String {
let res = format!("Helloooooooo {}!", name);
res
}
}
hellotwo: func(name: string) -> string
#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