diff --git a/doc/export_native_api.md b/doc/export_native_api.md index fba83d3ea..684c1cfb4 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -129,11 +129,19 @@ int main(int argc, char **argv) } ``` +## Build native lib into shared library and register it with `iwasm` application + +Developer can also build the native library into a shared library and register it with iwasm application: +```bash +iwasm --native-lib= +``` + +Refer to [native lib sample](../samples/native-lib) for more details. ## Buffer address conversion and boundary check - A WebAssembly sandbox ensures applications only access to its own memory with a private address space. When passing a pointer address from WASM to native, the address value must be converted to native address before the native function can access it. It is also the native world's responsibility to check the buffer length is not over its sandbox boundary. +A WebAssembly sandbox ensures applications only access to its own memory with a private address space. When passing a pointer address from WASM to native, the address value must be converted to native address before the native function can access it. It is also the native world's responsibility to check the buffer length is not over its sandbox boundary. diff --git a/product-mini/platforms/posix/main.c b/product-mini/platforms/posix/main.c index 466bda89b..71cb278a3 100644 --- a/product-mini/platforms/posix/main.c +++ b/product-mini/platforms/posix/main.c @@ -8,6 +8,9 @@ #endif #include #include +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) +#include +#endif #include "bh_platform.h" #include "bh_read_file.h" @@ -39,13 +42,18 @@ print_help() printf(" --dir= Grant wasi access to the given host directories\n"); printf(" to the program, for example:\n"); printf(" --dir= --dir=\n"); - printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); + printf(" --addr-pool= Grant wasi access to the given network addresses in\n"); printf(" CIRD notation to the program, seperated with ',',\n"); printf(" for example:\n"); printf(" --addr-pool=1.2.3.4/15,2.3.4.5/16\n"); #endif +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) + printf(" --native-lib= Register native libraries to the WASM module, which\n"); + printf(" are shared object (.so) files, for example:\n"); + printf(" --native-lib=test1.so --native-lib=test2.so\n"); +#endif #if WASM_ENABLE_MULTI_MODULE != 0 - printf(" --module-path= Indicate a module search path. default is current\n" + printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif #if WASM_ENABLE_LIB_PTHREAD != 0 @@ -174,13 +182,57 @@ validate_env_str(char *env) } #endif -#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 -#ifdef __NuttX__ -static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE * BH_KB] = { 0 }; -#else -static char global_heap_buf[10 * 1024 * 1024] = { 0 }; -#endif -#endif +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) +typedef uint32 (*get_native_lib_func)(char **p_module_name, + NativeSymbol **p_native_symbols); + +static uint32 +load_and_register_native_libs(const char **native_lib_list, + uint32 native_lib_count, + void **native_handle_list) +{ + uint32 i, native_handle_count = 0, n_native_symbols; + NativeSymbol *native_symbols; + char *module_name; + void *handle; + + for (i = 0; i < native_lib_count; i++) { + /* open the native library */ + if (!(handle = dlopen(native_lib_list[i], RTLD_NOW | RTLD_GLOBAL)) + && !(handle = dlopen(native_lib_list[i], RTLD_LAZY))) { + LOG_WARNING("warning: failed to load native library %s", + native_lib_list[i]); + continue; + } + + /* lookup get_native_lib func */ + get_native_lib_func get_native_lib = dlsym(handle, "get_native_lib"); + if (!get_native_lib) { + LOG_WARNING("warning: failed to lookup `get_native_lib` function " + "from native lib %s", + native_lib_list[i]); + dlclose(handle); + continue; + } + + n_native_symbols = get_native_lib(&module_name, &native_symbols); + + /* register native symbols */ + if (!(n_native_symbols > 0 && module_name && native_symbols + && wasm_runtime_register_natives(module_name, native_symbols, + n_native_symbols))) { + LOG_WARNING("warning: failed to register native lib %s", + native_lib_list[i]); + dlclose(handle); + continue; + } + + native_handle_list[native_handle_count++] = handle; + } + + return native_handle_count; +} +#endif /* end of defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) */ #if WASM_ENABLE_MULTI_MODULE != 0 static char * @@ -224,6 +276,14 @@ moudle_destroyer(uint8 *buffer, uint32 size) } #endif /* WASM_ENABLE_MULTI_MODULE */ +#if WASM_ENABLE_GLOBAL_HEAP_POOL != 0 +#ifdef __NuttX__ +static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE * BH_KB] = { 0 }; +#else +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif +#endif + int main(int argc, char *argv[]) { @@ -249,6 +309,12 @@ main(int argc, char *argv[]) const char *addr_pool[8] = { NULL }; uint32 addr_pool_size = 0; #endif +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) + const char *native_lib_list[8] = { NULL }; + uint32 native_lib_count = 0; + void *native_handle_list[8] = { NULL }; + uint32 native_handle_count = 0, native_handle_idx; +#endif #if WASM_ENABLE_DEBUG_INTERP != 0 char *ip_addr = NULL; /* int platform_port = 0; */ @@ -337,6 +403,18 @@ main(int argc, char *argv[]) } } #endif /* WASM_ENABLE_LIBC_WASI */ +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) + else if (!strncmp(argv[0], "--native-lib=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + if (native_lib_count >= sizeof(native_lib_list) / sizeof(char *)) { + printf("Only allow max native lib number %d\n", + (int)(sizeof(native_lib_list) / sizeof(char *))); + return -1; + } + native_lib_list[native_lib_count++] = argv[0] + 13; + } +#endif #if WASM_ENABLE_MULTI_MODULE != 0 else if (!strncmp(argv[0], "--module-path=", strlen("--module-path="))) { @@ -407,6 +485,11 @@ main(int argc, char *argv[]) bh_log_set_verbose_level(log_verbose_level); #endif +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) + native_handle_count = load_and_register_native_libs( + native_lib_list, native_lib_count, native_handle_list); +#endif + /* load WASM byte buffer from WASM bin file */ if (!(wasm_file_buf = (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) @@ -481,6 +564,13 @@ fail2: os_munmap(wasm_file_buf, wasm_file_size); fail1: +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) + /* unload the native libraries */ + for (native_handle_idx = 0; native_handle_idx < native_handle_count; + native_handle_idx++) + dlclose(native_handle_list[native_handle_idx]); +#endif + /* destroy runtime environment */ wasm_runtime_destroy(); return 0; diff --git a/product-mini/platforms/windows/main.c b/product-mini/platforms/windows/main.c index 6e25c4359..9baecc1a4 100644 --- a/product-mini/platforms/windows/main.c +++ b/product-mini/platforms/windows/main.c @@ -40,7 +40,7 @@ print_help() printf(" --dir= --dir=\n"); #endif #if WASM_ENABLE_MULTI_MODULE != 0 - printf(" --module-path= Indicate a module search path. default is current\n" + printf(" --module-path= Indicate a module search path. default is current\n" " directory('./')\n"); #endif #if WASM_ENABLE_LIB_PTHREAD != 0 diff --git a/samples/multi-thread/CMakeLists.txt b/samples/multi-thread/CMakeLists.txt index 89b59833a..89c983862 100644 --- a/samples/multi-thread/CMakeLists.txt +++ b/samples/multi-thread/CMakeLists.txt @@ -70,5 +70,5 @@ set (RUNTIME_SOURCE_ALL ${UNCOMMON_SHARED_SOURCE} ) add_executable (iwasm ${RUNTIME_SOURCE_ALL}) -target_link_libraries(iwasm vmlib -lpthread -lm) +target_link_libraries(iwasm vmlib -lpthread -lm -ldl) diff --git a/samples/native-lib/CMakeLists.txt b/samples/native-lib/CMakeLists.txt new file mode 100644 index 000000000..3968393c6 --- /dev/null +++ b/samples/native-lib/CMakeLists.txt @@ -0,0 +1,76 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.0) +project(native_lib) + +################ runtime settings ############## +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch + +# Set WAMR_BUILD_TARGET, currently values supported are: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", +# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)") + set (WAMR_BUILD_TARGET "AARCH64") + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64") + set (WAMR_BUILD_TARGET "RISCV64") + elseif (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set (CMAKE_BUILD_TYPE Release) +endif () + +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_FAST_INTERP 1) + +# compiling and linking flags +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +################ wamr runtime ################### +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${WAMR_ROOT_DIR}/product-mini/platforms/posix/main.c + ${UNCOMMON_SHARED_SOURCE} +) + +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) + +target_link_libraries(iwasm vmlib -lpthread -lm -ldl) + +################ native libraries ############### +add_library (test_add SHARED test_add.c) +add_library (test_sqrt SHARED test_sqrt.c) + +################ wasm application ############### +add_subdirectory(wasm-app) diff --git a/samples/native-lib/README.md b/samples/native-lib/README.md new file mode 100644 index 000000000..668d28b66 --- /dev/null +++ b/samples/native-lib/README.md @@ -0,0 +1,59 @@ +# "native-lib" sample introduction + +This sample demonstrates how to write required interfaces in native library, build it into a shared library and register the shared library to iwasm. + +The native library should provide `get_native_lib` API for iwasm to return the native library info, including the module name, the native symbol list and the native symbol count, so that iwasm can use them to regiter the native library, for example: + +```C +static int +foo_wrapper(wasm_exec_env_t *exec_env, int x, int y) +{ + return x + y; +} + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(foo, "(ii)i") +}; + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} +``` + +## Preparation + +Please install WASI SDK, download the [wasi-sdk release](https://github.com/CraneStation/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk`. + +## Build the sample + +```bash +mkdir build +cd build +cmake .. +make +``` + +`iwasm`, one wasm module `test.wasm` and two shared libraries `libtest_add.so`, `libtest_sqrt.so` +will be generated. + +## Run workload + +```bash +cd build +./iwasm --native-lib=libtest_add.so --native-lib=libtest_sqrt.so wasm-app/test.wasm +``` + +The output is: + +```bash +Hello World! +10 + 20 = 30 +sqrt(10, 20) = 500 +``` diff --git a/samples/native-lib/test_add.c b/samples/native-lib/test_add.c new file mode 100644 index 000000000..d66ba81fc --- /dev/null +++ b/samples/native-lib/test_add.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_add_wrapper(wasm_exec_env_t *exec_env, int x, int y) +{ + return x + y; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_add, "(ii)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-lib/test_sqrt.c b/samples/native-lib/test_sqrt.c new file mode 100644 index 000000000..e867a2c94 --- /dev/null +++ b/samples/native-lib/test_sqrt.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wasm_export.h" + +static int +test_sqrt_wrapper(wasm_exec_env_t *exec_env, int x, int y) +{ + return x * x + y * y; +} + +/* clang-format off */ +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols[] = { + REG_NATIVE_FUNC(test_sqrt, "(ii)i") +}; +/* clang-format on */ + +uint32_t +get_native_lib(char **p_module_name, NativeSymbol **p_native_symbols) +{ + *p_module_name = "env"; + *p_native_symbols = native_symbols; + return sizeof(native_symbols) / sizeof(NativeSymbol); +} diff --git a/samples/native-lib/wasm-app/CMakeLists.txt b/samples/native-lib/wasm-app/CMakeLists.txt new file mode 100644 index 000000000..ffcd9005a --- /dev/null +++ b/samples/native-lib/wasm-app/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.0) +project(wasm-app) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () + +set (CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--max-memory=131072 -z stack-size=8192 \ + -Wl,--no-entry,--strip-all \ + -Wl,--export=__main_argc_argv \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--allow-undefined" +) + +add_executable(test.wasm main.c) +target_link_libraries(test.wasm) diff --git a/samples/native-lib/wasm-app/main.c b/samples/native-lib/wasm-app/main.c new file mode 100644 index 000000000..f00ec0602 --- /dev/null +++ b/samples/native-lib/wasm-app/main.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int +test_add(int x, int y); + +int +test_sqrt(int x, int y); + +int +main(int argc, char **argv) +{ + int x = 10, y = 20, res; + + printf("Hello World!\n"); + + res = test_add(x, y); + printf("%d + %d = %d\n", x, y, res); + + res = test_sqrt(x, y); + printf("sqrt(%d, %d) = %d\n", x, y, res); + + return 0; +} diff --git a/samples/socket-api/CMakeLists.txt b/samples/socket-api/CMakeLists.txt index 12b979517..936287c7d 100644 --- a/samples/socket-api/CMakeLists.txt +++ b/samples/socket-api/CMakeLists.txt @@ -162,4 +162,4 @@ set (RUNTIME_SOURCE_ALL ${UNCOMMON_SHARED_SOURCE} ) add_executable (iwasm ${RUNTIME_SOURCE_ALL}) -target_link_libraries(iwasm vmlib -lpthread -lm) +target_link_libraries(iwasm vmlib -lpthread -lm -ldl)