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.
This commit is contained in:
liang.he 2023-02-13 15:06:04 +08:00 committed by GitHub
parent 427abf02c8
commit 3698f2279b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1168 additions and 219 deletions

View File

@ -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;
}

View File

@ -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 "

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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 <module_name> <func_name> (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 <module_name> <global_name> (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

View File

@ -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;

View File

@ -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;

2
samples/wasm-c-api-imports/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/wasm/inc/**
!/wasm/inc/.*

View File

@ -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}
)

View File

@ -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 <some_example>.wasm
Section Details:
Import[27]:
- func[0] sig=2 <pthread_mutex_lock> <- env.pthread_mutex_lock
- func[1] sig=2 <pthread_mutex_unlock> <- env.pthread_mutex_unlock
- func[2] sig=2 <pthread_cond_signal> <- env.pthread_cond_signal
- func[3] sig=3 <host_log> <- 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);
}
}
```

View File

@ -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)

View File

@ -0,0 +1,204 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <stdlib.h>
#include <stdio.h>
#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;
}

View File

@ -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()

View File

@ -0,0 +1,231 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <arpa/inet.h>
#include <assert.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef __wasi__
#include <wasi_socket_ext.h>
#include "pthread.h"
#else
#include <pthread.h>
#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;
}