Browse Source

Add WASI ABI compatibility check for multi-module (#913)

Refer to https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md
to check the WASI ABI compatibility:
- Command (main module) may export _start function with signature "()"
- Reactor (sub module) may export _initialize function with signature "()"
- _start and _initialize can not be exported at the same time
- Reactor cannot export _start function
- Command and Reactor must export memory

And
- Rename module->is_wasi_module to module->import_wasi_api
- Refactor wasm_loader_find_export()
- Remove MULTI_MODULE related codes from mini_loader
- Update multi-module samples
- Fix a "use-after-free" issue. Since we reuse the memory instance of sub module,
   just to protect it from freeing an imported memory instance
liang.he 4 years ago
parent
commit
50b6474f54

+ 7 - 0
.gitignore

@@ -4,6 +4,10 @@
 /.idea
 **/cmake-build-*/
 **/*build/
+*.obj
+*.a
+*.so
+
 core/deps/**
 core/shared/mem-alloc/tlsf
 core/app-framework/wgl
@@ -12,6 +16,9 @@ wamr-sdk/out/
 wamr-sdk/runtime/build_runtime_sdk/
 test-tools/host-tool/bin/
 product-mini/app-samples/hello-world/test.wasm
+product-mini/platforms/linux-sgx/enclave-sample/App/
+product-mini/platforms/linux-sgx/enclave-sample/Enclave/
+product-mini/platforms/linux-sgx/enclave-sample/iwasm
 
 build_out
 tests/wamr-test-suites/workspace

+ 2 - 2
core/iwasm/aot/aot_loader.c

@@ -1284,7 +1284,7 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
 #if WASM_ENABLE_LIBC_WASI != 0
         if (!strcmp(import_funcs[i].module_name, "wasi_unstable")
             || !strcmp(import_funcs[i].module_name, "wasi_snapshot_preview1"))
-            module->is_wasi_module = true;
+            module->import_wasi_api = true;
 #endif
     }
 
@@ -2925,7 +2925,7 @@ aot_load_from_comp_data(AOTCompData *comp_data, AOTCompContext *comp_ctx,
     module->comp_data = comp_data;
 
 #if WASM_ENABLE_LIBC_WASI != 0
-    module->is_wasi_module = comp_data->wasm_module->is_wasi_module;
+    module->import_wasi_api = comp_data->wasm_module->import_wasi_api;
 #endif
 
     return module;

+ 1 - 1
core/iwasm/aot/aot_runtime.c

@@ -1059,7 +1059,7 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, uint32 stack_size,
 
 #if WASM_ENABLE_BULK_MEMORY != 0
 #if WASM_ENABLE_LIBC_WASI != 0
-    if (!module->is_wasi_module) {
+    if (!module->import_wasi_api) {
 #endif
         /* Only execute the memory init function for main instance because
             the data segments will be dropped once initialized.

+ 1 - 1
core/iwasm/aot/aot_runtime.h

@@ -256,7 +256,7 @@ typedef struct AOTModule {
 
 #if WASM_ENABLE_LIBC_WASI != 0
     WASIArguments wasi_args;
-    bool is_wasi_module;
+    bool import_wasi_api;
 #endif
 #if WASM_ENABLE_DEBUG_AOT != 0
     void *elf_hdr;

+ 53 - 84
core/iwasm/common/wasm_application.c

@@ -48,10 +48,6 @@ static union {
 /**
  * Implementation of wasm_application_execute_main()
  */
-
-static WASMFunctionInstanceCommon *
-resolve_function(const WASMModuleInstanceCommon *module_inst, const char *name);
-
 static bool
 check_main_func_type(const WASMType *type)
 {
@@ -96,23 +92,29 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, int32 argc,
     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 was found, we execute
-           the main function as normal */
+    /* 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);
     }
 #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");
+    if (!(func = wasm_runtime_lookup_function(module_inst, "main", NULL))
+        && !(func = wasm_runtime_lookup_function(module_inst,
+                                                 "__main_argc_argv", NULL))
+        && !(func = wasm_runtime_lookup_function(module_inst, "_main", NULL))) {
+#if WASM_ENABLE_LIBC_WASI != 0
+        wasm_runtime_set_exception(
+            module_inst, "lookup the entry point symbol (like _start, main, "
+                         "_main, __main_argc_argv) failed");
+#else
+        wasm_runtime_set_exception(module_inst,
+                                   "lookup the entry point symbol (like main, "
+                                   "_main, __main_argc_argv) failed");
+#endif
         return false;
     }
 
@@ -238,21 +240,23 @@ parse_function_name(char *orig_function_name, char **p_module_name,
  * Implementation of wasm_application_execute_func()
  */
 
-static WASMFunctionInstanceCommon *
-resolve_function(const WASMModuleInstanceCommon *module_inst, const char *name)
+static bool
+resolve_function(WASMModuleInstanceCommon *module_inst, const char *name,
+                 WASMFunctionInstanceCommon **out_func,
+                 WASMModuleInstanceCommon **out_module_inst)
 {
-    uint32 i = 0;
-    WASMFunctionInstanceCommon *ret = NULL;
+    WASMFunctionInstanceCommon *target_func = NULL;
+    WASMModuleInstanceCommon *target_inst = NULL;
+
 #if WASM_ENABLE_MULTI_MODULE != 0
-    WASMModuleInstance *sub_module_inst = NULL;
+    char *function_name = NULL;
     char *orig_name = NULL;
     char *sub_module_name = NULL;
-    char *function_name = NULL;
     uint32 length = (uint32)(strlen(name) + 1);
 
     orig_name = runtime_malloc(sizeof(char) * length, NULL, NULL, 0);
     if (!orig_name) {
-        return NULL;
+        goto LEAVE;
     }
 
     strncpy(orig_name, name, length);
@@ -264,53 +268,33 @@ resolve_function(const WASMModuleInstanceCommon *module_inst, const char *name)
     LOG_DEBUG("%s -> %s and %s", name, sub_module_name, function_name);
 
     if (sub_module_name) {
-        sub_module_inst = get_sub_module_inst((WASMModuleInstance *)module_inst,
-                                              sub_module_name);
-        if (!sub_module_inst) {
+        target_inst = (WASMModuleInstanceCommon *)get_sub_module_inst(
+            (WASMModuleInstance *)module_inst, sub_module_name);
+        if (!target_inst) {
             LOG_DEBUG("can not find a sub module named %s", sub_module_name);
             goto LEAVE;
         }
     }
+    else {
+        target_inst = module_inst;
+    }
 #else
     const char *function_name = name;
+    target_inst = module_inst;
 #endif
 
-#if WASM_ENABLE_INTERP != 0
-    if (module_inst->module_type == Wasm_Module_Bytecode) {
-        WASMModuleInstance *wasm_inst = (WASMModuleInstance *)module_inst;
-
-#if WASM_ENABLE_MULTI_MODULE != 0
-        wasm_inst = sub_module_inst ? sub_module_inst : wasm_inst;
-#endif /* WASM_ENABLE_MULTI_MODULE */
-
-        for (i = 0; i < wasm_inst->export_func_count; i++) {
-            if (!strcmp(wasm_inst->export_functions[i].name, function_name)) {
-                ret = wasm_inst->export_functions[i].function;
-                break;
-            }
-        }
-    }
-#endif /* WASM_ENABLE_INTERP */
-
-#if WASM_ENABLE_AOT != 0
-    if (module_inst->module_type == Wasm_Module_AoT) {
-        AOTModuleInstance *aot_inst = (AOTModuleInstance *)module_inst;
-        AOTFunctionInstance *export_funcs =
-            (AOTFunctionInstance *)aot_inst->export_funcs.ptr;
-        for (i = 0; i < aot_inst->export_func_count; i++) {
-            if (!strcmp(export_funcs[i].func_name, function_name)) {
-                ret = &export_funcs[i];
-                break;
-            }
-        }
-    }
-#endif
+    target_func =
+        wasm_runtime_lookup_function(target_inst, function_name, NULL);
 
 #if WASM_ENABLE_MULTI_MODULE != 0
 LEAVE:
-    wasm_runtime_free(orig_name);
+    if (orig_name)
+        wasm_runtime_free(orig_name);
 #endif
-    return ret;
+
+    *out_func = target_func;
+    *out_module_inst = target_inst;
+    return target_func;
 }
 
 union ieee754_float {
@@ -358,7 +342,8 @@ bool
 wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
                               const char *name, int32 argc, char *argv[])
 {
-    WASMFunctionInstanceCommon *func;
+    WASMFunctionInstanceCommon *target_func;
+    WASMModuleInstanceCommon *target_inst;
     WASMType *type = NULL;
     uint32 argc1, *argv1 = NULL, cell_num = 0, j, k = 0;
     int32 i, p, module_type;
@@ -368,31 +353,15 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
 
     bh_assert(argc >= 0);
     LOG_DEBUG("call a function \"%s\" with %d arguments", name, argc);
-    func = resolve_function(module_inst, name);
 
-    if (!func) {
+    if (!resolve_function(module_inst, name, &target_func, &target_inst)) {
         snprintf(buf, sizeof(buf), "lookup function %s failed", name);
         wasm_runtime_set_exception(module_inst, buf);
         goto fail;
     }
 
-#if WASM_ENABLE_INTERP != 0
-    if (module_inst->module_type == Wasm_Module_Bytecode) {
-        WASMFunctionInstance *wasm_func = (WASMFunctionInstance *)func;
-        if (wasm_func->is_import_func
-#if WASM_ENABLE_MULTI_MODULE != 0
-            && !wasm_func->import_func_inst
-#endif
-        ) {
-            snprintf(buf, sizeof(buf), "lookup function %s failed", name);
-            wasm_runtime_set_exception(module_inst, buf);
-            goto fail;
-        }
-    }
-#endif
-
-    module_type = module_inst->module_type;
-    type = wasm_runtime_get_function_type(func, module_type);
+    module_type = target_inst->module_type;
+    type = wasm_runtime_get_function_type(target_func, module_type);
 
     if (!type) {
         LOG_ERROR("invalid module instance type");
@@ -408,7 +377,7 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
     cell_num = (argc1 > type->ret_cell_num) ? argc1 : type->ret_cell_num;
 
     total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2);
-    if ((!(argv1 = runtime_malloc((uint32)total_size, module_inst, NULL, 0)))) {
+    if ((!(argv1 = runtime_malloc((uint32)total_size, target_inst, NULL, 0)))) {
         goto fail;
     }
 
@@ -538,7 +507,7 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
                     void *extern_obj = (void *)(uintptr_t)val;
                     uint32 externref_idx;
 
-                    if (!wasm_externref_obj2ref(module_inst, extern_obj,
+                    if (!wasm_externref_obj2ref(target_inst, extern_obj,
                                                 &externref_idx)) {
                         wasm_runtime_set_exception(
                             module_inst, "map extern object to ref failed");
@@ -563,8 +532,8 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst,
     bh_assert(p == (int32)argc1);
 
     wasm_runtime_set_exception(module_inst, NULL);
-    if (!wasm_runtime_create_exec_env_and_call_wasm(module_inst, func, argc1,
-                                                    argv1)) {
+    if (!wasm_runtime_create_exec_env_and_call_wasm(target_inst, target_func,
+                                                    argc1, argv1)) {
         goto fail;
     }
 

+ 2 - 2
core/iwasm/common/wasm_runtime_common.c

@@ -2294,13 +2294,13 @@ wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst)
 {
 #if WASM_ENABLE_INTERP != 0
     if (module_inst->module_type == Wasm_Module_Bytecode
-        && ((WASMModuleInstance *)module_inst)->module->is_wasi_module)
+        && ((WASMModuleInstance *)module_inst)->module->import_wasi_api)
         return true;
 #endif
 #if WASM_ENABLE_AOT != 0
     if (module_inst->module_type == Wasm_Module_AoT
         && ((AOTModule *)((AOTModuleInstance *)module_inst)->aot_module.ptr)
-               ->is_wasi_module)
+               ->import_wasi_api)
         return true;
 #endif
     return false;

+ 1 - 1
core/iwasm/interpreter/wasm.h

@@ -403,7 +403,7 @@ struct WASMModule {
 
 #if WASM_ENABLE_LIBC_WASI != 0
     WASIArguments wasi_args;
-    bool is_wasi_module;
+    bool import_wasi_api;
 #endif
 
 #if WASM_ENABLE_MULTI_MODULE != 0

+ 167 - 22
core/iwasm/interpreter/wasm_loader.c

@@ -647,11 +647,11 @@ adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size)
 static WASMExport *
 wasm_loader_find_export(const WASMModule *module, const char *module_name,
                         const char *field_name, uint8 export_kind,
-                        uint32 export_index_boundary, char *error_buf,
-                        uint32 error_buf_size)
+                        char *error_buf, uint32 error_buf_size)
 {
     WASMExport *export;
     uint32 i;
+    uint32 export_index_boundary = 0;
 
     for (i = 0, export = module->exports; i < module->export_count;
          ++i, ++export) {
@@ -674,6 +674,27 @@ wasm_loader_find_export(const WASMModule *module, const char *module_name,
         return NULL;
     }
 
+    switch (export_kind) {
+        case EXPORT_KIND_FUNC:
+            export_index_boundary =
+                module->import_function_count + module->function_count;
+            break;
+        case EXPORT_KIND_GLOBAL:
+            export_index_boundary =
+                module->import_global_count + module->global_count;
+            break;
+        case EXPORT_KIND_MEMORY:
+            export_index_boundary =
+                module->import_memory_count + module->memory_count;
+            break;
+        case EXPORT_KIND_TABLE:
+            export_index_boundary =
+                module->import_table_count + module->table_count;
+            break;
+        default:
+            bh_assert(0);
+    }
+
     if (export->index >= export_index_boundary) {
         LOG_DEBUG("%s in the module %s is out of index (%d >= %d )", field_name,
                   module_name, export->index, export_index_boundary);
@@ -704,10 +725,9 @@ wasm_loader_resolve_function(const char *module_name, const char *function_name,
     }
 
     module = (WASMModule *)module_reg;
-    export = wasm_loader_find_export(
-        module, module_name, function_name, EXPORT_KIND_FUNC,
-        module->import_function_count + module->function_count, error_buf,
-        error_buf_size);
+    export =
+        wasm_loader_find_export(module, module_name, function_name,
+                                EXPORT_KIND_FUNC, error_buf, error_buf_size);
     if (!export) {
         return NULL;
     }
@@ -755,10 +775,9 @@ wasm_loader_resolve_table(const char *module_name, const char *table_name,
     }
 
     module = (WASMModule *)module_reg;
-    export = wasm_loader_find_export(
-        module, module_name, table_name, EXPORT_KIND_TABLE,
-        module->table_count + module->import_table_count, error_buf,
-        error_buf_size);
+    export =
+        wasm_loader_find_export(module, module_name, table_name,
+                                EXPORT_KIND_TABLE, error_buf, error_buf_size);
     if (!export) {
         return NULL;
     }
@@ -800,10 +819,9 @@ wasm_loader_resolve_memory(const char *module_name, const char *memory_name,
     }
 
     module = (WASMModule *)module_reg;
-    export = wasm_loader_find_export(
-        module, module_name, memory_name, EXPORT_KIND_MEMORY,
-        module->import_memory_count + module->memory_count, error_buf,
-        error_buf_size);
+    export =
+        wasm_loader_find_export(module, module_name, memory_name,
+                                EXPORT_KIND_MEMORY, error_buf, error_buf_size);
     if (!export) {
         return NULL;
     }
@@ -846,10 +864,9 @@ wasm_loader_resolve_global(const char *module_name, const char *global_name,
     }
 
     module = (WASMModule *)module_reg;
-    export = wasm_loader_find_export(
-        module, module_name, global_name, EXPORT_KIND_GLOBAL,
-        module->import_global_count + module->global_count, error_buf,
-        error_buf_size);
+    export =
+        wasm_loader_find_export(module, module_name, global_name,
+                                EXPORT_KIND_GLOBAL, error_buf, error_buf_size);
     if (!export) {
         return NULL;
     }
@@ -969,7 +986,7 @@ load_depended_module(const WASMModule *parent_module,
     }
 
     sub_module =
-        wasm_loader_load(buffer, buffer_size, error_buf, error_buf_size);
+        wasm_loader_load(buffer, buffer_size, false, error_buf, error_buf_size);
     if (!sub_module) {
         LOG_DEBUG("error: can not load the sub_module %s", sub_module_name);
         /* others will be destroyed in runtime_destroy() */
@@ -1736,7 +1753,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
             if (!strcmp(import->u.names.module_name, "wasi_unstable")
                 || !strcmp(import->u.names.module_name,
                            "wasi_snapshot_preview1")) {
-                module->is_wasi_module = true;
+                module->import_wasi_api = true;
                 break;
             }
         }
@@ -3440,9 +3457,129 @@ fail:
     return false;
 }
 
+#if (WASM_ENABLE_MULTI_MODULE != 0) && (WASM_ENABLE_LIBC_WASI != 0)
+/*
+ * refer to
+ * https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md
+ */
+static bool
+check_wasi_abi_compatibility(const WASMModule *module, bool main_module,
+                             char *error_buf, uint32 error_buf_size)
+{
+    /*
+     * need to handle:
+     * - non-wasi compatiable modules
+     * - a fake wasi compatiable module
+     * - a command acts as a main_module
+     * - a command acts as a sub_module
+     * - a reactor acts as a main_module
+     * - a reactor acts as a sub_module
+     *
+     * be careful with:
+     * wasi compatiable modules(command/reactor) which don't import any wasi
+     * APIs. usually, a command has to import a "prox_exit" at least. but a
+     * reactor can depend on nothing. At the same time, each has its own entry
+     * point.
+     *
+     * observations:
+     * - clang always injects `_start` into a command
+     * - clang always injects `_initialize` into a reactor
+     * - `iwasm -f` allows to run a function in the reactor
+     *
+     * strong assumptions:
+     * - no one will define either `_start` or `_initialize` on purpose
+     * - `_start` should always be `void _start(void)`
+     * - `_initialize` should always be `void _initialize(void)`
+     */
+
+    WASMExport *initialize = NULL, *memory = NULL, *start = NULL;
+
+    /* (func (export "_start") (...) */
+    start = wasm_loader_find_export(module, "", "_start", EXPORT_KIND_FUNC,
+                                    error_buf, error_buf_size);
+    if (start) {
+        WASMType *func_type =
+            module->functions[start->index - module->import_function_count]
+                ->func_type;
+        if (func_type->param_count || func_type->result_count) {
+            set_error_buf(error_buf, error_buf_size,
+                          "The builtin _start() is with a wrong signature");
+            return false;
+        }
+    }
+
+    /* (func (export "_initialize") (...) */
+    initialize = wasm_loader_find_export(
+        module, "", "_initialize", EXPORT_KIND_FUNC, error_buf, error_buf_size);
+    if (initialize) {
+        WASMType *func_type =
+            module->functions[initialize->index - module->import_function_count]
+                ->func_type;
+        if (func_type->param_count || func_type->result_count) {
+            set_error_buf(
+                error_buf, error_buf_size,
+                "The builtin _initiazlie() is with a wrong signature");
+            return false;
+        }
+    }
+
+    /* filter out non-wasi compatiable modules */
+    if (!module->import_wasi_api && !start && !initialize) {
+        return true;
+    }
+
+    /* should have one at least */
+    if (module->import_wasi_api && !start && !initialize) {
+        set_error_buf(
+            error_buf, error_buf_size,
+            "A module with WASI apis should be either a command or a reactor");
+        return false;
+    }
+
+    /*
+     * there is at least one of `_start` and `_initialize` in below cases.
+     * according to the assumption, they should be all wasi compatiable
+     */
+
+    /* always can not have both at the same time  */
+    if (start && initialize) {
+        set_error_buf(
+            error_buf, error_buf_size,
+            "Neither a command nor a reactor can have both at the same time");
+        return false;
+    }
+
+    /* filter out commands (with `_start`) cases */
+    if (start && !main_module) {
+        set_error_buf(error_buf, error_buf_size,
+                      "A command(with _start) can not be a sud-module");
+        return false;
+    }
+
+    /*
+     * it is ok a reactor acts as a main module,
+     * so skip the check about (with `_initialize`)
+     */
+
+    memory = wasm_loader_find_export(module, "", "memory", EXPORT_KIND_MEMORY,
+                                     error_buf, error_buf_size);
+    if (!memory) {
+        set_error_buf(
+            error_buf, error_buf_size,
+            "A module with WASI apis should export memory by default");
+        return false;
+    }
+
+    return true;
+}
+#endif
+
 WASMModule *
-wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf,
-                 uint32 error_buf_size)
+wasm_loader_load(const uint8 *buf, uint32 size,
+#if WASM_ENABLE_MULTI_MODULE != 0
+                 bool main_module,
+#endif
+                 char *error_buf, uint32 error_buf_size)
 {
     WASMModule *module = create_module(error_buf, error_buf_size);
     if (!module) {
@@ -3458,6 +3595,14 @@ wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf,
         goto fail;
     }
 
+#if (WASM_ENABLE_MULTI_MODULE != 0) && (WASM_ENABLE_LIBC_WASI != 0)
+    /* do a check about WASI Application ABI */
+    if (!check_wasi_abi_compatibility(module, main_module, error_buf,
+                                      error_buf_size)) {
+        goto fail;
+    }
+#endif
+
     LOG_VERBOSE("Load module success.\n");
     return module;
 

+ 5 - 2
core/iwasm/interpreter/wasm_loader.h

@@ -24,8 +24,11 @@ extern "C" {
  * @return return module loaded, NULL if failed
  */
 WASMModule *
-wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf,
-                 uint32 error_buf_size);
+wasm_loader_load(const uint8 *buf, uint32 size,
+#if WASM_ENABLE_MULTI_MODULE != 0
+                 bool main_module,
+#endif
+                 char *error_buf, uint32 error_buf_size);
 
 /**
  * Load a WASM module from a specified WASM section list.

+ 2 - 6
core/iwasm/interpreter/wasm_mini_loader.c

@@ -851,7 +851,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
             if (!strcmp(import->u.names.module_name, "wasi_unstable")
                 || !strcmp(import->u.names.module_name,
                            "wasi_snapshot_preview1")) {
-                module->is_wasi_module = true;
+                module->import_wasi_api = true;
                 break;
             }
         }
@@ -2120,7 +2120,6 @@ load_from_sections(WASMModule *module, WASMSection *sections,
             }
         }
 
-#if WASM_ENABLE_MULTI_MODULE == 0
         if (module->import_memory_count) {
             memory_import = &module->import_memories[0].u.memory;
             /* Memory init page count cannot be larger than 65536, we don't
@@ -2128,6 +2127,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
             memory_import->num_bytes_per_page *= memory_import->init_page_count;
             memory_import->init_page_count = memory_import->max_page_count = 1;
         }
+
         if (module->memory_count) {
             /* Memory init page count cannot be larger than 65536, we don't
                check integer overflow again. */
@@ -2135,7 +2135,6 @@ load_from_sections(WASMModule *module, WASMSection *sections,
             memory->num_bytes_per_page *= memory->init_page_count;
             memory->init_page_count = memory->max_page_count = 1;
         }
-#endif
     }
 
 #if WASM_ENABLE_MEMORY_TRACING != 0
@@ -2159,9 +2158,6 @@ create_module(char *error_buf, uint32 error_buf_size)
     /* Set start_function to -1, means no start function */
     module->start_function = (uint32)-1;
 
-#if WASM_ENABLE_MULTI_MODULE != 0
-    module->import_module_list = &module->import_module_list_head;
-#endif
     return module;
 }
 

+ 46 - 18
core/iwasm/interpreter/wasm_runtime.c

@@ -47,7 +47,11 @@ set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, ...)
 WASMModule *
 wasm_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size)
 {
-    return wasm_loader_load(buf, size, error_buf, error_buf_size);
+    return wasm_loader_load(buf, size,
+#if WASM_ENABLE_MULTI_MODULE != 0
+                            true,
+#endif
+                            error_buf, error_buf_size);
 }
 
 WASMModule *
@@ -105,7 +109,7 @@ memories_deinstantiate(WASMModuleInstance *module_inst,
         for (i = 0; i < count; i++) {
             if (memories[i]) {
 #if WASM_ENABLE_MULTI_MODULE != 0
-                if (memories[i]->owner != module_inst)
+                if (i < module_inst->module->import_memory_count)
                     continue;
 #endif
 #if WASM_ENABLE_SHARED_MEMORY != 0
@@ -384,13 +388,6 @@ memories_instantiate(const WASMModule *module, WASMModuleInstance *module_inst,
                 memories_deinstantiate(module_inst, memories, memory_count);
                 return NULL;
             }
-#if WASM_ENABLE_MULTI_MODULE != 0
-            /* The module of the import memory is a builtin module, and
-               the memory is created by current module, set its owner
-               to current module, so the memory can be destroyed in
-               memories_deinstantiate. */
-            memory->owner = module_inst;
-#endif
         }
     }
 
@@ -404,9 +401,6 @@ memories_instantiate(const WASMModule *module, WASMModuleInstance *module_inst,
             memories_deinstantiate(module_inst, memories, memory_count);
             return NULL;
         }
-#if WASM_ENABLE_MULTI_MODULE != 0
-        memory->owner = module_inst;
-#endif
     }
 
     if (mem_index == 0) {
@@ -1007,15 +1001,17 @@ sub_module_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
         bh_list_first_elem(module->import_module_list);
 
     while (sub_module_list_node) {
-        WASMSubModInstNode *sub_module_inst_list_node;
+        WASMSubModInstNode *sub_module_inst_list_node = NULL;
         WASMModule *sub_module = (WASMModule *)sub_module_list_node->module;
-        WASMModuleInstance *sub_module_inst =
+        WASMModuleInstance *sub_module_inst = NULL;
+
+        sub_module_inst =
             wasm_instantiate(sub_module, false, stack_size, heap_size,
                              error_buf, error_buf_size);
         if (!sub_module_inst) {
             LOG_DEBUG("instantiate %s failed",
                       sub_module_list_node->module_name);
-            return false;
+            goto failed;
         }
 
         sub_module_inst_list_node = runtime_malloc(sizeof(WASMSubModInstNode),
@@ -1023,8 +1019,7 @@ sub_module_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
         if (!sub_module_inst_list_node) {
             LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ:%d",
                       sizeof(WASMSubModInstNode));
-            wasm_deinstantiate(sub_module_inst, false);
-            return false;
+            goto failed;
         }
 
         sub_module_inst_list_node->module_inst = sub_module_inst;
@@ -1036,6 +1031,39 @@ sub_module_instantiate(WASMModule *module, WASMModuleInstance *module_inst,
         (void)ret;
 
         sub_module_list_node = bh_list_elem_next(sub_module_list_node);
+
+#if WASM_ENABLE_LIBC_WASI != 0
+        {
+            /*
+             * reactor instances may assume that _initialize will be called by
+             * the environment at most once, and that none of their other
+             * exports are accessed before that call.
+             *
+             * let the loader decide how to act if there is no _initialize
+             * in a reactor
+             */
+            WASMFunctionInstance *initialize =
+                wasm_lookup_function(sub_module_inst, "_initialize", NULL);
+            if (initialize
+                && !wasm_create_exec_env_and_call_function(
+                    sub_module_inst, initialize, 0, NULL, false)) {
+                set_error_buf(error_buf, error_buf_size,
+                              "Call _initialize failed ");
+                goto failed;
+            }
+        }
+#endif
+
+        continue;
+    failed:
+        if (sub_module_inst_list_node) {
+            bh_list_remove(sub_module_inst_list, sub_module_inst_list_node);
+            wasm_runtime_free(sub_module_inst_list_node);
+        }
+
+        if (sub_module_inst)
+            wasm_deinstantiate(sub_module_inst, false);
+        return false;
     }
 
     return true;
@@ -1518,7 +1546,7 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
 
 #if WASM_ENABLE_BULK_MEMORY != 0
 #if WASM_ENABLE_LIBC_WASI != 0
-    if (!module->is_wasi_module) {
+    if (!module->import_wasi_api) {
 #endif
         /* Only execute the memory init function for main instance because
             the data segments will be dropped once initialized.

+ 0 - 5
core/iwasm/interpreter/wasm_runtime.h

@@ -40,11 +40,6 @@ struct WASMMemoryInstance {
     /* The heap created */
     void *heap_handle;
 
-#if WASM_ENABLE_MULTI_MODULE != 0
-    /* to indicate which module instance create it */
-    WASMModuleInstance *owner;
-#endif
-
 #if WASM_ENABLE_SHARED_MEMORY != 0
     /* mutex lock for the memory, used in atomic operation */
     korp_mutex mem_lock;

+ 75 - 4
samples/multi-module/CMakeLists.txt

@@ -1,7 +1,7 @@
 # Copyright (C) 2019 Intel Corporation.  All rights reserved.
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required (VERSION 2.8...3.16)
 project(multi_module)
 
 ################  runtime settings  ################
@@ -41,7 +41,7 @@ set(WAMR_BUILD_INTERP 1)
 set(WAMR_BUILD_AOT 0)
 set(WAMR_BUILD_JIT 0)
 set(WAMR_BUILD_LIBC_BUILTIN 1)
-set(WAMR_BUILD_LIBC_WASI 0)
+set(WAMR_BUILD_LIBC_WASI 1)
 set(WAMR_BUILD_MULTI_MODULE 1)
 
 # compiling and linking flags
@@ -66,8 +66,79 @@ add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE})
 ################  application related  ################
 
 ################ WASM MODULES
+include(ExternalProject)
+
+message(CHECK_START "Detecting WASI-SDK")
+if(NOT (DEFINED WASI_SDK_DIR OR DEFINED CACHE{WASI_SDK_DIR}))
+  find_path(WASI_SDK_PARENT
+    wasi-sdk
+    PATHS /opt
+    NO_DEFAULT_PATH
+    NO_CMAKE_FIND_ROOT_PATH
+  )
+  if(WASI_SDK_PARENT)
+    set(WASI_SDK_DIR ${WASI_SDK_PARENT}/wasi-sdk)
+  endif()
+endif()
+if(WASI_SDK_DIR)
+  message(CHECK_PASS "found")
+else()
+  message(CHECK_FAIL "not found")
+endif()
+
+message(CHECK_START "Detecting WASI_TOOLCHAIN_FILE at ${WASI_SDK_DIR}")
+find_file(WASI_TOOLCHAIN_FILE
+  wasi-sdk.cmake
+  PATHS "${WASI_SDK_DIR}/share/cmake"
+  NO_DEFAULT_PATH
+  NO_CMAKE_FIND_ROOT_PATH
+)
+if(WASI_TOOLCHAIN_FILE)
+  message(CHECK_PASS "found")
+else()
+  message(CHECK_FAIL "not found")
+endif()
+
+message(CHECK_START "Detecting WASI_SYS_ROOT at ${WASI_SDK_DIR}")
+find_path(WASI_SYS_ROOT
+  wasi-sysroot
+  PATHS "${WASI_SDK_DIR}/share"
+  NO_DEFAULT_PATH
+  NO_CMAKE_FIND_ROOT_PATH
+)
+if(WASI_SYS_ROOT)
+  message(CHECK_PASS "found")
+  set(WASI_SYS_ROOT ${WASI_SYS_ROOT}/wasi-sysroot)
+else()
+  message(CHECK_FAIL "not found")
+endif()
+
+if(NOT EXISTS ${WASI_SDK_DIR} OR NOT EXISTS ${WASI_TOOLCHAIN_FILE} OR NOT EXISTS ${WASI_SYS_ROOT})
+  message(FATAL_ERROR "Please set the absolute path of wasi-sdk with \'cmake -DWASI_SDK_HOME=XXX\'")
+else()
+  message(STATUS "WASI_SDK_DIR is ${WASI_SDK_DIR}")
+  message(STATUS "WASI_TOOLCHAIN_FILE is ${WASI_TOOLCHAIN_FILE}")
+  message(STATUS "WASI_SYS_ROOT is ${WASI_SYS_ROOT}")
+endif()
+
 # .c -> .wasm
-add_subdirectory(wasm-apps)
+ExternalProject_Add(WASM_MODULE
+  SOURCE_DIR         ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
+  UPDATE_COMMAND     ""
+  PATCH_COMMAND      ""
+  CONFIGURE_COMMAND  ${CMAKE_COMMAND}
+                       -DWASI_SDK_PREFIX=${WASI_SDK_DIR}
+                       -DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE}
+                       -DCMAKE_SYSROOT=${WASI_SYS_ROOT}
+                       -S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
+  BUILD_COMMAND      ${CMAKE_COMMAND} --build .
+  INSTALL_COMMAND    ${CMAKE_COMMAND} -E copy
+                       ./mA.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
+                       ./mB.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
+                       ./mC.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
+                       ./mD.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
+                       ./mE.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
+)
 
 ################ NATIVE
 include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
@@ -75,7 +146,7 @@ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
 
 add_executable(multi_module src/main.c ${UNCOMMON_SHARED_SOURCE})
 
-add_dependencies(multi_module vmlib wasm-modules)
+add_dependencies(multi_module vmlib WASM_MODULE)
 
 # libraries
 target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm)

+ 30 - 18
samples/multi-module/src/main.c

@@ -9,7 +9,7 @@
 static char *
 build_module_path(const char *module_name)
 {
-    const char *module_search_path = "./wasm-apps";
+    const char *module_search_path = ".";
     const char *format = "%s/%s.wasm";
     int sz = strlen(module_search_path) + strlen("/") + strlen(module_name)
              + strlen(".wasm") + 1;
@@ -107,24 +107,36 @@ main()
         goto UNLOAD_MODULE;
     }
 
-    /* call some functions of mC */
+    /* call functions of mC */
     printf("\n----------------------------------------\n");
-    printf("call \"C\", it will return 0xc:i32, ===> ");
-    wasm_application_execute_func(module_inst, "C", 0, &args[0]);
-    printf("call \"call_B\", it will return 0xb:i32, ===> ");
-    wasm_application_execute_func(module_inst, "call_B", 0, &args[0]);
-    printf("call \"call_A\", it will return 0xa:i32, ===>");
-    wasm_application_execute_func(module_inst, "call_A", 0, &args[0]);
-
-    /* call some functions of mB */
-    printf("call \"mB.B\", it will return 0xb:i32, ===>");
-    wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]);
-    printf("call \"mB.call_A\", it will return 0xa:i32, ===>");
-    wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]);
-
-    /* call some functions of mA */
-    printf("call \"mA.A\", it will return 0xa:i32, ===>");
-    wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]);
+    printf("call \"C1\", it will return 0x1f:i32, ===> ");
+    wasm_application_execute_func(module_inst, "C1", 0, args);
+    printf("call \"C2\", it will call B1() of mB and return 0x15:i32, ===> ");
+    wasm_application_execute_func(module_inst, "C2", 0, args);
+    printf("call \"C3\", it will call A1() of mA and return 0xb:i32, ===> ");
+    wasm_application_execute_func(module_inst, "C3", 0, args);
+    printf("call \"C4\", it will call B2() of mB and call A1() of mA and "
+           "return 0xb:i32, ===> ");
+    wasm_application_execute_func(module_inst, "C4", 0, args);
+    printf(
+        "call \"C5\", it will be failed since it is a export function, ===> ");
+    wasm_application_execute_func(module_inst, "C5", 0, args);
+
+    /* call functions of mB */
+    printf("call \"mB.B1\", it will return 0x15:i32, ===> ");
+    wasm_application_execute_func(module_inst, "$mB$B1", 0, args);
+    printf("call \"mB.B2\", it will call A1() of mA and return 0xb:i32, ===> ");
+    wasm_application_execute_func(module_inst, "$mB$B2", 0, args);
+    printf("call \"mB.B3\", it will be failed since it is a export function, "
+           "===> ");
+    wasm_application_execute_func(module_inst, "$mB$B3", 0, args);
+
+    /* call functions of mA */
+    printf("call \"mA.A1\", it will return 0xb:i32, ===>");
+    wasm_application_execute_func(module_inst, "$mA$A1", 0, args);
+    printf("call \"mA.A2\", it will be failed since it is a export function, "
+           "===> ");
+    wasm_application_execute_func(module_inst, "$mA$A2", 0, args);
     printf("----------------------------------------\n\n");
     ret = true;
 

