Path: wasm-micro-runtime/core/iwasm/common/wasm_runtime_common.c
- Find out the function
main
(__main_argc_argv
/_main
) and make sure it's not imported - Get the type of the main function and check its validity
- Prepare parameters (
argc
andargv
) and do the address translation (native to app) - Execute: call
wasm_runtime_create_exec_env_and_call_wasm
wasm_runtime_module_free
bool
wasm_application_execute_main(WASMModuleInstanceCommon *module_inst,
int32 argc, char *argv[])
{
WASMFunctionInstanceCommon *func;
WASMType *func_type = NULL;
uint32 argc1 = 0, argv1[2] = { 0 };
uint32 total_argv_size = 0;
uint64 total_size;
uint32 argv_buf_offset = 0;
int32 i;
char *argv_buf, *p, *p_end;
uint32 *argv_offsets, module_type;
bool ret, is_import_func = true;
#if WASM_ENABLE_LIBC_WASI != 0
if (wasm_runtime_is_wasi_mode(module_inst)) {
/* In wasi mode, we should call function named "_start"
which initializes the wasi envrionment and then calls
the actual main function. Directly call main function
may cause exception thrown. */
if ((func = wasm_runtime_lookup_wasi_start_function(module_inst)))
return wasm_runtime_create_exec_env_and_call_wasm(
module_inst, func, 0, NULL);
/* if no start function is found, we execute
the main function as normal */
}
#endif /* end of WASM_ENABLE_LIBC_WASI */
if (!(func = resolve_function(module_inst, "main"))
&& !(func = resolve_function(module_inst, "__main_argc_argv"))
&& !(func = resolve_function(module_inst, "_main"))) {
wasm_runtime_set_exception(module_inst,
"lookup main function failed");
return false;
}
#if WASM_ENABLE_INTERP != 0
if (module_inst->module_type == Wasm_Module_Bytecode) {
is_import_func = ((WASMFunctionInstance*)func)->is_import_func;
}
#endif
#if WASM_ENABLE_AOT != 0
if (module_inst->module_type == Wasm_Module_AoT) {
is_import_func = ((AOTFunctionInstance*)func)->is_import_func;
}
#endif
if (is_import_func) {
wasm_runtime_set_exception(module_inst,
"lookup main function failed");
return false;
}
module_type = module_inst->module_type;
func_type = wasm_runtime_get_function_type(func, module_type);
if (!func_type) {
LOG_ERROR("invalid module instance type");
return false;
}
if (!check_main_func_type(func_type)) {
wasm_runtime_set_exception(module_inst,
"invalid function type of main function");
return false;
}
if (func_type->param_count) {
for (i = 0; i < argc; i++)
total_argv_size += (uint32)(strlen(argv[i]) + 1);
total_argv_size = align_uint(total_argv_size, 4);
total_size = (uint64)total_argv_size + sizeof(int32) * (uint64)argc;
if (total_size >= UINT32_MAX
|| !(argv_buf_offset =
wasm_runtime_module_malloc(module_inst, (uint32)total_size,
(void**)&argv_buf))) {
wasm_runtime_set_exception(module_inst,
"allocate memory failed");
return false;
}
p = argv_buf;
argv_offsets = (uint32*)(p + total_argv_size);
p_end = p + total_size;
for (i = 0; i < argc; i++) {
bh_memcpy_s(p, (uint32)(p_end - p), argv[i], (uint32)(strlen(argv[i]) + 1));
argv_offsets[i] = argv_buf_offset + (uint32)(p - argv_buf);
p += strlen(argv[i]) + 1;
}
argc1 = 2;
argv1[0] = (uint32)argc;
argv1[1] = (uint32)wasm_runtime_addr_native_to_app(module_inst, argv_offsets);
}
ret = wasm_runtime_create_exec_env_and_call_wasm(module_inst, func,
argc1, argv1);
if (argv_buf_offset)
wasm_runtime_module_free(module_inst, argv_buf_offset);
return ret;
}
wasm_runtime_get_function_type
WASMType *
wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function,
uint32 module_type)
{
WASMType *type = NULL;
#if WASM_ENABLE_INTERP != 0
if (module_type == Wasm_Module_Bytecode) {
WASMFunctionInstance *wasm_func = (WASMFunctionInstance *)function;
type = wasm_func->is_import_func
? wasm_func->u.func_import->func_type
: wasm_func->u.func->func_type;
}
#endif
#if WASM_ENABLE_AOT != 0
if (module_type == Wasm_Module_AoT) {
AOTFunctionInstance *aot_func = (AOTFunctionInstance *)function;
type = aot_func->is_import_func
? aot_func->u.func_import->func_type
: aot_func->u.func.func_type;
}
#endif
return type;
}
check_main_func_type
- Two or no parameters
- At least one result(return?)
- The two parameters are
i32
(if it has two params) - The first return value is i32
WASMType
typedef struct WASMType {
uint16 param_count;
uint16 result_count;
uint16 param_cell_num;
uint16 ret_cell_num;
/* types of params and results */
uint8 types[1];
} WASMType;
static bool
check_main_func_type(const WASMType *type)
{
if (!(type->param_count == 0 || type->param_count == 2)
||type->result_count > 1) {
LOG_ERROR("WASM execute application failed: invalid main function type.\n");
return false;
}
if (type->param_count == 2
&& !(type->types[0] == VALUE_TYPE_I32
&& type->types[1] == VALUE_TYPE_I32)) {
LOG_ERROR("WASM execute application failed: invalid main function type.\n");
return false;
}
if (type->result_count
&& type->types[type->param_count] != VALUE_TYPE_I32) {
LOG_ERROR("WASM execute application failed: invalid main function type.\n");
return false;
}
return true;
}
wasm_runtime_create_exec_env_and_call_wasm
Depending on the type of module, invokes AoT or WASM executor.
bool
wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst,
WASMFunctionInstanceCommon *function,
uint32 argc, uint32 argv[])
{
bool ret = false;
#if WASM_ENABLE_INTERP != 0
if (module_inst->module_type == Wasm_Module_Bytecode)
ret = wasm_create_exec_env_and_call_function(
(WASMModuleInstance *)module_inst, (WASMFunctionInstance *)function,
argc, argv);
#endif
#if WASM_ENABLE_AOT != 0
if (module_inst->module_type == Wasm_Module_AoT)
ret = aot_create_exec_env_and_call_function(
(AOTModuleInstance *)module_inst, (AOTFunctionInstance *)function,
argc, argv);
#endif
return ret;
}
wasm_create_exec_env_and_call_function
- Create environment
- Call the function
- Destory environment
bool
wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
WASMFunctionInstance *func,
unsigned argc, uint32 argv[])
{
WASMExecEnv *exec_env;
bool ret;
#if WASM_ENABLE_THREAD_MGR != 0
WASMExecEnv *existing_exec_env = NULL;
if (!(existing_exec_env = exec_env =
wasm_clusters_search_exec_env(
(WASMModuleInstanceCommon*)module_inst))) {
#endif
if (!(exec_env = wasm_exec_env_create(
(WASMModuleInstanceCommon*)module_inst,
module_inst->default_wasm_stack_size))) {
wasm_set_exception(module_inst, "allocate memory failed");
return false;
}
#if WASM_ENABLE_THREAD_MGR != 0
}
#endif
#if WASM_ENABLE_REF_TYPES != 0
wasm_runtime_prepare_call_function(exec_env, func);
#endif
ret = wasm_call_function(exec_env, func, argc, argv);
#if WASM_ENABLE_REF_TYPES != 0
wasm_runtime_finalize_call_function(exec_env, func, ret, argv);
#endif
#if WASM_ENABLE_THREAD_MGR != 0
/* don't destroy the exec_env if it's searched from the cluster */
if (!existing_exec_env)
#endif
wasm_exec_env_destroy(exec_env);
return ret;
}
wasm_exec_env_create_internal
Mainly initializes the stack
WASMExecEnv *
wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
uint32 stack_size)
{
uint64 total_size = offsetof(WASMExecEnv, wasm_stack.s.bottom)
+ (uint64)stack_size;
WASMExecEnv *exec_env;
if (total_size >= UINT32_MAX
|| !(exec_env = wasm_runtime_malloc((uint32)total_size)))
return NULL;
memset(exec_env, 0, (uint32)total_size);
#if WASM_ENABLE_AOT != 0
if (!(exec_env->argv_buf = wasm_runtime_malloc(sizeof(uint32) * 64))) {
goto fail1;
}
#endif
#if WASM_ENABLE_THREAD_MGR != 0
if (os_mutex_init(&exec_env->wait_lock) != 0)
goto fail2;
if (os_cond_init(&exec_env->wait_cond) != 0)
goto fail3;
#endif
exec_env->module_inst = module_inst;
exec_env->wasm_stack_size = stack_size;
exec_env->wasm_stack.s.top_boundary =
exec_env->wasm_stack.s.bottom + stack_size;
exec_env->wasm_stack.s.top = exec_env->wasm_stack.s.bottom;
#if WASM_ENABLE_MEMORY_TRACING != 0
wasm_runtime_dump_exec_env_mem_consumption(exec_env);
#endif
return exec_env;
#if WASM_ENABLE_THREAD_MGR != 0
fail3:
os_mutex_destroy(&exec_env->wait_lock);
fail2:
#endif
#if WASM_ENABLE_AOT != 0
wasm_runtime_free(exec_env->argv_buf);
fail1:
#endif
wasm_runtime_free(exec_env);
return NULL;
}
wasm_call_function
bool
wasm_call_function(WASMExecEnv *exec_env,
WASMFunctionInstance *function,
unsigned argc, uint32 argv[])
{
WASMModuleInstance *module_inst = (WASMModuleInstance*)exec_env->module_inst;
/* set thread handle and stack boundary */
wasm_exec_env_set_thread_info(exec_env);
wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv);
(void)clear_wasi_proc_exit_exception(module_inst);
return !wasm_get_exception(module_inst) ? true : false;
}
void
wasm_exec_env_set_thread_info(WASMExecEnv *exec_env)
{
exec_env->handle = os_self_thread();
exec_env->native_stack_boundary = os_thread_get_stack_boundary()
+ RESERVED_BYTES_TO_NATIVE_STACK_BOUNDARY;
}
The function os_thread_get_stack_boundary
is empty for SGX platform!
wasm_interp_call_wasm
\
- What's frame in the interpreter?
- Check the number of arguments coordinates with the function signature
- Check native stack boundary
- Allocate new frame and output pointer
- Copy args and update environment
- Execute the imported native function or WASM bytecode
- Get the result and return (to argv)
- Free frame
void
wasm_interp_call_wasm(WASMModuleInstance *module_inst,
WASMExecEnv *exec_env,
WASMFunctionInstance *function,
uint32 argc, uint32 argv[])
{
WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env);
WASMInterpFrame *frame, *outs_area;
/* Allocate sufficient cells for all kinds of return values. */
unsigned all_cell_num = function->ret_cell_num > 2 ?
function->ret_cell_num : 2, i;
/* This frame won't be used by JITed code, so only allocate interp
frame here. */
unsigned frame_size = wasm_interp_interp_frame_size(all_cell_num);
if (argc != function->param_cell_num) {
char buf[128];
snprintf(buf, sizeof(buf),
"invalid argument count %d, expected %d",
argc, function->param_cell_num);
wasm_set_exception(module_inst, buf);
return;
}
if ((uint8*)&prev_frame < exec_env->native_stack_boundary) {
wasm_set_exception((WASMModuleInstance*)exec_env->module_inst,
"native stack overflow");
return;
}
if (!(frame = ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame*)prev_frame)))
return;
outs_area = wasm_exec_env_wasm_stack_top(exec_env);
frame->function = NULL;
frame->ip = NULL;
/* There is no local variable. */
frame->sp = frame->lp + 0;
if (argc > 0)
word_copy(outs_area->lp, argv, argc);
wasm_exec_env_set_cur_frame(exec_env, frame);
if (function->is_import_func) {
#if WASM_ENABLE_MULTI_MODULE != 0
if (function->import_module_inst) {
wasm_interp_call_func_import(module_inst, exec_env,
function, frame);
}
else
#endif
{
/* it is a native function */
wasm_interp_call_func_native(module_inst, exec_env,
function, frame);
}
}
else {
wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame);
}
/* Output the return value to the caller */
if (!wasm_get_exception(module_inst)) {
for (i = 0; i < function->ret_cell_num; i++) {
argv[i] = *(frame->sp + i - function->ret_cell_num);
}
}
else {
#if WASM_ENABLE_DUMP_CALL_STACK != 0
wasm_interp_dump_call_stack(exec_env);
#endif
LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst));
}
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
FREE_FRAME(exec_env, frame);
}
wasm_interp_call_func_bytecode
This is the core function to interpret the WASM bytecode.