Add WASI ABI compatibility check for multi-module (#913)
Refer to https://github.com/WebAssembly/WASI/blob/main/design/application-abi.md to check the WASI ABI compatibility: - Command (main module) may export _start function with signature "()" - Reactor (sub module) may export _initialize function with signature "()" - _start and _initialize can not be exported at the same time - Reactor cannot export _start function - Command and Reactor must export memory And - Rename module->is_wasi_module to module->import_wasi_api - Refactor wasm_loader_find_export() - Remove MULTI_MODULE related codes from mini_loader - Update multi-module samples - Fix a "use-after-free" issue. Since we reuse the memory instance of sub module, just to protect it from freeing an imported memory instance
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required (VERSION 2.8...3.16)
|
||||
project(multi_module)
|
||||
|
||||
################ runtime settings ################
|
||||
@ -41,7 +41,7 @@ set(WAMR_BUILD_INTERP 1)
|
||||
set(WAMR_BUILD_AOT 0)
|
||||
set(WAMR_BUILD_JIT 0)
|
||||
set(WAMR_BUILD_LIBC_BUILTIN 1)
|
||||
set(WAMR_BUILD_LIBC_WASI 0)
|
||||
set(WAMR_BUILD_LIBC_WASI 1)
|
||||
set(WAMR_BUILD_MULTI_MODULE 1)
|
||||
|
||||
# compiling and linking flags
|
||||
@ -66,8 +66,79 @@ add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE})
|
||||
################ application related ################
|
||||
|
||||
################ WASM MODULES
|
||||
include(ExternalProject)
|
||||
|
||||
message(CHECK_START "Detecting WASI-SDK")
|
||||
if(NOT (DEFINED WASI_SDK_DIR OR DEFINED CACHE{WASI_SDK_DIR}))
|
||||
find_path(WASI_SDK_PARENT
|
||||
wasi-sdk
|
||||
PATHS /opt
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(WASI_SDK_PARENT)
|
||||
set(WASI_SDK_DIR ${WASI_SDK_PARENT}/wasi-sdk)
|
||||
endif()
|
||||
endif()
|
||||
if(WASI_SDK_DIR)
|
||||
message(CHECK_PASS "found")
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
message(CHECK_START "Detecting WASI_TOOLCHAIN_FILE at ${WASI_SDK_DIR}")
|
||||
find_file(WASI_TOOLCHAIN_FILE
|
||||
wasi-sdk.cmake
|
||||
PATHS "${WASI_SDK_DIR}/share/cmake"
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(WASI_TOOLCHAIN_FILE)
|
||||
message(CHECK_PASS "found")
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
message(CHECK_START "Detecting WASI_SYS_ROOT at ${WASI_SDK_DIR}")
|
||||
find_path(WASI_SYS_ROOT
|
||||
wasi-sysroot
|
||||
PATHS "${WASI_SDK_DIR}/share"
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(WASI_SYS_ROOT)
|
||||
message(CHECK_PASS "found")
|
||||
set(WASI_SYS_ROOT ${WASI_SYS_ROOT}/wasi-sysroot)
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS ${WASI_SDK_DIR} OR NOT EXISTS ${WASI_TOOLCHAIN_FILE} OR NOT EXISTS ${WASI_SYS_ROOT})
|
||||
message(FATAL_ERROR "Please set the absolute path of wasi-sdk with \'cmake -DWASI_SDK_HOME=XXX\'")
|
||||
else()
|
||||
message(STATUS "WASI_SDK_DIR is ${WASI_SDK_DIR}")
|
||||
message(STATUS "WASI_TOOLCHAIN_FILE is ${WASI_TOOLCHAIN_FILE}")
|
||||
message(STATUS "WASI_SYS_ROOT is ${WASI_SYS_ROOT}")
|
||||
endif()
|
||||
|
||||
# .c -> .wasm
|
||||
add_subdirectory(wasm-apps)
|
||||
ExternalProject_Add(WASM_MODULE
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
|
||||
UPDATE_COMMAND ""
|
||||
PATCH_COMMAND ""
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND}
|
||||
-DWASI_SDK_PREFIX=${WASI_SDK_DIR}
|
||||
-DCMAKE_TOOLCHAIN_FILE=${WASI_TOOLCHAIN_FILE}
|
||||
-DCMAKE_SYSROOT=${WASI_SYS_ROOT}
|
||||
-S ${CMAKE_CURRENT_SOURCE_DIR}/wasm-apps
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build .
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy
|
||||
./mA.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
|
||||
./mB.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
|
||||
./mC.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
|
||||
./mD.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
|
||||
./mE.wasm ${CMAKE_CURRENT_SOURCE_DIR}/build/
|
||||
)
|
||||
|
||||
################ NATIVE
|
||||
include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
|
||||
@ -75,7 +146,7 @@ include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
|
||||
|
||||
add_executable(multi_module src/main.c ${UNCOMMON_SHARED_SOURCE})
|
||||
|
||||
add_dependencies(multi_module vmlib wasm-modules)
|
||||
add_dependencies(multi_module vmlib WASM_MODULE)
|
||||
|
||||
# libraries
|
||||
target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm)
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
static char *
|
||||
build_module_path(const char *module_name)
|
||||
{
|
||||
const char *module_search_path = "./wasm-apps";
|
||||
const char *module_search_path = ".";
|
||||
const char *format = "%s/%s.wasm";
|
||||
int sz = strlen(module_search_path) + strlen("/") + strlen(module_name)
|
||||
+ strlen(".wasm") + 1;
|
||||
@ -107,24 +107,36 @@ main()
|
||||
goto UNLOAD_MODULE;
|
||||
}
|
||||
|
||||
/* call some functions of mC */
|
||||
/* call functions of mC */
|
||||
printf("\n----------------------------------------\n");
|
||||
printf("call \"C\", it will return 0xc:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C", 0, &args[0]);
|
||||
printf("call \"call_B\", it will return 0xb:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "call_B", 0, &args[0]);
|
||||
printf("call \"call_A\", it will return 0xa:i32, ===>");
|
||||
wasm_application_execute_func(module_inst, "call_A", 0, &args[0]);
|
||||
printf("call \"C1\", it will return 0x1f:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C1", 0, args);
|
||||
printf("call \"C2\", it will call B1() of mB and return 0x15:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C2", 0, args);
|
||||
printf("call \"C3\", it will call A1() of mA and return 0xb:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C3", 0, args);
|
||||
printf("call \"C4\", it will call B2() of mB and call A1() of mA and "
|
||||
"return 0xb:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C4", 0, args);
|
||||
printf(
|
||||
"call \"C5\", it will be failed since it is a export function, ===> ");
|
||||
wasm_application_execute_func(module_inst, "C5", 0, args);
|
||||
|
||||
/* call 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 functions of mB */
|
||||
printf("call \"mB.B1\", it will return 0x15:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "$mB$B1", 0, args);
|
||||
printf("call \"mB.B2\", it will call A1() of mA and return 0xb:i32, ===> ");
|
||||
wasm_application_execute_func(module_inst, "$mB$B2", 0, args);
|
||||
printf("call \"mB.B3\", it will be failed since it is a export function, "
|
||||
"===> ");
|
||||
wasm_application_execute_func(module_inst, "$mB$B3", 0, args);
|
||||
|
||||
/* call some functions of mA */
|
||||
printf("call \"mA.A\", it will return 0xa:i32, ===>");
|
||||
wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]);
|
||||
/* call functions of mA */
|
||||
printf("call \"mA.A1\", it will return 0xb:i32, ===>");
|
||||
wasm_application_execute_func(module_inst, "$mA$A1", 0, args);
|
||||
printf("call \"mA.A2\", it will be failed since it is a export function, "
|
||||
"===> ");
|
||||
wasm_application_execute_func(module_inst, "$mA$A2", 0, args);
|
||||
printf("----------------------------------------\n\n");
|
||||
ret = true;
|
||||
|
||||
|
||||
@ -1,41 +1,89 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
# Copyright (C) 2019 Intel Corporation. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
cmake_minimum_required (VERSION 2.8...3.16)
|
||||
project(wasm-apps)
|
||||
|
||||
set(CMAKE_VERBOSE_MAKEFILE on)
|
||||
message(CHECK_START "Detecting WABT")
|
||||
if(NOT (DEFINED WABT_DIR OR DEFINED CACHE{WABT_DIR}))
|
||||
find_path(WABT_DIR
|
||||
wabt
|
||||
PATHS /opt
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(DEFINED WABT_DIR)
|
||||
set(WABT_DIR ${WABT_DIR}/wabt)
|
||||
endif()
|
||||
endif()
|
||||
if(WABT_DIR)
|
||||
message(CHECK_PASS "found")
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
set(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
|
||||
message(CHECK_START "Detecting WASM_OBJDUMP at ${WABT_DIR}")
|
||||
find_program(WASM_OBJDUMP
|
||||
wasm-objdump
|
||||
PATHS "${WABT_DIR}/bin"
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(WASM_OBJDUMP)
|
||||
message(CHECK_PASS "found")
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
set(SOURCE_B ${CMAKE_CURRENT_SOURCE_DIR}/mB.c)
|
||||
add_custom_command(
|
||||
OUTPUT mB.wasm
|
||||
COMMENT "Transform mB.C to mB.WASM"
|
||||
COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mB.wasm ${SOURCE_B}
|
||||
DEPENDS ${SOURCE_B}
|
||||
VERBATIM
|
||||
message(CHECK_START "Detecting WASM2WAT at ${WABT_DIR}")
|
||||
find_program(WASM2WAT
|
||||
wasm2wat
|
||||
PATHS "${WABT_DIR}/bin"
|
||||
NO_DEFAULT_PATH
|
||||
NO_CMAKE_FIND_ROOT_PATH
|
||||
)
|
||||
if(WASM2WAT)
|
||||
message(CHECK_PASS "found")
|
||||
else()
|
||||
message(CHECK_FAIL "not found")
|
||||
endif()
|
||||
|
||||
set(SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/mC.c)
|
||||
add_custom_command(
|
||||
OUTPUT mC.wasm
|
||||
COMMENT "Transform mC.C to mC.WASM"
|
||||
COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mC.wasm ${SOURCE_C}
|
||||
DEPENDS ${SOURCE_C}
|
||||
VERBATIM
|
||||
)
|
||||
function(COMPILE_WITH_CLANG SOURCE_FILE COMMAND)
|
||||
get_filename_component(FILE_NAME ${SOURCE_FILE} NAME_WLE)
|
||||
|
||||
set(WASM_MODULE ${FILE_NAME}.wasm)
|
||||
|
||||
set(MAIN_TARGET_NAME MODULE_${FILE_NAME})
|
||||
|
||||
add_executable(${MAIN_TARGET_NAME} ${SOURCE_FILE})
|
||||
set_target_properties(${MAIN_TARGET_NAME} PROPERTIES OUTPUT_NAME ${WASM_MODULE})
|
||||
|
||||
if(${COMMAND})
|
||||
message(STATUS "Generating ${WASM_MODULE} as COMMAND...")
|
||||
else()
|
||||
message(STATUS "Generating ${WASM_MODULE} as REACTOR...")
|
||||
target_link_options(${MAIN_TARGET_NAME} PRIVATE -mexec-model=reactor)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${WASM2WAT})
|
||||
message(STATUS "Dumping ${WASM_MODULE}...")
|
||||
set(WASM_WAT ${FILE_NAME}.wat)
|
||||
set(DUMP_TARGET_NAME DUMP_${FILE_NAME})
|
||||
add_custom_command(OUTPUT ${WASM_WAT}
|
||||
COMMAND ${WASM2WAT} --enable-all -o ${WASM_WAT} ${WASM_MODULE}
|
||||
COMMENT "Dumping ${WASM_MODULE}..."
|
||||
DEPENDS ${MAIN_TARGET_NAME}
|
||||
)
|
||||
|
||||
add_custom_target(${DUMP_TARGET_NAME} ALL
|
||||
DEPENDS ${WASM_WAT}
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
compile_with_clang(mA.c OFF)
|
||||
compile_with_clang(mB.c OFF)
|
||||
compile_with_clang(mC.c ON)
|
||||
compile_with_clang(mD.cpp ON)
|
||||
compile_with_clang(mE.cpp OFF)
|
||||
|
||||
add_custom_target(wasm-modules ALL
|
||||
DEPENDS mA.wasm mB.wasm mC.wasm
|
||||
)
|
||||
@ -1,5 +1,13 @@
|
||||
int
|
||||
A()
|
||||
__attribute__((export_name("A1"))) int
|
||||
A1()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
return 11;
|
||||
}
|
||||
|
||||
int
|
||||
A2()
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
/* mA is a reactor. it doesn't need a main() */
|
||||
@ -1,15 +1,23 @@
|
||||
__attribute__((import_module("mA")))
|
||||
__attribute__((import_name("A"))) extern int
|
||||
A();
|
||||
__attribute__((import_name("A1"))) extern int
|
||||
A1();
|
||||
|
||||
int
|
||||
B()
|
||||
__attribute__((export_name("B1"))) int
|
||||
B1()
|
||||
{
|
||||
return 11;
|
||||
return 21;
|
||||
}
|
||||
|
||||
__attribute__((export_name("B2"))) int
|
||||
B2()
|
||||
{
|
||||
return A1();
|
||||
}
|
||||
|
||||
int
|
||||
call_A()
|
||||
B3()
|
||||
{
|
||||
return A();
|
||||
return 23;
|
||||
}
|
||||
|
||||
/* mA is a reactor. it doesn't need a main() */
|
||||
@ -1,25 +1,51 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
__attribute__((import_module("mA")))
|
||||
__attribute__((import_name("A"))) extern int
|
||||
A();
|
||||
__attribute__((import_name("A1"))) extern int
|
||||
A1();
|
||||
|
||||
__attribute__((import_module("mB")))
|
||||
__attribute__((import_name("B"))) extern int
|
||||
B();
|
||||
__attribute__((import_name("B1"))) extern int
|
||||
B1();
|
||||
|
||||
int
|
||||
C()
|
||||
__attribute__((import_module("mB")))
|
||||
__attribute__((import_name("B2"))) extern int
|
||||
B2();
|
||||
|
||||
__attribute__((export_name("C1"))) int
|
||||
C1()
|
||||
{
|
||||
return 12;
|
||||
return 31;
|
||||
}
|
||||
|
||||
__attribute__((export_name("C2"))) int
|
||||
C2()
|
||||
{
|
||||
return B1();
|
||||
}
|
||||
|
||||
__attribute__((export_name("C3"))) int
|
||||
C3()
|
||||
{
|
||||
return A1();
|
||||
}
|
||||
|
||||
__attribute__((export_name("C4"))) int
|
||||
C4()
|
||||
{
|
||||
return B2();
|
||||
}
|
||||
|
||||
int
|
||||
call_A()
|
||||
C5()
|
||||
{
|
||||
return A();
|
||||
return C1() + C2() + C3() + 35;
|
||||
}
|
||||
|
||||
int
|
||||
call_B()
|
||||
main()
|
||||
{
|
||||
return B();
|
||||
printf("%u\n", C5());
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
74
samples/multi-module/wasm-apps/mD.cpp
Normal file
74
samples/multi-module/wasm-apps/mD.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
static void
|
||||
bye_main()
|
||||
{
|
||||
std::cout << "mD " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
static void
|
||||
bye_setup()
|
||||
{
|
||||
std::cout << "mD " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
static void
|
||||
bye_func()
|
||||
{
|
||||
std::cout << "mD " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
void
|
||||
func3() __attribute__((__import_module__("mE"), __import_name__("func1")));
|
||||
|
||||
void
|
||||
func4() __attribute__((__import_module__("mE"), __import_name__("func2")));
|
||||
|
||||
void
|
||||
func1()
|
||||
{
|
||||
std::printf("mD %s\n", __FUNCTION__);
|
||||
if (std::atexit(bye_func) != 0) {
|
||||
std::perror("register an atexit handler failed");
|
||||
}
|
||||
func3();
|
||||
}
|
||||
|
||||
void
|
||||
func2()
|
||||
{
|
||||
std::printf("mD %s\n", __FUNCTION__);
|
||||
func4();
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void
|
||||
setup()
|
||||
{
|
||||
std::cout << "mD " << __FUNCTION__ << std::endl;
|
||||
if (std::atexit(bye_setup) != 0) {
|
||||
std::perror("register an atexit handler failed");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((destructor)) void
|
||||
teardown()
|
||||
{
|
||||
std::cout << "mD " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
std::printf("mD %s\n", __FUNCTION__);
|
||||
|
||||
if (std::atexit(bye_main) != 0) {
|
||||
std::perror("register an atexit handler failed");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
func1();
|
||||
func2();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
45
samples/multi-module/wasm-apps/mE.cpp
Normal file
45
samples/multi-module/wasm-apps/mE.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
static void
|
||||
bye_setup()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
static void
|
||||
bye_func()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void
|
||||
setup()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
if (std::atexit(bye_setup) != 0) {
|
||||
std::perror("register an atexit handler failed");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((destructor)) void
|
||||
teardown()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
__attribute__((export_name("func1"))) void
|
||||
func1()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
if (std::atexit(bye_func) != 0) {
|
||||
std::perror("register an atexit handler failed");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((export_name("func2"))) void
|
||||
func2()
|
||||
{
|
||||
std::cout << "mE " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
Reference in New Issue
Block a user