+ 81 - 33
samples/multi-module/wasm-apps/CMakeLists.txt

@@ -1,41 +1,89 @@
-cmake_minimum_required(VERSION 2.8)
-project(wasm-apps)
-
-set(CMAKE_VERBOSE_MAKEFILE on)
+# Copyright (C) 2019 Intel Corporation.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
-set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
-set(CLANG_COMMAND "/opt/wasi-sdk/bin/clang")
+cmake_minimum_required (VERSION 2.8...3.16)
+project(wasm-apps)
 
-set(CLANG_FLAGS --target=wasm32 -nostdlib)
-set(CLANG_FLAGS ${CLANG_FLAGS} -Wl,--no-entry,--allow-undefined,--export-all)
+message(CHECK_START "Detecting WABT")
+if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR}))
+  find_path(WABT_DIR
+    wabt
+    PATHS /opt
+    NO_DEFAULT_PATH
+    NO_CMAKE_FIND_ROOT_PATH
+  )
+  if(DEFINED WABT_DIR)
+    set(WABT_DIR ${WABT_DIR}/wabt)
+  endif()
+endif()
+if(WABT_DIR)
+  message(CHECK_PASS "found")
+else()
+  message(CHECK_FAIL "not found")
+endif()
 
-set(SOURCE_A ${CMAKE_CURRENT_SOURCE_DIR}/mA.c)
-add_custom_command(
-  OUTPUT mA.wasm
-  COMMENT "Transform mA.C to mA.WASM"
-  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mA.wasm ${SOURCE_A}
-  DEPENDS ${SOURCE_A}
-  VERBATIM
+message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}")
+find_program(WASM_OBJDUMP
+  wasm-objdump
+  PATHS "${WABT_DIR}/bin"
+  NO_DEFAULT_PATH
+  NO_CMAKE_FIND_ROOT_PATH
 )
