diff --git a/_clang-format b/.clang-format similarity index 78% rename from _clang-format rename to .clang-format index c99997022..f1f14f519 100644 --- a/_clang-format +++ b/.clang-format @@ -1,13 +1,47 @@ ---- +RawStringFormats: + - Language: Cpp + Delimiters: + - c + - C + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + - h + - hpp + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google + +Language: Cpp BasedOnStyle: Mozilla IndentWidth: 4 - ---- -Language: Cpp +AlignAfterOpenBracket: Align +AllowAllArgumentsOnNextLine: false AlignConsecutiveMacros: true AllowShortBlocksOnASingleLine: true +AlwaysBreakAfterReturnType: All BinPackArguments: true -BinPackParameters: true +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BraceWrapping: AfterCaseLabel: false @@ -17,16 +51,17 @@ BraceWrapping: AfterFunction: true AfterNamespace: false AfterObjCDeclaration: false - AfterStruct: true + AfterStruct: false AfterUnion: false - AfterExternBlock: true + AfterExternBlock: false BeforeCatch: false - BeforeElse: false + BeforeElse: true IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: false SplitEmptyNamespace: true ColumnLimit: 79 +ContinuationIndentWidth: 2 DerivePointerAlignment: false IncludeBlocks: Regroup IncludeCategories: @@ -36,20 +71,22 @@ IncludeCategories: Priority: 1 - Regex: ".*" Priority: 3 +IndentPPDirectives: None +KeepEmptyLinesAtTheStartOfBlocks: false +NamespaceIndentation: None PointerAlignment: Right ReflowComments: false -Standard: Cpp03 +Standard: Auto StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION + # AccessModifierOffset: -2 -# AlignAfterOpenBracket: Align # AlignConsecutiveAssignments: false # AlignConsecutiveDeclarations: false # AlignEscapedNewlines: Right # AlignOperands: true # AlignTrailingComments: true -# AllowAllArgumentsOnNextLine: true # AllowAllConstructorInitializersOnNextLine: true # AllowAllParametersOfDeclarationOnNextLine: false # AllowShortCaseLabelsOnASingleLine: false @@ -61,7 +98,6 @@ StatementMacros: # AlwaysBreakAfterReturnType: TopLevel # AlwaysBreakBeforeMultilineStrings: false # AlwaysBreakTemplateDeclarations: Yes -# BreakBeforeBinaryOperators: None # BreakBeforeInheritanceComma: false # BreakInheritanceList: BeforeComma # BreakBeforeTernaryOperators: true @@ -73,7 +109,6 @@ StatementMacros: # CompactNamespaces: false # ConstructorInitializerAllOnOneLineOrOnePerLine: false # ConstructorInitializerIndentWidth: 2 -# ContinuationIndentWidth: 2 # Cpp11BracedListStyle: false # DisableFormat: false # ExperimentalAutoDetectBinPacking: false @@ -84,7 +119,6 @@ StatementMacros: # - BOOST_FOREACH # IncludeIsMainRegex: '(Test)?$' # IndentCaseLabels: true -# IndentPPDirectives: None # IndentWrappedFunctionNames: false # JavaScriptQuotes: Leave # JavaScriptWrapImports: true @@ -92,7 +126,6 @@ StatementMacros: # MacroBlockBegin: '' # MacroBlockEnd: '' # MaxEmptyLinesToKeep: 1 -# NamespaceIndentation: None # ObjCBinPackProtocolList: Auto # ObjCBlockIndentWidth: 2 # ObjCSpaceAfterProperty: true diff --git a/.gitignore b/.gitignore index 0b2d07118..ef08be476 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,11 @@ core/deps/lv_drivers core/deps/llvm core/deps/lvgl +core/deps/tlsf core/shared/mem-alloc/tlsf +core/app-framework/wgl wamr-sdk/out/ wamr-sdk/runtime/build_runtime_sdk/ test-tools/host-tool/bin/ +product-mini/app-samples/hello-world/test.wasm \ No newline at end of file diff --git a/README.md b/README.md index be93b8e50..bdb707c86 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,17 @@ iwasm VM core - 100% compliant to the W3C WASM MVP - Small runtime binary size (85K for interpreter and 50K for AoT) and low memory usage -- Near to native speed by AoT +- Near to native speed by AoT - Self-implemented module loader enables AoT working cross Linux, SGX and MCU systems - Choices of WASM application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc - [Embeddable with the supporting C API's](./doc/embed_wamr.md) - [The mechanism for exporting native API's to WASM applications](./doc/export_native_api.md) +- [Multiple modules as dependencies](./doc/multi_module.md) + +### post-MVP features +- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) +- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops) +- [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) ### Performance and memory usage The WAMR performance, footprint and memory usage data are available at the [performance](../../wiki/Performance) wiki page. diff --git a/assembly-script/.gitignore b/assembly-script/.gitignore new file mode 100644 index 000000000..07e6e472c --- /dev/null +++ b/assembly-script/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index dc8f59d3f..6187910d8 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -123,8 +123,20 @@ else () add_definitions (-DWASM_ENABLE_FAST_INTERP=0) message (" Fast interpreter disabled") endif () - +if (WAMR_BUILD_MULTI_MODULE EQUAL 1) + add_definitions (-DWASM_ENABLE_MULTI_MODULE=1) + message (" Multiple modules enabled") +else () + add_definitions (-DWASM_ENABLE_MULTI_MODULE=0) + message (" Multiple modules disabled") +endif () if (WAMR_BUILD_SPEC_TEST EQUAL 1) add_definitions (-DWASM_ENABLE_SPEC_TEST=1) message (" spec test compatible mode is on") endif() +if (WAMR_BUILD_BULK_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_BULK_MEMORY=1) + message (" Bulk memory feature enabled") +else () + add_definitions (-DWASM_ENABLE_BULK_MEMORY=0) +endif() diff --git a/core/app-framework/app-native-shared/bi-inc/attr_container.h b/core/app-framework/app-native-shared/bi-inc/attr_container.h index d8fc08e4b..e70d0e4c4 100644 --- a/core/app-framework/app-native-shared/bi-inc/attr_container.h +++ b/core/app-framework/app-native-shared/bi-inc/attr_container.h @@ -6,7 +6,7 @@ #ifndef _ATTR_CONTAINER_H_ #define _ATTR_CONTAINER_H_ -#include +#include #include #include #include diff --git a/core/app-framework/app-native-shared/restful_utils.c b/core/app-framework/app-native-shared/restful_utils.c index 4907df017..5268b22a7 100644 --- a/core/app-framework/app-native-shared/restful_utils.c +++ b/core/app-framework/app-native-shared/restful_utils.c @@ -71,23 +71,18 @@ void free_req_resp_packet(char * packet) request_t * unpack_request(char * packet, int size, request_t * request) { if (*packet != REQUES_PACKET_VER) { - printf("version fail\n"); return NULL; } if (size < REQUEST_PACKET_FIX_PART_LEN) { - printf("size error: %d\n", size); return NULL; } uint16 url_len = ntohs(*((uint16*) (packet + 12))); uint32 payload_len = ntohl(*((uint32*) (packet + 14))); if (size != ( REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) { - printf("size error: %d, expect: %d\n", size, - REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len); return NULL; } if (*(packet + REQUEST_PACKET_FIX_PART_LEN + url_len - 1) != 0) { - printf("url not end with 0\n"); return NULL; } diff --git a/core/app-mgr/app-manager/app_manager_host.c b/core/app-mgr/app-manager/app_manager_host.c index c98a2c8ad..d9024a346 100644 --- a/core/app-mgr/app-manager/app_manager_host.c +++ b/core/app-mgr/app-manager/app_manager_host.c @@ -246,7 +246,7 @@ bool app_manager_host_init(host_interface *interface) return true; } -int app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size) +int app_manager_host_send_msg(int msg_type, const char *buf, int size) { /* send an IMRT LINK message contains the buf as payload */ if (host_commu.send != NULL) { @@ -276,10 +276,10 @@ int app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size) n = host_commu.send(NULL, buf, size); os_mutex_unlock(&host_lock); - printf("sent %d bytes to host\n", n); + app_manager_printf("sent %d bytes to host\n", n); return n; } else { - printf("no send api provided\n"); + app_manager_printf("no send api provided\n"); } return 0; } diff --git a/core/app-mgr/app-manager/module_wasm_app.c b/core/app-mgr/app-manager/module_wasm_app.c index 18f877e9c..f58580b6e 100644 --- a/core/app-mgr/app-manager/module_wasm_app.c +++ b/core/app-mgr/app-manager/module_wasm_app.c @@ -445,7 +445,7 @@ wasm_app_routine(void *arg) 0, NULL)) { const char *exception = wasm_runtime_get_exception(inst); bh_assert(exception); - printf("Got exception running wasi start function: %s\n", + app_manager_printf("Got exception running wasi start function: %s\n", exception); wasm_runtime_clear_exception(inst); goto fail1; @@ -467,7 +467,7 @@ wasm_app_routine(void *arg) 0, NULL)) { const char *exception = wasm_runtime_get_exception(inst); bh_assert(exception); - printf("Got exception running WASM code: %s\n", + app_manager_printf("Got exception running WASM code: %s\n", exception); wasm_runtime_clear_exception(inst); /* call on_destroy() in case some resources are opened in on_init() @@ -644,7 +644,7 @@ wasm_app_module_install(request_t * msg) if (!module) { SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: load WASM file failed."); - printf("error: %s\n", err); + app_manager_printf("error: %s\n", err); destroy_all_aot_sections(aot_file->sections); return false; } @@ -674,7 +674,7 @@ wasm_app_module_install(request_t * msg) if (!inst) { SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: instantiate wasm runtime failed."); - printf("error: %s\n", err); + app_manager_printf("error: %s\n", err); wasm_runtime_unload(module); destroy_all_aot_sections(aot_file->sections); return false; @@ -713,7 +713,7 @@ wasm_app_module_install(request_t * msg) if (!module) { SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: load WASM file failed."); - printf("error: %s\n", err); + app_manager_printf("error: %s\n", err); destroy_all_wasm_sections(bytecode_file->sections); return false; } @@ -744,7 +744,7 @@ wasm_app_module_install(request_t * msg) if (!inst) { SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: instantiate wasm runtime failed."); - printf("error: %s\n", err); + app_manager_printf("error: %s\n", err); wasm_runtime_unload(module); destroy_all_wasm_sections(bytecode_file->sections); return false; diff --git a/core/app-mgr/app-mgr-shared/app_manager_export.h b/core/app-mgr/app-mgr-shared/app_manager_export.h index 877a2df55..4e29b1f92 100644 --- a/core/app-mgr/app-mgr-shared/app_manager_export.h +++ b/core/app-mgr/app-mgr-shared/app_manager_export.h @@ -285,7 +285,7 @@ bool bh_applet_check_permission(const char *perm); int -app_manager_host_send_msg(int msg_type, const unsigned char *buf, int size); +app_manager_host_send_msg(int msg_type, const char *buf, int size); #ifdef __cplusplus } /* end of extern "C" */ diff --git a/core/config.h b/core/config.h index 25d546c33..e54a4be0c 100644 --- a/core/config.h +++ b/core/config.h @@ -94,6 +94,11 @@ enum { #define WASM_ENABLE_APP_FRAMEWORK 0 #endif +/* Bulk memory operation */ +#ifndef WASM_ENABLE_BULK_MEMORY +#define WASM_ENABLE_BULK_MEMORY 0 +#endif + /* WASM log system */ #ifndef WASM_ENABLE_LOG #define WASM_ENABLE_LOG 1 @@ -120,6 +125,11 @@ enum { #define WASM_ENABLE_OPCODE_COUNTER 0 #endif +/* Support a module with dependency, other modules */ +#ifndef WASM_ENABLE_MULTI_MODULE +#define WASM_ENABLE_MULTI_MODULE 0 +#endif + /* Heap and stack profiling */ #define BH_ENABLE_MEMORY_PROFILING 0 diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 560030d56..ab043afe1 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -363,6 +363,11 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end, for (i = 0; i < module->mem_init_data_count; i++) { uint32 init_expr_type, byte_count; uint64 init_expr_value; + uint32 is_passive; + uint32 memory_index; + + read_uint32(buf, buf_end, is_passive); + read_uint32(buf, buf_end, memory_index); read_uint32(buf, buf_end, init_expr_type); read_uint64(buf, buf_end, init_expr_value); read_uint32(buf, buf_end, byte_count); @@ -375,6 +380,11 @@ load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end, return false; } +#if WASM_ENABLE_BULK_MEMORY != 0 + /* is_passive and memory_index is only used in bulk memory mode */ + data_list[i]->is_passive = (bool)is_passive; + data_list[i]->memory_index = memory_index; +#endif data_list[i]->offset.init_expr_type = (uint8)init_expr_type; data_list[i]->offset.u.i64 = (int64)init_expr_value; data_list[i]->byte_count = byte_count; @@ -773,8 +783,7 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, read_uint16(buf, buf_end, import_funcs[i].func_type_index); if (import_funcs[i].func_type_index >= module->func_type_count) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function type index."); + "AOT module load failed: unknown type."); return false; } import_funcs[i].func_type = module->func_types[import_funcs[i].func_type_index]; @@ -1067,8 +1076,7 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, read_uint32(p, p_end, module->func_type_indexes[i]); if (module->func_type_indexes[i] >= module->func_type_count) { set_error_buf(error_buf, error_buf_size, - "AOT module load failed: " - "invalid function type index."); + "AOT module load failed: unknown type."); return false; } } diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index c98a58e26..198880850 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -12,6 +12,30 @@ typedef struct { #define REG_SYM(symbol) { #symbol, (void*)symbol } +#if WASM_ENABLE_BULK_MEMORY != 0 +#define REG_COMMON_SYMBOLS \ + REG_SYM(aot_set_exception_with_id), \ + REG_SYM(aot_invoke_native), \ + REG_SYM(aot_call_indirect), \ + REG_SYM(wasm_runtime_enlarge_memory), \ + REG_SYM(wasm_runtime_set_exception), \ + REG_SYM(fmin), \ + REG_SYM(fminf), \ + REG_SYM(fmax), \ + REG_SYM(fmaxf), \ + REG_SYM(ceil), \ + REG_SYM(ceilf), \ + REG_SYM(floor), \ + REG_SYM(floorf), \ + REG_SYM(trunc), \ + REG_SYM(truncf), \ + REG_SYM(rint), \ + REG_SYM(rintf), \ + REG_SYM(memset), \ + REG_SYM(memmove), \ + REG_SYM(aot_memory_init), \ + REG_SYM(aot_data_drop) +#else #define REG_COMMON_SYMBOLS \ REG_SYM(aot_set_exception_with_id), \ REG_SYM(aot_invoke_native), \ @@ -30,6 +54,7 @@ typedef struct { REG_SYM(truncf), \ REG_SYM(rint), \ REG_SYM(rintf) +#endif #define CHECK_RELOC_OFFSET(data_size) do { \ if (!check_reloc_offset(target_section_size, reloc_offset, data_size, \ diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index d64c1f1ba..d42989d21 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -68,42 +68,62 @@ table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, uint32 i, global_index, global_data_offset, base_offset, length; AOTTableInitData *table_seg; - if (module->table_init_data_count > 0) { - for (i = 0; i < module->table_init_data_count; i++) { - table_seg = module->table_init_data_list[i]; - bh_assert(table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); + for (i = 0; i < module->table_init_data_count; i++) { + table_seg = module->table_init_data_list[i]; + bh_assert(table_seg->offset.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + || table_seg->offset.init_expr_type == + INIT_EXPR_TYPE_GET_GLOBAL); - /* Resolve table data base offset */ - if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - global_index = table_seg->offset.u.global_index; - bh_assert(global_index < - module->import_global_count + module->global_count); - /* TODO: && globals[table_seg->offset.u.global_index].type == - VALUE_TYPE_I32*/ - if (global_index < module->import_global_count) - global_data_offset = - module->import_globals[global_index].data_offset; - else - global_data_offset = - module->globals[global_index - module->import_global_count] - .data_offset; - - base_offset = *(uint32*) - ((uint8*)module_inst->global_data.ptr + global_data_offset); - } + /* Resolve table data base offset */ + if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = table_seg->offset.u.global_index; + bh_assert(global_index < + module->import_global_count + module->global_count); + /* TODO: && globals[table_seg->offset.u.global_index].type == + VALUE_TYPE_I32*/ + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; else - base_offset = (uint32)table_seg->offset.u.i32; + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; - /* Copy table data */ - length = table_seg->func_index_count; - if (base_offset < module_inst->table_size) { - memcpy((uint32*)module_inst->table_data.ptr + base_offset, - table_seg->func_indexes, length * sizeof(uint32)); - } + base_offset = *(uint32*) + ((uint8*)module_inst->global_data.ptr + global_data_offset); } + else + base_offset = (uint32)table_seg->offset.u.i32; + + /* Copy table data */ + bh_assert(module_inst->table_data.ptr); + /* base_offset only since length might negative */ + if (base_offset > module_inst->table_size) { + LOG_DEBUG("base_offset(%d) > table_size(%d)", base_offset, + module_inst->table_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + return false; + } + + /* base_offset + length(could be zero) */ + length = table_seg->func_index_count; + if (base_offset + length > module_inst->table_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > table_size(%d)", + base_offset, length, module_inst->table_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + return false; + } + + /** + * Check function index in the current module inst for now. + * will check the linked table inst owner in future + */ + memcpy((uint32 *)module_inst->table_data.ptr + base_offset, + table_seg->func_indexes, + length * sizeof(uint32)); } return true; @@ -169,50 +189,64 @@ memory_instantiate(AOTModuleInstance *module_inst, AOTModule *module, module_inst->mem_bound_check_8bytes = module_inst->total_mem_size - 8; } - if (module->mem_init_page_count > 0) { - for (i = 0; i < module->mem_init_data_count; i++) { - data_seg = module->mem_init_data_list[i]; - bh_assert(data_seg->offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || data_seg->offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); + for (i = 0; i < module->mem_init_data_count; i++) { + data_seg = module->mem_init_data_list[i]; +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif - /* Resolve memory data base offset */ - if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - global_index = data_seg->offset.u.global_index; - bh_assert(global_index < - module->import_global_count + module->global_count); - /* TODO: && globals[data_seg->offset.u.global_index].type == - VALUE_TYPE_I32*/ - if (global_index < module->import_global_count) - global_data_offset = - module->import_globals[global_index].data_offset; - else - global_data_offset = - module->globals[global_index - module->import_global_count] - .data_offset; + bh_assert(data_seg->offset.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + || data_seg->offset.init_expr_type == + INIT_EXPR_TYPE_GET_GLOBAL); - base_offset = *(uint32*) - ((uint8*)module_inst->global_data.ptr + global_data_offset); - } + /* Resolve memory data base offset */ + if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = data_seg->offset.u.global_index; + bh_assert(global_index < + module->import_global_count + module->global_count); + /* TODO: && globals[data_seg->offset.u.global_index].type == + VALUE_TYPE_I32*/ + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; else - base_offset = (uint32)data_seg->offset.u.i32; + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; - length = data_seg->byte_count; - - /* Check memory data */ - if (length > 0 - && (base_offset >= module_inst->memory_data_size - || base_offset + length > module_inst->memory_data_size)) { - set_error_buf(error_buf, error_buf_size, - "AOT module instantiate failed: data segment out of range."); - goto fail2; - } - - /* Copy memory data */ - memcpy((uint8*)module_inst->memory_data.ptr + base_offset, - data_seg->bytes, length); + base_offset = *(uint32*) + ((uint8*)module_inst->global_data.ptr + global_data_offset); + } else { + base_offset = (uint32)data_seg->offset.u.i32; } + + /* Copy memory data */ + bh_assert(module_inst->memory_data.ptr); + + /* Check memory data */ + /* check offset since length might negative */ + if (base_offset > module_inst->memory_data_size) { + LOG_DEBUG("base_offset(%d) > memory_data_size(%d)", base_offset, + module_inst->memory_data_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + goto fail2; + } + + /* check offset + length(could be zero) */ + length = data_seg->byte_count; + if (base_offset + length > module_inst->memory_data_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%d)", + base_offset, length, module_inst->memory_data_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + goto fail2; + } + + memcpy((uint8*)module_inst->memory_data.ptr + base_offset, + data_seg->bytes, length); } return true; @@ -952,3 +986,61 @@ aot_call_indirect(WASMExecEnv *exec_env, func_type, signature, attachment, argv, argc, argv); } + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, uint32 dst) +{ + AOTModule *aot_module; + uint8 *data = NULL; + uint8 *maddr; + uint64 seg_len = 0; + + aot_module = (AOTModule *)module_inst->aot_module.ptr; + if (aot_module->is_jit_mode) { +#if WASM_ENABLE_JIT != 0 + seg_len = aot_module->wasm_module->data_segments[seg_index]->data_length; + data = aot_module->wasm_module->data_segments[seg_index]->data; +#endif + } + else { + seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; + data = aot_module->mem_init_data_list[seg_index]->bytes; + } + + if (!aot_validate_app_addr(module_inst, dst, len)) + return false; + + if ((uint64)offset + (uint64)len > seg_len) { + aot_set_exception(module_inst, "out of bounds memory access"); + return false; + } + + maddr = aot_addr_app_to_native(module_inst, dst); + + bh_memcpy_s(maddr, module_inst->memory_data_size - dst, + data + offset, len); + return true; +} + +bool +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index) +{ + AOTModule *aot_module = (AOTModule *)(module_inst->aot_module.ptr); + + if (aot_module->is_jit_mode) { +#if WASM_ENABLE_JIT != 0 + aot_module->wasm_module->data_segments[seg_index]->data_length = 0; + /* Currently we can't free the dropped data segment + as they are stored in wasm bytecode */ +#endif + } + else { + aot_module->mem_init_data_list[seg_index]->byte_count = 0; + /* Currently we can't free the dropped data segment + as the mem_init_data_count is a continuous array */ + } + return true; +} +#endif /* WASM_ENABLE_BULK_MEMORY */ diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 41f9b7a85..74d9667e2 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -458,6 +458,15 @@ aot_call_indirect(WASMExecEnv *exec_env, uint32 aot_get_plt_table_size(); +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, uint32 dst); + +bool +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index); +#endif + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index 2a2c41c1c..115296eed 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -243,6 +243,7 @@ wasm_native_init() if (!wasm_native_register_natives("env", native_symbols, n_native_symbols)) return false; +#endif /* WASM_ENABLE_LIBC_BUILTIN */ #if WASM_ENABLE_SPEC_TEST n_native_symbols = get_spectest_export_apis(&native_symbols); @@ -250,7 +251,6 @@ wasm_native_init() native_symbols, n_native_symbols)) return false; #endif /* WASM_ENABLE_SPEC_TEST */ -#endif /* WASM_ENABLE_LIBC_BUILTIN */ #if WASM_ENABLE_LIBC_WASI != 0 n_native_symbols = get_libc_wasi_export_apis(&native_symbols); diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 1db69b462..c3b6da951 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -16,6 +16,43 @@ #include "../aot/aot_runtime.h" #endif +#if WASM_ENABLE_MULTI_MODULE != 0 +/* + * a safety insurance to prevent + * circular depencies leading a stack overflow + * try break early + */ +typedef struct LoadingModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; +} LoadingModule; + +static bh_list loading_module_list_head; +static bh_list *const loading_module_list = &loading_module_list_head; +static korp_mutex loading_module_list_lock; + +/* + * a list about all exported functions, globals, memories, tables of every + * fully loaded module + */ +static bh_list registered_module_list_head; +static bh_list *const registered_module_list = ®istered_module_list_head; +static korp_mutex registered_module_list_lock; +static void +wasm_runtime_destroy_registered_module_list(); +#endif /* WASM_ENABLE_MULTI_MODULE */ + +void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, + ...) +{ + va_list args; + va_start(args, format); + vsnprintf(error_buf, error_buf_size, format, args); + va_end(args); +} + static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { @@ -34,11 +71,25 @@ wasm_runtime_env_init() return false; } +#if WASM_ENABLE_MULTI_MODULE + if (BHT_OK != os_mutex_init(®istered_module_list_lock)) { + wasm_native_destroy(); + bh_platform_destroy(); + return false; + } + + if (BHT_OK != os_mutex_init(&loading_module_list_lock)) { + os_mutex_destroy(®istered_module_list_lock); + wasm_native_destroy(); + bh_platform_destroy(); + return false; + } +#endif return true; } static bool -wasm_runtime_env_check(WASMExecEnv *exec_env) +wasm_runtime_exec_env_check(WASMExecEnv *exec_env) { return !(!exec_env || !exec_env->module_inst @@ -65,8 +116,17 @@ wasm_runtime_init() void wasm_runtime_destroy() { + /* runtime env destroy */ +#if WASM_ENABLE_MULTI_MODULE + wasm_runtime_destroy_loading_module_list(); + os_mutex_destroy(&loading_module_list_lock); + + wasm_runtime_destroy_registered_module_list(); + os_mutex_destroy(®istered_module_list_lock); +#endif wasm_native_destroy(); bh_platform_destroy(); + wasm_runtime_memory_destroy(); } @@ -105,10 +165,342 @@ get_package_type(const uint8 *buf, uint32 size) return Package_Type_Unknown; } +#if WASM_ENABLE_MULTI_MODULE != 0 +static module_reader reader; +static module_destroyer destroyer; + +void +wasm_runtime_set_module_reader(const module_reader reader_cb, + const module_destroyer destroyer_cb) +{ + reader = reader_cb; + destroyer = destroyer_cb; +} + +module_reader +wasm_runtime_get_module_reader() +{ + return reader; +} + +module_destroyer +wasm_runtime_get_module_destroyer() +{ + return destroyer; +} + +static WASMRegisteredModule * +wasm_runtime_find_module_registered_by_reference(WASMModuleCommon *module) +{ + WASMRegisteredModule *reg_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module && module != reg_module->module) { + reg_module = bh_list_elem_next(reg_module); + } + os_mutex_unlock(®istered_module_list_lock); + + return reg_module; +} + +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, + uint32_t error_buf_size) +{ + WASMRegisteredModule *node = NULL; + + node = wasm_runtime_find_module_registered_by_reference(module); + if (node) { /* module has been registered */ + if (node->module_name) { /* module has name */ + if (strcmp(node->module_name, module_name)) { + /* module has different name */ + LOG_DEBUG("module(%p) has been registered with name %s", + module, node->module_name); + set_error_buf_v(error_buf, error_buf_size, + "can not rename the module"); + return false; + } + else { + /* module has the same name */ + LOG_DEBUG("module(%p) has been registered with the same name %s", + module, node->module_name); + return true; + } + } + else { + /* module has empyt name, reset it */ + node->module_name = module_name; + return true; + } + } + + /* module hasn't been registered */ + node = wasm_runtime_malloc(sizeof(WASMRegisteredModule)); + if (!node) { + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%d", + sizeof(WASMRegisteredModule)); + set_error_buf_v(error_buf, error_buf_size, + "malloc WASMRegisteredModule failed. SZ=%d", + sizeof(WASMRegisteredModule)); + return false; + } + + /* share the string and the module */ + node->module_name = module_name; + node->module = module; + node->orig_file_buf = orig_file_buf; + node->orig_file_buf_size = orig_file_buf_size; + + os_mutex_lock(®istered_module_list_lock); + bh_list_status ret = bh_list_insert(registered_module_list, node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(®istered_module_list_lock); + return true; +} + +bool +wasm_runtime_register_module(const char *module_name, WASMModuleCommon *module, + char *error_buf, uint32_t error_buf_size) +{ + if (!error_buf || !error_buf_size) { + LOG_ERROR("error buffer is required"); + return false; + } + + if (!module_name || !module) { + LOG_DEBUG("module_name and module are required"); + set_error_buf_v(error_buf, error_buf_size, + "module_name and module are required"); + return false; + } + + if (wasm_runtime_is_built_in_module(module_name)) { + LOG_DEBUG("%s is a built-in module name", module_name); + set_error_buf(error_buf, error_buf_size, + "can not register as a built-in module"); + return false; + } + + return wasm_runtime_register_module_internal( + module_name, module, NULL, 0, + error_buf, error_buf_size); +} + +void +wasm_runtime_unregister_module(const WASMModuleCommon *module) +{ + WASMRegisteredModule *registered_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + registered_module = bh_list_first_elem(registered_module_list); + while (registered_module && module != registered_module->module) { + registered_module = bh_list_elem_next(registered_module); + } + + /* it does not matter if it is not exist. after all, it is gone */ + if (registered_module) { + bh_list_remove(registered_module_list, registered_module); + wasm_runtime_free(registered_module); + } + os_mutex_unlock(®istered_module_list_lock); +} + +WASMModuleCommon * +wasm_runtime_find_module_registered(const char *module_name) +{ + WASMRegisteredModule *module = NULL, *module_next; + + os_mutex_lock(®istered_module_list_lock); + module = bh_list_first_elem(registered_module_list); + while (module) { + module_next = bh_list_elem_next(module); + if (module->module_name + && !strcmp(module_name, module->module_name)) { + break; + } + module = module_next; + } + os_mutex_unlock(®istered_module_list_lock); + + return module ? module->module : NULL; +} + +bool +wasm_runtime_is_module_registered(const char *module_name) +{ + return NULL != wasm_runtime_find_module_registered(module_name); +} + +/* + * simply destroy all + */ +static void +wasm_runtime_destroy_registered_module_list() +{ + WASMRegisteredModule *reg_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module) { + WASMRegisteredModule *next_reg_module = bh_list_elem_next(reg_module); + + bh_list_remove(registered_module_list, reg_module); + + /* now, it is time to release every module in the runtime */ +#if WASM_ENABLE_INTERP != 0 + if (reg_module->module->module_type == Wasm_Module_Bytecode) + wasm_unload((WASMModule *)reg_module->module); +#endif +#if WASM_ENABLE_AOT != 0 + if (reg_module->module->module_type == Wasm_Module_AoT) + aot_unload((AOTModule *)reg_module->module); +#endif + + /* destroy the file buffer */ + if (destroyer && reg_module->orig_file_buf) { + destroyer(reg_module->orig_file_buf, + reg_module->orig_file_buf_size); + reg_module->orig_file_buf = NULL; + reg_module->orig_file_buf_size = 0; + } + + wasm_runtime_free(reg_module); + reg_module = next_reg_module; + } + os_mutex_unlock(®istered_module_list_lock); +} + +bool +wasm_runtime_add_loading_module(const char *module_name, char *error_buf, + uint32 error_buf_size) +{ + LOG_DEBUG("add %s into a loading list", module_name); + LoadingModule *loadingModule = wasm_runtime_malloc(sizeof(LoadingModule)); + + if (!loadingModule) { + set_error_buf_v(error_buf, error_buf_size, + "malloc LoadingModule failed. SZ=%d", + sizeof(LoadingModule)); + return false; + } + + /* share the incoming string */ + loadingModule->module_name = module_name; + + os_mutex_lock(&loading_module_list_lock); + bh_list_status ret = bh_list_insert(loading_module_list, loadingModule); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(&loading_module_list_lock); + return true; +} + +void +wasm_runtime_delete_loading_module(const char *module_name) +{ + LOG_DEBUG("delete %s from a loading list", module_name); + + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module->module_name, module_name)) { + module = bh_list_elem_next(module); + } + + /* it does not matter if it is not exist. after all, it is gone */ + if (module) { + bh_list_remove(loading_module_list, module); + wasm_runtime_free(module); + } + os_mutex_unlock(&loading_module_list_lock); +} + +bool +wasm_runtime_is_loading_module(const char *module_name) +{ + LOG_DEBUG("find %s in a loading list", module_name); + + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module_name, module->module_name)) { + module = bh_list_elem_next(module); + } + os_mutex_unlock(&loading_module_list_lock); + + return module != NULL; +} + +void +wasm_runtime_destroy_loading_module_list() +{ + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module) { + LoadingModule *next_module = bh_list_elem_next(module); + + bh_list_remove(loading_module_list, module); + /* + * will not free the module_name since it is + * shared one of the const string pool + */ + wasm_runtime_free(module); + + module = next_module; + } + + os_mutex_unlock(&loading_module_list_lock); +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name) +{ + return (!strcmp("env", module_name) + || !strcmp("wasi_unstable", module_name) + || !strcmp("wasi_snapshot_preview1", module_name) + || !strcmp("spectest", module_name) + ); +} + +static WASMModuleCommon * +register_module_with_null_name(WASMModuleCommon *module_common, + char *error_buf, uint32 error_buf_size) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + if (module_common) { + if (!wasm_runtime_register_module_internal(NULL, module_common, + NULL, 0, + error_buf, + error_buf_size)) { + wasm_runtime_unload(module_common); + return NULL; + } + return module_common; + } + else + return NULL; +#else + return module_common; +#endif +} + WASMModuleCommon * wasm_runtime_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size) { + WASMModuleCommon *module_common = NULL; + if (get_package_type(buf, size) == Wasm_Module_Bytecode) { #if WASM_ENABLE_AOT != 0 && WASM_ENABLE_JIT != 0 AOTModule *aot_module; @@ -121,18 +513,24 @@ wasm_runtime_load(const uint8 *buf, uint32 size, wasm_unload(module); return NULL; } - return (WASMModuleCommon*)aot_module; + + module_common = (WASMModuleCommon*)aot_module; + return register_module_with_null_name(module_common, + error_buf, error_buf_size); #elif WASM_ENABLE_INTERP != 0 - return (WASMModuleCommon*) + module_common = (WASMModuleCommon*) wasm_load(buf, size, error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); #endif } else if (get_package_type(buf, size) == Wasm_Module_AoT) { #if WASM_ENABLE_AOT != 0 - return (WASMModuleCommon*) + module_common = (WASMModuleCommon*) aot_load_from_aot_file(buf, size, error_buf, error_buf_size); - -#endif /* end of WASM_ENABLE_AOT */ + return register_module_with_null_name(module_common, + error_buf, error_buf_size); +#endif } if (size < 4) @@ -148,17 +546,25 @@ WASMModuleCommon * wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, char *error_buf, uint32_t error_buf_size) { + WASMModuleCommon *module_common; + #if WASM_ENABLE_INTERP != 0 - if (!is_aot) - return (WASMModuleCommon*) + if (!is_aot) { + module_common = (WASMModuleCommon*) wasm_load_from_sections(section_list, error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); + } #endif #if WASM_ENABLE_AOT != 0 - if (is_aot) - return (WASMModuleCommon*) + if (is_aot) { + module_common = (WASMModuleCommon*) aot_load_from_sections(section_list, error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); + } #endif set_error_buf(error_buf, error_buf_size, @@ -169,12 +575,21 @@ wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, void wasm_runtime_unload(WASMModuleCommon *module) { +#if WASM_ENABLE_MULTI_MODULE != 0 + /** + * since we will unload and free all module when runtime_destroy() + * we don't want users to unwillingly disrupt it + */ + return; +#endif + #if WASM_ENABLE_INTERP != 0 if (module->module_type == Wasm_Module_Bytecode) { wasm_unload((WASMModule*)module); return; } #endif + #if WASM_ENABLE_AOT != 0 if (module->module_type == Wasm_Module_AoT) { aot_unload((AOTModule*)module); @@ -285,9 +700,9 @@ wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, bool wasm_runtime_call_wasm(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]) + uint32 argc, uint32 argv[]) { - if (!wasm_runtime_env_check(exec_env)) { + if (!wasm_runtime_exec_env_check(exec_env)) { LOG_ERROR("Invalid exec env stack info."); return false; } @@ -313,7 +728,7 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, bool wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]) + uint32 argc, uint32 argv[]) { #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) @@ -1077,7 +1492,7 @@ check_main_func_type(const WASMType *type) bool wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, - int argc, char *argv[]) + int32 argc, char *argv[]) { WASMFunctionInstanceCommon *func; WASMType *func_type = NULL; @@ -1165,6 +1580,54 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, argc1, argv1); } + + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModuleInstance * +get_sub_module_inst(const WASMModuleInstance *parent_module_inst, + const char *sub_module_name) +{ + WASMSubModInstNode *node = + bh_list_first_elem(parent_module_inst->sub_module_inst_list); + + while (node && strcmp(node->module_name, sub_module_name)) { + node = bh_list_elem_next(node); + } + return node ? node->module_inst : NULL; +} + +static bool +parse_function_name(char *orig_function_name, char **p_module_name, + char **p_function_name) +{ + if (orig_function_name[0] != '$') { + *p_module_name = NULL; + *p_function_name = orig_function_name; + return true; + } + + /** + * $module_name$function_name\0 + * ===> + * module_name\0function_name\0 + * ===> + * module_name + * function_name + */ + char *p1 = orig_function_name; + char *p2 = strchr(p1 + 1, '$'); + if (!p2) { + LOG_DEBUG("can not parse the incoming function name"); + return false; + } + + *p_module_name = p1 + 1; + *p2 = '\0'; + *p_function_name = p2 + 1; + return strlen(*p_module_name) && strlen(*p_function_name); +} +#endif + /** * Implementation of wasm_application_execute_func() */ @@ -1173,32 +1636,76 @@ static WASMFunctionInstanceCommon* resolve_function(const WASMModuleInstanceCommon *module_inst, const char *name) { - uint32 i; + uint32 i = 0; + WASMFunctionInstanceCommon *ret = NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *sub_module_inst = NULL; + char *orig_name = NULL; + char *sub_module_name = NULL; + char *function_name = NULL; + uint32 length = strlen(name) + 1; + + orig_name = wasm_runtime_malloc(sizeof(char) * length); + if (!orig_name) { + return NULL; + } + + memset(orig_name, 0, sizeof(char) * length); + strncpy(orig_name, name, length); + + if (!parse_function_name(orig_name, &sub_module_name, &function_name)) { + goto LEAVE; + } + + 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) { + LOG_DEBUG("can not find a sub module named %s", sub_module_name); + goto LEAVE; + } + } +#else + const char *function_name = name; +#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, name)) - return wasm_inst->export_functions[i].function; + if (!strcmp(wasm_inst->export_functions[i].name, function_name)) { + ret = wasm_inst->export_functions[i].function; + break; + } } - return NULL; } -#endif +#endif /* WASM_ENABLE_INTERP */ #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) { AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; AOTModule *module = (AOTModule*)aot_inst->aot_module.ptr; for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, name)) - return (WASMFunctionInstance*)&module->export_funcs[i]; + if (!strcmp(module->export_funcs[i].func_name, function_name)) { + ret = (WASMFunctionInstance*)&module->export_funcs[i]; + break; + } } - return NULL; } #endif - return NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 +LEAVE: + wasm_runtime_free(orig_name); +#endif + return ret; } union ieee754_float { @@ -1251,7 +1758,7 @@ static union { bool wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, - const char *name, int argc, char *argv[]) + const char *name, int32 argc, char *argv[]) { WASMFunctionInstanceCommon *func; WASMType *type = NULL; @@ -1262,6 +1769,7 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, char buf[128]; bh_assert(argc >= 0); + LOG_DEBUG("call a function \"%s\" with %d arguments", name, argc); func = resolve_function(module_inst, name); if (!func) { @@ -1273,7 +1781,11 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, #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_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; @@ -2146,7 +2658,7 @@ wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32_t element_indices, uint32_t argc, uint32_t argv[]) { - if (!wasm_runtime_env_check(exec_env)) { + if (!wasm_runtime_exec_env_check(exec_env)) { LOG_ERROR("Invalid exec env stack info."); return false; } diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 432db5988..97db71f8e 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -21,7 +21,6 @@ extern "C" { #endif - typedef struct WASMModuleCommon { /* Module type, for module loaded from WASM bytecode binary, this field is Wasm_Module_Bytecode, and this structure should @@ -56,9 +55,25 @@ typedef struct WASIContext { } WASIContext; #endif +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMRegisteredModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleCommon *module; + /* to store the original module file buffer address */ + uint8 *orig_file_buf; + uint32 orig_file_buf_size; +} WASMRegisteredModule; +#endif + typedef package_type_t PackageType; typedef wasm_section_t WASMSection, AOTSection; +void +set_error_buf_v(char *error_buf, uint32 error_buf_size, const char *format, + ...); + /* See wasm_export.h for description */ bool wasm_runtime_init(); @@ -75,6 +90,7 @@ wasm_runtime_destroy(); PackageType get_package_type(const uint8 *buf, uint32 size); + /* See wasm_export.h for description */ WASMModuleCommon * wasm_runtime_load(const uint8 *buf, uint32 size, @@ -83,7 +99,7 @@ wasm_runtime_load(const uint8 *buf, uint32 size, /* See wasm_export.h for description */ WASMModuleCommon * wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, - char *error_buf, uint32_t error_buf_size); + char *error_buf, uint32 error_buf_size); /* See wasm_export.h for description */ void @@ -119,21 +135,21 @@ wasm_runtime_get_module_inst(WASMExecEnv *exec_env); /* See wasm_export.h for description */ void * -wasm_runtime_get_function_attachment(wasm_exec_env_t exec_env); +wasm_runtime_get_function_attachment(WASMExecEnv *exec_env); /* See wasm_export.h for description */ void -wasm_runtime_set_user_data(wasm_exec_env_t exec_env, void *user_data); +wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data); /* See wasm_export.h for description */ void * -wasm_runtime_get_user_data(wasm_exec_env_t exec_env); +wasm_runtime_get_user_data(WASMExecEnv *exec_env); /* See wasm_export.h for description */ bool wasm_runtime_call_wasm(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]); + uint32 argc, uint32 argv[]); /** * Call a function reference of a given WASM runtime instance with @@ -154,23 +170,23 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, */ bool wasm_runtime_call_indirect(WASMExecEnv *exec_env, - uint32_t element_indices, - uint32_t argc, uint32_t argv[]); + uint32 element_indices, + uint32 argc, uint32 argv[]); bool wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, WASMFunctionInstanceCommon *function, - unsigned argc, uint32 argv[]); + uint32 argc, uint32 argv[]); /* See wasm_export.h for description */ bool wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, - int argc, char *argv[]); + int32 argc, char *argv[]); /* See wasm_export.h for description */ bool wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, - const char *name, int argc, char *argv[]); + const char *name, int32 argc, char *argv[]); /* See wasm_export.h for description */ void @@ -261,6 +277,48 @@ void wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst, uint32 llvm_stack); +#if WASM_ENABLE_MULTI_MODULE != 0 +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); + +module_reader +wasm_runtime_get_module_reader(); + +module_destroyer +wasm_runtime_get_module_destroyer(); + +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, + uint32 error_buf_size); + +void +wasm_runtime_unregister_module(const WASMModuleCommon *module); + +bool +wasm_runtime_is_module_registered(const char *module_name); + +bool +wasm_runtime_add_loading_module(const char *module_name, + char *error_buf, uint32 error_buf_size); + +void +wasm_runtime_delete_loading_module(const char *module_name); + +bool +wasm_runtime_is_loading_module(const char *module_name); + +void +wasm_runtime_destroy_loading_module_list(); +#endif /* WASM_ENALBE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name); + #if WASM_ENABLE_LIBC_WASI != 0 /* See wasm_export.h for description */ void diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index a130ba7a6..025294b6f 100644 --- a/core/iwasm/compilation/aot.c +++ b/core/iwasm/compilation/aot.c @@ -60,6 +60,10 @@ aot_create_mem_init_data_list(const WASMModule *module) goto fail; } +#if WASM_ENABLE_BULK_MEMORY != 0 + data_list[i]->is_passive = module->data_segments[i]->is_passive; + data_list[i]->memory_index = module->data_segments[i]->memory_index; +#endif data_list[i]->offset = module->data_segments[i]->base_offset; data_list[i]->byte_count = module->data_segments[i]->data_length; memcpy(data_list[i]->bytes, module->data_segments[i]->data, diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index ede02b434..7a533fa75 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -24,6 +24,12 @@ typedef WASMType AOTFuncType; * A segment of memory init data */ typedef struct AOTMemInitData { +#if WASM_ENABLE_BULK_MEMORY != 0 + /* Passive flag */ + bool is_passive; + /* memory index */ + uint32 memory_index; +#endif /* Start address of init data */ AOTInitExpr offset; /* Byte count */ diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index 15fc1dd2c..617bf6e65 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -741,6 +741,39 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, true)) return false; break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + frame_ip ++; + if (!aot_compile_op_memory_init(comp_ctx, func_ctx, seg_index)) + return false; + break; + } + case WASM_OP_DATA_DROP: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + if (!aot_compile_op_data_drop(comp_ctx, func_ctx, seg_index)) + return false; + break; + } + case WASM_OP_MEMORY_COPY: + { + frame_ip += 2; + if (!aot_compile_op_memory_copy(comp_ctx, func_ctx)) + return false; + break; + } + case WASM_OP_MEMORY_FILL: + { + frame_ip ++; + if (!aot_compile_op_memory_fill(comp_ctx, func_ctx)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ default: break; } diff --git a/core/iwasm/compilation/aot_emit_aot_file.c b/core/iwasm/compilation/aot_emit_aot_file.c index 2cffb44f6..2095388e0 100644 --- a/core/iwasm/compilation/aot_emit_aot_file.c +++ b/core/iwasm/compilation/aot_emit_aot_file.c @@ -124,8 +124,18 @@ get_mem_init_data_size(AOTMemInitData *mem_init_data) { /* init expr type (4 bytes) + init expr value (8 bytes) + byte count (4 bytes) + bytes */ - return (uint32)(sizeof(uint32) + sizeof(uint64) - + sizeof(uint32) + mem_init_data->byte_count); + uint32 total_size = + (uint32)(sizeof(uint32) + sizeof(uint64) + + sizeof(uint32) + mem_init_data->byte_count); + + /* bulk_memory enabled: + is_passive (4 bytes) + memory_index (4 bytes) + bulk memory disabled: + placeholder (4 bytes) + placeholder (4 bytes) + */ + total_size += (sizeof(uint32) + sizeof(uint32)); + + return total_size; } static uint32 @@ -682,7 +692,8 @@ get_relocation_section_size(AOTObjectData *obj_data) } static uint32 -get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data) +get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 size = 0; @@ -868,7 +879,8 @@ aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 offset = *p_offset, i; AOTMemInitData **init_datas = comp_data->mem_init_data_list; @@ -882,6 +894,18 @@ aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, for (i = 0; i < comp_data->mem_init_data_count; i++) { offset = align_uint(offset, 4); +#if WASM_ENABLE_BULK_MEMORY != 0 + if (comp_ctx->enable_bulk_memory) { + EMIT_U32(init_datas[i]->is_passive); + EMIT_U32(init_datas[i]->memory_index); + } + else +#endif + { + /* emit two placeholder to keep the same size */ + EMIT_U32(0); + EMIT_U32(0); + } EMIT_U32(init_datas[i]->offset.init_expr_type); EMIT_U64(init_datas[i]->offset.u.i64); EMIT_U32(init_datas[i]->byte_count); @@ -1077,7 +1101,8 @@ aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, static bool aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, - AOTCompData *comp_data, AOTObjectData *obj_data) + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) { uint32 section_size = get_init_data_section_size(comp_data, obj_data); uint32 offset = *p_offset; @@ -1087,7 +1112,7 @@ aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U32(AOT_SECTION_TYPE_INIT_DATA); EMIT_U32(section_size); - if (!aot_emit_mem_info(buf, buf_end, &offset, comp_data, obj_data) + if (!aot_emit_mem_info(buf, buf_end, &offset, comp_ctx, comp_data, obj_data) || !aot_emit_table_info(buf, buf_end, &offset, comp_data, obj_data) || !aot_emit_func_type_info(buf, buf_end, &offset, comp_data, obj_data) || !aot_emit_import_global_info(buf, buf_end, &offset, comp_data, obj_data) @@ -1405,7 +1430,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) obj_data->target_info.bin_type = bin_type - LLVMBinaryTypeELF32L; - if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF32B) { + if (bin_type == LLVMBinaryTypeELF32L + || bin_type == LLVMBinaryTypeELF32B) { struct elf32_ehdr *elf_header; bool is_little_bin = bin_type == LLVMBinaryTypeELF32L; @@ -1420,7 +1446,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); } - else { + else if (bin_type == LLVMBinaryTypeELF64L + || bin_type == LLVMBinaryTypeELF64B) { struct elf64_ehdr *elf_header; bool is_little_bin = bin_type == LLVMBinaryTypeELF64L; @@ -1435,6 +1462,19 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); } + else if (bin_type == LLVMBinaryTypeMachO32L + || bin_type == LLVMBinaryTypeMachO32B) { + /* TODO: parse file type of Mach-O 32 */ + aot_set_last_error("invaid llvm binary bin_type."); + return false; + } + else if (bin_type == LLVMBinaryTypeMachO64L + || bin_type == LLVMBinaryTypeMachO64B) { + /* TODO: parse file type of Mach-O 64 */ + aot_set_last_error("invaid llvm binary bin_type."); + return false; + } + strncpy(obj_data->target_info.arch, comp_ctx->target_arch, sizeof(obj_data->target_info.arch)); @@ -1941,7 +1981,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, bh_print_time("Begin to emit AOT file"); - aot_file_size = get_aot_file_size(comp_data, obj_data); + aot_file_size = get_aot_file_size(comp_ctx, comp_data, obj_data); if (!(buf = aot_file_buf = wasm_runtime_malloc(aot_file_size))) { aot_set_last_error("allocate memory failed."); @@ -1953,7 +1993,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, if (!aot_emit_file_header(buf, buf_end, &offset, comp_data, obj_data) || !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, obj_data) - || !aot_emit_init_data_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_init_data_section(buf, buf_end, &offset, comp_ctx, comp_data, obj_data) || !aot_emit_text_section(buf, buf_end, &offset, comp_data, obj_data) || !aot_emit_func_section(buf, buf_end, &offset, comp_data, obj_data) || !aot_emit_export_section(buf, buf_end, &offset, comp_data, obj_data) diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index 680d24c7e..70e6a0faa 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/core/iwasm/compilation/aot_emit_memory.c @@ -626,3 +626,290 @@ fail: return false; } +#if WASM_ENABLE_BULK_MEMORY != 0 + +static LLVMValueRef +check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef offset, LLVMValueRef bytes) +{ + LLVMValueRef maddr, max_addr, cmp; + LLVMValueRef mem_base_addr; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_succ; + uint32 off = offsetof(AOTModuleInstance, memory_data_size); + LLVMValueRef mem_size_offset, mem_size_ptr, mem_size; + + /* Get memory base address and memory data size */ + if (func_ctx->mem_space_unchanged) { + mem_base_addr = func_ctx->mem_base_addr; + } + else { + if (!(mem_base_addr = LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_base_addr, + "mem_base"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + /* return addres directly if constant offset and inside memory space */ + if (LLVMIsConstant(offset) && LLVMIsConstant(bytes)) { + uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(offset); + uint64 mem_len = (uint64)LLVMConstIntGetZExtValue(bytes); + uint32 num_bytes_per_page = comp_ctx->comp_data->num_bytes_per_page; + uint32 init_page_count = comp_ctx->comp_data->mem_init_page_count; + uint32 mem_data_size = num_bytes_per_page * init_page_count; + if (mem_data_size > 0 + && mem_offset + mem_len <= mem_data_size) { + /* inside memory space */ + /* maddr = mem_base_addr + moffset */ + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, + mem_base_addr, + &offset, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; + } + } + + /* mem_size_offset = aot_inst + off */ + mem_size_offset = I32_CONST(off); + if (!(mem_size_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->aot_inst, + &mem_size_offset, 1, + "mem_size_ptr_tmp"))) { + aot_set_last_error("llvm build inbounds gep failed."); + return NULL; + } + + /* cast to int32* */ + if (!(mem_size_ptr = LLVMBuildBitCast(comp_ctx->builder, mem_size_ptr, + INT32_PTR_TYPE, "mem_size_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + return NULL; + } + + /* load memory size */ + if (!(mem_size = LLVMBuildLoad(comp_ctx->builder, + mem_size_ptr, "mem_size"))) { + aot_set_last_error("llvm build load failed."); + return NULL; + } + + ADD_BASIC_BLOCK(check_succ, "check_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + + offset = LLVMBuildZExt(comp_ctx->builder, offset, I64_TYPE, "extend_offset"); + bytes = LLVMBuildZExt(comp_ctx->builder, bytes, I64_TYPE, "extend_len"); + mem_size = LLVMBuildZExt(comp_ctx->builder, mem_size, I64_TYPE, "extend_size"); + + BUILD_OP(Add, offset, bytes, max_addr, "max_addr"); + BUILD_ICMP(LLVMIntUGT, max_addr, mem_size, cmp, + "cmp_max_mem_addr"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + true, cmp, check_succ)) { + goto fail; + } + + /* maddr = mem_base_addr + offset */ + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr, + &offset, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; +fail: + return NULL; +} + +#define GET_AOT_FUNCTION(name, argc) do { \ + if (!(func_type = LLVMFunctionType(ret_type, param_types, \ + argc, false))) { \ + aot_set_last_error("llvm add function type failed."); \ + return false; \ + } \ + if (comp_ctx->is_jit_mode) { \ + /* JIT mode, call the function directly */ \ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ + aot_set_last_error("llvm add pointer type failed."); \ + return false; \ + } \ + if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ + aot_set_last_error("create LLVM value failed."); \ + return false; \ + } \ + } \ + else { \ + char *func_name = #name; \ + /* AOT mode, delcare the function */ \ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \ + && !(func = LLVMAddFunction(comp_ctx->module, \ + func_name, func_type))) { \ + aot_set_last_error("llvm add function failed."); \ + return false; \ + } \ + } \ + } while (0) + +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, offset, dst, len, param_values[5], ret_value, func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef mem_init_fail, init_success; + + seg = I32_CONST(seg_index); + + POP_I32(len); + POP_I32(offset); + POP_I32(dst); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(aot_memory_init, 5); + + /* Call function aot_memory_init() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + param_values[2] = offset; + param_values[3] = len; + param_values[4] = dst; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_init_ret"); + + ADD_BASIC_BLOCK(mem_init_fail, "mem_init_fail"); + ADD_BASIC_BLOCK(init_success, "init_success"); + + LLVMMoveBasicBlockAfter(mem_init_fail, block_curr); + LLVMMoveBasicBlockAfter(init_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, ret_value, + init_success, mem_init_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* If memory.init failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail); + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, init_success); + + return true; +fail: + return false; +} + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, param_values[2], ret_value, func, value; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + seg = I32_CONST(seg_index); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(aot_data_drop, 2); + + /* Call function aot_data_drop() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 2, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + return true; +} + +bool +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef src, dst, src_addr, dst_addr, len, res; + + POP_I32(len); + POP_I32(src); + POP_I32(dst); + + if (!(src_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, src, len))) + return false; + + if (!(dst_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) + return false; + + if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1, + src_addr, 1, len))) { + aot_set_last_error("llvm build memmove failed."); + return false; + } + return true; +fail: + return false; +} + +bool +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef val, dst, dst_addr, len, res; + + POP_I32(len); + POP_I32(val); + POP_I32(dst); + + if (!(dst_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) + return false; + + val = LLVMBuildIntCast2(comp_ctx->builder, val, INT8_TYPE, true, "mem_set_value"); + + if (!(res = LLVMBuildMemSet(comp_ctx->builder, dst_addr, + val, len, 1))) { + aot_set_last_error("llvm build memset failed."); + return false; + } + return true; +fail: + return false; +} +#endif /* WASM_ENABLE_BULK_MEMORY */ diff --git a/core/iwasm/compilation/aot_emit_memory.h b/core/iwasm/compilation/aot_emit_memory.h index d34264c36..c2415ff92 100644 --- a/core/iwasm/compilation/aot_emit_memory.h +++ b/core/iwasm/compilation/aot_emit_memory.h @@ -50,6 +50,22 @@ aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); bool aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); + +bool +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +#endif + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index 28572be3e..09155918c 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -914,6 +914,9 @@ aot_create_comp_context(AOTCompData *comp_data, goto fail; } + if (option->enable_bulk_memory) + comp_ctx->enable_bulk_memory = true; + if (option->is_jit_mode) { /* Create LLVM execution engine */ LLVMInitializeMCJITCompilerOptions(&jit_options, sizeof(jit_options)); diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 8883681c0..fc4821e22 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -185,6 +185,9 @@ typedef struct AOTCompContext { LLVMExecutionEngineRef exec_engine; bool is_jit_mode; + /* Bulk memory feature */ + bool enable_bulk_memory; + /* Whether optimize the JITed code */ bool optimize; @@ -223,6 +226,7 @@ typedef struct AOTCompOption{ char *target_abi; char *target_cpu; char *cpu_features; + bool enable_bulk_memory; uint32 opt_level; uint32 size_level; uint32 output_format; diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index 4338465cf..531d2df79 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -39,6 +39,7 @@ typedef struct AOTCompOption{ char *target_abi; char *target_cpu; char *cpu_features; + bool enable_bulk_memory; uint32_t opt_level; uint32_t size_level; uint32_t output_format; diff --git a/core/iwasm/include/lib_export.h b/core/iwasm/include/lib_export.h index 0bc4317b8..3a4c02d43 100644 --- a/core/iwasm/include/lib_export.h +++ b/core/iwasm/include/lib_export.h @@ -6,7 +6,7 @@ #ifndef _LIB_EXPORT_H_ #define _LIB_EXPORT_H_ -#include +#include #ifdef __cplusplus extern "C" { diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 0a915f83a..0ef3bb982 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -6,7 +6,7 @@ #ifndef _WASM_EXPORT_H #define _WASM_EXPORT_H -#include +#include #include #include "lib_export.h" @@ -178,6 +178,56 @@ void wasm_runtime_free(void *ptr); package_type_t get_package_type(const uint8_t *buf, uint32_t size); +#if WASM_ENABLE_MULTI_MODULE != 0 +/** + * It is a callback for WAMR providing by embedding to load a module file + * into a buffer + */ +typedef bool (*module_reader)(const char *module_name, + uint8_t **p_buffer, uint32_t *p_size); + +/** + * It is a callback for WAMR providing by embedding to release the buffer which + * is used by loading a module file + */ +typedef void (*module_destroyer)(uint8_t *buffer, uint32_t size); + +/** + * To setup callbacks for reading and releasing a buffer about a module file + * + * @param reader a callback to read a module file into a buffer + * @param destroyer a callback to release above buffer + */ +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); +/** + * Give the "module" a name "module_name". + * can not assign a new name to a module if it already has a name + * + * @param module_name indicate a name + * @param module the target module + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return true means success, false means failed + */ +bool +wasm_runtime_register_module(const char *module_name, wasm_module_t module, + char *error_buf, uint32_t error_buf_size); + +/** + * To check if there is already a loaded module named module_name in the + * runtime. you will not want to load repeately + * + * @param module_name indicate a name + * + * @return return WASM module loaded, NULL if failed + */ +wasm_module_t +wasm_runtime_find_module_registered(const char *module_name); +#endif /* WASM_ENABLE_MULTI_MODULE */ + /** * Load a WASM module from a specified byte buffer. The byte buffer can be * WASM binary data when interpreter or JIT is enabled, or AOT binary data @@ -348,7 +398,9 @@ wasm_application_execute_main(wasm_module_inst_t module_inst, * and execute that function. * * @param module_inst the WASM module instance - * @param name the name of the function to execute + * @param name the name of the function to execute. + * to indicate the module name via: $module_name$function_name + * or just a function name: function_name * @param argc the number of arguments * @param argv the arguments array * diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 96ed3954a..b54d09aa3 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -22,6 +22,8 @@ extern "C" { #define VALUE_TYPE_VOID 0x40 /* Used by AOT */ #define VALUE_TYPE_I1 0x41 +/* Used by loader to represent any type of i32/i64/f32/f64 */ +#define VALUE_TYPE_ANY 0x42 /* Table Element Type */ #define TABLE_ELEM_TYPE_ANY_FUNC 0x70 @@ -50,6 +52,9 @@ extern "C" { #define SECTION_TYPE_ELEM 9 #define SECTION_TYPE_CODE 10 #define SECTION_TYPE_DATA 11 +#if WASM_ENABLE_BULK_MEMORY != 0 +#define SECTION_TYPE_DATACOUNT 12 +#endif #define IMPORT_KIND_FUNC 0 #define IMPORT_KIND_TABLE 1 @@ -66,6 +71,10 @@ extern "C" { #define BLOCK_TYPE_IF 2 #define BLOCK_TYPE_FUNCTION 3 +typedef struct WASMModule WASMModule; +typedef struct WASMFunction WASMFunction; +typedef struct WASMGlobal WASMGlobal; + typedef union WASMValue { int32 i32; uint32 u32; @@ -119,6 +128,10 @@ typedef struct WASMTableImport { uint32 init_size; /* specified if (flags & 1), else it is 0x10000 */ uint32 max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMTable *import_table_linked; +#endif } WASMTableImport; typedef struct WASMMemoryImport { @@ -128,6 +141,10 @@ typedef struct WASMMemoryImport { uint32 num_bytes_per_page; uint32 init_page_count; uint32 max_page_count; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMMemory *import_memory_linked; +#endif } WASMMemoryImport; typedef struct WASMFunctionImport { @@ -135,13 +152,17 @@ typedef struct WASMFunctionImport { char *field_name; /* function type */ WASMType *func_type; - /* function pointer after linked */ + /* native function pointer after linked */ void *func_ptr_linked; /* signature from registered native symbols */ const char *signature; /* attachment */ void *attachment; bool call_conv_raw; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMFunction *import_func_linked; +#endif } WASMFunctionImport; typedef struct WASMGlobalImport { @@ -151,6 +172,12 @@ typedef struct WASMGlobalImport { bool is_mutable; /* global data after linked */ WASMValue global_data_linked; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* imported function pointer after linked */ + // TODO: remove if not necessary + WASMModule *import_module; + WASMGlobal *import_global_linked; +#endif } WASMGlobalImport; typedef struct WASMImport { @@ -223,6 +250,9 @@ typedef struct WASMDataSeg { uint32 memory_index; InitializerExpression base_offset; uint32 data_length; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive; +#endif uint8 *data; } WASMDataSeg; @@ -266,7 +296,12 @@ typedef struct WASMModule { uint32 global_count; uint32 export_count; uint32 table_seg_count; + /* data seg count read from data segment section */ uint32 data_seg_count; +#if WASM_ENABLE_BULK_MEMORY != 0 + /* data count read from datacount section */ + uint32 data_seg_count1; +#endif uint32 import_function_count; uint32 import_table_count; @@ -308,6 +343,12 @@ typedef struct WASMModule { WASIArguments wasi_args; bool is_wasi_module; #endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + // TODO: mutex ? mutli-threads ? + bh_list import_module_list_head; + bh_list *import_module_list; +#endif } WASMModule; typedef struct WASMBranchBlock { @@ -430,5 +471,4 @@ wasm_type_equal(const WASMType *type1, const WASMType *type2) } /* end of extern "C" */ #endif -#endif /* end of _WASM_H_ */ - +#endif /* end of _WASM_H_ */ \ No newline at end of file diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 54fb7ccb2..56e95fce5 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -236,6 +236,15 @@ LOAD_I16(void *addr) goto out_of_bounds; \ } while (0) +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do { \ + uint64 offset1 = (int32)(start); \ + if (offset1 + bytes <= linear_mem_size) \ + /* App heap space is not valid space for bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + static inline uint32 rotl32(uint32 n, uint32 c) { @@ -877,6 +886,59 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, wasm_exec_env_set_cur_frame(exec_env, prev_frame); } +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "fail to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* replace exec_env's module_inst with sub_module_inst so we can + call it */ + exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, + sub_func_inst, prev_frame); + + /* restore ip and module_inst */ + prev_frame->ip = ip; + exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; + + /* transfer exception if it is thrown */ + if (wasm_get_exception(sub_module_inst)) { + bh_memcpy_s(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + sub_module_inst->cur_exception, + sizeof(sub_module_inst->cur_exception)); + } +} +#endif + #if WASM_ENABLE_LABELS_AS_VALUES != 0 #define HANDLE_OP(opcode) HANDLE_##opcode @@ -902,6 +964,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 total_mem_size = memory ? num_bytes_per_page * memory->cur_page_count - heap_base_offset : 0; uint8 *global_data = module->global_data; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; +#endif WASMTableInstance *table = module->default_table; WASMGlobalInstance *globals = module->globals; uint8 opcode_IMPDEP = WASM_OP_IMPDEP; @@ -1086,14 +1151,27 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP (WASM_OP_CALL): read_leb_uint32(frame_ip, frame_ip_end, fidx); - bh_assert(fidx < module->function_count); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->functions + fidx; goto call_func_from_interp; HANDLE_OP (WASM_OP_CALL_INDIRECT): { WASMType *cur_type, *cur_func_type; + WASMTableInstance *cur_table_inst; + /** + * type check. compiler will make sure all like + * (call_indirect (type $x) (i32.const 1)) + * the function type has to be defined in the module also + * no matter it is used or not + */ read_leb_uint32(frame_ip, frame_ip_end, tidx); if (tidx >= module->module->type_count) { wasm_set_exception(module, "type index is overflow"); @@ -1105,27 +1183,48 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, frame_ip++; val = POP_I32(); - if (val < 0 || val >= (int32)table->cur_size) { + /* careful, it might be a table in another module */ + cur_table_inst = table; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table->table_inst_linked) { + cur_table_inst = table->table_inst_linked; + } +#endif + + if (val < 0 || val >= (int32)cur_table_inst->cur_size) { wasm_set_exception(module, "undefined element"); goto got_exception; } - fidx = ((uint32*)table->base_addr)[val]; + fidx = ((uint32*)cur_table_inst->base_addr)[val]; if (fidx == (uint32)-1) { wasm_set_exception(module, "uninitialized element"); goto got_exception; } +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + /* always call module own functions */ cur_func = module->functions + fidx; - if (cur_func->is_import_func) - cur_func_type = cur_func->u.func_import->func_type; + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + cur_func_type = cur_func->u.func_import->func_type; else cur_func_type = cur_func->u.func->func_type; if (!wasm_type_equal(cur_type, cur_func_type)) { wasm_set_exception(module, "indirect call type mismatch"); goto got_exception; } + goto call_func_from_interp; } @@ -1264,7 +1363,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, bh_assert(global_idx < module->global_count); global = globals + global_idx; - global_addr = global_data + global->data_offset; + global_addr = +#if WASM_ENABLE_MULTI_MODULE != 0 + global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : +#endif + global_data + global->data_offset; switch (global->type) { case VALUE_TYPE_I32: @@ -1289,7 +1395,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, bh_assert(global_idx < module->global_count); global = globals + global_idx; - global_addr = global_data + global->data_offset; + global_addr = +#if WASM_ENABLE_MULTI_MODULE != 0 + global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : +#endif + global_data + global->data_offset; switch (global->type) { case VALUE_TYPE_I32: @@ -1521,6 +1634,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, memory = module->default_memory; total_mem_size = num_bytes_per_page * memory->cur_page_count - heap_base_offset; +#if WASM_ENABLE_BULK_MEMORY != 0 + linear_mem_size = num_bytes_per_page * memory->cur_page_count; +#endif } (void)reserved; @@ -2357,6 +2473,78 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, DEF_OP_TRUNC_SAT_F64(-1.0f, 18446744073709551616.0, false, false); break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 addr, segment; + uint64 bytes, offset, seg_len; + uint8* data; + + read_leb_uint32(frame_ip, frame_ip_end, segment); + /* skip memory index */ + frame_ip++; + + bytes = (uint64)(uint32)POP_I32(); + offset = (uint64)(uint32)POP_I32(); + addr = (uint32)POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); + + seg_len = (uint64)module->module->data_segments[segment]->data_length; + data = module->module->data_segments[segment]->data; + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, linear_mem_size - addr, + data + offset, bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + read_leb_uint32(frame_ip, frame_ip_end, segment); + module->module->data_segments[segment]->data_length = 0; + + break; + } + case WASM_OP_MEMORY_COPY: + { + uint32 dst, src, len; + uint8 *mdst, *msrc; + + frame_ip += 2; + + len = POP_I32(); + src = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + /* allowing the destination and source to overlap */ + bh_memmove_s(mdst, linear_mem_size - dst, + msrc, len); + + break; + } + case WASM_OP_MEMORY_FILL: + { + uint32 dst, len; + uint8 val, *mdst; + frame_ip++; + + len = POP_I32(); + val = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + memset(mdst, val, len); + + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ default: wasm_set_exception(module, "WASM interp failed: unsupported opcode."); goto got_exception; @@ -2430,14 +2618,25 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, call_func_from_entry: { if (cur_func->is_import_func) { - wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame); - prev_frame = frame->prev_frame; - cur_func = frame->function; - UPDATE_ALL_FROM_FRAME(); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); + } + else +#endif + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); + } - memory = module->default_memory; - if (wasm_get_exception(module)) - goto got_exception; + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + + memory = module->default_memory; + if (wasm_get_exception(module)) + goto got_exception; } else { WASMFunction *cur_wasm_func = cur_func->u.func; @@ -2521,6 +2720,7 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMFunctionInstance *function, uint32 argc, uint32 argv[]) { + // TODO: since module_inst = exec_env->module_inst, shall we remove the 1st arg? WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); WASMInterpFrame *frame, *outs_area; @@ -2559,15 +2759,44 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, wasm_exec_env_set_cur_frame(exec_env, frame); - if (function->is_import_func) - wasm_interp_call_func_native(module_inst, exec_env, function, frame); - else + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + LOG_DEBUG("it is a function of a sub module"); + wasm_interp_call_func_import(module_inst, + exec_env, + function, + frame); + } + else +#endif + { + LOG_DEBUG("it is an native function"); + /* it is a native function */ + wasm_interp_call_func_native(module_inst, + exec_env, + function, + frame); + } + } + else { + LOG_DEBUG("it is a function of the module itself"); 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++) + for (i = 0; i < function->ret_cell_num; i++) { argv[i] = *(frame->sp + i - function->ret_cell_num); + } + + if (function->ret_cell_num) { + LOG_DEBUG("first return value argv[0]=%d", argv[0]); + } else { + LOG_DEBUG("no return value"); + } + } else { + LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst)); } wasm_exec_env_set_cur_frame(exec_env, prev_frame); diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 60a04dc50..02d0ee573 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -238,6 +238,15 @@ LOAD_I16(void *addr) goto out_of_bounds; \ } while (0) +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do { \ + uint64 offset1 = (int32)(start); \ + if (offset1 + bytes <= linear_mem_size) \ + /* App heap space is not valid space for bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + static inline uint32 rotl32(uint32 n, uint32 c) { @@ -815,6 +824,59 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, wasm_exec_env_set_cur_frame(exec_env, prev_frame); } +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "fail to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* replace exec_env's module_inst with sub_module_inst so we can + call it */ + exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, + sub_func_inst, prev_frame); + + /* restore ip and module_inst */ + prev_frame->ip = ip; + exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; + + /* transfer exception if it is thrown */ + if (wasm_get_exception(sub_module_inst)) { + bh_memcpy_s(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + sub_module_inst->cur_exception, + sizeof(sub_module_inst->cur_exception)); + } +} +#endif + #if WASM_ENABLE_OPCODE_COUNTER != 0 typedef struct OpcodeInfo { char *name; @@ -894,6 +956,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 total_mem_size = memory ? num_bytes_per_page * memory->cur_page_count - heap_base_offset : 0; uint8 *global_data = module->global_data; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; +#endif WASMTableInstance *table = module->default_table; WASMGlobalInstance *globals = module->globals; uint8 opcode_IMPDEP = WASM_OP_IMPDEP; @@ -997,6 +1062,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP (WASM_OP_CALL_INDIRECT): { WASMType *cur_type, *cur_func_type; + WASMTableInstance *cur_table_inst; tidx = GET_OPERAND(int32, 0); val = GET_OPERAND(int32, 2); @@ -1008,21 +1074,41 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, } cur_type = module->module->types[tidx]; - if (val < 0 || val >= (int32)table->cur_size) { + /* careful, it might be a table in another module */ + cur_table_inst = table; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table->table_inst_linked) { + cur_table_inst = table->table_inst_linked; + } +#endif + + if (val < 0 || val >= (int32)cur_table_inst->cur_size) { wasm_set_exception(module, "undefined element"); goto got_exception; } - fidx = ((uint32*)table->base_addr)[val]; + fidx = ((uint32*)cur_table_inst->base_addr)[val]; if (fidx == (uint32)-1) { wasm_set_exception(module, "uninitialized element"); goto got_exception; } +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + /* always call module own functions */ cur_func = module->functions + fidx; - if (cur_func->is_import_func) - cur_func_type = cur_func->u.func_import->func_type; + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + cur_func_type = cur_func->u.func_import->func_type; else cur_func_type = cur_func->u.func->func_type; if (!wasm_type_equal(cur_type, cur_func_type)) { @@ -1115,7 +1201,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, bh_assert(global_idx < module->global_count); global = globals + global_idx; - global_addr = global_data + global->data_offset; + global_addr = +#if WASM_ENABLE_MULTI_MODULE != 0 + global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : +#endif + global_data + global->data_offset; switch (global->type) { case VALUE_TYPE_I32: @@ -1141,7 +1234,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, bh_assert(global_idx < module->global_count); global = globals + global_idx; - global_addr = global_data + global->data_offset; + global_addr = +#if WASM_ENABLE_MULTI_MODULE != 0 + global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : +#endif + global_data + global->data_offset; switch (global->type) { case VALUE_TYPE_I32: @@ -1429,6 +1529,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, memory = module->default_memory; total_mem_size = num_bytes_per_page * memory->cur_page_count - heap_base_offset; +#if WASM_ENABLE_BULK_MEMORY != 0 + linear_mem_size = num_bytes_per_page * memory->cur_page_count; +#endif } (void)reserved; @@ -2295,6 +2398,76 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, DEF_OP_TRUNC_SAT_F64(-1.0, 18446744073709551616.0, false, false); break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 addr, segment; + uint64 bytes, offset, seg_len; + uint8* data; + + segment = GET_OPERAND(uint32, 0); + frame_ip += 2; + + bytes = (uint64)POP_I32(); + offset = (uint64)POP_I32(); + addr = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); + + seg_len = (uint64)module->module->data_segments[segment]->data_length; + data = module->module->data_segments[segment]->data; + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, linear_mem_size - addr, + data + offset, bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + segment = GET_OPERAND(uint32, 0); + frame_ip += 2; + + module->module->data_segments[segment]->data_length = 0; + + break; + } + case WASM_OP_MEMORY_COPY: + { + uint32 dst, src, len; + uint8 *mdst, *msrc; + + len = POP_I32(); + src = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + /* allowing the destination and source to overlap */ + bh_memmove_s(mdst, linear_mem_size - dst, + msrc, len); + + break; + } + case WASM_OP_MEMORY_FILL: + { + uint32 dst, len; + uint8 val, *mdst; + + len = POP_I32(); + val = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + memset(mdst, val, len); + + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ default: wasm_set_exception(module, "WASM interp failed: unsupported opcode."); goto got_exception; @@ -2310,7 +2483,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP (WASM_OP_CALL): fidx = frame_lp[GET_OFFSET()]; - bh_assert(fidx < module->function_count); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif cur_func = module->functions + fidx; goto call_func_from_interp; @@ -2383,7 +2561,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, GET_OPERAND(int64, (2 * (cur_func->param_count - i - 1))); outs_area->lp += 2; } else { - *(outs_area->lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1)));; + *(outs_area->lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1))); outs_area->lp ++; } } @@ -2397,14 +2575,25 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, call_func_from_entry: { if (cur_func->is_import_func) { - wasm_interp_call_func_native(module, exec_env, cur_func, prev_frame); - prev_frame = frame->prev_frame; - cur_func = frame->function; - UPDATE_ALL_FROM_FRAME(); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); + } + else +#endif + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); + } - memory = module->default_memory; - if (wasm_get_exception(module)) - goto got_exception; + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + + memory = module->default_memory; + if (wasm_get_exception(module)) + goto got_exception; } else { WASMFunction *cur_wasm_func = cur_func->u.func; @@ -2528,10 +2717,28 @@ wasm_interp_call_wasm(WASMModuleInstance *module_inst, wasm_exec_env_set_cur_frame(exec_env, frame); - if (function->is_import_func) - wasm_interp_call_func_native(module_inst, exec_env, function, frame); - else + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + LOG_DEBUG("it is a function of a sub module"); + wasm_interp_call_func_import(module_inst, + exec_env, + function, + frame); + } + else +#endif + { + LOG_DEBUG("it is an 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)) { diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 16a5c7674..b8f4f6933 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -251,8 +251,10 @@ const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, node = node_next; } - if (node) + if (node) { + LOG_DEBUG("reuse %s", node->str); return node->str; + } if (!(node = wasm_runtime_malloc(sizeof(StringNode) + len + 1))) { set_error_buf(error_buf, error_buf_size, @@ -427,6 +429,338 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return true; } +#if WASM_ENABLE_MULTI_MODULE != 0 +/** + * Find export item of a module with export info: + * module name, field name and export kind + */ +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) +{ + WASMExport *export; + uint32 i; + + for (i = 0, export = module->exports; i < module->export_count; + ++i, ++export) { + /** + * need to consider a scenario that different kinds of exports + * may have the same name, like + * (table (export "m1" "exported") 10 funcref) + * (memory (export "m1" "exported") 10) + **/ + if (export->kind == export_kind && !strcmp(field_name, export->name)) { + break; + } + } + + if (i == module->export_count) { + LOG_DEBUG("can not find an export %d named %s in the module %s", + export_kind, field_name, module_name); + set_error_buf_v(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return NULL; + } + + 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); + set_error_buf_v(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return export; +} + +static WASMFunction * +wasm_loader_resolve_function(const char *module_name, + const char *function_name, + const WASMType *expected_function_type, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMFunction *function = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + WASMType *target_function_type = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for function", module_name); + set_error_buf_v(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + 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); + if (!export) { + return NULL; + } + + /* run a function type check */ + if (export->index < module->import_function_count) { + target_function_type = + module->import_functions[export->index].u.function.func_type; + function = module->import_functions[export->index] + .u.function.import_func_linked; + } + else { + target_function_type = + module->functions[export->index - module->import_function_count] + ->func_type; + function = + module->functions[export->index - module->import_function_count]; + } + + if (!wasm_type_equal(expected_function_type, target_function_type)) { + LOG_DEBUG("%s.%s failed the type check", module_name, function_name); + set_error_buf_v(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return function; +} + +static WASMTable * +wasm_loader_resolve_table(const char *module_name, const char *table_name, + uint32 init_size, uint32 max_size, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMTable *table = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for table", module_name); + set_error_buf_v(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + 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); + if (!export) { + return NULL; + } + + /* run a table type check */ + if (export->index < module->import_table_count) { + table = + module->import_tables[export->index].u.table.import_table_linked; + } + else { + table = &(module->tables[export->index - module->import_table_count]); + } + if (table->init_size < init_size || table->max_size > max_size) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, table_name, table->init_size, table->max_size, + init_size, max_size); + set_error_buf_v(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return table; +} + +static WASMMemory * +wasm_loader_resolve_memory(const char *module_name, const char *memory_name, + uint32 init_page_count, uint32 max_page_count, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMMemory *memory = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for memory", module_name); + set_error_buf_v(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + 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); + if (!export) { + return NULL; + } + + + /* run a memory check */ + if (export->index < module->import_memory_count) { + memory = + module->import_memories[export->index].u.memory.import_memory_linked; + } + else { + memory = + &(module->memories[export->index - module->import_memory_count]); + } + if (memory->init_page_count < init_page_count || + memory->max_page_count > max_page_count) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, memory_name, memory->init_page_count, + memory->max_page_count, init_page_count, max_page_count); + set_error_buf_v(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return memory; +} + +static WASMGlobal * +wasm_loader_resolve_global(const char *module_name, + const char *global_name, + uint8 type, bool is_mutable, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMGlobal *global = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for global", module_name); + set_error_buf_v(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + 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); + if (!export) { + return NULL; + } + + /* run a global check */ + if (export->index < module->import_global_count) { + global = + module->import_globals[export->index].u.global.import_global_linked; + } else { + global = + &(module->globals[export->index - module->import_global_count]); + } + if (global->type != type || global->is_mutable != is_mutable) { + LOG_DEBUG("%s,%s failed type check(%d, %d), expected(%d, %d)", + module_name, global_name, global->type, global->is_mutable, + type, is_mutable); + set_error_buf_v(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return global; +} +#endif /* end of WASM_ENABLE_MULTI_MODULE */ + +static bool +load_function_import(const WASMModule *parent_module, WASMModule *sub_module, + char *sub_module_name, char *function_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMFunctionImport *function, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_type_index = 0; + WASMType *declare_func_type = NULL; + WASMFunction *linked_func = NULL; + const char *linked_signature = NULL; + void *linked_attachment = NULL; + bool linked_call_conv_raw = false; + bool is_built_in_module = false; + + CHECK_BUF(p, p_end, 1); + read_leb_uint32(p, p_end, declare_type_index); + *p_buf = p; + + if (declare_type_index >= parent_module->type_count) { + set_error_buf(error_buf, error_buf_size, + "Load import section failed: unknown type."); + LOG_DEBUG("the type index is out of range"); + return false; + } + + declare_func_type = parent_module->types[declare_type_index]; + + is_built_in_module = wasm_runtime_is_built_in_module(sub_module_name); + if (is_built_in_module) { + LOG_DEBUG("%s is a function of a built-in module %s", + function_name, + sub_module_name); + /* check built-in modules */ + linked_func = wasm_native_resolve_symbol(sub_module_name, + function_name, + declare_func_type, + &linked_signature, + &linked_attachment, + &linked_call_conv_raw); + } +#if WASM_ENABLE_MULTI_MODULE != 0 + else { + LOG_DEBUG("%s is a function of a sub-module %s", + function_name, + sub_module_name); + linked_func = wasm_loader_resolve_function(sub_module_name, + function_name, + declare_func_type, + error_buf, + error_buf_size); + } +#endif + + if (!linked_func) { +#if WASM_ENABLE_SPEC_TEST != 0 + set_error_buf(error_buf, + error_buf_size, + "unknown import or incompatible import type"); + return false; +#else +#if WASM_ENABLE_WAMR_COMPILER == 0 + LOG_WARNING( + "warning: fail to link import function (%s, %s)", + sub_module_name, function_name); +#endif +#endif + } + + function->module_name = sub_module_name; + function->field_name = function_name; + function->func_type = declare_func_type; + /* func_ptr_linked is for built-in functions */ + function->func_ptr_linked = is_built_in_module ? linked_func : NULL; + function->signature = linked_signature; + function->attachment = linked_attachment; + function->call_conv_raw = linked_call_conv_raw; +#if WASM_ENABLE_MULTI_MODULE != 0 + function->import_module = is_built_in_module ? NULL : sub_module; + /* can not set both func_ptr_linked and import_func_linked not NULL */ + function->import_func_linked = is_built_in_module ? NULL : linked_func; +#endif + return true; +} + static bool check_table_max_size(uint32 init_size, uint32 max_size, char *error_buf, uint32 error_buf_size) @@ -440,28 +774,97 @@ check_table_max_size(uint32 init_size, uint32 max_size, } static bool -load_table_import(const uint8 **p_buf, const uint8 *buf_end, - WASMTableImport *table, +load_table_import(WASMModule *sub_module, const char *sub_module_name, + const char *table_name, const uint8 **p_buf, + const uint8 *buf_end, WASMTableImport *table, char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_elem_type = 0; + uint32 declare_max_size_flag = 0; + uint32 declare_init_size = 0; + uint32 declare_max_size = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMTable *linked_table = NULL; +#endif CHECK_BUF(p, p_end, 1); /* 0x70 */ - table->elem_type = read_uint8(p); - bh_assert(table->elem_type == TABLE_ELEM_TYPE_ANY_FUNC); - read_leb_uint32(p, p_end, table->flags); - read_leb_uint32(p, p_end, table->init_size); - if (table->flags & 1) { - read_leb_uint32(p, p_end, table->max_size); + declare_elem_type = read_uint8(p); + if (TABLE_ELEM_TYPE_ANY_FUNC != declare_elem_type) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } + + read_leb_uint32(p, p_end, declare_max_size_flag); + read_leb_uint32(p, p_end, declare_init_size); + if (declare_max_size_flag & 1) { + read_leb_uint32(p, p_end, declare_max_size); if (!check_table_max_size(table->init_size, table->max_size, error_buf, error_buf_size)) return false; + } else { + declare_max_size = 0x10000; } - else - table->max_size = 0x10000; - *p_buf = p; + + if ((declare_max_size_flag & 1) && declare_init_size > declare_max_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + linked_table = wasm_loader_resolve_table( + sub_module_name, table_name, + declare_init_size, declare_max_size, + error_buf, error_buf_size); + if (!linked_table) { + LOG_DEBUG("(%s, %s) is not an exported from one of modules", + table_name, sub_module_name); + return false; + } + + /** + * reset with linked table limit + */ + declare_elem_type = linked_table->elem_type; + declare_init_size = linked_table->init_size; + declare_max_size = linked_table->max_size; + declare_max_size_flag = linked_table->flags; + table->import_table_linked = linked_table; + table->import_module = sub_module; + } +#endif + + /* (table (export "table") 10 20 funcref) */ + if (!strcmp("spectest", sub_module_name)) { + uint32 spectest_table_init_size = 10; + uint32 spectest_table_max_size = 20; + + if (strcmp("table", table_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } + + if (declare_init_size > spectest_table_init_size + || declare_max_size < spectest_table_max_size) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_size = spectest_table_init_size; + declare_max_size = spectest_table_max_size; + } + + /* now we believe all declaration are ok */ + table->elem_type = declare_elem_type; + table->init_size = declare_init_size; + table->flags = declare_max_size_flag; + table->max_size = declare_max_size; return true; } @@ -499,8 +902,9 @@ check_memory_max_size(uint32 init_size, uint32 max_size, } static bool -load_memory_import(const uint8 **p_buf, const uint8 *buf_end, - WASMMemoryImport *memory, +load_memory_import(WASMModule *sub_module, const char *sub_module_name, + const char *memory_name, const uint8 **p_buf, + const uint8 *buf_end, WASMMemoryImport *memory, char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; @@ -510,33 +914,160 @@ load_memory_import(const uint8 **p_buf, const uint8 *buf_end, / DEFAULT_NUM_BYTES_PER_PAGE; #else uint32 max_page_count = pool_size / DEFAULT_NUM_BYTES_PER_PAGE; +#endif /* WASM_ENABLE_APP_FRAMEWORK */ + uint32 declare_max_page_count_flag = 0; + uint32 declare_init_page_count = 0; + uint32 declare_max_page_count = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMMemory *linked_memory = NULL; #endif - read_leb_uint32(p, p_end, memory->flags); - read_leb_uint32(p, p_end, memory->init_page_count); - if (!check_memory_init_size(memory->init_page_count, - error_buf, error_buf_size)) + read_leb_uint32(p, p_end, declare_max_page_count_flag); + read_leb_uint32(p, p_end, declare_init_page_count); + if (!check_memory_init_size(declare_init_page_count, error_buf, + error_buf_size)) { return false; - - if (memory->flags & 1) { - read_leb_uint32(p, p_end, memory->max_page_count); - if (!check_memory_max_size(memory->init_page_count, - memory->max_page_count, - error_buf, error_buf_size)) - return false; - if (memory->max_page_count > max_page_count) - memory->max_page_count = max_page_count; } - else - /* Limit the maximum memory size to max_page_count */ - memory->max_page_count = max_page_count; + if (declare_max_page_count_flag & 1) { + read_leb_uint32(p, p_end, declare_max_page_count); + if (!check_memory_max_size(declare_init_page_count, + declare_max_page_count, error_buf, + error_buf_size)) { + return false; + } + if (declare_max_page_count > max_page_count) { + declare_max_page_count = max_page_count; + } + } + else { + /* Limit the maximum memory size to max_page_count */ + declare_max_page_count = max_page_count; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + linked_memory = wasm_loader_resolve_memory( + sub_module_name, memory_name, + declare_init_page_count, declare_max_page_count, + error_buf, error_buf_size); + if (!linked_memory) { + return false; + } + + /** + * reset with linked memory limit + */ + memory->import_module = sub_module; + memory->import_memory_linked = linked_memory; + declare_init_page_count = linked_memory->init_page_count; + declare_max_page_count = linked_memory->max_page_count; + } +#endif + + /* (memory (export "memory") 1 2) */ + if (!strcmp("spectest", sub_module_name)) { + uint32 spectest_memory_init_page = 1; + uint32 spectest_memory_max_page = 2; + + if (strcmp("memory", memory_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } + + if (declare_init_page_count > spectest_memory_init_page + || declare_max_page_count < spectest_memory_max_page) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_page_count = spectest_memory_init_page; + declare_max_page_count = spectest_memory_max_page; + } + + /* now we believe all declaration are ok */ + memory->flags = declare_max_page_count_flag; + memory->init_page_count = declare_init_page_count; + memory->max_page_count = declare_max_page_count; memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; *p_buf = p; return true; } +static bool +load_global_import(const WASMModule *parent_module, + WASMModule *sub_module, + char *sub_module_name, char *global_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMGlobalImport *global, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 declare_type = 0; + uint8 declare_mutable = 0; + bool is_mutable = false; + bool ret = false; + + CHECK_BUF(p, p_end, 2); + declare_type = read_uint8(p); + declare_mutable = read_uint8(p); + *p_buf = p; + + if (declare_mutable >= 2) { + set_error_buf(error_buf, error_buf_size, + "Load import section failed: " + "invalid mutability"); + return false; + } + + is_mutable = declare_mutable & 1 ? true : false; + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + ret = wasm_runtime_is_built_in_module(sub_module_name); + if (ret) { + /* check built-in modules */ + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, + global_name, global); + if (ret) { + LOG_DEBUG("(%s, %s) is a global of a built-in module", + sub_module_name, global_name); + } + } +#endif /* WASM_ENABLE_LIBC_BUILTIN */ + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!ret) { + /* check sub modules */ + WASMGlobal *linked_global = + wasm_loader_resolve_global(sub_module_name, global_name, + declare_type, declare_mutable, + error_buf, error_buf_size); + if (linked_global) { + LOG_DEBUG("(%s, %s) is a global of external module", + sub_module_name, global_name); + global->import_module = sub_module; + global->import_global_linked = linked_global; + ret = true; + } + } +#endif + + if (!ret) { + set_error_buf_v(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; + } + + global->module_name = sub_module_name; + global->field_name = global_name; + global->type = declare_type; + global->is_mutable = is_mutable; + return true; +} + static bool load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, char *error_buf, uint32 error_buf_size) @@ -546,7 +1077,11 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, CHECK_BUF(p, p_end, 1); /* 0x70 */ table->elem_type = read_uint8(p); - bh_assert(table->elem_type == TABLE_ELEM_TYPE_ANY_FUNC); + if (TABLE_ELEM_TYPE_ANY_FUNC != table->elem_type) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } + read_leb_uint32(p, p_end, table->flags); read_leb_uint32(p, p_end, table->init_size); if (table->flags & 1) { @@ -558,6 +1093,12 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, else table->max_size = 0x10000; + if ((table->flags & 1) && table->init_size > table->max_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } + *p_buf = p; return true; } @@ -600,6 +1141,174 @@ load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, return true; } +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModule * +search_sub_module(const WASMModule *parent_module, const char *sub_module_name) +{ + WASMRegisteredModule *node = + bh_list_first_elem(parent_module->import_module_list); + while (node && strcmp(sub_module_name, node->module_name)) { + node = bh_list_elem_next(node); + } + return node ? (WASMModule*)node->module : NULL; +} + +static bool +register_sub_module(const WASMModule *parent_module, + const char *sub_module_name, WASMModule *sub_module) +{ + /* register a sub_module on its parent sub module list */ + WASMRegisteredModule *node = NULL; + bh_list_status ret; + + if (search_sub_module(parent_module, sub_module_name)) { + LOG_DEBUG("%s has been registered in its parent", sub_module_name); + return true; + } + + node = wasm_runtime_malloc(sizeof(WASMRegisteredModule)); + if (!node) { + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ %d\n", + sizeof(WASMRegisteredModule)); + return false; + } + + node->module_name = sub_module_name; + node->module = (WASMModuleCommon*)sub_module; + ret = bh_list_insert(parent_module->import_module_list, node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + return true; +} + +static WASMModule * +load_depended_module(const WASMModule *parent_module, + const char *sub_module_name, char *error_buf, + uint32 error_buf_size) +{ + WASMModule *sub_module = NULL; + bool ret = false; + uint8 *buffer = NULL; + uint32 buffer_size = 0; + const module_reader reader = wasm_runtime_get_module_reader(); + const module_destroyer destroyer = wasm_runtime_get_module_destroyer(); + + /* check the registered module list of the parent */ + sub_module = search_sub_module(parent_module, sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded before", sub_module_name); + return sub_module; + } + + /* check the global registered module list */ + sub_module = + (WASMModule *)wasm_runtime_find_module_registered(sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded", sub_module_name); + goto REGISTER_SUB_MODULE; + } + + LOG_VERBOSE("to load %s", sub_module_name); + + if (!reader) { + LOG_DEBUG("error: there is no sub_module reader to load %s", + sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "error: there is no sub_module reader to load %s", + sub_module_name); + return NULL; + } + + /* start to maintain a loading module list */ + ret = wasm_runtime_is_loading_module(sub_module_name); + if (ret) { + LOG_DEBUG("find a circular dependency on %s", sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "error: find a circular dependency on %s", + sub_module_name); + return NULL; + } + + ret = wasm_runtime_add_loading_module(sub_module_name, error_buf, + error_buf_size); + if (!ret) { + LOG_DEBUG("can not add %s into loading module list\n", + sub_module_name); + return NULL; + } + + ret = reader(sub_module_name, &buffer, &buffer_size); + if (!ret) { + LOG_DEBUG("read the file of %s failed", sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "error: can not read the module file of %s", + sub_module_name); + goto DELETE_FROM_LOADING_LIST; + } + + sub_module = + wasm_loader_load(buffer, buffer_size, 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() + */ + goto DESTROY_FILE_BUFFER; + } + + wasm_runtime_delete_loading_module(sub_module_name); + + /* register on a global list */ + ret = wasm_runtime_register_module_internal(sub_module_name, + (WASMModuleCommon*)sub_module, + buffer, buffer_size, error_buf, + error_buf_size); + if (!ret) { + LOG_DEBUG("error: can not register module %s globally\n", + sub_module_name); + /* + * others will be unload in runtime_destroy() + */ + goto UNLOAD_MODULE; + } + + /* register on its parent list */ +REGISTER_SUB_MODULE: + ret = register_sub_module(parent_module, sub_module_name, sub_module); + if (!ret) { + LOG_DEBUG("error: can not register a sub module %s with its parent", + sizeof(WASMRegisteredModule)); + set_error_buf_v( + error_buf, error_buf_size, + "error: can not register a sub module %s with its parent", + sizeof(WASMRegisteredModule)); + /* + * since it is in the global module list, there is no need to + * unload the module. the runtime_destroy() will do it + */ + return NULL; + } + + return sub_module; + +UNLOAD_MODULE: + wasm_loader_unload(sub_module); + +DESTROY_FILE_BUFFER: + if (destroyer) { + destroyer(buffer, buffer_size); + } + else { + LOG_WARNING("need to release the reading buffer of %s manually", + sub_module_name); + } + +DELETE_FROM_LOADING_LIST: + wasm_runtime_delete_loading_module(sub_module_name); + return NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + static bool load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) @@ -610,8 +1319,8 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMImport *import; WASMImport *import_functions = NULL, *import_tables = NULL; WASMImport *import_memories = NULL, *import_globals = NULL; - char *module_name, *field_name; - uint8 mutable, u8, kind; + char *sub_module_name, *field_name; + uint8 u8, kind; read_leb_uint32(p, p_end, import_count); @@ -708,6 +1417,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, p = p_old; + // TODO: move it out of the loop /* insert "env", "wasi_unstable" and "wasi_snapshot_preview1" to const str list */ if (!const_str_list_insert((uint8*)"env", 3, module, error_buf, error_buf_size) || !const_str_list_insert((uint8*)"wasi_unstable", 13, module, @@ -719,11 +1429,13 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, /* Scan again to read the data */ for (i = 0; i < import_count; i++) { + WASMModule *sub_module = NULL; + /* load module name */ read_leb_uint32(p, p_end, name_len); CHECK_BUF(p, p_end, name_len); - if (!(module_name = const_str_list_insert - (p, name_len, module, error_buf, error_buf_size))) { + if (!(sub_module_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { return false; } p += name_len; @@ -731,12 +1443,31 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, /* load field name */ read_leb_uint32(p, p_end, name_len); CHECK_BUF(p, p_end, name_len); - if (!(field_name = const_str_list_insert - (p, name_len, module, error_buf, error_buf_size))) { + if (!(field_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { return false; } p += name_len; + LOG_DEBUG("import #%d: (%s, %s)", i, sub_module_name, field_name); +#if WASM_ENABLE_MULTI_MODULE != 0 + /* assume built-in modules have been loaded */ + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + LOG_DEBUG("%s is an exported field of a %s", field_name, + sub_module_name); + /* + * if it returns well, guarantee that + * the sub_module_name and its dependencies + * have been loaded well + */ + sub_module = load_depended_module(module, sub_module_name, + error_buf, error_buf_size); + if (!sub_module) { + return false; + } + } +#endif + CHECK_BUF(p, p_end, 1); /* 0x00/0x01/0x02/0x03 */ kind = read_uint8(p); @@ -744,36 +1475,27 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, case IMPORT_KIND_FUNC: /* import function */ bh_assert(import_functions); import = import_functions++; - read_leb_uint32(p, p_end, type_index); - if (type_index >= module->type_count) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "unknown type."); + if (!load_function_import(module, sub_module, + sub_module_name, field_name, &p, + p_end, &import->u.function, + error_buf, error_buf_size)) { return false; } - import->u.function.func_type = module->types[type_index]; - - if (!(import->u.function.func_ptr_linked = - wasm_native_resolve_symbol(module_name, field_name, - import->u.function.func_type, - &import->u.function.signature, - &import->u.function.attachment, - &import->u.function.call_conv_raw))) { -#if WASM_ENABLE_WAMR_COMPILER == 0 /* Output warning except running aot compiler */ - LOG_WARNING("warning: fail to link import function (%s, %s)\n", - module_name, field_name); -#endif - } break; case IMPORT_KIND_TABLE: /* import table */ bh_assert(import_tables); import = import_tables++; - if (!load_table_import(&p, p_end, &import->u.table, - error_buf, error_buf_size)) - return false; - if (module->import_table_count > 1) { - set_error_buf(error_buf, error_buf_size, "multiple tables"); + if (!load_table_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.table, + error_buf, + error_buf_size)) { + LOG_DEBUG("can not import such a table (%s,%s)", + sub_module_name, field_name); return false; } break; @@ -781,12 +1503,14 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, case IMPORT_KIND_MEMORY: /* import memory */ bh_assert(import_memories); import = import_memories++; - if (!load_memory_import(&p, p_end, &import->u.memory, - error_buf, error_buf_size)) - return false; - if (module->import_memory_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: multiple memories"); + if (!load_memory_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.memory, + error_buf, + error_buf_size)) { return false; } break; @@ -794,28 +1518,13 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, case IMPORT_KIND_GLOBAL: /* import global */ bh_assert(import_globals); import = import_globals++; - CHECK_BUF(p, p_end, 2); - import->u.global.type = read_uint8(p); - mutable = read_uint8(p); - if (mutable >= 2) { - set_error_buf(error_buf, error_buf_size, - "Load import section failed: " - "invalid mutability"); + if (!load_global_import(module, + sub_module, + sub_module_name, field_name, &p, + p_end, &import->u.global, + error_buf, error_buf_size)) { return false; } - import->u.global.is_mutable = mutable & 1 ? true : false; -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if (!(wasm_native_lookup_libc_builtin_global( - module_name, field_name, - &import->u.global))) { - if (error_buf != NULL) - snprintf(error_buf, error_buf_size, - "Load import section failed: " - "resolve import global (%s, %s) failed.", - module_name, field_name); - return false; - } -#endif break; default: @@ -825,8 +1534,9 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return false; } import->kind = kind; - import->u.names.module_name = module_name; + import->u.names.module_name = sub_module_name; import->u.names.field_name = field_name; + (void)sub_module; } #if WASM_ENABLE_LIBC_WASI != 0 @@ -850,6 +1560,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, LOG_VERBOSE("Load import section success.\n"); (void)u8; (void)u32; + (void)type_index; return true; } @@ -985,6 +1696,16 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, if (local_count > 0) func->local_types = (uint8*)func + sizeof(WASMFunction); func->code_size = code_size; + /* + * we shall make a copy of code body [p_code, p_code + code_size] + * when we are worrying about inappropriate releasing behaviour. + * all code bodies are actually in a buffer which user allocates in + * his embedding environment and we don't have power on them. + * it will be like: + * code_body_cp = malloc(code_size); + * memcpy(code_body_cp, p_code, code_size); + * func->code = code_body_cp; + */ func->code = (uint8*)p_code; /* Load each local type */ @@ -1045,13 +1766,13 @@ load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMTable *table; read_leb_uint32(p, p_end, table_count); + /* a total of one table is allowed */ + if (module->import_table_count + table_count > 1) { + set_error_buf(error_buf, error_buf_size, "multiple tables"); + return false; + } if (table_count) { - if (table_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load table section failed: multiple tables"); - return false; - } module->table_count = table_count; total_size = sizeof(WASMTable) * (uint64)table_count; if (total_size >= UINT32_MAX @@ -1090,13 +1811,13 @@ load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMMemory *memory; read_leb_uint32(p, p_end, memory_count); + /* a total of one memory is allowed */ + if (module->import_memory_count + memory_count > 1) { + set_error_buf(error_buf, error_buf_size, "multiple memories"); + return false; + } if (memory_count) { - if (memory_count > 1) { - set_error_buf(error_buf, error_buf_size, - "Load memory section failed: multiple memories"); - return false; - } module->memory_count = memory_count; total_size = sizeof(WASMMemory) * (uint64)memory_count; if (total_size >= UINT32_MAX @@ -1168,6 +1889,20 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (!load_init_expr(&p, p_end, &(global->init_expr), global->type, error_buf, error_buf_size)) return false; + + if (INIT_EXPR_TYPE_GET_GLOBAL == global->init_expr.init_expr_type) { + /** + * Currently, constant expressions occurring as initializers + * of globals are further constrained in that contained + * global.get instructions are + * only allowed to refer to imported globals. + */ + uint32 target_global_index = global->init_expr.u.global_index; + if (target_global_index >= module->import_global_count) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + } } } @@ -1186,10 +1921,11 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; - uint32 export_count, i, index; + uint32 export_count, i, j, index; uint64 total_size; uint32 str_len; WASMExport *export; + const char *name; read_leb_uint32(p, p_end, export_count); @@ -1210,10 +1946,22 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, for (i = 0; i < export_count; i++, export++) { read_leb_uint32(p, p_end, str_len); CHECK_BUF(p, p_end, str_len); + + for (j = 0; j < i; j++) { + name = module->exports[j].name; + if (strlen(name) == str_len + && memcmp(name, p, str_len) == 0) { + set_error_buf(error_buf, error_buf_size, + "duplicate export name"); + return false; + } + } + if (!(export->name = const_str_list_insert(p, str_len, module, error_buf, error_buf_size))) { return false; } + p += str_len; CHECK_BUF(p, p_end, 1); export->kind = read_uint8(p); @@ -1309,6 +2057,13 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *m return false; } read_leb_uint32(p, p_end, table_index); + if (table_index + >= module->import_table_count + module->table_count) { + LOG_DEBUG("table#%d does not exist", table_index); + set_error_buf(error_buf, error_buf_size, "unknown table"); + return false; + } + table_segment->table_index = table_index; /* initialize expression */ @@ -1329,6 +2084,12 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *m } for (j = 0; j < function_count; j++) { read_leb_uint32(p, p_end, function_index); + if (function_index >= module->function_count + module->function_count) { + set_error_buf(error_buf, error_buf_size, + "Load table segment section failed: " + "unknown function"); + return false; + } table_segment->func_indexes[j] = function_index; } } @@ -1354,9 +2115,22 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, uint64 total_size; WASMDataSeg *dataseg; InitializerExpression init_expr; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive = false; + uint32 mem_flag; +#endif read_leb_uint32(p, p_end, data_seg_count); +#if WASM_ENABLE_BULK_MEMORY != 0 + if ((module->data_seg_count1 != 0) + && (data_seg_count != module->data_seg_count1)) { + set_error_buf(error_buf, error_buf_size, + "data count and data section have inconsistent lengths"); + return false; + } +#endif + if (data_seg_count) { module->data_seg_count = data_seg_count; total_size = sizeof(WASMDataSeg*) * (uint64)data_seg_count; @@ -1372,10 +2146,50 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, for (i = 0; i < data_seg_count; i++) { read_leb_uint32(p, p_end, mem_index); - - if (!load_init_expr(&p, p_end, &init_expr, VALUE_TYPE_I32, - error_buf, error_buf_size)) +#if WASM_ENABLE_BULK_MEMORY != 0 + is_passive = false; + mem_flag = mem_index & 0x03; + switch (mem_flag) { + case 0x01: + is_passive = true; + break; + case 0x00: + /* no memory index, treat index as 0 */ + mem_index = 0; + goto check_mem_index; + break; + case 0x02: + /* read following memory index */ + read_leb_uint32(p, p_end, mem_index); +check_mem_index: + if (mem_index + >= module->import_memory_count + module->memory_count) { + LOG_DEBUG("memory#%d does not exist", mem_index); + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } + break; + case 0x03: + default: + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + break; + } +#else + if (mem_index + >= module->import_memory_count + module->memory_count) { + LOG_DEBUG("memory#%d does not exist", mem_index); + set_error_buf(error_buf, error_buf_size, "unknown memory"); return false; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + if (!load_init_expr(&p, p_end, &init_expr, VALUE_TYPE_I32, + error_buf, error_buf_size)) + return false; read_leb_uint32(p, p_end, data_seg_len); @@ -1387,10 +2201,17 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, return false; } - bh_memcpy_s(&dataseg->base_offset, sizeof(InitializerExpression), - &init_expr, sizeof(InitializerExpression)); +#if WASM_ENABLE_BULK_MEMORY != 0 + dataseg->is_passive = is_passive; + if (!is_passive) +#endif + { + bh_memcpy_s(&dataseg->base_offset, sizeof(InitializerExpression), + &init_expr, sizeof(InitializerExpression)); + + dataseg->memory_index = mem_index; + } - dataseg->memory_index = mem_index; dataseg->data_length = data_seg_len; CHECK_BUF(p, p_end, data_seg_len); dataseg->data = (uint8*)p; @@ -1408,6 +2229,28 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, return true; } +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +load_datacount_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count1 = 0; + + read_leb_uint32(p, p_end, data_seg_count1); + module->data_seg_count1 = data_seg_count1; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, + "Load datacount section failed: section size mismatch"); + return false; + } + + LOG_VERBOSE("Load datacount section success.\n"); + return true; +} +#endif + static bool load_code_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_func, @@ -1447,23 +2290,24 @@ load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, read_leb_uint32(p, p_end, start_function); - if (start_function >= module->function_count - + module->import_function_count) { + if (start_function + >= module->function_count + module->import_function_count) { set_error_buf(error_buf, error_buf_size, - "Load start section failed: " - "unknown function."); + "Load start section failed: " + "unknown function."); return false; } if (start_function < module->import_function_count) type = module->import_functions[start_function].u.function.func_type; else - type = module->functions - [start_function - module->import_function_count]->func_type; + type = + module->functions[start_function - module->import_function_count] + ->func_type; if (type->param_count != 0 || type->result_count != 0) { set_error_buf(error_buf, error_buf_size, - "Load start section failed: " - "invalid start function."); + "Load start section failed: " + "invalid start function."); return false; } @@ -1560,6 +2404,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, while (section) { buf = section->section_body; buf_end = buf + section->section_body_size; + LOG_DEBUG("to section %d", section->section_type); switch (section->section_type) { case SECTION_TYPE_USER: /* unsupported user section, ignore it. */ @@ -1612,6 +2457,12 @@ load_from_sections(WASMModule *module, WASMSection *sections, if (!load_data_segment_section(buf, buf_end, module, error_buf, error_buf_size)) return false; break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case SECTION_TYPE_DATACOUNT: + if (!load_datacount_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; +#endif default: set_error_buf(error_buf, error_buf_size, "WASM module load failed: invalid section id"); @@ -1781,6 +2632,9 @@ 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; } @@ -1812,6 +2666,37 @@ destroy_sections(WASMSection *section_list) } } +static uint8 section_ids[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM, +#if WASM_ENABLE_BULK_MEMORY != 0 + SECTION_TYPE_DATACOUNT, +#endif + SECTION_TYPE_CODE, + SECTION_TYPE_DATA +}; + +static uint8 +get_section_index(uint8 section_type) +{ + uint8 max_id = sizeof(section_ids) / sizeof(uint8); + + for (uint8 i = 0; i < max_id; i++) { + if (section_type == section_ids[i]) + return i; + } + + return (uint8)-1; +} + static bool create_sections(const uint8 *buf, uint32 size, WASMSection **p_section_list, @@ -1819,7 +2704,7 @@ create_sections(const uint8 *buf, uint32 size, { WASMSection *section_list_end = NULL, *section; const uint8 *p = buf, *p_end = buf + size/*, *section_body*/; - uint8 section_type, last_section_type = (uint8)-1; + uint8 section_type, section_index, last_section_index = (uint8)-1; uint32 section_size; bh_assert(!*p_section_list); @@ -1828,19 +2713,20 @@ create_sections(const uint8 *buf, uint32 size, while (p < p_end) { CHECK_BUF(p, p_end, 1); section_type = read_uint8(p); - if (section_type <= SECTION_TYPE_DATA) { + section_index = get_section_index(section_type); + if (section_index != (uint8)-1) { if (section_type != SECTION_TYPE_USER) { /* Custom sections may be inserted at any place, while other sections must occur at most once and in prescribed order. */ - if (last_section_type != (uint8)-1 - && section_type <= last_section_type) { + if (last_section_index != (uint8)-1 + && (section_index <= last_section_index)) { set_error_buf(error_buf, error_buf_size, "WASM module load failed: " "junk after last section"); return false; } - last_section_type = section_type; + last_section_index = section_index; } CHECK_BUF1(p, p_end, 1); read_leb_uint32(p, p_end, section_size); @@ -1940,25 +2826,17 @@ load(const uint8 *buf, uint32 size, WASMModule *module, WASMModule* wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size) { - WASMModule *module = wasm_runtime_malloc(sizeof(WASMModule)); - + WASMModule *module = create_module(error_buf, error_buf_size); if (!module) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: allocate memory failed."); return NULL; } - memset(module, 0, sizeof(WASMModule)); - - module->module_type = Wasm_Module_Bytecode; - - /* Set start_function to -1, means no start function */ - module->start_function = (uint32)-1; - - if (!load(buf, size, module, error_buf, error_buf_size)) + if (!load(buf, size, module, error_buf, error_buf_size)) { + LOG_VERBOSE("Load module failed, %s", error_buf); goto fail; + } - LOG_VERBOSE("Load module success.\n"); + LOG_VERBOSE("Load module success"); return module; fail: @@ -2039,6 +2917,30 @@ wasm_loader_unload(WASMModule *module) } } +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just release the sub module list */ + if (module->import_module_list) { + WASMRegisteredModule *node = + bh_list_first_elem(module->import_module_list); + while (node) { + WASMRegisteredModule *next = bh_list_elem_next(node); + bh_list_remove(module->import_module_list, node); + /* + * unload(sub_module) will be trigged during runtime_destroy(). + * every module in the global module list will be unloaded one by + * one. so don't worry. + */ + wasm_runtime_free(node); + /* + * + * the module file reading buffer will be released + * in runtime_destroy() + */ + node = next; + } + } +#endif + wasm_runtime_free(module); } @@ -2359,10 +3261,45 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, case WASM_OP_I64_EXTEND32_S: break; case WASM_OP_MISC_PREFIX: - /* skip extend op */ - if (p < code_end_addr) - p++; + { + opcode = read_uint8(p); + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + skip_leb_uint32(p, p_end); + /* skip memory idx */ + p++; + break; + case WASM_OP_DATA_DROP: + skip_leb_uint32(p, p_end); + break; + case WASM_OP_MEMORY_COPY: + /* skip two memory idx */ + p += 2; + break; + case WASM_OP_MEMORY_FILL: + /* skip memory idx */ + p++; + break; +#endif + default: + if (error_buf) + snprintf(error_buf, error_buf_size, + "WASM loader find block addr failed: " + "invalid opcode fc %02x.", opcode); + return false; + } break; + } default: if (error_buf) @@ -2383,6 +3320,7 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, #define REF_I64_2 VALUE_TYPE_I64 #define REF_F64_1 VALUE_TYPE_F64 #define REF_F64_2 VALUE_TYPE_F64 +#define REF_ANY VALUE_TYPE_ANY #if WASM_ENABLE_FAST_INTERP != 0 @@ -2404,8 +3342,6 @@ typedef struct BranchBlockPatch { typedef struct BranchBlock { uint8 block_type; uint8 return_type; - bool is_block_reachable; - bool skip_else_branch; uint8 *start_addr; uint8 *else_addr; uint8 *end_addr; @@ -2415,6 +3351,14 @@ typedef struct BranchBlock { uint8 *code_compiled; BranchBlockPatch *patch_list; #endif + + /* Indicate the operand stack is in polymorphic state. + * If the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed. + * If stack is in polymorphic state and stack is empty, instruction can + * pop any type of value directly without decreasing stack top pointer + * and stack cell num. */ + bool is_stack_polymorphic; } BranchBlock; typedef struct WASMLoaderContext { @@ -2519,19 +3463,28 @@ static bool check_offset_push(WASMLoaderContext *ctx, char *error_buf, uint32 error_buf_size) { + uint32 cell_num = (ctx->frame_offset - ctx->frame_offset_bottom); if (ctx->frame_offset >= ctx->frame_offset_boundary) { MEM_REALLOC(ctx->frame_offset_bottom, ctx->frame_offset_size, ctx->frame_offset_size + 16); ctx->frame_offset_size += 16; ctx->frame_offset_boundary = ctx->frame_offset_bottom + ctx->frame_offset_size / sizeof(int16); - ctx->frame_offset = ctx->frame_offset_bottom + ctx->stack_cell_num; + ctx->frame_offset = ctx->frame_offset_bottom + cell_num; } return true; fail: return false; } +static bool +check_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->frame_offset - cells < ctx->frame_offset_bottom) + return false; + return true; +} + static void free_label_patch_list(BranchBlock *frame_csp) { BranchBlockPatch *label_patch = frame_csp->patch_list; @@ -2572,38 +3525,58 @@ fail: return false; } + static bool -check_stack_pop(WASMLoaderContext *ctx, uint8 type, - char *error_buf, uint32 error_buf_size, - const char *type_str) +check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, + char *error_buf, uint32 error_buf_size) { - uint32 block_stack_cell_num = ctx->stack_cell_num - - (ctx->frame_csp - 1)->stack_cell_num; + char *type_str[] = { "f64", "f32", "i64", "i32" }; if (((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) - && block_stack_cell_num < 1) + && stack_cell_num < 1) || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) - && block_stack_cell_num < 2)) { + && stack_cell_num < 2)) { set_error_buf(error_buf, error_buf_size, "WASM module load failed: " "type mismatch: expect data but stack was empty"); return false; } - if ((type == VALUE_TYPE_I32 && *(ctx->frame_ref - 1) != REF_I32) - || (type == VALUE_TYPE_F32 && *(ctx->frame_ref - 1) != REF_F32) + if ((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) + || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) || (type == VALUE_TYPE_I64 - && (*(ctx->frame_ref - 2) != REF_I64_1 - || *(ctx->frame_ref - 1) != REF_I64_2)) + && (*(frame_ref - 2) != REF_I64_1 + || *(frame_ref - 1) != REF_I64_2)) || (type == VALUE_TYPE_F64 - && (*(ctx->frame_ref - 2) != REF_F64_1 - || *(ctx->frame_ref - 1) != REF_F64_2))) { + && (*(frame_ref - 2) != REF_F64_1 + || *(frame_ref - 1) != REF_F64_2))) { if (error_buf != NULL) snprintf(error_buf, error_buf_size, "%s%s%s", "WASM module load failed: type mismatch: expect ", - type_str, " but got other"); + type_str[type - VALUE_TYPE_F64], " but got other"); return false; } + + return true; +} + +static bool +check_stack_pop(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + int32 block_stack_cell_num = (int32) + (ctx->stack_cell_num - (ctx->frame_csp - 1)->stack_cell_num); + + if (block_stack_cell_num > 0 + && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + /* the stack top is a value of any type, return success */ + return true; + } + + if (!check_stack_top_values(ctx->frame_ref, block_stack_cell_num, + type, error_buf, error_buf_size)) + return false; + return true; } @@ -2693,7 +3666,9 @@ wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, if (ctx->stack_cell_num > ctx->max_stack_cell_num) ctx->max_stack_cell_num = ctx->stack_cell_num; - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || type == VALUE_TYPE_ANY) return true; if (!check_stack_push(ctx, error_buf, error_buf_size)) @@ -2709,18 +3684,27 @@ static bool wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, char *error_buf, uint32 error_buf_size) { - char *type_str[] = { "f64", "f32", "i64", "i32" }; + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + if (type == VALUE_TYPE_VOID) return true; - if (!check_stack_pop(ctx, type, error_buf, error_buf_size, - type_str[type - VALUE_TYPE_F64])) + if (!check_stack_pop(ctx, type, error_buf, error_buf_size)) return false; ctx->frame_ref--; ctx->stack_cell_num--; - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || *ctx->frame_ref == VALUE_TYPE_ANY) return true; ctx->frame_ref--; @@ -2770,17 +3754,7 @@ static bool wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, char *error_buf, uint32 error_buf_size) { - uint8 block_return_type; - CHECK_CSP_POP(); - - block_return_type = (ctx->frame_csp - 1)->return_type; - if (!wasm_loader_pop_frame_ref(ctx, block_return_type, - error_buf, error_buf_size) - || !wasm_loader_push_frame_ref(ctx, block_return_type, - error_buf, error_buf_size)) - goto fail; - ctx->frame_csp--; ctx->csp_num--; return true; @@ -2792,32 +3766,30 @@ static bool wasm_loader_check_br(WASMLoaderContext *ctx, uint32 depth, char *error_buf, uint32 error_buf_size) { + BranchBlock *target_block, *cur_block; + int32 available_stack_cell; + if (ctx->csp_num < depth + 1) { set_error_buf(error_buf, error_buf_size, "WASM module load failed: unknown label, " "unexpected end of section or function"); return false; } - if ((ctx->frame_csp - (depth + 1))->block_type != BLOCK_TYPE_LOOP) { - uint8 tmp_ret_type = (ctx->frame_csp - (depth + 1))->return_type; - if ((tmp_ret_type == VALUE_TYPE_I32 - && (ctx->stack_cell_num < 1 || *(ctx->frame_ref - 1) != REF_I32)) - || (tmp_ret_type == VALUE_TYPE_F32 - && (ctx->stack_cell_num < 1 || *(ctx->frame_ref - 1) != REF_F32)) - || (tmp_ret_type == VALUE_TYPE_I64 - && (ctx->stack_cell_num < 2 - || *(ctx->frame_ref - 2) != REF_I64_1 - || *(ctx->frame_ref - 1) != REF_I64_2)) - || (tmp_ret_type == VALUE_TYPE_F64 - && (ctx->stack_cell_num < 2 - || *(ctx->frame_ref - 2) != REF_F64_1 - || *(ctx->frame_ref - 1) != REF_F64_2))) { - set_error_buf(error_buf, error_buf_size, - "WASM module load failed: type mismatch: " - "expect data but stack was empty or other type"); + + target_block = ctx->frame_csp - (depth + 1); + cur_block = ctx->frame_csp - 1; + + available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 && target_block->is_stack_polymorphic) + return true; + + if (target_block->block_type != BLOCK_TYPE_LOOP) { + uint8 type = target_block->return_type; + if (!check_stack_top_values(ctx->frame_ref, available_stack_cell, + type, error_buf, error_buf_size)) return false; - } - (ctx->frame_csp - (depth + 1))->is_block_reachable = true; } return true; } @@ -3209,22 +4181,45 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, return true; } -/* The frame_offset stack should always keep the same depth with - frame_ref, so we don't check pop of frame_offset */ +/* This function should be in front of wasm_loader_pop_frame_ref + as they both use ctx->stack_cell_num, and ctx->stack_cell_num + will be modified by wasm_loader_pop_frame_ref */ static bool wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, char *error_buf, uint32 error_buf_size) { + /* if ctx->frame_csp equals ctx->frame_csp_bottom, + then current block is the function block */ + uint32 depth = ctx->frame_csp > ctx->frame_csp_bottom ? 1 : 0; + BranchBlock *cur_block = ctx->frame_csp - depth; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + if (type == VALUE_TYPE_VOID) return true; if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + /* Check the offset stack bottom to ensure the frame offset + stack will not go underflow. But we don't thrown error + and return true here, because the error msg should be + given in wasm_loader_pop_frame_ref */ + if (!check_offset_pop(ctx, 1)) + return true; + ctx->frame_offset -= 1; if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) ctx->dynamic_offset -= 1; } else { + if (!check_offset_pop(ctx, 2)) + return true; + ctx->frame_offset -= 2; if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) @@ -3257,11 +4252,11 @@ wasm_loader_push_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, bool disable_emit, int16 operand_offset, char *error_buf, uint32 error_buf_size) { - if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) - return false; if (!(wasm_loader_push_frame_offset(ctx, type, disable_emit, operand_offset, error_buf, error_buf_size))) return false; + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) + return false; return true; } @@ -3270,10 +4265,11 @@ static bool wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, char *error_buf, uint32 error_buf_size) { - if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) - return false; + /* put wasm_loader_pop_frame_offset in front of wasm_loader_pop_frame_ref */ if (!wasm_loader_pop_frame_offset(ctx, type, error_buf, error_buf_size)) return false; + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) + return false; return true; } @@ -3284,13 +4280,13 @@ wasm_loader_push_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 pop_cnt, bool disable_emit, int16 operand_offset, char *error_buf, uint32 error_buf_size) { - if (!wasm_loader_push_pop_frame_ref(ctx, pop_cnt, type_push, type_pop, - error_buf, error_buf_size)) - return false; if (!wasm_loader_push_pop_frame_offset(ctx, pop_cnt, type_push, type_pop, disable_emit, operand_offset, error_buf, error_buf_size)) return false; + if (!wasm_loader_push_pop_frame_ref(ctx, pop_cnt, type_push, type_pop, + error_buf, error_buf_size)) + return false; return true; } @@ -3656,6 +4652,24 @@ check_memory(WASMModule *module, goto fail; \ } while (0) +static bool +check_memory_access_align(uint8 opcode, uint32 align, + char *error_buf, uint32 error_buf_size) +{ + uint8 mem_access_aligns[] = { + 2, 3, 2, 3, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, /* loads */ + 2, 3, 2, 3, 0, 1, 0, 1, 2 /* stores */ + }; + bh_assert(opcode >= WASM_OP_I32_LOAD + && opcode <= WASM_OP_I64_STORE32); + if (align > mem_access_aligns[opcode - WASM_OP_I32_LOAD]) { + set_error_buf(error_buf, error_buf_size, + "alignment must not be larger than natural"); + return false; + } + return true; +} + static bool is_block_type_valid(uint8 type) { @@ -3701,9 +4715,22 @@ check_branch_block_ret(WASMLoaderContext *loader_ctx, BranchBlock *frame_csp_tmp, char *error_buf, uint32 error_buf_size) { - frame_csp_tmp->is_block_reachable = true; +#if WASM_ENABLE_FAST_INTERP != 0 + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + bool disable_emit = true; + int16 operand_offset = 0; +#endif if (frame_csp_tmp->block_type != BLOCK_TYPE_LOOP) { uint8 block_return_type = frame_csp_tmp->return_type; +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the same + with ref stack */ + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(block_return_type); + PUSH_OFFSET_TYPE(block_return_type); + } +#endif POP_TYPE(block_return_type); PUSH_TYPE(block_return_type); } @@ -3712,6 +4739,92 @@ fail: return false; } +static bool +check_block_stack(WASMLoaderContext *ctx, BranchBlock *block, + char *error_buf, uint32 error_buf_size) +{ + uint8 type = block->return_type; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - block->stack_cell_num); + + if (type != VALUE_TYPE_VOID + && available_stack_cell <= 0 + && block->is_stack_polymorphic) { + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size)) +#if WASM_ENABLE_FAST_INTERP != 0 + || !(wasm_loader_push_frame_offset(ctx, type, true, 0, error_buf, error_buf_size)) +#endif + ) + return false; + return true; + } + + if (type != VALUE_TYPE_VOID + && available_stack_cell == 1 + && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + /* If the stack top is a value of any type, change its type to the + * same as block return type and return success */ + *(ctx->frame_ref - 1) = type; + } + else { + if (!(wasm_loader_push_frame_ref(ctx, VALUE_TYPE_I32, + error_buf, error_buf_size)) +#if WASM_ENABLE_FAST_INTERP != 0 + || !(wasm_loader_push_frame_offset(ctx, VALUE_TYPE_I32, + true, 0, + error_buf, error_buf_size)) +#endif + ) + return false; + *(ctx->frame_ref - 1) = *(ctx->frame_ref - 2) = type; + } + return true; + } + + if (((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + && available_stack_cell != 1) + || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + && available_stack_cell != 2) + || (type == VALUE_TYPE_VOID && available_stack_cell > 0)) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: " + "type mismatch: stack size does not match block type"); + return false; + } + + if (!check_stack_top_values(ctx->frame_ref, available_stack_cell, + type, error_buf, error_buf_size)) + return false; + + return true; +} + +/* reset the stack to the state of before entering the last block */ +#if WASM_ENABLE_FAST_INTERP != 0 +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + loader_ctx->frame_offset = \ + loader_ctx->frame_offset_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#else +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#endif + +/* set current block's stack polymorphic state */ +#define SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(flag) do { \ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; \ + cur_block->is_stack_polymorphic = flag; \ +} while (0) + static bool wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, BlockAddr *block_addr_cache, @@ -3725,9 +4838,12 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, int32 i32, i32_const = 0; int64 i64; uint8 opcode, u8, block_return_type; - bool return_value = false, is_i32_const = false; + bool return_value = false; WASMLoaderContext *loader_ctx; BranchBlock *frame_csp_tmp; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 segment_index; +#endif #if WASM_ENABLE_FAST_INTERP != 0 uint8 *func_const_end, *func_const; int16 operand_offset; @@ -3775,7 +4891,6 @@ re_scan: #endif PUSH_CSP(BLOCK_TYPE_FUNCTION, ret_type, p); - (loader_ctx->frame_csp - 1)->is_block_reachable = true; while (p < p_end) { opcode = *p++; @@ -3787,7 +4902,9 @@ re_scan: switch (opcode) { case WASM_OP_UNREACHABLE: - goto handle_next_reachable_block; + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; case WASM_OP_NOP: #if WASM_ENABLE_FAST_INTERP != 0 @@ -3827,39 +4944,6 @@ re_scan: emit_empty_label_addr_and_frame_ip(PATCH_ELSE); emit_empty_label_addr_and_frame_ip(PATCH_END); #endif - if (!is_i32_const) - (loader_ctx->frame_csp - 1)->is_block_reachable = true; - else { - if (!wasm_loader_find_block_addr(block_addr_cache, - (loader_ctx->frame_csp - 1)->start_addr, - p_end, - (loader_ctx->frame_csp - 1)->block_type, - &(loader_ctx->frame_csp - 1)->else_addr, - &(loader_ctx->frame_csp - 1)->end_addr, - error_buf, error_buf_size)) - goto fail; - - if (!i32_const) { - if ((loader_ctx->frame_csp - 1)->else_addr) { -#if WASM_ENABLE_FAST_INTERP != 0 - loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + - (loader_ctx->frame_csp - 1)->stack_cell_num; - apply_label_patch(loader_ctx, 1, PATCH_ELSE); -#endif - p = (loader_ctx->frame_csp - 1)->else_addr + 1; - } - else { - p = (loader_ctx->frame_csp - 1)->end_addr; - } - - is_i32_const = false; - continue; - } - else { - /* The else branch cannot be reached, ignored it. */ - (loader_ctx->frame_csp - 1)->skip_else_branch = true; - } - } break; case WASM_OP_ELSE: @@ -3871,32 +4955,43 @@ re_scan: goto fail; } - if ((loader_ctx->frame_csp - 1)->skip_else_branch) { - /* The else branch is ignored. */ - is_i32_const = false; - p = (loader_ctx->frame_csp - 1)->end_addr; -#if WASM_ENABLE_FAST_INTERP != 0 - skip_label(); -#endif - continue; - } + /* check whether if branch's stack matches its result type */ + if (!check_block_stack(loader_ctx, loader_ctx->frame_csp - 1, + error_buf, error_buf_size)) + goto fail; (loader_ctx->frame_csp - 1)->else_addr = p - 1; - loader_ctx->stack_cell_num = (loader_ctx->frame_csp - 1)->stack_cell_num; - loader_ctx->frame_ref = loader_ctx->frame_ref_bottom + - loader_ctx->stack_cell_num; + #if WASM_ENABLE_FAST_INTERP != 0 /* if the result of if branch is in local or const area, add a copy op */ RESERVE_BLOCK_RET(); - loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + - loader_ctx->stack_cell_num; + emit_empty_label_addr_and_frame_ip(PATCH_END); apply_label_patch(loader_ctx, 1, PATCH_ELSE); #endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); break; case WASM_OP_END: { + + /* check whether block stack matches its result type */ + if (!check_block_stack(loader_ctx, loader_ctx->frame_csp - 1, + error_buf, error_buf_size)) + goto fail; + + /* if has return value, but no else branch, fail */ + if ((loader_ctx->frame_csp - 1)->block_type == BLOCK_TYPE_IF + && (loader_ctx->frame_csp - 1)->return_type != VALUE_TYPE_VOID + && !(loader_ctx->frame_csp - 1)->else_addr) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: " + "type mismatch: if has return value and else is missing"); + + goto fail; + } + POP_CSP(); #if WASM_ENABLE_FAST_INTERP != 0 @@ -3919,9 +5014,10 @@ re_scan: ignore the following bytecodes */ p = p_end; - is_i32_const = false; continue; } + + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); break; } @@ -3935,50 +5031,9 @@ re_scan: error_buf, error_buf_size)) goto fail; -handle_next_reachable_block: - for (i = 1; i <= loader_ctx->csp_num; i++) - if ((loader_ctx->frame_csp - i)->is_block_reachable) - break; - - block_return_type = (loader_ctx->frame_csp - i)->return_type; - - if(!wasm_loader_find_block_addr(block_addr_cache, - (loader_ctx->frame_csp - i)->start_addr, - p_end, - (loader_ctx->frame_csp - i)->block_type, - &(loader_ctx->frame_csp - i)->else_addr, - &(loader_ctx->frame_csp - i)->end_addr, - error_buf, error_buf_size)) - goto fail; - - loader_ctx->stack_cell_num = (loader_ctx->frame_csp - i)->stack_cell_num; - loader_ctx->frame_ref = loader_ctx->frame_ref_bottom + - loader_ctx->stack_cell_num; - loader_ctx->csp_num -= i - 1; - loader_ctx->frame_csp -= i - 1; - - if ((loader_ctx->frame_csp - 1)->block_type == BLOCK_TYPE_IF - && (loader_ctx->frame_csp - 1)->else_addr != NULL - && p <= (loader_ctx->frame_csp - 1)->else_addr) { -#if WASM_ENABLE_FAST_INTERP != 0 - loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + - (loader_ctx->frame_csp - 1)->stack_cell_num; - apply_label_patch(loader_ctx, 1, PATCH_ELSE); -#endif - p = (loader_ctx->frame_csp - 1)->else_addr + 1; - - } - else { - p = (loader_ctx->frame_csp - 1)->end_addr; - PUSH_TYPE(block_return_type); -#if WASM_ENABLE_FAST_INTERP != 0 - loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + - loader_ctx->stack_cell_num; -#endif - } - - is_i32_const = false; - continue; + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; } case WASM_OP_BR_IF: @@ -3989,19 +5044,17 @@ handle_next_reachable_block: error_buf, error_buf_size))) goto fail; - if (!is_i32_const || i32_const) { - /* The branch can be reached */ - if (!check_branch_block_ret(loader_ctx, frame_csp_tmp, - error_buf, error_buf_size)) - goto fail; - } - if (is_i32_const && i32_const) - goto handle_next_reachable_block; + if (!check_branch_block_ret(loader_ctx, frame_csp_tmp, + error_buf, error_buf_size)) + goto fail; + break; } case WASM_OP_BR_TABLE: { + uint8 ret_type; + read_leb_uint32(p, p_end, count); #if WASM_ENABLE_FAST_INTERP != 0 emit_const(count); @@ -4011,15 +5064,34 @@ handle_next_reachable_block: /* TODO: check the const */ for (i = 0; i <= count; i++) { if (!(frame_csp_tmp = check_branch_block(loader_ctx, &p, p_end, - error_buf, error_buf_size))) + error_buf, error_buf_size))) goto fail; if (!check_branch_block_ret(loader_ctx, frame_csp_tmp, error_buf, error_buf_size)) goto fail; + + if (i == 0) { + ret_type = frame_csp_tmp->block_type == BLOCK_TYPE_LOOP ? + VALUE_TYPE_VOID : frame_csp_tmp->return_type; + } + else { + /* Check whether all table items have the same return type */ + uint8 tmp_ret_type = frame_csp_tmp->block_type == BLOCK_TYPE_LOOP ? + VALUE_TYPE_VOID : frame_csp_tmp->return_type; + if (ret_type != tmp_ret_type) { + set_error_buf(error_buf, error_buf_size, + "WASM loader prepare bytecode failed: " + "type mismatch: br_table targets must " + "all use same result type"); + goto fail; + } + } } - goto handle_next_reachable_block; + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; } case WASM_OP_RETURN: @@ -4030,12 +5102,11 @@ handle_next_reachable_block: #if WASM_ENABLE_FAST_INTERP != 0 // emit the offset after return opcode POP_OFFSET_TYPE(ret_type); - loader_ctx->frame_offset = loader_ctx->frame_offset_bottom + - loader_ctx->stack_cell_num; #endif - is_i32_const = false; - goto handle_next_reachable_block; + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; } case WASM_OP_CALL: @@ -4145,8 +5216,12 @@ handle_next_reachable_block: case WASM_OP_DROP: case WASM_OP_DROP_64: { - if (loader_ctx->stack_cell_num - - (loader_ctx->frame_csp - 1)->stack_cell_num <= 0) { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { set_error_buf(error_buf, error_buf_size, "WASM loader prepare bytecode failed: " "type mismatch, opcode drop was found " @@ -4154,38 +5229,37 @@ handle_next_reachable_block: goto fail; } - if (*(loader_ctx->frame_ref - 1) == REF_I32 - || *(loader_ctx->frame_ref - 1) == REF_F32) { - loader_ctx->frame_ref--; - loader_ctx->stack_cell_num--; + if (available_stack_cell > 0) { + if (*(loader_ctx->frame_ref - 1) == REF_I32 + || *(loader_ctx->frame_ref - 1) == REF_F32) { + loader_ctx->frame_ref--; + loader_ctx->stack_cell_num--; #if WASM_ENABLE_FAST_INTERP != 0 - skip_label(); - loader_ctx->frame_offset--; - if (*(loader_ctx->frame_offset) > - loader_ctx->start_dynamic_offset) - loader_ctx->dynamic_offset --; + skip_label(); + loader_ctx->frame_offset--; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset --; #endif + } + else { + loader_ctx->frame_ref -= 2; + loader_ctx->stack_cell_num -= 2; +#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) + *(p - 1) = WASM_OP_DROP_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 2; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset -= 2; +#endif + } } else { - if (loader_ctx->stack_cell_num - - (loader_ctx->frame_csp - 1)->stack_cell_num <= 0) { - set_error_buf(error_buf, error_buf_size, - "WASM loader prepare bytecode failed: " - "type mismatch, opcode drop was found " - "but stack was empty"); - goto fail; - } - loader_ctx->frame_ref -= 2; - loader_ctx->stack_cell_num -= 2; -#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) - *(p - 1) = WASM_OP_DROP_64; -#endif #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); - loader_ctx->frame_offset -= 2; - if (*(loader_ctx->frame_offset) > - loader_ctx->start_dynamic_offset) - loader_ctx->dynamic_offset -= 2; #endif } break; @@ -4195,10 +5269,16 @@ handle_next_reachable_block: case WASM_OP_SELECT_64: { uint8 ref_type; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell; POP_I32(); - if (loader_ctx->stack_cell_num <= 0) { + available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { set_error_buf(error_buf, error_buf_size, "WASM loader prepare bytecode failed: " "type mismatch, opcode select was found " @@ -4206,38 +5286,50 @@ handle_next_reachable_block: goto fail; } - switch (*(loader_ctx->frame_ref - 1)) { - case REF_I32: - case REF_F32: - break; - case REF_I64_2: - case REF_F64_2: + if (available_stack_cell > 0) { + switch (*(loader_ctx->frame_ref - 1)) { + case REF_I32: + case REF_F32: + break; + case REF_I64_2: + case REF_F64_2: #if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) - *(p - 1) = WASM_OP_SELECT_64; + *(p - 1) = WASM_OP_SELECT_64; #endif #if WASM_ENABLE_FAST_INTERP != 0 - if (loader_ctx->p_code_compiled) { + if (loader_ctx->p_code_compiled) { #if WASM_ENABLE_ABS_LABEL_ADDR != 0 - *(void**)(loader_ctx->p_code_compiled - 2 - sizeof(void*)) = - handle_table[WASM_OP_SELECT_64]; + *(void**)(loader_ctx->p_code_compiled - 2 - sizeof(void*)) = + handle_table[WASM_OP_SELECT_64]; #else - *((int16*)loader_ctx->p_code_compiled - 2) = (int16) - (handle_table[WASM_OP_SELECT_64] - handle_table[0]); + *((int16*)loader_ctx->p_code_compiled - 2) = (int16) + (handle_table[WASM_OP_SELECT_64] - handle_table[0]); #endif - } + } #endif - break; - } + break; + } - ref_type = *(loader_ctx->frame_ref - 1); - POP_TYPE(ref_type); - POP_TYPE(ref_type); - PUSH_TYPE(ref_type); + ref_type = *(loader_ctx->frame_ref - 1); #if WASM_ENABLE_FAST_INTERP != 0 - POP_OFFSET_TYPE(ref_type); - POP_OFFSET_TYPE(ref_type); - PUSH_OFFSET_TYPE(ref_type); + POP_OFFSET_TYPE(ref_type); #endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(VALUE_TYPE_ANY); +#endif + PUSH_TYPE(VALUE_TYPE_ANY); + } break; } @@ -4335,6 +5427,16 @@ handle_next_reachable_block: { p_org = p - 1; GET_LOCAL_INDEX_TYPE_AND_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the same + with ref stack */ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(local_type); + PUSH_OFFSET_TYPE(local_type); + } +#endif POP_TYPE(local_type); PUSH_TYPE(local_type); @@ -4403,6 +5505,7 @@ handle_next_reachable_block: case WASM_OP_SET_GLOBAL: { + bool is_multable = false; read_leb_uint32(p, p_end, global_idx); if (global_idx >= global_count) { set_error_buf(error_buf, error_buf_size, @@ -4411,9 +5514,23 @@ handle_next_reachable_block: goto fail; } - global_type = global_idx < module->import_global_count - ? module->import_globals[global_idx].u.global.type - : module->globals[global_idx - module->import_global_count].type; + is_multable = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.is_mutable + : module->globals[global_idx - module->import_global_count] + .is_mutable; + if (!is_multable) { + set_error_buf(error_buf, + error_buf_size, + "global is immutable"); + goto fail; + } + + global_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.type + : module->globals[global_idx - module->import_global_count] + .type; POP_TYPE(global_type); #if WASM_ENABLE_FAST_INTERP != 0 @@ -4471,6 +5588,10 @@ handle_next_reachable_block: CHECK_MEMORY(); read_leb_uint32(p, p_end, align); /* align */ read_leb_uint32(p, p_end, mem_offset); /* offset */ + if (!check_memory_access_align(opcode, align, + error_buf, error_buf_size)) { + goto fail; + } #if WASM_ENABLE_FAST_INTERP != 0 emit_const(mem_offset); #endif @@ -4556,12 +5677,12 @@ handle_next_reachable_block: case WASM_OP_I32_CONST: read_leb_int32(p, p_end, i32_const); - /* Currently we only track simple I32_CONST opcode. */ - is_i32_const = true; #if WASM_ENABLE_FAST_INTERP != 0 skip_label(); disable_emit = true; GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); +#else + (void)i32_const; #endif PUSH_I32(); break; @@ -4849,6 +5970,92 @@ handle_next_reachable_block: case WASM_OP_I64_TRUNC_SAT_U_F64: POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_const(segment_index); +#endif + if (module->import_memory_count == 0 && module->memory_count == 0) + goto fail_unknown_memory; + + if (*p++ != 0x00) + goto fail_zero_flag_expected; + + if (segment_index >= module->data_seg_count) { + char msg[128]; + snprintf(msg, 128, "WASM loader prepare bytecode failed: " + "unknown data segment %d", segment_index); + set_error_buf(error_buf, error_buf_size, msg); + goto fail; + } + + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_DATA_DROP: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_const(segment_index); +#endif + if (segment_index >= module->data_seg_count) { + set_error_buf(error_buf, error_buf_size, + "WASM loader prepare bytecode failed: " + "unknown data segment"); + goto fail; + } + + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; + + break; + case WASM_OP_MEMORY_COPY: + /* both src and dst memory index should be 0 */ + if (*(int16*)p != 0x0000) + goto fail_zero_flag_expected; + p += 2; + + if (module->import_memory_count == 0 && module->memory_count == 0) + goto fail_unknown_memory; + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_MEMORY_FILL: + if (*p++ != 0x00) { + goto fail_zero_flag_expected; + } + if (module->import_memory_count == 0 && module->memory_count == 0) { + goto fail_unknown_memory; + } + + POP_I32(); + POP_I32(); + POP_I32(); + break; +fail_zero_flag_expected: + set_error_buf(error_buf, error_buf_size, + "WASM loader prepare bytecode failed: " + "zero flag expected"); + goto fail; + +fail_unknown_memory: + set_error_buf(error_buf, error_buf_size, + "WASM loader prepare bytecode failed: " + "unknown memory 0"); + goto fail; +fail_data_cnt_sec_require: + set_error_buf(error_buf, error_buf_size, + "WASM loader prepare bytecode failed: " + "data count section required"); + goto fail; + /* TODO: to support bulk table operation */ +#endif /* WASM_ENABLE_BULK_MEMORY */ default: if (error_buf != NULL) snprintf(error_buf, error_buf_size, @@ -4859,7 +6066,6 @@ handle_next_reachable_block: } break; } - default: if (error_buf != NULL) snprintf(error_buf, error_buf_size, @@ -4868,9 +6074,6 @@ handle_next_reachable_block: goto fail; } - if (opcode != WASM_OP_I32_CONST) - is_i32_const = false; - #if WASM_ENABLE_FAST_INTERP != 0 last_op = opcode; #endif diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index d8a4b274f..c2cd39948 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -262,7 +262,7 @@ typedef enum WASMOpcode { WASM_OP_MISC_PREFIX = 0xfc, } WASMOpcode; -typedef enum WASMEXTOpcode { +typedef enum WASMMiscEXTOpcode { WASM_OP_I32_TRUNC_SAT_S_F32 = 0x00, WASM_OP_I32_TRUNC_SAT_U_F32 = 0x01, WASM_OP_I32_TRUNC_SAT_S_F64 = 0x02, @@ -271,7 +271,16 @@ typedef enum WASMEXTOpcode { WASM_OP_I64_TRUNC_SAT_U_F32 = 0x05, WASM_OP_I64_TRUNC_SAT_S_F64 = 0x06, WASM_OP_I64_TRUNC_SAT_U_F64 = 0x07, -} WASMEXTOpcode; +#if WASM_ENABLE_BULK_MEMORY != 0 + WASM_OP_MEMORY_INIT = 0x08, + WASM_OP_DATA_DROP = 0x09, + WASM_OP_MEMORY_COPY = 0x0a, + WASM_OP_MEMORY_FILL = 0x0b, + WASM_OP_TABLE_INIT = 0x0c, + WASM_OP_ELEM_DROP = 0x0d, + WASM_OP_TABLE_COPY = 0x0e +#endif +} WASMMiscEXTOpcode; #ifdef __cplusplus } diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index bca20e6dc..923005a4d 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -39,22 +39,46 @@ wasm_unload(WASMModule *module) wasm_loader_unload(module); } +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModuleInstance * +get_sub_module_inst(const WASMModuleInstance *parent_module_inst, + const WASMModule *sub_module) +{ + bh_list *sub_module_inst_list = parent_module_inst->sub_module_inst_list; + WASMSubModInstNode *node = bh_list_first_elem(sub_module_inst_list); + + while (node && sub_module != node->module_inst->module) { + node = bh_list_elem_next(node); + } + return node ? node->module_inst : NULL; +} +#endif + /** * Destroy memory instances. */ static void -memories_deinstantiate(WASMMemoryInstance **memories, uint32 count) +memories_deinstantiate(WASMModuleInstance *module_inst, + WASMMemoryInstance **memories, + uint32 count) { uint32 i; if (memories) { for (i = 0; i < count; i++) if (memories[i]) { - if (memories[i]->heap_handle) +#if WASM_ENABLE_MULTI_MODULE != 0 + if (memories[i]->owner != module_inst) + continue; +#endif + if (memories[i]->heap_handle) { mem_allocator_destroy(memories[i]->heap_handle); + memories[i]->heap_handle = NULL; + } wasm_runtime_free(memories[i]); } wasm_runtime_free(memories); } + (void)module_inst; } static WASMMemoryInstance* @@ -89,8 +113,9 @@ memory_instantiate(uint32 num_bytes_per_page, bh_assert(memory->end_addr - (uint8*)memory == (uint32)total_size); /* Initialize heap */ - if (!(memory->heap_handle = mem_allocator_create - (memory->heap_data, heap_size))) { + if (heap_size > 0 + && !(memory->heap_handle = + mem_allocator_create(memory->heap_data, heap_size))) { wasm_runtime_free(memory); return NULL; } @@ -106,10 +131,10 @@ memory_instantiate(uint32 num_bytes_per_page, /** * Instantiate memories in a module. */ -static WASMMemoryInstance** +static WASMMemoryInstance ** memories_instantiate(const WASMModule *module, - uint32 heap_size, - char *error_buf, uint32 error_buf_size) + WASMModuleInstance *module_inst, + uint32 heap_size, char *error_buf, uint32 error_buf_size) { WASMImport *import; uint32 mem_index = 0, i, memory_count = @@ -132,16 +157,47 @@ memories_instantiate(const WASMModule *module, /* instantiate memories from import section */ import = module->import_memories; for (i = 0; i < module->import_memory_count; i++, import++) { - if (!(memory = memories[mem_index++] = - memory_instantiate(import->u.memory.num_bytes_per_page, - import->u.memory.init_page_count, - import->u.memory. max_page_count, - heap_size, error_buf, error_buf_size))) { - set_error_buf(error_buf, error_buf_size, - "Instantiate memory failed: " - "allocate memory failed."); - memories_deinstantiate(memories, memory_count); - return NULL; + uint32 num_bytes_per_page = import->u.memory.num_bytes_per_page; + uint32 init_page_count = import->u.memory.init_page_count; + uint32 max_page_count = import->u.memory.max_page_count; + uint32 actual_heap_size = heap_size; + +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMMemoryInstance *memory_inst_linked = NULL; + if (import->u.memory.import_module != NULL) { + LOG_DEBUG("(%s, %s) is a memory of a sub-module", + import->u.memory.module_name, + import->u.memory.field_name); + + // TODO: how about native memory ? + WASMModuleInstance *module_inst_linked = + get_sub_module_inst( + module_inst, + import->u.memory.import_module); + bh_assert(module_inst_linked); + + memory_inst_linked = + wasm_lookup_memory(module_inst_linked, + import->u.memory.field_name); + bh_assert(memory_inst_linked); + + memories[mem_index++] = memory_inst_linked; + memory = memory_inst_linked; + } + else +#endif + { + if (!(memory = memories[mem_index++] = memory_instantiate( + num_bytes_per_page, init_page_count, max_page_count, + actual_heap_size, error_buf, error_buf_size))) { + set_error_buf(error_buf, error_buf_size, + "Instantiate memory failed: " + "allocate memory failed."); + memories_deinstantiate( + module_inst, + memories, memory_count); + return NULL; + } } } @@ -155,25 +211,34 @@ memories_instantiate(const WASMModule *module, set_error_buf(error_buf, error_buf_size, "Instantiate memory failed: " "allocate memory failed."); - memories_deinstantiate(memories, memory_count); + memories_deinstantiate( + module_inst, + memories, memory_count); return NULL; } +#if WASM_ENABLE_MULTI_MODULE != 0 + memory->owner = module_inst; +#endif } if (mem_index == 0) { - /* no import memory and define memory, but has global variables */ + /** + * no import memory and define memory, but still need heap + * for wasm code + */ if (!(memory = memories[mem_index++] = memory_instantiate(0, 0, 0, heap_size, error_buf, error_buf_size))) { set_error_buf(error_buf, error_buf_size, "Instantiate memory failed: " "allocate memory failed.\n"); - memories_deinstantiate(memories, memory_count); + memories_deinstantiate(module_inst, memories, memory_count); return NULL; } } bh_assert(mem_index == memory_count); + (void)module_inst; return memories; } @@ -195,8 +260,9 @@ tables_deinstantiate(WASMTableInstance **tables, uint32 count) /** * Instantiate tables in a module. */ -static WASMTableInstance** +static WASMTableInstance ** tables_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, char *error_buf, uint32 error_buf_size) { WASMImport *import; @@ -218,11 +284,35 @@ tables_instantiate(const WASMModule *module, /* instantiate tables from import section */ import = module->import_tables; for (i = 0; i < module->import_table_count; i++, import++) { - total_size = offsetof(WASMTableInstance, base_addr) + - sizeof(uint32) * (uint64)import->u.table.init_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMTableInstance *table_inst_linked = NULL; + WASMModuleInstance *module_inst_linked = NULL; + if (import->u.table.import_module) { + LOG_DEBUG("(%s, %s) is a table of a sub-module", + import->u.table.module_name, + import->u.memory.field_name); + + module_inst_linked = + get_sub_module_inst(module_inst, import->u.table.import_module); + bh_assert(module_inst_linked); + + table_inst_linked = wasm_lookup_table(module_inst_linked, + import->u.table.field_name); + bh_assert(table_inst_linked); + + total_size = offsetof(WASMTableInstance, base_addr); + } + else +#endif + { + /* it is a built-in table */ + total_size = offsetof(WASMTableInstance, base_addr) + + sizeof(uint32) * (uint64)import->u.table.init_size; + } + if (total_size >= UINT32_MAX || !(table = tables[table_index++] = - wasm_runtime_malloc((uint32)total_size))) { + wasm_runtime_malloc((uint32)total_size))) { set_error_buf(error_buf, error_buf_size, "Instantiate table failed: " "allocate memory failed."); @@ -232,9 +322,20 @@ tables_instantiate(const WASMModule *module, /* Set all elements to -1 to mark them as uninitialized elements */ memset(table, -1, (uint32)total_size); - table->elem_type = import->u.table.elem_type; - table->cur_size = import->u.table.init_size; - table->max_size = import->u.table.max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + table->table_inst_linked = table_inst_linked; + if (table_inst_linked != NULL) { + table->elem_type = table_inst_linked->elem_type; + table->cur_size = table_inst_linked->cur_size; + table->max_size = table_inst_linked->max_size; + } + else +#endif + { + table->elem_type = import->u.table.elem_type; + table->cur_size = import->u.table.init_size; + table->max_size = import->u.table.max_size; + } } /* instantiate tables from table section */ @@ -256,9 +357,13 @@ tables_instantiate(const WASMModule *module, table->elem_type = module->tables[i].elem_type; table->cur_size = module->tables[i].init_size; table->max_size = module->tables[i].max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + table->table_inst_linked = NULL; +#endif } bh_assert(table_index == table_count); + (void)module_inst; return tables; } @@ -276,8 +381,9 @@ functions_deinstantiate(WASMFunctionInstance *functions, uint32 count) /** * Instantiate functions in a module. */ -static WASMFunctionInstance* +static WASMFunctionInstance * functions_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, char *error_buf, uint32 error_buf_size) { WASMImport *import; @@ -301,19 +407,59 @@ functions_instantiate(const WASMModule *module, import = module->import_functions; for (i = 0; i < module->import_function_count; i++, import++) { function->is_import_func = true; - function->u.func_import = &import->u.function; - function->param_cell_num = - wasm_type_param_cell_num(import->u.function.func_type); - function->ret_cell_num = - wasm_type_return_cell_num(import->u.function.func_type); - function->local_cell_num = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.function.import_module) { + LOG_DEBUG("(%s, %s) is a function of a sub-module", + import->u.function.module_name, + import->u.function.field_name); - function->param_count = - (uint16)function->u.func_import->func_type->param_count; - function->local_count = 0; - function->param_types = function->u.func_import->func_type->types; - function->local_types = NULL; + function->import_module_inst = + get_sub_module_inst(module_inst, + import->u.function.import_module); + bh_assert(function->import_module_inst); + + WASMFunction *function_linked = + import->u.function.import_func_linked; + + function->u.func = function_linked; + function->import_func_inst = + wasm_lookup_function(function->import_module_inst, + import->u.function.field_name, + NULL); + bh_assert(function->import_func_inst); + + function->param_cell_num = function->u.func->param_cell_num; + function->ret_cell_num = function->u.func->ret_cell_num; + function->local_cell_num = function->u.func->local_cell_num; + function->param_count = + (uint16)function->u.func->func_type->param_count; + function->local_count = (uint16)function->u.func->local_count; + function->param_types = function->u.func->func_type->types; + function->local_types = function->u.func->local_types; + function->local_offsets = function->u.func->local_offsets; +#if WASM_ENABLE_FAST_INTERP != 0 + function->const_cell_num = function->u.func->const_cell_num; +#endif + } + else +#endif /* WASM_ENABLE_MULTI_MODULE */ + { + LOG_DEBUG("(%s, %s) is a function of native", + import->u.function.module_name, + import->u.function.field_name); + function->u.func_import = &import->u.function; + function->param_cell_num = + wasm_type_param_cell_num(import->u.function.func_type); + function->ret_cell_num = + wasm_type_return_cell_num(import->u.function.func_type); + function->param_count = + (uint16)function->u.func_import->func_type->param_count; + function->param_types = function->u.func_import->func_type->types; + function->local_cell_num = 0; + function->local_count = 0; + function->local_types = NULL; + } function++; } @@ -342,6 +488,7 @@ functions_instantiate(const WASMModule *module, } bh_assert((uint32)(function - functions) == function_count); + (void)module_inst; return functions; } @@ -355,13 +502,51 @@ globals_deinstantiate(WASMGlobalInstance *globals) wasm_runtime_free(globals); } +/** + * init_expr->u ==> init_val + */ +static bool +parse_init_expr(const InitializerExpression *init_expr, + const WASMGlobalInstance *global_inst_array, + uint32 boundary, WASMValue *init_val) +{ + if (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + uint32 target_global_index = init_expr->u.global_index; + /** + * a global gets the init value of another global + */ + if (target_global_index >= boundary) { + LOG_DEBUG("unknown target global, %d", target_global_index); + return false; + } + + /** + * it will work if using WASMGlobalImport and WASMGlobal in + * WASMModule, but will have to face complicated cases + * + * but we still have no sure the target global has been + * initialized before + */ + WASMValue target_value = + global_inst_array[target_global_index].initial_value; + bh_memcpy_s(init_val, sizeof(WASMValue), &target_value, + sizeof(target_value)); + } + else { + bh_memcpy_s(init_val, sizeof(WASMValue), &init_expr->u, + sizeof(init_expr->u)); + } + return true; +} + /** * Instantiate globals in a module. */ -static WASMGlobalInstance* +static WASMGlobalInstance * globals_instantiate(const WASMModule *module, - uint32 *p_global_data_size, - char *error_buf, uint32 error_buf_size) + WASMModuleInstance *module_inst, + uint32 *p_global_data_size, char *error_buf, + uint32 error_buf_size) { WASMImport *import; uint32 global_data_offset = 0; @@ -387,7 +572,45 @@ globals_instantiate(const WASMModule *module, WASMGlobalImport *global_import = &import->u.global; global->type = global_import->type; global->is_mutable = global_import->is_mutable; - global->initial_value = global_import->global_data_linked; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (global_import->import_module) { + WASMModuleInstance *sub_module_inst = get_sub_module_inst( + module_inst, global_import->import_module); + bh_assert(sub_module_inst); + + WASMGlobalInstance *global_inst_linked = + wasm_lookup_global(sub_module_inst, global_import->field_name); + bh_assert(global_inst_linked); + + global->import_global_inst = global_inst_linked; + global->import_module_inst = sub_module_inst; + + /** + * although, actually don't need initial_value for an imported + * global, we keep it here like a place holder because of + * global-data and + * (global $g2 i32 (global.get $g1)) + */ + WASMGlobal *linked_global = global_import->import_global_linked; + InitializerExpression *linked_init_expr = + &(linked_global->init_expr); + + bool ret = parse_init_expr( + linked_init_expr, + sub_module_inst->globals, + sub_module_inst->global_count, &(global->initial_value)); + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "Instantiate global failed: unknown global."); + return NULL; + } + } + else +#endif + { + /* native globals share their initial_values in one module */ + global->initial_value = global_import->global_data_linked; + } global->data_offset = global_data_offset; global_data_offset += wasm_value_type_size(global->type); @@ -396,44 +619,69 @@ globals_instantiate(const WASMModule *module, /* instantiate globals from global section */ for (i = 0; i < module->global_count; i++) { + bool ret = false; + uint32 global_count = + module->import_global_count + module->global_count; + InitializerExpression *init_expr = &(module->globals[i].init_expr); + global->type = module->globals[i].type; global->is_mutable = module->globals[i].is_mutable; - global->data_offset = global_data_offset; + global_data_offset += wasm_value_type_size(global->type); + /** + * first init, it might happen that the target global instance + * has not been initialize yet + */ + if (init_expr->init_expr_type != INIT_EXPR_TYPE_GET_GLOBAL) { + ret = + parse_init_expr(init_expr, globals, global_count, + &(global->initial_value)); + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "Instantiate global failed: unknown global."); + return NULL; + } + } global++; } bh_assert((uint32)(global - globals) == global_count); *p_global_data_size = global_data_offset; + (void)module_inst; return globals; } static bool globals_instantiate_fix(WASMGlobalInstance *globals, const WASMModule *module, - WASMModuleInstance *module_inst, char *error_buf, uint32 error_buf_size) { WASMGlobalInstance *global = globals; uint32 i; + uint32 global_count = module->import_global_count + module->global_count; + /** + * second init, only target global instances from global + * (ignore import_global) + * to fix skipped init_value in the previous round + * hope two rounds are enough but how about a chain ? + */ for (i = 0; i < module->global_count; i++) { + bool ret = false; InitializerExpression *init_expr = &module->globals[i].init_expr; if (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - if (init_expr->u.global_index >= module->import_global_count + i) { + ret = parse_init_expr(init_expr, globals, global_count, + &global->initial_value); + if (!ret) { set_error_buf(error_buf, error_buf_size, "Instantiate global failed: unknown global."); return false; } - global->initial_value = globals[init_expr->u.global_index].initial_value; - } - else { - bh_memcpy_s(&global->initial_value, sizeof(WASMValue), - &init_expr->u, sizeof(init_expr->u)); } + global++; } return true; @@ -443,13 +691,13 @@ globals_instantiate_fix(WASMGlobalInstance *globals, * Return export function count in module export section. */ static uint32 -get_export_function_count(const WASMModule *module) +get_export_count(const WASMModule *module, uint8 kind) { WASMExport *export = module->exports; uint32 count = 0, i; for (i = 0; i < module->export_count; i++, export++) - if (export->kind == EXPORT_KIND_FUNC) + if (export->kind == kind) count++; return count; @@ -500,6 +748,48 @@ export_functions_instantiate(const WASMModule *module, return export_funcs; } +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +export_globals_deinstantiate(WASMExportGlobInstance *globals) +{ + if (globals) + wasm_runtime_free(globals); +} + +static WASMExportGlobInstance * +export_globals_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_glob_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportGlobInstance *export_globals, *export_global; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = sizeof(WASMExportGlobInstance) * (uint64)export_glob_count; + + if (total_size >= UINT32_MAX + || !(export_global = export_globals = + wasm_runtime_malloc((uint32)total_size))) { + set_error_buf(error_buf, error_buf_size, + "Instantiate export global failed: " + "allocate memory failed."); + return NULL; + } + + memset(export_globals, 0, (uint32)total_size); + + for (i = 0; i < module->export_count; i++, export++) + if (export->kind == EXPORT_KIND_GLOBAL) { + export_global->name = export->name; + export_global->global = &module_inst->globals[export->index]; + export_global++; + } + + bh_assert((uint32)(export_global - export_globals) == export_glob_count); + return export_globals; +} +#endif + static bool execute_post_inst_function(WASMModuleInstance *module_inst) { @@ -541,6 +831,66 @@ execute_start_function(WASMModuleInstance *module_inst) return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL); } +#if WASM_ENABLE_MULTI_MODULE != 0 +static bool +sub_module_instantiate(WASMModule *module, WASMModuleInstance *module_inst, + uint32 stack_size, uint32 heap_size, char *error_buf, + uint32 error_buf_size) +{ + bh_list *sub_module_inst_list = module_inst->sub_module_inst_list; + WASMRegisteredModule *sub_module_list_node = + bh_list_first_elem(module->import_module_list); + + while (sub_module_list_node) { + WASMModule *sub_module = (WASMModule*)sub_module_list_node->module; + WASMModuleInstance *sub_module_inst = wasm_instantiate( + sub_module, stack_size, heap_size, error_buf, error_buf_size); + if (!sub_module_inst) { + LOG_DEBUG("instantiate %s failed", + sub_module_list_node->module_name); + set_error_buf_v(error_buf, error_buf_size, "instantiate %s failed", + sub_module_list_node->module_name); + return false; + } + + WASMSubModInstNode *sub_module_inst_list_node = + wasm_runtime_malloc(sizeof(WASMSubModInstNode)); + if (!sub_module_inst_list_node) { + LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ:%d", + sizeof(WASMSubModInstNode)); + set_error_buf_v(error_buf, error_buf_size, "malloc failed"); + wasm_deinstantiate(sub_module_inst); + return false; + } + + sub_module_inst_list_node->module_inst = sub_module_inst; + sub_module_inst_list_node->module_name = + sub_module_list_node->module_name; + bh_list_status ret = + bh_list_insert(sub_module_inst_list, sub_module_inst_list_node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + + sub_module_list_node = bh_list_elem_next(sub_module_list_node); + } + + return true; +} + +static void +sub_module_deinstantiate(WASMModuleInstance *module_inst) +{ + bh_list *list = module_inst->sub_module_inst_list; + WASMSubModInstNode *node = bh_list_first_elem(list); + while (node) { + WASMSubModInstNode *next_node = bh_list_elem_next(node); + bh_list_remove(list, node); + wasm_deinstantiate(node->module_inst); + node = next_node; + } +} +#endif + /** * Instantiate module */ @@ -550,14 +900,13 @@ wasm_instantiate(WASMModule *module, char *error_buf, uint32 error_buf_size) { WASMModuleInstance *module_inst; - WASMTableSeg *table_seg; - WASMDataSeg *data_seg; WASMGlobalInstance *globals = NULL, *global; - uint32 global_count, global_data_size = 0, i, j; - uint32 base_offset, length, memory_size; + uint32 global_count, global_data_size = 0, i; + uint32 base_offset, length; uint8 *global_data, *global_data_end; - uint8 *memory_data; - uint32 *table_data; +#if WASM_ENABLE_MULTI_MODULE != 0 + bool ret = false; +#endif if (!module) return NULL; @@ -571,23 +920,38 @@ wasm_instantiate(WASMModule *module, if (heap_size > APP_HEAP_SIZE_MAX) heap_size = APP_HEAP_SIZE_MAX; - /* Instantiate global firstly to get the mutable data size */ - global_count = module->import_global_count + module->global_count; - if (global_count && - !(globals = globals_instantiate(module, - &global_data_size, - error_buf, error_buf_size))) - return NULL; - /* Allocate the memory */ if (!(module_inst = wasm_runtime_malloc((uint32)sizeof(WASMModuleInstance)))) { set_error_buf(error_buf, error_buf_size, "Instantiate module failed: allocate memory failed."); - globals_deinstantiate(globals); return NULL; } + LOG_DEBUG("Instantiate a module %p -> %p", module, module_inst); + memset(module_inst, 0, (uint32)sizeof(WASMModuleInstance)); + +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->sub_module_inst_list = + &module_inst->sub_module_inst_list_head; + ret = sub_module_instantiate(module, module_inst, stack_size, heap_size, + error_buf, error_buf_size); + if (!ret) { + LOG_DEBUG("build a sub module list failed"); + wasm_deinstantiate(module_inst); + return NULL; + } +#endif + + /* Instantiate global firstly to get the mutable data size */ + global_count = module->import_global_count + module->global_count; + if (global_count && !(globals = globals_instantiate( + module, + module_inst, + &global_data_size, error_buf, error_buf_size))) { + wasm_deinstantiate(module_inst); + return NULL; + } module_inst->global_count = global_count; module_inst->globals = globals; @@ -597,7 +961,14 @@ wasm_instantiate(WASMModule *module, module->import_table_count + module->table_count; module_inst->function_count = module->import_function_count + module->function_count; - module_inst->export_func_count = get_export_function_count(module); + + /* export */ + module_inst->export_func_count = get_export_count(module, EXPORT_KIND_FUNC); +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->export_tab_count = get_export_count(module, EXPORT_KIND_TABLE); + module_inst->export_mem_count = get_export_count(module, EXPORT_KIND_MEMORY); + module_inst->export_glob_count = get_export_count(module, EXPORT_KIND_GLOBAL); +#endif if (global_count > 0) { if (!(module_inst->global_data = @@ -611,27 +982,40 @@ wasm_instantiate(WASMModule *module, /* Instantiate memories/tables/functions */ if ((module_inst->memory_count > 0 && !(module_inst->memories = - memories_instantiate(module, heap_size, - error_buf, error_buf_size))) + memories_instantiate(module, + module_inst, + heap_size, error_buf, error_buf_size))) || (module_inst->table_count > 0 - && !(module_inst->tables = tables_instantiate(module, - error_buf, - error_buf_size))) + && !(module_inst->tables = + tables_instantiate(module, + module_inst, + error_buf, error_buf_size))) || (module_inst->function_count > 0 - && !(module_inst->functions = functions_instantiate(module, - error_buf, - error_buf_size))) + && !(module_inst->functions = + functions_instantiate(module, + module_inst, + error_buf, error_buf_size))) || (module_inst->export_func_count > 0 && !(module_inst->export_functions = export_functions_instantiate( - module, module_inst, module_inst->export_func_count, - error_buf, error_buf_size)))) { + module, module_inst, module_inst->export_func_count, + error_buf, error_buf_size))) +#if WASM_ENABLE_MULTI_MODULE != 0 + || (module_inst->export_glob_count > 0 + && !(module_inst->export_globals = export_globals_instantiate( + module, module_inst, module_inst->export_glob_count, + error_buf, error_buf_size))) +#endif + ) { wasm_deinstantiate(module_inst); return NULL; } if (global_count > 0) { - /* fix globals */ - if (!globals_instantiate_fix(globals, module, module_inst, + /** + * since there might be some globals are not instantiate the first + * instantiate round + */ + if (!globals_instantiate_fix(globals, module, error_buf, error_buf_size)) { wasm_deinstantiate(module_inst); return NULL; @@ -659,97 +1043,137 @@ wasm_instantiate(WASMModule *module, } } bh_assert(global_data == global_data_end); - } - if (module_inst->memory_count) { - WASMMemoryInstance *memory; + /* Initialize the memory data with data segment section */ + module_inst->default_memory = + module_inst->memory_count ? module_inst->memories[0] : NULL; - memory = module_inst->default_memory = module_inst->memories[0]; - memory_data = module_inst->default_memory->memory_data; + for (i = 0; i < module->data_seg_count; i++) { + WASMMemoryInstance *memory = NULL; + uint8 *memory_data = NULL; + uint32 memory_size = 0; + WASMDataSeg *data_seg = module->data_segments[i]; - /* Initialize the memory data with data segment section */ - if (memory->cur_page_count > 0) { - for (i = 0; i < module->data_seg_count; i++) { - data_seg = module->data_segments[i]; - bh_assert(data_seg->memory_index == 0); - bh_assert(data_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || data_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif - if (data_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { - bh_assert(data_seg->base_offset.u.global_index < global_count - && globals[data_seg->base_offset.u.global_index].type == - VALUE_TYPE_I32); - data_seg->base_offset.u.i32 = - globals[data_seg->base_offset.u.global_index].initial_value.i32; - } + /* has check it in loader */ + memory = module_inst->memories[data_seg->memory_index]; + bh_assert(memory); - base_offset = (uint32)data_seg->base_offset.u.i32; - length = data_seg->data_length; - memory_size = memory->num_bytes_per_page - * memory->cur_page_count; + memory_data = memory->memory_data; + bh_assert(memory_data); - if (length > 0 - && (base_offset >= memory_size - || base_offset + length > memory_size)) { - set_error_buf(error_buf, error_buf_size, - "Instantiate module failed: data segment does not fit."); - wasm_deinstantiate(module_inst); - return NULL; - } + memory_size = memory->num_bytes_per_page * memory->cur_page_count; - bh_memcpy_s(memory_data + base_offset, memory_size - base_offset, - data_seg->data, length); - } + bh_assert(data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + || data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL); + + if (data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL) { + bh_assert(data_seg->base_offset.u.global_index < global_count + && globals[data_seg->base_offset.u.global_index].type + == VALUE_TYPE_I32); + data_seg->base_offset.u.i32 = + globals[data_seg->base_offset.u.global_index] + .initial_value.i32; } + + /* check offset since length might negative */ + base_offset = (uint32)data_seg->base_offset.u.i32; + if (base_offset > memory_size) { + LOG_DEBUG("base_offset(%d) > memory_size(%d)", base_offset, + memory_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit."); + wasm_deinstantiate(module_inst); + return NULL; + } + + /* check offset + length(could be zero) */ + length = data_seg->data_length; + if (base_offset + length > memory_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > memory_size(%d)", + base_offset, length, memory_size); + set_error_buf( + error_buf, error_buf_size, + "Instantiate module failed: data segment does not fit."); + wasm_deinstantiate(module_inst); + return NULL; + } + + bh_memcpy_s(memory_data + base_offset, memory_size - base_offset, + data_seg->data, length); } - if (module_inst->table_count) { - module_inst->default_table = module_inst->tables[0]; + /* Initialize the table data with table segment section */ + module_inst->default_table = + module_inst->table_count ? module_inst->tables[0] : NULL; + for (i = 0; i < module->table_seg_count; i++) { + WASMTableSeg *table_seg = module->table_segments + i; + /* has check it in loader */ + WASMTableInstance *table = module_inst->tables[table_seg->table_index]; + bh_assert(table); - /* Initialize the table data with table segment section */ - table_data = (uint32*)module_inst->default_table->base_addr; - table_seg = module->table_segments; - for (i = 0; i < module->table_seg_count; i++, table_seg++) { - bh_assert(table_seg->table_index == 0); - bh_assert(table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); + uint32 *table_data = (uint32 *)table->base_addr; +#if WASM_ENABLE_MULTI_MODULE != 0 + table_data = table->table_inst_linked + ? (uint32 *)table->table_inst_linked->base_addr + : table_data; +#endif + bh_assert(table_data); - if (table_seg->base_offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL) { - bh_assert(table_seg->base_offset.u.global_index < global_count - && globals[table_seg->base_offset.u.global_index].type == - VALUE_TYPE_I32); - table_seg->base_offset.u.i32 = - globals[table_seg->base_offset.u.global_index].initial_value.i32; - } - if ((uint32)table_seg->base_offset.u.i32 < - module_inst->default_table->cur_size) { - length = table_seg->function_count; - if ((uint32)table_seg->base_offset.u.i32 + length > - module_inst->default_table->cur_size) - length = module_inst->default_table->cur_size - - (uint32)table_seg->base_offset.u.i32; - /* Check function index */ - for (j = 0; j < length; j++) { - if (table_seg->func_indexes[j] >= module_inst->function_count) { - set_error_buf(error_buf, error_buf_size, - "WASM instantiate failed: unknown function"); - wasm_deinstantiate(module_inst); - return NULL; - } - } - bh_memcpy_s(table_data + table_seg->base_offset.u.i32, - (uint32)((module_inst->default_table->cur_size - - (uint32)table_seg->base_offset.u.i32) - * sizeof(uint32)), - table_seg->func_indexes, (uint32)(length * sizeof(uint32))); - } + /* init vec(funcidx) */ + bh_assert(table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + || table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL); + + if (table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL) { + bh_assert(table_seg->base_offset.u.global_index < global_count + && globals[table_seg->base_offset.u.global_index].type + == VALUE_TYPE_I32); + table_seg->base_offset.u.i32 = + globals[table_seg->base_offset.u.global_index].initial_value.i32; } + + /* check offset since length might negative */ + if ((uint32)table_seg->base_offset.u.i32 > table->cur_size) { + LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", + table_seg->base_offset.u.i32, table->cur_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + wasm_deinstantiate(module_inst); + return NULL; + } + + /* check offset + length(could be zero) */ + length = table_seg->function_count; + if ((uint32)table_seg->base_offset.u.i32 + length > table->cur_size) { + LOG_DEBUG("base_offset(%d) + length(%d)> table->cur_size(%d)", + table_seg->base_offset.u.i32, length, table->cur_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + wasm_deinstantiate(module_inst); + return NULL; + } + + /** + * Check function index in the current module inst for now. + * will check the linked table inst owner in future. + * so loader check is enough + */ + bh_memcpy_s( + table_data + table_seg->base_offset.u.i32, + (uint32)((table->cur_size - (uint32)table_seg->base_offset.u.i32) + * sizeof(uint32)), + table_seg->func_indexes, (uint32)(length * sizeof(uint32))); } #if WASM_ENABLE_LIBC_WASI != 0 @@ -808,6 +1232,10 @@ wasm_deinstantiate(WASMModuleInstance *module_inst) if (!module_inst) return; +#if WASM_ENABLE_MULTI_MODULE != 0 + sub_module_deinstantiate(module_inst); +#endif + #if WASM_ENABLE_LIBC_WASI != 0 /* Destroy wasi resource before freeing app heap, since some fields of wasi contex are allocated from app heap, and if app heap is freed, @@ -817,12 +1245,17 @@ wasm_deinstantiate(WASMModuleInstance *module_inst) #endif if (module_inst->memory_count > 0) - memories_deinstantiate(module_inst->memories, module_inst->memory_count); + memories_deinstantiate( + module_inst, + module_inst->memories, module_inst->memory_count); tables_deinstantiate(module_inst->tables, module_inst->table_count); functions_deinstantiate(module_inst->functions, module_inst->function_count); globals_deinstantiate(module_inst->globals); export_functions_deinstantiate(module_inst->export_functions); +#if WASM_ENABLE_MULTI_MODULE != 0 + export_globals_deinstantiate(module_inst->export_globals); +#endif if (module_inst->global_data) wasm_runtime_free(module_inst->global_data); @@ -842,6 +1275,40 @@ wasm_lookup_function(const WASMModuleInstance *module_inst, return NULL; } +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name) +{ + uint32 i; + for (i = 0; i < module_inst->export_glob_count; i++) + if (!strcmp(module_inst->export_globals[i].name, name)) + return module_inst->export_globals[i].global; + return NULL; +} + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name) +{ + /** + * using a strong assumption that one module instance only has + * one memory instance + */ + (void)module_inst->export_memories; + return module_inst->memories[0]; +} + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) +{ + /** + * using a strong assumption that one module instance only has + * one table instance + */ + (void)module_inst->export_tables; + return module_inst->tables[0]; +} +#endif + bool wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function, @@ -1141,14 +1608,28 @@ wasm_call_indirect(WASMExecEnv *exec_env, goto got_exception; } + /** + * please be aware that table_inst->base_addr may point + * to another module's table + **/ function_indices = ((uint32_t*)table_inst->base_addr)[element_indices]; if (function_indices == 0xFFFFFFFF) { wasm_set_exception(module_inst, "uninitialized element"); goto got_exception; } + /** + * we insist to call functions owned by the module itself + **/ + if (function_indices >= module_inst->function_count) { + wasm_set_exception(module_inst, "unknown function"); + goto got_exception; + } + function_inst = module_inst->functions + function_indices; + wasm_interp_call_wasm(module_inst, exec_env, function_inst, argc, argv); + return !wasm_get_exception(module_inst) ? true : false; got_exception: diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 03268e63d..6b3882b3e 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -15,6 +15,12 @@ extern "C" { #endif +typedef struct WASMModuleInstance WASMModuleInstance; +typedef struct WASMFunctionInstance WASMFunctionInstance; +typedef struct WASMMemoryInstance WASMMemoryInstance; +typedef struct WASMTableInstance WASMTableInstance; +typedef struct WASMGlobalInstance WASMGlobalInstance; + typedef struct WASMMemoryInstance { /* Number bytes per page */ uint32 num_bytes_per_page; @@ -36,6 +42,10 @@ typedef struct WASMMemoryInstance { /* End address of memory */ uint8 *end_addr; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* to indicate which module instance create it */ + WASMModuleInstance *owner; +#endif /* Base address, the layout is: heap_data + memory data memory data init size is: num_bytes_per_page * cur_page_count @@ -52,6 +62,10 @@ typedef struct WASMTableInstance { uint32 cur_size; /* Maximum size */ uint32 max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just for import, keep the reference here */ + WASMTableInstance *table_inst_linked; +#endif /* Base address */ uint8 base_addr[1]; } WASMTableInstance; @@ -65,6 +79,11 @@ typedef struct WASMGlobalInstance { uint32 data_offset; /* initial value */ WASMValue initial_value; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just for import, keep the reference here */ + WASMModuleInstance *import_module_inst; + WASMGlobalInstance *import_global_inst; +#endif } WASMGlobalInstance; typedef struct WASMFunctionInstance { @@ -93,6 +112,10 @@ typedef struct WASMFunctionInstance { WASMFunctionImport *func_import; WASMFunction *func; } u; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMFunctionInstance *import_func_inst; +#endif } WASMFunctionInstance; typedef struct WASMExportFuncInstance { @@ -100,6 +123,23 @@ typedef struct WASMExportFuncInstance { WASMFunctionInstance *function; } WASMExportFuncInstance; +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMExportGlobInstance { + char *name; + WASMGlobalInstance *global; +} WASMExportGlobInstance; + +typedef struct WASMExportTabInstance { + char *name; + WASMTableInstance *table; +} WASMExportTabInstance; + +typedef struct WASMExportMemInstance { + char *name; + WASMMemoryInstance *memory; +} WASMExportMemInstance; +#endif + typedef struct WASMModuleInstance { /* Module instance type, for module instance loaded from WASM bytecode binary, this field is Wasm_Module_Bytecode; @@ -112,13 +152,25 @@ typedef struct WASMModuleInstance { uint32 table_count; uint32 global_count; uint32 function_count; + uint32 export_func_count; +#if WASM_ENABLE_MULTI_MODULE != 0 + uint32 export_glob_count; + uint32 export_mem_count; + uint32 export_tab_count; +#endif WASMMemoryInstance **memories; WASMTableInstance **tables; WASMGlobalInstance *globals; WASMFunctionInstance *functions; + WASMExportFuncInstance *export_functions; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMExportGlobInstance *export_globals; + WASMExportMemInstance *export_memories; + WASMExportTabInstance *export_tables; +#endif WASMMemoryInstance *default_memory; WASMTableInstance *default_table; @@ -148,11 +200,26 @@ typedef struct WASMModuleInstance { /* Main exec env */ WASMExecEnv *main_exec_env; + +#if WASM_ENABLE_MULTI_MODULE != 0 + // TODO: mutex ? mutli-threads ? + bh_list sub_module_inst_list_head; + bh_list *sub_module_inst_list; +#endif } WASMModuleInstance; struct WASMInterpFrame; typedef struct WASMInterpFrame WASMRuntimeFrame; +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMSubModInstNode { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleInstance *module_inst; +} WASMSubModInstNode; +#endif + /** * Return the code block of a function. * @@ -182,10 +249,11 @@ wasm_get_func_code_end(WASMFunctionInstance *func) { #if WASM_ENABLE_FAST_INTERP == 0 return func->is_import_func - ? NULL : func->u.func->code + func->u.func->code_size; + ? NULL : func->u.func->code + func->u.func->code_size; #else return func->is_import_func - ? NULL : func->u.func->code_compiled + func->u.func->code_compiled_size; + ? NULL + : func->u.func->code_compiled + func->u.func->code_compiled_size; #endif } @@ -212,6 +280,17 @@ WASMFunctionInstance * wasm_lookup_function(const WASMModuleInstance *module_inst, const char *name, const char *signature); +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name); + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name); + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name); +#endif + bool wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function, diff --git a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c index 76f821ad8..88ebc0131 100644 --- a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c +++ b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c @@ -1098,7 +1098,7 @@ static NativeSymbol native_symbols_spectest[] = { REG_NATIVE_FUNC(print, "()"), REG_NATIVE_FUNC(print_i32, "(i)"), REG_NATIVE_FUNC(print_i32_f32, "(if)"), - REG_NATIVE_FUNC(print_f64_f64, "(fF)"), + REG_NATIVE_FUNC(print_f64_f64, "(FF)"), REG_NATIVE_FUNC(print_f32, "(f)"), REG_NATIVE_FUNC(print_f64, "(F)") }; diff --git a/core/shared/platform/common/posix/posix_memmap.c b/core/shared/platform/common/posix/posix_memmap.c index 7cf83f44d..7625cf66a 100644 --- a/core/shared/platform/common/posix/posix_memmap.c +++ b/core/shared/platform/common/posix/posix_memmap.c @@ -11,13 +11,11 @@ os_mmap(void *hint, uint32 size, int prot, int flags) int map_prot = PROT_NONE; int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; uint64 request_size, page_size; - uint8 *addr, *addr_aligned; + uint8 *addr; uint32 i; - /* align to 2M if no less than 2M, else align to 4K */ - page_size = size < 2 * 1024 * 1024 ? 4096 : 2 * 1024 * 1024; + page_size = getpagesize(); request_size = (size + page_size - 1) & ~(page_size - 1); - request_size += page_size; if (request_size >= UINT32_MAX) return NULL; @@ -47,43 +45,25 @@ os_mmap(void *hint, uint32 size, int prot, int flags) if (addr != MAP_FAILED) break; } + if (addr == MAP_FAILED) return NULL; - addr_aligned = (uint8*)(uintptr_t) - (((uint64)(uintptr_t)addr + page_size - 1) & ~(page_size - 1)); - - /* Unmap memory allocated before the aligned base address */ - if (addr != addr_aligned) { - uint32 prefix_size = (uint32)(addr_aligned - addr); - munmap(addr, prefix_size); - request_size -= prefix_size; - } - - /* Unmap memory allocated after the potentially unaligned end */ - if (size != request_size) { - uint32 suffix_size = (uint32)(request_size - size); - munmap(addr_aligned + size, suffix_size); - request_size -= size; - } - -#ifndef __APPLE__ - if (size >= 2 * 1024 * 1024) { - /* Try to use huge page to improve performance */ - if (!madvise(addr, size, MADV_HUGEPAGE)) - /* make huge page become effective */ - memset(addr, 0, size); - } -#endif - - return addr_aligned; + return addr; } void os_munmap(void *addr, uint32 size) { - if (addr) - munmap(addr, size); + uint64 page_size = getpagesize(); + uint64 request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (munmap(addr, request_size)) { + os_printf("os_munmap error addr:%p, size:0x%lx, errno:%d\n", + addr, request_size, errno); + } + } } int diff --git a/core/shared/platform/common/posix/posix_thread.c b/core/shared/platform/common/posix/posix_thread.c index 9c0cf3835..e2d45b450 100644 --- a/core/shared/platform/common/posix/posix_thread.c +++ b/core/shared/platform/common/posix/posix_thread.c @@ -249,5 +249,4 @@ uint8 *os_thread_get_stack_boundary() #endif return addr; -} - +} \ No newline at end of file diff --git a/core/shared/platform/linux-sgx/sgx_platform.c b/core/shared/platform/linux-sgx/sgx_platform.c index 08aa37c36..e52df1da5 100644 --- a/core/shared/platform/linux-sgx/sgx_platform.c +++ b/core/shared/platform/linux-sgx/sgx_platform.c @@ -82,29 +82,39 @@ int os_vprintf(const char * format, va_list arg) return 0; } -void* os_mmap(void *hint, unsigned int size, int prot, int flags) +void* os_mmap(void *hint, uint32 size, int prot, int flags) { #if WASM_ENABLE_AOT != 0 int mprot = 0; - unsigned alignedSize = (size+4095) & (unsigned)~4095; //Page aligned + uint64 aligned_size, page_size; void* ret = NULL; sgx_status_t st = 0; - ret = sgx_alloc_rsrv_mem(alignedSize); + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + + if (aligned_size >= UINT32_MAX) + return NULL; + + ret = sgx_alloc_rsrv_mem(aligned_size); if (ret == NULL) { - os_printf("os_mmap(size=%d, alignedSize=%d, prot=0x%x) failed.",size, alignedSize, prot); + os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.", + size, aligned_size, prot); return NULL; } + if (prot & MMAP_PROT_READ) mprot |= SGX_PROT_READ; if (prot & MMAP_PROT_WRITE) mprot |= SGX_PROT_WRITE; if (prot & MMAP_PROT_EXEC) mprot |= SGX_PROT_EXEC; - st = sgx_tprotect_rsrv_mem(ret, alignedSize, mprot); - if (st != SGX_SUCCESS){ - os_printf("os_mmap(size=%d,prot=0x%x) failed to set protect.",size, prot); - sgx_free_rsrv_mem(ret, alignedSize); + + st = sgx_tprotect_rsrv_mem(ret, aligned_size, mprot); + if (st != SGX_SUCCESS) { + os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.", + size, prot); + sgx_free_rsrv_mem(ret, aligned_size); return NULL; } @@ -117,7 +127,11 @@ void* os_mmap(void *hint, unsigned int size, int prot, int flags) void os_munmap(void *addr, uint32 size) { #if WASM_ENABLE_AOT != 0 - sgx_free_rsrv_mem(addr, size); + uint64 aligned_size, page_size; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + sgx_free_rsrv_mem(addr, aligned_size); #endif } @@ -135,7 +149,8 @@ int os_mprotect(void *addr, uint32 size, int prot) mprot |= SGX_PROT_EXEC; st = sgx_tprotect_rsrv_mem(addr, size, mprot); if (st != SGX_SUCCESS) - os_printf("os_mprotect(addr=0x%lx,size=%d,prot=0x%x) failed.", addr, size, prot); + os_printf("os_mprotect(addr=0x%lx, size=%u, prot=0x%x) failed.", + addr, size, prot); return (st == SGX_SUCCESS? 0:-1); #else diff --git a/core/shared/utils/bh_common.c b/core/shared/utils/bh_common.c index a3f47a52d..44bdbed1b 100644 --- a/core/shared/utils/bh_common.c +++ b/core/shared/utils/bh_common.c @@ -32,6 +32,26 @@ b_memcpy_s(void * s1, unsigned int s1max, return 0; } +int b_memmove_s(void * s1, unsigned int s1max, + const void * s2, unsigned int n) +{ + char *dest = (char*)s1; + char *src = (char*)s2; + if (n == 0) { + return 0; + } + + if (s1 == NULL || s1max > RSIZE_MAX) { + return -1; + } + if (s2 == NULL || n > s1max) { + memset(dest, 0, s1max); + return -1; + } + memmove(dest, src, n); + return 0; +} + int b_strcat_s(char * s1, unsigned int s1max, const char * s2) { diff --git a/core/shared/utils/bh_common.h b/core/shared/utils/bh_common.h index 682e3d2e3..cd3bcdacf 100644 --- a/core/shared/utils/bh_common.h +++ b/core/shared/utils/bh_common.h @@ -18,6 +18,12 @@ extern "C" { bh_assert (_ret == 0); \ } while (0) +#define bh_memmove_s(dest, dlen, src, slen) do { \ + int _ret = slen == 0 ? 0 : b_memmove_s (dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert (_ret == 0); \ + } while (0) + #define bh_strcat_s(dest, dlen, src) do { \ int _ret = b_strcat_s (dest, dlen, src); \ (void)_ret; \ @@ -31,6 +37,7 @@ extern "C" { } while (0) int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); +int b_memmove_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); int b_strcat_s(char * s1, unsigned int s1max, const char * s2); int b_strcpy_s(char * s1, unsigned int s1max, const char * s2); diff --git a/core/shared/utils/bh_log.h b/core/shared/utils/bh_log.h index 5dd13a8ec..8f7a179d1 100644 --- a/core/shared/utils/bh_log.h +++ b/core/shared/utils/bh_log.h @@ -41,12 +41,22 @@ bh_log_set_verbose_level(uint32 level); void bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...); +#if BH_DEBUG == 1 #define LOG_FATAL(...) bh_log(BH_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_FATAL(...) bh_log(BH_LOG_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__) +#endif + #define LOG_ERROR(...) bh_log(BH_LOG_LEVEL_ERROR, NULL, 0, __VA_ARGS__) -#define LOG_DEBUG(...) bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, 0, __VA_ARGS__) #define LOG_WARNING(...) bh_log(BH_LOG_LEVEL_WARNING, NULL, 0, __VA_ARGS__) #define LOG_VERBOSE(...) bh_log(BH_LOG_LEVEL_VERBOSE, NULL, 0, __VA_ARGS__) +#if BH_DEBUG == 1 +#define LOG_DEBUG(...) bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_DEBUG(...) /* do nothing */ +#endif + void bh_print_time(const char *prompt); diff --git a/core/shared/utils/runtime_timer.h b/core/shared/utils/runtime_timer.h index f173de546..8e8cc59ef 100644 --- a/core/shared/utils/runtime_timer.h +++ b/core/shared/utils/runtime_timer.h @@ -17,7 +17,7 @@ uint32 bh_get_elpased_ms(uint32 *last_system_clock); struct _timer_ctx; typedef struct _timer_ctx * timer_ctx_t; -typedef void (*timer_callback_f)(uint32 id, unsigned int owner); +typedef void (*timer_callback_f)(unsigned int id, unsigned int owner); typedef void (*check_timer_expiry_f)(timer_ctx_t ctx); timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 4485a4345..eb4f7e6fa 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -7,7 +7,7 @@ It is recommended to use the [WAMR SDK](../wamr-sdk) tools to build a project th ## iwasm VM core CMake building configurations -By including the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in CMakeList.txt, it is easy to build minimal product with CMake. +By including the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in CMakeList.txt, it is easy to build minimal product with CMake. ```cmake # add this in your CMakeList.text @@ -21,19 +21,19 @@ The script `runtime_lib.cmake` defined a number of variables for configuring the #### **Configure platform and architecture** -- **WAMR_BUILD_PLATFORM**: set the target platform. It can be set to any platform name (folder name) under folder [core/shared/platform](../core/shared/platform). +- **WAMR_BUILD_PLATFORM**: set the target platform. It can be set to any platform name (folder name) under folder [core/shared/platform](../core/shared/platform). - **WAMR_BUILD_TARGET**: set the target CPU architecture. Current supported targets: X86_64, X86_32, AArch64, ARM, THUMB, XTENSA and MIPS. For AArch64, ARM and THUMB, the format is [][_VFP] where is the ARM sub-architecture and the "_VFP" suffix means VFP coprocessor registers s0-s15 (d0-d7) are used for passing arguments or returning results in standard procedure-call. Both and "_VFP" are optional. e.g. AARCH64, AARCH64V8, AARCHV8.1, ARMV7, ARMV7_VFP, THUMBV7, THUMBV7_VFP and so on. ```bash - cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM + cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM ``` -#### **Configure interpreter** +#### **Configure interpreter** - **WAMR_BUILD_INTERP**=1/0: enable or disable WASM interpreter -- **WAMR_BUILD_FAST_INTERP**=1/0:build fast (default) or classic WASM interpreter. +- **WAMR_BUILD_FAST_INTERP**=1/0:build fast (default) or classic WASM interpreter. NOTE: the fast interpreter will run ~2X faster than classic interpreter, but it consumes about 2X memory to hold the WASM bytecode code. @@ -48,14 +48,16 @@ The script `runtime_lib.cmake` defined a number of variables for configuring the - **WAMR_BUILD_LIBC_WASI**=1/0, default to disable if no set - +#### **Enable Multi-Module feature** + +- **WAMR_BUILD_MULTI_MODULE**=1/0, default to disable if not set **Combination of configurations:** We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: ``` Bash -cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_PLATFORM=linux +cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_PLATFORM=linux ``` Or if we want to enable interpreter, disable AOT and WASI, and build as X86_32, we can run command: @@ -73,7 +75,7 @@ If you are building for ARM architecture on a X86 development machine, you can u ``` cmake .. -DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE \ -DWAMR_BUILD_PLATFORM=linux \ - -DWAMR_BUILD_TARGET=ARM + -DWAMR_BUILD_TARGET=ARM ``` Refer to toochain sample file [`samples/simple/profiles/arm-interp/toolchain.cmake`](../samples/simple/profiles/arm-interp/toolchain.cmake) for how to build mini product for ARM target architecture. @@ -260,7 +262,7 @@ AliOS-Things ``` 7. build source code and run For linux host: - + ``` Bash aos make helloworld@linuxhost -c config aos make @@ -269,7 +271,7 @@ AliOS-Things For developerkit: Modify file middleware/iwasm/aos.mk, patch as: - + ``` C WAMR_BUILD_TARGET := THUMBV7M ``` @@ -285,11 +287,11 @@ Android able to generate a shared library support Android platform. - need an [android SDK](https://developer.android.com/studio). Go and get the "Command line tools only" - look for a command named *sdkmanager* and download below components. version numbers might need to check and pick others - - "build-tools;29.0.3" - - "cmake;3.10.2.4988404" - - "ndk;21.0.6113669" + - "build-tools;29.0.3" + - "cmake;3.10.2.4988404" + - "ndk;21.0.6113669" - "patcher;v4" - - "platform-tools" + - "platform-tools" - "platforms;android-29" - add bin/ of the downloaded cmake to $PATH - export ANDROID_SDK_HOME=/the/path/of/downloaded/sdk/ diff --git a/doc/multi_module.md b/doc/multi_module.md new file mode 100644 index 000000000..08e827eea --- /dev/null +++ b/doc/multi_module.md @@ -0,0 +1,228 @@ +Multiple Modules as Dependencies +========================= + +It is allowed that one WASM module can *import* *functions*, *globals*, *memories* and *tables* from other modules as its dependencies, and also one module can *export* those entities for other modules to *access* and may *write*. + +WAMR loads all dependencies recursively according to the *import section* of a module. + +> Currently WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details. + +## Multi-Module Related APIs + +### Register a module + +``` c +bool +wasm_runtime_register_module(const char *module_name, + wasm_module_t module, + char *error_buf, + uint32_t error_buf_size); +``` + +It is used to register a *module* with a *module_name* to WASM runtime, especially for the root module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its *module name*. + +Fot all the sub modules, WAMR will get their names and load the .wasm files from the filesystem or stream, so no need to register the sub modules again. + +### Find a registered module + +``` c +wasm_module_t +wasm_runtime_find_module_registered( + const char *module_name); +``` + +It is used to check if a module with a given *module_name* has been registered, if yes return the module. + +### Module reader and destroyer + +``` c +typedef bool (*module_reader)(const char *module_name, + uint8_t **p_buffer, + uint32_t *p_size); + +typedef void (*module_destroyer)(uint8_t *buffer, + uint32_t size); + +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); +``` + +WAMR hopes that the native host or embedding environment loads/unloads the module WASM files by themselves and only passes runtime the binary content without worrying filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading the sub modules. Developers must implement the two callbacks by themselves. + +### Call function of sub module + +```c +wasm_function_inst_t +wasm_runtime_lookup_function(wasm_module_inst_t const module_inst, + const char *name, + const char *signature); +``` + +Multi-module allows to lookup the function of sub module and call it. There are two ways to indicate the function *name*: + +- parent function name only by default, used to lookup the function of parent module +- sub module name, function name of sub module and two $ symbols, e.g. `$sub_module_name$function_name`, used to lookup function of sub module + +## Example + +### WASM modules +Suppose we have three C files, *mA.c*, *mB.c* and *mC.c*. Each of them has some exported functions and import some from others except mA. + +Undefined symbols can be marked in the source code with the *import_name* clang attribute which means that they are expected to be undefined at static link time. Without the *import_module* clang attribute, undefined symbols will be marked from the *env* module. + +``` C +// mA.c +int A() { return 10; } +``` + +``` C +// mB.c +__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A(); +int B() { return 11; } +int call_A() { return A(); } +``` + +``` C +// mC.c +__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A(); +__attribute__((import_module("mB"))) __attribute__((import_name("B"))) extern int B(); +int C() { return 12; } +int call_A() { return A(); } +int call_B() { return B(); } +``` + +By default no undefined symbols are allowed in the final binary. The flag *--allow-undefined* results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols. + +When building an executable, only the entry point (_start) and symbols with the *export_name* attribute exported by default. in addition, symbols can be exported via the linker command line using *--export*. + +In the example, another linked command option *--export-all* is used. + +> with more detail, please refer to [WebAssembly lld port][https://lld.llvm.org/WebAssembly.html] + +Here is an example how to compile a *.c* to a *.wasm* with clang. Since there is no *start* function, we use *--no-entry* option. + +``` shell +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mA.wasm mA.c +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mB.wasm mB.c +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mC.wasm mC.c +``` + +put *mA.wasm*, *mB.wasm* and *mC.wasm* in the directory *wasm-apps* + +``` shell +$ # copy mA.wasm, mB.wasm and mC.wasm into wasm-apps +$ tree wasm-apps/ +wasm-apps/ +├── mA.wasm +├── mB.wasm +└── mC.wasm +``` + +eventually, their *import relationships* will be like: + +![import relationships](./pics/multi_module_pic1.png) + +### libvmlib + +We need to enable *WAMR_BUILD_MULTI_MODULE* option when building WAMR vmlib. Please ref to [Build WAMR core](./build_wamr.md) for a thoughtful guide. + +### code + +After all above preparation, we can call some functions from native code with APIs + +first, create two callbacks to load WASM module files into memory and unload them later + +``` c +static bool +module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + // ... + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + // ... +} + +static void +module_destroyer_cb(uint8 *buffer, uint32 size) +{ + BH_FREE(buffer); +} +``` + +second, create a large buffer and tell WAMR malloc any resource only from this buffer later + +``` c +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +``` + +third, put all together + +``` c +int main() +{ + /* all malloc() only from the given buffer */ + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space; + init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space); + + /* initialize runtime environment */ + wasm_runtime_full_init(&init_args); + + /* set module reader and destroyer */ + wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb); + + /* load WASM byte buffer from WASM bin file */ + module_reader_cb("mC", &file_buf, &file_buf_size)); + + /* load mC and let WAMR load mA and mB */ + module = wasm_runtime_load(file_buf, file_buf_size, + error_buf, sizeof(error_buf)); + + /* instantiate the module */ + module_inst = + wasm_runtime_instantiate(module, stack_size, + heap_size, error_buf, sizeof(error_buf))); + + + 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]); + + // ... +} +``` + +> please refer to [main.c](../samples/multi_modules/src/main.c) + +The output of the main.c will like: + +``` shell +$ ./a.out + +call "C", it will return 0xc:i32, ===> 0xc:i32 +call "call_B", it will return 0xb:i32, ===> 0xb:i32 +call "call_A", it will return 0xa:i32, ===>0xa:i32 +call "mB.B", it will return 0xb:i32, ===>0xb:i32 +call "mB.call_A", it will return 0xa:i32, ===>0xa:i32 +call "mA.A", it will return 0xa:i32, ===>0xa:i32 + +``` diff --git a/doc/pics/multi_module_pic1.png b/doc/pics/multi_module_pic1.png new file mode 100644 index 000000000..f0c8e33a3 Binary files /dev/null and b/doc/pics/multi_module_pic1.png differ diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index fd8657a91..fc2cb2a0b 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -4,6 +4,7 @@ cmake_minimum_required (VERSION 2.8) project (iwasm) +# set (CMAKE_VERBOSE_MAKEFILE 1) set (WAMR_BUILD_PLATFORM "linux") @@ -11,6 +12,8 @@ set (WAMR_BUILD_PLATFORM "linux") set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") +set (CMAKE_C_STANDARD 99) + # Set WAMR_BUILD_TARGET, currently values supported: # "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" if (NOT DEFINED WAMR_BUILD_TARGET) @@ -57,6 +60,11 @@ if (NOT DEFINED WAMR_BUILD_FAST_INTERP) set (WAMR_BUILD_FAST_INTERP 1) endif () +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) diff --git a/product-mini/platforms/linux/main.c b/product-mini/platforms/linux/main.c index defc0d949..4926f8ac8 100644 --- a/product-mini/platforms/linux/main.c +++ b/product-mini/platforms/linux/main.c @@ -8,24 +8,26 @@ #endif #include #include + #include "bh_platform.h" -#include "bh_assert.h" -#include "bh_log.h" #include "bh_read_file.h" #include "wasm_export.h" static int app_argc; static char **app_argv; -static int print_help() +#define MODULE_PATH ("--module-path=") + +static int +print_help() { printf("Usage: iwasm [-options] wasm_file [args...]\n"); printf("options:\n"); - printf(" -f|--function name Specify function name to run in module\n" - " rather than main\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); #if WASM_ENABLE_LOG != 0 - printf(" -v=n Set log verbose level (0 to 5, default is 2),\n" - " larger level with more log\n"); + printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" + " level with more log\n"); #endif printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); @@ -39,11 +41,14 @@ static int print_help() printf(" to the program, for example:\n"); printf(" --dir= --dir=\n"); #endif - +#if WASM_ENABLE_MULTI_MODULE != 0 + printf(" --module-path= Indicate a module search path. default is current\n" + " directory('./')\n"); +#endif return 1; } -static void* +static void * app_instance_main(wasm_module_inst_t module_inst) { const char *exception; @@ -54,7 +59,7 @@ app_instance_main(wasm_module_inst_t module_inst) return NULL; } -static void* +static void * app_instance_func(wasm_module_inst_t module_inst, const char *func_name) { wasm_application_execute_func(module_inst, func_name, app_argc - 1, @@ -81,20 +86,31 @@ split_string(char *str, int *count) do { p = strtok(str, " "); str = NULL; - res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); + res = (char **)realloc(res, sizeof(char *) * (uint32)(idx + 1)); if (res == NULL) { return NULL; } res[idx++] = p; } while (p); + /** + * since the function name, + * res[0] might be contains a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + if (count) { *count = idx - 1; } return res; } -static void* +static void * app_instance_repl(wasm_module_inst_t module_inst) { char *cmd = NULL; @@ -149,7 +165,49 @@ validate_env_str(char *env) static char global_heap_buf[10 * 1024 * 1024] = { 0 }; #endif -int main(int argc, char *argv[]) +#if WASM_ENABLE_MULTI_MODULE != 0 +static char * +handle_module_path(const char *module_path) +{ + // next character after = + return (strchr(module_path, '=')) + 1; +} + +static char *module_search_path = "."; +static bool +module_reader_callback(const char *module_name, uint8 **p_buffer, + uint32 *p_size) +{ + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return false; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +moudle_destroyer(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +int +main(int argc, char *argv[]) { char *wasm_file = NULL; const char *func_name = NULL; @@ -172,6 +230,8 @@ int main(int argc, char *argv[]) #endif /* Process options. */ + // TODO: use a option name and option handler pair table to + // optimize for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { argc--, argv++; @@ -188,8 +248,9 @@ int main(int argc, char *argv[]) return print_help(); } #endif - else if (!strcmp(argv[0], "--repl")) + else if (!strcmp(argv[0], "--repl")) { is_repl_mode = true; + } else if (!strncmp(argv[0], "--stack-size=", 13)) { if (argv[0][13] == '\0') return print_help(); @@ -204,9 +265,9 @@ int main(int argc, char *argv[]) else if (!strncmp(argv[0], "--dir=", 6)) { if (argv[0][6] == '\0') return print_help(); - if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { + if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { printf("Only allow max dir number %d\n", - (int)(sizeof(dir_list) / sizeof(char*))); + (int)(sizeof(dir_list) / sizeof(char *))); return -1; } dir_list[dir_list_size++] = argv[0] + 6; @@ -216,20 +277,29 @@ int main(int argc, char *argv[]) if (argv[0][6] == '\0') return print_help(); - if (env_list_size >= sizeof(env_list) / sizeof(char*)) { + if (env_list_size >= sizeof(env_list) / sizeof(char *)) { printf("Only allow max env number %d\n", - (int)(sizeof(env_list) / sizeof(char*))); + (int)(sizeof(env_list) / sizeof(char *))); return -1; } tmp_env = argv[0] + 6; if (validate_env_str(tmp_env)) env_list[env_list_size++] = tmp_env; else { - printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", tmp_env); return print_help(); } } +#endif /* WASM_ENABLE_LIBC_WASI */ +#if WASM_ENABLE_MULTI_MODULE != 0 + else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) { + module_search_path = handle_module_path(argv[0]); + if (!strlen(module_search_path)) { + return print_help(); + } + } #endif else return print_help(); @@ -261,13 +331,19 @@ int main(int argc, char *argv[]) return -1; } +#if WASM_ENABLE_LOG != 0 bh_log_set_verbose_level(log_verbose_level); +#endif /* load WASM byte buffer from WASM bin file */ - if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, - &wasm_file_size))) + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) goto fail1; +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer); +#endif + /* load WASM module */ if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, error_buf, sizeof(error_buf)))) { @@ -276,19 +352,14 @@ int main(int argc, char *argv[]) } #if WASM_ENABLE_LIBC_WASI != 0 - wasm_runtime_set_wasi_args(wasm_module, - dir_list, dir_list_size, - NULL, 0, - env_list, env_list_size, - argv, argc); + wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0, + env_list, env_list_size, argv, argc); #endif /* instantiate the module */ - if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, - stack_size, - heap_size, - error_buf, - sizeof(error_buf)))) { + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { printf("%s\n", error_buf); goto fail3; } @@ -316,4 +387,3 @@ fail1: wasm_runtime_destroy(); return 0; } - diff --git a/samples/multi-module/CMakeLists.txt b/samples/multi-module/CMakeLists.txt new file mode 100644 index 000000000..774524c6f --- /dev/null +++ b/samples/multi-module/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(multi_module) +set(CMAKE_VERBOSE_MAKEFILE on) + +################ runtime settings ################ +set(WAMR_BUILD_PLATFORM "linux") + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") +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 1) +set(WAMR_BUILD_FAST_INTERP 0) +set(WAMR_BUILD_MULTI_MODULE 1) + +# compiling and linking flags +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security -mindirect-branch-register") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + +################ application related ################ + +################ WASM MODULES +# .c -> .wasm +add_subdirectory(wasm-apps) + +################ NATIVE +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +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) + +# libraries +target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm) diff --git a/samples/multi-module/src/main.c b/samples/multi-module/src/main.c new file mode 100644 index 000000000..77a443797 --- /dev/null +++ b/samples/multi-module/src/main.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include "bh_read_file.h" +#include "platform_common.h" +#include "wasm_export.h" + +static char * +build_module_path(const char *module_name) +{ + const char *module_search_path = "./wasm-apps"; + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return NULL; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + return wasm_file_name; +} + +static bool +module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + char *wasm_file_path = build_module_path(module_name); + if (!wasm_file_path) { + return false; + } + + printf("- bh_read_file_to_buffer %s\n", wasm_file_path); + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + BH_FREE(wasm_file_path); + return *p_buffer != NULL; +} + +static void +module_destroyer_cb(uint8 *buffer, uint32 size) +{ + printf("- release the read file buffer\n"); + if (!buffer) { + return; + } + + BH_FREE(buffer); + buffer = NULL; +} + +/* 10M */ +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +int +main() +{ + bool ret = false; + /* 16K */ + const uint32 stack_size = 16 * 1024; + const uint32 heap_size = 16 * 1024; + + RuntimeInitArgs init_args = { 0 }; + char error_buf[128] = { 0 }; + /* parameters and return values */ + char* args[1] = { 0 }; + + uint8 *file_buf = NULL; + uint32 file_buf_size = 0; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + + /* all malloc() only from the given buffer */ + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space; + init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space); + + printf("- wasm_runtime_full_init\n"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + goto EXIT; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + printf("- wasm_runtime_set_module_reader\n"); + /* set module reader and destroyer */ + wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!module_reader_cb("mC", &file_buf, &file_buf_size)) { + goto RELEASE_RUNTIME; + } + + /* load mC and let WAMR load mA and mB */ + printf("- wasm_runtime_load\n"); + if (!(module = wasm_runtime_load(file_buf, file_buf_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto RELEASE_BINARY; + } + + /* instantiate the module */ + printf("- wasm_runtime_instantiate\n"); + if (!(module_inst = + wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto UNLOAD_MODULE; + } + + /* call some 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("----------------------------------------\n\n"); + ret = true; + + printf("- wasm_runtime_deinstantiate\n"); + wasm_runtime_deinstantiate(module_inst); +UNLOAD_MODULE: + printf("- wasm_runtime_unload\n"); + wasm_runtime_unload(module); +RELEASE_BINARY: + module_destroyer_cb(file_buf, file_buf_size); +RELEASE_RUNTIME: + printf("- wasm_runtime_destroy\n"); + wasm_runtime_destroy(); +EXIT: + return ret ? 0 : 1; +} diff --git a/samples/multi-module/wasm-apps/CMakeLists.txt b/samples/multi-module/wasm-apps/CMakeLists.txt new file mode 100644 index 000000000..2691c50b9 --- /dev/null +++ b/samples/multi-module/wasm-apps/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 2.8) +project(wasm-apps) + +set(CMAKE_VERBOSE_MAKEFILE on) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) +set(CLANG_COMMAND "/opt/wasi-sdk/bin/clang") + +set(CLANG_FLAGS --target=wasm32 -nostdlib) +set(CLANG_FLAGS ${CLANG_FLAGS} -Wl,--no-entry,--allow-undefined,--export-all) + +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 +) + +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 +) + +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 +) + +add_custom_target(wasm-modules ALL + DEPENDS mA.wasm mB.wasm mC.wasm +) \ No newline at end of file diff --git a/samples/multi-module/wasm-apps/mA.c b/samples/multi-module/wasm-apps/mA.c new file mode 100644 index 000000000..d5e8d833e --- /dev/null +++ b/samples/multi-module/wasm-apps/mA.c @@ -0,0 +1,5 @@ +int +A() +{ + return 10; +} \ No newline at end of file diff --git a/samples/multi-module/wasm-apps/mB.c b/samples/multi-module/wasm-apps/mB.c new file mode 100644 index 000000000..76aa1e7dc --- /dev/null +++ b/samples/multi-module/wasm-apps/mB.c @@ -0,0 +1,16 @@ +__attribute__((import_module("mA"))) +__attribute__((import_name("A"))) extern int +A(); + +int +B() +{ + return 11; +} + +int +call_A() +{ + return A(); +} + diff --git a/samples/multi-module/wasm-apps/mC.c b/samples/multi-module/wasm-apps/mC.c new file mode 100644 index 000000000..5a378dd15 --- /dev/null +++ b/samples/multi-module/wasm-apps/mC.c @@ -0,0 +1,25 @@ +__attribute__((import_module("mA"))) +__attribute__((import_name("A"))) extern int +A(); + +__attribute__((import_module("mB"))) +__attribute__((import_name("B"))) extern int +B(); + +int +C() +{ + return 12; +} + +int +call_A() +{ + return A(); +} + +int +call_B() +{ + return B(); +} \ No newline at end of file diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index a8c70677f..a00884782 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -14,6 +14,7 @@ set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") add_definitions(-DWASM_ENABLE_INTERP=1) add_definitions(-DWASM_ENABLE_WAMR_COMPILER=1) +add_definitions(-DWASM_ENABLE_BULK_MEMORY=1) # Set WAMR_BUILD_TARGET, currently values supported: # "X86_64", "AMD_64", "X86_32", "ARM_32", "MIPS_32", "XTENSA_32" diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index 9a9d212fa..72f717075 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -35,6 +35,7 @@ print_help() printf(" object Native object file\n"); printf(" llvmir-unopt Unoptimized LLVM IR\n"); printf(" llvmir-opt Optimized LLVM IR\n"); + printf(" --enable-bulk-memory Enable the post-MVP bulk memory feature\n"); printf(" -v=n Set log verbose level (0 to 5, default is 2), larger with more log\n"); printf("Examples: wamrc -o test.aot test.wasm\n"); printf(" wamrc --target=i386 -o test.aot test.wasm\n"); @@ -127,6 +128,9 @@ main(int argc, char *argv[]) if (log_verbose_level < 0 || log_verbose_level > 5) return print_help(); } + else if (!strcmp(argv[0], "--enable-bulk-memory")) { + option.enable_bulk_memory = true; + } else return print_help(); }