From 3698f2279b43bfa33a364a70174b67a990ca5c35 Mon Sep 17 00:00:00 2001 From: "liang.he" Date: Mon, 13 Feb 2023 15:06:04 +0800 Subject: [PATCH] Improve wasm-c-api instantiation-time linking (#1902) Add APIs to help prepare the imports for the wasm-c-api `wasm_instance_new`: - wasm_importtype_is_linked - wasm_runtime_is_import_func_linked - wasm_runtime_is_import_global_linked - wasm_extern_new_empty For wasm-c-api, developer may use `wasm_module_imports` to get the import types info, check whether an import func/global is linked with the above API, and ignore the linking of an import func/global with `wasm_extern_new_empty`. Sample `wasm-c-api-import` is added and document is updated. --- core/iwasm/common/wasm_c_api.c | 439 +++++++++--------- core/iwasm/common/wasm_native.c | 9 +- core/iwasm/common/wasm_runtime_common.c | 22 + core/iwasm/common/wasm_runtime_common.h | 8 + core/iwasm/include/wasm_c_api.h | 4 + core/iwasm/include/wasm_export.h | 16 + core/iwasm/interpreter/wasm_loader.c | 9 +- .../lib-pthread/lib_pthread_wrapper.c | 41 ++ samples/wasm-c-api-imports/.gitignore | 2 + samples/wasm-c-api-imports/CMakeLists.txt | 169 +++++++ samples/wasm-c-api-imports/README.md | 174 +++++++ .../wasm-c-api-imports/host/CMakeLists.txt | 12 + samples/wasm-c-api-imports/host/example1.c | 204 ++++++++ .../wasm-c-api-imports/wasm/CMakeLists.txt | 47 ++ samples/wasm-c-api-imports/wasm/inc/.gitkeep | 0 samples/wasm-c-api-imports/wasm/send_recv.c | 231 +++++++++ 16 files changed, 1168 insertions(+), 219 deletions(-) create mode 100644 samples/wasm-c-api-imports/.gitignore create mode 100644 samples/wasm-c-api-imports/CMakeLists.txt create mode 100644 samples/wasm-c-api-imports/README.md create mode 100644 samples/wasm-c-api-imports/host/CMakeLists.txt create mode 100644 samples/wasm-c-api-imports/host/example1.c create mode 100644 samples/wasm-c-api-imports/wasm/CMakeLists.txt create mode 100644 samples/wasm-c-api-imports/wasm/inc/.gitkeep create mode 100644 samples/wasm-c-api-imports/wasm/send_recv.c diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c index 325f231d6..d3c8ea1ec 100644 --- a/core/iwasm/common/wasm_c_api.c +++ b/core/iwasm/common/wasm_c_api.c @@ -398,7 +398,7 @@ wasm_engine_new_internal(mem_alloc_type_t type, const MemAllocOption *opts) } /* global engine instance */ -static wasm_engine_t *singleton_engine = NULL; +static wasm_engine_t *singleton_engine; #ifdef os_thread_local_attribute /* categorize wasm_store_t as threads*/ static os_thread_local_attribute unsigned thread_local_stores_num = 0; @@ -1458,6 +1458,30 @@ wasm_importtype_type(const wasm_importtype_t *import_type) return import_type->extern_type; } +bool +wasm_importtype_is_linked(const wasm_importtype_t *import_type) +{ + if (!import_type) + return false; + + const wasm_name_t *module_name = wasm_importtype_module(import_type); + const wasm_name_t *field_name = wasm_importtype_name(import_type); + + switch (wasm_externtype_kind(wasm_importtype_type(import_type))) { + case WASM_EXTERN_FUNC: + return wasm_runtime_is_import_func_linked(module_name->data, + field_name->data); + case WASM_EXTERN_GLOBAL: + return wasm_runtime_is_import_global_linked(module_name->data, + field_name->data); + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + default: + break; + } + return false; +} + own wasm_exporttype_t * wasm_exporttype_new(own wasm_byte_vec_t *name, own wasm_externtype_t *extern_type) @@ -2537,12 +2561,12 @@ wasm_module_imports(const wasm_module_t *module, own wasm_importtype_vec_t *out) bh_assert(extern_type); - wasm_name_new_from_string(&module_name, module_name_rt); + wasm_name_new_from_string_nt(&module_name, module_name_rt); if (strlen(module_name_rt) && !module_name.data) { goto failed; } - wasm_name_new_from_string(&name, field_name_rt); + wasm_name_new_from_string_nt(&name, field_name_rt); if (strlen(field_name_rt) && !name.data) { goto failed; } @@ -2622,7 +2646,7 @@ wasm_module_exports(const wasm_module_t *module, wasm_exporttype_vec_t *out) } /* byte* -> wasm_byte_vec_t */ - wasm_name_new_from_string(&name, export->name); + wasm_name_new_from_string_nt(&name, export->name); if (strlen(export->name) && !name.data) { goto failed; } @@ -3008,6 +3032,20 @@ failed: return NULL; } +static wasm_func_t * +wasm_func_new_empty(wasm_store_t *store) +{ + wasm_func_t *func = NULL; + + if (!(func = malloc_internal(sizeof(wasm_func_t)))) + goto failed; + + func->store = store; + func->kind = WASM_EXTERN_FUNC; + + RETURN_OBJ(func, wasm_func_delete) +} + void wasm_func_delete(wasm_func_t *func) { @@ -3211,7 +3249,8 @@ wasm_func_call(const wasm_func_t *func, const wasm_val_vec_t *params, wasm_name_t message = { 0 }; wasm_trap_t *trap; - wasm_name_new_from_string(&message, "failed to call unlinked function"); + wasm_name_new_from_string_nt(&message, + "failed to call unlinked function"); trap = wasm_trap_new(func->store, &message); wasm_byte_vec_delete(&message); @@ -3371,6 +3410,25 @@ failed: return NULL; } +static wasm_global_t * +wasm_global_new_empty(wasm_store_t *store) +{ + wasm_global_t *global = NULL; + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) + goto failed; + + global->store = store; + global->kind = WASM_EXTERN_GLOBAL; + + return global; +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + /* almost same with wasm_global_new */ wasm_global_t * wasm_global_copy(const wasm_global_t *src) @@ -4288,6 +4346,11 @@ interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, imported_func_interp = module_interp->import_functions + func_idx_rt; bh_assert(imported_func_interp); + bh_assert(imported_func_interp->kind == IMPORT_KIND_FUNC); + + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; /* type comparison */ if (!wasm_functype_same_internal( @@ -4302,6 +4365,8 @@ interp_link_func(const wasm_instance_t *inst, const WASMModule *module_interp, imported_func_interp->u.function.func_ptr_linked = import->u.cb_env.cb; else imported_func_interp->u.function.func_ptr_linked = import->u.cb; + bh_assert(imported_func_interp->u.function.func_ptr_linked); + import->func_idx_rt = func_idx_rt; (void)inst; @@ -4320,12 +4385,19 @@ interp_link_global(const WASMModule *module_interp, uint16 global_idx_rt, imported_global_interp = module_interp->import_globals + global_idx_rt; bh_assert(imported_global_interp); + bh_assert(imported_global_interp->kind == IMPORT_KIND_GLOBAL); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + + /* type comparison */ if (!cmp_val_kind_with_val_type(wasm_valtype_kind(import->type->val_type), imported_global_interp->u.global.type)) return false; /* set init value */ + bh_assert(import->init); switch (wasm_valtype_kind(import->type->val_type)) { case WASM_I32: imported_global_interp->u.global.global_data_linked.i32 = @@ -4352,58 +4424,6 @@ interp_link_global(const WASMModule *module_interp, uint16 global_idx_rt, return true; } -static bool -interp_link(const wasm_instance_t *inst, const WASMModule *module_interp, - wasm_extern_t *imports[]) -{ - uint32 i = 0; - uint32 import_func_i = 0; - uint32 import_global_i = 0; - - bh_assert(inst && module_interp && imports); - - for (i = 0; i < module_interp->import_count; ++i) { - wasm_extern_t *import = imports[i]; - WASMImport *import_rt = module_interp->imports + i; - - switch (import_rt->kind) { - case IMPORT_KIND_FUNC: - { - if (!interp_link_func(inst, module_interp, import_func_i, - wasm_extern_as_func(import))) { - LOG_WARNING("link #%d function failed", import_func_i); - goto failed; - } - import_func_i++; - break; - } - case IMPORT_KIND_GLOBAL: - { - if (!interp_link_global(module_interp, import_global_i, - wasm_extern_as_global(import))) { - LOG_WARNING("link #%d global failed", import_global_i); - goto failed; - } - import_global_i++; - break; - } - case IMPORT_KIND_MEMORY: - case IMPORT_KIND_TABLE: - default: - ASSERT_NOT_IMPLEMENTED(); - LOG_WARNING("%s meets unsupported kind: %d", __FUNCTION__, - import_rt->kind); - goto failed; - } - } - - return true; - -failed: - LOG_DEBUG("%s failed", __FUNCTION__); - return false; -} - static bool interp_process_export(wasm_store_t *store, const WASMModuleInstance *inst_interp, @@ -4503,6 +4523,10 @@ aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, import_aot_func = module_aot->import_funcs + import_func_idx_rt; bh_assert(import_aot_func); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + /* type comparison */ if (!wasm_functype_same_internal(import->type, import_aot_func->func_type)) return false; @@ -4515,6 +4539,8 @@ aot_link_func(const wasm_instance_t *inst, const AOTModule *module_aot, import_aot_func->func_ptr_linked = import->u.cb_env.cb; else import_aot_func->func_ptr_linked = import->u.cb; + bh_assert(import_aot_func->func_ptr_linked); + import->func_idx_rt = import_func_idx_rt; return true; @@ -4532,6 +4558,10 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, import_aot_global = module_aot->import_globals + global_idx_rt; bh_assert(import_aot_global); + /* it is a placeholder and let's skip it*/ + if (!import->type) + return true; + val_type = wasm_globaltype_content(import->type); bh_assert(val_type); @@ -4539,6 +4569,7 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, import_aot_global->type)) return false; + bh_assert(import->init); switch (wasm_valtype_kind(val_type)) { case WASM_I32: import_aot_global->global_data_linked.i32 = import->init->of.i32; @@ -4559,62 +4590,6 @@ aot_link_global(const AOTModule *module_aot, uint16 global_idx_rt, import->global_idx_rt = global_idx_rt; import_aot_global->is_linked = true; return true; - -failed: - LOG_DEBUG("%s failed", __FUNCTION__); - return false; -} - -static bool -aot_link(const wasm_instance_t *inst, const AOTModule *module_aot, - wasm_extern_t *imports[]) -{ - uint32 i = 0; - uint32 import_func_i = 0; - uint32 import_global_i = 0; - wasm_extern_t *import = NULL; - wasm_func_t *func = NULL; - wasm_global_t *global = NULL; - - bh_assert(inst && module_aot && imports); - - while (import_func_i < module_aot->import_func_count - || import_global_i < module_aot->import_global_count) { - import = imports[i++]; - - bh_assert(import); - - switch (wasm_extern_kind(import)) { - case WASM_EXTERN_FUNC: - bh_assert(import_func_i < module_aot->import_func_count); - func = wasm_extern_as_func((wasm_extern_t *)import); - if (!aot_link_func(inst, module_aot, import_func_i, func)) { - LOG_WARNING("link #%d function failed", import_func_i); - goto failed; - } - import_func_i++; - - break; - case WASM_EXTERN_GLOBAL: - bh_assert(import_global_i < module_aot->import_global_count); - global = wasm_extern_as_global((wasm_extern_t *)import); - if (!aot_link_global(module_aot, import_global_i, global)) { - LOG_WARNING("link #%d global failed", import_global_i); - goto failed; - } - import_global_i++; - - break; - case WASM_EXTERN_MEMORY: - case WASM_EXTERN_TABLE: - default: - ASSERT_NOT_IMPLEMENTED(); - goto failed; - } - } - - return true; - failed: LOG_DEBUG("%s failed", __FUNCTION__); return false; @@ -4695,7 +4670,7 @@ aot_process_export(wasm_store_t *store, const AOTModuleInstance *inst_aot, goto failed; } - wasm_name_new_from_string(external->name, export->name); + wasm_name_new_from_string_nt(external->name, export->name); if (strlen(export->name) && !external->name->data) { goto failed; } @@ -4713,6 +4688,95 @@ failed: } #endif /* WASM_ENABLE_AOT */ +static bool +do_link(const wasm_instance_t *inst, const wasm_module_t *module, + const wasm_extern_vec_t *imports) +{ + uint32 i, import_func_i, import_global_i; + + bh_assert(inst && module); + + /* we have run a module_type check before. */ + + for (i = 0, import_func_i = 0, import_global_i = 0; i < imports->num_elems; + i++) { + wasm_extern_t *import = imports->data[i]; + + if (!import) { + LOG_ERROR("imports[%d] is NULL and it is fatal\n", i); + goto failed; + } + + switch (wasm_extern_kind(import)) { + case WASM_EXTERN_FUNC: + { + bool ret = false; +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + ret = interp_link_func(inst, MODULE_INTERP(module), + import_func_i, + wasm_extern_as_func(import)); + } +#endif +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + ret = aot_link_func(inst, MODULE_AOT(module), import_func_i, + wasm_extern_as_func(import)); + } +#endif + if (!ret) { + LOG_WARNING("link function #%d failed", import_func_i); + goto failed; + } + + import_func_i++; + break; + } + case WASM_EXTERN_GLOBAL: + { + bool ret = false; +#if WASM_ENABLE_INTERP != 0 + if ((*module)->module_type == Wasm_Module_Bytecode) { + ret = interp_link_global(MODULE_INTERP(module), + import_global_i, + wasm_extern_as_global(import)); + } +#endif +#if WASM_ENABLE_AOT != 0 + if ((*module)->module_type == Wasm_Module_AoT) { + ret = aot_link_global(MODULE_AOT(module), import_global_i, + wasm_extern_as_global(import)); + } +#endif + if (!ret) { + LOG_WARNING("link global #%d failed", import_global_i); + goto failed; + } + + import_global_i++; + break; + } + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + { + LOG_WARNING("doesn't support import memories and tables for " + "now, ignore them"); + break; + } + default: + { + UNREACHABLE(); + break; + } + } + } + + return true; +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return false; +} + wasm_instance_t * wasm_instance_new(wasm_store_t *store, const wasm_module_t *module, const wasm_extern_vec_t *imports, own wasm_trap_t **trap) @@ -4721,57 +4785,6 @@ wasm_instance_new(wasm_store_t *store, const wasm_module_t *module, KILOBYTE(32), KILOBYTE(32)); } -static bool -compare_imports(const wasm_module_t *module, const wasm_extern_vec_t *imports) -{ - unsigned import_func_count = 0; - unsigned import_global_count = 0; - unsigned import_memory_count = 0; - unsigned import_table_count = 0; - unsigned i = 0; - - for (i = 0; imports && i < imports->num_elems; i++) { - wasm_extern_t *import = imports->data[i]; - switch (wasm_extern_kind(import)) { - case WASM_EXTERN_FUNC: - import_func_count++; - break; - case WASM_EXTERN_GLOBAL: - import_global_count++; - break; - case WASM_EXTERN_MEMORY: - import_memory_count++; - break; - case WASM_EXTERN_TABLE: - import_table_count++; - break; - default: - UNREACHABLE(); - return false; - } - } - -#if WASM_ENABLE_INTERP != 0 - if ((*module)->module_type == Wasm_Module_Bytecode) - return import_func_count == MODULE_INTERP(module)->import_function_count - && import_global_count - == MODULE_INTERP(module)->import_global_count - && import_memory_count - == MODULE_INTERP(module)->import_memory_count - && import_table_count - == MODULE_INTERP(module)->import_table_count; -#endif -#if WASM_ENABLE_AOT != 0 - if ((*module)->module_type == Wasm_Module_AoT) - return import_func_count == MODULE_AOT(module)->import_func_count - && import_global_count == MODULE_AOT(module)->import_global_count - && import_memory_count == MODULE_AOT(module)->import_memory_count - && import_table_count == MODULE_AOT(module)->import_table_count; -#endif - - return false; -} - wasm_instance_t * wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, const wasm_extern_vec_t *imports, @@ -4781,7 +4794,6 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, char sub_error_buf[128] = { 0 }; char error_buf[256] = { 0 }; wasm_instance_t *instance = NULL; - WASMModuleInstance *inst_rt; CApiFuncImport *func_import = NULL, **p_func_imports = NULL; uint32 i = 0, import_func_count = 0; uint64 total_size; @@ -4792,11 +4804,9 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, if (!module) return NULL; - if (!compare_imports(module, imports)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to match imports"); - goto failed; - } + /* + * will do the check at the end of wasm_runtime_instantiate + */ WASM_C_DUMP_PROC_MEM(); @@ -4807,43 +4817,17 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, goto failed; } - /* link module and imports */ - if (imports && imports->num_elems) { - bool link = false; -#if WASM_ENABLE_INTERP != 0 - if ((*module)->module_type == Wasm_Module_Bytecode) { - if (!interp_link(instance, MODULE_INTERP(module), - (wasm_extern_t **)imports->data)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to validate imports"); - goto failed; - } - link = true; - } -#endif - -#if WASM_ENABLE_AOT != 0 - if ((*module)->module_type == Wasm_Module_AoT) { - if (!aot_link(instance, MODULE_AOT(module), - (wasm_extern_t **)imports->data)) { - snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to validate imports"); - goto failed; - } - link = true; - } -#endif - - /* - * a wrong combination of module filetype and compilation flags - * also leads to below branch - */ - if (!link) { + /* executes the instantiate-time linking if provided */ + if (imports) { + if (!do_link(instance, module, imports)) { snprintf(sub_error_buf, sizeof(sub_error_buf), - "Failed to verify import count"); + "Failed to validate imports"); goto failed; } } + /* + * will do the linking result check at the end of wasm_runtime_instantiate + */ instance->inst_comm_rt = wasm_runtime_instantiate( *module, stack_size, heap_size, sub_error_buf, sizeof(sub_error_buf)); @@ -4858,18 +4842,22 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, } /* create the c-api func import list */ - inst_rt = (WASMModuleInstance *)instance->inst_comm_rt; #if WASM_ENABLE_INTERP != 0 if (instance->inst_comm_rt->module_type == Wasm_Module_Bytecode) { - p_func_imports = &inst_rt->e->c_api_func_imports; - import_func_count = inst_rt->module->import_function_count; + WASMModuleInstanceExtra *e = + ((WASMModuleInstance *)instance->inst_comm_rt)->e; + p_func_imports = &(e->c_api_func_imports); + import_func_count = MODULE_INTERP(module)->import_function_count; } #endif #if WASM_ENABLE_AOT != 0 if (instance->inst_comm_rt->module_type == Wasm_Module_AoT) { - p_func_imports = - &((AOTModuleInstanceExtra *)inst_rt->e)->c_api_func_imports; - import_func_count = ((AOTModule *)inst_rt->module)->import_func_count; + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *) + instance->inst_comm_rt) + ->e; + p_func_imports = &(e->c_api_func_imports); + import_func_count = MODULE_AOT(module)->import_func_count; } #endif bh_assert(p_func_imports); @@ -4882,16 +4870,21 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, goto failed; } - /* fill in c-api func import list */ + /* fill in module_inst->e->c_api_func_imports */ for (i = 0; imports && i < imports->num_elems; i++) { - wasm_func_t *func_host; - wasm_extern_t *in; + wasm_func_t *func_host = NULL; + wasm_extern_t *in = imports->data[i]; + bh_assert(in); - in = imports->data[i]; if (wasm_extern_kind(in) != WASM_EXTERN_FUNC) continue; func_host = wasm_extern_as_func(in); + /* it is a placeholder and let's skip it*/ + if (!func_host->type) { + func_import++; + continue; + } func_import->with_env_arg = func_host->with_env; if (func_host->with_env) { @@ -4902,6 +4895,7 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, func_import->func_ptr_linked = func_host->u.cb; func_import->env_arg = NULL; } + bh_assert(func_import->func_ptr_linked); func_import++; } @@ -4909,6 +4903,8 @@ wasm_instance_new_with_args(wasm_store_t *store, const wasm_module_t *module, /* fill with inst */ for (i = 0; imports && imports->data && i < imports->num_elems; ++i) { wasm_extern_t *import = imports->data[i]; + bh_assert(import); + switch (import->kind) { case WASM_EXTERN_FUNC: wasm_extern_as_func(import)->inst_comm_rt = @@ -5004,7 +5000,7 @@ failed: sub_error_buf); if (trap != NULL) { wasm_message_t message = { 0 }; - wasm_name_new_from_string(&message, error_buf); + wasm_name_new_from_string_nt(&message, error_buf); *trap = wasm_trap_new(store, &message); wasm_byte_vec_delete(&message); } @@ -5204,3 +5200,16 @@ BASIC_FOUR_LIST(WASM_EXTERN_AS_OTHER_CONST) BASIC_FOUR_LIST(WASM_OTHER_AS_EXTERN_CONST) #undef WASM_OTHER_AS_EXTERN_CONST + +wasm_extern_t * +wasm_extern_new_empty(wasm_store_t *store, wasm_externkind_t extern_kind) +{ + if (extern_kind == WASM_EXTERN_FUNC) + return wasm_func_as_extern(wasm_func_new_empty(store)); + + if (extern_kind == WASM_EXTERN_GLOBAL) + return wasm_global_as_extern(wasm_global_new_empty(store)); + + LOG_ERROR("Don't support linking table and memory for now"); + return NULL; +} diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 0fa596277..8fe014ea8 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -239,6 +239,10 @@ lookup_symbol(NativeSymbol *native_symbols, uint32 n_native_symbols, return NULL; } +/** + * allow func_type and all outputs, like p_signature, p_attachment and + * p_call_conv_raw to be NULL + */ void * wasm_native_resolve_symbol(const char *module_name, const char *field_name, const WASMType *func_type, const char **p_signature, @@ -264,10 +268,13 @@ wasm_native_resolve_symbol(const char *module_name, const char *field_name, node = node_next; } + if (!p_signature || !p_attachment || !p_call_conv_raw) + return func_ptr; + if (func_ptr) { if (signature && signature[0] != '\0') { /* signature is not empty, check its format */ - if (!check_symbol_signature(func_type, signature)) { + if (!func_type || !check_symbol_signature(func_type, signature)) { #if WASM_ENABLE_WAMR_COMPILER == 0 /* Output warning except running aot compiler */ LOG_WARNING("failed to check signature '%s' and resolve " diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 8662af1ce..c820de7ba 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -7,6 +7,7 @@ #include "bh_common.h" #include "bh_assert.h" #include "bh_log.h" +#include "wasm_native.h" #include "wasm_runtime_common.h" #include "wasm_memory.h" #if WASM_ENABLE_INTERP != 0 @@ -5331,3 +5332,24 @@ wasm_runtime_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch) *minor = WAMR_VERSION_MINOR; *patch = WAMR_VERSION_PATCH; } + +bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name) +{ + return wasm_native_resolve_symbol(module_name, func_name, NULL, NULL, NULL, + NULL); +} + +bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name) +{ +#if WASM_ENABLE_LIBC_BUILTIN != 0 + WASMGlobalImport global = { 0 }; + return wasm_native_lookup_libc_builtin_global(module_name, global_name, + &global); +#else + return false; +#endif +} diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index a70c9fb68..e01f91ab1 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -993,6 +993,14 @@ void wasm_runtime_destroy_custom_sections(WASMCustomSection *section_list); #endif +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name); + +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name); + #ifdef __cplusplus } #endif diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h index 85fcd1868..324a43bd5 100644 --- a/core/iwasm/include/wasm_c_api.h +++ b/core/iwasm/include/wasm_c_api.h @@ -354,6 +354,7 @@ WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); +WASM_API_EXTERN bool wasm_importtype_is_linked(const wasm_importtype_t*); // Export Types @@ -797,6 +798,9 @@ static inline void* wasm_val_ptr(const wasm_val_t* val) { #define KILOBYTE(n) ((n) * 1024) +// Create placeholders filled in `wasm_externvec_t* imports` for `wasm_instance_new()` +WASM_API_EXTERN wasm_extern_t *wasm_extern_new_empty(wasm_store_t *, wasm_externkind_t); + /////////////////////////////////////////////////////////////////////////////// #undef own diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 161a4905a..3bb5f873a 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -1324,6 +1324,22 @@ wasm_runtime_get_custom_section(wasm_module_t const module_comm, */ WASM_RUNTIME_API_EXTERN void wasm_runtime_get_version(uint32_t *major, uint32_t *minor, uint32_t *patch); + +/** + * Check whether an import func `(import (func ...))` is linked or not + * with runtime registered natvie functions + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_func_linked(const char *module_name, + const char *func_name); + +/** + * Check whether an import global `(import (global ...))` is linked or not + * with runtime registered natvie globals + */ +WASM_RUNTIME_API_EXTERN bool +wasm_runtime_is_import_global_linked(const char *module_name, + const char *global_name); /* clang-format on */ #ifdef __cplusplus diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 4c6c6aaa9..5a9fcbfb4 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -1399,6 +1399,7 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, WASMModule *sub_module = NULL; WASMGlobal *linked_global = NULL; #endif + bool ret = false; CHECK_BUF(p, p_end, 2); declare_type = read_uint8(p); @@ -1411,15 +1412,16 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, } #if WASM_ENABLE_LIBC_BUILTIN != 0 - global->is_linked = wasm_native_lookup_libc_builtin_global( - sub_module_name, global_name, global); - if (global->is_linked) { + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, global_name, + global); + if (ret) { if (global->type != declare_type || global->is_mutable != declare_mutable) { set_error_buf(error_buf, error_buf_size, "incompatible import type"); return false; } + global->is_linked = true; } #endif #if WASM_ENABLE_MULTI_MODULE != 0 @@ -1449,6 +1451,7 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end, global->is_mutable = (declare_mutable == 1); (void)parent_module; + (void)ret; return true; fail: return false; diff --git a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c index 3bf3269b8..4f8cc0593 100644 --- a/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c +++ b/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -570,6 +570,7 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, #if WASM_ENABLE_LIBC_WASI != 0 WASIContext *wasi_ctx; #endif + CApiFuncImport **new_c_api_func_imports = NULL; bh_assert(module); bh_assert(module_inst); @@ -602,6 +603,46 @@ pthread_create_wrapper(wasm_exec_env_t exec_env, wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx); #endif + /* workaround about passing instantiate-linking information */ + { + CApiFuncImport *c_api_func_imports; + uint32 import_func_count = 0; + uint32 size_in_bytes = 0; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + new_c_api_func_imports = &( + ((WASMModuleInstance *)new_module_inst)->e->c_api_func_imports); + c_api_func_imports = + ((WASMModuleInstance *)module_inst)->e->c_api_func_imports; + import_func_count = ((WASMModule *)module)->import_function_count; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstanceExtra *e = + (AOTModuleInstanceExtra *)((AOTModuleInstance *)new_module_inst) + ->e; + new_c_api_func_imports = &(e->c_api_func_imports); + + e = (AOTModuleInstanceExtra *)((AOTModuleInstance *)module_inst)->e; + c_api_func_imports = e->c_api_func_imports; + + import_func_count = ((AOTModule *)module)->import_func_count; + } +#endif + + if (import_func_count != 0 && c_api_func_imports) { + size_in_bytes = sizeof(CApiFuncImport *) * import_func_count; + *new_c_api_func_imports = wasm_runtime_malloc(size_in_bytes); + if (!(*new_c_api_func_imports)) + goto fail; + + bh_memcpy_s(*new_c_api_func_imports, size_in_bytes, + c_api_func_imports, size_in_bytes); + } + } + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) goto fail; diff --git a/samples/wasm-c-api-imports/.gitignore b/samples/wasm-c-api-imports/.gitignore new file mode 100644 index 000000000..ab998a6eb --- /dev/null +++ b/samples/wasm-c-api-imports/.gitignore @@ -0,0 +1,2 @@ +/wasm/inc/** +!/wasm/inc/.* diff --git a/samples/wasm-c-api-imports/CMakeLists.txt b/samples/wasm-c-api-imports/CMakeLists.txt new file mode 100644 index 000000000..7d65c9b24 --- /dev/null +++ b/samples/wasm-c-api-imports/CMakeLists.txt @@ -0,0 +1,169 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(how-to-deal-with-import) + +include(CMakePrintHelpers) +include(CTest) +include(ExternalProject) +include(FetchContent) + +# +# dependencies +# +set(WAMR_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../) +# wasm required headers +execute_process( + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${WARM_ROOT}/${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h + ${CMAKE_CURRENT_LIST_DIR}/wasm/inc +) + +# vmlib +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 4) + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + else () + message(SEND_ERROR "Unsupported build target platform!") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_INTERP 0) +set(WAMR_BUILD_JIT 0) + +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIB_PTHREAD 1) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_SIMD 0) + +# compiling and linking flags +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +target_link_libraries(vmlib INTERFACE dl m pthread) +if(WAMR_BUILD_AOT EQUAL 1) + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_AOT=1) +else() + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_AOT=0) +endif() + +if(WAMR_BUILD_INTERP EQUAL 1) + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_INTERP=1) +else() + target_compile_definitions(vmlib INTERFACE -DWASM_ENABLE_INTERP=0) +endif() + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + # ASAN + UBSAN + target_compile_options(vmlib INTERFACE -fsanitize=address,undefined) + target_link_options(vmlib INTERFACE -fsanitize=address,undefined) +endif() + +# # MSAN +# target_compile_options(vmlib INTERFACE -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer) +# target_link_options(vmlib INTERFACE -fsanitize=memory) + +# wamrc +if(WAMR_BUILD_AOT EQUAL 1 AND WAMR_BUILD_INTERP EQUAL 0) + ExternalProject_Add(wamrc + PREFIX wamrc-build + SOURCE_DIR ${WAMR_ROOT}/wamr-compiler + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${WAMR_ROOT}/wamr-compiler -B build + BUILD_COMMAND ${CMAKE_COMMAND} --build build --target wamrc + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_if_different build/wamrc ${CMAKE_CURRENT_BINARY_DIR}/wamrc + ) +endif() + +# +# host +add_subdirectory(host) +add_custom_target( + install_host ALL + COMMAND ${CMAKE_COMMAND} -E copy_if_different ./host/example1 . + DEPENDS example1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +# TODO: replace it with a find_package() +set(WASI_SDK_DIR /opt/wasi-sdk-19.0/) +set(WASI_TOOLCHAIN_FILE ${WASI_SDK_DIR}/share/cmake/wasi-sdk.cmake) +set(WASI_SYS_ROOT ${WASI_SDK_DIR}/share/wasi-sysroot) + +# +# wasm +if(WAMR_BUILD_AOT EQUAL 1 AND WAMR_BUILD_INTERP EQUAL 0) + ExternalProject_Add(wasm + PREFIX wasm-build + DEPENDS wamrc + BUILD_ALWAYS TRUE + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/wasm + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_LIST_DIR}/wasm -B build + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + -DWASM_TO_AOT=ON + -DWAMRC_PATH=${CMAKE_CURRENT_BINARY_DIR}/wamrc + -DSOCKET_WASI_CMAKE=${WAMR_ROOT}/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + ExternalProject_Add(wasm + PREFIX wasm-build + BUILD_ALWAYS TRUE + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/wasm + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_LIST_DIR}/wasm -B build + -DWASI_SDK_PREFIX=${WASI_SDK_DIR} + -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE} + -DCMAKE_SYSROOT=${WASI_SYS_ROOT} + -DSOCKET_WASI_CMAKE=${WAMR_ROOT}/core/iwasm/libraries/lib-socket/lib_socket_wasi.cmake + BUILD_COMMAND ${CMAKE_COMMAND} --build build + INSTALL_COMMAND ${CMAKE_COMMAND} --install build --prefix ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +# +# Test +# +add_test( + NAME run_example1 + COMMAND ./example1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/samples/wasm-c-api-imports/README.md b/samples/wasm-c-api-imports/README.md new file mode 100644 index 000000000..9b61a6e74 --- /dev/null +++ b/samples/wasm-c-api-imports/README.md @@ -0,0 +1,174 @@ +# How to create `imports` for wasm_instance_new() properly + +It's always been asked how to create `wasm_extern_vec_t *imports` for +`wasm_instance_new()`? + +```c +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t *imports, + own wasm_trap_t** trap +); +``` + +`wasm_extern_vec_t *imports` is required to match the requirement of _the +import section_ of a .wasm. + +```bash +$ /opt/wabt-1.0.31/bin/wasm-objdump -j Import -x .wasm + +Section Details: + +Import[27]: + - func[0] sig=2 <- env.pthread_mutex_lock + - func[1] sig=2 <- env.pthread_mutex_unlock + - func[2] sig=2 <- env.pthread_cond_signal + - func[3] sig=3 <- env.log + ... + - func[11] sig=4 <__imported_wasi_snapshot_preview1_sock_bind> <- wasi_snapshot_preview1.sock_bind + - func[12] sig=4 <__imported_wasi_snapshot_preview1_sock_connect> <- wasi_snapshot_preview1.sock_connect + - func[13] sig=4 <__imported_wasi_snapshot_preview1_sock_listen> <- wasi_snapshot_preview1.sock_listen + - func[14] sig=5 <__imported_wasi_snapshot_preview1_sock_open> <- wasi_snapshot_preview1.sock_open + - func[15] sig=4 <__imported_wasi_snapshot_preview1_sock_addr_remote> <- wasi_snapshot_preview1.sock_addr_remote + - func[16] sig=4 <__imported_wasi_snapshot_preview1_args_get> <- wasi_snapshot_preview1.args_get + - func[17] sig=4 <__imported_wasi_snapshot_preview1_args_sizes_get> <- wasi_snapshot_preview1.args_sizes_get + ... +``` + +Developers should fill in _imports_ with enough host functions and make sure +there are no linking problems during instantiation. + +```bash +TODO: linking warnings +``` + +## A natural way + +One natural answer is "to create a list which matches every item in _the import +section_" of the .wasm. Since developers can see the section details of +a .wasm by tools like _wasm-objdump_, the answer is doable. Most of the time, +if they also prepare Wasm modules, developers have full control over import +requirements, and they only need to take a look at the order of _the import +section_. + +Yes, _the order_. A proper `wasm_extern_vec_t *imports` includes two things: + +1. how many `wasm_extern_t` +2. and order of those + +Because there is no "name information" in a `wasm_extern_t`. The only way is let +`wasm_instance_new()` to tell which item in _the import section_ of a .wasm +should match any item in `wasm_extern_vec_t *imports` is based on **_index_**. + +The algorithm is quite straightforward. The first one of _the import section_ matches +`wasm_extern_vec_t *imports->data[0] `. The second one matches `wasm_extern_vec_t *imports->data[1]`. +And so on. + +So the order of `wasm_extern_vec_t *imports` becomes quite a burden. It requires +developers always checking _the import section_ visually. + +Until here, the natural way is still workable although involving some handy work. +Right? + +## A blocker + +Sorry, the situation changes a lot when driving wasm32-wasi Wasm modules with +wasm-c-api. + +As you know, WASI provides _a set of crossing-platform standard libraries_ for +Wasm modules, and leaves some _interfaces_ for native platform-dependent supports. +Those _interfaces_ are those import items with the module name `wasi_snapshot_preview1` +in a Wasm module. + +It seems not economical to let developers provide their version of host +implementations of the `wasi_snapshot_preview1.XXX` functions. All those support +should be packed into a common library and shared in different Wasm modules. +Like a [cargo WASI](https://github.com/bytecodealliance/cargo-wasi). + +WAMR chooses to integrate the WASI support library in the runtime to reduce +developers' compilation work. It brings developers a new thing of a proper +`wasm_extern_vec_t *imports` that developers should avoid overwriting those items +of _the import section_ of a Wasm module that will be provided by the runtime. It +also not economical to code for those functions. + +Using module names as a filter seems to be a simple way. But some private +additional c/c++ libraries are supported in WAMR. Those supporting will bring +more import items that don't use `wasi_snapshot_preview1` as module names but are still +covered by the WASM runtime. Like `env.pthread_`. Plus, [the native lib registeration](https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md) +provides another possible way to fill in the requirement of _the import section_. + +Let's take summarize. A proper `wasm_extern_vec_t *imports` should include: + +1. provides all necessary host implementations for items in _the import section_ +2. should not override runtime provided implementation or covered by native + registrations. functinal or econmical. +3. keep them in a right order + +## A recommendation + +The recommendation is: + +- use `wasm_module_imports()` to build the order +- use `wasm_importtype_is_linked()` to avoid overwriting + +[wasm-c-api-imports](.) is a simple showcase of how to do that. + +First, let's take a look at the Wasm module. [send_recv](./wasm/send_recv.c) +uses both standard WASI and WAMR_BUILD_LIB_PTHREAD supporting. Plus a private +native function `host_log`. + +So, `wasm_extern_vec_t *imports` should only include the host implementation of +`host_log` and avoid WASI related(`wasm-c-api-imports.XXX`) and pthread related(`env.pthread_XXX`). + +[Here is how to do](./host/example1.c): + +- get import types with `wasm_module_imports(0)`. it contains name information + +```c + wasm_importtype_vec_t importtypes = { 0 }; + wasm_module_imports(module, &importtypes); +``` + +- traversal import types. The final `wasm_importvec_t *imports` should have the + same order with `wasm_importtype_vec_t` + +```c + for (unsigned i = 0; i < importtypes.num_elems; i++) +``` + +- use `wasm_importtype_is_linked()` to avoid those covered by the runtime and + registered natives. A little tip is use "wasm_extern_new_empty()" to create + a placeholder. + +```c + /* use wasm_extern_new_empty() to create a placeholder */ + if (wasm_importtype_is_linked(importtype)) { + externs[i] = wasm_extern_new_empty( + store, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } +``` + +- use `wasm_importtype_module()` to get the module name, use `wasm_importtype_name()` + to get the field name. + +```c + const wasm_name_t *module_name = + wasm_importtype_module(importtypes.data[i]); + const wasm_name_t *field_name = + wasm_importtype_name(importtypes.data[i]); +``` + +- fill in `wasm_externvec_t *imports` dynamically and programmatically. + +```c + if (strncmp(module_name->data, "env", strlen("env")) == 0 + && strncmp(field_name->data, "log", strlen("log")) == 0) { + wasm_functype_t *log_type = wasm_functype_new_2_0( + wasm_valtype_new_i64(), wasm_valtype_new_i32()); + wasm_func_t *log_func = wasm_func_new(store, log_type, host_logs); + wasm_functype_delete(log_type); + + externs[i] = wasm_func_as_extern(log_func); + } + } +``` diff --git a/samples/wasm-c-api-imports/host/CMakeLists.txt b/samples/wasm-c-api-imports/host/CMakeLists.txt new file mode 100644 index 000000000..e2636f09e --- /dev/null +++ b/samples/wasm-c-api-imports/host/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.14) +project(host) + +set(CMAKE_BUILD_TYPE Debug) + +# +# host +add_executable(example1 ./example1.c) +target_link_libraries(example1 vmlib) diff --git a/samples/wasm-c-api-imports/host/example1.c b/samples/wasm-c-api-imports/host/example1.c new file mode 100644 index 000000000..ccf574d79 --- /dev/null +++ b/samples/wasm-c-api-imports/host/example1.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "wasm_c_api.h" +#include "wasm_export.h" + +static wasm_trap_t * +host_logs(const wasm_val_vec_t *args, wasm_val_vec_t *results) +{ + return NULL; +} + +static bool +build_imports(wasm_store_t *store, const wasm_module_t *module, + wasm_extern_vec_t *out) +{ + wasm_importtype_vec_t importtypes = { 0 }; + wasm_module_imports(module, &importtypes); + + wasm_extern_t *externs[32] = { 0 }; + + for (unsigned i = 0; i < importtypes.num_elems; i++) { + wasm_importtype_t *importtype = importtypes.data[i]; + + /* use wasm_extern_new_empty() to create a placeholder */ + if (wasm_importtype_is_linked(importtype)) { + externs[i] = wasm_extern_new_empty( + store, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } + + const wasm_name_t *module_name = + wasm_importtype_module(importtypes.data[i]); + const wasm_name_t *field_name = + wasm_importtype_name(importtypes.data[i]); + + if (strncmp(module_name->data, "env", strlen("env")) == 0 + && strncmp(field_name->data, "log", strlen("log")) == 0) { + wasm_functype_t *log_type = wasm_functype_new_2_0( + wasm_valtype_new_i64(), wasm_valtype_new_i32()); + wasm_func_t *log_func = wasm_func_new(store, log_type, host_logs); + wasm_functype_delete(log_type); + + externs[i] = wasm_func_as_extern(log_func); + } + } + + wasm_extern_vec_new(out, importtypes.num_elems, externs); + wasm_importtype_vec_delete(&importtypes); + return true; +} + +int +main() +{ + int main_ret = EXIT_FAILURE; + + // Initialize. + printf("Initializing...\n"); + wasm_engine_t *engine = wasm_engine_new(); + if (!engine) + goto quit; + + wasm_store_t *store = wasm_store_new(engine); + if (!store) + goto delete_engine; + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE *file = fopen("send_recv.aot", "rb"); + printf("> Load .aot\n"); +#else + FILE *file = fopen("send_recv.wasm", "rb"); + printf("> Load .wasm\n"); +#endif + if (!file) { + printf("> Error loading module!\n"); + goto delete_store; + } + + int ret = fseek(file, 0L, SEEK_END); + if (ret == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + long file_size = ftell(file); + if (file_size == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + ret = fseek(file, 0L, SEEK_SET); + if (ret == -1) { + printf("> Error loading module!\n"); + goto close_file; + } + + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + goto delete_binary; + } + + // Compile. + printf("Compiling module...\n"); + wasm_module_t *module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + goto delete_binary; + } + + // Set Wasi Context + const char *addr_pool[1] = { "127.0.0.1" }; + wasm_runtime_set_wasi_addr_pool(*module, addr_pool, 1); + + // Instantiate. + printf("Instantiating module...\n"); + wasm_extern_vec_t imports = { 0 }; + ret = build_imports(store, module, &imports); + if (!ret) { + printf("> Error building imports!\n"); + goto delete_module; + } + + wasm_instance_t *instance = + wasm_instance_new(store, module, &imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + goto delete_imports; + } + + // Extract export. + printf("Extracting export...\n"); + wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + goto delete_instance; + } + + /** + * should use information from wasm_module_exports to avoid hard coding "1" + */ + const wasm_func_t *start_func = wasm_extern_as_func(exports.data[1]); + if (start_func == NULL) { + printf("> Error accessing export!\n"); + goto delete_exports; + } + + // Call. "_start(nil) -> i32" + printf("Calling _start ...\n"); + wasm_val_t rs[1] = { WASM_I32_VAL(0) }; + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_ARRAY_VEC(rs); + wasm_trap_t *trap = wasm_func_call(start_func, &args, &results); + if (trap) { + wasm_name_t message = { 0 }; + wasm_trap_message(trap, &message); + + printf("> Error calling function! %s\n", message.data); + + wasm_name_delete(&message); + wasm_trap_delete(trap); + goto delete_exports; + } + + // Print result. + printf("Printing result...\n"); + printf("> %u\n", rs[0].of.i32); + + // Shut down. + printf("Shutting down...\n"); + + // All done. + printf("Done.\n"); + main_ret = EXIT_SUCCESS; + +delete_exports: + wasm_extern_vec_delete(&exports); +delete_instance: + wasm_instance_delete(instance); +delete_imports: + wasm_extern_vec_delete(&imports); +delete_module: + wasm_module_delete(module); +delete_binary: + wasm_byte_vec_delete(&binary); +close_file: + fclose(file); +delete_store: + wasm_store_delete(store); +delete_engine: + wasm_engine_delete(engine); +quit: + return main_ret; +} \ No newline at end of file diff --git a/samples/wasm-c-api-imports/wasm/CMakeLists.txt b/samples/wasm-c-api-imports/wasm/CMakeLists.txt new file mode 100644 index 000000000..6b2743cb5 --- /dev/null +++ b/samples/wasm-c-api-imports/wasm/CMakeLists.txt @@ -0,0 +1,47 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.14) +project(wasm_modules) + +if(NOT SOCKET_WASI_CMAKE) + message(FATAL_ERROR "Require SOCKET_WASI_CMAKE") +endif() + +option(WASM_TO_AOT "transfer wasm to aot" OFF) +if(WASM_TO_AOT AND NOT WAMRC_PATH) + message(FATAL_ERROR "Require WAMRC_PATH when WASM_TO_AOT is ON") +endif() + +# +# c -> wasm +include(${SOCKET_WASI_CMAKE}) +add_executable(send_recv ${CMAKE_CURRENT_LIST_DIR}/send_recv.c) +set_target_properties(send_recv PROPERTIES SUFFIX .wasm) +target_include_directories(send_recv PUBLIC ${CMAKE_CURRENT_LIST_DIR}/inc) +target_link_libraries(send_recv socket_wasi_ext) +target_link_options(send_recv PRIVATE + LINKER:--export=__heap_base + LINKER:--export=__data_end + LINKER:--shared-memory,--max-memory=196608 + LINKER:--no-check-features + LINKER:--allow-undefined +) + +if(WASM_TO_AOT) + # wasm -> aot + add_custom_target(send_recv_aot ALL + COMMAND pwd && ${WAMRC_PATH} --enable-multi-thread -o ./send_recv.aot ./send_recv.wasm + DEPENDS send_recv + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif() + +# +# install +if(WASM_TO_AOT) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/send_recv.aot DESTINATION . ) +else() + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/send_recv.wasm DESTINATION . ) +endif() + diff --git a/samples/wasm-c-api-imports/wasm/inc/.gitkeep b/samples/wasm-c-api-imports/wasm/inc/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/samples/wasm-c-api-imports/wasm/send_recv.c b/samples/wasm-c-api-imports/wasm/send_recv.c new file mode 100644 index 000000000..72e1a6729 --- /dev/null +++ b/samples/wasm-c-api-imports/wasm/send_recv.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __wasi__ +#include +#include "pthread.h" +#else +#include +#endif + +static pthread_mutex_t lock = { 0 }; +static pthread_cond_t cond = { 0 }; +static bool server_is_ready = false; + +#ifdef __wasi__ +__attribute__((import_name("log"))) extern void +host_log(uint64_t message, uint32_t length); +#endif + +static void +local_printf(const char *formatter, ...) +{ + char buffer[128] = { 0 }; + va_list args; + + va_start(args, formatter); + vsnprintf(buffer, 128, formatter, args); + va_end(args); + +#ifdef __wasi__ + host_log((uint64_t)(void *)buffer, strlen(buffer)); +#endif + printf("--> %s", buffer); +} + +void * +run_as_server(void *arg) +{ + int sock = -1, on = 1; + struct sockaddr_in addr = { 0 }; + int addrlen = 0; + int new_sock = -1; + char *buf[] = { + "The stars shine down", "It brings us light", "Light comes down", + "To make us paths", "It watches us", "And mourns for us", + }; + struct iovec iov[] = { + { .iov_base = buf[0], .iov_len = strlen(buf[0]) + 1 }, + { .iov_base = buf[1], .iov_len = strlen(buf[1]) + 1 }, + { .iov_base = buf[2], .iov_len = strlen(buf[2]) + 1 }, + { .iov_base = buf[3], .iov_len = strlen(buf[3]) + 1 }, + { .iov_base = buf[4], .iov_len = strlen(buf[4]) + 1 }, + { .iov_base = buf[5], .iov_len = strlen(buf[5]) + 1 }, + }; + struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 6 }; + ssize_t send_len = 0; + + pthread_mutex_lock(&lock); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + pthread_mutex_unlock(&lock); + perror("Create a socket failed"); + return NULL; + } + +#ifndef __wasi__ + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) { + pthread_mutex_unlock(&lock); + perror("Setsockopt failed"); + goto fail1; + } +#endif + + /* 0.0.0.0:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + addrlen = sizeof(addr); + if (bind(sock, (struct sockaddr *)&addr, addrlen) < 0) { + pthread_mutex_unlock(&lock); + perror("Bind failed"); + goto fail1; + } + + if (listen(sock, 0) < 0) { + pthread_mutex_unlock(&lock); + perror("Listen failed"); + goto fail1; + } + + server_is_ready = true; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&lock); + + local_printf("Server is online ... \n"); + + new_sock = accept(sock, (struct sockaddr *)&addr, (socklen_t *)&addrlen); + if (new_sock < 0) { + perror("Accept failed"); + goto fail1; + } + + local_printf("Start sending. \n"); + send_len = sendmsg(new_sock, &msg, 0); + if (send_len < 0) { + perror("Sendmsg failed"); + goto fail2; + } + local_printf("Send %ld bytes successfully!\n", send_len); + +fail2: + close(new_sock); +fail1: + shutdown(sock, SHUT_RD); + close(sock); + return NULL; +} + +void * +run_as_client(void *arg) +{ + int sock = -1; + struct sockaddr_in addr = { 0 }; + /* buf of server is 106 bytes */ + char buf[110] = { 0 }; + struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) }; + struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; + ssize_t recv_len = 0; + + pthread_mutex_lock(&lock); + while (false == server_is_ready) { + pthread_cond_wait(&cond, &lock); + } + pthread_mutex_unlock(&lock); + + local_printf("Client is running...\n"); + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("Create a socket failed"); + return NULL; + } + + /* 127.0.0.1:1234 */ + addr.sin_family = AF_INET; + addr.sin_port = htons(1234); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("Connect failed"); + goto fail; + } + + local_printf("Start receiving. \n"); + recv_len = recvmsg(sock, &msg, 0); + if (recv_len < 0) { + perror("Recvmsg failed"); + goto fail; + } + + local_printf("Receive %ld bytes successlly!\n", recv_len); + assert(recv_len == 106); + + local_printf("Data:\n"); + char *s = msg.msg_iov->iov_base; + while (strlen(s) > 0) { + local_printf(" %s\n", s); + s += strlen(s) + 1; + } + +fail: + shutdown(sock, SHUT_RD); + close(sock); + return NULL; +} + +int +main(int argc, char *argv[]) +{ + pthread_t cs[2] = { 0 }; + uint8_t i = 0; + int ret = EXIT_SUCCESS; + + if (pthread_mutex_init(&lock, NULL)) { + perror("Initialize mutex failed"); + ret = EXIT_FAILURE; + goto RETURN; + } + + if (pthread_cond_init(&cond, NULL)) { + perror("Initialize condition failed"); + ret = EXIT_FAILURE; + goto DESTROY_MUTEX; + } + + if (pthread_create(&cs[0], NULL, run_as_server, NULL)) { + perror("Create a server thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + if (pthread_create(&cs[1], NULL, run_as_client, NULL)) { + perror("Create a client thread failed"); + ret = EXIT_FAILURE; + goto DESTROY_COND; + } + + for (i = 0; i < 2; i++) { + pthread_join(cs[i], NULL); + } + +DESTROY_COND: + pthread_cond_destroy(&cond); +DESTROY_MUTEX: + pthread_mutex_destroy(&lock); +RETURN: + return ret; +}