+if(WASM_OBJDUMP)
+  message(CHECK_PASS "found")
+else()
+  message(CHECK_FAIL "not found")
+endif()
 
-set(SOURCE_B ${CMAKE_CURRENT_SOURCE_DIR}/mB.c)
-add_custom_command(
-  OUTPUT mB.wasm
-  COMMENT "Transform mB.C to mB.WASM"
-  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mB.wasm ${SOURCE_B}
-  DEPENDS ${SOURCE_B}
-  VERBATIM
+message(CHECK_START "Detecting WASM2WAT at ${WABT_DIR}")
+find_program(WASM2WAT
+  wasm2wat
+  PATHS "${WABT_DIR}/bin"
+  NO_DEFAULT_PATH
+  NO_CMAKE_FIND_ROOT_PATH
 )
+if(WASM2WAT)
+  message(CHECK_PASS "found")
+else()
+  message(CHECK_FAIL "not found")
+endif()
 
-set(SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/mC.c)
-add_custom_command(
-  OUTPUT mC.wasm
-  COMMENT "Transform mC.C to mC.WASM"
-  COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mC.wasm ${SOURCE_C}
-  DEPENDS ${SOURCE_C}
-  VERBATIM
-)
+function(COMPILE_WITH_CLANG SOURCE_FILE COMMAND)
+  get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WLE)
+
+  set(WASM_MODULE ${FILE_NAME}.wasm)
+
+  set(MAIN_TARGET_NAME MODULE_${FILE_NAME})
+
+  add_executable(${MAIN_TARGET_NAME} ${SOURCE_FILE})
+  set_target_properties(${MAIN_TARGET_NAME} PROPERTIES OUTPUT_NAME ${WASM_MODULE})
+
+  if(${COMMAND})
+    message(STATUS "Generating ${WASM_MODULE} as COMMAND...")
+  else()
+    message(STATUS "Generating ${WASM_MODULE} as REACTOR...")
+    target_link_options(${MAIN_TARGET_NAME} PRIVATE -mexec-model=reactor)
+  endif()
+
+  if(EXISTS ${WASM2WAT})
+    message(STATUS "Dumping ${WASM_MODULE}...")
+    set(WASM_WAT ${FILE_NAME}.wat)
+    set(DUMP_TARGET_NAME DUMP_${FILE_NAME})
+    add_custom_command(OUTPUT ${WASM_WAT}
+      COMMAND ${WASM2WAT} --enable-all -o ${WASM_WAT} ${WASM_MODULE}
+      COMMENT "Dumping ${WASM_MODULE}..."
+      DEPENDS ${MAIN_TARGET_NAME}
+    )
+
+    add_custom_target(${DUMP_TARGET_NAME} ALL
+      DEPENDS ${WASM_WAT}
+    )
+  endif()
+endfunction()
+
+compile_with_clang(mA.c OFF)
+compile_with_clang(mB.c OFF)
+compile_with_clang(mC.c ON)
+compile_with_clang(mD.cpp ON)
+compile_with_clang(mE.cpp OFF)
 
-add_custom_target(wasm-modules ALL
-  DEPENDS mA.wasm mB.wasm mC.wasm
-)

+ 11 - 3
samples/multi-module/wasm-apps/mA.c

@@ -1,5 +1,13 @@
+__attribute__((export_name("A1"))) int
+A1()
+{
+    return 11;
+}
+
 int
-A()
+A2()
 {
-    return 10;
-}
+    return 12;
+}
+
+/* mA is a  reactor. it doesn't need a main() */

+ 15 - 7
samples/multi-module/wasm-apps/mB.c

@@ -1,15 +1,23 @@
 __attribute__((import_module("mA")))
-__attribute__((import_name("A"))) extern int
-A();
+__attribute__((import_name("A1"))) extern int
+A1();
 
-int
-B()
+__attribute__((export_name("B1"))) int
+B1()
 {
-    return 11;
+    return 21;
+}
+
+__attribute__((export_name("B2"))) int
+B2()
+{
+    return A1();
 }
 
 int
-call_A()
+B3()
 {
-    return A();
+    return 23;
 }
+
+/* mA is a  reactor. it doesn't need a main() */

+ 37 - 11
samples/multi-module/wasm-apps/mC.c

@@ -1,25 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+
 __attribute__((import_module("mA")))
-__attribute__((import_name("A"))) extern int
-A();
+__attribute__((import_name("A1"))) extern int
+A1();
 
 __attribute__((import_module("mB")))
