From 2e36149e326dab2374eda01bd7be5810942a5db5 Mon Sep 17 00:00:00 2001 From: Daisuke Ogawa Date: Fri, 28 Feb 2020 13:19:35 +0900 Subject: [PATCH 1/5] Fix bug of Dockerfile (#183) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 565b740e6..993f11a28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,8 +14,8 @@ WORKDIR /root RUN git clone https://github.com/intel/wasm-micro-runtime -RUN cd wasm-micro-runtime/core/iwasm/products/linux/ && mkdir build && \ +RUN cd wasm-micro-runtime/product-mini/platforms/linux/ && mkdir build && \ cd build && cmake .. && make RUN cd /usr/bin && ln -s wasm-ld-8 wasm-ld -RUN cd /usr/bin && ln -s ~/wasm-micro-runtime/core/iwasm/products/linux/build/iwasm iwasm +RUN cd /usr/bin && ln -s ~/wasm-micro-runtime/product-mini/platforms/linux/build/iwasm iwasm From 0d3f304191789fcac5f1aa822e5c01f2232e8804 Mon Sep 17 00:00:00 2001 From: wenyongh Date: Wed, 4 Mar 2020 20:12:38 +0800 Subject: [PATCH 2/5] Implement native function pointer check, addr conversion and register, update documents (#185) Modified WASM runtime API: - wasm_runtime_module_malloc() - wasm_runtime_lookup_function() Introduced runtime API - wasm_runtime_register_natives() --- README.md | 119 +-- core/app-framework/README.md | 125 +++ .../app-native-shared/native_interface.h | 4 +- core/app-framework/app_ext_lib_export.c | 39 + core/app-framework/app_framework.cmake | 13 +- core/app-framework/base/native/base_lib.inl | 18 +- .../base/native/req_resp_native_api.h | 14 +- .../base/native/request_response.c | 36 +- .../connection/native/connection.inl | 8 +- .../connection/native/connection_native_api.h | 6 +- .../connection/native/connection_wrapper.c | 26 +- .../connection/native/wasm_lib.cmake | 3 + .../sensor/native/runtime_sensor.c | 19 +- .../sensor/native/runtime_sensor.inl | 8 +- .../sensor/native/sensor_native_api.h | 5 +- .../sensor/native/wasm_lib.cmake | 2 + .../app-framework/wgl/native/gui_native_api.h | 10 +- core/app-framework/wgl/native/wamr_gui.inl | 16 +- core/app-framework/wgl/native/wasm_lib.cmake | 1 + .../wgl/native/wgl_btn_wrapper.c | 4 +- .../app-framework/wgl/native/wgl_cb_wrapper.c | 4 +- .../wgl/native/wgl_label_wrapper.c | 4 +- .../wgl/native/wgl_list_wrapper.c | 4 +- .../wgl/native/wgl_native_utils.c | 8 +- .../wgl/native/wgl_native_utils.h | 2 +- .../wgl/native/wgl_obj_wrapper.c | 4 +- core/app-mgr/README.md | 10 + core/iwasm/aot/aot_loader.c | 53 +- core/iwasm/aot/aot_reloc.h | 1 + core/iwasm/aot/aot_runtime.c | 121 +-- core/iwasm/aot/aot_runtime.h | 12 +- core/iwasm/common/wasm_exec_env.c | 11 + core/iwasm/common/wasm_exec_env.h | 4 + core/iwasm/common/wasm_native.c | 312 +++++-- core/iwasm/common/wasm_native.h | 80 +- core/iwasm/common/wasm_runtime_common.c | 246 +++-- core/iwasm/common/wasm_runtime_common.h | 14 +- core/iwasm/compilation/aot.c | 1 + core/iwasm/compilation/aot.h | 2 + core/iwasm/compilation/aot_compiler.h | 1 + core/iwasm/compilation/aot_emit_function.c | 303 +++--- core/iwasm/compilation/aot_llvm.c | 33 +- core/iwasm/compilation/aot_llvm.h | 2 + core/iwasm/include/ext_lib_export.h | 27 - core/iwasm/include/lib_export.h | 10 +- core/iwasm/include/wasm_export.h | 39 +- core/iwasm/interpreter/wasm.h | 5 +- core/iwasm/interpreter/wasm_interp.c | 19 +- core/iwasm/interpreter/wasm_loader.c | 42 +- core/iwasm/interpreter/wasm_runtime.c | 74 +- core/iwasm/interpreter/wasm_runtime.h | 3 +- .../libc-builtin/libc_builtin_wrapper.c | 654 +++++-------- .../libraries/libc-wasi/libc_wasi_wrapper.c | 871 ++++++------------ core/shared/include/config.h | 8 +- core/shared/platform/zephyr/bh_platform.c | 2 +- doc/embed_wamr.md | 159 +++- doc/export_native_api.md | 394 ++++---- doc/pics/app_framework.PNG | Bin 0 -> 41221 bytes doc/pics/sensor_callflow.PNG | Bin 76558 -> 39167 bytes doc/pics/workflow.PNG | Bin 68967 -> 0 bytes product-mini/platforms/alios-things/aos.mk | 2 +- .../alios-things/src/ext_lib_export.c | 10 - product-mini/platforms/android/CMakeLists.txt | 15 +- .../platforms/android/ext_lib_export.c | 10 - product-mini/platforms/darwin/CMakeLists.txt | 2 +- .../platforms/darwin/ext_lib_export.c | 10 - .../platforms/linux-sgx/CMakeLists.txt | 1 - .../linux-sgx/enclave-sample/Makefile | 8 +- .../platforms/linux-sgx/ext_lib_export.c | 10 - product-mini/platforms/linux/CMakeLists.txt | 2 +- product-mini/platforms/linux/ext_lib_export.c | 10 - product-mini/platforms/vxworks/CMakeLists.txt | 2 +- .../platforms/vxworks/ext_lib_export.c | 10 - .../platforms/zephyr/simple/CMakeLists.txt | 3 +- .../zephyr/simple/src/ext_lib_export.c | 10 - samples/gui/README.md | 6 + .../linux-build/CMakeLists.txt | 1 - .../gui/wasm-runtime-wgl/src/ext_lib_export.c | 12 - .../zephyr-build/CMakeLists.txt | 1 - samples/littlevgl/README.md | 1 - samples/littlevgl/build.sh | 2 +- .../littlevgl/vgl-wasm-runtime/CMakeLists.txt | 2 +- .../vgl-wasm-runtime/src/display_indev.h | 19 +- .../vgl-wasm-runtime/src/ext_lib_export.c | 18 - .../src/platform/linux/display_indev.c | 96 +- .../src/platform/linux/iwasm_main.c | 27 + .../src/platform/zephyr/display_indev.c | 57 +- .../src/platform/zephyr/iwasm_main.c | 23 +- .../zephyr-build/CMakeLists.txt | 1 - .../littlevgl/wasm-apps/src/display_indev.h | 23 +- samples/littlevgl/wasm-apps/src/main.c | 21 +- samples/simple/CMakeLists.txt | 2 +- samples/simple/src/ext_lib_export.c | 12 - wamr-compiler/CMakeLists.txt | 2 +- wamr-compiler/ext_lib_export.c | 10 - wamr-sdk/README.md | 154 +++- 96 files changed, 2293 insertions(+), 2317 deletions(-) create mode 100644 core/app-framework/README.md create mode 100644 core/app-framework/app_ext_lib_export.c create mode 100644 core/app-mgr/README.md delete mode 100644 core/iwasm/include/ext_lib_export.h create mode 100644 doc/pics/app_framework.PNG delete mode 100644 doc/pics/workflow.PNG delete mode 100644 product-mini/platforms/alios-things/src/ext_lib_export.c delete mode 100644 product-mini/platforms/android/ext_lib_export.c delete mode 100644 product-mini/platforms/darwin/ext_lib_export.c delete mode 100644 product-mini/platforms/linux-sgx/ext_lib_export.c delete mode 100644 product-mini/platforms/linux/ext_lib_export.c delete mode 100644 product-mini/platforms/vxworks/ext_lib_export.c delete mode 100644 product-mini/platforms/zephyr/simple/src/ext_lib_export.c delete mode 100644 samples/gui/wasm-runtime-wgl/src/ext_lib_export.c delete mode 100644 samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c delete mode 100644 samples/simple/src/ext_lib_export.c delete mode 100644 wamr-compiler/ext_lib_export.c diff --git a/README.md b/README.md index 3bf0b7b0f..d16353ea8 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ WebAssembly Micro Runtime ========================= -[Building WAMR VM core](./doc/build_wamr.md) | [Embedding WAMR VM core](./doc/embed_wamr.md) | [Building WASM applications](./doc/build_wasm_app.md) | [Samples and demos](https://github.com/bytecodealliance/wasm-micro-runtime#samples-and-demos) +[Build WAMR VM core](./doc/build_wamr.md) | [Embed WAMR](./doc/embed_wamr.md) | [Export native function](./doc/export_native_api.md) | [Build WASM applications](./doc/build_wasm_app.md) | [Samples](https://github.com/bytecodealliance/wasm-micro-runtime#samples-and-demos) **A [Bytecode Alliance][BA] project** [BA]: https://bytecodealliance.org/ -WebAssembly Micro Runtime (WAMR) is a standalone WebAssembly (WASM) runtime with small footprint. It includes a few parts as below: -- The "iwasm" VM core, supporting WebAssembly interpreter, ahead of time compilation (AoT) and Just-in-Time compilation (JIT) +WebAssembly Micro Runtime (WAMR) is a standalone WebAssembly (WASM) runtime with a small footprint. It includes a few parts as below: +- The **"iwasm" VM core**, supporting WebAssembly interpreter, ahead of time compilation (AoT) and Just-in-Time compilation (JIT) -- The application framework and the supporting API's for the WASM applications +- The **application framework** and the supporting API's for the WASM applications -- The dynamic management of the WASM applications +- The **dynamic management** of the WASM applications @@ -20,16 +20,16 @@ iwasm VM core ### key features -- Embeddable with the supporting C API's +- [Embeddable with the supporting C API's](./doc/embed_wamr.md) - Small runtime binary size (85K for interpreter and 50K for AoT) and low memory usage - Near to native speed by AoT -- AoT module loader works for both embedded OS and Linux system -- Choices of WASM application libc support: the built-in libc subset for embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc -- The mechanism for exporting native API's to WASM applications +- Unique AoT support for embedded systems which have no system loaders +- 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 +- [The mechanism for exporting native API's to WASM applications](./doc/export_native_api.md) ### Supported architectures and platforms -The iwasm supports following architectures: +The iwasm supports the following architectures: - X86-64, X86-32 - ARM, THUMB (ARMV7 Cortex-M7 and Cortex-A15 are tested) @@ -38,13 +38,7 @@ The iwasm supports following architectures: Following platforms are supported: -- [Linux](./doc/build_wamr.md#linux) -- [Zephyr](./doc/build_wamr.md#zephyr) -- [MacOS](./doc/build_wamr.md#macos) -- [VxWorks](./doc/build_wamr.md#vxworks) -- [AliOS-Things](./doc/build_wamr.md#alios-things) -- [Intel Software Guard Extention (Linux)](./doc/build_wamr.md#linux-sgx-intel-software-guard-extention) -- [Android](./doc/build_wamr.md#android) +- [Linux](./doc/build_wamr.md#linux), [Zephyr](./doc/build_wamr.md#zephyr), [MacOS](./doc/build_wamr.md#macos), [VxWorks](./doc/build_wamr.md#vxworks), [AliOS-Things](./doc/build_wamr.md#alios-things), [Intel Software Guard Extention (Linux)](./doc/build_wamr.md#linux-sgx-intel-software-guard-extention), [Android](./doc/build_wamr.md#android) Refer to [WAMR porting guide](./doc/port_wamr.md) for how to port WAMR to a new platform. @@ -55,107 +49,51 @@ Execute following commands to build **wamrc** compiler: ```shell cd wamr-compiler ./build_llvm.sh -mkdir build -cd build +mkdir build && cd build cmake .. make +ln -s ./wamrc /usr/bin/wamrc ``` -After build is completed, create a symbolic link **/usr/bin/wamrc** to the generated wamrc. - ### Build the mini product -WAMR supports building the iwasm VM core only (no app framework) to the mini product. The WAMR mini product takes the WASM application file name as input, and then executes it. For the detailed procedure, see **[build WAMR VM core](./doc/build_wamr.md)** and **[build and run WASM application](./doc/build_wasm_app.md)**. - -### Embed WAMR VM core - -WAMR provides a set of C API for loading the WASM module, instantiating the module and invoking a WASM function from a native call. For the details, see [embed WAMR VM core](./doc/embed_wamr.md). +WAMR supports building the iwasm VM core only (no app framework) to the mini product. The WAMR mini product takes the WASM application file name as input and then executes it. For the detailed procedure, see **[build WAMR VM core](./doc/build_wamr.md)** and **[build and run WASM application](./doc/build_wasm_app.md)**. Application framework =================================== -By using the iwasm VM core, we are flexible to build different application frameworks for the specific domains, although it would take quite some efforts. +By using the iwasm VM core, we are flexible to build different application frameworks for the specific domains, although it would take quite some effort. -The WAMR has offered a comprehensive framework for programming WASM applications for device and IoT usages. The framework supports running multiple applications, which are based on the event driven programming model. Here are the supporting API sets by the [WAMR application library](./doc/wamr_api.md) : +The WAMR has offered a comprehensive framework for programming WASM applications for device and IoT usages. The framework supports running multiple applications, that are based on the event driven programming model. Here are the supporting API sets by the [WAMR application framework library](./doc/wamr_api.md) : -- Timer -- Micro service (Request/Response) and Pub/Sub inter-app communication -- Sensor -- Connectivity and data transmission -- 2D graphic UI (based on littlevgl) +- Timer, Inter-app communication (request/response and pub/sub), Sensor, Connectivity and data transmission, 2D graphic UI -Every subfolder under [WAMR application framework](./core/app-framework) folder is a compilation configurable component. The developers can copy the template folder to create new components to the application framework. If a component needs to export native functions to the WASM application, refer to the [export_native_api.md](./doc/export_native_api.md) . +Browse the folder [core/app-framework](./core/app-framework) for how to extend the application framework. # Remote application management -The WAMR application manager supports remote application management from host environment or the cloud through any physical communications such as TCP, UPD, UART, BLE, etc. Its modular design makes it able to support application management for different managed runtimes. - - - - - -The tool [host_tool](./test-tools/host-tool) communicates to the WAMR app manager for installing/uninstalling the WASM applications on companion chip from host system. And the [IoT App Store Demo](./test-tools/IoT-APP-Store-Demo/) shows the conception of remotely managing the device applications from cloud. +The WAMR application manager supports remote application management from the host environment or the cloud through any physical communications such as TCP, UPD, UART, BLE, etc. Its modular design makes it able to support application management for different managed runtimes. +The tool [host_tool](./test-tools/host-tool) communicates to the WAMR app manager for installing/uninstalling the WASM applications on companion chip from the host system. And the [IoT App Store Demo](./test-tools/IoT-APP-Store-Demo/) shows the conception of remotely managing the device applications from the cloud. +Browse the folder [core/app-mgr](./core/app-mgr) for the details. WAMR SDK ========== -The **wamr-sdk** tools build the WAMR to both **runtime SDK** for embedding by your native codes and **APP SDK** for developing the WASM applications. A SDK profile presents a configuration of build parameters for the selection of CPU arch, software platforms, execution mode, libc and application framework components. +Usually there are two tasks for integrating the WAMR into a particular project: -**Note**: [WASI-SDK](https://github.com/CraneStation/wasi-sdk/releases) version 7 and above should be installed before building the WAMR SDK. +- Select what WAMR components (vmcore, libc, app-mgr, app-framework components) to be integrated, and get the associated source files added into the project building configuration +- Generate the APP SDK for developing the WASM apps on the selected libc and framework components -### Menu configuration for building SDK - -Menu configuration is supported for easy integration of runtime components and application libraries for the target architecture and platform. - -``` -cd wamr-sdk -./build_sdk.sh -i -n [profile name] -``` - -wamr build menu configuration - -After the menu configuration is finished, the building process is automatically started. When the building gets successful, the SDK package is generated under folder $wamr-sdk/out/{profile}, and the header files of configured components were copied into the SDK package. - -The directory structure of a SDK package with profile name "simple": - -``` -simple/ -├── app-sdk -│   ├── libc-builtin-sysroot -│   │   ├── include -│   │   └── share -│   └── wamr-app-framework -│   ├── include -│   │   ├── bi-inc -│   │   └── wa-inc -│   ├── lib -│   └── share -└── runtime-sdk - ├── include - │   └── bi-inc - └── lib -``` +The **[WAMR SDK](./wamr-sdk)** tools is helpful to finish the two tasks quickly. It supports menu configuration for selecting WAMR components and builds the WAMR to a SDK package that includes **runtime SDK** and **APP SDK**. The runtime SDK is used for building the native application and the APP SDK should be shipped to WASM application developers. - -### Use Runtime SDK - -The folder "**runtime-sdk**" contains all the header files and library files for integration with project native code. - -### Build WASM applications with APP-SDK - -The folder “**app-sdk**” contains all the header files and WASM library for developing the WASM application. For C/C++ based WASM applications, the developers can use conventional cross-compilation procedure to build the WASM application. Refer to [build WASM applications](./doc/build_wasm_app.md) for the details. - - - - -Samples and demos +Samples ================= The WAMR samples integrate the iwasm VM core, application manager and selected application framework components. The samples are located in folder [samples](./samples): @@ -164,9 +102,6 @@ The WAMR samples integrate the iwasm VM core, application manager and selected a - **[gui](./samples/gui/README.md)**: Moved the [LittlevGL](https://github.com/littlevgl/) library into the runtime and defined a WASM application interface by wrapping the littlevgl API. It uses **WASI libc** and executes apps in **interpreter** mode by default. -The graphic user interface demo photo: - -![WAMR samples diagram](./doc/pics/vgl_demo.png "WAMR samples diagram") Releases and acknowledgments @@ -182,7 +117,7 @@ Roadmap See the [roadmap](./doc/roadmap.md) to understand what major features are planned or under development. -Please submit issues for any new feature request, or your plan for contributing new features. +Please submit issues for any new feature request or your plan for contributing new features. License diff --git a/core/app-framework/README.md b/core/app-framework/README.md new file mode 100644 index 000000000..0deb4af86 --- /dev/null +++ b/core/app-framework/README.md @@ -0,0 +1,125 @@ +Application framework +======= + +## Directory structure + + + +This folder "app-native-shared" is for the source files shared by both WASM APP and native runtime + +- The c files in this directory are compiled into both the WASM APP and runtime. +- The header files for distributing to SDK are placed in the "bi-inc" folder. + + + +This folder "template" contains a pre-defined directory structure for a framework component. The developers can copy the template folder to create new components to the application framework. + + + +Every other subfolder is framework component. Each component contains two library parts: **app and native**. + +- The "base" component provide timer API and inter-app communication support. It must be enabled if other components are selected. +- Under the "app" folder of a component, the subfolder "wa_inc" holds all header files that should be included by the WASM applications + + + +## Application framework basic model + +The app framework is built on top of two fundamental operations: + +- [Native calls into WASM function](../../doc/embed_wamr.md) + +- [WASM app calls into native API](../../doc/export_native_api.md) + +Asynchronized programming model is supported for WASM applications + +- Every WASM app has its own sandbox and thread + +- Queue and messaging + + + + + +## Customized building of app framework + +A component can be compilation configurable to the runtime. The wamr SDK tool "build_sdk.sh" supports menu config to select app components for building a customized runtime. + +A number of CMAKE variables are defined to control build of framework and components. You can create a cmake file for defining these variables and include it in the CMakeList.txt for your software, or pass it in "-x" argument when run the [build_sdk.sh](../../wamr-sdk/build_sdk.sh) for building the runtime SDK. + +```cmake +set (WAMR_BUILD_APP_FRAMEWORK 1) +set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE) +``` + +Variables: + +- **WAMR_BUILD_APP_FRAMEWORK**: enable the application framework +- **WAMR_BUILD_APP_LIST**: the selected components to be built into the final runtime + + + +The configuration file can be generated through the wamr-sdk menu config: + +```bash +cd wamr-sdk +./build_sdk -n [profile] -i +``` + + + +## Create new components + +Generally you should follow following steps to create a new component: + +- Copy the “template” for creating a new folder + +- Implement the app part + + - If your component exports native function to WASM, ensure your created a header file under app for declaring the function prototype. + - If you component provides header files for the WASM applications to include, ensure it is placed under subfolder "wa_inc". + +- Implement the native part + + - If your native function is exported to WASM, you need to create an inl file for the registration. It can be any file name, assuming the file name is "my_component.inl" here: + + ```c + //use right signature for your functions + EXPORT_WASM_API_WITH_SIG(wasm_my_component_api_1, "(i*~)i"), + EXPORT_WASM_API_WITH_SIG(wasm_my_component_api_2, "(i)i"), + ``` + + - Ensure "wasm_lib.cmake" is provided as it will be included by the WAMR SDK building script + + - Add a definition in "wasm_lib.cmake" for your component, e.g. + + ```cmake + add_definitions (-DAPP_FRAMEWORK_MY_COMPONENT) + ``` + +- Modify the file [app_ext_lib_export.c](./app_ext_lib_export.c) to register native APIs exported for the new introduced component. Skip it if not exporting native functions. + + ``` + #include "lib_export.h" + + ... + #ifdef APP_FRAMEWORK_MY_COMPONENT // this definition is created in wasm_lib.cmake + #include "my_component_native_api.h" + #endif + + static NativeSymbol extended_native_symbol_defs[] = { + ... + #ifdef APP_FRAMEWORK_MY_COMPONENT + #include "my_component.inl" + #endif + }; + ``` + + + +## Sensor component working flow + + + +![](../../doc/pics/sensor_callflow.PNG) + diff --git a/core/app-framework/app-native-shared/native_interface.h b/core/app-framework/app-native-shared/native_interface.h index 0c094267a..0e0d063b1 100644 --- a/core/app-framework/app-native-shared/native_interface.h +++ b/core/app-framework/app-native-shared/native_interface.h @@ -26,8 +26,8 @@ #define addr_native_to_app(ptr) \ wasm_runtime_addr_native_to_app(module_inst, ptr) -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) #define module_free(offset) \ wasm_runtime_module_free(module_inst, offset) diff --git a/core/app-framework/app_ext_lib_export.c b/core/app-framework/app_ext_lib_export.c new file mode 100644 index 000000000..6e66fdfb6 --- /dev/null +++ b/core/app-framework/app_ext_lib_export.c @@ -0,0 +1,39 @@ +#include "lib_export.h" + +#ifdef APP_FRAMEWORK_SENSOR + #include "sensor_native_api.h" +#endif + +#ifdef APP_FRAMEWORK_CONNECTION + #include "connection_native_api.h" +#endif + +#ifdef APP_FRAMEWORK_WGL + #include "gui_native_api.h" +#endif + +/* More header file here */ + +static NativeSymbol extended_native_symbol_defs[] = { +#ifdef APP_FRAMEWORK_SENSOR + #include "runtime_sensor.inl" +#endif + +#ifdef APP_FRAMEWORK_CONNECTION + #include "connection.inl" +#endif + +#ifdef APP_FRAMEWORK_WGL + #include "wamr_gui.inl" +#endif + +/* More inl file here */ +}; + +int +get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis) +{ + *p_ext_lib_apis = extended_native_symbol_defs; + return sizeof(extended_native_symbol_defs) / sizeof(NativeSymbol); +} + diff --git a/core/app-framework/app_framework.cmake b/core/app-framework/app_framework.cmake index 68439d874..8ad43dec1 100644 --- a/core/app-framework/app_framework.cmake +++ b/core/app-framework/app_framework.cmake @@ -1,8 +1,15 @@ # Copyright (C) 2019 Intel Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +add_definitions (-DWASM_ENABLE_APP_FRAMEWORK=1) + set (APP_FRAMEWORK_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) +if ( NOT DEFINED APP_FRAMEWORK_INCLUDE_TYPE ) + LIST (APPEND WASM_APP_LIB_SOURCE_ALL ${CMAKE_CURRENT_LIST_DIR}/app_ext_lib_export.c) +endif() + # app-native-shared and base are required include (${APP_FRAMEWORK_ROOT_DIR}/app-native-shared/native_interface.cmake) LIST (APPEND WASM_APP_SOURCE_ALL ${NATIVE_INTERFACE_SOURCE}) @@ -31,10 +38,6 @@ function (add_module_native arg) LIST (APPEND WASM_APP_LIB_SOURCE_ALL ${WASM_APP_LIB_CURRENT_SOURCE}) set (WASM_APP_LIB_SOURCE_ALL ${WASM_APP_LIB_SOURCE_ALL} PARENT_SCOPE) - - # VARIABLES in function are only used in this scope, - # set PARENT_SCOPE to pass to top CMakeLists - set (WASM_LIB_BASE_SOURCE ${WASM_LIB_BASE_SOURCE} PARENT_SCOPE) endfunction () function (add_module_app arg) @@ -83,4 +86,4 @@ else () endif () ENDFOREACH (dir) -endif() \ No newline at end of file +endif() diff --git a/core/app-framework/base/native/base_lib.inl b/core/app-framework/base/native/base_lib.inl index 84df855f8..3c228cc93 100644 --- a/core/app-framework/base/native/base_lib.inl +++ b/core/app-framework/base/native/base_lib.inl @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ - EXPORT_WASM_API(wasm_register_resource), - EXPORT_WASM_API(wasm_response_send), - EXPORT_WASM_API(wasm_post_request), - EXPORT_WASM_API(wasm_sub_event), - EXPORT_WASM_API(wasm_create_timer), - EXPORT_WASM_API(wasm_timer_destroy), - EXPORT_WASM_API(wasm_timer_cancel), - EXPORT_WASM_API(wasm_timer_restart), - EXPORT_WASM_API(wasm_get_sys_tick_ms), + EXPORT_WASM_API_WITH_SIG(wasm_register_resource, "($)"), + EXPORT_WASM_API_WITH_SIG(wasm_response_send, "(*~)i"), + EXPORT_WASM_API_WITH_SIG(wasm_post_request, "(*~)"), + EXPORT_WASM_API_WITH_SIG(wasm_sub_event, "($)"), + EXPORT_WASM_API_WITH_SIG(wasm_create_timer, "(iii)i"), + EXPORT_WASM_API_WITH_SIG(wasm_timer_destroy, "(i)"), + EXPORT_WASM_API_WITH_SIG(wasm_timer_cancel, "(i)"), + EXPORT_WASM_API_WITH_SIG(wasm_timer_restart, "(ii)"), + EXPORT_WASM_API_WITH_SIG(wasm_get_sys_tick_ms, "()i"), diff --git a/core/app-framework/base/native/req_resp_native_api.h b/core/app-framework/base/native/req_resp_native_api.h index 7e8592b89..6fe0581b6 100644 --- a/core/app-framework/base/native/req_resp_native_api.h +++ b/core/app-framework/base/native/req_resp_native_api.h @@ -14,19 +14,13 @@ extern "C" { #endif bool -wasm_response_send(wasm_exec_env_t exec_env, - int32 buffer_offset, int size); +wasm_response_send(wasm_exec_env_t exec_env, char *buffer, int size); void -wasm_register_resource(wasm_exec_env_t exec_env, - int32 url_offset); +wasm_register_resource(wasm_exec_env_t exec_env, char *url); void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size); +wasm_post_request(wasm_exec_env_t exec_env, char *buffer, int size); void -wasm_sub_event(wasm_exec_env_t exec_env, - int32 url_offset); - - +wasm_sub_event(wasm_exec_env_t exec_env, char *url); #ifdef __cplusplus } diff --git a/core/app-framework/base/native/request_response.c b/core/app-framework/base/native/request_response.c index 2d725d35d..4088c3885 100644 --- a/core/app-framework/base/native/request_response.c +++ b/core/app-framework/base/native/request_response.c @@ -11,17 +11,8 @@ extern void module_request_handler(request_t *request, void *user_data); bool -wasm_response_send(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) +wasm_response_send(wasm_exec_env_t exec_env, char *buffer, int size) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, size)) - return false; - - buffer = addr_app_to_native(buffer_offset); - if (buffer != NULL) { response_t response[1]; @@ -37,15 +28,9 @@ wasm_response_send(wasm_exec_env_t exec_env, } void -wasm_register_resource(wasm_exec_env_t exec_env, int32 url_offset) +wasm_register_resource(wasm_exec_env_t exec_env, char *url) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *url = NULL; - - if (!validate_app_str_addr(url_offset)) - return; - - url = addr_app_to_native(url_offset); if (url != NULL) { unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, @@ -56,16 +41,9 @@ wasm_register_resource(wasm_exec_env_t exec_env, int32 url_offset) } void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) +wasm_post_request(wasm_exec_env_t exec_env, char *buffer, int size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, size)) - return; - - buffer = addr_app_to_native(buffer_offset); if (buffer != NULL) { request_t req[1]; @@ -91,15 +69,9 @@ wasm_post_request(wasm_exec_env_t exec_env, } void -wasm_sub_event(wasm_exec_env_t exec_env, int32 url_offset) +wasm_sub_event(wasm_exec_env_t exec_env, char *url) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *url = NULL; - - if (!validate_app_str_addr(url_offset)) - return; - - url = addr_app_to_native(url_offset); if (url != NULL) { unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, diff --git a/core/app-framework/connection/native/connection.inl b/core/app-framework/connection/native/connection.inl index 2956e2696..b2d01aa9f 100644 --- a/core/app-framework/connection/native/connection.inl +++ b/core/app-framework/connection/native/connection.inl @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -EXPORT_WASM_API(wasm_open_connection), -EXPORT_WASM_API(wasm_close_connection), -EXPORT_WASM_API(wasm_send_on_connection), -EXPORT_WASM_API(wasm_config_connection), +EXPORT_WASM_API_WITH_SIG(wasm_open_connection, "($*~)i"), +EXPORT_WASM_API_WITH_SIG(wasm_close_connection, "(i)"), +EXPORT_WASM_API_WITH_SIG(wasm_send_on_connection, "(i*~)i"), +EXPORT_WASM_API_WITH_SIG(wasm_config_connection, "(i*~)i"), diff --git a/core/app-framework/connection/native/connection_native_api.h b/core/app-framework/connection/native/connection_native_api.h index 3b4fcaa54..79e911d19 100644 --- a/core/app-framework/connection/native/connection_native_api.h +++ b/core/app-framework/connection/native/connection_native_api.h @@ -20,16 +20,16 @@ extern "C" { uint32 wasm_open_connection(wasm_exec_env_t exec_env, - int32 name_offset, int32 args_offset, uint32 len); + char *name, char *args_buf, uint32 len); void wasm_close_connection(wasm_exec_env_t exec_env, uint32 handle); int wasm_send_on_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 data_offset, uint32 len); + uint32 handle, char *data, uint32 len); bool wasm_config_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 cfg_offset, uint32 len); + uint32 handle, char *cfg_buf, uint32 len); diff --git a/core/app-framework/connection/native/connection_wrapper.c b/core/app-framework/connection/native/connection_wrapper.c index b60a24988..742a45b28 100644 --- a/core/app-framework/connection/native/connection_wrapper.c +++ b/core/app-framework/connection/native/connection_wrapper.c @@ -16,17 +16,10 @@ uint32 wasm_open_connection(wasm_exec_env_t exec_env, - int32 name_offset, int32 args_offset, uint32 len) + char *name, char *args_buf, uint32 len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); attr_container_t *args; - char *name, *args_buf; - - if (!validate_app_str_addr(name_offset) || - !validate_app_addr(args_offset, len) || - !(name = addr_app_to_native(name_offset)) || - !(args_buf = addr_app_to_native(args_offset))) - return -1; args = (attr_container_t *)args_buf; @@ -45,15 +38,8 @@ wasm_close_connection(wasm_exec_env_t exec_env, uint32 handle) int wasm_send_on_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 data_offset, uint32 len) + uint32 handle, char *data, uint32 len) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *data; - - if (!validate_app_addr(data_offset, len) || - !(data = addr_app_to_native(data_offset))) - return -1; - if (connection_impl._send != NULL) return connection_impl._send(handle, data, len); @@ -62,16 +48,10 @@ wasm_send_on_connection(wasm_exec_env_t exec_env, bool wasm_config_connection(wasm_exec_env_t exec_env, - uint32 handle, int32 cfg_offset, uint32 len) + uint32 handle, char *cfg_buf, uint32 len) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *cfg_buf; attr_container_t *cfg; - if (!validate_app_addr(cfg_offset, len) || - !(cfg_buf = addr_app_to_native(cfg_offset))) - return false; - cfg = (attr_container_t *)cfg_buf; if (connection_impl._config != NULL) diff --git a/core/app-framework/connection/native/wasm_lib.cmake b/core/app-framework/connection/native/wasm_lib.cmake index d78763f74..58db0c1d8 100644 --- a/core/app-framework/connection/native/wasm_lib.cmake +++ b/core/app-framework/connection/native/wasm_lib.cmake @@ -5,6 +5,9 @@ set (WASM_LIB_CONN_DIR ${CMAKE_CURRENT_LIST_DIR}) include_directories(${WASM_LIB_CONN_DIR}) +add_definitions (-DAPP_FRAMEWORK_CONNECTION) + + include (${CMAKE_CURRENT_LIST_DIR}/${WAMR_BUILD_PLATFORM}/connection_mgr.cmake) file (GLOB source_all diff --git a/core/app-framework/sensor/native/runtime_sensor.c b/core/app-framework/sensor/native/runtime_sensor.c index 5a10434cb..63a03983d 100644 --- a/core/app-framework/sensor/native/runtime_sensor.c +++ b/core/app-framework/sensor/native/runtime_sensor.c @@ -135,15 +135,9 @@ wasm_sensor_config(wasm_exec_env_t exec_env, uint32 wasm_sensor_open(wasm_exec_env_t exec_env, - int32 name_offset, int instance) + char *name, int instance) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *name = NULL; - - if (!validate_app_str_addr(name_offset)) - return -1; - - name = addr_app_to_native(name_offset); if (name != NULL) { sensor_client_t *c; @@ -192,17 +186,8 @@ wasm_sensor_open(wasm_exec_env_t exec_env, bool wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, - uint32 sensor, int32 buffer_offset, - int len) + uint32 sensor, char *buffer, int len) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - if (!validate_app_addr(buffer_offset, len)) - return false; - - buffer = addr_app_to_native(buffer_offset); - if (buffer != NULL) { attr_container_t *cfg = (attr_container_t *)buffer; sensor_obj_t s = find_sys_sensor_id(sensor); diff --git a/core/app-framework/sensor/native/runtime_sensor.inl b/core/app-framework/sensor/native/runtime_sensor.inl index 1b7af765b..a7b9f4778 100644 --- a/core/app-framework/sensor/native/runtime_sensor.inl +++ b/core/app-framework/sensor/native/runtime_sensor.inl @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -EXPORT_WASM_API(wasm_sensor_open), -EXPORT_WASM_API(wasm_sensor_config), -EXPORT_WASM_API(wasm_sensor_config_with_attr_container), -EXPORT_WASM_API(wasm_sensor_close), +EXPORT_WASM_API_WITH_SIG(wasm_sensor_open, "($i)i"), +EXPORT_WASM_API_WITH_SIG(wasm_sensor_config, "(iiii)i"), +EXPORT_WASM_API_WITH_SIG(wasm_sensor_config_with_attr_container, "(i*~)i"), +EXPORT_WASM_API_WITH_SIG(wasm_sensor_close, "(i)i"), diff --git a/core/app-framework/sensor/native/sensor_native_api.h b/core/app-framework/sensor/native/sensor_native_api.h index 5cc8ad836..ed323174e 100644 --- a/core/app-framework/sensor/native/sensor_native_api.h +++ b/core/app-framework/sensor/native/sensor_native_api.h @@ -19,12 +19,11 @@ wasm_sensor_config(wasm_exec_env_t exec_env, int bit_cfg, int delay); uint32 wasm_sensor_open(wasm_exec_env_t exec_env, - int32 name_offset, int instance); + char *name, int instance); bool wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, - uint32 sensor, int32 buffer_offset, - int len); + uint32 sensor, char *buffer, int len); bool wasm_sensor_close(wasm_exec_env_t exec_env, uint32 sensor); diff --git a/core/app-framework/sensor/native/wasm_lib.cmake b/core/app-framework/sensor/native/wasm_lib.cmake index e233334c1..65a83ba59 100644 --- a/core/app-framework/sensor/native/wasm_lib.cmake +++ b/core/app-framework/sensor/native/wasm_lib.cmake @@ -3,6 +3,8 @@ set (WASM_LIB_SENSOR_DIR ${CMAKE_CURRENT_LIST_DIR}) +add_definitions (-DAPP_FRAMEWORK_SENSOR) + include_directories(${WASM_LIB_SENSOR_DIR}) diff --git a/core/app-framework/wgl/native/gui_native_api.h b/core/app-framework/wgl/native/gui_native_api.h index 57d987104..f61d9b51a 100644 --- a/core/app-framework/wgl/native/gui_native_api.h +++ b/core/app-framework/wgl/native/gui_native_api.h @@ -20,23 +20,23 @@ extern "C" { void wasm_obj_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); + int32 func_id, uint32 *argv, uint32 argc); void wasm_btn_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); + int32 func_id, uint32 *argv, uint32 argc); void wasm_label_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); + int32 func_id, uint32 *argv, uint32 argc); void wasm_cb_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); + int32 func_id, uint32 *argv, uint32 argc); void wasm_list_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc); + int32 func_id, uint32 *argv, uint32 argc); #ifdef __cplusplus } diff --git a/core/app-framework/wgl/native/wamr_gui.inl b/core/app-framework/wgl/native/wamr_gui.inl index 908a19155..c7855b17b 100644 --- a/core/app-framework/wgl/native/wamr_gui.inl +++ b/core/app-framework/wgl/native/wamr_gui.inl @@ -4,25 +4,25 @@ */ /* button */ -EXPORT_WASM_API(wasm_btn_native_call), +EXPORT_WASM_API_WITH_SIG(wasm_btn_native_call, "(i*i)"), /* obj */ -EXPORT_WASM_API(wasm_obj_native_call), +EXPORT_WASM_API_WITH_SIG(wasm_obj_native_call, "(i*i)"), /* label */ -EXPORT_WASM_API(wasm_label_native_call), +EXPORT_WASM_API_WITH_SIG(wasm_label_native_call, "(i*i)"), /* cont */ -//EXPORT_WASM_API(wasm_cont_native_call), +//EXPORT_WASM_API_WITH_SIG(wasm_cont_native_call, "(i*i)"), /* page */ -//EXPORT_WASM_API(wasm_page_native_call), +//EXPORT_WASM_API_WITH_SIG(wasm_page_native_call, "(i*i)"), /* list */ -EXPORT_WASM_API(wasm_list_native_call), +EXPORT_WASM_API_WITH_SIG(wasm_list_native_call, "(i*i)"), /* drop down list */ -//EXPORT_WASM_API(wasm_ddlist_native_call), +//EXPORT_WASM_API_WITH_SIG(wasm_ddlist_native_call, "(i*i)"), /* check box */ -EXPORT_WASM_API(wasm_cb_native_call), +EXPORT_WASM_API_WITH_SIG(wasm_cb_native_call, "(i*i)"), diff --git a/core/app-framework/wgl/native/wasm_lib.cmake b/core/app-framework/wgl/native/wasm_lib.cmake index d2ccf3c25..b452fb114 100644 --- a/core/app-framework/wgl/native/wasm_lib.cmake +++ b/core/app-framework/wgl/native/wasm_lib.cmake @@ -6,6 +6,7 @@ set (WASM_LIB_GUI_DIR ${CMAKE_CURRENT_LIST_DIR}) set (DEPS_DIR ${WASM_LIB_GUI_DIR}/../../../deps) add_definitions(-DLV_CONF_INCLUDE_SIMPLE) +add_definitions (-DAPP_FRAMEWORK_WGL) include_directories(${WASM_LIB_GUI_DIR} ${DEPS_DIR} diff --git a/core/app-framework/wgl/native/wgl_btn_wrapper.c b/core/app-framework/wgl/native/wgl_btn_wrapper.c index 7a08062e9..4fd35e39f 100644 --- a/core/app-framework/wgl/native/wgl_btn_wrapper.c +++ b/core/app-framework/wgl/native/wgl_btn_wrapper.c @@ -126,7 +126,7 @@ static WGLNativeFuncDef btn_native_func_defs[] = { /*************** Native Interface to Wasm App ***********/ void wasm_btn_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) + int32 func_id, uint32 *argv, uint32 argc) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint32 size = sizeof(btn_native_func_defs) / sizeof(WGLNativeFuncDef); @@ -135,6 +135,6 @@ wasm_btn_native_call(wasm_exec_env_t exec_env, btn_native_func_defs, size, func_id, - argv_offset, + argv, argc); } diff --git a/core/app-framework/wgl/native/wgl_cb_wrapper.c b/core/app-framework/wgl/native/wgl_cb_wrapper.c index d0609b402..77ea8e9ee 100644 --- a/core/app-framework/wgl/native/wgl_cb_wrapper.c +++ b/core/app-framework/wgl/native/wgl_cb_wrapper.c @@ -73,7 +73,7 @@ static WGLNativeFuncDef cb_native_func_defs[] = { /*************** Native Interface to Wasm App ***********/ void wasm_cb_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) + int32 func_id, uint32 *argv, uint32 argc) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint32 size = sizeof(cb_native_func_defs) / sizeof(WGLNativeFuncDef); @@ -82,6 +82,6 @@ wasm_cb_native_call(wasm_exec_env_t exec_env, cb_native_func_defs, size, func_id, - argv_offset, + argv, argc); } diff --git a/core/app-framework/wgl/native/wgl_label_wrapper.c b/core/app-framework/wgl/native/wgl_label_wrapper.c index 2aaa38594..8106c73d4 100644 --- a/core/app-framework/wgl/native/wgl_label_wrapper.c +++ b/core/app-framework/wgl/native/wgl_label_wrapper.c @@ -64,7 +64,7 @@ static WGLNativeFuncDef label_native_func_defs[] = { /*************** Native Interface to Wasm App ***********/ void wasm_label_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) + int32 func_id, uint32 *argv, uint32 argc) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint32 size = sizeof(label_native_func_defs) / sizeof(WGLNativeFuncDef); @@ -73,6 +73,6 @@ wasm_label_native_call(wasm_exec_env_t exec_env, label_native_func_defs, size, func_id, - argv_offset, + argv, argc); } diff --git a/core/app-framework/wgl/native/wgl_list_wrapper.c b/core/app-framework/wgl/native/wgl_list_wrapper.c index 2db29481b..98430b750 100644 --- a/core/app-framework/wgl/native/wgl_list_wrapper.c +++ b/core/app-framework/wgl/native/wgl_list_wrapper.c @@ -49,7 +49,7 @@ static WGLNativeFuncDef list_native_func_defs[] = { /*************** Native Interface to Wasm App ***********/ void wasm_list_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) + int32 func_id, uint32 *argv, uint32 argc) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint32 size = sizeof(list_native_func_defs) / sizeof(WGLNativeFuncDef); @@ -58,6 +58,6 @@ wasm_list_native_call(wasm_exec_env_t exec_env, list_native_func_defs, size, func_id, - argv_offset, + argv, argc); } diff --git a/core/app-framework/wgl/native/wgl_native_utils.c b/core/app-framework/wgl/native/wgl_native_utils.c index 5fdaffdd7..353329802 100644 --- a/core/app-framework/wgl/native/wgl_native_utils.c +++ b/core/app-framework/wgl/native/wgl_native_utils.c @@ -109,17 +109,11 @@ void wgl_native_func_call(wasm_module_inst_t module_inst, WGLNativeFuncDef *funcs, uint32 size, int32 func_id, - uint32 argv_offset, + uint32 *argv, uint32 argc) { WGLNativeFuncDef *func_def = funcs; WGLNativeFuncDef *func_def_end = func_def + size; - uint32 *argv; - - if (!validate_app_addr(argv_offset, argc * sizeof(uint32))) - return; - - argv = addr_app_to_native(argv_offset); while (func_def < func_def_end) { if (func_def->func_id == func_id) { diff --git a/core/app-framework/wgl/native/wgl_native_utils.h b/core/app-framework/wgl/native/wgl_native_utils.h index cc8b2d215..fdb5e505a 100644 --- a/core/app-framework/wgl/native/wgl_native_utils.h +++ b/core/app-framework/wgl/native/wgl_native_utils.h @@ -74,7 +74,7 @@ void wgl_native_func_call(wasm_module_inst_t module_inst, WGLNativeFuncDef *funcs, uint32 size, int32 func_id, - uint32 argv_offset, + uint32 *argv, uint32 argc); #ifdef __cplusplus diff --git a/core/app-framework/wgl/native/wgl_obj_wrapper.c b/core/app-framework/wgl/native/wgl_obj_wrapper.c index d8116571f..9f2940ff9 100644 --- a/core/app-framework/wgl/native/wgl_obj_wrapper.c +++ b/core/app-framework/wgl/native/wgl_obj_wrapper.c @@ -378,7 +378,7 @@ static WGLNativeFuncDef obj_native_func_defs[] = { /*************** Native Interface to Wasm App ***********/ void wasm_obj_native_call(wasm_exec_env_t exec_env, - int32 func_id, uint32 argv_offset, uint32 argc) + int32 func_id, uint32 *argv, uint32 argc) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint32 size = sizeof(obj_native_func_defs) / sizeof(WGLNativeFuncDef); @@ -387,6 +387,6 @@ wasm_obj_native_call(wasm_exec_env_t exec_env, obj_native_func_defs, size, func_id, - argv_offset, + argv, argc); } diff --git a/core/app-mgr/README.md b/core/app-mgr/README.md new file mode 100644 index 000000000..d5e058815 --- /dev/null +++ b/core/app-mgr/README.md @@ -0,0 +1,10 @@ +WASM application management +======= + +## structure + + + + + + diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index 8915103ab..211ba61f9 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -735,42 +735,13 @@ fail: } static void -destroy_import_funcs(AOTImportFunc *import_funcs, bool is_jit_mode) +destroy_import_funcs(AOTImportFunc *import_funcs, + bool is_jit_mode) { if (!is_jit_mode) wasm_free(import_funcs); } -static void* -resolve_sym(const char *module_name, const char *field_name) -{ - void *sym; - -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if ((sym = wasm_native_lookup_libc_builtin_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_LIBC_WASI != 0 - if ((sym = wasm_native_lookup_libc_wasi_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_BASE_LIB != 0 - if ((sym = wasm_native_lookup_base_lib_func(module_name, - field_name))) - return sym; -#endif - - if ((sym = wasm_native_lookup_extension_lib_func(module_name, - field_name))) - return sym; - - return NULL; -} - static bool load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, @@ -804,13 +775,16 @@ load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, "invalid function type index."); return false; } + import_funcs[i].func_type = module->func_types[import_funcs[i].func_type_index]; read_string(buf, buf_end, import_funcs[i].module_name); read_string(buf, buf_end, import_funcs[i].func_name); module_name = import_funcs[i].module_name; field_name = import_funcs[i].func_name; if (!(import_funcs[i].func_ptr_linked = - resolve_sym(module_name, field_name))) { + wasm_native_resolve_symbol(module_name, field_name, + import_funcs[i].func_type, + &import_funcs[i].signature))) { LOG_WARNING("warning: fail to link import function (%s, %s)\n", module_name, field_name); } @@ -2053,46 +2027,61 @@ aot_unload(AOTModule *module) #if WASM_ENABLE_JIT != 0 if (module->comp_data) aot_destroy_comp_data(module->comp_data); + if (module->comp_ctx) aot_destroy_comp_context(module->comp_ctx); + if (module->wasm_module) wasm_loader_unload(module->wasm_module); #endif + if (module->mem_init_data_list) destroy_mem_init_data_list(module->mem_init_data_list, module->mem_init_data_count, module->is_jit_mode); + if (module->table_init_data_list) destroy_table_init_data_list(module->table_init_data_list, module->table_init_data_count, module->is_jit_mode); + if (module->func_types) destroy_func_types(module->func_types, module->func_type_count, module->is_jit_mode); + if (module->import_globals) destroy_import_globals(module->import_globals, module->is_jit_mode); + if (module->globals) destroy_globals(module->globals, module->is_jit_mode); + if (module->import_funcs) destroy_import_funcs(module->import_funcs, module->is_jit_mode); + if (module->export_funcs) destroy_export_funcs(module->export_funcs, module->is_jit_mode); + if (module->func_type_indexes) wasm_free(module->func_type_indexes); + if (module->func_ptrs) wasm_free(module->func_ptrs); + if (module->const_str_set) bh_hash_map_destroy(module->const_str_set); + if (module->code) bh_munmap(module->code, module->code_size); + if (module->data_sections) destroy_object_data_sections(module->data_sections, module->data_section_count); + wasm_free(module); } diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index 9102b89ec..02931dd29 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -16,6 +16,7 @@ typedef struct { REG_SYM(aot_set_exception_with_id), \ REG_SYM(aot_get_exception), \ REG_SYM(aot_is_wasm_type_equal), \ + REG_SYM(aot_invoke_native), \ REG_SYM(wasm_runtime_enlarge_memory), \ REG_SYM(wasm_runtime_set_exception), \ REG_SYM(fmin), \ diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 9deacf0c9..44c7d48fd 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -28,7 +28,7 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, /* Initialize import global data */ for (i = 0; i < module->import_global_count; i++, import_global++) { bh_assert(import_global->data_offset == - p - (uint8*)module_inst->global_data.ptr); + (uint32)(p - (uint8*)module_inst->global_data.ptr)); memcpy(p, &import_global->global_data_linked, import_global->size); p += import_global->size; } @@ -36,7 +36,7 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, /* Initialize defined global data */ for (i = 0; i < module->global_count; i++, global++) { bh_assert(global->data_offset == - p - (uint8*)module_inst->global_data.ptr); + (uint32)(p - (uint8*)module_inst->global_data.ptr)); init_expr = &global->init_expr; switch (init_expr->init_expr_type) { case INIT_EXPR_TYPE_GET_GLOBAL: @@ -53,7 +53,8 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, p += global->size; } - bh_assert(module_inst->global_data_size == p - (uint8*)module_inst->global_data.ptr); + bh_assert(module_inst->global_data_size == + (uint32)(p - (uint8*)module_inst->global_data.ptr)); return true; } @@ -420,65 +421,6 @@ aot_deinstantiate(AOTModuleInstance *module_inst) wasm_free(module_inst); } -static bool -check_type(uint8 type, const char *p) -{ - const char *str = "i32"; - - if (strlen(p) < 3) - return false; - - switch (type) { - case VALUE_TYPE_I32: - str = "i32"; - break; - case VALUE_TYPE_I64: - str = "i64"; - break; - case VALUE_TYPE_F32: - str = "f32"; - break; - case VALUE_TYPE_F64: - str = "f64"; - break; - } - if (strncmp(p, str, 3)) - return false; - - return true; -} - -static bool -check_function_type(const WASMType *type, - const char *signature) -{ - uint32 i; - const char *p = signature; - - if (!p || *p++ != '(') - return false; - - for (i = 0; i < type->param_count; i++) { - if (!check_type(type->types[i], p)) - return false; - p += 3; - } - - if (*p++ != ')') - return false; - - if (type->result_count) { - if (!check_type(type->types[type->param_count], p)) - return false; - p += 3; - } - - if (*p != '\0') - return false; - - return true; -} - AOTFunctionInstance* aot_lookup_function(const AOTModuleInstance *module_inst, const char *name, const char *signature) @@ -486,13 +428,10 @@ aot_lookup_function(const AOTModuleInstance *module_inst, uint32 i; AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; - for (i = 0; i < module->export_func_count; i++) { - if (!strcmp(module->export_funcs[i].func_name, name) - && check_function_type(module->export_funcs[i].func_type, - signature)) + for (i = 0; i < module->export_func_count; i++) + if (!strcmp(module->export_funcs[i].func_name, name)) return &module->export_funcs[i]; - } - + (void)signature; return NULL; } @@ -517,8 +456,8 @@ aot_call_function(WASMExecEnv *exec_env, { AOTModuleInstance *module_inst = (AOTModuleInstance*)exec_env->module_inst; AOTFuncType *func_type = function->func_type; - bool ret = wasm_runtime_invoke_native(function->func_ptr, func_type, - exec_env, argv, argc, argv); + bool ret = wasm_runtime_invoke_native(exec_env, function->func_ptr, + func_type, NULL, argv, argc, argv); return ret && !aot_get_exception(module_inst) ? true : false; } @@ -612,11 +551,14 @@ aot_clear_exception(AOTModuleInstance *module_inst) } int32 -aot_module_malloc(AOTModuleInstance *module_inst, uint32 size) +aot_module_malloc(AOTModuleInstance *module_inst, uint32 size, + void **p_native_addr) { uint8 *addr = mem_allocator_malloc(module_inst->heap_handle.ptr, size); + if (p_native_addr) + *p_native_addr = addr; if (!addr) { aot_set_exception(module_inst, "out of memory"); return 0; @@ -641,10 +583,11 @@ int32 aot_module_dup_data(AOTModuleInstance *module_inst, const char *src, uint32 size) { - int32 buffer_offset = aot_module_malloc(module_inst, size); + char *buffer; + int32 buffer_offset = aot_module_malloc(module_inst, size, + (void**)&buffer); if (buffer_offset != 0) { - char *buffer; buffer = aot_addr_app_to_native(module_inst, buffer_offset); memcpy(buffer, src, size); } @@ -858,3 +801,35 @@ aot_is_wasm_type_equal(AOTModuleInstance *module_inst, return wasm_type_equal(type1, type2); } + +void +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + uint32 *frame_lp, uint32 argc, uint32 *argv_ret) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance*) + wasm_runtime_get_module_inst(exec_env); + AOTModule *aot_module = (AOTModule*)module_inst->aot_module.ptr; + uint32 *func_type_indexes = (uint32*)module_inst->func_type_indexes.ptr; + uint32 func_type_idx = func_type_indexes[func_idx]; + AOTFuncType *func_type = aot_module->func_types[func_type_idx]; + void **func_ptrs = (void**)module_inst->func_ptrs.ptr; + void *func_ptr = func_ptrs[func_idx]; + AOTImportFunc *import_func; + const char *signature = NULL; + char buf[128]; + + if (func_idx < aot_module->import_func_count) { + import_func = aot_module->import_funcs + func_idx; + if (!func_ptr) { + snprintf(buf, sizeof(buf), + "fail to call unlinked import function (%s, %s)", + import_func->module_name, import_func->func_name); + aot_set_exception(module_inst, buf); + return; + } + signature = import_func->signature; + } + wasm_runtime_invoke_native(exec_env, func_ptr, + func_type, signature, frame_lp, argc, argv_ret); +} + diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 5a941321a..977d3ec96 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -141,7 +141,7 @@ typedef struct AOTModule { /* is jit mode or not */ bool is_jit_mode; -#if WASM_ENABLE_JIT +#if WASM_ENABLE_JIT != 0 WASMModule *wasm_module; AOTCompContext *comp_ctx; AOTCompData *comp_data; @@ -379,7 +379,8 @@ void aot_clear_exception(AOTModuleInstance *module_inst); int32 -aot_module_malloc(AOTModuleInstance *module_inst, uint32 size); +aot_module_malloc(AOTModuleInstance *module_inst, uint32 size, + void **p_native_addr); void aot_module_free(AOTModuleInstance *module_inst, int32 ptr); @@ -431,6 +432,13 @@ bool aot_is_wasm_type_equal(AOTModuleInstance *module_inst, uint32 type1_idx, uint32 type2_idx); +/** + * Invoke native function from aot code + */ +void +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + uint32 *frame_lp, uint32 argc, uint32 *argv_ret); + uint32 aot_get_plt_table_size(); diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c index 6e7054e26..92ec42c96 100644 --- a/core/iwasm/common/wasm_exec_env.c +++ b/core/iwasm/common/wasm_exec_env.c @@ -20,6 +20,14 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, return NULL; memset(exec_env, 0, (uint32)total_size); + +#if WASM_ENABLE_AOT != 0 + if (!(exec_env->argv_buf = wasm_malloc(sizeof(uint32) * 64))) { + wasm_free(exec_env); + return NULL; + } +#endif + exec_env->module_inst = module_inst; exec_env->wasm_stack_size = stack_size; exec_env->wasm_stack.s.top_boundary = @@ -31,6 +39,9 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, void wasm_exec_env_destroy(WASMExecEnv *exec_env) { +#if WASM_ENABLE_AOT != 0 + wasm_free(exec_env->argv_buf); +#endif wasm_free(exec_env); } diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index d2d49b869..d25469669 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -30,6 +30,10 @@ typedef struct WASMExecEnv { /* The WASM module instance of current thread */ struct WASMModuleInstanceCommon *module_inst; +#if WASM_ENABLE_AOT != 0 + uint32 *argv_buf; +#endif + /* Current interpreter frame of current thread */ struct WASMInterpFrame *cur_frame; diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index d7244391c..29b762da8 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -4,44 +4,124 @@ */ #include "wasm_native.h" +#include "wasm_runtime_common.h" +#include "bh_log.h" -typedef struct NativeSymbol { - const char *symbol; - void *func_ptr; -} NativeSymbol; +static NativeSymbolsList g_native_symbols_list = NULL; +static NativeSymbolsList g_native_symbols_list_end = NULL; + +uint32 +get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis); + +uint32 +get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis); + +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis); + +uint32 +get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); + +uint32 +get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis); static bool -sort_symbol_ptr(NativeSymbol *ptr, int len) +check_symbol_signature(const WASMType *type, const char *signature) { - int i, j; - NativeSymbol temp; + const char *p = signature, *p_end; + char sig_map[] = { 'F', 'f', 'I', 'i' }, sig; + uint32 i = 0; - for (i = 0; i < len - 1; ++i) { - for (j = i + 1; j < len; ++j) { - if (strcmp((ptr+i)->symbol, (ptr+j)->symbol) > 0) { - temp = ptr[i]; - ptr[i] = ptr[j]; - ptr[j] = temp; + if (!p || strlen(p) < 2) + return false; + + p_end = p + strlen(signature); + + if (*p++ != '(') + return false; + + if ((uint32)(p_end - p) < type->param_count + 1) + /* signatures of parameters, and ')' */ + return false; + + for (i = 0; i < type->param_count; i++) { + sig = *p++; + if (sig == sig_map[type->types[i] - VALUE_TYPE_F64]) + /* normal parameter */ + continue; + + if (type->types[i] != VALUE_TYPE_I32) + /* pointer and string must be i32 type */ + return false; + + if (sig == '*') { + /* it is a pointer */ + if (i + 1 < type->param_count + && type->types[i + 1] == VALUE_TYPE_I32 + && *p == '~') { + /* pointer length followed */ + i++; + p++; } } + else if (sig == '$') { + /* it is a string */ + } + else { + /* invalid signature */ + return false; + } } + if (*p++ != ')') + return false; + + if (type->result_count) { + if (p >= p_end) + return false; + if (*p++ != sig_map[type->types[i] - VALUE_TYPE_F64]) + return false; + } + + if (*p != '\0') + return false; + return true; } +static void +sort_symbol_ptr(NativeSymbol *native_symbols, uint32 n_native_symbols) +{ + uint32 i, j; + NativeSymbol temp; + + for (i = 0; i < n_native_symbols - 1; i++) { + for (j = i + 1; j < n_native_symbols; j++) { + if (strcmp(native_symbols[i].symbol, + native_symbols[j].symbol) > 0) { + temp = native_symbols[i]; + native_symbols[i] = native_symbols[j]; + native_symbols[j] = temp; + } + } + } +} + static void * -lookup_symbol(NativeSymbol *ptr, int len, const char *symbol) +lookup_symbol(NativeSymbol *native_symbols, uint32 n_native_symbols, + const char *symbol, const char **p_signature) { int low = 0, mid, ret; - int high = len - 1; + int high = n_native_symbols - 1; while (low <= high) { mid = (low + high) / 2; - ret = strcmp(symbol, ptr[mid].symbol); - - if (ret == 0) - return ptr[mid].func_ptr; + ret = strcmp(symbol, native_symbols[mid].symbol); + if (ret == 0) { + *p_signature = native_symbols[mid].signature; + return native_symbols[mid].func_ptr; + } else if (ret < 0) high = mid - 1; else @@ -51,75 +131,135 @@ lookup_symbol(NativeSymbol *ptr, int len, const char *symbol) return NULL; } +void* +wasm_native_resolve_symbol(const char *module_name, const char *field_name, + const WASMType *func_type, const char **p_signature) +{ + NativeSymbolsNode *node, *node_next; + const char *signature = NULL; + void *func_ptr = NULL; + + node = g_native_symbols_list; + while (node) { + node_next = node->next; + if (!strcmp(node->module_name, module_name)) { + if ((func_ptr = lookup_symbol(node->native_symbols, + node->n_native_symbols, + field_name, &signature)) + || (field_name[0] == '_' + && (func_ptr = lookup_symbol(node->native_symbols, + node->n_native_symbols, + field_name + 1, &signature)))) + break; + } + node = node_next; + } + + if (func_ptr) { + if (signature && signature[0] != '\0') { + /* signature is not empty, check its format */ + if (!check_symbol_signature(func_type, signature)) { + LOG_WARNING("failed to check signature '%s' and resolve " + "pointer params for import function (%s %s)\n", + signature, module_name, field_name); + return NULL; + } + else + /* Save signature for runtime to do pointer check and + address conversion */ + *p_signature = signature; + } + else + /* signature is empty */ + *p_signature = NULL; + } + + return func_ptr; +} + +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + NativeSymbolsNode *node; + + if (!(node = bh_malloc(sizeof(NativeSymbolsNode)))) + return false; + + node->module_name = module_name; + node->native_symbols = native_symbols; + node->n_native_symbols = n_native_symbols; + node->next = NULL; + + if (g_native_symbols_list_end) { + g_native_symbols_list_end->next = node; + g_native_symbols_list_end = node; + } + else { + g_native_symbols_list = g_native_symbols_list_end = node; + } + + sort_symbol_ptr(native_symbols, n_native_symbols); + return true; +} + +bool +wasm_native_init() +{ + NativeSymbol *native_symbols; + uint32 n_native_symbols; + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + n_native_symbols = get_libc_builtin_export_apis(&native_symbols); + if (!wasm_native_register_natives("env", + native_symbols, n_native_symbols)) + return false; + + n_native_symbols = get_spectest_export_apis(&native_symbols); + if (!wasm_native_register_natives("spectest", + native_symbols, n_native_symbols)) + return false; +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + n_native_symbols = get_libc_wasi_export_apis(&native_symbols); + if (!wasm_native_register_natives("wasi_unstable", + native_symbols, n_native_symbols)) + return false; +#endif + #if WASM_ENABLE_BASE_LIB != 0 -static bool is_base_lib_sorted = false; -static NativeSymbol *base_native_symbol_defs; -static int base_native_symbol_len; + n_native_symbols = get_base_lib_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", + native_symbols, n_native_symbols)) + return false; +#endif -int -get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); +#if WASM_ENABLE_APP_FRAMEWORK != 0 + n_native_symbols = get_ext_lib_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", + native_symbols, n_native_symbols)) + return false; +#endif -void * -wasm_native_lookup_base_lib_func(const char *module_name, - const char *func_name) -{ - void *ret; - - if (strcmp(module_name, "env")) - return NULL; - - if (!is_base_lib_sorted) { - base_native_symbol_len = get_base_lib_export_apis(&base_native_symbol_defs); - - if (base_native_symbol_len > 0) - sort_symbol_ptr(base_native_symbol_defs, base_native_symbol_len); - - is_base_lib_sorted = true; - } - - if ((ret = lookup_symbol(base_native_symbol_defs, base_native_symbol_len, - func_name)) - || (func_name[0] == '_' - && (ret = lookup_symbol(base_native_symbol_defs, base_native_symbol_len, - func_name + 1)))) - return ret; - - return NULL; -} -#endif /* end of WASM_ENABLE_BASE_LIB */ - -static bool is_ext_lib_sorted = false; -static NativeSymbol *ext_native_symbol_defs; -static int ext_native_symbol_len; - -int -get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis); - -void * -wasm_native_lookup_extension_lib_func(const char *module_name, - const char *func_name) -{ - void *ret; - - if (strcmp(module_name, "env")) - return NULL; - - if (!is_ext_lib_sorted) { - ext_native_symbol_len = get_ext_lib_export_apis(&ext_native_symbol_defs); - - if (ext_native_symbol_len > 0) - sort_symbol_ptr(ext_native_symbol_defs, ext_native_symbol_len); - - is_ext_lib_sorted = true; - } - - if ((ret = lookup_symbol(ext_native_symbol_defs, ext_native_symbol_len, - func_name)) - || (func_name[0] == '_' - && (ret = lookup_symbol(ext_native_symbol_defs, ext_native_symbol_len, - func_name + 1)))) - return ret; - - return NULL; + return true; +} + +void +wasm_native_destroy() +{ + NativeSymbolsNode *node, *node_next; + + node = g_native_symbols_list; + while (node) { + node_next = node->next; + bh_free(node); + node = node_next; + } + + g_native_symbols_list = g_native_symbols_list_end = NULL; } diff --git a/core/iwasm/common/wasm_native.h b/core/iwasm/common/wasm_native.h index a8fdc3f7a..dcacd5826 100644 --- a/core/iwasm/common/wasm_native.h +++ b/core/iwasm/common/wasm_native.h @@ -7,82 +7,66 @@ #define _WASM_NATIVE_H #include "bh_common.h" -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 #include "../interpreter/wasm.h" -#endif #ifdef __cplusplus extern "C" { #endif -/** - * Lookup native function implementation of a given import function - * in libc builtin API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ -void * -wasm_native_lookup_libc_builtin_func(const char *module_name, - const char *func_name); +typedef struct NativeSymbol { + const char *symbol; + void *func_ptr; + const char *signature; +} NativeSymbol; + +typedef struct NativeSymbolsNode { + struct NativeSymbolsNode *next; + const char *module_name; + NativeSymbol *native_symbols; + uint32 n_native_symbols; +} NativeSymbolsNode, *NativeSymbolsList; -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 /** * Lookup global variable of a given import global - * in libc builtin globals + * from libc builtin globals * * @param module_name the module name of the import global * @param global_name the global name of the import global * @param global return the global data * - * @param return true if success, false otherwise + * @param true if success, false otherwise */ bool wasm_native_lookup_libc_builtin_global(const char *module_name, const char *global_name, WASMGlobalImport *global); -#endif /** - * Lookup native function implementation of a given import function - * in libc wasi API's + * Resolve native symbol in all libraries, including libc-builtin, libc-wasi, + * base lib and extension lib, and user registered natives + * function, which can be auto checked by vm before calling native function * * @param module_name the module name of the import function * @param func_name the function name of the import function + * @param func_type the function prototype of the import function + * @param p_signature output the signature if resolve success * - * @return return the native function pointer if success, NULL otherwise + * @return the native function pointer if success, NULL otherwise */ -void * -wasm_native_lookup_libc_wasi_func(const char *module_name, - const char *func_name); +void* +wasm_native_resolve_symbol(const char *module_name, const char *field_name, + const WASMType *func_type, const char **p_signature); -/** - * Lookup native function implementation of a given import function - * in base lib API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ -void * -wasm_native_lookup_base_lib_func(const char *module_name, - const char *func_name); +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); -/** - * Lookup native function implementation of a given import function - * in extension lib API's - * - * @param module_name the module name of the import function - * @param func_name the function name of the import function - * - * @return return the native function pointer if success, NULL otherwise - */ -void * -wasm_native_lookup_extension_lib_func(const char *module_name, - const char *func_name); +bool +wasm_native_init(); + +void +wasm_native_destroy(); #ifdef __cplusplus } diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 4727fd511..8aaa97674 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -35,12 +35,18 @@ wasm_runtime_init() if (vm_thread_sys_init() != 0) return false; + if (wasm_native_init() == false) { + wasm_runtime_destroy(); + return false; + } + return true; } void wasm_runtime_destroy() { + wasm_native_destroy(); vm_thread_sys_destroy(); } @@ -342,15 +348,18 @@ wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst) } int32 -wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size) +wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size, + void **p_native_addr) { #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_module_malloc((WASMModuleInstance*)module_inst, size); + return wasm_module_malloc((WASMModuleInstance*)module_inst, size, + p_native_addr); #endif #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) - return aot_module_malloc((AOTModuleInstance*)module_inst, size); + return aot_module_malloc((AOTModuleInstance*)module_inst, size, + p_native_addr); #endif return 0; } @@ -691,20 +700,17 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, total_size = sizeof(size_t) * (uint64)argc; if (total_size >= UINT32_MAX || !(offset_argv_offsets = wasm_runtime_module_malloc - (module_inst, (uint32)total_size)) + (module_inst, (uint32)total_size, + (void**)&argv_offsets)) || argv_buf_len >= UINT32_MAX || !(offset_argv_buf = wasm_runtime_module_malloc - (module_inst, (uint32)argv_buf_len))) { + (module_inst, (uint32)argv_buf_len, + (void**)&argv_buf))) { set_error_buf(error_buf, error_buf_size, "Init wasi environment failed: allocate memory failed."); goto fail; } - argv_offsets = (size_t*) - wasm_runtime_addr_app_to_native(module_inst, offset_argv_offsets); - argv_buf = (char*) - wasm_runtime_addr_app_to_native(module_inst, offset_argv_buf); - for (i = 0; i < argc; i++) { argv_offsets[i] = argv_buf_offset; bh_strcpy_s(argv_buf + argv_buf_offset, @@ -718,20 +724,17 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, total_size = sizeof(size_t) * (uint64)argc; if (total_size >= UINT32_MAX || !(offset_env_offsets = wasm_runtime_module_malloc - (module_inst, (uint32)total_size)) + (module_inst, (uint32)total_size, + (void**)&env_offsets)) || env_buf_len >= UINT32_MAX || !(offset_env_buf = wasm_runtime_module_malloc - (module_inst, (uint32)env_buf_len))) { + (module_inst, (uint32)env_buf_len, + (void**)&env_buf))) { set_error_buf(error_buf, error_buf_size, "Init wasi environment failed: allocate memory failed."); goto fail; } - env_offsets = (size_t*) - wasm_runtime_addr_app_to_native(module_inst, offset_env_offsets); - env_buf = (char*) - wasm_runtime_addr_app_to_native(module_inst, offset_env_buf); - for (i = 0; i < env_count; i++) { env_offsets[i] = env_buf_offset; bh_strcpy_s(env_buf + env_buf_offset, @@ -740,23 +743,20 @@ wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, } if (!(offset_curfds = wasm_runtime_module_malloc - (module_inst, sizeof(struct fd_table))) + (module_inst, sizeof(struct fd_table), (void**)&curfds)) || !(offset_prestats = wasm_runtime_module_malloc - (module_inst, sizeof(struct fd_prestats))) + (module_inst, sizeof(struct fd_prestats), (void**)&prestats)) || !(offset_argv_environ = wasm_runtime_module_malloc - (module_inst, sizeof(struct argv_environ_values)))) { + (module_inst, sizeof(struct argv_environ_values), + (void**)&argv_environ))) { set_error_buf(error_buf, error_buf_size, "Init wasi environment failed: allocate memory failed."); goto fail; } - curfds = wasi_ctx->curfds = (struct fd_table*) - wasm_runtime_addr_app_to_native(module_inst, offset_curfds); - prestats = wasi_ctx->prestats = (struct fd_prestats*) - wasm_runtime_addr_app_to_native(module_inst, offset_prestats); - argv_environ = wasi_ctx->argv_environ = - (struct argv_environ_values*)wasm_runtime_addr_app_to_native - (module_inst, offset_argv_environ); + wasi_ctx->curfds = curfds; + wasi_ctx->prestats = prestats; + wasi_ctx->argv_environ = argv_environ; fd_table_init(curfds); fd_prestats_init(prestats); @@ -1064,13 +1064,14 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, if (total_size >= UINT32_MAX || !(argv_buf_offset = - wasm_runtime_module_malloc(module_inst, (uint32)total_size))) { + wasm_runtime_module_malloc(module_inst, (uint32)total_size, + (void**)&argv_buf))) { wasm_runtime_set_exception(module_inst, "allocate memory failed."); return false; } - argv_buf = p = wasm_runtime_addr_app_to_native(module_inst, argv_buf_offset); + p = argv_buf; argv_offsets = (int32*)(p + total_argv_size); p_end = p + total_size; @@ -1379,6 +1380,15 @@ fail: return false; } +bool +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return wasm_native_register_natives(module_name, + native_symbols, n_native_symbols); +} + /** * Implementation of wasm_runtime_invoke_native() */ @@ -1425,13 +1435,16 @@ static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; #define MAX_REG_FLOATS 16 bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + uint32 *argv, uint32 argc, uint32 *argv_ret) { + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); /* argv buf layout: int args(fix cnt) + float args(fix cnt) + stack args */ uint32 argv_buf[32], *argv1 = argv_buf, *fps, *ints, *stacks, size; uint32 *argv_src = argv, i, argc1, n_ints = 0, n_fps = 0, n_stacks = 0; + uint32 arg_i32, ptr_len; + bool ret = false; n_ints++; /* exec env */ @@ -1508,11 +1521,41 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, for (i = 0; i < func_type->param_count; i++) { switch (func_type->types[i]) { case VALUE_TYPE_I32: + { + arg_i32 = *argv_src++; + + if (signature) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr(module, arg_i32, ptr_len)) + goto fail; + + arg_i32 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr(module, arg_i32)) + goto fail; + + arg_i32 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + } + if (n_ints < MAX_REG_INTS) - ints[n_ints++] = *argv_src++; + ints[n_ints++] = arg_i32; else - stacks[n_stacks++] = *argv_src++; + stacks[n_stacks++] = arg_i32; break; + } case VALUE_TYPE_I64: if (n_ints < MAX_REG_INTS - 1) { /* 64-bit data must be 8 bytes aligned in arm */ @@ -1565,26 +1608,29 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, else { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); break; case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, n_stacks); + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); break; default: bh_assert(0); break; } } + + ret = true; + +fail: if (argv1 != argv_buf) wasm_free(argv1); - - return true; + return ret; } #endif /* end of defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) */ @@ -1609,12 +1655,15 @@ static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + uint32 *argv, uint32 argc, uint32 *argv_ret) { + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); uint32 argv_buf[32], *argv1 = argv_buf, argc1, i, j = 0; + uint32 arg_i32, ptr_len; uint64 size; + bool ret = false; #if defined(BUILD_TARGET_X86_32) argc1 = argc + 2; @@ -1637,20 +1686,49 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, for (i = 0; i < sizeof(WASMExecEnv*) / sizeof(uint32); i++) argv1[j++] = ((uint32*)&exec_env)[i]; -#if defined(BUILD_TARGET_X86_32) - word_copy(argv1 + j, argv, argc); - j += argc; -#else for (i = 0; i < func_type->param_count; i++) { switch (func_type->types[i]) { case VALUE_TYPE_I32: - argv1[j++] = *argv++; + { + arg_i32 = *argv++; + + if (signature) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr(module, arg_i32, ptr_len)) + goto fail; + + arg_i32 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr(module, arg_i32)) + goto fail; + + arg_i32 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + } + + argv1[j++] = arg_i32; break; + } case VALUE_TYPE_I64: case VALUE_TYPE_F64: - /* 64-bit data must be 8 bytes aligned in arm and mips */ +#if !defined(BUILD_TARGET_X86_32) + /* 64-bit data must be 8 bytes aligned in arm, thumb, mips + and xtensa */ if (j & 1) j++; +#endif argv1[j++] = *argv++; argv1[j++] = *argv++; break; @@ -1662,7 +1740,6 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, break; } } -#endif /* end of defined(BUILD_TARGET_X86_32) */ argc1 = j; if (func_type->result_count == 0) { @@ -1671,16 +1748,16 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, else { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, argc1); + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, argc1); break; case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, argc1)); + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, argc1)); break; case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, argc1); + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, argc1); break; case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, argc1)); + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, argc1)); break; default: bh_assert(0); @@ -1688,9 +1765,12 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, } } + ret = true; + +fail: if (argv1 != argv_buf) wasm_free(argv1); - return true; + return ret; } #endif /* end of defined(BUILD_TARGET_X86_32) \ @@ -1724,12 +1804,15 @@ static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; #endif bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, - uint32 *argv, uint32 argc, uint32 *ret) +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + uint32 *argv, uint32 argc, uint32 *argv_ret) { - uint64 argv_buf[32], *argv1 = argv_buf, *fps, *ints, *stacks, size; + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); + uint64 argv_buf[32], *argv1 = argv_buf, *fps, *ints, *stacks, size, arg_i64; uint32 *argv_src = argv, i, argc1, n_ints = 0, n_stacks = 0; + uint32 arg_i32, ptr_len; + bool ret = false; #if defined(_WIN32) || defined(_WIN32_) /* important difference in calling conventions */ #define n_fps n_ints @@ -1757,11 +1840,40 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, for (i = 0; i < func_type->param_count; i++) { switch (func_type->types[i]) { case VALUE_TYPE_I32: + { + arg_i32 = *argv_src++; + arg_i64 = arg_i32; + if (signature) { + if (signature[i + 1] == '*') { + /* param is a pointer */ + if (signature[i + 2] == '~') + /* pointer with length followed */ + ptr_len = *argv_src; + else + /* pointer without length followed */ + ptr_len = 1; + + if (!wasm_runtime_validate_app_addr(module, arg_i32, ptr_len)) + goto fail; + + arg_i64 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + else if (signature[i + 1] == '$') { + /* param is a string */ + if (!wasm_runtime_validate_app_str_addr(module, arg_i32)) + goto fail; + + arg_i64 = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + } if (n_ints < MAX_REG_INTS) - ints[n_ints++] = *argv_src++; + ints[n_ints++] = arg_i64; else - stacks[n_stacks++] = *argv_src++; + stacks[n_stacks++] = arg_i64; break; + } case VALUE_TYPE_I64: if (n_ints < MAX_REG_INTS) ints[n_ints++] = *(uint64*)argv_src; @@ -1794,16 +1906,16 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, else { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: - ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_I64: - PUT_I64_TO_ADDR(ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); break; case VALUE_TYPE_F32: - *(float32*)ret = invokeNative_Float32(func_ptr, argv1, n_stacks); + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_F64: - PUT_F64_TO_ADDR(ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); break; default: bh_assert(0); @@ -1811,10 +1923,12 @@ wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, } } + ret = true; +fail: if (argv1 != argv_buf) wasm_free(argv1); - return true; + return ret; } #endif /* end of defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) */ diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index 5e3fc2501..11fc5c797 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -10,6 +10,7 @@ #include "bh_common.h" #include "bh_thread.h" #include "wasm_exec_env.h" +#include "wasm_native.h" #include "../interpreter/wasm.h" #if WASM_ENABLE_LIBC_WASI != 0 #include "wasmtime_ssp.h" @@ -172,7 +173,8 @@ wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst); /* See wasm_export.h for description */ int32 -wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size); +wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size, + void **p_native_addr); /* See wasm_export.h for description */ void @@ -282,9 +284,15 @@ wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst); bool wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module, uint32 inc_page_count); +/* See wasm_export.h for description */ bool -wasm_runtime_invoke_native(void *func_ptr, WASMType *func_type, - WASMExecEnv *exec_env, +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, uint32 *argv, uint32 argc, uint32 *ret); diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index 5c2f9eb97..83b6bb981 100644 --- a/core/iwasm/compilation/aot.c +++ b/core/iwasm/compilation/aot.c @@ -265,6 +265,7 @@ aot_create_import_funcs(const WASMModule *module) import_funcs[i].func_name = import_func->field_name; import_funcs[i].func_ptr_linked = import_func->func_ptr_linked; import_funcs[i].func_type = import_func->func_type; + import_funcs[i].signature = import_func->signature; /* Resolve function type index */ for (j = 0; j < module->type_count; j++) if (import_func->func_type == module->types[j]) { diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index be33867de..afc099eb7 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -83,6 +83,8 @@ typedef struct AOTImportFunc { uint32 func_type_index; /* function pointer after linked */ void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; } AOTImportFunc; /** diff --git a/core/iwasm/compilation/aot_compiler.h b/core/iwasm/compilation/aot_compiler.h index fc447090c..c9959e0ad 100644 --- a/core/iwasm/compilation/aot_compiler.h +++ b/core/iwasm/compilation/aot_compiler.h @@ -208,6 +208,7 @@ typedef enum FloatArithmetic { #define F64_ZERO (comp_ctx->llvm_consts.f64_zero) #define I32_ONE (comp_ctx->llvm_consts.i32_one) #define I32_TWO (comp_ctx->llvm_consts.i32_two) +#define I32_THREE (comp_ctx->llvm_consts.i32_three) #define I32_FOUR (comp_ctx->llvm_consts.i32_four) #define I32_EIGHT (comp_ctx->llvm_consts.i32_eight) #define I32_NEG_ONE (comp_ctx->llvm_consts.i32_neg_one) diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index 3311806b7..21c7324e0 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -82,21 +82,149 @@ check_exception_thrown(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) return true; } +static bool +call_aot_invoke_native_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef func_idx, AOTFuncType *aot_func_type, + LLVMTypeRef *param_types, LLVMValueRef *param_values, + uint32 param_count, uint32 param_cell_num, + LLVMTypeRef ret_type, uint8 wasm_ret_type, + LLVMValueRef *p_value_ret) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[5]; + LLVMTypeRef ret_ptr_type, elem_ptr_type; + LLVMValueRef func, elem_idx, elem_ptr; + LLVMValueRef func_param_values[5], value_ret, value_ret_ptr, res; + char buf[32], *func_name = "aot_invoke_native"; + uint32 i, cell_num = 0; + + /* prepare function type of aot_invoke_native */ + func_param_types[0] = comp_ctx->exec_env_type; /* exec_env */ + func_param_types[1] = I32_TYPE; /* func_idx */ + func_param_types[2] = INT32_PTR_TYPE; /* frame_lp */ + func_param_types[3] = I32_TYPE; /* argc */ + func_param_types[4] = INT32_PTR_TYPE; /* argv_ret */ + if (!(func_type = LLVMFunctionType(VOID_TYPE, func_param_types, 5, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + /* prepare function pointer */ + if (comp_ctx->is_jit_mode) { + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + /* JIT mode, call the function directly */ + if (!(func = I64_CONST((uint64)(uintptr_t)aot_invoke_native)) + || !(func = LLVMConstIntToPtr(func, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else { + if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) + && !(func = LLVMAddFunction(comp_ctx->module, + func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + if (param_count > 64) { + aot_set_last_error("prepare native arguments failed: " + "maximum 64 parameter cell number supported."); + return false; + } + + /* prepare frame_lp */ + for (i = 0; i < param_count; i++) { + if (!(elem_idx = I32_CONST(cell_num)) + || !(elem_ptr_type = LLVMPointerType(param_types[i], 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + return false; + } + + snprintf(buf, sizeof(buf), "%s%d", "elem", i); + if (!(elem_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->argv_buf, &elem_idx, 1, buf)) + || !(elem_ptr = LLVMBuildBitCast(comp_ctx->builder, elem_ptr, + elem_ptr_type, buf))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + if (!(res = LLVMBuildStore(comp_ctx->builder, param_values[i], elem_ptr))) { + aot_set_last_error("llvm build store failed."); + return false; + } + LLVMSetAlignment(res, 1); + + cell_num += wasm_value_type_cell_num(aot_func_type->types[i]); + } + + if (wasm_ret_type != VALUE_TYPE_VOID) { + if (!(ret_ptr_type = LLVMPointerType(ret_type, 0))) { + aot_set_last_error("llvm add pointer type failed."); + return false; + } + + if (!(value_ret = LLVMBuildBitCast(comp_ctx->builder, func_ctx->argv_buf, + ret_ptr_type, "argv_ret"))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + /* convert to int32 pointer */ + if (!(value_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, value_ret, + INT32_PTR_TYPE, "argv_ret_ptr"))) { + aot_set_last_error("llvm build store failed."); + return false; + } + } + else { + value_ret_ptr = LLVMConstNull(INT32_PTR_TYPE); + } + + func_param_values[0] = func_ctx->exec_env; + func_param_values[1] = func_idx; + func_param_values[2] = func_ctx->argv_buf; + func_param_values[3] = I32_CONST(param_cell_num); + func_param_values[4] = value_ret_ptr; + + if (!func_param_values[3]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_invoke_native() function */ + if (!(LLVMBuildCall(comp_ctx->builder, func, func_param_values, 5, ""))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + if (wasm_ret_type != VALUE_TYPE_VOID) + /* get function return value */ + *p_value_ret = LLVMBuildLoad(comp_ctx->builder, value_ret, "value_ret"); + + return true; +} + bool aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 func_idx, uint8 **p_frame_ip) { uint32 import_func_count = comp_ctx->comp_data->import_func_count; AOTImportFunc *import_funcs = comp_ctx->comp_data->import_funcs; - uint32 func_count = comp_ctx->func_ctx_count; + uint32 func_count = comp_ctx->func_ctx_count, param_cell_num = 0; AOTFuncContext **func_ctxes = comp_ctx->func_ctxes; AOTFuncType *func_type; - LLVMTypeRef *param_types = NULL, ret_type, f_type, f_ptr_type; - LLVMValueRef *param_values = NULL, value_ret, func, value, cmp; - LLVMBasicBlockRef check_func_ptr_succ; + LLVMTypeRef *param_types = NULL, ret_type; + LLVMValueRef *param_values = NULL, value_ret = NULL, func; + LLVMValueRef import_func_idx; int32 i, j = 0, param_count; - void *func_ptr; uint64 total_size; + uint8 wasm_ret_type; bool ret = false; /* Check function index */ @@ -112,6 +240,9 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, func_type = func_ctxes[func_idx - import_func_count]-> aot_func->func_type; + /* Get param cell number */ + param_cell_num = wasm_type_param_cell_num(func_type); + /* Allocate memory for parameters */ param_count = (int32)func_type->param_count; total_size = sizeof(LLVMValueRef) * (uint64)(param_count + 1); @@ -129,8 +260,10 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, POP(param_values[i + j], func_type->types[i]); if (func_idx < import_func_count) { - /* Get function pointer linked */ - func_ptr = import_funcs[func_idx].func_ptr_linked; + if (!(import_func_idx = I32_CONST(func_idx))) { + aot_set_last_error("llvm build inbounds gep failed."); + goto fail; + } /* Initialize parameter types of the LLVM function */ total_size = sizeof(LLVMTypeRef) * (uint64)(param_count + 1); @@ -146,99 +279,37 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, for (i = 0; i < param_count; i++) param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); - /* Resolve return type of the LLVM function */ - if (func_type->result_count) - ret_type = TO_LLVM_TYPE(func_type->types[func_type->param_count]); - else - ret_type = VOID_TYPE; - - /* Resolve function prototype */ - if (!(f_type = LLVMFunctionType(ret_type, param_types, - (uint32)param_count + 1, false)) - || !(f_ptr_type = LLVMPointerType(f_type, 0))) { - aot_set_last_error("create LLVM function type failed."); - goto fail; - } - - if (comp_ctx->is_jit_mode) { - if (!func_ptr) { - /* The import function isn't linked, throw exception - when calling it. */ - if (!aot_emit_exception(comp_ctx, func_ctx, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - false, NULL, NULL)) - goto fail; - ret = aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); - goto fail; - } - - /* JIT mode, call the linked function directly */ - if (!(value = I64_CONST((uint64)(uintptr_t)func_ptr)) - || !(func = LLVMConstIntToPtr(value, f_ptr_type))) { - aot_set_last_error("create LLVM value failed."); - goto fail; - } + if (func_type->result_count) { + wasm_ret_type = func_type->types[func_type->param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); } else { - /* Load function pointer */ - if (!(value = I32_CONST(func_idx)) - || !(func_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->func_ptrs, - &value, 1, "func_ptr"))) { - aot_set_last_error("llvm build inbounds gep failed."); - goto fail; - } - - if (!(func = LLVMBuildLoad(comp_ctx->builder, func_ptr, "func_tmp"))) { - aot_set_last_error("llvm build load failed."); - goto fail; - } - - /* Check whether import function is NULL */ - if (!(cmp = LLVMBuildIsNull(comp_ctx->builder, func, "is_func_null"))) { - aot_set_last_error("llvm build icmp failed."); - goto fail; - } - - /* Throw exception if import function is NULL */ - if (!(check_func_ptr_succ = - LLVMAppendBasicBlockInContext(comp_ctx->context, - func_ctx->func, - "check_func_ptr_succ"))) { - aot_set_last_error("llvm add basic block failed."); - goto fail; - } - - LLVMMoveBasicBlockAfter(check_func_ptr_succ, - LLVMGetInsertBlock(comp_ctx->builder)); - - if (!(aot_emit_exception(comp_ctx, func_ctx, - EXCE_CALL_UNLINKED_IMPORT_FUNC, - true, cmp, check_func_ptr_succ))) - goto fail; - - if (!(func = LLVMBuildBitCast(comp_ctx->builder, func, - f_ptr_type, "func"))) { - aot_set_last_error("create LLVM value failed."); - goto fail; - } + wasm_ret_type = VALUE_TYPE_VOID; + ret_type = VOID_TYPE; } + + /* call aot_invoke_native() */ + if (!call_aot_invoke_native_func(comp_ctx, func_ctx, import_func_idx, func_type, + param_types + 1, param_values + 1, + param_count, param_cell_num, + ret_type, wasm_ret_type, &value_ret)) + goto fail; } else { func = func_ctxes[func_idx - import_func_count]->func; - } - /* Call the function */ - if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, - param_values, (uint32)param_count + 1, - (func_type->result_count > 0 - ? "call" : "")))) { - aot_set_last_error("LLVM build call failed."); - goto fail; - } + /* Call the function */ + if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, + param_values, (uint32)param_count + 1, + (func_type->result_count > 0 + ? "call" : "")))) { + aot_set_last_error("LLVM build call failed."); + goto fail; + } - /* Set calling convention for the call with the func's calling convention */ - LLVMSetInstructionCallConv(value_ret, LLVMGetFunctionCallConv(func)); + /* Set calling convention for the call with the func's calling convention */ + LLVMSetInstructionCallConv(value_ret, LLVMGetFunctionCallConv(func)); + } if (func_type->result_count > 0) PUSH(value_ret, func_type->types[func_type->param_count]); @@ -269,10 +340,12 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, f_type, f_ptr_type; LLVMBasicBlockRef check_elem_idx_succ, check_ftype_idx_succ; LLVMBasicBlockRef check_func_idx_succ, check_func_ptr_succ; - int32 i, j = 0, param_count; - uint64 total_size; - bool ret; char *func_name = "aot_is_wasm_type_equal"; + int32 i, j = 0, param_count; + uint32 param_cell_num; + uint64 total_size; + uint8 wasm_ret_type; + bool ret; /* Check function type index */ if (type_idx >= comp_ctx->comp_data->func_type_count) { @@ -282,6 +355,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, func_type = comp_ctx->comp_data->func_types[type_idx]; + param_cell_num = wasm_type_param_cell_num(func_type); + POP_I32(elem_idx); table_size_const = I32_CONST(comp_ctx->comp_data->table_size); @@ -383,9 +458,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, goto fail; } /* Create LLVM function with const function pointer */ - if (!(func_const = - I64_CONST((uint64)(uintptr_t)aot_is_wasm_type_equal)) - || !(func = LLVMConstIntToPtr(func_const, f_ptr_type))) { + if (!(func_const = I64_CONST((uint64)(uintptr_t)aot_is_wasm_type_equal)) + || !(func = LLVMConstIntToPtr(func_const, f_ptr_type))) { aot_set_last_error("create LLVM value failed."); goto fail; } @@ -485,23 +559,13 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); /* Resolve return type of the LLVM function */ - if (func_type->result_count) - ret_type = TO_LLVM_TYPE(func_type->types[func_type->param_count]); - else - ret_type = VOID_TYPE; - - /* Resolve function prototype */ - if (!(f_type = LLVMFunctionType(ret_type, param_types, - (uint32)param_count + 1, false)) - || !(f_ptr_type = LLVMPointerType(f_type, 0))) { - aot_set_last_error("create LLVM function type failed."); - goto fail; + if (func_type->result_count) { + wasm_ret_type = func_type->types[func_type->param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); } - - if (!(func = LLVMBuildBitCast(comp_ctx->builder, func, - f_ptr_type, "func"))) { - aot_set_last_error("create LLVM value failed."); - goto fail; + else { + wasm_ret_type = VALUE_TYPE_VOID; + ret_type = VOID_TYPE; } /* Allocate memory for parameters */ @@ -520,14 +584,11 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, for (i = param_count - 1; i >= 0; i--) POP(param_values[i + j], func_type->types[i]); - /* Call the function */ - if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, - param_values, (uint32)param_count + 1, - (func_type->result_count > 0 - ? "call_indirect" : "")))) { - aot_set_last_error("LLVM build call failed."); + if (!call_aot_invoke_native_func(comp_ctx, func_ctx, func_idx, func_type, + param_types + 1, param_values + 1, + param_count, param_cell_num, + ret_type, wasm_ret_type, &value_ret)) goto fail; - } if (func_type->result_count > 0) PUSH(value_ret, func_type->types[func_type->param_count]); diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index e07aea1e6..f12ff7c28 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -440,8 +440,9 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, AOTFuncContext *func_ctx; AOTFuncType *aot_func_type = comp_data->func_types[func->func_type_index]; AOTBlock *aot_block; - LLVMTypeRef int8_ptr_type; + LLVMTypeRef int8_ptr_type, int32_ptr_type; LLVMValueRef aot_inst_offset = I32_TWO, aot_inst_addr; + LLVMValueRef argv_buf_offset = I32_THREE, argv_buf_addr; char local_name[32]; uint64 size; uint32 i, j = 0; @@ -476,7 +477,7 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, func_ctx->exec_env = LLVMGetParam(func_ctx->func, j++); /* Get aot inst address, the layout of exec_env is: - exec_env->next, exec_env->prev, and exec_env->module_inst */ + exec_env->next, exec_env->prev, exec_env->module_inst, and argv_buf */ if (!(aot_inst_addr = LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->exec_env, &aot_inst_offset, 1, "aot_inst_addr"))) { @@ -491,6 +492,32 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, goto fail; } + /* Get argv buffer address */ + if (!(argv_buf_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->exec_env, + &argv_buf_offset, 1, "argv_buf_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + goto fail; + } + + if (!(int32_ptr_type = LLVMPointerType(INT32_PTR_TYPE, 0))) { + aot_set_last_error("llvm add pointer type failed"); + goto fail; + } + + /* Convert to int32 pointer type */ + if (!(argv_buf_addr = LLVMBuildBitCast(comp_ctx->builder, argv_buf_addr, + int32_ptr_type, "argv_buf_ptr"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + + if (!(func_ctx->argv_buf = LLVMBuildLoad(comp_ctx->builder, + argv_buf_addr, "argv_buf"))) { + aot_set_last_error("llvm build load failed"); + goto fail; + } + for (i = 0; i < aot_func_type->param_count; i++, j++) { snprintf(local_name, sizeof(local_name), "l%d", i); func_ctx->locals[i] = @@ -674,6 +701,7 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) consts->f64_zero = F64_CONST(0); consts->i32_one = I32_CONST(1); consts->i32_two = I32_CONST(2); + consts->i32_three = I32_CONST(3); consts->i32_four = I32_CONST(4); consts->i32_eight = I32_CONST(8); consts->i32_neg_one = I32_CONST((uint32)-1); @@ -692,6 +720,7 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) && consts->f64_zero && consts->i32_one && consts->i32_two + && consts->i32_three && consts->i32_four && consts->i32_eight && consts->i32_neg_one diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index c654eae7a..80113b73e 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -92,6 +92,7 @@ typedef struct AOTFuncContext { LLVMValueRef exec_env; LLVMValueRef aot_inst; LLVMValueRef table_base; + LLVMValueRef argv_buf; LLVMValueRef mem_data_size; LLVMValueRef mem_base_addr; @@ -150,6 +151,7 @@ typedef struct AOTLLVMConsts { LLVMValueRef f64_zero; LLVMValueRef i32_one; LLVMValueRef i32_two; + LLVMValueRef i32_three; LLVMValueRef i32_four; LLVMValueRef i32_eight; LLVMValueRef i32_neg_one; diff --git a/core/iwasm/include/ext_lib_export.h b/core/iwasm/include/ext_lib_export.h deleted file mode 100644 index fbf8d17d4..000000000 --- a/core/iwasm/include/ext_lib_export.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#ifndef _EXT_LIB_EXPORT_H_ -#define _EXT_LIB_EXPORT_H_ - -#include "lib_export.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int -get_ext_lib_export_apis(NativeSymbol **p_ext_lib_apis) -{ - *p_ext_lib_apis = extended_native_symbol_defs; - return sizeof(extended_native_symbol_defs) / sizeof(NativeSymbol); -} - -#ifdef __cplusplus -} -#endif - -#endif /* end of _EXT_LIB_EXPORT_H_ */ - diff --git a/core/iwasm/include/lib_export.h b/core/iwasm/include/lib_export.h index c7f0dbc63..97e043e11 100644 --- a/core/iwasm/include/lib_export.h +++ b/core/iwasm/include/lib_export.h @@ -13,10 +13,16 @@ extern "C" { typedef struct NativeSymbol { const char *symbol; void *func_ptr; + const char *signature; } NativeSymbol; -#define EXPORT_WASM_API(symbol) {#symbol, (void*)symbol} -#define EXPORT_WASM_API2(symbol) {#symbol, (void*)symbol##_wrapper} +#define EXPORT_WASM_API(symbol) {#symbol, (void*)symbol, NULL} +#define EXPORT_WASM_API2(symbol) {#symbol, (void*)symbol##_wrapper, NULL} + +#define EXPORT_WASM_API_WITH_SIG(symbol, signature) \ + {#symbol, (void*)symbol, signature} +#define EXPORT_WASM_API_WITH_SIG2(symbol, signature) \ + {#symbol, (void*)symbol##_wrapper, signature} /** * Get the exported APIs of base lib diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 92bdece48..a0654c496 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -8,6 +8,7 @@ #include #include +#include "lib_export.h" #ifdef __cplusplus @@ -161,8 +162,7 @@ wasm_runtime_lookup_wasi_start_function(wasm_module_inst_t module_inst); * * @param module_inst the module instance * @param name the name of the function - * @param signature the signature of the function, use "i32"/"i64"/"f32"/"f64" - * to represent the type of i32/i64/f32/f64, e.g. "(i32i64)" "(i32)f32" + * @param signature the signature of the function, ignored currently * * @return the function instance found */ @@ -293,6 +293,8 @@ wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); * * @param module_inst the WASM module instance which contains heap * @param size the size bytes to allocate + * @param p_native_addr return native address of the allocated memory + * if it is not NULL, and return NULL if memory malloc failed * * @return the allocated memory address, which is a relative offset to the * base address of the module instance's memory space, the value range @@ -300,7 +302,8 @@ wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); * Return non-zero if success, zero if failed. */ int32_t -wasm_runtime_module_malloc(wasm_module_inst_t module_inst, uint32_t size); +wasm_runtime_module_malloc(wasm_module_inst_t module_inst, uint32_t size, + void **p_native_addr); /** * Free memory to the heap of WASM module instance @@ -432,6 +435,36 @@ wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, uint8_t **p_native_start_addr, uint8_t **p_native_end_addr); +/** + * Register native functions with same module name + * + * @param module_name the module name of the native functions + * @param native_symbols specifies an array of NativeSymbol structures which + * contain the names, function pointers and signatures + * Note: WASM runtime will not allocate memory to clone the data, so + * user must ensure the array can be used forever + * Meanings of letters in function signature: + * 'i': the parameter is i32 type + * 'I': the parameter is i64 type + * 'f': the parameter is f32 type + * 'F': the parameter is f64 type + * '*': the parameter is a pointer (i32 in WASM), and runtime will + * auto check its boundary before calling the native function. + * If it is followed by '~', the checked length of the pointer + * is gotten from the following parameter, if not, the checked + * length of the pointer is 1. + * '~': the parameter is the pointer's length with i32 type, and must + * follow after '*' + * '$': the parameter is a string (i32 in WASM), and runtime will + * auto check its boundary before calling the native function + * @param n_native_symbols specifies the number of native symbols in the array + * + * @return true if success, false otherwise + */ +bool wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32_t n_native_symbols); + #ifdef __cplusplus } #endif diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index 2ecc973a1..f730d7a11 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -137,6 +137,8 @@ typedef struct WASMFunctionImport { WASMType *func_type; /* function pointer after linked */ void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; } WASMFunctionImport; typedef struct WASMGlobalImport { @@ -288,8 +290,7 @@ typedef struct WASMModule { auxiliary stack top pointer */ uint32 llvm_aux_stack_global_index; - /* Whether there is possible memory grow, e.g. - memory.grow opcode or call enlargeMemory */ + /* Whether there is possible memory grow, e.g. memory.grow opcode */ bool possible_memory_grow; StringList const_str_list; diff --git a/core/iwasm/interpreter/wasm_interp.c b/core/iwasm/interpreter/wasm_interp.c index e46fd9b5e..bcba0d516 100644 --- a/core/iwasm/interpreter/wasm_interp.c +++ b/core/iwasm/interpreter/wasm_interp.c @@ -726,9 +726,11 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, WASMFunctionInstance *cur_func, WASMInterpFrame *prev_frame) { + WASMFunctionImport *func_import = cur_func->u.func_import; unsigned local_cell_num = 2; WASMInterpFrame *frame; uint32 argv_ret[2]; + char buf[128]; bool ret; if (!(frame = ALLOC_FRAME(exec_env, @@ -742,19 +744,16 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst, wasm_exec_env_set_cur_frame(exec_env, frame); - if (!cur_func->u.func_import->func_ptr_linked) { - char buf[128]; - snprintf(buf, - sizeof(buf), "fail to call unlinked import function (%s, %s)", - cur_func->u.func_import->module_name, - cur_func->u.func_import->field_name); - wasm_set_exception((WASMModuleInstance*)module_inst, buf); + if (!func_import->func_ptr_linked) { + 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; } - ret = wasm_runtime_invoke_native(cur_func->u.func_import->func_ptr_linked, - cur_func->u.func_import->func_type, - exec_env, + ret = wasm_runtime_invoke_native(exec_env, func_import->func_ptr_linked, + func_import->func_type, func_import->signature, frame->lp, cur_func->param_cell_num, argv_ret); if (!ret) diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 26f5e0b18..f9968c92d 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -556,36 +556,6 @@ load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, return true; } -static void* -resolve_sym(const char *module_name, const char *field_name) -{ - void *sym; - -#if WASM_ENABLE_LIBC_BUILTIN != 0 - if ((sym = wasm_native_lookup_libc_builtin_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_LIBC_WASI != 0 - if ((sym = wasm_native_lookup_libc_wasi_func(module_name, - field_name))) - return sym; -#endif - -#if WASM_ENABLE_BASE_LIB != 0 - if ((sym = wasm_native_lookup_base_lib_func(module_name, - field_name))) - return sym; -#endif - - if ((sym = wasm_native_lookup_extension_lib_func(module_name, - field_name))) - return sym; - - return NULL; -} - static bool load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) @@ -737,13 +707,10 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, } import->u.function.func_type = module->types[type_index]; - if (!module->possible_memory_grow - && !strcmp(module_name, "env") - && !(strcmp(field_name, "enlargeMemory"))) - module->possible_memory_grow = true; - if (!(import->u.function.func_ptr_linked = - resolve_sym(module_name, field_name))) { + wasm_native_resolve_symbol(module_name, field_name, + import->u.function.func_type, + &import->u.function.signature))) { #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); @@ -3016,7 +2983,8 @@ handle_next_reachable_block: POP_TYPE(func_type->types[idx]); } - PUSH_TYPE(func_type->types[func_type->param_count]); + if (func_type->result_count > 0) + PUSH_TYPE(func_type->types[func_type->param_count]); func->has_op_func_call = true; break; diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 5f78bfa62..6f56cce69 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -849,75 +849,15 @@ wasm_deinstantiate(WASMModuleInstance *module_inst) wasm_free(module_inst); } -static bool -check_type(uint8 type, const char *p) -{ - const char *str = "i32"; - - if (strlen(p) < 3) - return false; - - switch (type) { - case VALUE_TYPE_I32: - str = "i32"; - break; - case VALUE_TYPE_I64: - str = "i64"; - break; - case VALUE_TYPE_F32: - str = "f32"; - break; - case VALUE_TYPE_F64: - str = "f64"; - break; - } - if (strncmp(p, str, 3)) - return false; - - return true; -} - -static bool -check_function_type(const WASMType *type, const char *signature) -{ - uint32 i; - const char *p = signature; - - if (!p || *p++ != '(') - return false; - - for (i = 0; i < type->param_count; i++) { - if (!check_type(type->types[i], p)) - return false; - p += 3; - } - - if (*p++ != ')') - return false; - - if (type->result_count) { - if (!check_type(type->types[type->param_count], p)) - return false; - p += 3; - } - - if (*p != '\0') - return false; - - return true; -} - WASMFunctionInstance* wasm_lookup_function(const WASMModuleInstance *module_inst, const char *name, const char *signature) { uint32 i; for (i = 0; i < module_inst->export_func_count; i++) - if (!strcmp(module_inst->export_functions[i].name, name) - && check_function_type( - module_inst->export_functions[i].function->u.func->func_type, - signature)) + if (!strcmp(module_inst->export_functions[i].name, name)) return module_inst->export_functions[i].function; + (void)signature; return NULL; } @@ -972,10 +912,13 @@ wasm_get_exception(WASMModuleInstance *module_inst) } int32 -wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size) +wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size, + void **p_native_addr) { WASMMemoryInstance *memory = module_inst->default_memory; uint8 *addr = mem_allocator_malloc(memory->heap_handle, size); + if (p_native_addr) + *p_native_addr = addr; if (!addr) { wasm_set_exception(module_inst, "out of memory"); return 0; @@ -998,9 +941,10 @@ int32 wasm_module_dup_data(WASMModuleInstance *module_inst, const char *src, uint32 size) { - int32 buffer_offset = wasm_module_malloc(module_inst, size); + char *buffer; + int32 buffer_offset = wasm_module_malloc(module_inst, size, + (void**)&buffer); if (buffer_offset != 0) { - char *buffer; buffer = wasm_addr_app_to_native(module_inst, buffer_offset); bh_memcpy_s(buffer, size, src, size); } diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 85f5d7242..aa025fcc6 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -222,7 +222,8 @@ const char* wasm_get_exception(WASMModuleInstance *module); int32 -wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size); +wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size, + void **p_native_addr); void wasm_module_free(WASMModuleInstance *module_inst, int32 ptr); diff --git a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c index 28aba59db..66f1badf8 100644 --- a/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c +++ b/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c @@ -6,9 +6,7 @@ #include "bh_common.h" #include "bh_log.h" #include "wasm_export.h" -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 #include "../interpreter/wasm.h" -#endif void wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); @@ -34,14 +32,17 @@ wasm_runtime_set_llvm_stack(wasm_module_inst_t module, uint32 llvm_stack); #define validate_app_str_addr(offset) \ wasm_runtime_validate_app_str_addr(module_inst, offset) +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + #define addr_app_to_native(offset) \ wasm_runtime_addr_app_to_native(module_inst, offset) #define addr_native_to_app(ptr) \ wasm_runtime_addr_native_to_app(module_inst, ptr) -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) #define module_free(offset) \ wasm_runtime_module_free(module_inst, offset) @@ -392,73 +393,46 @@ printf_out(int c, struct str_context *ctx) return c; } -static bool -parse_printf_args(wasm_module_inst_t module_inst, int32 fmt_offset, - int32 va_list_offset, const char **p_fmt, - _va_list *p_va_args) -{ - const char *fmt; - union { - uintptr_t u; - _va_list v; - } u; - - if (!validate_app_str_addr(fmt_offset) - || !validate_app_addr(va_list_offset, sizeof(int32))) - return false; - - fmt = (const char*) addr_app_to_native(fmt_offset); - u.u = (uintptr_t) addr_app_to_native(va_list_offset); - - *p_fmt = fmt; - *p_va_args = u.v; - return true; -} - static int -_printf_wrapper(wasm_exec_env_t exec_env, - int32 fmt_offset, int32 va_list_offset) +printf_wrapper(wasm_exec_env_t exec_env, + const char * format, _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); struct str_context ctx = { NULL, 0, 0 }; - const char *fmt; - _va_list va_args; - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) + /* format has been checked by runtime */ + if (!validate_native_addr(va_args, sizeof(int32))) return 0; - if (!_vprintf_wa((out_func_t)printf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)printf_out, &ctx, format, va_args, module_inst)) return 0; + return (int)ctx.count; } static int -_sprintf_wrapper(wasm_exec_env_t exec_env, - int32 str_offset, int32 fmt_offset, int32 va_list_offset) +sprintf_wrapper(wasm_exec_env_t exec_env, + char *str, const char *format, _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - int32 app_end_offset; + uint8 *native_end_offset; struct str_context ctx; - char *str; - const char *fmt; - _va_list va_args; - if (!wasm_runtime_get_app_addr_range(module_inst, str_offset, - NULL, &app_end_offset)) { + /* str and format have been checked by runtime */ + if (!validate_native_addr(va_args, sizeof(uint32))) + return 0; + + if (!wasm_runtime_get_native_addr_range(module_inst, (uint8*)str, + NULL, &native_end_offset)) { wasm_runtime_set_exception(module_inst, "out of bounds memory access"); return false; } - str = addr_app_to_native(str_offset); - - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) - return 0; - ctx.str = str; - ctx.max = (uint32)(app_end_offset - str_offset); + ctx.max = (uint32)(native_end_offset - (uint8*)str); ctx.count = 0; - if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, module_inst)) return 0; if (ctx.count < ctx.max) { @@ -469,29 +443,21 @@ _sprintf_wrapper(wasm_exec_env_t exec_env, } static int -_snprintf_wrapper(wasm_exec_env_t exec_env, - int32 str_offset, uint32 size, int32 fmt_offset, - int32 va_list_offset) +snprintf_wrapper(wasm_exec_env_t exec_env, char *str, uint32 size, + const char *format, _va_list va_args) { wasm_module_inst_t module_inst = get_module_inst(exec_env); struct str_context ctx; - char *str; - const char *fmt; - _va_list va_args; - if (!validate_app_addr(str_offset, size)) - return 0; - - str = addr_app_to_native(str_offset); - - if (!parse_printf_args(module_inst, fmt_offset, va_list_offset, &fmt, &va_args)) + /* str and format have been checked by runtime */ + if (!validate_native_addr(va_args, sizeof(uint32))) return 0; ctx.str = str; ctx.max = size; ctx.count = 0; - if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, fmt, va_args, module_inst)) + if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, module_inst)) return 0; if (ctx.count < ctx.max) { @@ -502,46 +468,32 @@ _snprintf_wrapper(wasm_exec_env_t exec_env, } static int -_puts_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +puts_wrapper(wasm_exec_env_t exec_env, const char *str) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - const char *str; - - if (!validate_app_str_addr(str_offset)) - return 0; - - str = addr_app_to_native(str_offset); return bh_printf("%s\n", str); } static int -_putchar_wrapper(wasm_exec_env_t exec_env, int c) +putchar_wrapper(wasm_exec_env_t exec_env, int c) { bh_printf("%c", c); return 1; } static int32 -_strdup_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +strdup_wrapper(wasm_exec_env_t exec_env, const char *str) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *str, *str_ret; + char *str_ret; uint32 len; int32 str_ret_offset = 0; - if (!validate_app_str_addr(str_offset)) - return 0; - - str = addr_app_to_native(str_offset); - + /* str has been checked by runtime */ if (str) { len = (uint32)strlen(str) + 1; - str_ret_offset = module_malloc(len); + str_ret_offset = module_malloc(len, (void**)&str_ret); if (str_ret_offset) { - str_ret = addr_app_to_native(str_ret_offset); bh_memcpy_s(str_ret, len, str, len); } } @@ -550,192 +502,151 @@ _strdup_wrapper(wasm_exec_env_t exec_env, } static int32 -__strdup_wrapper(wasm_exec_env_t exec_env, - int32 str_offset) +_strdup_wrapper(wasm_exec_env_t exec_env, const char *str) { - return _strdup_wrapper(exec_env, str_offset); + return strdup_wrapper(exec_env, str); } static int32 -_memcmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset, uint32 size) +memcmp_wrapper(wasm_exec_env_t exec_env, + const void *s1, const void *s2, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - if (!validate_app_addr(s1_offset, size) - || !validate_app_addr(s2_offset, size)) + /* s2 has been checked by runtime */ + if (!validate_native_addr((void*)s1, size)) return 0; - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); return memcmp(s1, s2, size); } static int32 -_memcpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +memcpy_wrapper(wasm_exec_env_t exec_env, + void *dst, const void *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + int32 dst_offset = addr_native_to_app(dst); if (size == 0) return dst_offset; - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) return dst_offset; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); bh_memcpy_s(dst, size, src, size); return dst_offset; } static int32 -_memmove_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +memmove_wrapper(wasm_exec_env_t exec_env, + void *dst, void *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + int32 dst_offset = addr_native_to_app(dst); - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + if (size == 0) + return dst_offset; + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) return dst_offset; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); memmove(dst, src, size); return dst_offset; } static int32 -_memset_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, int32 c, uint32 size) +memset_wrapper(wasm_exec_env_t exec_env, + void *s, int32 c, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s; + int32 s_offset = addr_native_to_app(s); - if (!validate_app_addr(s_offset, size)) + if (!validate_native_addr(s, size)) return s_offset; - s = addr_app_to_native(s_offset); memset(s, c, size); return s_offset; } static int32 -_strchr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, int32 c) +strchr_wrapper(wasm_exec_env_t exec_env, + const char *s, int32 c) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - const char *s; char *ret; - if (!validate_app_str_addr(s_offset)) - return s_offset; - - s = addr_app_to_native(s_offset); + /* s has been checked by runtime */ ret = strchr(s, c); return ret ? addr_native_to_app(ret) : 0; } static int32 -_strcmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset) +strcmp_wrapper(wasm_exec_env_t exec_env, + const char *s1, const char *s2) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - - if (!validate_app_str_addr(s1_offset) - || !validate_app_str_addr(s2_offset)) - return 0; - - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); + /* s1 and s2 have been checked by runtime */ return strcmp(s1, s2); } static int32 -_strncmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, int32 s2_offset, uint32 size) +strncmp_wrapper(wasm_exec_env_t exec_env, + const char *s1, const char *s2, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s1, *s2; - if (!validate_app_addr(s1_offset, size) - || !validate_app_addr(s2_offset, size)) + /* s2 has been checked by runtime */ + if (!validate_native_addr((void*)s1, size)) return 0; - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); return strncmp(s1, s2, size); } static int32 -_strcpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset) +strcpy_wrapper(wasm_exec_env_t exec_env, char *dst, const char *src) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *dst, *src; - uint32 len; + uint32 len = strlen(src) + 1; - if (!validate_app_str_addr(src_offset)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, len)) return 0; - src = addr_app_to_native(src_offset); - len = (uint32)strlen(src); - - if (!validate_app_addr(dst_offset, len + 1)) - return 0; - - dst = addr_app_to_native(dst_offset); - strncpy(dst, src, len + 1); - return dst_offset; + strncpy(dst, src, len); + return addr_native_to_app(dst); } static int32 -_strncpy_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, uint32 size) +strncpy_wrapper(wasm_exec_env_t exec_env, + char *dst, const char *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *dst, *src; - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) return 0; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); strncpy(dst, src, size); - return dst_offset; + return addr_native_to_app(dst); } static uint32 -_strlen_wrapper(wasm_exec_env_t exec_env, - int32 s_offset) +strlen_wrapper(wasm_exec_env_t exec_env, const char *s) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s; - - if (!validate_app_str_addr(s_offset)) - return 0; - - s = addr_app_to_native(s_offset); + /* s has been checked by runtime */ return (uint32)strlen(s); } static int32 -_malloc_wrapper(wasm_exec_env_t exec_env, - uint32 size) +malloc_wrapper(wasm_exec_env_t exec_env, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - return module_malloc(size); + return module_malloc(size, NULL); } static int32 -_calloc_wrapper(wasm_exec_env_t exec_env, - uint32 nmemb, uint32 size) +calloc_wrapper(wasm_exec_env_t exec_env, uint32 nmemb, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); uint64 total_size = (uint64) nmemb * (uint64) size; @@ -745,9 +656,8 @@ _calloc_wrapper(wasm_exec_env_t exec_env, if (total_size >= UINT32_MAX) return 0; - ret_offset = module_malloc((uint32)total_size); + ret_offset = module_malloc((uint32)total_size, (void**)&ret_ptr); if (ret_offset) { - ret_ptr = addr_app_to_native(ret_offset); memset(ret_ptr, 0, (uint32) total_size); } @@ -755,47 +665,25 @@ _calloc_wrapper(wasm_exec_env_t exec_env, } static void -_free_wrapper(wasm_exec_env_t exec_env, - int32 ptr_offset) +free_wrapper(wasm_exec_env_t exec_env, void *ptr) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!validate_app_addr(ptr_offset, 4)) + + if (!validate_native_addr(ptr, sizeof(uint32))) return; - return module_free(ptr_offset); + + return module_free(addr_native_to_app(ptr)); } static int32 -_atoi_wrapper(wasm_exec_env_t exec_env, - int32 s_offset) +atoi_wrapper(wasm_exec_env_t exec_env, const char *s) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *str; - - if (!validate_app_str_addr(s_offset)) - return 0; - - str = addr_app_to_native(s_offset); - - return atoi(str); -} - -static int32 -_bsearch_wrapper(wasm_exec_env_t exec_env, - int32 key_offset, /* const void * */ - int32 array_offset, /* const void * */ - uint32 count, - uint32 size, - int32 cmp_index) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasm_runtime_set_exception(module_inst, "bsearch not implemented."); - - return 0; + /* s has been checked by runtime */ + return atoi(s); } static void -_exit_wrapper(wasm_exec_env_t exec_env, - int32 status) +exit_wrapper(wasm_exec_env_t exec_env, int32 status) { wasm_module_inst_t module_inst = get_module_inst(exec_env); char buf[32]; @@ -804,45 +692,33 @@ _exit_wrapper(wasm_exec_env_t exec_env, } static int32 -_strtol_wrapper(wasm_exec_env_t exec_env, - int32 nptr_offset, /* const char * */ - int32 endptr_offset, /* char ** */ - int32 base) +strtol_wrapper(wasm_exec_env_t exec_env, + const char *nptr, char **endptr, int32 base) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *nptr, **endptr; int32 num = 0; - if (!validate_app_str_addr(nptr_offset) - || !validate_app_addr(endptr_offset, sizeof(int32))) + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, sizeof(uint32))) return 0; - nptr = addr_app_to_native(nptr_offset); - endptr = addr_app_to_native(endptr_offset); - num = (int32)strtol(nptr, endptr, base); - *(int32 *)endptr = addr_native_to_app(*endptr); + *(int32*)endptr = addr_native_to_app(*endptr); return num; } static uint32 -_strtoul_wrapper(wasm_exec_env_t exec_env, - int32 nptr_offset, /* const char * */ - int32 endptr_offset, /* char ** */ - int32 base) +strtoul_wrapper(wasm_exec_env_t exec_env, + const char *nptr, char **endptr, int32 base) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *nptr, **endptr; uint32 num = 0; - if (!validate_app_str_addr(nptr_offset) - || !validate_app_addr(endptr_offset, sizeof(int32))) + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, sizeof(uint32))) return 0; - nptr = addr_app_to_native(nptr_offset); - endptr = addr_app_to_native(endptr_offset); - num = (uint32)strtoul(nptr, endptr, base); *(int32 *)endptr = addr_native_to_app(*endptr); @@ -850,162 +726,115 @@ _strtoul_wrapper(wasm_exec_env_t exec_env, } static int32 -_memchr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const void * */ - int32 c, - uint32 n) +memchr_wrapper(wasm_exec_env_t exec_env, + const void *s, int32 c, uint32 n) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *s, *res; + void *res; - if (!validate_app_addr(s_offset, n)) + if (!validate_native_addr((void*)s, n)) return 0; - s = (void*)addr_app_to_native(s_offset); - res = memchr(s, c, n); - return addr_native_to_app(res); } static int32 -_strncasecmp_wrapper(wasm_exec_env_t exec_env, - int32 s1_offset, /* const char * */ - int32 s2_offset, /* const char * */ - uint32 n) +strncasecmp_wrapper(wasm_exec_env_t exec_env, + const char *s1, const char *s2, int32 n) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s1, *s2; - - if (!validate_app_str_addr(s1_offset) - || !validate_app_str_addr(s2_offset)) - return 0; - - s1 = addr_app_to_native(s1_offset); - s2 = addr_app_to_native(s2_offset); - + /* s1 and s2 have been checked by runtime */ return strncasecmp(s1, s2, n); } static uint32 -_strspn_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 accept_offset) /* const char * */ +strspn_wrapper(wasm_exec_env_t exec_env, + const char *s, const char *accept) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *accept; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(accept_offset)) - return 0; - - s = addr_app_to_native(s_offset); - accept = addr_app_to_native(accept_offset); - + /* s and accept have been checked by runtime */ return (uint32)strspn(s, accept); } static uint32 -_strcspn_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 reject_offset) /* const char * */ +strcspn_wrapper(wasm_exec_env_t exec_env, + const char *s, const char *reject) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *reject; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(reject_offset)) - return 0; - - s = addr_app_to_native(s_offset); - reject = addr_app_to_native(reject_offset); - + /* s and reject have been checked by runtime */ return (uint32)strcspn(s, reject); } static int32 -_strstr_wrapper(wasm_exec_env_t exec_env, - int32 s_offset, /* const char * */ - int32 find_offset) /* const char * */ +strstr_wrapper(wasm_exec_env_t exec_env, + const char *s, const char *find) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *s, *find, *res; - - if (!validate_app_str_addr(s_offset) - || !validate_app_str_addr(find_offset)) - return 0; - - s = addr_app_to_native(s_offset); - find = addr_app_to_native(find_offset); - - res = strstr(s, find); - + /* s and find have been checked by runtime */ + char *res = strstr(s, find); return addr_native_to_app(res); } static int32 -_isupper_wrapper(wasm_exec_env_t exec_env, int32 c) +isupper_wrapper(wasm_exec_env_t exec_env, int32 c) { return isupper(c); } static int32 -_isalpha_wrapper(wasm_exec_env_t exec_env, int32 c) +isalpha_wrapper(wasm_exec_env_t exec_env, int32 c) { return isalpha(c); } static int32 -_isspace_wrapper(wasm_exec_env_t exec_env, int32 c) +isspace_wrapper(wasm_exec_env_t exec_env, int32 c) { return isspace(c); } static int32 -_isgraph_wrapper(wasm_exec_env_t exec_env, int32 c) +isgraph_wrapper(wasm_exec_env_t exec_env, int32 c) { return isgraph(c); } static int32 -_isprint_wrapper(wasm_exec_env_t exec_env, int32 c) +isprint_wrapper(wasm_exec_env_t exec_env, int32 c) { return isprint(c); } static int32 -_isdigit_wrapper(wasm_exec_env_t exec_env, int32 c) +isdigit_wrapper(wasm_exec_env_t exec_env, int32 c) { return isdigit(c); } static int32 -_isxdigit_wrapper(wasm_exec_env_t exec_env, int32 c) +isxdigit_wrapper(wasm_exec_env_t exec_env, int32 c) { return isxdigit(c); } static int32 -_tolower_wrapper(wasm_exec_env_t exec_env, int32 c) +tolower_wrapper(wasm_exec_env_t exec_env, int32 c) { return tolower(c); } static int32 -_toupper_wrapper(wasm_exec_env_t exec_env, int32 c) +toupper_wrapper(wasm_exec_env_t exec_env, int32 c) { return toupper(c); } static int32 -_isalnum_wrapper(wasm_exec_env_t exec_env, int32 c) +isalnum_wrapper(wasm_exec_env_t exec_env, int32 c) { return isalnum(c); } static void -setTempRet0_wrapper(wasm_exec_env_t exec_env, - uint32 temp_ret) +setTempRet0_wrapper(wasm_exec_env_t exec_env, uint32 temp_ret) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasm_runtime_set_temp_ret(module_inst, temp_ret); @@ -1019,8 +848,7 @@ getTempRet0_wrapper(wasm_exec_env_t exec_env) } static uint32 -_llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, - uint32 data) +llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, uint32 data) { return (data & 0xFFFF0000) | ((data & 0xFF) << 8) @@ -1028,8 +856,7 @@ _llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, } static uint32 -_llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, - uint32 data) +llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, uint32 data) { return ((data & 0xFF) << 24) | ((data & 0xFF00) << 8) @@ -1038,9 +865,9 @@ _llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, } static uint32 -_bitshift64Lshr_wrapper(wasm_exec_env_t exec_env, - uint32 uint64_part0, uint32 uint64_part1, - uint32 bits) +bitshift64Lshr_wrapper(wasm_exec_env_t exec_env, + uint32 uint64_part0, uint32 uint64_part1, + uint32 bits) { wasm_module_inst_t module_inst = get_module_inst(exec_env); union { @@ -1058,9 +885,9 @@ _bitshift64Lshr_wrapper(wasm_exec_env_t exec_env, } static uint32 -_bitshift64Shl_wrapper(wasm_exec_env_t exec_env, - uint32 int64_part0, uint32 int64_part1, - uint32 bits) +bitshift64Shl_wrapper(wasm_exec_env_t exec_env, + uint32 int64_part0, uint32 int64_part1, + uint32 bits) { wasm_module_inst_t module_inst = get_module_inst(exec_env); union { @@ -1078,8 +905,7 @@ _bitshift64Shl_wrapper(wasm_exec_env_t exec_env, } static void -_llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, - uint32 llvm_stack) +llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, uint32 llvm_stack) { wasm_module_inst_t module_inst = get_module_inst(exec_env); bh_printf("_llvm_stackrestore called!\n"); @@ -1087,7 +913,7 @@ _llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, } static uint32 -_llvm_stacksave_wrapper(wasm_exec_env_t exec_env) +llvm_stacksave_wrapper(wasm_exec_env_t exec_env) { wasm_module_inst_t module_inst = get_module_inst(exec_env); bh_printf("_llvm_stacksave called!\n"); @@ -1095,27 +921,22 @@ _llvm_stacksave_wrapper(wasm_exec_env_t exec_env) } static int32 -_emscripten_memcpy_big_wrapper(wasm_exec_env_t exec_env, - int32 dst_offset, int32 src_offset, - uint32 size) +emscripten_memcpy_big_wrapper(wasm_exec_env_t exec_env, + void *dst, const void *src, uint32 size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *dst, *src; + int32 dst_offset = addr_native_to_app(dst); - if (!validate_app_addr(dst_offset, size) - || !validate_app_addr(src_offset, size)) + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) return dst_offset; - dst = addr_app_to_native(dst_offset); - src = addr_app_to_native(src_offset); - bh_memcpy_s(dst, size, src, size); return dst_offset; } static void -abort_wrapper(wasm_exec_env_t exec_env, - int32 code) +abort_wrapper(wasm_exec_env_t exec_env, int32 code) { wasm_module_inst_t module_inst = get_module_inst(exec_env); char buf[32]; @@ -1124,8 +945,7 @@ abort_wrapper(wasm_exec_env_t exec_env, } static void -abortStackOverflow_wrapper(wasm_exec_env_t exec_env, - int32 code) +abortStackOverflow_wrapper(wasm_exec_env_t exec_env, int32 code) { wasm_module_inst_t module_inst = get_module_inst(exec_env); char buf[32]; @@ -1134,8 +954,7 @@ abortStackOverflow_wrapper(wasm_exec_env_t exec_env, } static void -nullFunc_X_wrapper(wasm_exec_env_t exec_env, - int32 code) +nullFunc_X_wrapper(wasm_exec_env_t exec_env, int32 code) { wasm_module_inst_t module_inst = get_module_inst(exec_env); char buf[32]; @@ -1148,7 +967,7 @@ __cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env, uint32 thrown_size) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - int32 exception = module_malloc(thrown_size); + int32 exception = module_malloc(thrown_size, NULL); if (!exception) return 0; @@ -1157,15 +976,14 @@ __cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env, static void __cxa_begin_catch_wrapper(wasm_exec_env_t exec_env, - int32 exception_object_offset) + void *exception_object) { - } static void __cxa_throw_wrapper(wasm_exec_env_t exec_env, - int32 thrown_exception_offset, - int32 tinfo_offset, + void *thrown_exception, + void *tinfo, uint32 table_elem_idx) { wasm_module_inst_t module_inst = get_module_inst(exec_env); @@ -1175,114 +993,90 @@ __cxa_throw_wrapper(wasm_exec_env_t exec_env, wasm_runtime_set_exception(module_inst, buf); } -#ifndef ENABLE_SPEC_TEST -#define ENABLE_SPEC_TEST 0 -#endif - -#if ENABLE_SPEC_TEST != 0 static void print_i32_wrapper(wasm_exec_env_t exec_env, int32 i32) { bh_printf("%d\n", i32); } -#endif -/* TODO: add function parameter/result types check */ -#define REG_NATIVE_FUNC(module_name, func_name) \ - { #module_name, #func_name, func_name##_wrapper } +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature } -typedef struct WASMNativeFuncDef { - const char *module_name; - const char *func_name; - void *func_ptr; -} WASMNativeFuncDef; - -static WASMNativeFuncDef native_func_defs[] = { -#if ENABLE_SPEC_TEST != 0 - REG_NATIVE_FUNC(spectest, print_i32), -#endif - REG_NATIVE_FUNC(env, _printf), - REG_NATIVE_FUNC(env, _sprintf), - REG_NATIVE_FUNC(env, _snprintf), - REG_NATIVE_FUNC(env, _puts), - REG_NATIVE_FUNC(env, _putchar), - REG_NATIVE_FUNC(env, _memcmp), - REG_NATIVE_FUNC(env, _memcpy), - REG_NATIVE_FUNC(env, _memmove), - REG_NATIVE_FUNC(env, _memset), - REG_NATIVE_FUNC(env, _strchr), - REG_NATIVE_FUNC(env, _strcmp), - REG_NATIVE_FUNC(env, _strcpy), - REG_NATIVE_FUNC(env, _strlen), - REG_NATIVE_FUNC(env, _strncmp), - REG_NATIVE_FUNC(env, _strncpy), - REG_NATIVE_FUNC(env, _malloc), - REG_NATIVE_FUNC(env, _calloc), - REG_NATIVE_FUNC(env, _strdup), +static NativeSymbol native_symbols_libc_builtin[] = { + REG_NATIVE_FUNC(printf, "($*)i"), + REG_NATIVE_FUNC(sprintf, "($$*)i"), + REG_NATIVE_FUNC(snprintf, "(*~$*)i"), + REG_NATIVE_FUNC(puts, "($)i"), + REG_NATIVE_FUNC(putchar, "(i)i"), + REG_NATIVE_FUNC(memcmp, "(**~)i"), + REG_NATIVE_FUNC(memcpy, "(**~)i"), + REG_NATIVE_FUNC(memmove, "(**~)i"), + REG_NATIVE_FUNC(memset, "(*ii)i"), + REG_NATIVE_FUNC(strchr, "($i)i"), + REG_NATIVE_FUNC(strcmp, "($$)i"), + REG_NATIVE_FUNC(strcpy, "(*$)i"), + REG_NATIVE_FUNC(strlen, "($)i"), + REG_NATIVE_FUNC(strncmp, "(**~)i"), + REG_NATIVE_FUNC(strncpy, "(**~)i"), + REG_NATIVE_FUNC(malloc, "(i)i"), + REG_NATIVE_FUNC(calloc, "(ii)i"), + REG_NATIVE_FUNC(strdup, "($)i"), /* clang may introduce __strdup */ - REG_NATIVE_FUNC(env, __strdup), - REG_NATIVE_FUNC(env, _free), - REG_NATIVE_FUNC(env, _atoi), - REG_NATIVE_FUNC(env, _bsearch), - REG_NATIVE_FUNC(env, _exit), - REG_NATIVE_FUNC(env, _strtol), - REG_NATIVE_FUNC(env, _strtoul), - REG_NATIVE_FUNC(env, _memchr), - REG_NATIVE_FUNC(env, _strncasecmp), - REG_NATIVE_FUNC(env, _strspn), - REG_NATIVE_FUNC(env, _strcspn), - REG_NATIVE_FUNC(env, _strstr), - REG_NATIVE_FUNC(env, _isupper), - REG_NATIVE_FUNC(env, _isalpha), - REG_NATIVE_FUNC(env, _isspace), - REG_NATIVE_FUNC(env, _isgraph), - REG_NATIVE_FUNC(env, _isprint), - REG_NATIVE_FUNC(env, _isdigit), - REG_NATIVE_FUNC(env, _isxdigit), - REG_NATIVE_FUNC(env, _tolower), - REG_NATIVE_FUNC(env, _toupper), - REG_NATIVE_FUNC(env, _isalnum), - REG_NATIVE_FUNC(env, setTempRet0), - REG_NATIVE_FUNC(env, getTempRet0), - REG_NATIVE_FUNC(env, _llvm_bswap_i16), - REG_NATIVE_FUNC(env, _llvm_bswap_i32), - REG_NATIVE_FUNC(env, _bitshift64Lshr), - REG_NATIVE_FUNC(env, _bitshift64Shl), - REG_NATIVE_FUNC(env, _llvm_stackrestore), - REG_NATIVE_FUNC(env, _llvm_stacksave), - REG_NATIVE_FUNC(env, _emscripten_memcpy_big), - REG_NATIVE_FUNC(env, abort), - REG_NATIVE_FUNC(env, abortStackOverflow), - REG_NATIVE_FUNC(env, nullFunc_X), - REG_NATIVE_FUNC(env, __cxa_allocate_exception), - REG_NATIVE_FUNC(env, __cxa_begin_catch), - REG_NATIVE_FUNC(env, __cxa_throw) + REG_NATIVE_FUNC(_strdup, "($)i"), + REG_NATIVE_FUNC(free, "(*)"), + REG_NATIVE_FUNC(atoi, "($)i"), + REG_NATIVE_FUNC(exit, "(i)"), + REG_NATIVE_FUNC(strtol, "($*i)i"), + REG_NATIVE_FUNC(strtoul, "($*i)i"), + REG_NATIVE_FUNC(memchr, "(*ii)"), + REG_NATIVE_FUNC(strncasecmp, "($$i)"), + REG_NATIVE_FUNC(strspn, "($$)i"), + REG_NATIVE_FUNC(strcspn, "($$)i"), + REG_NATIVE_FUNC(strstr, "($$)i"), + REG_NATIVE_FUNC(isupper, "(i)i"), + REG_NATIVE_FUNC(isalpha, "(i)i"), + REG_NATIVE_FUNC(isspace, "(i)i"), + REG_NATIVE_FUNC(isgraph, "(i)i"), + REG_NATIVE_FUNC(isprint, "(i)i"), + REG_NATIVE_FUNC(isdigit, "(i)i"), + REG_NATIVE_FUNC(isxdigit, "(i)i"), + REG_NATIVE_FUNC(tolower, "(i)i"), + REG_NATIVE_FUNC(toupper, "(i)i"), + REG_NATIVE_FUNC(isalnum, "(i)i"), + REG_NATIVE_FUNC(setTempRet0, "(i)"), + REG_NATIVE_FUNC(getTempRet0, "()i"), + REG_NATIVE_FUNC(llvm_bswap_i16, "(i)i"), + REG_NATIVE_FUNC(llvm_bswap_i32, "(i)i"), + REG_NATIVE_FUNC(bitshift64Lshr, "(iii)i"), + REG_NATIVE_FUNC(bitshift64Shl, "(iii)i"), + REG_NATIVE_FUNC(llvm_stackrestore, "(i)"), + REG_NATIVE_FUNC(llvm_stacksave, "()i"), + REG_NATIVE_FUNC(emscripten_memcpy_big, "(**~)i"), + REG_NATIVE_FUNC(abort, "(i)"), + REG_NATIVE_FUNC(abortStackOverflow, "(i)"), + REG_NATIVE_FUNC(nullFunc_X, "(i)"), + REG_NATIVE_FUNC(__cxa_allocate_exception, "(i)i"), + REG_NATIVE_FUNC(__cxa_begin_catch, "(*)"), + REG_NATIVE_FUNC(__cxa_throw, "(**i)") }; -void * -wasm_native_lookup_libc_builtin_func(const char *module_name, - const char *func_name) +static NativeSymbol native_symbols_spectest[] = { + REG_NATIVE_FUNC(print_i32, "(i)") +}; + +uint32 +get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis) { - uint32 size = sizeof(native_func_defs) / sizeof(WASMNativeFuncDef); - WASMNativeFuncDef *func_def = native_func_defs; - WASMNativeFuncDef *func_def_end = func_def + size; - - if (!module_name || !func_name) - return NULL; - - while (func_def < func_def_end) { - if (!strcmp(func_def->module_name, module_name) - && (!strcmp(func_def->func_name, func_name) - || (func_def->func_name[0] == '_' - && !strcmp(func_def->func_name + 1, func_name)))) - return (void*) (uintptr_t) func_def->func_ptr; - func_def++; - } - - return NULL; + *p_libc_builtin_apis = native_symbols_libc_builtin; + return sizeof(native_symbols_libc_builtin) / sizeof(NativeSymbol); } -#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +uint32 +get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis) +{ + *p_libc_builtin_apis = native_symbols_spectest; + return sizeof(native_symbols_spectest) / sizeof(NativeSymbol); +} /************************************* * Global Variables * @@ -1295,13 +1089,11 @@ typedef struct WASMNativeGlobalDef { } WASMNativeGlobalDef; static WASMNativeGlobalDef native_global_defs[] = { -#if ENABLE_SPEC_TEST != 0 { "spectest", "global_i32", .global_data.i32 = 666 }, { "spectest", "global_f32", .global_data.f32 = 0 }, { "spectest", "global_f64", .global_data.f64 = 0 }, { "test", "global-i32", .global_data.i32 = 0 }, { "test", "global-f32", .global_data.f32 = 0 }, -#endif { "env", "STACKTOP", .global_data.u32 = 0 }, { "env", "STACK_MAX", .global_data.u32 = 0 }, { "env", "ABORT", .global_data.u32 = 0 }, @@ -1340,5 +1132,3 @@ wasm_native_lookup_libc_builtin_global(const char *module_name, return false; } -#endif /* end of WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ - diff --git a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c index 59ee56baf..724fe61c9 100644 --- a/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c +++ b/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -20,24 +20,21 @@ wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); #define validate_app_addr(offset, size) \ wasm_runtime_validate_app_addr(module_inst, offset, size) +#define validate_native_addr(addr, size) \ + wasm_runtime_validate_native_addr(module_inst, addr, size) + #define addr_app_to_native(offset) \ wasm_runtime_addr_app_to_native(module_inst, offset) #define addr_native_to_app(ptr) \ wasm_runtime_addr_native_to_app(module_inst, ptr) -#define module_malloc(size) \ - wasm_runtime_module_malloc(module_inst, size) +#define module_malloc(size, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) #define module_free(offset) \ wasm_runtime_module_free(module_inst, offset) -#define WASI_CHECK_ERR() do { \ - if (err) { \ - return err; \ - } \ - } while (0) - typedef struct wasi_prestat_app { wasi_preopentype_t pr_type; uint32 pr_name_len; @@ -58,247 +55,193 @@ wasi_ctx_t wasm_runtime_get_wasi_ctx(wasm_module_inst_t module_inst); static wasi_errno_t -wasi_args_get(wasm_exec_env_t exec_env, - int32 argv_offset /* char ** */, - int32 argv_buf_offset /* char * */) +wasi_args_get(wasm_exec_env_t exec_env, int32 *argv_offsets, char *argv_buf) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); size_t argc, argv_buf_size, i; - uint64 total_size1, total_size2; - int32 *argv_app; - char *argv_buf_app; - wasi_errno_t err; char **argv; + uint64 total_size; + wasi_errno_t err; err = wasmtime_ssp_args_sizes_get(wasi_ctx->argv_environ, &argc, &argv_buf_size); - WASI_CHECK_ERR(); - - total_size1 = sizeof(char *) * ((uint64)argc + 1); - total_size2 = sizeof(char) * (uint64)argv_buf_size; - if (total_size1 >= UINT32_MAX - || !validate_app_addr(argv_offset, (uint32)total_size1) - || total_size2 >= UINT32_MAX - || !validate_app_addr(argv_buf_offset, (uint32)total_size2)) - return (wasi_errno_t)-1; - - argv = bh_malloc((uint32)total_size1); - if (!argv) - return (wasi_errno_t)-1; - - argv_app = (int32*)addr_app_to_native(argv_offset); - argv_buf_app= (char*)addr_app_to_native(argv_buf_offset); - - err = wasmtime_ssp_args_get(wasi_ctx->argv_environ, - argv, argv_buf_app); if (err) - goto fail; + return err; + + total_size = sizeof(int32) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !validate_native_addr(argv_offsets, (uint32)total_size) + || argv_buf_size >= UINT32_MAX + || !validate_native_addr(argv_buf, (uint32)argv_buf_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(char*) * ((uint64)argc + 1); + if (total_size >= UINT32_MAX + || !(argv = bh_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_args_get(wasi_ctx->argv_environ, argv, argv_buf); + if (err) { + bh_free(argv); + return err; + } for (i = 0; i < argc; i++) - argv_app[i] = addr_native_to_app(argv[i]); - argv_app[argc] = 0; + argv_offsets[i] = addr_native_to_app(argv[i]); + argv_offsets[argc] = 0; - /* success */ - err = 0; - -fail: bh_free(argv); - return err; + return 0; } static wasi_errno_t wasi_args_sizes_get(wasm_exec_env_t exec_env, - int32 argc_offset /* size_t * */, - int32 argv_buf_size_offset /* size_t * */) + uint32 *argc_app, uint32 *argv_buf_size_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); size_t argc, argv_buf_size; - uint32 *argc_app, *argv_buf_size_app; wasi_errno_t err; - if (!validate_app_addr(argc_offset, sizeof(uint32)) - || !validate_app_addr(argv_buf_size_offset, sizeof(uint32))) + if (!validate_native_addr(argc_app, sizeof(uint32)) + || !validate_native_addr(argv_buf_size_app, sizeof(uint32))) return (wasi_errno_t)-1; - argc_app = (uint32*)addr_app_to_native(argc_offset); - argv_buf_size_app = (uint32*)addr_app_to_native(argv_buf_size_offset); - err = wasmtime_ssp_args_sizes_get(wasi_ctx->argv_environ, &argc, &argv_buf_size); - WASI_CHECK_ERR(); - - *(uint32*)argc_app = (uint32)argc; - *(uint32*)argv_buf_size_app = (uint32)argv_buf_size; + if (err) + return err; + *argc_app = (uint32)argc; + *argv_buf_size_app = (uint32)argv_buf_size; return 0; } static wasi_errno_t wasi_clock_res_get(wasm_exec_env_t exec_env, - wasi_clockid_t clock_id, - int32 resolution_offset /* wasi_timestamp_t * */) + wasi_clockid_t clock_id, /* uint32 clock_id */ + wasi_timestamp_t *resolution /* uint64 *resolution */) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasi_timestamp_t resolution; - uint32 *resolution_app; - wasi_errno_t err; - if (!validate_app_addr(resolution_offset, sizeof(wasi_timestamp_t))) + if (!validate_native_addr(resolution, sizeof(wasi_timestamp_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_clock_res_get(clock_id, &resolution); - WASI_CHECK_ERR(); - - resolution_app = addr_app_to_native(resolution_offset); - - memcpy(resolution_app, &resolution, sizeof(wasi_timestamp_t)); - - return 0; + return wasmtime_ssp_clock_res_get(clock_id, resolution); } static wasi_errno_t wasi_clock_time_get(wasm_exec_env_t exec_env, - wasi_clockid_t clock_id, - wasi_timestamp_t precision, - int32 time_offset /*wasi_timestamp_t * */) + wasi_clockid_t clock_id, /* uint32 clock_id */ + wasi_timestamp_t precision, /* uint64 precision */ + wasi_timestamp_t *time /* uint64 *time */) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - wasi_timestamp_t time; - uint32 *time_app; - wasi_errno_t err; - if (!validate_app_addr(time_offset, sizeof(wasi_timestamp_t))) + if (!validate_native_addr(time, sizeof(wasi_timestamp_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_clock_time_get(clock_id, precision, &time); - WASI_CHECK_ERR(); - - time_app = addr_app_to_native(time_offset); - - memcpy(time_app, &time, sizeof(wasi_timestamp_t)); - - return 0; + return wasmtime_ssp_clock_time_get(clock_id, precision, time); } static wasi_errno_t wasi_environ_get(wasm_exec_env_t exec_env, - int32 environ_offset /* char ** */, - int32 environ_buf_offset /* char */) + int32 *environ_offsets, char *environ_buf) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); size_t environ_count, environ_buf_size, i; uint64 total_size; - int32 *environ_app; - char *environ_buff_app; - char **environ; + char **environs; wasi_errno_t err; err = wasmtime_ssp_environ_sizes_get(wasi_ctx->argv_environ, &environ_count, &environ_buf_size); - WASI_CHECK_ERR(); - - total_size = sizeof(char*) * ((uint64)environ_count + 1); - if (total_size >= UINT32_MAX - || !validate_app_addr(environ_offset, (uint32)total_size) - || environ_buf_size >= UINT32_MAX - || !validate_app_addr(environ_buf_offset, (uint32)environ_buf_size)) - return (wasi_errno_t)-1; - - environ_app = (int32*)addr_app_to_native(environ_offset); - environ_buff_app = (char*)addr_app_to_native(environ_buf_offset); - - environ = bh_malloc((uint32)total_size); - if (!environ) - return (wasi_errno_t)-1; - - err = wasmtime_ssp_environ_get(wasi_ctx->argv_environ, - environ, environ_buff_app); if (err) - goto fail; + return err; + + total_size = sizeof(int32) * ((uint64)environ_count + 1); + if (total_size >= UINT32_MAX + || !validate_native_addr(environ_offsets, (uint32)total_size) + || environ_buf_size >= UINT32_MAX + || !validate_native_addr(environ_buf, (uint32)environ_buf_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(char*) * (((uint64)environ_count + 1)); + + if (total_size >= UINT32_MAX + || !(environs = bh_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_environ_get(wasi_ctx->argv_environ, environs, environ_buf); + if (err) { + bh_free(environs); + return err; + } for (i = 0; i < environ_count; i++) - environ_app[i] = addr_native_to_app(environ[i]); - environ_app[environ_count] = 0; + environ_offsets[i] = addr_native_to_app(environs[i]); + environ_offsets[environ_count] = 0; - /* success */ - err = 0; - -fail: - bh_free(environ); - return err; + bh_free(environs); + return 0; } static wasi_errno_t wasi_environ_sizes_get(wasm_exec_env_t exec_env, - int32 environ_count_offset /* size_t * */, - int32 environ_buf_size_offset /* size_t * */) + uint32 *environ_count_app, uint32 *environ_buf_size_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); size_t environ_count, environ_buf_size; - uint32 *environ_count_app, *environ_buf_size_app; wasi_errno_t err; - if (!validate_app_addr(environ_count_offset, sizeof(uint32)) - || !validate_app_addr(environ_buf_size_offset, sizeof(uint32))) + if (!validate_native_addr(environ_count_app, sizeof(uint32)) + || !validate_native_addr(environ_buf_size_app, sizeof(uint32))) return (wasi_errno_t)-1; err = wasmtime_ssp_environ_sizes_get(wasi_ctx->argv_environ, &environ_count, &environ_buf_size); - WASI_CHECK_ERR(); + if (err) + return err; - environ_count_app = (uint32*)addr_app_to_native(environ_count_offset); - environ_buf_size_app = (uint32*)addr_app_to_native(environ_buf_size_offset); - - *(uint32*)environ_count_app = (uint32)environ_count; - *(uint32*)environ_buf_size_app = (uint32)environ_buf_size; + *environ_count_app = (uint32)environ_count; + *environ_buf_size_app = (uint32)environ_buf_size; return 0; } static wasi_errno_t wasi_fd_prestat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_prestat_t * */) + wasi_fd_t fd, wasi_prestat_app_t *prestat_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_prestat_app_t *prestat_app; wasi_prestat_t prestat; wasi_errno_t err; - if (!validate_app_addr(buf_offset, sizeof(wasi_prestat_app_t))) + if (!validate_native_addr(prestat_app, sizeof(wasi_prestat_app_t))) return (wasi_errno_t)-1; err = wasmtime_ssp_fd_prestat_get(wasi_ctx->prestats, fd, &prestat); - WASI_CHECK_ERR(); + if (err) + return err; - prestat_app = (wasi_prestat_app_t*)addr_app_to_native(buf_offset); prestat_app->pr_type = prestat.pr_type; prestat_app->pr_name_len = (uint32)prestat.u.dir.pr_name_len; - return 0; } static wasi_errno_t wasi_fd_prestat_dir_name(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* char * */, - uint32 path_len) + wasi_fd_t fd, char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path_app; - if (!validate_app_addr(path_offset, path_len)) - return (wasi_errno_t)-1; - - path_app = (char*)addr_app_to_native(path_offset); - return wasmtime_ssp_fd_prestat_dir_name(wasi_ctx->prestats, fd, - path_app, path_len); + return wasmtime_ssp_fd_prestat_dir_name(wasi_ctx->prestats, + fd, path, path_len); } static wasi_errno_t @@ -321,39 +264,30 @@ wasi_fd_datasync(wasm_exec_env_t exec_env, wasi_fd_t fd) static wasi_errno_t wasi_fd_pread(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_iovec_t * */, - uint32 iovs_len, - wasi_filesize_t offset, - int32 nread_offset /* size_t * */) + wasi_fd_t fd, iovec_app_t *iovec_app, uint32 iovs_len, + wasi_filesize_t offset, uint32 *nread_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; wasi_iovec_t *iovec, *iovec_begin; - iovec_app_t *iovec_app; - uint32 i; - size_t nread; - uint32 *nread_app; uint64 total_size; + size_t nread; + int32 mem; + uint32 i; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nread_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nread_app, (uint32)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) - return (wasi_errno_t)-1; - - iovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - if (!iovec_app) + || !validate_native_addr(iovec_app, (uint32)total_size)) return (wasi_errno_t)-1; total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&iovec_begin))) return (wasi_errno_t)-1; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + iovec = iovec_begin; for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { @@ -369,8 +303,7 @@ wasi_fd_pread(wasm_exec_env_t exec_env, if (err) goto fail; - nread_app = (uint32*)addr_app_to_native(nread_offset); - *(uint32*)nread_app = (uint32)nread; + *nread_app = (uint32)nread; /* success */ err = 0; @@ -382,44 +315,38 @@ fail: static wasi_errno_t wasi_fd_pwrite(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_ciovec_t * */, - uint32 iovs_len, - wasi_filesize_t offset, - int32 nwritten_offset /* size_t * */) + wasi_fd_t fd, const iovec_app_t *iovec_app, uint32 iovs_len, + wasi_filesize_t offset, uint32 *nwritten_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *ciovec_app; - uint32 i; - size_t nwritten; - uint32 *nwritten_app; uint64 total_size; + size_t nwritten; + int32 mem; + uint32 i; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nwritten_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void*)iovec_app, (uint32)total_size)) return (wasi_errno_t)-1; - ciovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) return (wasi_errno_t)-1; - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); - for (i = 0; i < iovs_len; i++, ciovec_app++, ciovec++) { - if (!validate_app_addr(ciovec_app->buf_offset, ciovec_app->buf_len)) { + ciovec = ciovec_begin; + + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - ciovec->buf = (char*)addr_app_to_native(ciovec_app->buf_offset); - ciovec->buf_len = ciovec_app->buf_len; + ciovec->buf = (char*)addr_app_to_native(iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; } err = wasmtime_ssp_fd_pwrite(wasi_ctx->curfds, fd, ciovec_begin, @@ -427,8 +354,7 @@ wasi_fd_pwrite(wasm_exec_env_t exec_env, if (err) goto fail; - nwritten_app = (uint32*)addr_app_to_native(nwritten_offset); - *(uint32*)nwritten_app = (uint32)nwritten; + *nwritten_app = (uint32)nwritten; /* success */ err = 0; @@ -440,36 +366,30 @@ fail: static wasi_errno_t wasi_fd_read(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_iovec_t * */, - uint32 iovs_len, - int32 nread_offset /* size_t * */) + wasi_fd_t fd, const iovec_app_t *iovec_app, uint32 iovs_len, + uint32 *nread_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; wasi_iovec_t *iovec, *iovec_begin; - iovec_app_t *iovec_app; - uint32 i; - size_t nread; - uint32 *nread_app; uint64 total_size; + size_t nread; + uint32 i; + int32 mem; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nread_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nread_app, (uint32)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void*)iovec_app, (uint32)total_size)) return (wasi_errno_t)-1; - iovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_iovec_t) * (uint64)iovs_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&iovec_begin))) return (wasi_errno_t)-1; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + iovec = iovec_begin; for (i = 0; i < iovs_len; i++, iovec_app++, iovec++) { if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { @@ -485,8 +405,7 @@ wasi_fd_read(wasm_exec_env_t exec_env, if (err) goto fail; - nread_app = (uint32*)addr_app_to_native(nread_offset); - *(uint32*)nread_app = (uint32)nread; + *nread_app = (uint32)nread; /* success */ err = 0; @@ -497,8 +416,7 @@ fail: } static wasi_errno_t -wasi_fd_renumber(wasm_exec_env_t exec_env, - wasi_fd_t from, wasi_fd_t to) +wasi_fd_renumber(wasm_exec_env_t exec_env, wasi_fd_t from, wasi_fd_t to) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); @@ -509,83 +427,60 @@ wasi_fd_renumber(wasm_exec_env_t exec_env, static wasi_errno_t wasi_fd_seek(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_filedelta_t offset, - wasi_whence_t whence, - int32 newoffset_offset /* wasi_filesize_t * */) + wasi_fd_t fd, wasi_filedelta_t offset, wasi_whence_t whence, + wasi_filesize_t *newoffset) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filesize_t newoffset, *newoffset_app; - wasi_errno_t err; - if (!validate_app_addr(newoffset_offset, sizeof(wasi_filesize_t))) + if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_seek(wasi_ctx->curfds, fd, - offset, whence, &newoffset); - WASI_CHECK_ERR(); - - newoffset_app = (wasi_filesize_t*)addr_app_to_native(newoffset_offset); - *newoffset_app = newoffset; - - return 0; + return wasmtime_ssp_fd_seek(wasi_ctx->curfds, fd, + offset, whence, newoffset); } static wasi_errno_t wasi_fd_tell(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 newoffset_offset /* wasi_filesize_t * */) + wasi_fd_t fd, wasi_filesize_t *newoffset) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filesize_t newoffset, *newoffset_app; - wasi_errno_t err; - if (!validate_app_addr(newoffset_offset, sizeof(wasi_filesize_t))) + if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_tell(wasi_ctx->curfds, fd, &newoffset); - WASI_CHECK_ERR(); - - newoffset_app = (wasi_filesize_t*)addr_app_to_native(newoffset_offset); - *newoffset_app = newoffset; - - return 0; + return wasmtime_ssp_fd_tell(wasi_ctx->curfds, fd, newoffset); } static wasi_errno_t wasi_fd_fdstat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_fdstat_t * */) + wasi_fd_t fd, wasi_fdstat_t *fdstat_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_fdstat_t fdstat, *fdstat_app; + wasi_fdstat_t fdstat; wasi_errno_t err; - if (!validate_app_addr(buf_offset, sizeof(wasi_fdstat_t))) + if (!validate_native_addr(fdstat_app, sizeof(wasi_fdstat_t))) return (wasi_errno_t)-1; err = wasmtime_ssp_fd_fdstat_get(wasi_ctx->curfds, fd, &fdstat); - WASI_CHECK_ERR(); + if (err) + return err; - fdstat_app = (wasi_fdstat_t*)addr_app_to_native(buf_offset); memcpy(fdstat_app, &fdstat, sizeof(wasi_fdstat_t)); - return 0; } static wasi_errno_t wasi_fd_fdstat_set_flags(wasm_exec_env_t exec_env, - wasi_fd_t fd, - wasi_fdflags_t flags) + wasi_fd_t fd, wasi_fdflags_t flags) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); return wasmtime_ssp_fd_fdstat_set_flags(wasi_ctx->curfds, fd, flags); - } static wasi_errno_t @@ -611,44 +506,39 @@ wasi_fd_sync(wasm_exec_env_t exec_env, wasi_fd_t fd) } static wasi_errno_t -wasi_fd_write(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 iovs_offset /* const wasi_ciovec_t * */, - uint32 iovs_len, - int32 nwritten_offset /* size_t * */) +wasi_fd_write(wasm_exec_env_t exec_env, wasi_fd_t fd, + const iovec_app_t *iovec_app, uint32 iovs_len, + uint32 *nwritten_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *ciovec_app; - uint32 i; - size_t nwritten; - uint32 *nwritten_app; uint64 total_size; + size_t nwritten; + int32 mem; + uint32 i; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)iovs_len; - if (!validate_app_addr(nwritten_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(iovs_offset, (uint32)total_size)) + || !validate_native_addr((void*)iovec_app, (uint32)total_size)) return (wasi_errno_t)-1; - ciovec_app = (iovec_app_t*)addr_app_to_native(iovs_offset); - total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) return (wasi_errno_t)-1; - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); - for (i = 0; i < iovs_len; i++, ciovec_app++, ciovec++) { - if (!validate_app_addr(ciovec_app->buf_offset, ciovec_app->buf_len)) { + ciovec = ciovec_begin; + + for (i = 0; i < iovs_len; i++, iovec_app++, ciovec++) { + if (!validate_app_addr(iovec_app->buf_offset, iovec_app->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - ciovec->buf = (char*)addr_app_to_native(ciovec_app->buf_offset); - ciovec->buf_len = ciovec_app->buf_len; + ciovec->buf = (char*)addr_app_to_native(iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; } err = wasmtime_ssp_fd_write(wasi_ctx->curfds, fd, @@ -656,8 +546,7 @@ wasi_fd_write(wasm_exec_env_t exec_env, if (err) goto fail; - nwritten_app = (uint32*)addr_app_to_native(nwritten_offset); - *(uint32*)nwritten_app = (uint32)nwritten; + *nwritten_app = (uint32)nwritten; /* success */ err = 0; @@ -694,18 +583,10 @@ wasi_fd_allocate(wasm_exec_env_t exec_env, static wasi_errno_t wasi_path_create_directory(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len) + wasi_fd_t fd, const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - - if (!validate_app_addr(path_offset, path_len)) - return (wasi_errno_t)-1; - - path = (char*)addr_app_to_native(path_offset); return wasmtime_ssp_path_create_directory(wasi_ctx->curfds, fd, path, path_len); @@ -715,22 +596,12 @@ static wasi_errno_t wasi_path_link(wasm_exec_env_t exec_env, wasi_fd_t old_fd, wasi_lookupflags_t old_flags, - int32 old_path_offset /* const char * */, - uint32 old_path_len, + const char *old_path, uint32 old_path_len, wasi_fd_t new_fd, - int32 new_path_offset /* const char * */, - uint32 new_path_len) + const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; - - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) - return (wasi_errno_t)-1; - - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); return wasmtime_ssp_path_link(wasi_ctx->curfds, wasi_ctx->prestats, old_fd, old_flags, old_path, old_path_len, @@ -741,156 +612,107 @@ static wasi_errno_t wasi_path_open(wasm_exec_env_t exec_env, wasi_fd_t dirfd, wasi_lookupflags_t dirflags, - int32 path_offset /* const char * */, - uint32 path_len, + const char *path, uint32 path_len, wasi_oflags_t oflags, wasi_rights_t fs_rights_base, wasi_rights_t fs_rights_inheriting, wasi_fdflags_t fs_flags, - int32 fd_offset /* wasi_fd_t * */) + wasi_fd_t *fd_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - wasi_fd_t fd = (wasi_fd_t)-1; - uint32 *fd_app; + wasi_fd_t fd = -1; /* set fd_app -1 if path open failed */ wasi_errno_t err; - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(fd_offset, 1)) + if (!validate_native_addr(fd_app, sizeof(wasi_fd_t))) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - err = wasmtime_ssp_path_open(wasi_ctx->curfds, - dirfd, dirflags, - path, path_len, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - &fd); + dirfd, dirflags, + path, path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + &fd); - fd_app = (wasi_fd_t*)addr_app_to_native(fd_offset); - *fd_app = (uint32)fd; - - WASI_CHECK_ERR(); - - return 0; + *fd_app = fd; + return err; } static wasi_errno_t wasi_fd_readdir(wasm_exec_env_t exec_env, wasi_fd_t fd, - int32 buf_offset /* void *buf */, - uint32 buf_len, + void *buf, uint32 buf_len, wasi_dircookie_t cookie, - int32 bufused_offset /* size_t * */) + uint32 *bufused_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - void *buf; size_t bufused; - uint32 *bufused_app; wasi_errno_t err; - if (!validate_app_addr(buf_offset, buf_len) - || !validate_app_addr(bufused_offset, sizeof(uint32))) + if (!validate_native_addr(bufused_app, sizeof(uint32))) return (wasi_errno_t)-1; - buf = (void*)addr_app_to_native(buf_offset); - err = wasmtime_ssp_fd_readdir(wasi_ctx->curfds, fd, buf, buf_len, cookie, &bufused); - WASI_CHECK_ERR(); - - bufused_app = (uint32*)addr_app_to_native(bufused_offset); - *(uint32*)bufused_app = (uint32)bufused; + if (err) + return err; + *bufused_app = (uint32)bufused; return 0; } static wasi_errno_t wasi_path_readlink(wasm_exec_env_t exec_env, wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len, - int32 buf_offset /* char * */, - uint32 buf_len, - int32 bufused_offset /* size_t * */) + const char *path, uint32 path_len, + char *buf, uint32 buf_len, + uint32 *bufused_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path, *buf; size_t bufused; - uint32 *bufused_app; wasi_errno_t err; - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(buf_offset, buf_len) - || !validate_app_addr(bufused_offset, sizeof(uint32))) + if (!validate_native_addr(bufused_app, sizeof(uint32))) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - buf = (char*)addr_app_to_native(buf_offset); - err = wasmtime_ssp_path_readlink(wasi_ctx->curfds, fd, - path, path_len, buf, - buf_len, &bufused); - WASI_CHECK_ERR(); - - bufused_app = (uint32*)addr_app_to_native(bufused_offset); - *(uint32*)bufused_app = (uint32)bufused; + path, path_len, + buf, buf_len, &bufused); + if (err) + return err; + *bufused_app = (uint32)bufused; return 0; } static wasi_errno_t wasi_path_rename(wasm_exec_env_t exec_env, - wasi_fd_t old_fd, - int32 old_path_offset /* const char * */, - uint32 old_path_len, - wasi_fd_t new_fd, - int32 new_path_offset /* const char * */, - uint32 new_path_len) + wasi_fd_t old_fd, const char *old_path, uint32 old_path_len, + wasi_fd_t new_fd, const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) - return (wasi_errno_t)-1; - - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); - - return wasmtime_ssp_path_rename(wasi_ctx->curfds, old_fd, - old_path, old_path_len, - new_fd, new_path, - new_path_len); + return wasmtime_ssp_path_rename(wasi_ctx->curfds, + old_fd, old_path, old_path_len, + new_fd, new_path, new_path_len); } static wasi_errno_t wasi_fd_filestat_get(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 buf_offset /* wasi_filestat_t * */) + wasi_fd_t fd, wasi_filestat_t *filestat) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_filestat_t filestat, *filestat_app; - wasi_errno_t err; - if (!validate_app_addr(buf_offset, sizeof(wasi_filestat_t))) + if (!validate_native_addr(filestat, sizeof(wasi_filestat_t))) return (wasi_errno_t)-1; - err = wasmtime_ssp_fd_filestat_get(wasi_ctx->curfds, fd, &filestat); - WASI_CHECK_ERR(); - - filestat_app = (wasi_filestat_t*)addr_app_to_native(buf_offset); - memcpy(filestat_app, &filestat, sizeof(wasi_filestat_t)); - - return 0; + return wasmtime_ssp_fd_filestat_get(wasi_ctx->curfds, fd, filestat); } static wasi_errno_t @@ -922,50 +744,30 @@ static wasi_errno_t wasi_path_filestat_get(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_lookupflags_t flags, - int32 path_offset /* const char * */, - uint32 path_len, - int32 buf_offset /* wasi_filestat_t * */) + const char *path, uint32 path_len, + wasi_filestat_t *filestat) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - wasi_filestat_t filestat, *filestat_app; - wasi_errno_t err; - if (!validate_app_addr(path_offset, path_len) - || !validate_app_addr(buf_offset, sizeof(wasi_filestat_t))) + if (!validate_native_addr(filestat, sizeof(wasi_filestat_t))) return (wasi_errno_t)-1; - path = (char*)addr_app_to_native(path_offset); - - err = wasmtime_ssp_path_filestat_get(wasi_ctx->curfds, fd, - flags, path, path_len, &filestat); - WASI_CHECK_ERR(); - - filestat_app = (wasi_filestat_t*)addr_app_to_native(buf_offset); - memcpy(filestat_app, &filestat, sizeof(wasi_filestat_t)); - - return 0; + return wasmtime_ssp_path_filestat_get(wasi_ctx->curfds, fd, + flags, path, path_len, filestat); } static wasi_errno_t wasi_path_filestat_set_times(wasm_exec_env_t exec_env, wasi_fd_t fd, wasi_lookupflags_t flags, - int32 path_offset /* const char * */, - uint32 path_len, + const char *path, uint32 path_len, wasi_timestamp_t st_atim, wasi_timestamp_t st_mtim, wasi_fstflags_t fstflags) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - - if (!validate_app_addr(path_offset, path_len)) - return (wasi_errno_t)-1; - - path = (char*)addr_app_to_native(path_offset); return wasmtime_ssp_path_filestat_set_times(wasi_ctx->curfds, fd, flags, path, path_len, @@ -974,93 +776,58 @@ wasi_path_filestat_set_times(wasm_exec_env_t exec_env, static wasi_errno_t wasi_path_symlink(wasm_exec_env_t exec_env, - int32 old_path_offset /* const char * */, - uint32 old_path_len, - wasi_fd_t fd, - int32 new_path_offset /* const char * */, - uint32 new_path_len) + const char *old_path, uint32 old_path_len, + wasi_fd_t fd, const char *new_path, uint32 new_path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *old_path, *new_path; - - if (!validate_app_addr(old_path_offset, old_path_len) - || !validate_app_addr(new_path_offset, new_path_len)) - return (wasi_errno_t)-1; - - old_path = (char*)addr_app_to_native(old_path_offset); - new_path = (char*)addr_app_to_native(new_path_offset); return wasmtime_ssp_path_symlink(wasi_ctx->curfds, wasi_ctx->prestats, - old_path, old_path_len, fd, new_path, - new_path_len); + old_path, old_path_len, fd, + new_path, new_path_len); } static wasi_errno_t wasi_path_unlink_file(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len) + wasi_fd_t fd, const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - - if (!validate_app_addr(path_offset, path_len)) - return (wasi_errno_t)-1; - - path = (char*)addr_app_to_native(path_offset); return wasmtime_ssp_path_unlink_file(wasi_ctx->curfds, fd, path, path_len); } static wasi_errno_t wasi_path_remove_directory(wasm_exec_env_t exec_env, - wasi_fd_t fd, - int32 path_offset /* const char * */, - uint32 path_len) + wasi_fd_t fd, const char *path, uint32 path_len) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - char *path; - - if (!validate_app_addr(path_offset, path_len)) - return (wasi_errno_t)-1; - - path = (char*)addr_app_to_native(path_offset); return wasmtime_ssp_path_remove_directory(wasi_ctx->curfds, fd, path, path_len); } static wasi_errno_t wasi_poll_oneoff(wasm_exec_env_t exec_env, - int32 in_offset /* const wasi_subscription_t * */, - int32 out_offset /* wasi_event_t * */, - uint32 nsubscriptions, - int32 nevents_offset /* size_t * */) + const wasi_subscription_t *in, wasi_event_t *out, + uint32 nsubscriptions, uint32 *nevents_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - wasi_subscription_t *in; - wasi_event_t *out; size_t nevents; - uint32 *nevents_app; wasi_errno_t err; - if (!validate_app_addr(in_offset, sizeof(wasi_subscription_t)) - || !validate_app_addr(out_offset, sizeof(wasi_event_t)) - || !validate_app_addr(nevents_offset, sizeof(uint32))) + if (!validate_native_addr((void*)in, sizeof(wasi_subscription_t)) + || !validate_native_addr(out, sizeof(wasi_event_t)) + || !validate_native_addr(nevents_app, sizeof(uint32))) return (wasi_errno_t)-1; - in = (wasi_subscription_t*)addr_app_to_native(in_offset); - out = (wasi_event_t*)addr_app_to_native(out_offset); - - err = wasmtime_ssp_poll_oneoff(wasi_ctx->curfds, in, out, nsubscriptions, &nevents); - WASI_CHECK_ERR(); - - nevents_app = (uint32*)addr_app_to_native(nevents_offset); - *(uint32*)nevents_app = (uint32)nevents; + err = wasmtime_ssp_poll_oneoff(wasi_ctx->curfds, in, out, + nsubscriptions, &nevents); + if (err) + return err; + *nevents_app = (uint32)nevents; return 0; } @@ -1082,79 +849,59 @@ wasi_proc_raise(wasm_exec_env_t exec_env, wasi_signal_t sig) } static wasi_errno_t -wasi_random_get(wasm_exec_env_t exec_env, - int32 buf_offset /* void * */, - uint32 buf_len) +wasi_random_get(wasm_exec_env_t exec_env, void *buf, uint32 buf_len) { - wasm_module_inst_t module_inst = get_module_inst(exec_env); - void *buf; - - if (!validate_app_addr(buf_offset, buf_len)) - return (wasi_errno_t)-1; - - buf = (void*)addr_app_to_native(buf_offset); - return wasmtime_ssp_random_get(buf, buf_len); } static wasi_errno_t wasi_sock_recv(wasm_exec_env_t exec_env, wasi_fd_t sock, - int32 ri_data_offset /* const wasi_iovec_t * */, - uint32 ri_data_len, + iovec_app_t *ri_data, uint32 ri_data_len, wasi_riflags_t ri_flags, - int32 ro_datalen_offset /* size_t * */, - int32 ro_flags_offset /* wasi_roflags_t * */) + uint32 *ro_datalen_app, + wasi_roflags_t *ro_flags) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); wasi_iovec_t *iovec, *iovec_begin; - int32 mem; - iovec_app_t *ri_data_app; - uint32 i; - size_t ro_datalen; - wasi_roflags_t ro_flags; - uint32 *ro_datalen_app, *ro_flags_app; uint64 total_size; + size_t ro_datalen; + int32 mem; + uint32 i; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)ri_data_len; - if (!validate_app_addr(ro_datalen_offset, (uint32)sizeof(uint32)) - || !validate_app_addr(ro_flags_offset, (uint32)sizeof(uint32)) + if (!validate_native_addr(ro_datalen_app, (uint32)sizeof(uint32)) + || !validate_native_addr(ro_flags, (uint32)sizeof(wasi_roflags_t)) || total_size >= UINT32_MAX - || !validate_app_addr(ri_data_offset, (uint32)total_size)) + || !validate_native_addr(ri_data, (uint32)total_size)) return (wasi_errno_t)-1; - ri_data_app = (iovec_app_t*)addr_app_to_native(ri_data_offset); - total_size = sizeof(wasi_iovec_t) * (uint64)ri_data_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&iovec_begin))) return (wasi_errno_t)-1; - iovec = iovec_begin = (wasi_iovec_t*)addr_app_to_native(mem); + iovec = iovec_begin; - for (i = 0; i < ri_data_len; i++, ri_data_app++, iovec++) { - if (!validate_app_addr(ri_data_app->buf_offset, ri_data_app->buf_len)) { + for (i = 0; i < ri_data_len; i++, ri_data++, iovec++) { + if (!validate_app_addr(ri_data->buf_offset, ri_data->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - iovec->buf = (void*)addr_app_to_native(ri_data_app->buf_offset); - iovec->buf_len = ri_data_app->buf_len; + iovec->buf = (void*)addr_app_to_native(ri_data->buf_offset); + iovec->buf_len = ri_data->buf_len; } err = wasmtime_ssp_sock_recv(wasi_ctx->curfds, sock, iovec_begin, ri_data_len, ri_flags, &ro_datalen, - &ro_flags); + ro_flags); if (err) goto fail; - ro_datalen_app = (uint32*)addr_app_to_native(ro_datalen_offset); - ro_flags_app = (uint32*)addr_app_to_native(ro_flags_offset); - *(uint32*)ro_datalen_app = (uint32)ro_datalen; - *(uint32*)ro_flags_app = (uint32)ro_flags; /* success */ err = 0; @@ -1167,44 +914,39 @@ fail: static wasi_errno_t wasi_sock_send(wasm_exec_env_t exec_env, wasi_fd_t sock, - int32 si_data_offset /* const wasi_ciovec_t * */, - uint32 si_data_len, + const iovec_app_t *si_data, uint32 si_data_len, wasi_siflags_t si_flags, - int32 so_datalen_offset /* size_t * */) + uint32 *so_datalen_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); - int32 mem; wasi_ciovec_t *ciovec, *ciovec_begin; - iovec_app_t *si_data_app; - uint32 i; - size_t so_datalen; - uint32 *so_datalen_app; uint64 total_size; + size_t so_datalen; + int32 mem; + uint32 i; wasi_errno_t err; total_size = sizeof(iovec_app_t) * (uint64)si_data_len; - if (!validate_app_addr(so_datalen_offset, sizeof(uint32)) + if (!validate_native_addr(so_datalen_app, sizeof(uint32)) || total_size >= UINT32_MAX - || !validate_app_addr(si_data_offset, (uint32)total_size)) + || !validate_native_addr((void*)si_data, (uint32)total_size)) return (wasi_errno_t)-1; - si_data_app = (iovec_app_t*)addr_app_to_native(si_data_offset); - total_size = sizeof(wasi_ciovec_t) * (uint64)si_data_len; if (total_size >= UINT32_MAX - || !(mem = module_malloc((uint32)total_size))) + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) return (wasi_errno_t)-1; - ciovec_begin = ciovec = (wasi_ciovec_t*)addr_app_to_native(mem); + ciovec = ciovec_begin; - for (i = 0; i < si_data_len; i++, si_data_app++, ciovec++) { - if (!validate_app_addr(si_data_app->buf_offset, si_data_app->buf_len)) { + for (i = 0; i < si_data_len; i++, si_data++, ciovec++) { + if (!validate_app_addr(si_data->buf_offset, si_data->buf_len)) { err = (wasi_errno_t)-1; goto fail; } - ciovec->buf = (char*)addr_app_to_native(si_data_app->buf_offset); - ciovec->buf_len = si_data_app->buf_len; + ciovec->buf = (char*)addr_app_to_native(si_data->buf_offset); + ciovec->buf_len = si_data->buf_len; } err = wasmtime_ssp_sock_send(wasi_ctx->curfds, sock, @@ -1213,8 +955,7 @@ wasi_sock_send(wasm_exec_env_t exec_env, if (err) goto fail; - so_datalen_app = (uint32*)addr_app_to_native(so_datalen_offset); - *(uint32*)so_datalen_app = (uint32)so_datalen; + *so_datalen_app = (uint32)so_datalen; /* success */ err = 0; @@ -1240,81 +981,61 @@ wasi_sched_yield(wasm_exec_env_t exec_env) return wasmtime_ssp_sched_yield(); } -#define REG_NATIVE_FUNC(func_name) \ - { "wasi_unstable", #func_name, wasi_##func_name } +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, wasi_##func_name, signature } -typedef struct WASMNativeFuncDef { - const char *module_name; - const char *func_name; - void *func_ptr; -} WASMNativeFuncDef; - -static WASMNativeFuncDef native_func_defs[] = { - REG_NATIVE_FUNC(args_get), - REG_NATIVE_FUNC(args_sizes_get), - REG_NATIVE_FUNC(clock_res_get), - REG_NATIVE_FUNC(clock_time_get), - REG_NATIVE_FUNC(environ_get), - REG_NATIVE_FUNC(environ_sizes_get), - REG_NATIVE_FUNC(fd_prestat_get), - REG_NATIVE_FUNC(fd_prestat_dir_name), - REG_NATIVE_FUNC(fd_close), - REG_NATIVE_FUNC(fd_datasync), - REG_NATIVE_FUNC(fd_pread), - REG_NATIVE_FUNC(fd_pwrite), - REG_NATIVE_FUNC(fd_read), - REG_NATIVE_FUNC(fd_renumber), - REG_NATIVE_FUNC(fd_seek), - REG_NATIVE_FUNC(fd_tell), - REG_NATIVE_FUNC(fd_fdstat_get), - REG_NATIVE_FUNC(fd_fdstat_set_flags), - REG_NATIVE_FUNC(fd_fdstat_set_rights), - REG_NATIVE_FUNC(fd_sync), - REG_NATIVE_FUNC(fd_write), - REG_NATIVE_FUNC(fd_advise), - REG_NATIVE_FUNC(fd_allocate), - REG_NATIVE_FUNC(path_create_directory), - REG_NATIVE_FUNC(path_link), - REG_NATIVE_FUNC(path_open), - REG_NATIVE_FUNC(fd_readdir), - REG_NATIVE_FUNC(path_readlink), - REG_NATIVE_FUNC(path_rename), - REG_NATIVE_FUNC(fd_filestat_get), - REG_NATIVE_FUNC(fd_filestat_set_times), - REG_NATIVE_FUNC(fd_filestat_set_size), - REG_NATIVE_FUNC(path_filestat_get), - REG_NATIVE_FUNC(path_filestat_set_times), - REG_NATIVE_FUNC(path_symlink), - REG_NATIVE_FUNC(path_unlink_file), - REG_NATIVE_FUNC(path_remove_directory), - REG_NATIVE_FUNC(poll_oneoff), - REG_NATIVE_FUNC(proc_exit), - REG_NATIVE_FUNC(proc_raise), - REG_NATIVE_FUNC(random_get), - REG_NATIVE_FUNC(sock_recv), - REG_NATIVE_FUNC(sock_send), - REG_NATIVE_FUNC(sock_shutdown), - REG_NATIVE_FUNC(sched_yield), +static NativeSymbol native_symbols_libc_wasi[] = { + REG_NATIVE_FUNC(args_get, "(**)i"), + REG_NATIVE_FUNC(args_sizes_get, "(**)i"), + REG_NATIVE_FUNC(clock_res_get, "(i*)i"), + REG_NATIVE_FUNC(clock_time_get, "(iI*)i"), + REG_NATIVE_FUNC(environ_get, "(**)i"), + REG_NATIVE_FUNC(environ_sizes_get, "(**)i"), + REG_NATIVE_FUNC(fd_prestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_prestat_dir_name, "(i*~)i"), + REG_NATIVE_FUNC(fd_close, "(i)i"), + REG_NATIVE_FUNC(fd_datasync, "(i)i"), + REG_NATIVE_FUNC(fd_pread, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_pwrite, "(i*iI*)i"), + REG_NATIVE_FUNC(fd_read, "(i*i*)i"), + REG_NATIVE_FUNC(fd_renumber, "(ii)i"), + REG_NATIVE_FUNC(fd_seek, "(iIi*)i"), + REG_NATIVE_FUNC(fd_tell, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_fdstat_set_flags, "(ii)i"), + REG_NATIVE_FUNC(fd_fdstat_set_rights, "(iII)i"), + REG_NATIVE_FUNC(fd_sync, "(i)i"), + REG_NATIVE_FUNC(fd_write, "(i*i*)i"), + REG_NATIVE_FUNC(fd_advise, "(iIIi)i"), + REG_NATIVE_FUNC(fd_allocate, "(iII)i"), + REG_NATIVE_FUNC(path_create_directory, "(i*~)i"), + REG_NATIVE_FUNC(path_link, "(ii*~i*~)i"), + REG_NATIVE_FUNC(path_open, "(ii*~iIIi*)i"), + REG_NATIVE_FUNC(fd_readdir, "(i*~I*)i"), + REG_NATIVE_FUNC(path_readlink, "(i*~*~*)i"), + REG_NATIVE_FUNC(path_rename, "(i*~i*~)i"), + REG_NATIVE_FUNC(fd_filestat_get, "(i*)i"), + REG_NATIVE_FUNC(fd_filestat_set_times, "(iIIi)i"), + REG_NATIVE_FUNC(fd_filestat_set_size, "(iI)i"), + REG_NATIVE_FUNC(path_filestat_get, "(ii*~*)i"), + REG_NATIVE_FUNC(path_filestat_set_times, "(ii*~IIi)i"), + REG_NATIVE_FUNC(path_symlink, "(*~i*~)i"), + REG_NATIVE_FUNC(path_unlink_file, "(i*~)i"), + REG_NATIVE_FUNC(path_remove_directory, "(i*~)i"), + REG_NATIVE_FUNC(poll_oneoff, "(**i*)i"), + REG_NATIVE_FUNC(proc_exit, "(i)"), + REG_NATIVE_FUNC(proc_raise, "(i)i"), + REG_NATIVE_FUNC(random_get, "(*~)i"), + REG_NATIVE_FUNC(sock_recv, "(i*ii**)i"), + REG_NATIVE_FUNC(sock_send, "(i*ii*)i"), + REG_NATIVE_FUNC(sock_shutdown, "(ii)i"), + REG_NATIVE_FUNC(sched_yield, "()i"), }; -void * -wasm_native_lookup_libc_wasi_func(const char *module_name, - const char *func_name) +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis) { - uint32 size = sizeof(native_func_defs) / sizeof(WASMNativeFuncDef); - WASMNativeFuncDef *func_def = native_func_defs; - WASMNativeFuncDef *func_def_end = func_def + size; - - if (!module_name || !func_name) - return NULL; - - while (func_def < func_def_end) { - if (!strcmp(func_def->module_name, module_name) - && !strcmp(func_def->func_name, func_name)) - return (void*) (uintptr_t) func_def->func_ptr; - func_def++; - } - - return NULL; + *p_libc_wasi_apis = native_symbols_libc_wasi; + return sizeof(native_symbols_libc_wasi) / sizeof(NativeSymbol); } diff --git a/core/shared/include/config.h b/core/shared/include/config.h index ed00e2f89..bb98980ee 100644 --- a/core/shared/include/config.h +++ b/core/shared/include/config.h @@ -80,6 +80,10 @@ enum { #define WASM_ENABLE_BASE_LIB 0 #endif +#ifndef WASM_ENABLE_APP_FRAMEWORK +#define WASM_ENABLE_APP_FRAMEWORK 0 +#endif + /* WASM log system */ #ifndef WASM_ENABLE_LOG #define WASM_ENABLE_LOG 1 @@ -148,8 +152,8 @@ enum { #define APP_THREAD_STACK_SIZE_MIN (16 * 1024) #define APP_THREAD_STACK_SIZE_MAX (256 * 1024) #else -#define APP_THREAD_STACK_SIZE_DEFAULT (4 * 1024) -#define APP_THREAD_STACK_SIZE_MIN (2 * 1024) +#define APP_THREAD_STACK_SIZE_DEFAULT (6 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (4 * 1024) #define APP_THREAD_STACK_SIZE_MAX (256 * 1024) #endif diff --git a/core/shared/platform/zephyr/bh_platform.c b/core/shared/platform/zephyr/bh_platform.c index 27c35ff34..f2bc2e380 100755 --- a/core/shared/platform/zephyr/bh_platform.c +++ b/core/shared/platform/zephyr/bh_platform.c @@ -91,7 +91,7 @@ bh_mprotect(void *addr, uint32 size, int prot) void bh_dcache_flush() { -#if defined(CONFIG_CPU_CORTEX_M7) +#if defined(CONFIG_CPU_CORTEX_M7) && defined(CONFIG_ARM_MPU) uint32 key; key = irq_lock(); SCB_CleanDCache(); diff --git a/doc/embed_wamr.md b/doc/embed_wamr.md index 351dd351c..3a2c530aa 100644 --- a/doc/embed_wamr.md +++ b/doc/embed_wamr.md @@ -1,10 +1,13 @@ -Embed WAMR into software production +Embedding WAMR guideline ===================================== -![WAMR embed diagram](./pics/embed.PNG "WAMR embed architecture diagram") + +**Note**: All the embedding APIs supported by the runtime are defined under folder [core/iwasm/include](../core/iwasm/include). The API details are available in the header files. + +## The initialization procedure + -A typical WAMR API usage is shown below (some return value checks are ignored): ``` C static char global_heap_buf[512 * 1024]; @@ -13,27 +16,162 @@ A typical WAMR API usage is shown below (some return value checks are ignored): wasm_module_inst_t module_inst; wasm_function_inst_t func; wasm_exec_env_t exec_env; - uint32 argv[2], size, stack_size = 8092, heap_size = 8092; + uint32 size, stack_size = 8092, heap_size = 8092; + // all the WAMR heap and WASM applications are limited in this buffer bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)); + wasm_runtime_init(); + // read WASM file into a memory buffer buffer = read_wasm_binary_to_buffer(…, &size); + + // parse the WASM file from buffer and create a WASM module module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); - module_inst = wasm_runtime_instantiate(module, stack_size, heap_size, - error_buf, sizeof(error_buf)); - func = wasm_runtime_lookup_function(module_inst, "fib", "(i32)i32"); + + // create an instance of the WASM module (WASM linear memory is ready) + module_inst = wasm_runtime_instantiate(module, + stack_size, + heap_size, + error_buf, + sizeof(error_buf)); +``` + + + + + +## Native calls WASM functions and passes parameters + +After a module is instantiated, the runtime native can lookup WASM functions by the names and call them. + +```c + unit32 argv[2]; + + // lookup a WASM function by its name + func = wasm_runtime_lookup_function(module_inst, "fib", NULL); + + // creat a excution environment which can be used by executing WASM functions exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + // arguments are always transferred in 32 bits element argv[0] = 8; - if (wasm_runtime_call_wasm(exec_env, func, 1, argv_buf) ) { + + // call the WASM function + if (wasm_runtime_call_wasm(exec_env, func, 1, argv) ) { + /* the return value is stored in argv[0] */ printf("fib function return: %d\n", argv[0]); } else { printf("%s\n", wasm_runtime_get_exception(module_inst)); } +``` + + +The parameters are transferred in an array of 32 bits elements. For parameters that occupy 4 or fewer bytes, each parameter can be a single array element. For parameters in types like double or int64, each parameter will take two array elements. The function return value will be sent back in the first one or two elements of the array according to the value type. See the sample code below: + +```c + unit32 argv[6]; + char arg1 = 'a'; + int arg2 = 10; + double arg3 = 1.0; + int 64 arg4 = 100; + double ret; + + argv[0] = arg1; + argv[1] = arg2; + + // use memory copy for 8 bytes parameters rather than + // *(double*)(&argv[2]) = arg3 here because some archs + // like ARM, MIPS requires address is 8 aligned. + // Or use the aligned malloc or compiler align attribute + // to ensure the array address is 8 bytes aligned + memcpy(&argv[2], &arg3, sizeof(arg3)); + memcpy(&argv[4], &arg4, sizeof(arg4)); + + // + // attention: the arg number is 6 here since both + // arg3 and arg4 each takes 2 elements + // + wasm_runtime_call_wasm(exec_env, func, 6, argv); + + // if the return value is type of 8 bytes, it takes + // the first two array elements + memcpy(&ret, &argv[0], sizeof(ret)); + +``` + + + +## Pass buffer to WASM function + + + +If we need to transfer a buffer to WASM function, we can pass the buffer address through a parameter. **Attention**: The sandbox will forbid the WASM code to access outside memory, we must **allocate the buffer from WASM instance's own memory space and pass the buffer address in instance's space (not the runtime native address)**. + + + +There are two runtime APIs available for this purpose. + +```c +/* +* description: malloc a buffer from instance's private memory space. +* +* return: the buffer address in instance's memory space (pass to the WASM funciton) +* p_native_addr: return the native address of allocated memory +* size: the buffer size to allocate +*/ +int32_t +wasm_runtime_module_malloc(wasm_module_inst_t module_inst, + uint32_t size, + void **p_native_addr); + +/* +* description: malloc a buffer from instance's private memory space, +* and copy the data from another native buffer to it. +* return: the buffer address in instance's memory space (pass to the WASM funciton) +* src: the native buffer address +* size: the size of buffer to be allocated and copy data +*/ +int32 +wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, + const char *src, + uint32 size); +``` + + + +Usage sample: + +```c +char * buffer = NULL; +int32_t buffer_for_wasm; + +buffer_for_wasm = wasm_runtime_module_malloc(module_inst, 100, &buffer); +if(buffer_for_wasm != 0) +{ + unit32 argv[2]; + strncpy(buffer, "hello", 100); // use native address for accessing in runtime + argv[0] = buffer_for_wasm; // pass the buffer address for WASM space. + argv[1] = 100; // the size of buffer + wasm_runtime_call_wasm(exec_env, func, 2, argv); +} + +``` + + + +## Pass structured data to WASM function + +We can't pass structure data or class objects through the pointer since the memory layout can different in two worlds. The way to do it is serialization. Refer to [export_native_api.md](./export_native_api.md) for the details. + + + +## The deinitialization procedure + +``` wasm_runtime_destroy_exec_env(exec_env); wasm_runtime_deinstantiate(module_inst); wasm_runtime_unload(module); @@ -41,3 +179,8 @@ A typical WAMR API usage is shown below (some return value checks are ignored): bh_memory_destroy(); ``` + + +## Native calling WASM function working flow + +![WAMR embed diagram](./pics/embed.PNG "WAMR embed architecture diagram") diff --git a/doc/export_native_api.md b/doc/export_native_api.md index f1bfbabcf..308d3f56c 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -2,202 +2,232 @@ Export native API to WASM application ======================================================= -The basic working flow for WASM application calling into the native API is shown in the following diagram: - -![WAMR WASM API ext diagram](./pics/extend_library.PNG "WAMR WASM API ext architecture diagram") -WAMR provides the macro `EXPORT_WASM_API` to enable users to export a native API to a WASM application. WAMR has implemented a base API for the timer and messaging by using `EXPORT_WASM_API`. This can be a point of reference for extending your own library. +Exporting native API steps +-------------------------- + +#### Step 1: Declare the function interface in WASM app + +Create a header file in a WASM app and declare the functions that are exported from native. In this example, we declare foo and foo2 as below in the header file "example.h" + +```c +/*** file name: example.h ***/ + +int foo(int a, int b); +void foo2(char * msg, char * buffer, int buf_len); +``` + + + +#### Step 2: Define the native API + +Define the native functions which are executed from the WASM app in the runtime source file. The native function can be any name, for example **foo_native** and **foo2** here: + ``` C -static NativeSymbol extended_native_symbol_defs[] = { - EXPORT_WASM_API(wasm_register_resource), - EXPORT_WASM_API(wasm_response_send), - EXPORT_WASM_API(wasm_post_request), - EXPORT_WASM_API(wasm_sub_event), - EXPORT_WASM_API(wasm_create_timer), - EXPORT_WASM_API(wasm_timer_set_interval), - EXPORT_WASM_API(wasm_timer_cancel), - EXPORT_WASM_API(wasm_timer_restart) +int foo_native(wasm_exec_env_t exec_env , int a, int b) +{ + return a+b; +} + +void foo2(wasm_exec_env_t exec_env, char * msg, uint8 * buffer, int buf_len) +{ + strncpy(buffer, msg, buf_len); +} +``` + +The first parameter exec_env must be defined using type **wasm_exec_env_t** which is the calling convention for exporting native API by WAMR. + +The rest parameters should be in the same types as the parameters of WASM function foo(), but there are a few special cases that are explained in section "Buffer address conversion and boundary check". Regarding the parameter names, they don't have to be the same, but we would suggest using the same names for easy maintenance. + + + +#### Step 3: Register the native APIs + +Register the native APIs in the runtime, then everything is fine. It is ready to build the runtime software. + +``` C +// Define an array of NativeSymbol for the APIs to be exported. +// Note: the array must be static defined since runtime +// will keep it after registration +static NativeSymbol native_symbols[] = +{ + { + "foo", // the name of WASM function name + foo_native, // the native function pointer + "(ii)i" // the function prototype signature + }, + { + "foo2", // the name of WASM function name + foo2, // the native function pointer + "($*~)" // the function prototype signature + } +}; + + +int n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); +if (!wasm_runtime_register_natives("env", + native_symbols, + n_native_symbols)) { + goto fail1; +} + +``` + +**Function signature**: + +The function signature field in **NativeSymbol** structure is a string for describing the function prototype. It is critical to ensure the function signature is correctly mapping the native function interface. + +Each letter in the "()" represents a parameter type, and the one following after ")" represents the return value type. The meaning of each letter: + +- 'i': i32 +- 'I': i64 +- 'f': f32 +- 'F': f64 +- '*': the parameter is a buffer address in WASM application +- '~': the parameter is the byte length of WASM buffer as referred by preceding argument "\*". It must follow after '*', otherwise, registration will fail +- '$': the parameter is a string in WASM application + +**Use EXPORT_WASM_API_WITH_SIG** + +The above foo2 NativeSymbol element can be also defined with macro EXPORT_WASM_API_WITH_SIG. This macro can be used when the native function name is the same as the WASM symbol name. + +```c +static NativeSymbol native_symbols[] = +{ + EXPORT_WASM_API_WITH_SIG(foo2, "($*~)") }; ``` -**Security attention:** A WebAssembly application should only have access to its own memory space. As a result, the integrator should carefully design the native function to ensure that the memory accesses are safe. The native API to be exported to the WASM application must: +​ -- Only use 32 bits number for parameters -- Should not pass data to the structure pointer (do data serialization instead) -- Should do the pointer address conversion in the native API -- Should not pass function pointer as callback +## Call exported API in wasm application - - -Below is a sample of a library extension. All code invoked across WASM and native world must be serialized and de-serialized, and the native world must do a boundary check for every incoming address from the WASM world. - -In wasm world: -``` C -void api_send_request(request_t * request, response_handler_f response_handler, - void * user_data) -{ - int size; - char *buffer; - transaction_t *trans; - - if ((trans = (transaction_t *) malloc(sizeof(transaction_t))) == NULL) { - printf( - "send request: allocate memory for request transaction failed!\n"); - return; - } - - memset(trans, 0, sizeof(transaction_t)); - trans->handler = response_handler; - trans->mid = request->mid; - trans->time = wasm_get_sys_tick_ms(); - trans->user_data = user_data; - - // pack request - if ((buffer = pack_request(request, &size)) == NULL) { - printf("send request: pack request failed!\n"); - free(trans); - return; - } - - transaction_add(trans); - - /* if the trans is the 1st one, start the timer */ - if (trans == g_transactions) { - /* assert(g_trans_timer == NULL); */ - if (g_trans_timer == NULL) { - g_trans_timer = api_timer_create(TRANSACTION_TIMEOUT_MS, - false, - true, transaction_timeout_handler); - } - } - - // call native API - wasm_post_request(buffer, size); - - free_req_resp_packet(buffer); -} -``` - -In native world: -``` C -void -wasm_post_request(wasm_exec_env_t exec_env, - int32 buffer_offset, int size) -{ - wasm_module_inst_t module_inst = get_module_inst(exec_env); - char *buffer = NULL; - - // do boundary check - if (!validate_app_addr(buffer_offset, size)) - return; - - // do address conversion - buffer = addr_app_to_native(buffer_offset); - - if (buffer != NULL) { - request_t req[1]; - - // De-serialize data - if (!unpack_request(buffer, size, req)) - return; - - // set sender to help dispatch the response to the sender app later - unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, - module_inst); - bh_assert(mod_id != ID_NONE); - req->sender = mod_id; - - if (req->action == COAP_EVENT) { - am_publish_event(req); - return; - } - - am_dispatch_request(req); - } -} -``` - - - - - -Steps for exporting native API -========================== - -WAMR implemented a framework for developers to export API's. Below is the procedure to expose the platform API's in three steps: - - -## Step 1: Define the native API for exporting - -Define the function **example_native_func** in your source file, namely `example.c` here: -``` C -int example_native_func(wasm_exec_env_t exec_env, - int arg1, int arg2) -{ - // Your implementation here -} -``` -The first function argument must be defined using type **wasm_exec_env_t** which is the WAMR calling convention for native API exporting. - -The function prototype should also be declared in a header file so the wasm application can include it. -``` C -#ifndef _EXAMPLE_H_ -#define _EXAMPLE_H_ -#ifdef __cplusplus -extern "C" { -#endif - -void example_native_func(int arg1, int arg2); - -#ifdef __cplusplus -} -#endif -#endif -``` - -## Step 2: Declare the native API exporting - -Declare the function **example_native_func** with macro **EXPORT_WASM_API** in your **.inl** file, namely `example.inl` in this sample. -``` C -EXPORT_WASM_API(example_native_func), -``` - -Then include the file **example.inl** in definition of array **extended_native_symbol_defs** in the `ext_lib_export.c`. -``` C -static NativeSymbol extended_native_symbol_defs[] = { - #include "example.inl" -}; - -#include "ext_lib_export.h" -``` - - -## Step 3: Compile the runtime product -Add the source file **example.c** and **ext_lib_export.c** into the CMakeList.txt for building runtime with the exported API's: -``` cmake -set (EXT_API_SOURCE example.c) - -add_executable (sample - # other source files - # ...... - ${EXT_API_SOURCE} - ext_lib_export.c -) -``` - -# Use exported API in wasm application - -We can call the exported native API **example_native_func** in wasm application like this: +Now we can call the exported native API in wasm application like this: ``` C #include -#include "example.h" +#include "example.h" // where the APIs are declared int main(int argc, char **argv) { int a = 0, b = 1; + char * msg = "hello"; + char buffer[100]; - example_native_func(a, b); + int c = foo(a, b); // call into native foo_native() + foo2(msg, buffer, sizeof(buffer)); // call into native foo2() + return 0; } -``` \ No newline at end of file +``` + + + +## 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. + + + +The signature letter '$', '\*' and '\~' help the runtime do automatic address conversion and buffer boundary check, so the native function directly uses the string and buffer address. **Notes**: if '\*' is not followed by '\~', the native function should not assume the length of the buffer is more than 1 byte. + + + +As function parameters are always passed in 32 bits numbers, you can also use 'i' for the pointer type argument, then you must do all the address conversion and boundary checking in your native function. For example, if you change the foo2 signature to "(iii)", then you will implement the native part as the following sample: + +```c +void foo2(wasm_exec_env_t exec_env, + uint32 msg_offset, + uint32 buffer_offset, + int32 buf_len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char *buffer; + char * msg ; + + // do boundary check + if (!wasm_runtime_validate_app_str_add(msg_offset)) + return 0; + + if (!wasm_runtime_validate_app_addr(buffer_offset, buf_len)) + return; + + // do address conversion + buffer = wasm_runtime_addr_app_to_native(buffer_offset); + msg = wasm_runtime_addr_app_to_native(msg_offset); + + strncpy(buffer, msg, buf_len); +} +``` + + + + + +## Sandbox security attention + +The runtime builder should ensure not broking the memory sandbox when exporting the native function to WASM. + +A few key ground rules: + +- Never pass any structure/class object pointer to native (do data serialization instead) +- Do the pointer address conversion in the native API if "$\*" is not used for the pointer in the function signature +- Never pass a function pointer to the native + + + +## Pass structured data or class object + +We must do data serialization for passing structured data or class objects between the two worlds of WASM and native. There are two serialization methods available in WASM as below, and yet you can introduce more like json, cbor etc. + +- [attributes container](../core/app-framework/app-native-shared/attr_container.c) +- [restful request/response](../core/app-framework/app-native-shared/restful_utils.c) + +Note the serialization library is separately compiled into WASM and runtime. And the source files are located in the folder "[core/app-framework/app-native-shared](../core/app-framework/app-native-shared)“ where all source files will be compiled into both worlds. + + + +The following sample code demonstrates WASM app packs a response structure to buffer, then pass the buffer pointer to the native: + +```c +/*** file name: core/app-framework/base/app/request.c ***/ + +void api_response_send(response_t *response) +{ + int size; + char * buffer = pack_response(response, &size); + if (buffer == NULL) + return; + + wasm_response_send(buffer, size); // calling exported native API + free_req_resp_packet(buffer); +} +``` + + + +The following code demonstrates the native API unpack the WASM buffer to local native data structure: + +```c +/*** file name: core/app-framework/base/native/request_response.c ***/ + +bool +wasm_response_send(wasm_exec_env_t exec_env, char *buffer, int size) +{ + if (buffer != NULL) { + response_t response[1]; + + if (NULL == unpack_response(buffer, size, response)) + return false; + + am_send_response(response); + + return true; + } + + return false; +} +``` + + + diff --git a/doc/pics/app_framework.PNG b/doc/pics/app_framework.PNG new file mode 100644 index 0000000000000000000000000000000000000000..802c45ba27151e778e3f63ab67d5c89003e280de GIT binary patch literal 41221 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 literal 0 HcmV?d00001 diff --git a/doc/pics/sensor_callflow.PNG b/doc/pics/sensor_callflow.PNG index ff471da0bf9bd64ec36d31cf2aabe8587514d569..12839aa5a74d416185fef1b122291c78a41ac1d6 100644 GIT binary patch literal 39167 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ܯ|Zab<2tNH#j{Ezl>jtY04qe{M! z6USdrTK0D6v5Q;)i%`$eUdoRK94hwfWPQ5LbG(YP3a6Kos~um-*bS|EUBEi0YNp$9 zyj-tJQ)>z=NBbh-5FP~9%4jl4P!hq)hcFsepr=jS;l^B%+Cm=z3Sep zOUbe%&sp+lV|sQ$rMa=pN(LX@T&>kLO3I>68r_*Gy3{!>MR_ZBY)4ikdFEs}O7h57 zHE;iu#~)ig!fZI#mVaNm@_y`ARXO&qp`U;9j4W+osq2zom8L;$w+l-Q`2(xDPRQ)) z+$n*S)LP<7#bE3%pd-(AkUf7Qlpth}$O_r_vp1MZ+4Q48?wQ0hRKej`=!To zmDqf>V<9b{MjOcT3MR8nm!CqIOz%zJ>+|ReL}e9U|F&+9V&}K-9%~aT<)q0Hmf_O2 z;g(Ur32bJ%v|4_SopE-q_|^cBj@sMe;dK!dw>&Tjkk<*JYN-)Y<8}V#wxGh*wf&#u zr%k4@n$A_pm9)JnyNLql3e3`bxrg%e9Me0_WNMiXljGcR231EL06qP=C3f|Rm-K|} zo1I(l-;Tr$WG`_HW_J5>aBk}4uL>V-i!Y?%3W$ov7()21+*bJ*j*Du*2;w#4bLpT@@x5{Um1H%=Y zSksIgl$}@XpmqYl2NYf#j}hEPLk!kYM+~J47L0e~+8siN&lNZX`vR^ii#)D6tbH>o z7T9d8(VP--%wb`h}Xz5&&`bEJOA1@+@6$7u7vjmWlylwq9U@Y$xiW$Oym=k zFn%$Vszw2vdgw9b)Jjhi4ga?2aiYqZa=9jS3)tvHgSxCw1TR(HD1X^PJnKg$_l4+@ zknf*9Y1CadcXHC5+>F`kZXTFeHEl3|t+y|B3gde8SBI4bD7WQcQQEWqoc(j;XM!X_ zPQlW^OWc~fJxf7ofA$x5!8dfC`vkd|?NDLTvBlO$PsNgr3byjjqbJONo3264KzGw} z8AQ>yP*#4`kN2_FgHJZpE_kENc2g^}=(VQ@-F(^a$-O|wC{TIT&NI*$64ztUk5s?y+MqUKLetUoH_VT_Pu9%w4#vvxObRgs&z(H^&8qORn>4)Ire=AEUTI`W z>Je!~#NI5VoU_L>=-#X<&N9V!-TpDyKvtx0()w1`zl*&D=qc=zjx(xMp1XGoVf`;V zU_%24B>l?_@OceHP5*ze0stxbRZ>5Kv>JxXhqae|nD&+bpGJi)&@!=zcM}KNEDY$x zaXvG>Jau5?Fb!D!FmsM8LoMEwu!s4whnHph&Ov#CEqd&MnZ*+fA12vy07z{hS!Ccb q3zGQ-Shz$Y8)qG|1!2b_0<>B}M literal 76558 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 rL%b6F48QcxF9C-C5B+*4uzPT%C~k#f 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

|XZE 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 $@" diff --git a/product-mini/platforms/linux-sgx/ext_lib_export.c b/product-mini/platforms/linux-sgx/ext_lib_export.c deleted file mode 100644 index 8813f0dbf..000000000 --- a/product-mini/platforms/linux-sgx/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index 320534b55..193d9ec0e 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -76,7 +76,7 @@ endif () #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong --param ssp-buffer-size=4") #set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-z,noexecstack,-z,relro,-z,now") -add_executable (iwasm main.c ext_lib_export.c) +add_executable (iwasm main.c) install (TARGETS iwasm DESTINATION bin) diff --git a/product-mini/platforms/linux/ext_lib_export.c b/product-mini/platforms/linux/ext_lib_export.c deleted file mode 100644 index 8813f0dbf..000000000 --- a/product-mini/platforms/linux/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/vxworks/CMakeLists.txt b/product-mini/platforms/vxworks/CMakeLists.txt index 88ba8328c..dc92494eb 100644 --- a/product-mini/platforms/vxworks/CMakeLists.txt +++ b/product-mini/platforms/vxworks/CMakeLists.txt @@ -66,7 +66,7 @@ set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) -add_executable (iwasm main.c ext_lib_export.c) +add_executable (iwasm main.c) install (TARGETS iwasm DESTINATION bin) diff --git a/product-mini/platforms/vxworks/ext_lib_export.c b/product-mini/platforms/vxworks/ext_lib_export.c deleted file mode 100644 index 8813f0dbf..000000000 --- a/product-mini/platforms/vxworks/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/product-mini/platforms/zephyr/simple/CMakeLists.txt b/product-mini/platforms/zephyr/simple/CMakeLists.txt index cafc7002f..c55de4a76 100644 --- a/product-mini/platforms/zephyr/simple/CMakeLists.txt +++ b/product-mini/platforms/zephyr/simple/CMakeLists.txt @@ -80,6 +80,5 @@ set (VM_LIB_SRCS target_sources(app PRIVATE ${VM_LIB_SRCS} - src/main.c - src/ext_lib_export.c) + src/main.c) diff --git a/product-mini/platforms/zephyr/simple/src/ext_lib_export.c b/product-mini/platforms/zephyr/simple/src/ext_lib_export.c deleted file mode 100644 index 8813f0dbf..000000000 --- a/product-mini/platforms/zephyr/simple/src/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/samples/gui/README.md b/samples/gui/README.md index 364dab8c0..09cdc18fb 100644 --- a/samples/gui/README.md +++ b/samples/gui/README.md @@ -112,3 +112,9 @@ First, connect PC and STM32 with UART. Then install to use host_tool.
- Install AOT version WASM application `wamrc --target=thumbv7 --target-abi=eabi --cpu=cortex-m7 -o ui_app.aot ui_app.wasm` `./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app.aot` + + + +The graphic user interface demo photo: + +![WAMR samples diagram](../../doc/pics/vgl_demo.png "WAMR samples diagram") \ No newline at end of file diff --git a/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt b/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt index c1aa7b3c7..c4ec6637b 100644 --- a/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt +++ b/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt @@ -42,7 +42,6 @@ include_directories( set (SOURCES ${PROJECT_SRC_DIR}/main.c ${PROJECT_SRC_DIR}/iwasm_main.c - ${PROJECT_SRC_DIR}/../../ext_lib_export.c ${LV_DRIVERS_SOURCES} ) diff --git a/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c b/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c deleted file mode 100644 index 0058f9908..000000000 --- a/samples/gui/wasm-runtime-wgl/src/ext_lib_export.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "lib_export.h" -#include "sensor_native_api.h" -#include "connection_native_api.h" -#include "gui_native_api.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" -#include "wamr_gui.inl" -}; - -#include "ext_lib_export.h" diff --git a/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt b/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt index ef9dc919c..2ff88b002 100644 --- a/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt +++ b/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt @@ -73,5 +73,4 @@ target_sources(app PRIVATE ${LVGL_DRV_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/ext_lib_export.c ) diff --git a/samples/littlevgl/README.md b/samples/littlevgl/README.md index d7d574f55..bb41a27fb 100644 --- a/samples/littlevgl/README.md +++ b/samples/littlevgl/README.md @@ -4,7 +4,6 @@ This sample demonstrates that a graphic user interface application in WebAssembl In this sample, the whole LittlevGL source code is built into the WebAssembly code with the user application. The platform interfaces defined by LittlevGL is implemented in the runtime and exported to the application through the declarations from source "ext_lib_export.c" as below: - EXPORT_WASM_API(display_init), EXPORT_WASM_API(display_input_read), EXPORT_WASM_API(display_flush), EXPORT_WASM_API(display_fill), diff --git a/samples/littlevgl/build.sh b/samples/littlevgl/build.sh index bb9b198dc..dc51bdb3a 100755 --- a/samples/littlevgl/build.sh +++ b/samples/littlevgl/build.sh @@ -38,7 +38,7 @@ fi echo "##################### 0. build wamr-sdk littlevgl start#####################" cd ${WAMR_DIR}/wamr-sdk -./build_sdk.sh -n littlevgl -x ${PROJECT_DIR}/wamr_config_littlevgl.cmake -e ${LV_CFG_PATH} +./build_sdk.sh -n littlevgl -x ${PROJECT_DIR}/wamr_config_littlevgl.cmake -e ${LV_CFG_PATH} -c [ $? -eq 0 ] || exit $? echo "#####################build wamr-sdk littlevgl success" diff --git a/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt b/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt index aa9193361..d79b1f7ae 100644 --- a/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt +++ b/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt @@ -27,7 +27,7 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/src) add_executable (vgl_wasm_runtime src/platform/${WAMR_BUILD_PLATFORM}/main.c src/platform/${WAMR_BUILD_PLATFORM}/iwasm_main.c - src/ext_lib_export.c src/platform/${WAMR_BUILD_PLATFORM}/display_indev.c + src/platform/${WAMR_BUILD_PLATFORM}/display_indev.c src/platform/${WAMR_BUILD_PLATFORM}/mouse.c) target_link_libraries (vgl_wasm_runtime vmlib -lm -ldl -lpthread -lSDL2) diff --git a/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h b/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h index 78daefe33..c87c3df44 100644 --- a/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h +++ b/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h @@ -58,11 +58,11 @@ enum { extern void xpt2046_init(void); -extern bool touchscreen_read(lv_indev_data_t * data); +extern bool touchscreen_read(lv_indev_data_t *data); -extern bool mouse_read(lv_indev_data_t * data); +extern bool mouse_read(lv_indev_data_t *data); -extern void display_init(wasm_exec_env_t exec_env); +extern void display_init(void); extern void display_deinit(wasm_exec_env_t exec_env); @@ -70,22 +70,21 @@ extern int time_get_ms(wasm_exec_env_t exec_env); extern void display_flush(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset); + lv_color_t *color); extern void display_fill(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p); + lv_color_t *color); extern void display_map(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); + const lv_color_t *color); -extern bool display_input_read(wasm_exec_env_t exec_env, - int32 data_offset); +extern bool display_input_read(wasm_exec_env_t exec_env, void *data); void display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa); + void *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t *color, lv_opa_t opa); #endif diff --git a/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c b/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c deleted file mode 100644 index 8262770b4..000000000 --- a/samples/littlevgl/vgl-wasm-runtime/src/ext_lib_export.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "lib_export.h" -#include "sensor_native_api.h" -#include "connection_native_api.h" -#include "display_indev.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" - EXPORT_WASM_API(display_init), - EXPORT_WASM_API(display_input_read), - EXPORT_WASM_API(display_flush), - EXPORT_WASM_API(display_fill), - EXPORT_WASM_API(display_vdb_write), - EXPORT_WASM_API(display_map), - EXPORT_WASM_API(time_get_ms) -}; - -#include "ext_lib_export.h" diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c index 52d57d506..956d55d0c 100644 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c +++ b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c @@ -41,7 +41,7 @@ static volatile bool sdl_refr_qry = false; static volatile bool sdl_quit_qry = false; void monitor_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) + const lv_color_t * color) { /*Return if the area is out the screen*/ if (x2 < 0 || y2 < 0 || x1 > MONITOR_HOR_RES - 1 @@ -53,10 +53,10 @@ void monitor_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t w = x2 - x1 + 1; for (y = y1; y <= y2; y++) { - memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color_p, + memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color, w * sizeof(lv_color_t)); - color_p += w; + color += w; } sdl_refr_qry = true; @@ -72,7 +72,7 @@ void monitor_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, * @param color fill color */ void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color) + lv_color_t *color) { /*Return if the area is out the screen*/ if (x2 < 0) @@ -92,7 +92,7 @@ void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x; int32_t y; - uint32_t color32 = color.full; //lv_color_to32(color); + uint32_t color32 = color->full; //lv_color_to32(color); for (x = act_x1; x <= act_x2; x++) { for (y = act_y1; y <= act_y2; y++) { @@ -109,10 +109,10 @@ void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, * @param y1 top coordinate * @param x2 right coordinate * @param y2 bottom coordinate - * @param color_p an array of colors + * @param color an array of colors */ void monitor_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) + const lv_color_t *color) { /*Return if the area is out the screen*/ if (x2 < 0) @@ -135,11 +135,11 @@ void monitor_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, for (y = act_y1; y <= act_y2; y++) { for (x = act_x1; x <= act_x2; x++) { - tft_fb[y * MONITOR_HOR_RES + x] = color_p->full; //lv_color_to32(*color_p); - color_p++; + tft_fb[y * MONITOR_HOR_RES + x] = color->full; //lv_color_to32(*color); + color++; } - color_p += x2 - act_x2; + color += x2 - act_x2; } sdl_refr_qry = true; @@ -147,62 +147,64 @@ void monitor_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, void -display_init(wasm_exec_env_t exec_env) +display_init(void) { } void display_flush(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset) + lv_color_t *color) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) - return; - lv_color_t * color_p = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - monitor_flush(x1, y1, x2, y2, color_p); + if (!wasm_runtime_validate_native_addr(module_inst, + color, sizeof(lv_color_t))) + return; + + monitor_flush(x1, y1, x2, y2, color); } void display_fill(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p) + lv_color_t *color) { - monitor_fill(x1, y1, x2, y2, color_p); + monitor_fill(x1, y1, x2, y2, color); } void display_map(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) + const lv_color_t *color) { - monitor_map(x1, y1, x2, y2, color_p); + monitor_map(x1, y1, x2, y2, color); } +typedef struct display_input_data { + lv_point_t point; + int32 user_data_offset; + uint8 state; +} display_input_data; + bool display_input_read(wasm_exec_env_t exec_env, - int32 data_p_offset) + void *input_data_app) { wasm_module_inst_t module_inst = get_module_inst(exec_env); + display_input_data *data_app = (display_input_data*)input_data_app; bool ret; - if (!wasm_runtime_validate_app_addr(module_inst, data_p_offset, 1)) + + if (!wasm_runtime_validate_native_addr(module_inst, + data_app, + sizeof(display_input_data))) return false; - struct { - lv_point_t point; - int32 user_data_offset; - uint8 state; - } *data_app; lv_indev_data_t data = {0}; ret = mouse_read(&data); - data_app = wasm_runtime_addr_app_to_native(module_inst, - data_p_offset); - data_app->point = data.point; data_app->user_data_offset = wasm_runtime_addr_native_to_app(module_inst, data.user_data); @@ -218,35 +220,17 @@ display_deinit(wasm_exec_env_t exec_env) void display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa) + void *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t *color, lv_opa_t opa) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) + unsigned char *buf_xy = (unsigned char*)buf + 4 * x + 4 * y * buf_w; + + if (!wasm_runtime_validate_native_addr(module_inst, + color, sizeof(lv_color_t))) return; - lv_color_t *color = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - void *buf = wasm_runtime_addr_app_to_native(module_inst, buf_offset); - - unsigned char *buf_xy = buf + 4 * x + 4 * y * buf_w; - lv_color_t * temp = (lv_color_t *) buf_xy; - *temp = *color; - /* - if (opa != LV_OPA_COVER) { - lv_color_t mix_color; - - mix_color.red = *buf_xy; - mix_color.green = *(buf_xy+1); - mix_color.blue = *(buf_xy+2); - color = lv_color_mix(color, mix_color, opa); - } - */ - /* - *buf_xy = color->red; - *(buf_xy + 1) = color->green; - *(buf_xy + 2) = color->blue; - */ + *(lv_color_t *)buf_xy = *color; } int monitor_sdl_refr_thread(void * param) diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c index 1081cd727..18ad0331a 100644 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c +++ b/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c @@ -32,6 +32,10 @@ #include "bi-inc/attr_container.h" #include "module_wasm_app.h" #include "wasm_export.h" +#include "sensor_native_api.h" +#include "connection_native_api.h" +#include "display_indev.h" + #define MAX 2048 #ifndef CONNECTION_UART @@ -389,7 +393,9 @@ static bool parse_args(int argc, char *argv[]) { "uart", required_argument, NULL, 'u' }, { "baudrate", required_argument, NULL, 'b' }, #endif +#if WASM_ENABLE_LIBC_WASI != 0 { "wasi_root", required_argument, NULL, 'w' }, +#endif { "help", required_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; @@ -421,12 +427,14 @@ static bool parse_args(int argc, char *argv[]) printf("uart baudrate: %s\n", optarg); break; #endif +#if WASM_ENABLE_LIBC_WASI != 0 case 'w': if (!wasm_set_wasi_root_dir(optarg)) { printf("Fail to set wasi root dir: %s\n", optarg); return false; } break; +#endif case 'h': showUsage(); return false; @@ -439,10 +447,22 @@ static bool parse_args(int argc, char *argv[]) return true; } +static NativeSymbol native_symbols[] = { + #include "runtime_sensor.inl" + #include "connection.inl" + EXPORT_WASM_API_WITH_SIG(display_input_read, "(*)i"), + EXPORT_WASM_API_WITH_SIG(display_flush, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(display_fill, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(display_vdb_write, "(*iii*i)"), + EXPORT_WASM_API_WITH_SIG(display_map, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(time_get_ms, "()i") +}; + // Driver function int iwasm_main(int argc, char *argv[]) { korp_thread tid; + uint32 n_native_symbols; if (!parse_args(argc, argv)) return -1; @@ -457,6 +477,13 @@ int iwasm_main(int argc, char *argv[]) goto fail1; } + /* Register native functions */ + n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + if (!wasm_runtime_register_natives("env", + native_symbols, n_native_symbols)) { + goto fail1; + } + if (!init_connection_framework()) { vm_thread_sys_destroy(); goto fail1; diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c index d537db6d3..6701ceebb 100644 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c +++ b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c @@ -15,10 +15,12 @@ #define MONITOR_ZOOM 1 #endif +extern int ili9340_init(); + static int lcd_initialized = 0; void -display_init(wasm_exec_env_t exec_env) +display_init(void) { if (lcd_initialized != 0) { return; @@ -32,23 +34,23 @@ display_init(wasm_exec_env_t exec_env) void display_flush(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - int32 color_p_offset) + lv_color_t *color) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) + struct display_buffer_descriptor desc; + + if (!wasm_runtime_validate_native_addr(module_inst, + color, sizeof(lv_color_t))) return; - lv_color_t * color_p = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); u16_t w = x2 - x1 + 1; u16_t h = y2 - y1 + 1; - struct display_buffer_descriptor desc; desc.buf_size = 3 * w * h; desc.width = w; desc.pitch = w; desc.height = h; - display_write(NULL, x1, y1, &desc, (void *) color_p); + display_write(NULL, x1, y1, &desc, (void *)color); /*lv_flush_ready();*/ } @@ -56,27 +58,28 @@ display_flush(wasm_exec_env_t exec_env, void display_fill(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p) + lv_color_t *color) { } void display_map(wasm_exec_env_t exec_env, int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) + const lv_color_t *color) { } bool -display_input_read(wasm_exec_env_t exec_env, int32 data_p_offset) +display_input_read(wasm_exec_env_t exec_env, void *data) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, data_p_offset, 1)) - return false; - lv_indev_data_t * data = wasm_runtime_addr_app_to_native(module_inst, - data_p_offset); + lv_indev_data_t *lv_data = (lv_indev_data_t*)data; - return touchscreen_read(data); + if (!wasm_runtime_validate_native_addr(module_inst, + lv_data, sizeof(lv_indev_data_t))) + return false; + + return touchscreen_read(lv_data); } void @@ -86,28 +89,16 @@ display_deinit(wasm_exec_env_t exec_env) void display_vdb_write(wasm_exec_env_t exec_env, - int32 buf_offset, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, int32 color_p_offset, lv_opa_t opa) + void *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t *color, lv_opa_t opa) { wasm_module_inst_t module_inst = get_module_inst(exec_env); - if (!wasm_runtime_validate_app_addr(module_inst, color_p_offset, 1)) + u8_t *buf_xy = (u8_t*)buf + 3 * x + 3 * y * buf_w; + + if (!wasm_runtime_validate_native_addr(module_inst, + color, sizeof(lv_color_t))) return; - lv_color_t *color = wasm_runtime_addr_app_to_native(module_inst, - color_p_offset); - void *buf = wasm_runtime_addr_app_to_native(module_inst, buf_offset); - - u8_t *buf_xy = buf + 3 * x + 3 * y * buf_w; - /* - if (opa != LV_OPA_COVER) { - lv_color_t mix_color; - - mix_color.red = *buf_xy; - mix_color.green = *(buf_xy+1); - mix_color.blue = *(buf_xy+2); - color = lv_color_mix(color, mix_color, opa); - } - */ *buf_xy = color->red; *(buf_xy + 1) = color->green; *(buf_xy + 2) = color->blue; diff --git a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c index 6d4b00575..a9639dc20 100644 --- a/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c +++ b/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c @@ -15,6 +15,9 @@ #include "bi-inc/attr_container.h" #include "module_wasm_app.h" #include "wasm_export.h" +#include "sensor_native_api.h" +#include "connection_native_api.h" +#include "display_indev.h" #include #include @@ -30,7 +33,6 @@ int uart_char_cnt = 0; static void uart_irq_callback(struct device *dev) { unsigned char ch; - int size = 0; while (uart_poll_in(dev, &ch) == 0) { uart_char_cnt++; @@ -77,11 +79,21 @@ timer_ctx_t timer_ctx; static char global_heap_buf[370 * 1024] = { 0 }; -extern void display_init(void); +static NativeSymbol native_symbols[] = { + #include "runtime_sensor.inl" + #include "connection.inl" + EXPORT_WASM_API_WITH_SIG(display_input_read, "(*)i"), + EXPORT_WASM_API_WITH_SIG(display_flush, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(display_fill, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(display_vdb_write, "(*iii*i)"), + EXPORT_WASM_API_WITH_SIG(display_map, "(iiii*)"), + EXPORT_WASM_API_WITH_SIG(time_get_ms, "()i") +}; int iwasm_main() { korp_thread tid, tm_tid; + uint32 n_native_symbols; host_init(); @@ -95,6 +107,13 @@ int iwasm_main() goto fail1; } + /* Register native functions */ + n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + if (!wasm_runtime_register_natives("env", + native_symbols, n_native_symbols)) { + goto fail1; + } + display_init(); // timer manager diff --git a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt b/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt index 9da334037..7458080d2 100644 --- a/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt +++ b/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt @@ -65,5 +65,4 @@ target_sources(app PRIVATE ${LVGL_DRV_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c - ${CMAKE_CURRENT_SOURCE_DIR}/../src/ext_lib_export.c ) diff --git a/samples/littlevgl/wasm-apps/src/display_indev.h b/samples/littlevgl/wasm-apps/src/display_indev.h index fe07d9ab6..0d55cea2c 100644 --- a/samples/littlevgl/wasm-apps/src/display_indev.h +++ b/samples/littlevgl/wasm-apps/src/display_indev.h @@ -10,13 +10,26 @@ #include "lvgl/lv_misc/lv_color.h" #include "lvgl/lv_hal/lv_hal_indev.h" + extern void display_init(void); -extern void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); -extern bool display_input_read(lv_indev_data_t * data); + extern void display_deinit(void); -extern void display_vdb_write(void *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t *color, lv_opa_t opa); + +extern void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + const lv_color_t * color); + +extern bool display_input_read(lv_indev_data_t *data); + +extern void display_vdb_write(void *buf, + lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t *color, lv_opa_t opa); + +void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + const lv_color_t *color); + +void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + const lv_color_t *color); + extern uint32_t time_get_ms(void); #endif diff --git a/samples/littlevgl/wasm-apps/src/main.c b/samples/littlevgl/wasm-apps/src/main.c index f13e76f8c..04da156da 100644 --- a/samples/littlevgl/wasm-apps/src/main.c +++ b/samples/littlevgl/wasm-apps/src/main.c @@ -119,27 +119,32 @@ void on_init() * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library */ void display_flush_wrapper(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p) + const lv_color_t * color_p) { display_flush(x1, y1, x2, y2, color_p); lv_flush_ready(); } -void display_vdb_write_wrapper(uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, - lv_coord_t y, lv_color_t color, lv_opa_t opa) + +void display_vdb_write_wrapper(uint8_t *buf, + lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, + lv_color_t color, lv_opa_t opa) { display_vdb_write(buf, buf_w, x, y, &color, opa); } -extern void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - lv_color_t color_p); -extern void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, - const lv_color_t * color_p); + +void display_fill_wrapper(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t color) +{ + display_fill(x1, y1, x2, y2, &color); +} + static void hal_init(void) { /* Add a display*/ lv_disp_drv_t disp_drv; lv_disp_drv_init(&disp_drv); /*Basic initialization*/ disp_drv.disp_flush = display_flush_wrapper; /*Used when `LV_VDB_SIZE != 0` in lv_conf.h (buffered drawing)*/ - disp_drv.disp_fill = display_fill; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ + disp_drv.disp_fill = display_fill_wrapper; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ disp_drv.disp_map = display_map; /*Used when `LV_VDB_SIZE == 0` in lv_conf.h (unbuffered drawing)*/ #if LV_VDB_SIZE != 0 disp_drv.vdb_wr = display_vdb_write_wrapper; diff --git a/samples/simple/CMakeLists.txt b/samples/simple/CMakeLists.txt index 3d3e45f84..85f1e00bc 100644 --- a/samples/simple/CMakeLists.txt +++ b/samples/simple/CMakeLists.txt @@ -27,7 +27,7 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/src) #Note: uncomment below line to use UART mode #add_definitions (-DCONNECTION_UART) -add_executable (simple src/main.c src/iwasm_main.c src/ext_lib_export.c) +add_executable (simple src/main.c src/iwasm_main.c) target_link_libraries (simple vmlib -lm -ldl -lpthread -lrt) diff --git a/samples/simple/src/ext_lib_export.c b/samples/simple/src/ext_lib_export.c deleted file mode 100644 index b3868bb7e..000000000 --- a/samples/simple/src/ext_lib_export.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "lib_export.h" -#include "sensor_native_api.h" -#include "timer_native_api.h" -#include "req_resp_native_api.h" -#include "connection_native_api.h" - -static NativeSymbol extended_native_symbol_defs[] = { -#include "runtime_sensor.inl" -#include "connection.inl" -}; - -#include "ext_lib_export.h" diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index 78794b6b9..b3e0d80c6 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -121,7 +121,7 @@ add_library (vmlib add_library (aotclib ${IWASM_COMPL_SOURCE}) -add_executable (wamrc main.c ext_lib_export.c) +add_executable (wamrc main.c) target_link_libraries (wamrc aotclib vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) diff --git a/wamr-compiler/ext_lib_export.c b/wamr-compiler/ext_lib_export.c deleted file mode 100644 index 42bc64375..000000000 --- a/wamr-compiler/ext_lib_export.c +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2019 Intel Corporation. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - */ - -#include "lib_export.h" - -static NativeSymbol extended_native_symbol_defs[] = { }; - -#include "ext_lib_export.h" diff --git a/wamr-sdk/README.md b/wamr-sdk/README.md index a84be84f7..219a0333a 100644 --- a/wamr-sdk/README.md +++ b/wamr-sdk/README.md @@ -1,52 +1,126 @@ -# SDK for Wasm Micro Runtime -This folder contains some tools to generate sdk for wamr runtime and wasm applications, the script and cmake files here are called by `make`, don't use them manually. +# WebAssembly Micro Runtime SDK + + +**Note**: [WASI-SDK](https://github.com/CraneStation/wasi-sdk/releases) version 7 and above should be installed before building the WAMR SDK. + + + +### SDK profile and configuration file + +A SDK profile presents a configuration of build parameters for the selection of CPU architecture, software platforms, execution mode, libc and application framework components. The profile configurations are saved in a cmake file that will be included by the WAMR SDK building tool `build_sdk.sh`. + +Here is the default configuration file [wamr-sdk/wamr_config_default.cmake](./wamr_config_default.cmake): -## Build the SDK -``` Bash -cd ${WAMR_ROOT}/wamr-sdk -make config ``` -Following the prompt to finish the settings for your customized runtime and app sdk, then you will get `out` folder under `${WAMR_ROOT}` - -The structure of the output folder is like bellow: +set (WAMR_BUILD_PLATFORM "linux") +set (WAMR_BUILD_TARGET X86_64) +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 1) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 0) +set (WAMR_BUILD_APP_FRAMEWORK 1) +set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE) ``` -out -|--app-sdk/ -| |--sysroot/ -| |--wamr_toolchain.cmake -| -|--runtime-sdk/ - |--include - |--lib - |--wamr_config.cmake + + + +Execute following command to build the WAMR SDK for a configuration profile: + +``` +cd wamr-sdk +./build_sdk.sh -n [profile name] -x [config file path] ``` -### app-sdk usage -The `app-sdk` is used to develop wasm applications, if your project are built with cmake, then the `wamr_toolchain.cmake` file is what you need to compile your project into wasm bytecode. -### runtime-sdk usage -The `runtime-sdk` is used to help you embed WAMR runtime into your product easier. There are two method you can use the SDK: +The output directory structure of a SDK package with profile name "simple": -1. Use the provided `runtime_lib.cmake` file: +``` +simple/ +├── app-sdk +│   ├── libc-builtin-sysroot +│   │   ├── include +│   │   └── share +│   └── wamr-app-framework +│   ├── include +│   │   ├── bi-inc +│   │   └── wa-inc +│   ├── lib +│   └── share +└── runtime-sdk + ├── include + │   └── bi-inc + └── lib +``` - You can include `${WAMR_ROOT}/cmake/runtime_lib.cmake` in your project's `CMakeLists.txt` file: - ``` cmake - include (${WAMR_ROOT}/cmake/runtime_lib.cmake) - add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) - # ...... - target_link_libraries (your_target vmlib -lm -ldl -lpthread) - ``` -2. Use the pre-built static library: - You can link the pre-built library: - ``` cmake - link_directories(${SDK_DIR}/runtime-sdk/lib) - include_directories(${SDK_DIR}/runtime-sdk/include) - # ...... - target_link_libraries (your_target vmlib -lm -ldl -lpthread) - ``` - This method can also be used when you don't use cmake +Like the WAMR samples, a project probably has its own pre-defined SDK configuration file. The project building script can call the `build_sdk.sh` by passing the configuration file name to the build_sdk.sh to generate its own WAMR SDK package. + + + +### Menu configuration for building SDK + +Menu configuration is supported for easy integration of runtime components and application libraries for the target architecture and platform. Run following command to start the menu config. + +``` +cd wamr-sdk +./build_sdk.sh -i -n [profile name] +``` + + The argument "-i" will make the command enter menu config mode as depicted below. + +wamr build menu configuration + +After the menu configuration is finished, the profile config file is saved and the building process is automatically started. When the building gets successful, the SDK package is generated under folder $wamr-sdk/out/{profile}, and the header files of configured components were copied into the SDK package. + + + +### Build WASM applications with APP-SDK + +The folder “**app-sdk**” under the profile output directory contains all the header files and WASM library for developing the WASM application. For C/C++ based WASM applications, the developers can use conventional cross-compilation procedure to build the WASM application. According to the profile selection of libc, following cmake toolchain files under folder [wamr-sdk/app](./app) are available for cross compiling WASM application: + +- ` wamr_toolchain.cmake` +- `wasi_toolchain.cmake` + + + +Refer to [build WASM applications](./doc/build_wasm_app.md) for the details. + + + +### Use Runtime SDK to build native application + +The output folder "**runtime-sdk**" contains all the header files and library files for integration with project native code. + +You can link the pre-built library: +``` cmake +link_directories(${SDK_DIR}/runtime-sdk/lib) +include_directories(${SDK_DIR}/runtime-sdk/include) +# ...... +target_link_libraries (your_target vmlib -lm -ldl -lpthread) +``` + +This method can also be used when you don't use cmake You can refer to this sample: [CMakeLists.txt](../samples/simple/CMakeLists.txt). -> NOTE: If you are familiar with how to configure WAMR by cmake and don't want to build the SDK, you can set the related settings on the top of your `CMakeLists.txt`, then the `runtime_lib.cmake` will not load settings from the SDK. \ No newline at end of file +> NOTE: If you are familiar with how to configure WAMR by cmake and don't want to build the SDK, you can set the related settings on the top of your `CMakeLists.txt`, then the `runtime_lib.cmake` will not load settings from the SDK. + + + +### Integrate WAMR without pre-built WAMR library + +Use the provided `runtime_lib.cmake` file: + +You can include `${WAMR_ROOT}/cmake/runtime_lib.cmake` in your project's `CMakeLists.txt` file: + +``` cmake +include (${WAMR_ROOT}/cmake/runtime_lib.cmake) +add_library (vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +# ...... +target_link_libraries (your_target vmlib -lm -ldl -lpthread) +``` + +You can refer to to product-mini building for Linux: [`product-mini/platforms/linux/CMakeLists.txt`](../product-mini/platforms/linux/CMakeLists.txt). + +> \ No newline at end of file From eb1eb9d96dbbac2d042b02110a4c8870ff73375a Mon Sep 17 00:00:00 2001 From: Wang Xin Date: Wed, 4 Mar 2020 22:27:38 +0800 Subject: [PATCH 3/5] Update README.md fix the link of menu config image --- wamr-sdk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wamr-sdk/README.md b/wamr-sdk/README.md index 219a0333a..f9d143c7e 100644 --- a/wamr-sdk/README.md +++ b/wamr-sdk/README.md @@ -69,7 +69,7 @@ cd wamr-sdk The argument "-i" will make the command enter menu config mode as depicted below. -wamr build menu configuration +wamr build menu configuration After the menu configuration is finished, the profile config file is saved and the building process is automatically started. When the building gets successful, the SDK package is generated under folder $wamr-sdk/out/{profile}, and the header files of configured components were copied into the SDK package. @@ -123,4 +123,4 @@ target_link_libraries (your_target vmlib -lm -ldl -lpthread) You can refer to to product-mini building for Linux: [`product-mini/platforms/linux/CMakeLists.txt`](../product-mini/platforms/linux/CMakeLists.txt). -> \ No newline at end of file +> From a325c6d3fee23323a00eeb6da2cd6fb326d0bbff Mon Sep 17 00:00:00 2001 From: Wang Xin Date: Thu, 5 Mar 2020 10:48:26 +0800 Subject: [PATCH 4/5] Update export_native_api.md --- doc/export_native_api.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/export_native_api.md b/doc/export_native_api.md index 308d3f56c..970af75f2 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -64,6 +64,10 @@ static NativeSymbol native_symbols[] = } }; +// ensure the memory and runtime initialization is finsihed +// before registering the native functions +bh_memory_init_with_pool(global_heap_buf, sizeof(global_heap_buf)); +wasm_runtime_init(); int n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); if (!wasm_runtime_register_natives("env", From 881b46cfc87a331fefb3408f43c96570e4bd13e4 Mon Sep 17 00:00:00 2001 From: Wang Xin Date: Thu, 5 Mar 2020 12:48:29 +0800 Subject: [PATCH 5/5] Update export_native_api.md --- doc/export_native_api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/export_native_api.md b/doc/export_native_api.md index 970af75f2..04c914e2d 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -76,6 +76,9 @@ if (!wasm_runtime_register_natives("env", goto fail1; } +// natives registeration must be done before loading WASM modules +module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); + ``` **Function signature**: