From 67495919b0dd83941dae44f84714ae0053735058 Mon Sep 17 00:00:00 2001 From: Shi Lei <50089527+lucshi@users.noreply.github.com> Date: Fri, 20 Mar 2020 16:39:13 +0800 Subject: [PATCH] Add a basic sample to show how native runtime invokes wasm apps in WAMR and how wasm apps invoke native functions. (#207) * Add printingAdd print time for wamrc, fix posix mmap bug time for wamrc, fixed a posix mmap bug. Change-Id: Ib6517b8a69cf022a1a6a74efa1f98155aec143bc * Add a basic sample to show how native runtime invokes wasm app in WAMR, and how wasm app invokes native functions. Change-Id: I700ae413ad5e9ea04540d5187952305e1ee92d73 --- README.md | 4 +- core/iwasm/common/wasm_runtime_common.c | 10 +- samples/basic/CMakeLists.txt | 42 ++++++ samples/basic/README.md | 51 +++++++ samples/basic/build.sh | 57 ++++++++ samples/basic/run.sh | 3 + samples/basic/src/main.c | 178 ++++++++++++++++++++++++ samples/basic/src/native_impl.c | 65 +++++++++ samples/basic/wasm-apps/testapp.c | 56 ++++++++ 9 files changed, 460 insertions(+), 6 deletions(-) create mode 100644 samples/basic/CMakeLists.txt create mode 100644 samples/basic/README.md create mode 100755 samples/basic/build.sh create mode 100755 samples/basic/run.sh create mode 100644 samples/basic/src/main.c create mode 100644 samples/basic/src/native_impl.c create mode 100644 samples/basic/wasm-apps/testapp.c diff --git a/README.md b/README.md index 1140f4f48..af9a8cd83 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,9 @@ The **[WAMR SDK](./wamr-sdk)** tools is helpful to finish the two tasks quickly. Samples ================= -The WAMR [samples](./samples) integrate the iwasm VM core, application manager and selected application framework components. +The WAMR [samples](./samples) integrate the iwasm VM core, application manager and selected application framework components. + +- [**Basic**](./samples/basic): Demonstrating how host runtime calls WASM function as well as WASM function calls native function. - **[Simple](./samples/simple/README.md)**: The runtime is integrated with most of the WAMR APP libraries, and a few WASM applications are provided for testing the WAMR APP API set. It uses **built-in libc** and executes apps in **interpreter** mode by default. - **[littlevgl](./samples/littlevgl/README.md)**: Demonstrating the graphic user interface application usage on WAMR. The whole [LittlevGL](https://github.com/littlevgl/) 2D user graphic library and the UI application is built into WASM application. It uses **WASI libc** and executes apps in **AoT mode** by default. - **[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. diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index 2d5e0d569..c5e651cf7 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -1821,11 +1821,11 @@ typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint64*,uint64); typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint64*, uint64); typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint64*, uint64); -static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)invokeNative; -static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; -static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)invokeNative; -static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; -static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; +static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)(uintptr_t)invokeNative; +static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)(uintptr_t)invokeNative; +static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)(uintptr_t)invokeNative; +static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)(uintptr_t)invokeNative; +static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)(uintptr_t)invokeNative; #if defined(_WIN32) || defined(_WIN32_) #define MAX_REG_FLOATS 4 diff --git a/samples/basic/CMakeLists.txt b/samples/basic/CMakeLists.txt new file mode 100644 index 000000000..d55054c07 --- /dev/null +++ b/samples/basic/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (basic) + +################ runtime settings ################ +set (WAMR_BUILD_PLATFORM "linux") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set (WAMR_BUILD_TARGET "X86_64") +set(CMAKE_BUILD_TYPE Debug) +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 1) +set (WAMR_BUILD_FAST_INTERP 0) + +# linker flags +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -pie -fPIE") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + +# build out vmlib +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +################ application related ################ +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (basic src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) + +target_link_libraries (basic vmlib -lm -ldl -lpthread -lrt) \ No newline at end of file diff --git a/samples/basic/README.md b/samples/basic/README.md new file mode 100644 index 000000000..d12f501fd --- /dev/null +++ b/samples/basic/README.md @@ -0,0 +1,51 @@ + + +The "basic" sample project +============== + +This sample demonstrates a few basic usages of embedding WAMR: +- initialize runtime +- load wasm app and instantiate the module +- call wasm function and pass arguments +- export native functions to the WASM apps +- wasm function calls native function and pass arguments +- deinitialize runtime + +Build this sample +============== +Execute the ```build.sh``` script then all binaries including wasm application files would be generated in 'out' directory. + +``` +$ ./build.sh +``` + +Run the sample +========================== +Enter the out directory. +``` +$ cd ./out/ +$ +$ ./basic wasm-apps/testapp.wasm +calling into WASM function: generate_float +Native finished calling wasm function generate_float(), returned a float value: 102009.921875f +calling into WASM function: float_to_string +calling into native function: intToStr +calling into native function: get_pow +calling into native function: intToStr +Native finished calling wasm function: float_to_string, returned a formatted string: 102009.921 +``` +Or execute the ```run.sh``` script in ```samples/basic``` folder. +``` +$ ./run.sh +calling into WASM function: generate_float +Native finished calling wasm function generate_float(), returned a float value: 102009.921875f +calling into WASM function: float_to_string +calling into native function: intToStr +calling into native function: get_pow +calling into native function: intToStr +Native finished calling wasm function: float_to_string, returned a formatted string: 102009.921 +``` + + + + diff --git a/samples/basic/build.sh b/samples/basic/build.sh new file mode 100755 index 000000000..f16950e5d --- /dev/null +++ b/samples/basic/build.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out + +WASM_APPS=${PWD}/wasm-apps + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + + +echo "#####################build basic project" +cd ${CURR_DIR} +mkdir -p cmake_build +cd cmake_build +cmake .. +make +if [ $? != 0 ];then + echo "BUILD_FAIL basic exit as $?\n" + exit 2 +fi + +cp -a basic ${OUT_DIR} + +echo -e "\n" + +echo "#####################build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +# use WAMR SDK to build out the .wasm binary +/opt/wasi-sdk/bin/clang \ + --target=wasm32 -O0 -z stack-size=4096 -Wl,--initial-memory=65536 \ + --sysroot=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot \ + -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=generate_float \ + -Wl,--export=float_to_string \ + -Wl,--allow-undefined \ + -o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC} + + +if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done +echo "####################build wasm apps done" diff --git a/samples/basic/run.sh b/samples/basic/run.sh new file mode 100755 index 000000000..a5fb29166 --- /dev/null +++ b/samples/basic/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +out/basic -f out/wasm-apps/testapp.wasm \ No newline at end of file diff --git a/samples/basic/src/main.c b/samples/basic/src/main.c new file mode 100644 index 000000000..97cfa99c0 --- /dev/null +++ b/samples/basic/src/main.c @@ -0,0 +1,178 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_export.h" +#include "bh_read_file.h" + +int intToStr(int x, char* str, int str_len, int digit); +int get_pow(int x, int y); + +void print_usage(void) +{ + fprintf(stdout, "Options:\r\n"); + fprintf(stdout, " -f [path of wasm file] \n"); +} + + +int main(int argc, char *argv_main[]) +{ + static char global_heap_buf[512 * 1024]; + char *buffer, error_buf[128]; + int opt; + char * wasm_path; + + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + uint32 buf_size, stack_size = 8092, heap_size = 8092; + wasm_function_inst_t func = NULL; + wasm_function_inst_t func2 = NULL; + char * native_buffer = NULL; + int32_t wasm_buffer = 0; + + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + while ((opt = getopt(argc, argv_main, "hf:")) != -1) + { + switch (opt) + { + case 'f': + wasm_path = optarg; + break; + case 'h': + print_usage(); + return 0; + case '?': + print_usage(); + return 0; + } + } + if (optind == 1) { + print_usage(); + return 0; + } + + // 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 + // For the function signature specifications, goto the link: + // https://github.com/bytecodealliance/wasm-micro-runtime/blob/master/doc/export_native_api.md + + static NativeSymbol native_symbols[] = + { + { + "intToStr", // the name of WASM function name + intToStr, // the native function pointer + "(i*~i)i" // the function prototype signature, avoid to use i32 + }, + { + "get_pow", // the name of WASM function name + get_pow, // the native function pointer + "(ii)i" // the function prototype signature, avoid to use i32 + } + }; + + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); + + // Native symbols need below registration phase + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + buffer = bh_read_file_to_buffer(wasm_path, &buf_size); + + if(!buffer) { + printf("Open wasm app file [%s] failed.\n", wasm_path); + goto fail; + } + + module = wasm_runtime_load(buffer, buf_size, error_buf, sizeof(error_buf)); + if(!module) { + printf("Load wasm module failed. error: %s\n", error_buf); + goto fail; + } + + module_inst = wasm_runtime_instantiate(module, + stack_size, + heap_size, + error_buf, + sizeof(error_buf)); + + if(!module_inst) { + printf("Instantiate wasm module failed. error: %s\n", error_buf); + goto fail; + } + + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); + if(!exec_env) { + printf("Create wasm execution environment failed.\n"); + goto fail; + } + + uint32 argv[4]; + double arg_d = 0.000101; + argv[0] = 10; + // the second arg will occupy two array elements + memcpy(&argv[1], &arg_d, sizeof(arg_d)); + *(float*)(argv+3) = 300.002; + + if(!(func = wasm_runtime_lookup_function(module_inst, "generate_float", NULL))){ + printf("The generate_float wasm function is not found.\n"); + goto fail; + } + + // pass 4 elements for function arguments + if (!wasm_runtime_call_wasm(exec_env, func, 4, argv) ) { + printf("call wasm function generate_float failed. %s\n", wasm_runtime_get_exception(module_inst)); + goto fail; + } + + float ret_val = *(float*)argv; + printf("Native finished calling wasm function generate_float(), returned a float value: %ff\n", ret_val ); + + // Next we will pass a buffer to the WASM function + uint32 argv2[4]; + + // must allocate buffer from wasm instance memory space (never use pointer from host runtime) + wasm_buffer = wasm_runtime_module_malloc(module_inst, 100, (void**)&native_buffer); + + *(float*)argv2 = ret_val; // the first argument + argv2[1] = wasm_buffer; // the second argument is the wasm buffer address + argv2[2] = 100; // the third argument is the wasm buffer size + argv2[3] = 3; // the last argument is the digits after decimal point for converting float to string + + if(!(func2 = wasm_runtime_lookup_function(module_inst, "float_to_string", NULL))){ + printf("The wasm function float_to_string wasm function is not found.\n"); + goto fail; + } + + if (wasm_runtime_call_wasm(exec_env, func2, 4, argv2) ) { + printf("Native finished calling wasm function: float_to_string, returned a formatted string: %s\n", native_buffer); + } + else { + printf("call wasm function float_to_string failed. error: %s\n", wasm_runtime_get_exception(module_inst)); + goto fail; + } + +fail: + if(exec_env) wasm_runtime_destroy_exec_env(exec_env); + if(module_inst) { + if(wasm_buffer) wasm_runtime_module_free(module_inst, wasm_buffer); + wasm_runtime_deinstantiate(module_inst); + } + if(module) wasm_runtime_unload(module); + if(buffer) BH_FREE(buffer); + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/basic/src/native_impl.c b/samples/basic/src/native_impl.c new file mode 100644 index 000000000..6f27a3a48 --- /dev/null +++ b/samples/basic/src/native_impl.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "wasm_export.h" +#include "math.h" + +// The first parameter is not exec_env because it is invoked by native funtions +void reverse(char * str, int len) +{ + int i = 0, j = len - 1, temp; + while (i < j) { + temp = str[i]; + str[i] = str[j]; + str[j] = temp; + i++; + j--; + } +} + +// 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. +// +// Converts a given integer x to string str[]. +// digit is the number of digits required in the output. +// If digit is more than the number of digits in x, +// then 0s are added at the beginning. +int intToStr(wasm_exec_env_t exec_env, int x, char* str, int str_len, int digit) +{ + int i = 0; + + printf ("calling into native function: %s\n", __FUNCTION__); + + while (x) { + // native is responsible for checking the str_len overflow + if (i >= str_len) { + return -1; + } + str[i++] = (x % 10) + '0'; + x = x / 10; + } + + // If number of digits required is more, then + // add 0s at the beginning + while (i < digit) { + if (i >= str_len) { + return -1; + } + str[i++] = '0'; + } + + reverse(str, i); + + if (i >= str_len) + return -1; + str[i] = '\0'; + return i; +} + +int get_pow(wasm_exec_env_t exec_env, int x, int y) { + printf ("calling into native function: %s\n", __FUNCTION__); + return (int)pow(x, y); +} diff --git a/samples/basic/wasm-apps/testapp.c b/samples/basic/wasm-apps/testapp.c new file mode 100644 index 000000000..b4e64f917 --- /dev/null +++ b/samples/basic/wasm-apps/testapp.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include + +int intToStr(int x, char* str, int str_len, int digit); +int get_pow(int x, int y); + +// +// Primitive parameters functions +// +float generate_float(int iteration, double seed1, float seed2) +{ + float ret; + + printf ("calling into WASM function: %s\n", __FUNCTION__); + + for (int i=0; i