-__attribute__((import_name("B"))) extern int
-B();
+__attribute__((import_name("B1"))) extern int
+B1();
 
-int
-C()
+__attribute__((import_module("mB")))
+__attribute__((import_name("B2"))) extern int
+B2();
+
+__attribute__((export_name("C1"))) int
+C1()
+{
+    return 31;
+}
+
+__attribute__((export_name("C2"))) int
+C2()
+{
+    return B1();
+}
+
+__attribute__((export_name("C3"))) int
+C3()
+{
+    return A1();
+}
+
+__attribute__((export_name("C4"))) int
+C4()
 {
-    return 12;
+    return B2();
 }
 
 int
-call_A()
+C5()
 {
-    return A();
+    return C1() + C2() + C3() + 35;
 }
 
 int
-call_B()
+main()
 {
-    return B();
+    printf("%u\n", C5());
+    return EXIT_SUCCESS;
 }

+ 74 - 0
samples/multi-module/wasm-apps/mD.cpp

@@ -0,0 +1,74 @@
+#include <cstdlib>
+#include <cstdio>
+#include <iostream>
+
+static void
+bye_main()
+{
+    std::cout << "mD " << __FUNCTION__ << std::endl;
+}
+
+static void
+bye_setup()
+{
+    std::cout << "mD " << __FUNCTION__ << std::endl;
+}
+
+static void
+bye_func()
+{
+    std::cout << "mD " << __FUNCTION__ << std::endl;
+}
+
+void
+func3() __attribute__((__import_module__("mE"), __import_name__("func1")));
+
+void
+func4() __attribute__((__import_module__("mE"), __import_name__("func2")));
+
+void
+func1()
+{
+    std::printf("mD %s\n", __FUNCTION__);
+    if (std::atexit(bye_func) != 0) {
+        std::perror("register an atexit handler failed");
+    }
+    func3();
+}
+
+void
+func2()
+{
+    std::printf("mD %s\n", __FUNCTION__);
+    func4();
+}
+
+__attribute__((constructor)) void
+setup()
+{
+    std::cout << "mD " << __FUNCTION__ << std::endl;
+    if (std::atexit(bye_setup) != 0) {
+        std::perror("register an atexit handler failed");
+    }
+}
+
+__attribute__((destructor)) void
+teardown()
+{
+    std::cout << "mD " << __FUNCTION__ << std::endl;
+}
+
+int
+main()
+{
+    std::printf("mD %s\n", __FUNCTION__);
+
+    if (std::atexit(bye_main) != 0) {
+        std::perror("register an atexit handler failed");
+        return EXIT_FAILURE;
+    }
+
+    func1();
+    func2();
+    return EXIT_SUCCESS;
+}

+ 45 - 0
samples/multi-module/wasm-apps/mE.cpp

@@ -0,0 +1,45 @@
+#include <cstdlib>
+#include <cstdio>
+#include <iostream>
+
+static void
+bye_setup()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+}
+
+static void
+bye_func()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+}
+
+__attribute__((constructor)) void
+setup()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+    if (std::atexit(bye_setup) != 0) {
+        std::perror("register an atexit handler failed");
+    }
+}
+
+__attribute__((destructor)) void
+teardown()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+}
+
+__attribute__((export_name("func1"))) void
+func1()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+    if (std::atexit(bye_func) != 0) {
+        std::perror("register an atexit handler failed");
+    }
+}
+
+__attribute__((export_name("func2"))) void
+func2()
+{
+    std::cout << "mE " << __FUNCTION__ << std::endl;
+}