From fa983a4f08a18c8e19f101b612dc4b12195a2cdf Mon Sep 17 00:00:00 2001 From: oubotong Date: Tue, 8 Sep 2020 18:08:15 -0700 Subject: [PATCH] WAMR in Zephyr --- CMakeLists.txt | 52 + Kconfig | 26 + README.rst | 43 + boards/nrf52840dk_nrf52840.overlay | 13 + build_and_run.sh | 86 + esp32_custom_linker.ld | 274 + overlay-ot.conf | 38 + prf_nrf52840_dk.conf | 63 + prj_esp32.conf | 8 + prj_nucleo767zi.conf | 7 + prj_qemu_cortex_a53.conf | 7 + prj_qemu_x86_nommu.conf | 6 + prj_qemu_xtensa.conf | 6 + sample.yaml | 22 + src/API.h | 18 + src/convert.py | 3 + src/fac.c | 14 + src/main.c | 349 + src/test_wasm.h | 77 + wamr/.clang-format | 162 + wamr/ATTRIBUTIONS.md | 330 + wamr/CODE_OF_CONDUCT.md | 49 + wamr/CONTRIBUTING.md | 39 + wamr/Dockerfile | 21 + wamr/LICENSE | 219 + wamr/ORG_CODE_OF_CONDUCT.md | 139 + wamr/README.md | 150 + wamr/SECURITY.md | 29 + wamr/assembly-script/.gitignore | 1 + wamr/assembly-script/README.md | 124 + wamr/assembly-script/package-lock.json | 30 + wamr/assembly-script/package.json | 20 + .../samples/event_publisher.ts | 36 + .../samples/event_subscriber.ts | 36 + .../samples/request_handler.ts | 40 + .../assembly-script/samples/request_sender.ts | 43 + wamr/assembly-script/samples/timer.ts | 36 + wamr/assembly-script/samples/tsconfig.json | 6 + wamr/assembly-script/wamr_app_lib/console.ts | 15 + wamr/assembly-script/wamr_app_lib/request.ts | 495 ++ wamr/assembly-script/wamr_app_lib/timer.ts | 80 + .../wamr_app_lib/tsconfig.json | 6 + wamr/build-scripts/config_common.cmake | 168 + wamr/build-scripts/runtime_lib.cmake | 125 + wamr/core/app-framework/README.md | 125 + .../app-framework/app-native-shared/README.md | 11 + .../app-native-shared/attr_container.c | 827 ++ .../app-native-shared/bi-inc/attr_container.h | 425 + .../app-native-shared/bi-inc/shared_utils.h | 156 + .../bi-inc/wgl_shared_utils.h | 103 + .../app-native-shared/native_interface.cmake | 15 + .../app-native-shared/native_interface.h | 15 + .../app-native-shared/restful_utils.c | 413 + wamr/core/app-framework/app_ext_lib_export.c | 39 + wamr/core/app-framework/app_framework.cmake | 89 + .../core/app-framework/base/app/bh_platform.c | 81 + .../core/app-framework/base/app/bh_platform.h | 48 + .../app-framework/base/app/req_resp_api.h | 32 + wamr/core/app-framework/base/app/request.c | 349 + wamr/core/app-framework/base/app/timer.c | 95 + wamr/core/app-framework/base/app/timer_api.h | 37 + .../app-framework/base/app/wa-inc/request.h | 170 + .../base/app/wa-inc/timer_wasm_app.h | 68 + .../app-framework/base/app/wasm_app.cmake | 13 + wamr/core/app-framework/base/app/wasm_app.h | 21 + .../app-framework/base/native/base_lib.inl | 14 + .../base/native/base_lib_export.c | 25 + .../base/native/req_resp_native_api.h | 30 + .../base/native/request_response.c | 84 + .../app-framework/base/native/runtime_lib.h | 18 + .../base/native/timer_native_api.h | 43 + .../app-framework/base/native/timer_wrapper.c | 194 + .../app-framework/base/native/wasm_lib.cmake | 13 + .../app-framework/connection/app/connection.c | 119 + .../connection/app/connection_api.h | 32 + .../connection/app/wa-inc/connection.h | 95 + .../connection/app/wasm_app.cmake | 11 + .../connection/native/connection.inl | 9 + .../connection/native/connection_lib.h | 77 + .../connection/native/connection_native_api.h | 41 + .../connection/native/connection_wrapper.c | 61 + .../connection/native/linux/conn_tcp.c | 51 + .../connection/native/linux/conn_tcp.h | 26 + .../connection/native/linux/conn_uart.c | 99 + .../connection/native/linux/conn_uart.h | 26 + .../connection/native/linux/conn_udp.c | 59 + .../connection/native/linux/conn_udp.h | 26 + .../connection/native/linux/connection_mgr.c | 585 ++ .../native/linux/connection_mgr.cmake | 13 + .../connection/native/wasm_lib.cmake | 18 + .../native/zephyr/connection_lib_impl.c | 23 + .../native/zephyr/connection_mgr.cmake | 13 + wamr/core/app-framework/sensor/app/sensor.c | 115 + .../app-framework/sensor/app/sensor_api.h | 32 + .../app-framework/sensor/app/wa-inc/sensor.h | 92 + .../app-framework/sensor/app/wasm_app.cmake | 11 + .../sensor/native/runtime_sensor.c | 419 + .../sensor/native/runtime_sensor.h | 60 + .../sensor/native/runtime_sensor.inl | 9 + .../sensor/native/sensor_mgr_ref.c | 140 + .../sensor/native/sensor_native_api.h | 36 + .../sensor/native/wasm_lib.cmake | 14 + .../template/app/wa-inc/app_xxx.h | 8 + .../app-framework/template/app/wasm_app.cmake | 16 + .../app-framework/template/native/app_xxx.inl | 6 + .../template/native/wasm_lib.cmake | 17 + wamr/core/app-framework/wgl/app/gui_api.h | 37 + .../app-framework/wgl/app/prepare_headers.sh | 46 + wamr/core/app-framework/wgl/app/src/wgl_btn.c | 121 + wamr/core/app-framework/wgl/app/src/wgl_cb.c | 82 + .../app-framework/wgl/app/src/wgl_label.c | 261 + .../core/app-framework/wgl/app/src/wgl_list.c | 155 + wamr/core/app-framework/wgl/app/src/wgl_obj.c | 116 + .../app-framework/wgl/app/wa-inc/lv_conf.h | 497 ++ .../wgl/app/wa-inc/lvgl/LICENCE.txt | 8 + .../wgl/app/wa-inc/lvgl/lv_obj.h | 916 +++ .../app-framework/wgl/app/wa-inc/lvgl/lvgl.h | 86 + .../app-framework/wgl/app/wa-inc/lvgl/test.c | 11 + .../core/app-framework/wgl/app/wasm_app.cmake | 19 + .../app-framework/wgl/native/gui_native_api.h | 46 + .../app-framework/wgl/native/wamr_gui.inl | 28 + .../app-framework/wgl/native/wasm_lib.cmake | 19 + wamr/core/app-framework/wgl/native/wgl.h | 20 + .../wgl/native/wgl_btn_wrapper.c | 163 + .../app-framework/wgl/native/wgl_cb_wrapper.c | 112 + .../wgl/native/wgl_cont_wrapper.c | 7 + .../wgl/native/wgl_label_wrapper.c | 96 + .../wgl/native/wgl_list_wrapper.c | 77 + .../wgl/native/wgl_native_utils.c | 131 + .../wgl/native/wgl_native_utils.h | 69 + .../wgl/native/wgl_obj_wrapper.c | 411 + wamr/core/app-framework/wgl/readme.MD | 97 + wamr/core/app-mgr/README.md | 10 + wamr/core/app-mgr/app-manager/app_manager.c | 399 + wamr/core/app-mgr/app-manager/app_manager.h | 84 + .../app-mgr/app-manager/app_manager_host.c | 285 + .../app-mgr/app-manager/app_manager_host.h | 24 + wamr/core/app-mgr/app-manager/app_mgr.cmake | 17 + wamr/core/app-mgr/app-manager/ble_msg.c | 115 + wamr/core/app-mgr/app-manager/coding_rule.txt | 15 + wamr/core/app-mgr/app-manager/event.c | 192 + wamr/core/app-mgr/app-manager/event.h | 41 + wamr/core/app-mgr/app-manager/message.c | 86 + wamr/core/app-mgr/app-manager/module_config.h | 23 + wamr/core/app-mgr/app-manager/module_jeff.c | 1733 +++++ wamr/core/app-mgr/app-manager/module_jeff.h | 29 + wamr/core/app-mgr/app-manager/module_utils.c | 220 + .../app-mgr/app-manager/module_wasm_app.c | 1628 ++++ .../app-mgr/app-manager/module_wasm_app.h | 140 + .../app-mgr/app-manager/module_wasm_lib.c | 49 + .../app-mgr/app-manager/module_wasm_lib.h | 21 + .../platform/linux/app_mgr_linux.c | 43 + .../platform/zephyr/app_mgr_zephyr.c | 60 + wamr/core/app-mgr/app-manager/resource_reg.c | 203 + wamr/core/app-mgr/app-manager/watchdog.c | 126 + wamr/core/app-mgr/app-manager/watchdog.h | 38 + .../app-mgr-shared/app_manager_export.h | 294 + .../app-mgr-shared/app_mgr_shared.cmake | 16 + wamr/core/app-mgr/app-mgr-shared/host_link.h | 31 + wamr/core/app-mgr/module.json | 52 + wamr/core/config.h | 244 + wamr/core/deps/README.md | 0 wamr/core/deps/download.sh | 28 + wamr/core/iwasm/README.md | 0 wamr/core/iwasm/aot/aot_loader.c | 2215 ++++++ wamr/core/iwasm/aot/aot_reloc.h | 88 + wamr/core/iwasm/aot/aot_runtime.c | 1934 +++++ wamr/core/iwasm/aot/aot_runtime.h | 561 ++ wamr/core/iwasm/aot/arch/aot_reloc_aarch64.c | 248 + wamr/core/iwasm/aot/arch/aot_reloc_arm.c | 281 + wamr/core/iwasm/aot/arch/aot_reloc_mips.c | 69 + wamr/core/iwasm/aot/arch/aot_reloc_thumb.c | 287 + wamr/core/iwasm/aot/arch/aot_reloc_x86_32.c | 115 + wamr/core/iwasm/aot/arch/aot_reloc_x86_64.c | 193 + wamr/core/iwasm/aot/arch/aot_reloc_xtensa.c | 231 + wamr/core/iwasm/aot/iwasm_aot.cmake | 31 + .../iwasm/common/arch/invokeNative_aarch64.s | 76 + .../core/iwasm/common/arch/invokeNative_arm.s | 70 + .../iwasm/common/arch/invokeNative_arm_vfp.s | 79 + .../iwasm/common/arch/invokeNative_em64.asm | 62 + .../iwasm/common/arch/invokeNative_em64.s | 64 + .../iwasm/common/arch/invokeNative_general.c | 86 + .../iwasm/common/arch/invokeNative_ia32.asm | 27 + .../iwasm/common/arch/invokeNative_ia32.s | 32 + .../iwasm/common/arch/invokeNative_mips.s | 74 + .../iwasm/common/arch/invokeNative_thumb.s | 84 + .../common/arch/invokeNative_thumb_vfp.s | 93 + .../iwasm/common/arch/invokeNative_xtensa.s | 74 + wamr/core/iwasm/common/iwasm_common.cmake | 56 + wamr/core/iwasm/common/wasm_c_api.c | 2747 +++++++ wamr/core/iwasm/common/wasm_c_api_internal.h | 182 + wamr/core/iwasm/common/wasm_exec_env.c | 171 + wamr/core/iwasm/common/wasm_exec_env.h | 240 + wamr/core/iwasm/common/wasm_memory.c | 372 + wamr/core/iwasm/common/wasm_memory.h | 28 + wamr/core/iwasm/common/wasm_native.c | 406 + wamr/core/iwasm/common/wasm_native.h | 78 + wamr/core/iwasm/common/wasm_runtime_common.c | 3179 ++++++++ wamr/core/iwasm/common/wasm_runtime_common.h | 440 ++ wamr/core/iwasm/common/wasm_shared_memory.c | 419 + wamr/core/iwasm/common/wasm_shared_memory.h | 68 + wamr/core/iwasm/compilation/aot.c | 561 ++ wamr/core/iwasm/compilation/aot.h | 238 + wamr/core/iwasm/compilation/aot_compiler.c | 1121 +++ wamr/core/iwasm/compilation/aot_compiler.h | 247 + .../iwasm/compilation/aot_emit_aot_file.c | 2104 +++++ .../core/iwasm/compilation/aot_emit_compare.c | 196 + .../core/iwasm/compilation/aot_emit_compare.h | 37 + wamr/core/iwasm/compilation/aot_emit_const.c | 115 + wamr/core/iwasm/compilation/aot_emit_const.h | 36 + .../core/iwasm/compilation/aot_emit_control.c | 1096 +++ .../core/iwasm/compilation/aot_emit_control.h | 66 + .../iwasm/compilation/aot_emit_conversion.c | 681 ++ .../iwasm/compilation/aot_emit_conversion.h | 89 + .../iwasm/compilation/aot_emit_exception.c | 186 + .../iwasm/compilation/aot_emit_exception.h | 27 + .../iwasm/compilation/aot_emit_function.c | 742 ++ .../iwasm/compilation/aot_emit_function.h | 28 + wamr/core/iwasm/compilation/aot_emit_memory.c | 1292 ++++ wamr/core/iwasm/compilation/aot_emit_memory.h | 104 + .../iwasm/compilation/aot_emit_numberic.c | 1270 +++ .../iwasm/compilation/aot_emit_numberic.h | 84 + .../iwasm/compilation/aot_emit_parametric.c | 102 + .../iwasm/compilation/aot_emit_parametric.h | 29 + .../iwasm/compilation/aot_emit_variable.c | 201 + .../iwasm/compilation/aot_emit_variable.h | 40 + wamr/core/iwasm/compilation/aot_llvm.c | 1547 ++++ wamr/core/iwasm/compilation/aot_llvm.h | 313 + wamr/core/iwasm/compilation/iwasm_compl.cmake | 8 + wamr/core/iwasm/include/aot_export.h | 85 + wamr/core/iwasm/include/lib_export.h | 52 + wamr/core/iwasm/include/wasm_c_api.h | 685 ++ wamr/core/iwasm/include/wasm_export.h | 826 ++ .../core/iwasm/interpreter/iwasm_interp.cmake | 29 + wamr/core/iwasm/interpreter/wasm.h | 528 ++ wamr/core/iwasm/interpreter/wasm_interp.h | 81 + .../iwasm/interpreter/wasm_interp_classic.c | 3344 ++++++++ .../core/iwasm/interpreter/wasm_interp_fast.c | 3383 ++++++++ wamr/core/iwasm/interpreter/wasm_loader.c | 6870 +++++++++++++++++ wamr/core/iwasm/interpreter/wasm_loader.h | 79 + .../core/iwasm/interpreter/wasm_mini_loader.c | 5653 ++++++++++++++ wamr/core/iwasm/interpreter/wasm_opcode.h | 605 ++ wamr/core/iwasm/interpreter/wasm_runtime.c | 1930 +++++ wamr/core/iwasm/interpreter/wasm_runtime.h | 382 + .../libraries/lib-pthread/lib_pthread.cmake | 13 + .../lib-pthread/lib_pthread_wrapper.c | 1020 +++ .../libraries/libc-builtin/libc_builtin.cmake | 13 + .../libc-builtin/libc_builtin_wrapper.c | 1190 +++ .../iwasm/libraries/libc-wasi/libc_wasi.cmake | 13 + .../libraries/libc-wasi/libc_wasi_wrapper.c | 1241 +++ .../libraries/libc-wasi/libc_wasi_wrapper.h | 51 + .../sandboxed-system-primitives/LICENSE | 7 + .../include/LICENSE | 121 + .../include/wasmtime_ssp.h | 895 +++ .../sandboxed-system-primitives/src/LICENSE | 24 + .../sandboxed-system-primitives/src/README.md | 14 + .../sandboxed-system-primitives/src/locking.h | 259 + .../src/numeric_limits.h | 40 + .../sandboxed-system-primitives/src/posix.c | 2970 +++++++ .../sandboxed-system-primitives/src/posix.h | 60 + .../sandboxed-system-primitives/src/queue.h | 90 + .../sandboxed-system-primitives/src/random.c | 86 + .../sandboxed-system-primitives/src/random.h | 18 + .../src/refcount.h | 100 + .../sandboxed-system-primitives/src/rights.h | 83 + .../sandboxed-system-primitives/src/signals.h | 17 + .../src/ssp_config.h | 106 + .../sandboxed-system-primitives/src/str.c | 44 + .../sandboxed-system-primitives/src/str.h | 19 + .../libraries/thread-mgr/thread_manager.c | 612 ++ .../libraries/thread-mgr/thread_manager.h | 122 + .../libraries/thread-mgr/thread_mgr.cmake | 13 + wamr/core/shared/coap/er-coap/LICENSE.md | 30 + .../core/shared/coap/er-coap/coap-constants.h | 192 + wamr/core/shared/coap/extension/coap_ext.h | 20 + wamr/core/shared/coap/lib_coap.cmake | 12 + wamr/core/shared/mem-alloc/ems/ems_alloc.c | 646 ++ wamr/core/shared/mem-alloc/ems/ems_gc.h | 155 + .../shared/mem-alloc/ems/ems_gc_internal.h | 234 + wamr/core/shared/mem-alloc/ems/ems_hmu.c | 95 + wamr/core/shared/mem-alloc/ems/ems_kfc.c | 202 + wamr/core/shared/mem-alloc/mem_alloc.c | 187 + wamr/core/shared/mem-alloc/mem_alloc.cmake | 15 + wamr/core/shared/mem-alloc/mem_alloc.h | 47 + wamr/core/shared/platform/README.md | 10 + .../shared/platform/alios/alios_platform.c | 67 + .../core/shared/platform/alios/alios_thread.c | 347 + wamr/core/shared/platform/alios/alios_time.c | 13 + .../shared/platform/alios/platform_internal.h | 63 + .../platform/alios/shared_platform.cmake | 16 + .../shared/platform/android/platform_init.c | 35 + .../platform/android/platform_internal.h | 95 + .../platform/android/shared_platform.cmake | 18 + .../shared/platform/common/math/COPYRIGHT | 126 + wamr/core/shared/platform/common/math/math.c | 861 +++ .../common/math/platform_api_math.cmake | 8 + .../common/posix/platform_api_posix.cmake | 8 + .../platform/common/posix/posix_malloc.c | 27 + .../platform/common/posix/posix_memmap.c | 99 + .../platform/common/posix/posix_thread.c | 418 + .../shared/platform/common/posix/posix_time.c | 18 + .../shared/platform/darwin/platform_init.c | 37 + .../platform/darwin/platform_internal.h | 96 + .../platform/darwin/shared_platform.cmake | 18 + .../platform/include/platform_api_extension.h | 156 + .../platform/include/platform_api_vmcore.h | 129 + .../shared/platform/include/platform_common.h | 73 + .../platform/linux-sgx/platform_internal.h | 63 + .../core/shared/platform/linux-sgx/sgx_file.c | 832 ++ .../core/shared/platform/linux-sgx/sgx_file.h | 229 + .../shared/platform/linux-sgx/sgx_platform.c | 165 + .../shared/platform/linux-sgx/sgx_pthread.c | 75 + .../shared/platform/linux-sgx/sgx_pthread.h | 27 + .../platform/linux-sgx/sgx_rsrv_mem_mngr.h | 90 + .../shared/platform/linux-sgx/sgx_signal.c | 26 + .../shared/platform/linux-sgx/sgx_signal.h | 57 + .../shared/platform/linux-sgx/sgx_socket.c | 218 + .../shared/platform/linux-sgx/sgx_socket.h | 74 + .../shared/platform/linux-sgx/sgx_thread.c | 146 + .../core/shared/platform/linux-sgx/sgx_time.c | 115 + .../core/shared/platform/linux-sgx/sgx_time.h | 47 + .../shared/platform/linux-sgx/sgx_wamr.edl | 132 + .../platform/linux-sgx/shared_platform.cmake | 30 + .../platform/linux-sgx/untrusted/file.c | 288 + .../platform/linux-sgx/untrusted/pthread.c | 50 + .../platform/linux-sgx/untrusted/signal.c | 10 + .../platform/linux-sgx/untrusted/socket.c | 73 + .../platform/linux-sgx/untrusted/time.c | 47 + .../shared/platform/linux/platform_init.c | 37 + .../shared/platform/linux/platform_internal.h | 95 + .../platform/linux/shared_platform.cmake | 18 + .../shared/platform/vxworks/platform_init.c | 37 + .../platform/vxworks/platform_internal.h | 94 + .../platform/vxworks/shared_platform.cmake | 18 + .../shared/platform/windows/platform_init.c | 18 + .../platform/windows/platform_internal.h | 68 + .../shared/platform/windows/posix_malloc.c | 27 + .../platform/windows/shared_platform.cmake | 18 + .../core/shared/platform/windows/win_memmap.c | 82 + .../core/shared/platform/windows/win_thread.c | 164 + wamr/core/shared/platform/windows/win_time.c | 16 + .../platform/zephyr/platform_internal.h | 100 + .../platform/zephyr/shared_platform.cmake | 16 + .../shared/platform/zephyr/zephyr_platform.c | 155 + .../shared/platform/zephyr/zephyr_thread.c | 456 ++ .../core/shared/platform/zephyr/zephyr_time.c | 13 + wamr/core/shared/utils/bh_assert.c | 31 + wamr/core/shared/utils/bh_assert.h | 29 + wamr/core/shared/utils/bh_common.c | 108 + wamr/core/shared/utils/bh_common.h | 54 + wamr/core/shared/utils/bh_hashmap.c | 288 + wamr/core/shared/utils/bh_hashmap.h | 132 + wamr/core/shared/utils/bh_list.c | 102 + wamr/core/shared/utils/bh_list.h | 102 + wamr/core/shared/utils/bh_log.c | 79 + wamr/core/shared/utils/bh_log.h | 67 + wamr/core/shared/utils/bh_platform.h | 41 + wamr/core/shared/utils/bh_queue.c | 244 + wamr/core/shared/utils/bh_queue.h | 76 + wamr/core/shared/utils/bh_vector.c | 203 + wamr/core/shared/utils/bh_vector.h | 125 + wamr/core/shared/utils/runtime_timer.c | 431 ++ wamr/core/shared/utils/runtime_timer.h | 41 + wamr/core/shared/utils/shared_utils.cmake | 12 + .../core/shared/utils/uncommon/bh_read_file.c | 104 + .../core/shared/utils/uncommon/bh_read_file.h | 23 + .../utils/uncommon/shared_uncommon.cmake | 11 + wamr/doc/build_wamr.md | 321 + wamr/doc/build_wasm_app.md | 171 + wamr/doc/embed_wamr.md | 312 + wamr/doc/export_native_api.md | 245 + wamr/doc/linux_sgx.md | 197 + wamr/doc/multi_module.md | 228 + wamr/doc/other_wasm_compilers.md | 115 + wamr/doc/pics/app_framework.PNG | Bin 0 -> 41221 bytes wamr/doc/pics/embed.PNG | Bin 0 -> 26261 bytes wamr/doc/pics/extend_library.PNG | Bin 0 -> 30398 bytes wamr/doc/pics/multi_module_pic1.png | Bin 0 -> 8585 bytes wamr/doc/pics/native_call_wasm.PNG | Bin 0 -> 49074 bytes wamr/doc/pics/request.PNG | Bin 0 -> 19974 bytes wamr/doc/pics/safe.PNG | Bin 0 -> 67786 bytes wamr/doc/pics/sensor_callflow.PNG | Bin 0 -> 39167 bytes wamr/doc/pics/sub.PNG | Bin 0 -> 18622 bytes wamr/doc/pics/vgl_demo.png | Bin 0 -> 133339 bytes wamr/doc/pics/vgl_demo2.png | Bin 0 -> 149435 bytes wamr/doc/pics/vgl_demo_linux.png | Bin 0 -> 5792 bytes wamr/doc/pics/vgl_linux.PNG | Bin 0 -> 25976 bytes wamr/doc/pics/wamr-arch.JPG | Bin 0 -> 131955 bytes wamr/doc/pics/wamr_menu_config.png | Bin 0 -> 19135 bytes wamr/doc/port_wamr.md | 66 + wamr/doc/pthread_library.md | 172 + wamr/doc/release_ack.md | 61 + wamr/doc/roadmap.md | 23 + wamr/doc/wamr_api.md | 351 + wamr/doc/wasm_c_api.md | 77 + .../hello-world-cmake/CMakeLists.txt | 13 + .../app-samples/hello-world-cmake/build.sh | 10 + .../app-samples/hello-world-cmake/main.c | 15 + .../app-samples/hello-world-cmake/print.c | 12 + .../app-samples/hello-world/build.sh | 14 + .../app-samples/hello-world/main.c | 28 + .../platforms/alios-things/aos.mk | 115 + .../platforms/alios-things/src/main.c | 117 + .../platforms/alios-things/src/test_wasm.h | 59 + .../platforms/android/CMakeLists.txt | 105 + .../platforms/android/build_jit.sh | 10 + .../platforms/android/build_llvm.sh | 43 + .../platforms/android/wasm-jni.cpp | 144 + .../platforms/darwin/CMakeLists.txt | 80 + wamr/product-mini/platforms/darwin/main.c | 319 + .../platforms/linux-sgx/CMakeLists.txt | 90 + .../linux-sgx/enclave-sample/App/App.cpp | 736 ++ .../enclave-sample/Enclave/Enclave.config.xml | 14 + .../enclave-sample/Enclave/Enclave.cpp | 509 ++ .../enclave-sample/Enclave/Enclave.edl | 25 + .../Enclave/Enclave_private.pem | 39 + .../enclave-sample/Enclave/Enclave_test.cpp | 416 + .../linux-sgx/enclave-sample/Makefile | 210 + .../platforms/linux/CMakeLists.txt | 120 + .../product-mini/platforms/linux/build_jit.sh | 10 + .../platforms/linux/build_llvm.sh | 46 + wamr/product-mini/platforms/linux/main.c | 403 + .../platforms/vxworks/CMakeLists.txt | 84 + wamr/product-mini/platforms/vxworks/main.c | 319 + .../platforms/windows/CMakeLists.txt | 121 + wamr/product-mini/platforms/windows/main.c | 402 + .../platforms/zephyr/simple/CMakeLists.txt | 46 + .../platforms/zephyr/simple/build_and_run.sh | 77 + .../zephyr/simple/esp32_custom_linker.ld | 274 + .../platforms/zephyr/simple/prj_esp32.conf | 8 + .../zephyr/simple/prj_nucleo767zi.conf | 7 + .../zephyr/simple/prj_qemu_cortex_a53.conf | 7 + .../zephyr/simple/prj_qemu_x86_nommu.conf | 6 + .../zephyr/simple/prj_qemu_xtensa.conf | 6 + .../platforms/zephyr/simple/src/main.c | 218 + .../platforms/zephyr/simple/src/test_wasm.h | 59 + wamr/samples/basic/.gitignore | 1 + wamr/samples/basic/CMakeLists.txt | 56 + wamr/samples/basic/README.md | 51 + wamr/samples/basic/build.sh | 58 + wamr/samples/basic/run.sh | 3 + wamr/samples/basic/src/main.c | 204 + wamr/samples/basic/src/native_impl.c | 96 + wamr/samples/basic/wasm-apps/testapp.c | 82 + wamr/samples/gui/README.md | 118 + wamr/samples/gui/build.sh | 70 + wamr/samples/gui/lv_config/lv_conf.h | 498 ++ wamr/samples/gui/lv_config/lv_drv_conf.h | 310 + wamr/samples/gui/lv_config/system_header.h | 8 + wamr/samples/gui/wamr_config_gui.cmake | 9 + wamr/samples/gui/wasm-apps/build_apps.sh | 44 + wamr/samples/gui/wasm-apps/decrease/Makefile | 29 + .../samples/gui/wasm-apps/decrease/src/main.c | 77 + .../gui/wasm-apps/increase/CMakeLists.txt | 20 + wamr/samples/gui/wasm-apps/increase/Makefile | 34 + .../samples/gui/wasm-apps/increase/src/main.c | 78 + .../linux-build/CMakeLists.txt | 54 + .../src/platform/linux/iwasm_main.c | 517 ++ .../src/platform/linux/lv_drv_conf.h | 310 + .../src/platform/linux/main.c | 22 + .../src/platform/zephyr/LICENSE | 202 + .../src/platform/zephyr/XPT2046.c | 339 + .../src/platform/zephyr/XPT2046.h | 64 + .../src/platform/zephyr/board_config.h | 9 + .../src/platform/zephyr/display.h | 405 + .../src/platform/zephyr/display_ili9340.c | 266 + .../src/platform/zephyr/display_ili9340.h | 68 + .../zephyr/display_ili9340_adafruit_1480.c | 79 + .../src/platform/zephyr/iwasm_main.c | 173 + .../src/platform/zephyr/main.c | 26 + .../src/platform/zephyr/pin_config_jlf.h | 26 + .../src/platform/zephyr/pin_config_stm32.h | 30 + .../zephyr-build/CMakeLists.txt | 78 + .../wasm-runtime-wgl/zephyr-build/prj.conf | 10 + wamr/samples/littlevgl/LICENCE.txt | 8 + wamr/samples/littlevgl/README.md | 159 + wamr/samples/littlevgl/build.sh | 97 + wamr/samples/littlevgl/lv_config/lv_conf.h | 389 + .../samples/littlevgl/lv_config/lv_drv_conf.h | 310 + .../vgl-native-ui-app/CMakeLists.txt | 137 + .../vgl-native-ui-app/CMakeLists.txt.in | 18 + .../vgl-native-ui-app/lv-drivers/.gitignore | 1 + .../lv-drivers/display_indev.h | 22 + .../lv-drivers/indev/mouse.c | 95 + .../lv-drivers/indev/mouse.h | 69 + .../lv-drivers/linux_display_indev.c | 303 + .../lv-drivers/system_header.h | 8 + .../littlevgl/vgl-native-ui-app/main.c | 155 + .../littlevgl/vgl-wasm-runtime/CMakeLists.txt | 36 + .../vgl-wasm-runtime/src/display_indev.h | 90 + .../src/platform/linux/display_indev.c | 344 + .../src/platform/linux/iwasm_main.c | 513 ++ .../src/platform/linux/main.c | 9 + .../src/platform/linux/mouse.c | 96 + .../src/platform/zephyr/LICENSE | 202 + .../src/platform/zephyr/XPT2046.c | 339 + .../src/platform/zephyr/XPT2046.h | 86 + .../src/platform/zephyr/board_config.h | 9 + .../src/platform/zephyr/display.h | 405 + .../src/platform/zephyr/display_ili9340.c | 266 + .../src/platform/zephyr/display_ili9340.h | 68 + .../zephyr/display_ili9340_adafruit_1480.c | 79 + .../src/platform/zephyr/display_indev.c | 112 + .../src/platform/zephyr/iwasm_main.c | 120 + .../src/platform/zephyr/main.c | 24 + .../src/platform/zephyr/pin_config_jlf.h | 26 + .../src/platform/zephyr/pin_config_stm32.h | 30 + .../zephyr-build/CMakeLists.txt | 71 + .../vgl-wasm-runtime/zephyr-build/prj.conf | 10 + .../littlevgl/wamr_config_littlevgl.cmake | 9 + .../littlevgl/wasm-apps/Makefile_wasm_app | 57 + .../wasm-apps/Makefile_wasm_app_no_wasi | 59 + .../littlevgl/wasm-apps/build_wasm_app.sh | 22 + .../littlevgl/wasm-apps/src/display_indev.h | 35 + wamr/samples/littlevgl/wasm-apps/src/main.c | 164 + .../littlevgl/wasm-apps/src/system_header.h | 8 + wamr/samples/multi-module/CMakeLists.txt | 61 + wamr/samples/multi-module/src/main.c | 144 + .../multi-module/wasm-apps/CMakeLists.txt | 41 + wamr/samples/multi-module/wasm-apps/mA.c | 5 + wamr/samples/multi-module/wasm-apps/mB.c | 16 + wamr/samples/multi-module/wasm-apps/mC.c | 25 + wamr/samples/multi-thread/CMakeLists.txt | 53 + .../multi-thread/wasm-apps/CMakeLists.txt | 38 + wamr/samples/multi-thread/wasm-apps/main.c | 69 + wamr/samples/simple/.gitignore | 1 + wamr/samples/simple/CMakeLists.txt | 35 + wamr/samples/simple/README.md | 342 + wamr/samples/simple/build.sh | 165 + .../profiles/arm-interp/toolchain.cmake | 38 + .../arm-interp/wamr_config_simple.cmake | 9 + .../host-aot/wamr_config_simple.cmake | 9 + .../host-interp/wamr_config_simple.cmake | 9 + wamr/samples/simple/src/iwasm_main.c | 526 ++ wamr/samples/simple/src/main.c | 6 + wamr/samples/simple/wasm-apps/connection.c | 85 + .../simple/wasm-apps/event_publisher.c | 48 + .../simple/wasm-apps/event_subscriber.c | 26 + .../simple/wasm-apps/request_handler.c | 57 + .../samples/simple/wasm-apps/request_sender.c | 48 + wamr/samples/simple/wasm-apps/sensor.c | 49 + wamr/samples/simple/wasm-apps/timer.c | 31 + wamr/samples/spawn-thread/CMakeLists.txt | 52 + wamr/samples/spawn-thread/src/main.c | 220 + .../spawn-thread/wasm-apps/CMakeLists.txt | 38 + wamr/samples/spawn-thread/wasm-apps/sum.c | 15 + wamr/samples/wasm-c-api/CMakeLists.txt | 100 + wamr/samples/wasm-c-api/README.md | 39 + wamr/samples/wasm-c-api/src/callback.c | 172 + wamr/samples/wasm-c-api/src/callback.wasm | Bin 0 -> 102 bytes wamr/samples/wasm-c-api/src/callback.wat | 10 + wamr/samples/wasm-c-api/src/global.c | 236 + wamr/samples/wasm-c-api/src/global.wasm | Bin 0 -> 576 bytes wamr/samples/wasm-c-api/src/global.wat | 27 + wamr/samples/wasm-c-api/src/hello.c | 112 + wamr/samples/wasm-c-api/src/hello.wasm | Bin 0 -> 71 bytes wamr/samples/wasm-c-api/src/hello.wat | 4 + wamr/test-tools/IoT-APP-Store-Demo/README.md | 36 + .../IoT-APP-Store-Demo/wasm_django/db.sqlite3 | Bin 0 -> 45056 bytes .../wasm_django/devices/__init__.py | 0 .../wasm_django/devices/admin.py | 3 + .../wasm_django/devices/apps.py | 5 + .../devices/migrations/__init__.py | 0 .../wasm_django/devices/models.py | 3 + .../devices/templates/application.html | 141 + .../devices/templates/appstore.html | 98 + .../wasm_django/devices/templates/empty.html | 125 + .../wasm_django/devices/templates/help.html | 102 + .../wasm_django/devices/templates/mysite.html | 91 + .../wasm_django/devices/tests.py | 3 + .../wasm_django/devices/views.py | 273 + .../IoT-APP-Store-Demo/wasm_django/manage.py | 21 + .../wasm_django/mysite/__init__.py | 0 .../wasm_django/mysite/settings.py | 136 + .../wasm_django/mysite/urls.py | 41 + .../wasm_django/mysite/wsgi.py | 16 + .../wasm_django/server/wasm_server.py | 605 ++ .../wasm_django/static/css/application.css | 400 + .../wasm_django/static/css/appstore.css | 216 + .../wasm_django/static/css/index.css | 197 + .../wasm_django/static/js/application.js | 217 + .../wasm_django/static/js/appstore.js | 125 + .../wasm_django/static/js/index.js | 51 + .../wasm_django/static/photo/app(1).png | Bin 0 -> 5421 bytes .../wasm_django/static/photo/application.png | Bin 0 -> 7875 bytes .../wasm_django/static/photo/delete.png | Bin 0 -> 4107 bytes .../wasm_django/static/photo/download(1).png | Bin 0 -> 1502 bytes .../wasm_django/static/photo/menu.png | Bin 0 -> 1839 bytes .../static/photo/milky-way-2695569_1280.jpg | Bin 0 -> 535384 bytes .../wasm_django/static/photo/net_device.png | Bin 0 -> 6867 bytes .../static/photo/software-icon-32081.png | Bin 0 -> 39956 bytes .../wasm_django/static/photo/totalblack.png | Bin 0 -> 2301 bytes .../wasm_django/static/upload/connection.wasm | Bin 0 -> 7894 bytes .../static/upload/event_publisher.wasm | Bin 0 -> 6817 bytes .../static/upload/event_subscriber.wasm | Bin 0 -> 5273 bytes .../static/upload/request_handler.wasm | Bin 0 -> 7995 bytes .../static/upload/request_sender.wasm | Bin 0 -> 6019 bytes .../wasm_django/static/upload/sensor.wasm | Bin 0 -> 5356 bytes .../wasm_django/static/upload/simple | Bin 0 -> 1066464 bytes .../static/upload/sys/connection.wasm | Bin 0 -> 7894 bytes .../static/upload/sys/event_publisher.wasm | Bin 0 -> 6817 bytes .../static/upload/sys/event_subscriber.wasm | Bin 0 -> 5273 bytes .../static/upload/sys/request_handler.wasm | Bin 0 -> 7995 bytes .../static/upload/sys/request_sender.wasm | Bin 0 -> 6019 bytes .../wasm_django/static/upload/sys/timer.wasm | Bin 0 -> 4150 bytes .../wasm_django/static/upload/timer.wasm | Bin 0 -> 4150 bytes .../wasm_django/static/upload/ui_app.wasm | Bin 0 -> 1953 bytes .../static/upload/vgl_wasm_runtime | Bin 0 -> 512936 bytes .../static/upload/wasm_runtime_wgl | Bin 0 -> 1066908 bytes .../test-tools/binarydump-tool/CMakeLists.txt | 11 + wamr/test-tools/binarydump-tool/binarydump.c | 126 + wamr/test-tools/component_test/README.md | 56 + wamr/test-tools/component_test/__init__.py | 11 + .../component_test/framework/__init__.py | 11 + .../component_test/framework/case_base.py | 29 + .../component_test/framework/engine.py | 38 + .../component_test/framework/framework.py | 287 + .../component_test/framework/suite.py | 40 + .../component_test/framework/test_api.py | 98 + .../component_test/framework/test_utils.py | 70 + .../component_test/harness/__init__.py | 0 .../component_test/harness/harness_api.py | 150 + .../host-clients/src/host_app_sample.c | 285 + .../component_test/host-clients/src/makefile | 44 + wamr/test-tools/component_test/set_dev_env.sh | 7 + wamr/test-tools/component_test/start.py | 151 + .../suites/01-life-cycle/__init__.py | 0 .../cases/01-install/__init__.py | 0 .../01-life-cycle/cases/01-install/case.py | 94 + .../cases/02-request/__init__.py | 0 .../01-life-cycle/cases/02-request/case.py | 73 + .../01-life-cycle/cases/03-event/__init__.py | 0 .../01-life-cycle/cases/03-event/case.py | 67 + .../cases/04-request-internal/__init__.py | 0 .../cases/04-request-internal/case.py | 80 + .../cases/05-event-internal/__init__.py | 0 .../cases/05-event-internal/case.py | 70 + .../01-life-cycle/cases/06-timer/__init__.py | 0 .../01-life-cycle/cases/06-timer/case.py | 70 + .../01-life-cycle/cases/07-sensor/__init__.py | 0 .../01-life-cycle/cases/07-sensor/case.py | 65 + .../cases/08-on-destroy/__init__.py | 0 .../01-life-cycle/cases/08-on-destroy/case.py | 78 + .../suites/01-life-cycle/cases/__init__.py | 0 .../suites/01-life-cycle/suite_setup.py | 56 + .../01-life-cycle/test-app/01_install.c | 16 + .../01-life-cycle/test-app/02_request.c | 62 + .../suites/01-life-cycle/test-app/03_event.c | 53 + .../test-app/04_request_internal_req.c | 66 + .../test-app/04_request_internal_resp.c | 52 + .../test-app/05_event_internal_provider.c | 53 + .../test-app/05_event_internal_subscriber.c | 50 + .../suites/01-life-cycle/test-app/06_timer.c | 76 + .../suites/01-life-cycle/test-app/07_sensor.c | 69 + .../01-life-cycle/test-app/08_on_destroy.c | 67 + .../suites/01-life-cycle/test-app/build.sh | 39 + .../01-life-cycle/tools/product/start.sh | 10 + .../01-life-cycle/tools/product/stop.sh | 9 + .../component_test/suites/__init__.py | 0 .../component_test/suites/readme.txt | 19 + wamr/test-tools/host-tool/CMakeLists.txt | 62 + .../host-tool/external/cJSON/LICENSE | 20 + .../host-tool/external/cJSON/cJSON.c | 2753 +++++++ .../host-tool/external/cJSON/cJSON.h | 281 + .../host-tool/external/cJSON/cjson.cmake | 10 + .../host-tool/src/host_tool_utils.c | 298 + .../host-tool/src/host_tool_utils.h | 72 + wamr/test-tools/host-tool/src/main.c | 908 +++ wamr/test-tools/host-tool/src/transport.c | 252 + wamr/test-tools/host-tool/src/transport.h | 111 + wamr/wamr-compiler/CMakeLists.txt | 181 + wamr/wamr-compiler/build_llvm.py | 98 + wamr/wamr-compiler/build_llvm.sh | 46 + wamr/wamr-compiler/build_llvm_xtensa.sh | 47 + wamr/wamr-compiler/main.c | 263 + wamr/wamr-sdk/Kconfig | 84 + wamr/wamr-sdk/Makefile | 10 + wamr/wamr-sdk/README.md | 126 + wamr/wamr-sdk/app/CMakeLists.txt | 97 + .../app/libc-builtin-sysroot/include/assert.h | 20 + .../app/libc-builtin-sysroot/include/ctype.h | 28 + .../app/libc-builtin-sysroot/include/errno.h | 20 + .../app/libc-builtin-sysroot/include/fcntl.h | 20 + .../libc-builtin-sysroot/include/inttypes.h | 21 + .../app/libc-builtin-sysroot/include/limits.h | 34 + .../libc-builtin-sysroot/include/pthread.h | 59 + .../libc-builtin-sysroot/include/stdbool.h | 19 + .../app/libc-builtin-sysroot/include/stdint.h | 48 + .../app/libc-builtin-sysroot/include/stdio.h | 30 + .../app/libc-builtin-sysroot/include/stdlib.h | 28 + .../app/libc-builtin-sysroot/include/string.h | 36 + .../libc-builtin-sysroot/include/strings.h | 20 + .../share/defined-symbols.txt | 83 + wamr/wamr-sdk/app/wamr_toolchain.cmake | 29 + wamr/wamr-sdk/app/wasi_toolchain.cmake | 17 + wamr/wamr-sdk/build_sdk.sh | 246 + wamr/wamr-sdk/menuconfig.sh | 223 + wamr/wamr-sdk/runtime/CMakeLists.txt | 56 + wamr/wamr-sdk/wamr_config_default.cmake | 12 + 699 files changed, 124796 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Kconfig create mode 100644 README.rst create mode 100644 boards/nrf52840dk_nrf52840.overlay create mode 100755 build_and_run.sh create mode 100644 esp32_custom_linker.ld create mode 100644 overlay-ot.conf create mode 100644 prf_nrf52840_dk.conf create mode 100644 prj_esp32.conf create mode 100644 prj_nucleo767zi.conf create mode 100644 prj_qemu_cortex_a53.conf create mode 100644 prj_qemu_x86_nommu.conf create mode 100644 prj_qemu_xtensa.conf create mode 100644 sample.yaml create mode 100644 src/API.h create mode 100644 src/convert.py create mode 100644 src/fac.c create mode 100644 src/main.c create mode 100644 src/test_wasm.h create mode 100644 wamr/.clang-format create mode 100644 wamr/ATTRIBUTIONS.md create mode 100644 wamr/CODE_OF_CONDUCT.md create mode 100644 wamr/CONTRIBUTING.md create mode 100644 wamr/Dockerfile create mode 100644 wamr/LICENSE create mode 100644 wamr/ORG_CODE_OF_CONDUCT.md create mode 100644 wamr/README.md create mode 100644 wamr/SECURITY.md create mode 100644 wamr/assembly-script/.gitignore create mode 100644 wamr/assembly-script/README.md create mode 100644 wamr/assembly-script/package-lock.json create mode 100644 wamr/assembly-script/package.json create mode 100644 wamr/assembly-script/samples/event_publisher.ts create mode 100644 wamr/assembly-script/samples/event_subscriber.ts create mode 100644 wamr/assembly-script/samples/request_handler.ts create mode 100644 wamr/assembly-script/samples/request_sender.ts create mode 100644 wamr/assembly-script/samples/timer.ts create mode 100644 wamr/assembly-script/samples/tsconfig.json create mode 100644 wamr/assembly-script/wamr_app_lib/console.ts create mode 100644 wamr/assembly-script/wamr_app_lib/request.ts create mode 100644 wamr/assembly-script/wamr_app_lib/timer.ts create mode 100644 wamr/assembly-script/wamr_app_lib/tsconfig.json create mode 100644 wamr/build-scripts/config_common.cmake create mode 100644 wamr/build-scripts/runtime_lib.cmake create mode 100644 wamr/core/app-framework/README.md create mode 100644 wamr/core/app-framework/app-native-shared/README.md create mode 100644 wamr/core/app-framework/app-native-shared/attr_container.c create mode 100644 wamr/core/app-framework/app-native-shared/bi-inc/attr_container.h create mode 100644 wamr/core/app-framework/app-native-shared/bi-inc/shared_utils.h create mode 100644 wamr/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h create mode 100644 wamr/core/app-framework/app-native-shared/native_interface.cmake create mode 100644 wamr/core/app-framework/app-native-shared/native_interface.h create mode 100644 wamr/core/app-framework/app-native-shared/restful_utils.c create mode 100644 wamr/core/app-framework/app_ext_lib_export.c create mode 100644 wamr/core/app-framework/app_framework.cmake create mode 100644 wamr/core/app-framework/base/app/bh_platform.c create mode 100755 wamr/core/app-framework/base/app/bh_platform.h create mode 100644 wamr/core/app-framework/base/app/req_resp_api.h create mode 100644 wamr/core/app-framework/base/app/request.c create mode 100644 wamr/core/app-framework/base/app/timer.c create mode 100644 wamr/core/app-framework/base/app/timer_api.h create mode 100644 wamr/core/app-framework/base/app/wa-inc/request.h create mode 100644 wamr/core/app-framework/base/app/wa-inc/timer_wasm_app.h create mode 100644 wamr/core/app-framework/base/app/wasm_app.cmake create mode 100644 wamr/core/app-framework/base/app/wasm_app.h create mode 100644 wamr/core/app-framework/base/native/base_lib.inl create mode 100644 wamr/core/app-framework/base/native/base_lib_export.c create mode 100644 wamr/core/app-framework/base/native/req_resp_native_api.h create mode 100644 wamr/core/app-framework/base/native/request_response.c create mode 100644 wamr/core/app-framework/base/native/runtime_lib.h create mode 100644 wamr/core/app-framework/base/native/timer_native_api.h create mode 100644 wamr/core/app-framework/base/native/timer_wrapper.c create mode 100644 wamr/core/app-framework/base/native/wasm_lib.cmake create mode 100644 wamr/core/app-framework/connection/app/connection.c create mode 100644 wamr/core/app-framework/connection/app/connection_api.h create mode 100644 wamr/core/app-framework/connection/app/wa-inc/connection.h create mode 100644 wamr/core/app-framework/connection/app/wasm_app.cmake create mode 100644 wamr/core/app-framework/connection/native/connection.inl create mode 100644 wamr/core/app-framework/connection/native/connection_lib.h create mode 100644 wamr/core/app-framework/connection/native/connection_native_api.h create mode 100644 wamr/core/app-framework/connection/native/connection_wrapper.c create mode 100644 wamr/core/app-framework/connection/native/linux/conn_tcp.c create mode 100644 wamr/core/app-framework/connection/native/linux/conn_tcp.h create mode 100644 wamr/core/app-framework/connection/native/linux/conn_uart.c create mode 100644 wamr/core/app-framework/connection/native/linux/conn_uart.h create mode 100644 wamr/core/app-framework/connection/native/linux/conn_udp.c create mode 100644 wamr/core/app-framework/connection/native/linux/conn_udp.h create mode 100644 wamr/core/app-framework/connection/native/linux/connection_mgr.c create mode 100644 wamr/core/app-framework/connection/native/linux/connection_mgr.cmake create mode 100644 wamr/core/app-framework/connection/native/wasm_lib.cmake create mode 100644 wamr/core/app-framework/connection/native/zephyr/connection_lib_impl.c create mode 100644 wamr/core/app-framework/connection/native/zephyr/connection_mgr.cmake create mode 100644 wamr/core/app-framework/sensor/app/sensor.c create mode 100644 wamr/core/app-framework/sensor/app/sensor_api.h create mode 100644 wamr/core/app-framework/sensor/app/wa-inc/sensor.h create mode 100644 wamr/core/app-framework/sensor/app/wasm_app.cmake create mode 100644 wamr/core/app-framework/sensor/native/runtime_sensor.c create mode 100644 wamr/core/app-framework/sensor/native/runtime_sensor.h create mode 100644 wamr/core/app-framework/sensor/native/runtime_sensor.inl create mode 100644 wamr/core/app-framework/sensor/native/sensor_mgr_ref.c create mode 100644 wamr/core/app-framework/sensor/native/sensor_native_api.h create mode 100644 wamr/core/app-framework/sensor/native/wasm_lib.cmake create mode 100644 wamr/core/app-framework/template/app/wa-inc/app_xxx.h create mode 100644 wamr/core/app-framework/template/app/wasm_app.cmake create mode 100644 wamr/core/app-framework/template/native/app_xxx.inl create mode 100644 wamr/core/app-framework/template/native/wasm_lib.cmake create mode 100644 wamr/core/app-framework/wgl/app/gui_api.h create mode 100755 wamr/core/app-framework/wgl/app/prepare_headers.sh create mode 100644 wamr/core/app-framework/wgl/app/src/wgl_btn.c create mode 100644 wamr/core/app-framework/wgl/app/src/wgl_cb.c create mode 100644 wamr/core/app-framework/wgl/app/src/wgl_label.c create mode 100644 wamr/core/app-framework/wgl/app/src/wgl_list.c create mode 100644 wamr/core/app-framework/wgl/app/src/wgl_obj.c create mode 100644 wamr/core/app-framework/wgl/app/wa-inc/lv_conf.h create mode 100644 wamr/core/app-framework/wgl/app/wa-inc/lvgl/LICENCE.txt create mode 100644 wamr/core/app-framework/wgl/app/wa-inc/lvgl/lv_obj.h create mode 100644 wamr/core/app-framework/wgl/app/wa-inc/lvgl/lvgl.h create mode 100644 wamr/core/app-framework/wgl/app/wa-inc/lvgl/test.c create mode 100644 wamr/core/app-framework/wgl/app/wasm_app.cmake create mode 100644 wamr/core/app-framework/wgl/native/gui_native_api.h create mode 100644 wamr/core/app-framework/wgl/native/wamr_gui.inl create mode 100644 wamr/core/app-framework/wgl/native/wasm_lib.cmake create mode 100644 wamr/core/app-framework/wgl/native/wgl.h create mode 100644 wamr/core/app-framework/wgl/native/wgl_btn_wrapper.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_cb_wrapper.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_cont_wrapper.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_label_wrapper.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_list_wrapper.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_native_utils.c create mode 100644 wamr/core/app-framework/wgl/native/wgl_native_utils.h create mode 100644 wamr/core/app-framework/wgl/native/wgl_obj_wrapper.c create mode 100644 wamr/core/app-framework/wgl/readme.MD create mode 100644 wamr/core/app-mgr/README.md create mode 100644 wamr/core/app-mgr/app-manager/app_manager.c create mode 100644 wamr/core/app-mgr/app-manager/app_manager.h create mode 100644 wamr/core/app-mgr/app-manager/app_manager_host.c create mode 100644 wamr/core/app-mgr/app-manager/app_manager_host.h create mode 100644 wamr/core/app-mgr/app-manager/app_mgr.cmake create mode 100644 wamr/core/app-mgr/app-manager/ble_msg.c create mode 100644 wamr/core/app-mgr/app-manager/coding_rule.txt create mode 100644 wamr/core/app-mgr/app-manager/event.c create mode 100644 wamr/core/app-mgr/app-manager/event.h create mode 100644 wamr/core/app-mgr/app-manager/message.c create mode 100644 wamr/core/app-mgr/app-manager/module_config.h create mode 100644 wamr/core/app-mgr/app-manager/module_jeff.c create mode 100644 wamr/core/app-mgr/app-manager/module_jeff.h create mode 100644 wamr/core/app-mgr/app-manager/module_utils.c create mode 100644 wamr/core/app-mgr/app-manager/module_wasm_app.c create mode 100644 wamr/core/app-mgr/app-manager/module_wasm_app.h create mode 100644 wamr/core/app-mgr/app-manager/module_wasm_lib.c create mode 100644 wamr/core/app-mgr/app-manager/module_wasm_lib.h create mode 100644 wamr/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c create mode 100644 wamr/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c create mode 100644 wamr/core/app-mgr/app-manager/resource_reg.c create mode 100644 wamr/core/app-mgr/app-manager/watchdog.c create mode 100644 wamr/core/app-mgr/app-manager/watchdog.h create mode 100644 wamr/core/app-mgr/app-mgr-shared/app_manager_export.h create mode 100644 wamr/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake create mode 100644 wamr/core/app-mgr/app-mgr-shared/host_link.h create mode 100644 wamr/core/app-mgr/module.json create mode 100644 wamr/core/config.h create mode 100644 wamr/core/deps/README.md create mode 100755 wamr/core/deps/download.sh create mode 100644 wamr/core/iwasm/README.md create mode 100644 wamr/core/iwasm/aot/aot_loader.c create mode 100644 wamr/core/iwasm/aot/aot_reloc.h create mode 100644 wamr/core/iwasm/aot/aot_runtime.c create mode 100644 wamr/core/iwasm/aot/aot_runtime.h create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_aarch64.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_arm.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_mips.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_thumb.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_x86_32.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_x86_64.c create mode 100644 wamr/core/iwasm/aot/arch/aot_reloc_xtensa.c create mode 100644 wamr/core/iwasm/aot/iwasm_aot.cmake create mode 100644 wamr/core/iwasm/common/arch/invokeNative_aarch64.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_arm.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_arm_vfp.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_em64.asm create mode 100644 wamr/core/iwasm/common/arch/invokeNative_em64.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_general.c create mode 100644 wamr/core/iwasm/common/arch/invokeNative_ia32.asm create mode 100644 wamr/core/iwasm/common/arch/invokeNative_ia32.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_mips.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_thumb.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_thumb_vfp.s create mode 100644 wamr/core/iwasm/common/arch/invokeNative_xtensa.s create mode 100644 wamr/core/iwasm/common/iwasm_common.cmake create mode 100644 wamr/core/iwasm/common/wasm_c_api.c create mode 100644 wamr/core/iwasm/common/wasm_c_api_internal.h create mode 100644 wamr/core/iwasm/common/wasm_exec_env.c create mode 100644 wamr/core/iwasm/common/wasm_exec_env.h create mode 100644 wamr/core/iwasm/common/wasm_memory.c create mode 100644 wamr/core/iwasm/common/wasm_memory.h create mode 100644 wamr/core/iwasm/common/wasm_native.c create mode 100644 wamr/core/iwasm/common/wasm_native.h create mode 100644 wamr/core/iwasm/common/wasm_runtime_common.c create mode 100644 wamr/core/iwasm/common/wasm_runtime_common.h create mode 100644 wamr/core/iwasm/common/wasm_shared_memory.c create mode 100644 wamr/core/iwasm/common/wasm_shared_memory.h create mode 100644 wamr/core/iwasm/compilation/aot.c create mode 100644 wamr/core/iwasm/compilation/aot.h create mode 100644 wamr/core/iwasm/compilation/aot_compiler.c create mode 100644 wamr/core/iwasm/compilation/aot_compiler.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_aot_file.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_compare.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_compare.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_const.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_const.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_control.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_control.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_conversion.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_conversion.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_exception.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_exception.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_function.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_function.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_memory.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_memory.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_numberic.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_numberic.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_parametric.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_parametric.h create mode 100644 wamr/core/iwasm/compilation/aot_emit_variable.c create mode 100644 wamr/core/iwasm/compilation/aot_emit_variable.h create mode 100644 wamr/core/iwasm/compilation/aot_llvm.c create mode 100644 wamr/core/iwasm/compilation/aot_llvm.h create mode 100644 wamr/core/iwasm/compilation/iwasm_compl.cmake create mode 100644 wamr/core/iwasm/include/aot_export.h create mode 100644 wamr/core/iwasm/include/lib_export.h create mode 100644 wamr/core/iwasm/include/wasm_c_api.h create mode 100644 wamr/core/iwasm/include/wasm_export.h create mode 100644 wamr/core/iwasm/interpreter/iwasm_interp.cmake create mode 100644 wamr/core/iwasm/interpreter/wasm.h create mode 100644 wamr/core/iwasm/interpreter/wasm_interp.h create mode 100644 wamr/core/iwasm/interpreter/wasm_interp_classic.c create mode 100644 wamr/core/iwasm/interpreter/wasm_interp_fast.c create mode 100644 wamr/core/iwasm/interpreter/wasm_loader.c create mode 100644 wamr/core/iwasm/interpreter/wasm_loader.h create mode 100644 wamr/core/iwasm/interpreter/wasm_mini_loader.c create mode 100644 wamr/core/iwasm/interpreter/wasm_opcode.h create mode 100644 wamr/core/iwasm/interpreter/wasm_runtime.c create mode 100644 wamr/core/iwasm/interpreter/wasm_runtime.h create mode 100644 wamr/core/iwasm/libraries/lib-pthread/lib_pthread.cmake create mode 100644 wamr/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c create mode 100644 wamr/core/iwasm/libraries/libc-builtin/libc_builtin.cmake create mode 100644 wamr/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c create mode 100644 wamr/core/iwasm/libraries/libc-wasi/libc_wasi.cmake create mode 100644 wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c create mode 100644 wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/LICENSE create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/LICENSE create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/LICENSE create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c create mode 100644 wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h create mode 100644 wamr/core/iwasm/libraries/thread-mgr/thread_manager.c create mode 100644 wamr/core/iwasm/libraries/thread-mgr/thread_manager.h create mode 100644 wamr/core/iwasm/libraries/thread-mgr/thread_mgr.cmake create mode 100644 wamr/core/shared/coap/er-coap/LICENSE.md create mode 100644 wamr/core/shared/coap/er-coap/coap-constants.h create mode 100644 wamr/core/shared/coap/extension/coap_ext.h create mode 100644 wamr/core/shared/coap/lib_coap.cmake create mode 100644 wamr/core/shared/mem-alloc/ems/ems_alloc.c create mode 100644 wamr/core/shared/mem-alloc/ems/ems_gc.h create mode 100644 wamr/core/shared/mem-alloc/ems/ems_gc_internal.h create mode 100644 wamr/core/shared/mem-alloc/ems/ems_hmu.c create mode 100644 wamr/core/shared/mem-alloc/ems/ems_kfc.c create mode 100644 wamr/core/shared/mem-alloc/mem_alloc.c create mode 100644 wamr/core/shared/mem-alloc/mem_alloc.cmake create mode 100644 wamr/core/shared/mem-alloc/mem_alloc.h create mode 100644 wamr/core/shared/platform/README.md create mode 100644 wamr/core/shared/platform/alios/alios_platform.c create mode 100644 wamr/core/shared/platform/alios/alios_thread.c create mode 100644 wamr/core/shared/platform/alios/alios_time.c create mode 100644 wamr/core/shared/platform/alios/platform_internal.h create mode 100644 wamr/core/shared/platform/alios/shared_platform.cmake create mode 100644 wamr/core/shared/platform/android/platform_init.c create mode 100644 wamr/core/shared/platform/android/platform_internal.h create mode 100644 wamr/core/shared/platform/android/shared_platform.cmake create mode 100644 wamr/core/shared/platform/common/math/COPYRIGHT create mode 100644 wamr/core/shared/platform/common/math/math.c create mode 100644 wamr/core/shared/platform/common/math/platform_api_math.cmake create mode 100644 wamr/core/shared/platform/common/posix/platform_api_posix.cmake create mode 100644 wamr/core/shared/platform/common/posix/posix_malloc.c create mode 100644 wamr/core/shared/platform/common/posix/posix_memmap.c create mode 100644 wamr/core/shared/platform/common/posix/posix_thread.c create mode 100644 wamr/core/shared/platform/common/posix/posix_time.c create mode 100644 wamr/core/shared/platform/darwin/platform_init.c create mode 100644 wamr/core/shared/platform/darwin/platform_internal.h create mode 100644 wamr/core/shared/platform/darwin/shared_platform.cmake create mode 100644 wamr/core/shared/platform/include/platform_api_extension.h create mode 100644 wamr/core/shared/platform/include/platform_api_vmcore.h create mode 100644 wamr/core/shared/platform/include/platform_common.h create mode 100644 wamr/core/shared/platform/linux-sgx/platform_internal.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_file.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_file.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_platform.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_pthread.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_pthread.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_signal.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_signal.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_socket.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_socket.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_thread.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_time.c create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_time.h create mode 100644 wamr/core/shared/platform/linux-sgx/sgx_wamr.edl create mode 100644 wamr/core/shared/platform/linux-sgx/shared_platform.cmake create mode 100644 wamr/core/shared/platform/linux-sgx/untrusted/file.c create mode 100644 wamr/core/shared/platform/linux-sgx/untrusted/pthread.c create mode 100644 wamr/core/shared/platform/linux-sgx/untrusted/signal.c create mode 100644 wamr/core/shared/platform/linux-sgx/untrusted/socket.c create mode 100644 wamr/core/shared/platform/linux-sgx/untrusted/time.c create mode 100644 wamr/core/shared/platform/linux/platform_init.c create mode 100644 wamr/core/shared/platform/linux/platform_internal.h create mode 100644 wamr/core/shared/platform/linux/shared_platform.cmake create mode 100644 wamr/core/shared/platform/vxworks/platform_init.c create mode 100644 wamr/core/shared/platform/vxworks/platform_internal.h create mode 100644 wamr/core/shared/platform/vxworks/shared_platform.cmake create mode 100644 wamr/core/shared/platform/windows/platform_init.c create mode 100644 wamr/core/shared/platform/windows/platform_internal.h create mode 100644 wamr/core/shared/platform/windows/posix_malloc.c create mode 100644 wamr/core/shared/platform/windows/shared_platform.cmake create mode 100644 wamr/core/shared/platform/windows/win_memmap.c create mode 100644 wamr/core/shared/platform/windows/win_thread.c create mode 100644 wamr/core/shared/platform/windows/win_time.c create mode 100644 wamr/core/shared/platform/zephyr/platform_internal.h create mode 100644 wamr/core/shared/platform/zephyr/shared_platform.cmake create mode 100644 wamr/core/shared/platform/zephyr/zephyr_platform.c create mode 100644 wamr/core/shared/platform/zephyr/zephyr_thread.c create mode 100644 wamr/core/shared/platform/zephyr/zephyr_time.c create mode 100644 wamr/core/shared/utils/bh_assert.c create mode 100644 wamr/core/shared/utils/bh_assert.h create mode 100644 wamr/core/shared/utils/bh_common.c create mode 100644 wamr/core/shared/utils/bh_common.h create mode 100644 wamr/core/shared/utils/bh_hashmap.c create mode 100644 wamr/core/shared/utils/bh_hashmap.h create mode 100644 wamr/core/shared/utils/bh_list.c create mode 100644 wamr/core/shared/utils/bh_list.h create mode 100644 wamr/core/shared/utils/bh_log.c create mode 100644 wamr/core/shared/utils/bh_log.h create mode 100644 wamr/core/shared/utils/bh_platform.h create mode 100644 wamr/core/shared/utils/bh_queue.c create mode 100644 wamr/core/shared/utils/bh_queue.h create mode 100644 wamr/core/shared/utils/bh_vector.c create mode 100644 wamr/core/shared/utils/bh_vector.h create mode 100644 wamr/core/shared/utils/runtime_timer.c create mode 100644 wamr/core/shared/utils/runtime_timer.h create mode 100644 wamr/core/shared/utils/shared_utils.cmake create mode 100644 wamr/core/shared/utils/uncommon/bh_read_file.c create mode 100644 wamr/core/shared/utils/uncommon/bh_read_file.h create mode 100644 wamr/core/shared/utils/uncommon/shared_uncommon.cmake create mode 100644 wamr/doc/build_wamr.md create mode 100644 wamr/doc/build_wasm_app.md create mode 100644 wamr/doc/embed_wamr.md create mode 100644 wamr/doc/export_native_api.md create mode 100644 wamr/doc/linux_sgx.md create mode 100644 wamr/doc/multi_module.md create mode 100644 wamr/doc/other_wasm_compilers.md create mode 100644 wamr/doc/pics/app_framework.PNG create mode 100644 wamr/doc/pics/embed.PNG create mode 100644 wamr/doc/pics/extend_library.PNG create mode 100644 wamr/doc/pics/multi_module_pic1.png create mode 100644 wamr/doc/pics/native_call_wasm.PNG create mode 100644 wamr/doc/pics/request.PNG create mode 100644 wamr/doc/pics/safe.PNG create mode 100644 wamr/doc/pics/sensor_callflow.PNG create mode 100644 wamr/doc/pics/sub.PNG create mode 100644 wamr/doc/pics/vgl_demo.png create mode 100644 wamr/doc/pics/vgl_demo2.png create mode 100644 wamr/doc/pics/vgl_demo_linux.png create mode 100644 wamr/doc/pics/vgl_linux.PNG create mode 100644 wamr/doc/pics/wamr-arch.JPG create mode 100644 wamr/doc/pics/wamr_menu_config.png create mode 100644 wamr/doc/port_wamr.md create mode 100644 wamr/doc/pthread_library.md create mode 100644 wamr/doc/release_ack.md create mode 100644 wamr/doc/roadmap.md create mode 100644 wamr/doc/wamr_api.md create mode 100644 wamr/doc/wasm_c_api.md create mode 100644 wamr/product-mini/app-samples/hello-world-cmake/CMakeLists.txt create mode 100755 wamr/product-mini/app-samples/hello-world-cmake/build.sh create mode 100644 wamr/product-mini/app-samples/hello-world-cmake/main.c create mode 100644 wamr/product-mini/app-samples/hello-world-cmake/print.c create mode 100755 wamr/product-mini/app-samples/hello-world/build.sh create mode 100644 wamr/product-mini/app-samples/hello-world/main.c create mode 100644 wamr/product-mini/platforms/alios-things/aos.mk create mode 100644 wamr/product-mini/platforms/alios-things/src/main.c create mode 100644 wamr/product-mini/platforms/alios-things/src/test_wasm.h create mode 100644 wamr/product-mini/platforms/android/CMakeLists.txt create mode 100755 wamr/product-mini/platforms/android/build_jit.sh create mode 100755 wamr/product-mini/platforms/android/build_llvm.sh create mode 100644 wamr/product-mini/platforms/android/wasm-jni.cpp create mode 100644 wamr/product-mini/platforms/darwin/CMakeLists.txt create mode 100644 wamr/product-mini/platforms/darwin/main.c create mode 100644 wamr/product-mini/platforms/linux-sgx/CMakeLists.txt create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_private.pem create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp create mode 100644 wamr/product-mini/platforms/linux-sgx/enclave-sample/Makefile create mode 100644 wamr/product-mini/platforms/linux/CMakeLists.txt create mode 100755 wamr/product-mini/platforms/linux/build_jit.sh create mode 100755 wamr/product-mini/platforms/linux/build_llvm.sh create mode 100644 wamr/product-mini/platforms/linux/main.c create mode 100644 wamr/product-mini/platforms/vxworks/CMakeLists.txt create mode 100644 wamr/product-mini/platforms/vxworks/main.c create mode 100644 wamr/product-mini/platforms/windows/CMakeLists.txt create mode 100644 wamr/product-mini/platforms/windows/main.c create mode 100644 wamr/product-mini/platforms/zephyr/simple/CMakeLists.txt create mode 100755 wamr/product-mini/platforms/zephyr/simple/build_and_run.sh create mode 100644 wamr/product-mini/platforms/zephyr/simple/esp32_custom_linker.ld create mode 100644 wamr/product-mini/platforms/zephyr/simple/prj_esp32.conf create mode 100644 wamr/product-mini/platforms/zephyr/simple/prj_nucleo767zi.conf create mode 100644 wamr/product-mini/platforms/zephyr/simple/prj_qemu_cortex_a53.conf create mode 100644 wamr/product-mini/platforms/zephyr/simple/prj_qemu_x86_nommu.conf create mode 100644 wamr/product-mini/platforms/zephyr/simple/prj_qemu_xtensa.conf create mode 100644 wamr/product-mini/platforms/zephyr/simple/src/main.c create mode 100644 wamr/product-mini/platforms/zephyr/simple/src/test_wasm.h create mode 100644 wamr/samples/basic/.gitignore create mode 100644 wamr/samples/basic/CMakeLists.txt create mode 100644 wamr/samples/basic/README.md create mode 100755 wamr/samples/basic/build.sh create mode 100755 wamr/samples/basic/run.sh create mode 100644 wamr/samples/basic/src/main.c create mode 100644 wamr/samples/basic/src/native_impl.c create mode 100644 wamr/samples/basic/wasm-apps/testapp.c create mode 100644 wamr/samples/gui/README.md create mode 100755 wamr/samples/gui/build.sh create mode 100644 wamr/samples/gui/lv_config/lv_conf.h create mode 100644 wamr/samples/gui/lv_config/lv_drv_conf.h create mode 100644 wamr/samples/gui/lv_config/system_header.h create mode 100644 wamr/samples/gui/wamr_config_gui.cmake create mode 100755 wamr/samples/gui/wasm-apps/build_apps.sh create mode 100644 wamr/samples/gui/wasm-apps/decrease/Makefile create mode 100644 wamr/samples/gui/wasm-apps/decrease/src/main.c create mode 100644 wamr/samples/gui/wasm-apps/increase/CMakeLists.txt create mode 100644 wamr/samples/gui/wasm-apps/increase/Makefile create mode 100644 wamr/samples/gui/wasm-apps/increase/src/main.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h create mode 100644 wamr/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt create mode 100644 wamr/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf create mode 100644 wamr/samples/littlevgl/LICENCE.txt create mode 100644 wamr/samples/littlevgl/README.md create mode 100755 wamr/samples/littlevgl/build.sh create mode 100644 wamr/samples/littlevgl/lv_config/lv_conf.h create mode 100644 wamr/samples/littlevgl/lv_config/lv_drv_conf.h create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h create mode 100644 wamr/samples/littlevgl/vgl-native-ui-app/main.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt create mode 100644 wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf create mode 100644 wamr/samples/littlevgl/wamr_config_littlevgl.cmake create mode 100644 wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app create mode 100644 wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi create mode 100755 wamr/samples/littlevgl/wasm-apps/build_wasm_app.sh create mode 100644 wamr/samples/littlevgl/wasm-apps/src/display_indev.h create mode 100644 wamr/samples/littlevgl/wasm-apps/src/main.c create mode 100644 wamr/samples/littlevgl/wasm-apps/src/system_header.h create mode 100644 wamr/samples/multi-module/CMakeLists.txt create mode 100644 wamr/samples/multi-module/src/main.c create mode 100644 wamr/samples/multi-module/wasm-apps/CMakeLists.txt create mode 100644 wamr/samples/multi-module/wasm-apps/mA.c create mode 100644 wamr/samples/multi-module/wasm-apps/mB.c create mode 100644 wamr/samples/multi-module/wasm-apps/mC.c create mode 100644 wamr/samples/multi-thread/CMakeLists.txt create mode 100644 wamr/samples/multi-thread/wasm-apps/CMakeLists.txt create mode 100644 wamr/samples/multi-thread/wasm-apps/main.c create mode 100644 wamr/samples/simple/.gitignore create mode 100644 wamr/samples/simple/CMakeLists.txt create mode 100644 wamr/samples/simple/README.md create mode 100755 wamr/samples/simple/build.sh create mode 100644 wamr/samples/simple/profiles/arm-interp/toolchain.cmake create mode 100644 wamr/samples/simple/profiles/arm-interp/wamr_config_simple.cmake create mode 100644 wamr/samples/simple/profiles/host-aot/wamr_config_simple.cmake create mode 100644 wamr/samples/simple/profiles/host-interp/wamr_config_simple.cmake create mode 100644 wamr/samples/simple/src/iwasm_main.c create mode 100644 wamr/samples/simple/src/main.c create mode 100644 wamr/samples/simple/wasm-apps/connection.c create mode 100644 wamr/samples/simple/wasm-apps/event_publisher.c create mode 100644 wamr/samples/simple/wasm-apps/event_subscriber.c create mode 100644 wamr/samples/simple/wasm-apps/request_handler.c create mode 100644 wamr/samples/simple/wasm-apps/request_sender.c create mode 100644 wamr/samples/simple/wasm-apps/sensor.c create mode 100644 wamr/samples/simple/wasm-apps/timer.c create mode 100644 wamr/samples/spawn-thread/CMakeLists.txt create mode 100644 wamr/samples/spawn-thread/src/main.c create mode 100644 wamr/samples/spawn-thread/wasm-apps/CMakeLists.txt create mode 100644 wamr/samples/spawn-thread/wasm-apps/sum.c create mode 100644 wamr/samples/wasm-c-api/CMakeLists.txt create mode 100644 wamr/samples/wasm-c-api/README.md create mode 100644 wamr/samples/wasm-c-api/src/callback.c create mode 100644 wamr/samples/wasm-c-api/src/callback.wasm create mode 100644 wamr/samples/wasm-c-api/src/callback.wat create mode 100644 wamr/samples/wasm-c-api/src/global.c create mode 100644 wamr/samples/wasm-c-api/src/global.wasm create mode 100644 wamr/samples/wasm-c-api/src/global.wat create mode 100644 wamr/samples/wasm-c-api/src/hello.c create mode 100644 wamr/samples/wasm-c-api/src/hello.wasm create mode 100644 wamr/samples/wasm-c-api/src/hello.wat create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/README.md create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/__init__.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/admin.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/apps.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/migrations/__init__.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/models.py create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/application.html create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/__init__.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/appstore.css create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/index.css create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/index.js create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/app(1).png create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/application.png create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/delete.png create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/download(1).png create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/menu.png create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/milky-way-2695569_1280.jpg create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/net_device.png create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/software-icon-32081.png create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/photo/totalblack.png create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/connection.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_publisher.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/event_subscriber.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_handler.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/request_sender.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sensor.wasm create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/simple create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/connection.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_publisher.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/event_subscriber.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_handler.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/request_sender.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/sys/timer.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/timer.wasm create mode 100644 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/ui_app.wasm create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/vgl_wasm_runtime create mode 100755 wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/upload/wasm_runtime_wgl create mode 100644 wamr/test-tools/binarydump-tool/CMakeLists.txt create mode 100644 wamr/test-tools/binarydump-tool/binarydump.c create mode 100644 wamr/test-tools/component_test/README.md create mode 100644 wamr/test-tools/component_test/__init__.py create mode 100644 wamr/test-tools/component_test/framework/__init__.py create mode 100644 wamr/test-tools/component_test/framework/case_base.py create mode 100644 wamr/test-tools/component_test/framework/engine.py create mode 100644 wamr/test-tools/component_test/framework/framework.py create mode 100644 wamr/test-tools/component_test/framework/suite.py create mode 100644 wamr/test-tools/component_test/framework/test_api.py create mode 100644 wamr/test-tools/component_test/framework/test_utils.py create mode 100644 wamr/test-tools/component_test/harness/__init__.py create mode 100644 wamr/test-tools/component_test/harness/harness_api.py create mode 100644 wamr/test-tools/component_test/host-clients/src/host_app_sample.c create mode 100644 wamr/test-tools/component_test/host-clients/src/makefile create mode 100755 wamr/test-tools/component_test/set_dev_env.sh create mode 100755 wamr/test-tools/component_test/start.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/case.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/cases/__init__.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/suite_setup.py create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/01_install.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/02_request.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/03_event.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_req.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_resp.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_provider.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_subscriber.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/06_timer.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/07_sensor.c create mode 100644 wamr/test-tools/component_test/suites/01-life-cycle/test-app/08_on_destroy.c create mode 100755 wamr/test-tools/component_test/suites/01-life-cycle/test-app/build.sh create mode 100755 wamr/test-tools/component_test/suites/01-life-cycle/tools/product/start.sh create mode 100755 wamr/test-tools/component_test/suites/01-life-cycle/tools/product/stop.sh create mode 100644 wamr/test-tools/component_test/suites/__init__.py create mode 100644 wamr/test-tools/component_test/suites/readme.txt create mode 100644 wamr/test-tools/host-tool/CMakeLists.txt create mode 100644 wamr/test-tools/host-tool/external/cJSON/LICENSE create mode 100644 wamr/test-tools/host-tool/external/cJSON/cJSON.c create mode 100644 wamr/test-tools/host-tool/external/cJSON/cJSON.h create mode 100644 wamr/test-tools/host-tool/external/cJSON/cjson.cmake create mode 100644 wamr/test-tools/host-tool/src/host_tool_utils.c create mode 100644 wamr/test-tools/host-tool/src/host_tool_utils.h create mode 100644 wamr/test-tools/host-tool/src/main.c create mode 100644 wamr/test-tools/host-tool/src/transport.c create mode 100644 wamr/test-tools/host-tool/src/transport.h create mode 100644 wamr/wamr-compiler/CMakeLists.txt create mode 100644 wamr/wamr-compiler/build_llvm.py create mode 100755 wamr/wamr-compiler/build_llvm.sh create mode 100755 wamr/wamr-compiler/build_llvm_xtensa.sh create mode 100644 wamr/wamr-compiler/main.c create mode 100644 wamr/wamr-sdk/Kconfig create mode 100644 wamr/wamr-sdk/Makefile create mode 100644 wamr/wamr-sdk/README.md create mode 100644 wamr/wamr-sdk/app/CMakeLists.txt create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/assert.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/ctype.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/errno.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/fcntl.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/inttypes.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/limits.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdbool.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdio.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdlib.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/string.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/include/strings.h create mode 100644 wamr/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt create mode 100644 wamr/wamr-sdk/app/wamr_toolchain.cmake create mode 100644 wamr/wamr-sdk/app/wasi_toolchain.cmake create mode 100755 wamr/wamr-sdk/build_sdk.sh create mode 100755 wamr/wamr-sdk/menuconfig.sh create mode 100644 wamr/wamr-sdk/runtime/CMakeLists.txt create mode 100644 wamr/wamr-sdk/wamr_config_default.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8fabac1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +#set(CMAKE_C_COMPILER "/home/dafran/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-gcc") +#set(CMAKE_CXX_COMPILER "/home/dafran/gcc-arm-none-eabi-9-2020-q2-update/bin/arm-none-eabi-g++") +#find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "zephyr") + +# Build as X86_32 by default, change to "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" +# if we want to support arm, thumb, mips or xtensa +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "X86_32") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +#add_executable (app src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE}) +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + ${app_sources}) +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/net/ip) + diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000..71f419d --- /dev/null +++ b/Kconfig @@ -0,0 +1,26 @@ +# Copyright (c) 2020, Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config LOG + default y + +config LOG_PRINTK + default y + +config SENSOR_LOG_LEVEL + default 4 + +# Workaround for not being able to have commas in macro arguments +DT_COMPAT_BOSCH_BME280 := bosch,bme280 + +# Enable SPI support by default if there are any BME280 sensors +# on a SPI bus. +config SPI + default $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BME280),spi) + +# Enable I2C support by default if there are any BME280 sensors +# on an I2C bus. +config I2C + default $(dt_compat_on_bus,$(DT_COMPAT_BOSCH_BME280),i2c) + +source "Kconfig.zephyr" diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..0cd1f6d --- /dev/null +++ b/README.rst @@ -0,0 +1,43 @@ +.. _bme280: + +BME280 Humidity and Pressure Sensor +################################### + +Overview +******** + +This sample application periodically reads temperature, pressure and humidity data from +the first available device that implements SENSOR_CHAN_AMBIENT_TEMP, SENSOR_CHAN_PRESS, +and SENSOR_CHAN_HUMIDITY. This sample checks the sensor in polling mode (without +interrupt trigger). + +Building and Running +******************** + +This sample application uses an BME280 sensor connected to a board via I2C. +Connect the sensor pins according to the connection diagram given in the `bme280 datasheet`_ +at page 38. + + +.. zephyr-app-commands:: + :zephyr-app: samples/sensor/bme280 + :board: nrf52840dk_nrf52840 + :goals: flash + :compact: + +Sample Output +============= +To check output of this sample , any serial console program can be used. +This example uses ``picocom`` on the serial port ``/dev/ttyACM0``: + +.. code-block:: console + + $ sudo picocom -D /dev/ttyUSB0 + +.. code-block:: console + + temp: 27.950000; press: 100.571027; humidity: 61.014648 + temp: 27.940000; press: 100.570269; humidity: 61.012695 + temp: 27.950000; press: 100.570695; humidity: 61.002929 + +.. _bme280 datasheet: https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280-DS002.pdf diff --git a/boards/nrf52840dk_nrf52840.overlay b/boards/nrf52840dk_nrf52840.overlay new file mode 100644 index 0000000..a7e9480 --- /dev/null +++ b/boards/nrf52840dk_nrf52840.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2020, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&i2c0 { + bme280@76 { + compatible = "bosch,bme280"; + reg = <0x76>; + label = "BME280"; + }; +}; diff --git a/build_and_run.sh b/build_and_run.sh new file mode 100755 index 0000000..724a942 --- /dev/null +++ b/build_and_run.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +X86_TARGET="x86" +STM32_TARGET="stm32" +QEMU_CORTEX_A53="qemu_cortex_a53" +XTENSA_QEMU_TARGET="xtensa-qemu" +ESP32_TARGET="esp32" +NRF52840_DK="nrf52840dk_nrf52840" + +usage () +{ + echo "USAGE:" + echo "$0 $X86_TARGET|$STM32_TARGET|$QEMU_CORTEX_A53|$XTENSA_QEMU_TARGET|$ESP32_TARGET|$NRF52840_DK" + echo "Example:" + echo " $0 $X86_TARGET" + echo " $0 $STM32_TARGET" + echo " $0 $QEMU_CORTEX_A53" + echo " $0 $XTENSA_QEMU_TARGET" + echo " $0 $ESP32_TARGET" + echo " $0 $NRF52840_DK" + exit 1 +} + +if [ $# != 1 ] ; then + usage +fi + +TARGET=$1 + +case $TARGET in + $X86_TARGET) + west build -b qemu_x86_nommu \ + . -p always -- \ + -DCONF_FILE=prj_qemu_x86_nommu.conf \ + -DWAMR_BUILD_TARGET=X86_32 + west build -t run + ;; + $STM32_TARGET) + west build -b nucleo_f767zi \ + . -p always -- \ + -DCONF_FILE=prj_nucleo767zi.conf \ + -DWAMR_BUILD_TARGET=THUMBV7 + west flash + ;; + $XTENSA_QEMU_TARGET) + west build -b qemu_xtensa \ + . -p always -- \ + -DCONF_FILE=prj_qemu_xtensa.conf \ + -DWAMR_BUILD_TARGET=XTENSA + west build -t run + ;; + $ESP32_TARGET) + # suppose you have set environment variable ESP_IDF_PATH + west build -b esp32 \ + . -p always -- \ + -DESP_IDF_PATH=$ESP_IDF_PATH \ + -DCONF_FILE=prj_esp32.conf \ + -DWAMR_BUILD_TARGET=XTENSA + # suppose the serial port is /dev/ttyUSB1 and you should change to + # the real name accordingly + west flash --esp-device /dev/ttyUSB1 + ;; + $QEMU_CORTEX_A53) + west build -b qemu_cortex_a53 \ + . -p always -- \ + -DCONF_FILE=prj_qemu_cortex_a53.conf \ + -DWAMR_BUILD_TARGET=AARCH64 + west build -t run + ;; + $NRF52840_DK) + west build -b nrf52840dk_nrf52840 \ + . -p always -- \ + -DCONF_FILE=prf_nrf52840_dk.conf \ + -DWAMR_BUILD_TARGET=ARM + west build -t run + ;; + *) + echo "unsupported target: $TARGET" + usage + exit 1 + ;; +esac + diff --git a/esp32_custom_linker.ld b/esp32_custom_linker.ld new file mode 100644 index 0000000..3593205 --- /dev/null +++ b/esp32_custom_linker.ld @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016 Cadence Design Systems, Inc. + * Copyright (c) 2017 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Linker command/script file + * + * Linker script for the Xtensa platform. + */ + +#include +#include +#include +#include +#include + +#define RAMABLE_REGION dram0_0_seg :dram0_0_phdr +#define RAMABLE_REGION1 dram0_1_seg :dram0_0_phdr +#define ROMABLE_REGION iram0_0_seg :iram0_0_phdr + +PROVIDE ( __stack = 0x3ffe3f20 ); + +PROVIDE ( esp32_rom_uart_tx_one_char = 0x40009200 ); +PROVIDE ( esp32_rom_uart_rx_one_char = 0x400092d0 ); +PROVIDE ( esp32_rom_uart_attach = 0x40008fd0 ); +PROVIDE ( esp32_rom_intr_matrix_set = 0x4000681c ); +PROVIDE ( esp32_rom_gpio_matrix_in = 0x40009edc ); +PROVIDE ( esp32_rom_gpio_matrix_out = 0x40009f0c ); +PROVIDE ( esp32_rom_Cache_Flush = 0x40009a14 ); +PROVIDE ( esp32_rom_Cache_Read_Enable = 0x40009a84 ); +PROVIDE ( esp32_rom_ets_set_appcpu_boot_addr = 0x4000689c ); + +MEMORY +{ + iram0_0_seg(RX): org = 0x40080000, len = 0x20000 + iram0_2_seg(RX): org = 0x400D0018, len = 0x330000 + dram0_0_seg(RW): org = 0x3FFB0000, len = 0x30000 + dram0_1_seg(RWX):org = 0x400A0000, len = 0x20000 + drom0_0_seg(R): org = 0x3F400010, len = 0x800000 + rtc_iram_seg(RWX): org = 0x400C0000, len = 0x2000 + rtc_slow_seg(RW): org = 0x50000000, len = 0x1000 +#ifdef CONFIG_GEN_ISR_TABLES + IDT_LIST(RW): org = 0x3ebfe010, len = 0x2000 +#endif +} + +PHDRS +{ + iram0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; +} + +/* Default entry point: */ +PROVIDE ( _ResetVector = 0x40000400 ); +ENTRY(CONFIG_KERNEL_ENTRY) + +_rom_store_table = 0; + +PROVIDE(_memmap_vecbase_reset = 0x40000450); +PROVIDE(_memmap_reset_vector = 0x40000400); + +SECTIONS +{ + +#include + + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : ALIGN(4) + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } GROUP_LINK_IN(ROMABLE_REGION) + +#include +#include + + SECTION_PROLOGUE(_TEXT_SECTION_NAME, , ALIGN(4)) + { + /* Code marked as running out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + *(.iram0.literal .iram.literal .iram.text.literal .iram0.text .iram.text) + *(.literal .text .literal.* .text.*) + _iram_text_end = ABSOLUTE(.); + } GROUP_LINK_IN(ROMABLE_REGION) + + .dram0.text : + { + _data_start = ABSOLUTE(.); + *(.aot_code_buf) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } GROUP_LINK_IN(RAMABLE_REGION1) + + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + KEEP(*(.jcr)) + *(.dram1 .dram1.*) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } GROUP_LINK_IN(RAMABLE_REGION) + + SECTION_PROLOGUE(_RODATA_SECTION_NAME,,ALIGN(4)) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + KEEP (*(.xt_except_table)) + KEEP (*(.gcc_except_table .gcc_except_table.*)) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + KEEP (*(.eh_frame)) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _rodata_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + /* Shared RAM */ + SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + SECTION_DATA_PROLOGUE(_APP_NOINIT_SECTION_NAME, (NOLOAD),) + { + . = ALIGN (8); + *(.app_noinit) + *("app_noinit.*") + . = ALIGN (8); + _app_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + SECTION_DATA_PROLOGUE(_NOINIT_SECTION_NAME, (NOLOAD),) + { + . = ALIGN (8); + *(.noinit) + *(".noinit.*") + . = ALIGN (8); + _heap_start = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + +#ifdef CONFIG_GEN_ISR_TABLES +#include +#endif + +#include + + SECTION_PROLOGUE(.xtensa.info, 0,) + { + *(.xtensa.info) + } + +} diff --git a/overlay-ot.conf b/overlay-ot.conf new file mode 100644 index 0000000..e9c7ed2 --- /dev/null +++ b/overlay-ot.conf @@ -0,0 +1,38 @@ +CONFIG_NEWLIB_LIBC=y +CONFIG_OPENTHREAD_COAP=y +CONFIG_OPENTHREAD_MANUAL_START=y +# Disable TCP and IPv4 (TCP disabled to avoid heavy traffic) +CONFIG_NET_TCP=n +CONFIG_NET_IPV4=n + +CONFIG_NET_IPV6_NBR_CACHE=n +CONFIG_NET_IPV6_MLD=n +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="" + +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_SHELL_STACK_SIZE=3072 + +CONFIG_NET_L2_OPENTHREAD=y + +CONFIG_OPENTHREAD_DEBUG=y +CONFIG_OPENTHREAD_L2_DEBUG=y +CONFIG_OPENTHREAD_L2_LOG_LEVEL_INF=y + +CONFIG_OPENTHREAD_CHANNEL=25 +CONFIG_OPENTHREAD_PANID=64206 +CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread" + +CONFIG_NET_CONFIG_MY_IPV6_ADDR="fdde:ad00:beef::1" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="fdde:ad00:beef:0:9a2a:2a02:71bf:f061" + +# mbedTLS tweaks +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=768 + +# A sample configuration to enable Thread Joiner, uncomment if needed +#CONFIG_OPENTHREAD_JOINER=y +#CONFIG_OPENTHREAD_JOINER_AUTOSTART=y + +# Enable diagnostic module, uncomment if needed +#CONFIG_OPENTHREAD_DIAG=y diff --git a/prf_nrf52840_dk.conf b/prf_nrf52840_dk.conf new file mode 100644 index 0000000..31d17f5 --- /dev/null +++ b/prf_nrf52840_dk.conf @@ -0,0 +1,63 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_I2C_1=y +CONFIG_SENSOR=y +CONFIG_BME280=y +# Generic networking options +CONFIG_NETWORKING=y +CONFIG_NEWLIB_LIBC=y +CONFIG_NET_UDP=y +CONFIG_NET_TCP=y +CONFIG_NET_IPV6=y +CONFIG_NET_IPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y +CONFIG_NET_SOCKETS_POLL_MAX=4 +CONFIG_NET_CONNECTION_MANAGER=y + +CONFIG_COAP=y + +# Kernel options +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ENTROPY_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_INIT_STACKS=y + +# Logging +CONFIG_NET_LOG=y +CONFIG_LOG=y +CONFIG_NET_STATISTICS=y +CONFIG_PRINTK=y + +CONFIG_HEAP_MEM_POOL_SIZE=2048 + +# Network buffers +CONFIG_NET_PKT_RX_COUNT=16 +CONFIG_NET_PKT_TX_COUNT=16 +CONFIG_NET_BUF_RX_COUNT=80 +CONFIG_NET_BUF_TX_COUNT=80 +CONFIG_NET_CONTEXT_NET_PKT_POOL=y + +# IP address options +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=4 +CONFIG_NET_MAX_CONTEXTS=10 + +# Network shell +CONFIG_NET_SHELL=y + +# The addresses are selected so that qemu<->qemu connectivity works ok. +# For linux<->qemu connectivity, create a new conf file and swap the +# addresses (so that peer address is ending to 2). +CONFIG_NET_CONFIG_SETTINGS=y +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::2" +CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::1" +CONFIG_NET_CONFIG_NEED_IPV4=y +CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.2" +CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.1" + + diff --git a/prj_esp32.conf b/prj_esp32.conf new file mode 100644 index 0000000..5d6a67f --- /dev/null +++ b/prj_esp32.conf @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y +CONFIG_CUSTOM_LINKER_SCRIPT="esp32_custom_linker.ld" diff --git a/prj_nucleo767zi.conf b/prj_nucleo767zi.conf new file mode 100644 index 0000000..c495644 --- /dev/null +++ b/prj_nucleo767zi.conf @@ -0,0 +1,7 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MPU=y +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/prj_qemu_cortex_a53.conf b/prj_qemu_cortex_a53.conf new file mode 100644 index 0000000..d248565 --- /dev/null +++ b/prj_qemu_cortex_a53.conf @@ -0,0 +1,7 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MMU=n +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/prj_qemu_x86_nommu.conf b/prj_qemu_x86_nommu.conf new file mode 100644 index 0000000..7f4a328 --- /dev/null +++ b/prj_qemu_x86_nommu.conf @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/prj_qemu_xtensa.conf b/prj_qemu_xtensa.conf new file mode 100644 index 0000000..7f4a328 --- /dev/null +++ b/prj_qemu_xtensa.conf @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/sample.yaml b/sample.yaml new file mode 100644 index 0000000..ddc535d --- /dev/null +++ b/sample.yaml @@ -0,0 +1,22 @@ +sample: + name: BME280 Sensor sample +tests: + sample.sensor.bme280: + harness: console + tags: sensors + platform_whitelist: nrf52840dk_nrf52840 + harness_config: + type: one_line + regex: + - "temp: (.*); press: (.*); humidity: (.*)" + fixture: fixture_i2c_bme280 + sample.sensor.bme280.spi: + harness: console + tags: sensors + depends_on: spi bme280 + extra_args: "CONF_FILE=prj_spi.conf" + harness_config: + type: one_line + regex: + - "temp: (.*); press: (.*); humidity: (.*)" + fixture: fixture_spi_bme280 diff --git a/src/API.h b/src/API.h new file mode 100644 index 0000000..8abb129 --- /dev/null +++ b/src/API.h @@ -0,0 +1,18 @@ +#include + +typedef struct onboard_LED LED; +struct onboard_LED{ + int ID; + bool status; + struct device *dev; + char *p; + void (*turn)(void *myled, int action); + void (*blink)(void *myled); +}; + +void read(char *device,char *env); +void init_led(void *myled); +void turn(void *myled, int action); +void blink(void *myled); +void led(); +void unpack(char * buffer,int size); diff --git a/src/convert.py b/src/convert.py new file mode 100644 index 0000000..4cba851 --- /dev/null +++ b/src/convert.py @@ -0,0 +1,3 @@ +with open('test.wasm', 'r') as fp: + hex_list = ["0x{:02x}".format(ord(c)) for c in fp.read()] + print hex_list diff --git a/src/fac.c b/src/fac.c new file mode 100644 index 0000000..6e66f70 --- /dev/null +++ b/src/fac.c @@ -0,0 +1,14 @@ +// C program to find factorial of given number + +#include "bh_platform.h" +#include "wasm_export.h" +#include "math.h" + +// Function to find factorial of given number +int factorial(wasm_exec_env_t exec_env,int n) +{ + if (n == 0) + return 1; + return n * factorial(exec_env,n - 1); +} + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..fe2069b --- /dev/null +++ b/src/main.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +LOG_MODULE_REGISTER(net_echo_client_sample, LOG_LEVEL_DBG); + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "openthread/mqttsn.h" + +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "test_wasm.h" + +#define GATEWAY_PORT 47193 +#define GATEWAY_ADDRESS "fdde:ad00:beef:0:9a2a:2a02:71bf:f061" + +#define CLIENT_ID "NRF52840" +#define CLIENT_PORT 10000 + +#define CONFIG_GLOBAL_HEAP_BUF_SIZE 131072 +#define CONFIG_APP_STACK_SIZE 8192 +#define CONFIG_APP_HEAP_SIZE 8192 + +#ifdef CONFIG_NO_OPTIMIZATIONS +#define CONFIG_MAIN_THREAD_STACK_SIZE 8192 +#else +#define CONFIG_MAIN_THREAD_STACK_SIZE 4096 +#endif + +void mqttsnConnect(wasm_exec_env_t exec_env); +int convert(int cycles); +bool iwasm_init(void); + +static void on_thread_state_changed(uint32_t flags, void *context) +{ + struct openthread_context *ot_context =(struct openthread_context*) context; + + if (flags & OT_CHANGED_THREAD_ROLE) { + switch (otThreadGetDeviceRole(ot_context->instance)) { + case OT_DEVICE_ROLE_CHILD: + NET_INFO("ID: 2\n"); + break; + case OT_DEVICE_ROLE_ROUTER: + NET_INFO("ID: 3\n"); + iwasm_init(); + break; + case OT_DEVICE_ROLE_LEADER: + NET_INFO("ID: 4\n"); + break; + case OT_DEVICE_ROLE_DISABLED: + case OT_DEVICE_ROLE_DETACHED: + NET_INFO("ID: 1\n"); + break; + default: + NET_INFO("ID: Unknown\n"); + break; + } + } +} + +void mqttsnConnect(wasm_exec_env_t exec_env) +{ + otInstance *instance=openthread_get_default_instance(); + printf("Entered\n"); + otIp6Address address; + otIp6AddressFromString(GATEWAY_ADDRESS, &address); + otMqttsnConfig config; + config.mClientId = CLIENT_ID; + config.mKeepAlive = 30; + config.mCleanSession = true; + config.mPort = GATEWAY_PORT; + config.mAddress = &address; + config.mRetransmissionCount = 3; + config.mRetransmissionTimeout = 10; + otMqttsnConnect(instance, &config); + //otMqttsnDisconnect(instance); +} + + +int get_time(){ + return k_cycle_get_32(); +} +int convert(int cycles){ + return SYS_CLOCK_HW_CYCLES_TO_NS(cycles); +} + +static int app_argc; +static char **app_argv; + +/** + * Find the unique main function from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the main function is called, false otherwise. + */ +bool +wasm_application_execute_main(wasm_module_inst_t module_inst, + int argc, char *argv[]); + +static void* +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static char global_heap_buf[CONFIG_GLOBAL_HEAP_BUF_SIZE] = { 0 }; + +#ifdef CONFIG_BOARD_ESP32 +#include "mem_alloc.h" +/* +esp32_technical_reference_manual: +" +The capacity of Internal SRAM 1 is 128 KB. Either CPU can read and write this memory at addresses +0x3FFE_0000 ~ 0x3FFF_FFFF of the data bus, and also at addresses 0x400A_0000 ~ 0x400B_FFFF of the +instruction bus. +" + +The custom linker script defines dram0_1_seg and map it to 0x400A_0000 ~ 0x400B_FFFF for instruction bus access. +Here we define the buffer that will be placed to dram0_1_seg. +*/ +static char esp32_executable_memory_buf[100 * 1024] __attribute__((section (".aot_code_buf"))) = { 0 }; + +/* the poll allocator for executable memory */ +static mem_allocator_t esp32_exec_mem_pool_allocator; + +static int +esp32_exec_mem_init() +{ + if (!(esp32_exec_mem_pool_allocator = + mem_allocator_create(esp32_executable_memory_buf, + sizeof(esp32_executable_memory_buf)))) + return -1; + + return 0; +} + +static void +esp32_exec_mem_destroy() +{ + mem_allocator_destroy(esp32_exec_mem_pool_allocator); +} + +static void * +esp32_exec_mem_alloc(unsigned int size) +{ + return mem_allocator_malloc(esp32_exec_mem_pool_allocator, size); +} + +static void +esp32_exec_mem_free(void *addr) +{ + mem_allocator_free(esp32_exec_mem_pool_allocator, addr); +} +#endif /* end of #ifdef CONFIG_BOARD_ESP32 */ + +void iwasm_main(void *arg1, void *arg2, void *arg3) +{ + int start, end; + start = k_uptime_get_32(); + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + + (void) arg1; + (void) arg2; + (void) arg3; + + static NativeSymbol native_symbols[] = + { + + { + "get_time", // the name of WASM function name + get_time, // the native function pointer + "()i", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + }, + { + "convert", // the name of WASM function name + convert, // the native function pointer + "(i)i", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + }, + { + "mqttsnConnect", // the name of WASM function name + mqttsnConnect, // the native function pointer + "()", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + } + + + }; + + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } + +#ifdef CONFIG_BOARD_ESP32 + /* Initialize executable memory */ + if (esp32_exec_mem_init() != 0) { + printf("Init executable memory failed.\n"); + goto fail1; + } + /* Set hook functions for executable memory management */ + set_exec_mem_alloc_func(esp32_exec_mem_alloc, esp32_exec_mem_free); +#endif + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8*) wasm_test_file4; + wasm_file_size = sizeof(wasm_test_file4); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf,wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); +#ifdef CONFIG_BOARD_ESP32 + goto fail1_1; +#else + goto fail1; +#endif + } + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + CONFIG_APP_STACK_SIZE, + CONFIG_APP_HEAP_SIZE, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* invoke the main function */ + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +#ifdef CONFIG_BOARD_ESP32 +fail1_1: + /* destroy executable memory */ + esp32_exec_mem_destroy(); +#endif + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + end = k_uptime_get_32(); + + printf("elpase: %d\n", (end - start)); +} + +#define MAIN_THREAD_STACK_SIZE (CONFIG_MAIN_THREAD_STACK_SIZE) +#define MAIN_THREAD_PRIORITY 5 + +K_THREAD_STACK_DEFINE(iwasm_main_thread_stack, MAIN_THREAD_STACK_SIZE); +static struct k_thread iwasm_main_thread; + +bool iwasm_init(void) +{ + k_tid_t tid = k_thread_create(&iwasm_main_thread, iwasm_main_thread_stack, + MAIN_THREAD_STACK_SIZE, + iwasm_main, NULL, NULL, NULL, + MAIN_THREAD_PRIORITY, 0, K_NO_WAIT); + return tid ? true : false; +} +void main(void) +{ + openthread_set_state_changed_cb(on_thread_state_changed); + openthread_start(openthread_get_default_context()); + printf("CALL INTO MAIN\n"); + //iwasm_init(); + printf("MAIN CALLED\n"); +} + + diff --git a/src/test_wasm.h b/src/test_wasm.h new file mode 100644 index 0000000..c511e55 --- /dev/null +++ b/src/test_wasm.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * The byte array buffer is the file content of a test wasm binary file, + * which is compiled by emcc or clang toolchain from C source file of: + * core/iwasm/app-samples/hello-world/main.c. + */ +unsigned char __aligned(4) wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, + 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, + 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, + 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, + 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, + 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, + 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, + 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, + 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, + 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, + 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, + 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, + 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, + 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, + 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, + 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, + 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, + 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, + 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, + 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, + 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, + 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, + 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, + 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, + 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, + 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, + 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, + 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, + 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, + 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, + 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, + 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, + 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, + 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, + 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, + 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, + 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; + +unsigned char __aligned(4) wasm_test_file1[] = { +0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x02, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x1e, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x09, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x00, 0x01, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x07, 0x01, 0x7f, 0x01, 0x41, 0xb0, 0x28, 0x0b, 0x07, 0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x0a, 0xa2, 0x01, 0x01, 0x9f, 0x01, 0x01, 0x0d, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x20, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x1c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x18, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x14, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x06, 0x41, 0x00, 0x21, 0x07, 0x20, 0x06, 0x20, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x0a, 0x21, 0x08, 0x20, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x21, 0x09, 0x20, 0x04, 0x20, 0x09, 0x36, 0x02, 0x10, 0x20, 0x04, 0x28, 0x02, 0x10, 0x21, 0x0a, 0x20, 0x04, 0x20, 0x0a, 0x36, 0x02, 0x00, 0x41, 0x9f, 0x88, 0x80, 0x80, 0x00, 0x21, 0x0b, 0x20, 0x0b, 0x20, 0x04, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x0c, 0x41, 0x20, 0x21, 0x0d, 0x20, 0x04, 0x20, 0x0d, 0x6a, 0x21, 0x0e, 0x20, 0x0e, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x0c, 0x0f, 0x0b, 0x0b, 0x2e, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x27, 0x43, 0x61, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x61, 0x6c, 0x63, 0x75, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x66, 0x61, 0x63, 0x2e, 0x2e, 0x2e, 0x0a, 0x00, 0x46, 0x61, 0x63, 0x3a, 0x25, 0x64, 0x0a, 0x00 +}; +unsigned char __aligned(4) wasm_test_file2[]={ +0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x10, 0x03, 0x60, 0x00, 0x01, 0x7f, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x02, 0x3b, 0x04, 0x03, 0x65, 0x6e, 0x76, 0x08, 0x67, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x09, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x07, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x00, 0x01, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x07, 0x01, 0x7f, 0x01, 0x41, 0xb0, 0x28, 0x0b, 0x07, 0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x04, 0x0a, 0x89, 0x03, 0x01, 0x86, 0x03, 0x01, 0x24, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0xd0, 0x00, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x4c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x48, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x44, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x06, 0x20, 0x04, 0x20, 0x06, 0x36, 0x02, 0x38, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x40, 0x02, 0x40, 0x03, 0x40, 0x41, 0x0a, 0x21, 0x07, 0x20, 0x04, 0x28, 0x02, 0x40, 0x21, 0x08, 0x20, 0x08, 0x21, 0x09, 0x20, 0x07, 0x21, 0x0a, 0x20, 0x09, 0x20, 0x0a, 0x48, 0x21, 0x0b, 0x41, 0x01, 0x21, 0x0c, 0x20, 0x0b, 0x20, 0x0c, 0x71, 0x21, 0x0d, 0x20, 0x0d, 0x45, 0x0d, 0x01, 0x41, 0x0a, 0x21, 0x0e, 0x20, 0x0e, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x21, 0x0f, 0x20, 0x04, 0x20, 0x0f, 0x36, 0x02, 0x3c, 0x20, 0x04, 0x28, 0x02, 0x3c, 0x21, 0x10, 0x20, 0x04, 0x20, 0x10, 0x36, 0x02, 0x00, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x11, 0x20, 0x11, 0x20, 0x04, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x40, 0x21, 0x12, 0x41, 0x01, 0x21, 0x13, 0x20, 0x12, 0x20, 0x13, 0x6a, 0x21, 0x14, 0x20, 0x04, 0x20, 0x14, 0x36, 0x02, 0x40, 0x0c, 0x00, 0x0b, 0x0b, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x15, 0x20, 0x04, 0x20, 0x15, 0x36, 0x02, 0x34, 0x20, 0x04, 0x28, 0x02, 0x34, 0x21, 0x16, 0x20, 0x04, 0x28, 0x02, 0x38, 0x21, 0x17, 0x20, 0x16, 0x20, 0x17, 0x6b, 0x21, 0x18, 0x20, 0x04, 0x20, 0x18, 0x36, 0x02, 0x30, 0x20, 0x04, 0x28, 0x02, 0x30, 0x21, 0x19, 0x20, 0x19, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, 0x21, 0x1a, 0x20, 0x04, 0x20, 0x1a, 0x36, 0x02, 0x2c, 0x20, 0x04, 0x28, 0x02, 0x30, 0x21, 0x1b, 0x20, 0x04, 0x20, 0x1b, 0x36, 0x02, 0x10, 0x41, 0x8b, 0x88, 0x80, 0x80, 0x00, 0x21, 0x1c, 0x41, 0x10, 0x21, 0x1d, 0x20, 0x04, 0x20, 0x1d, 0x6a, 0x21, 0x1e, 0x20, 0x1c, 0x20, 0x1e, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x20, 0x04, 0x28, 0x02, 0x2c, 0x21, 0x1f, 0x20, 0x04, 0x20, 0x1f, 0x36, 0x02, 0x20, 0x41, 0x97, 0x88, 0x80, 0x80, 0x00, 0x21, 0x20, 0x41, 0x20, 0x21, 0x21, 0x20, 0x04, 0x20, 0x21, 0x6a, 0x21, 0x22, 0x20, 0x20, 0x20, 0x22, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x23, 0x41, 0xd0, 0x00, 0x21, 0x24, 0x20, 0x04, 0x20, 0x24, 0x6a, 0x21, 0x25, 0x20, 0x25, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x23, 0x0f, 0x0b, 0x0b, 0x2b, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x24, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x3a, 0x25, 0x64, 0x0a, 0x00, 0x43, 0x79, 0x63, 0x6c, 0x65, 0x73, 0x3a, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x4e, 0x61, 0x6e, 0x6f, 0x53, 0x65, 0x63, 0x3a, 0x20, 0x25, 0x64, 0x0a, 0x00 + +}; +unsigned char __aligned(4) wasm_test_file3[]={ +0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x15, 0x04, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x02, 0x42, 0x05, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x6d, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x08, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x6c, 0x65, 0x64, 0x00, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x04, 0x74, 0x75, 0x72, 0x6e, 0x00, 0x01, 0x03, 0x02, 0x01, 0x03, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x07, 0x01, 0x7f, 0x01, 0x41, 0x90, 0x28, 0x0b, 0x07, 0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x05, 0x0a, 0x81, 0x02, 0x01, 0xfe, 0x01, 0x01, 0x19, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0xc0, 0x00, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x20, 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x6a, 0x21, 0x06, 0x20, 0x06, 0x21, 0x07, 0x41, 0x10, 0x21, 0x08, 0x20, 0x04, 0x20, 0x08, 0x6a, 0x21, 0x09, 0x20, 0x09, 0x21, 0x0a, 0x41, 0x01, 0x21, 0x0b, 0x41, 0x04, 0x21, 0x0c, 0x41, 0x00, 0x21, 0x0d, 0x20, 0x04, 0x20, 0x0d, 0x36, 0x02, 0x3c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x38, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x34, 0x20, 0x04, 0x20, 0x0c, 0x36, 0x02, 0x0c, 0x20, 0x0b, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x0e, 0x20, 0x04, 0x20, 0x0e, 0x36, 0x02, 0x28, 0x20, 0x04, 0x28, 0x02, 0x28, 0x21, 0x0f, 0x20, 0x0f, 0x2d, 0x00, 0x00, 0x21, 0x10, 0x20, 0x0a, 0x20, 0x10, 0x3a, 0x00, 0x00, 0x20, 0x04, 0x28, 0x02, 0x0c, 0x21, 0x11, 0x20, 0x0a, 0x20, 0x11, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x20, 0x07, 0x10, 0x82, 0x80, 0x80, 0x80, 0x00, 0x20, 0x04, 0x28, 0x02, 0x10, 0x21, 0x12, 0x20, 0x04, 0x20, 0x12, 0x36, 0x02, 0x00, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x13, 0x20, 0x13, 0x20, 0x04, 0x10, 0x83, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x14, 0x41, 0x01, 0x21, 0x15, 0x41, 0x20, 0x21, 0x16, 0x20, 0x04, 0x20, 0x16, 0x6a, 0x21, 0x17, 0x20, 0x17, 0x21, 0x18, 0x20, 0x18, 0x20, 0x15, 0x10, 0x84, 0x80, 0x80, 0x80, 0x00, 0x41, 0xc0, 0x00, 0x21, 0x19, 0x20, 0x04, 0x20, 0x19, 0x6a, 0x21, 0x1a, 0x20, 0x1a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x14, 0x0f, 0x0b, 0x0b, 0x0b, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x04, 0x25, 0x73, 0x0a, 0x00 + +}; + +unsigned char __aligned(4) wasm_test_file4[]={ +0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x02, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x22, 0x02, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x66, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x0d, 0x6d, 0x71, 0x74, 0x74, 0x73, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x01, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x07, 0x01, 0x7f, 0x01, 0x41, 0xa0, 0x28, 0x0b, 0x07, 0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x0a, 0x71, 0x01, 0x6f, 0x01, 0x09, 0x7f, 0x23, 0x80, 0x80, 0x80, 0x80, 0x00, 0x21, 0x02, 0x41, 0x10, 0x21, 0x03, 0x20, 0x02, 0x20, 0x03, 0x6b, 0x21, 0x04, 0x20, 0x04, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x41, 0x00, 0x21, 0x05, 0x20, 0x04, 0x20, 0x05, 0x36, 0x02, 0x0c, 0x20, 0x04, 0x20, 0x00, 0x36, 0x02, 0x08, 0x20, 0x04, 0x20, 0x01, 0x36, 0x02, 0x04, 0x41, 0x80, 0x88, 0x80, 0x80, 0x00, 0x21, 0x06, 0x41, 0x00, 0x21, 0x07, 0x20, 0x06, 0x20, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x00, 0x1a, 0x41, 0x00, 0x21, 0x08, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x41, 0x10, 0x21, 0x09, 0x20, 0x04, 0x20, 0x09, 0x6a, 0x21, 0x0a, 0x20, 0x0a, 0x24, 0x80, 0x80, 0x80, 0x80, 0x00, 0x20, 0x08, 0x0f, 0x0b, 0x0b, 0x22, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0b, 0x1b, 0x45, 0x78, 0x63, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x0a, 0x00 +}; + + diff --git a/wamr/.clang-format b/wamr/.clang-format new file mode 100644 index 0000000..aa4cde6 --- /dev/null +++ b/wamr/.clang-format @@ -0,0 +1,162 @@ +RawStringFormats: + - Language: Cpp + Delimiters: + - c + - C + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + - h + - hpp + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google + +Language: Cpp +BasedOnStyle: Mozilla +IndentWidth: 4 +AlignAfterOpenBracket: Align +AllowAllArgumentsOnNextLine: false +AlignConsecutiveMacros: true +AllowShortBlocksOnASingleLine: true +AlwaysBreakAfterReturnType: All +BinPackArguments: true +BinPackParameters: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: false + SplitEmptyNamespace: true +ColumnLimit: 79 +ContinuationIndentWidth: 2 +DerivePointerAlignment: false +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 1 + - Regex: ".*" + Priority: 3 +IndentPPDirectives: None +KeepEmptyLinesAtTheStartOfBlocks: false +NamespaceIndentation: None +PointerAlignment: Right +ReflowComments: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION + +# AccessModifierOffset: -2 +# AlignConsecutiveAssignments: false +# AlignConsecutiveDeclarations: false +# AlignEscapedNewlines: Right +# AlignOperands: true +# AlignTrailingComments: true +# AllowAllConstructorInitializersOnNextLine: true +# AllowAllParametersOfDeclarationOnNextLine: false +# AllowShortCaseLabelsOnASingleLine: false +# AllowShortFunctionsOnASingleLine: Inline +# AllowShortLambdasOnASingleLine: All +# AllowShortIfStatementsOnASingleLine: Never +# AllowShortLoopsOnASingleLine: false +# AlwaysBreakAfterDefinitionReturnType: TopLevel +# AlwaysBreakAfterReturnType: TopLevel +# AlwaysBreakBeforeMultilineStrings: false +# AlwaysBreakTemplateDeclarations: Yes +# BreakBeforeInheritanceComma: false +# BreakInheritanceList: BeforeComma +# BreakBeforeTernaryOperators: true +# BreakConstructorInitializersBeforeComma: false +# BreakConstructorInitializers: BeforeComma +# BreakAfterJavaFieldAnnotations: false +# BreakStringLiterals: true +# CommentPragmas: '^ IWYU pragma:' +# CompactNamespaces: false +# ConstructorInitializerAllOnOneLineOrOnePerLine: false +# ConstructorInitializerIndentWidth: 2 +# Cpp11BracedListStyle: false +# DisableFormat: false +# ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: false +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IncludeIsMainRegex: '(Test)?$' +# IndentCaseLabels: true +# IndentWrappedFunctionNames: false +# JavaScriptQuotes: Leave +# JavaScriptWrapImports: true +# KeepEmptyLinesAtTheStartOfBlocks: true +# MacroBlockBegin: '' +# MacroBlockEnd: '' +# MaxEmptyLinesToKeep: 1 +# ObjCBinPackProtocolList: Auto +# ObjCBlockIndentWidth: 2 +# ObjCSpaceAfterProperty: true +# ObjCSpaceBeforeProtocolList: false +# PenaltyBreakAssignment: 2 +# PenaltyBreakBeforeFirstCallParameter: 19 +# PenaltyBreakComment: 300 +# PenaltyBreakFirstLessLess: 120 +# PenaltyBreakString: 1000 +# PenaltyBreakTemplateDeclaration: 10 +# PenaltyExcessCharacter: 1000000 +# PenaltyReturnTypeOnItsOwnLine: 200 +# SortIncludes: true +# SortUsingDeclarations: true +# SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false +# SpaceAfterTemplateKeyword: false +# SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: false +# SpaceBeforeCtorInitializerColon: true +# SpaceBeforeInheritanceColon: true +# SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true +# SpaceInEmptyParentheses: false +# SpacesBeforeTrailingComments: 1 +# SpacesInAngles: false +# SpacesInContainerLiterals: true +# SpacesInCStyleCastParentheses: false +# SpacesInParentheses: false +# SpacesInSquareBrackets: false +# TabWidth: 4 +# UseTab: Never +# ... + diff --git a/wamr/ATTRIBUTIONS.md b/wamr/ATTRIBUTIONS.md new file mode 100644 index 0000000..04b5fa2 --- /dev/null +++ b/wamr/ATTRIBUTIONS.md @@ -0,0 +1,330 @@ +WebAssembly Micro Runtime Attributions +====================================== + +WAMR project reused some components from other open source project: +- **wasmtime**: for the wasi libc implementation +- **cJson**: used in the host_tool for remotely managing wasm applications +- **contiki-ng**: for the coap protocol implementation +- **freebsd libm**: used in core/shared/platform/alios/bh_math.c +- **littlevgl**: for the gui samples and wrapped the wasm graphic layer. + +The WAMR fast interpreter is a clean room development. We would acknowledge the inspirations by [WASM3](https://github.com/wasm3/wasm3) open source project for the approach of pre-calculated oprand stack location. + +## Licenses + +### wasmtime + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. +``` + +### cJson + +``` +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +### contiki-ng + +``` + + +Copyright (c) (Year), (Name of copyright holder) All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +``` + +### freebsd libm + +``` +Copyright 1992-2011 The FreeBSD Project. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The views and conclusions contained in the software and documentation + are those of the authors and should not be interpreted as representing + official policies, either expressed or implied, of the FreeBSD + Project. +``` + +### littlevgl + +``` +MIT licence +Copyright (c) 2016 Gábor Kiss-Vámosi + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + + + + + diff --git a/wamr/CODE_OF_CONDUCT.md b/wamr/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5c5ebdd --- /dev/null +++ b/wamr/CODE_OF_CONDUCT.md @@ -0,0 +1,49 @@ +# Contributor Covenant Code of Conduct + +*Note*: this Code of Conduct pertains to individuals' behavior. Please also see the [Organizational Code of Conduct][OCoC]. + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Bytecode Alliance CoC team at [report@bytecodealliance.org](mailto:report@bytecodealliance.org). The CoC team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The CoC team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the Bytecode Alliance's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[OCoC]: ORG_CODE_OF_CONDUCT.md +[homepage]: https://www.contributor-covenant.org +[version]: https://www.contributor-covenant.org/version/1/4/ diff --git a/wamr/CONTRIBUTING.md b/wamr/CONTRIBUTING.md new file mode 100644 index 0000000..9210b3d --- /dev/null +++ b/wamr/CONTRIBUTING.md @@ -0,0 +1,39 @@ +Contributing to WAMR +===================== +As an open-source project, we welcome and encourage the community to submit patches directly to the project. In our collaborative open source environment, standards and methods for submitting changes help reduce the chaos that can result from an active development community. +We want to make contributing to this project as easy and transparent as possible, whether it's: +- Reporting a bug +- the current state of the code +- Submitting a fix +- Proposing new features + +License +======= +WAMR uses the same license as LLVM: the `Apache 2.0 license` with the LLVM +exception. See the LICENSE file for details. This license allows you to freely +use, modify, distribute and sell your own products based on WAMR. +Any contributions you make will be under the same license. + +Code changes +=================== +We Use Github Flow, So All Code Changes Happen Through Pull Requests. Pull requests are the best way to propose changes to the codebase. We actively welcome your pull requests: + +- If you've added code that should be tested, add tests. Ensure the test suite passes. +- Avoid use macros for different platforms. Use seperate folder of source files to host diffeent platform logic. +- Put macro definitions inside share_lib/include/config.h if you have to use macro. +- Make sure your code lints and compliant to our coding style. +- Extend the application library is highly welcome. + +Coding Style +=============================== +Please use [K&R](https://en.wikipedia.org/wiki/Indentation_style#K.26R) coding style, such as 4 spaces for indentation rather than tabs etc. +We suggest use Eclipse like IDE or stable coding format tools to make your code compliant to K&R format. + +Report bugs +=================== +We use GitHub issues to track public bugs. Report a bug by [open a new issue](https://github.com/intel/wasm-micro-runtime/issues/new). + +Code of Conduct +=============== + +WAMR is a [Bytecode Alliance](https://bytecodealliance.org/) project, and follows the Bytecode Alliance's [Code of Conduct](CODE_OF_CONDUCT.md) and [Organizational Code of Conduct](ORG_CODE_OF_CONDUCT.md). diff --git a/wamr/Dockerfile b/wamr/Dockerfile new file mode 100644 index 0000000..993f11a --- /dev/null +++ b/wamr/Dockerfile @@ -0,0 +1,21 @@ +# Currently supports clang-8 compiler +# Using the "test.c" app from the README.md: +# clang-8 --target=wasm32 -O3 -Wl,--initial-memory=131072,--allow-undefined,--export=main,--no-threads,--strip-all,--no-entry -nostdlib -o test.wasm test.c +# Pay attention to spacing above! ^ +# iwasm test.wasm + +FROM ubuntu:latest + +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get install -y build-essential clang-8 cmake g++-multilib git lib32gcc-5-dev llvm-8 lld-8 nano + +WORKDIR /root + +RUN git clone https://github.com/intel/wasm-micro-runtime + +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/product-mini/platforms/linux/build/iwasm iwasm diff --git a/wamr/LICENSE b/wamr/LICENSE new file mode 100644 index 0000000..c6bd7e0 --- /dev/null +++ b/wamr/LICENSE @@ -0,0 +1,219 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/wamr/ORG_CODE_OF_CONDUCT.md b/wamr/ORG_CODE_OF_CONDUCT.md new file mode 100644 index 0000000..e05e40c --- /dev/null +++ b/wamr/ORG_CODE_OF_CONDUCT.md @@ -0,0 +1,139 @@ +# Bytecode Alliance Organizational Code of Conduct (OCoC) + +*Note*: this Code of Conduct pertains to organizations' behavior. Please also see the [Individual Code of Conduct](CODE_OF_CONDUCT.md). + +## Preamble + +The Bytecode Alliance (BA) welcomes involvement from organizations, +including commercial organizations. This document is an +*organizational* code of conduct, intended particularly to provide +guidance to commercial organizations. It is distinct from the +[Individual Code of Conduct (ICoC)](CODE_OF_CONDUCT.md), and does not +replace the ICoC. This OCoC applies to any group of people acting in +concert as a BA member or as a participant in BA activities, whether +or not that group is formally incorporated in some jurisdiction. + +The code of conduct described below is not a set of rigid rules, and +we did not write it to encompass every conceivable scenario that might +arise. For example, it is theoretically possible there would be times +when asserting patents is in the best interest of the BA community as +a whole. In such instances, consult with the BA, strive for +consensus, and interpret these rules with an intent that is generous +to the community the BA serves. + +While we may revise these guidelines from time to time based on +real-world experience, overall they are based on a simple principle: + +*Bytecode Alliance members should observe the distinction between + public community functions and private functions — especially + commercial ones — and should ensure that the latter support, or at + least do not harm, the former.* + +## Guidelines + + * **Do not cause confusion about Wasm standards or interoperability.** + + Having an interoperable WebAssembly core is a high priority for + the BA, and members should strive to preserve that core. It is fine + to develop additional non-standard features or APIs, but they + should always be clearly distinguished from the core interoperable + Wasm. + + Treat the WebAssembly name and any BA-associated names with + respect, and follow BA trademark and branding guidelines. If you + distribute a customized version of software originally produced by + the BA, or if you build a product or service using BA-derived + software, use names that clearly distinguish your work from the + original. (You should still provide proper attribution to the + original, of course, wherever such attribution would normally be + given.) + + Further, do not use the WebAssembly name or BA-associated names in + other public namespaces in ways that could cause confusion, e.g., + in company names, names of commercial service offerings, domain + names, publicly-visible social media accounts or online service + accounts, etc. It may sometimes be reasonable, however, to + register such a name in a new namespace and then immediately donate + control of that account to the BA, because that would help the project + maintain its identity. + + * **Do not restrict contributors.** If your company requires + employees or contractors to sign non-compete agreements, those + agreements must not prevent people from participating in the BA or + contributing to related projects. + + This does not mean that all non-compete agreements are incompatible + with this code of conduct. For example, a company may restrict an + employee's ability to solicit the company's customers. However, an + agreement must not block any form of technical or social + participation in BA activities, including but not limited to the + implementation of particular features. + + The accumulation of experience and expertise in individual persons, + who are ultimately free to direct their energy and attention as + they decide, is one of the most important drivers of progress in + open source projects. A company that limits this freedom may hinder + the success of the BA's efforts. + + * **Do not use patents as offensive weapons.** If any BA participant + prevents the adoption or development of BA technologies by + asserting its patents, that undermines the purpose of the + coalition. The collaboration fostered by the BA cannot include + members who act to undermine its work. + + * **Practice responsible disclosure** for security vulnerabilities. + Use designated, non-public reporting channels to disclose technical + vulnerabilities, and give the project a reasonable period to + respond, remediate, and patch. + + Vulnerability reporters may patch their company's own offerings, as + long as that patching does not significantly delay the reporting of + the vulnerability. Vulnerability information should never be used + for unilateral commercial advantage. Vendors may legitimately + compete on the speed and reliability with which they deploy + security fixes, but withholding vulnerability information damages + everyone in the long run by risking harm to the BA project's + reputation and to the security of all users. + + * **Respect the letter and spirit of open source practice.** While + there is not space to list here all possible aspects of standard + open source practice, some examples will help show what we mean: + + * Abide by all applicable open source license terms. Do not engage + in copyright violation or misattribution of any kind. + + * Do not claim others' ideas or designs as your own. + + * When others engage in publicly visible work (e.g., an upcoming + demo that is coordinated in a public issue tracker), do not + unilaterally announce early releases or early demonstrations of + that work ahead of their schedule in order to secure private + advantage (such as marketplace advantage) for yourself. + + The BA reserves the right to determine what constitutes good open + source practices and to take action as it deems appropriate to + encourage, and if necessary enforce, such practices. + +## Enforcement + +Instances of organizational behavior in violation of the OCoC may +be reported by contacting the Bytecode Alliance CoC team at +[report@bytecodealliance.org](mailto:report@bytecodealliance.org). The +CoC team will review and investigate all complaints, and will respond +in a way that it deems appropriate to the circumstances. The CoC team +is obligated to maintain confidentiality with regard to the reporter of +an incident. Further details of specific enforcement policies may be +posted separately. + +When the BA deems an organization in violation of this OCoC, the BA +will, at its sole discretion, determine what action to take. The BA +will decide what type, degree, and duration of corrective action is +needed, if any, before a violating organization can be considered for +membership (if it was not already a member) or can have its membership +reinstated (if it was a member and the BA canceled its membership due +to the violation). + +In practice, the BA's first approach will be to start a conversation, +with punitive enforcement used only as a last resort. Violations +often turn out to be unintentional and swiftly correctable with all +parties acting in good faith. diff --git a/wamr/README.md b/wamr/README.md new file mode 100644 index 0000000..446209f --- /dev/null +++ b/wamr/README.md @@ -0,0 +1,150 @@ +WebAssembly Micro Runtime +========================= +[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) + +**A [Bytecode Alliance][BA] project** + +[BA]: https://bytecodealliance.org/ + +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 **dynamic management** of the WASM applications + +iwasm VM core +========================= + +### key features + +- 100% compliant to the W3C WASM MVP +- Small runtime binary size (85K for interpreter and 50K for AoT) and low memory usage +- Near to native speed by AoT +- Self-implemented module loader enables AoT working cross Linux, SGX and MCU systems +- Choices of WASM application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc +- [Embeddable with the supporting C API's](./doc/embed_wamr.md) +- [The mechanism for exporting native API's to WASM applications](./doc/export_native_api.md) +- [Multiple modules as dependencies](./doc/multi_module.md) +- [Thread management and pthread library](./doc/pthread_library.md) + +### post-MVP features +- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) +- [Sign-extension operators](https://github.com/WebAssembly/sign-extension-ops) +- [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) +- [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) +- [Multi-value](https://github.com/WebAssembly/multi-value) +- [wasm-c-api](https://github.com/WebAssembly/wasm-c-api) + +### Performance and memory usage +The WAMR performance, footprint and memory usage data are available at the [performance](../../wiki/Performance) wiki page. + +### Supported architectures and platforms + +The iwasm supports the following architectures: + +- X86-64, X86-32 +- ARM, THUMB (ARMV7 Cortex-M7 and Cortex-A15 are tested) +- AArch64 (Cortex-A57 and Cortex-A53 are tested) +- MIPS +- XTENSA + +Following platforms are supported. Refer to [WAMR porting guide](./doc/port_wamr.md) for how to port WAMR to a new platform. + +- [Linux](./doc/build_wamr.md#linux), [Linux SGX (Intel Software Guard Extension)](./doc/linux_sgx.md), [MacOS](./doc/build_wamr.md#macos), [Android](./doc/build_wamr.md#android) +- [Zephyr](./doc/build_wamr.md#zephyr), [AliOS-Things](./doc/build_wamr.md#alios-things), [VxWorks](./doc/build_wamr.md#vxworks) + +### Build iwasm VM core (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 or AoT file name as input and then executes it. For the detailed procedure, please see **[build WAMR VM core](./doc/build_wamr.md)** and **[build and run WASM application](./doc/build_wasm_app.md)**. Also we can click the link of each platform above to see how to build iwasm on it. + +### Build wamrc AoT compiler + +Both wasm binary file and AoT file are supported by iwasm. The wamrc AoT compiler is to compile wasm binary file to AoT file which can also be run by iwasm. Execute following commands to build **wamrc** compiler: + +```shell +cd wamr-compiler +./build_llvm.sh (use build_llvm_xtensa.sh instead to support xtensa target; use build_llvm.py for windows) +mkdir build && cd build +cmake .. +make +ln -s {current path}/wamrc /usr/bin/wamrc +``` +For MacOS, you should replace `cmake ..` with `cmake -DWAMR_BUILD_PLATFORM=darwin ..`. + +For Windows you should replace `cmake ..` with `cmake -D WAMR_BUILD_PLATFORM=windows -A Win32 ..`. + +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 effort. + +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, Inter-app communication (request/response and pub/sub), Sensor, Connectivity and data transmission, 2D graphic UI + +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](./core/app-mgr) 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. + + +WAMR SDK +========== + +Usually there are two tasks for integrating the WAMR into a particular project: + +- 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 + +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. + + +Samples +================= + +The WAMR [samples](./samples) integrate the iwasm VM core, application manager and selected application framework components. + +- [**basic**](./samples/basic): Demonstrating how to use runtime exposed API's to call WASM functions, how to register native functions and call them, and how to call WASM function from 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/lvgl/) 2D user graphic library and the UI application are built into WASM application. It uses **WASI libc** and executes apps in **AoT mode** by default. +- **[gui](./samples/gui/README.md)**: Move the [LittleVGL](https://github.com/lvgl/) library into the runtime and define a WASM application interface by wrapping the littlevgl API. It uses **WASI libc** and executes apps in **interpreter** mode by default. +- **[multi-thread](./samples/multi-thread/)**: Demonstrating how to run wasm application which creates multiple threads to execute wasm functions concurrently, and uses mutex/cond by calling pthread related API's. +- **[spawn-thread](./samples/spawn-thread)**: Demonstrating how to execute wasm functions of the same wasm application concurrently, in threads created by host embedder or runtime, but not the wasm application itself. +- **[multi-module](./samples/multi-module)**: Demonstrating the [multiple modules as dependencies](./doc/multi_module.md) feature which implements the [load-time dynamic linking](https://webassembly.org/docs/dynamic-linking/). +- **[wasm-c-api](./samples/wasm-c-api/README.md)**: Demonstrating how to run some samples from [wasm-c-api proposal](https://github.com/WebAssembly/wasm-c-api) and showing the supported API's. + + +Releases and acknowledgments +============================ + +WAMR is a community effort. Since Intel Corp contributed the first release of this open source project, this project has received many good contributions from the community. + +See the [major features releasing history and contributor names](./doc/release_ack.md) + + +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. + + +License +======= +WAMR uses the same license as LLVM: the `Apache 2.0 license` with the LLVM +exception. See the LICENSE file for details. This license allows you to freely +use, modify, distribute and sell your own products based on WAMR. +Any contributions you make will be under the same license. + + +Submit issues and contact the maintainers +========================================= +[Click here to submit. Your feedback is always welcome!](https://github.com/intel/wasm-micro-runtime/issues/new) + + +Contact the maintainers: imrt-public@intel.com diff --git a/wamr/SECURITY.md b/wamr/SECURITY.md new file mode 100644 index 0000000..3513b9c --- /dev/null +++ b/wamr/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +Building secure foundations for software development is at the core of what we do in the Bytecode Alliance. Contributions of external security researchers are a vital part of that. + +## Scope + +If you believe you've found a security issue in any website, service, or software owned or operated by the Bytecode Alliance, we encourage you to notify us. + +## How to Submit a Report + +To submit a vulnerability report to the Bytecode Alliance, please contact us at [security@bytecodealliance.org](mailto:security@bytecodealliance.org). Your submission will be reviewed and validated by a member of our security team. + +## Safe Harbor + +The Bytecode Alliance supports safe harbor for security researchers who: + +* Make a good faith effort to avoid privacy violations, destruction of data, and interruption or degradation of our services. +* Only interact with accounts you own or with explicit permission of the account holder. If you do encounter Personally Identifiable Information (PII) contact us immediately, do not proceed with access, and immediately purge any local information. +* Provide us with a reasonable amount of time to resolve vulnerabilities prior to any disclosure to the public or a third-party. + +We will consider activities conducted consistent with this policy to constitute "authorized" conduct and will not pursue civil action or initiate a complaint to law enforcement. We will help to the extent we can if legal action is initiated by a third party against you. + +Please submit a report to us before engaging in conduct that may be inconsistent with or unaddressed by this policy. + +## Preferences + +* Please provide detailed reports with reproducible steps and a clearly defined impact. +* Submit one vulnerability per report. +* Social engineering (e.g. phishing, vishing, smishing) is prohibited. diff --git a/wamr/assembly-script/.gitignore b/wamr/assembly-script/.gitignore new file mode 100644 index 0000000..07e6e47 --- /dev/null +++ b/wamr/assembly-script/.gitignore @@ -0,0 +1 @@ +/node_modules diff --git a/wamr/assembly-script/README.md b/wamr/assembly-script/README.md new file mode 100644 index 0000000..a1324e9 --- /dev/null +++ b/wamr/assembly-script/README.md @@ -0,0 +1,124 @@ +# AssemblyScript_on_WAMR +This project is based on [Wasm Micro Runtime](https://github.com/bytecodealliance/wasm-micro-runtime) (WAMR) and [AssemblyScript](https://github.com/AssemblyScript/assemblyscript). It implements some of the `wamr app framework` in *assemblyscript*, which allows you to write some applications in *assemblyscript* and dynamically installed on *WAMR Runtime* + +## Building +To build the samples in this repo, you need `npm` on your system +``` bash +sudo apt install npm +``` + +Then install all the dependencies under the repo's root dir +``` bash +cd $repo_root +npm install +``` + +Use the command to build all samples: +``` bash +npm run build:all +``` +or you can build every sample individually: +``` bash +npm run build:timer +npm run build:publisher +npm run build:subscriber +# ... +``` +You will get the compiled wasm file under `build` folder + +Please refer to [package.json](./package.json) for more commands. + +## Run +These applications require WAMR's application framework, you need to build WAMR first. + +``` bash +cd ${WAMR_ROOT}/samples/simple +./build.sh +``` + +You will get two executable files under `out` folder: + +`simple`: The wamr runtime with application framework + +`host_tool`: The tool used to dynamically install/uninstall applications + +1. Start the runtime: + ``` bash + ./simple -s + ``` + +2. Install the compiled wasm file using `host_tool`: + ``` bash + ./host_tool -i app_name -f your_compiled_wasm_file.wasm + ``` +You can also use the WAMR's AoT compiler `wamrc` to compile the wasm bytecode into native code before you run them. Please refer to this [guide](../README.md#build-wamrc-aot-compiler) to build and install `WAMR AoT compiler`. + +After installing `wamrc`, you can compile the wasm file using command: +``` bash +wamrc -o file_name.aot file_name.wasm +``` +and you can install the AoT file to the runtime: +``` bash +./host_tool -i app_name -f your_compiled_aot_file.aot +``` + +## Development +You can develop your own application based on the `wamr_app_lib` APIs. + +### Console APIs +``` typescript +function log(a: string): void; +function log_number(a: number): void; +``` + +### Timer APIs +``` typescript +function setTimeout(cb: () => void, timeout: i32): user_timer; +function setInterval(cb: () => void, timeout: i32): user_timer; +function timer_cancel(timer: user_timer): void; +function timer_restart(timer: user_timer, interval: number): void; +function now(): i32; + +// export to runtime +function on_timer_callback(on_timer_id: i32): void; +``` + +### Request APIs +``` typescript +// register handler +function register_resource_handler(url: string, + request_handle: request_handler_f): void; +// request +function post(url: string, payload: ArrayBuffer, payload_len: number, + tag: string, cb: (resp: wamr_response) => void): void; +function get(url: string, tag: string, + cb: (resp: wamr_response) => void): void; +function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void; +function del(url: string, tag: string, + cb: (resp: wamr_response) => void): void; + +// response +function make_response_for_request(req: wamr_request): wamr_response; +function api_response_send(resp: wamr_response): void; + +// event +function publish_event(url: string, fmt: number, + payload: ArrayBuffer, payload_len: number): void; +function subscribe_event(url: string, cb: request_handler_f): void; + +// export to runtime +function on_request(buffer_offset: i32, size: i32): void; +function on_response(buffer_offset : i32, size: i32): void; +``` + +You should export the `on_timer_callback`, `on_request` and `on_response` in your application entry file, refer to the samples for example. + +To build your application, you can use `asc`: +``` bash +asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize +``` +or you can add a command into [package.json](./package.json): +``` json +"build:app": "asc app.ts -b build/app.wasm -t build/app.wat --sourceMap --validate --optimize", +``` diff --git a/wamr/assembly-script/package-lock.json b/wamr/assembly-script/package-lock.json new file mode 100644 index 0000000..97d4e29 --- /dev/null +++ b/wamr/assembly-script/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "assembly_script", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assemblyscript": { + "version": "0.8.1", + "resolved": "https://registry.npm.taobao.org/assemblyscript/download/assemblyscript-0.8.1.tgz", + "integrity": "sha1-xcYnSSQG5th/QmiXs9kr0qUz9/4=", + "dev": true, + "requires": { + "binaryen": "89.0.0-nightly.20191113", + "long": "^4.0.0" + } + }, + "binaryen": { + "version": "89.0.0-nightly.20191113", + "resolved": "https://registry.npm.taobao.org/binaryen/download/binaryen-89.0.0-nightly.20191113.tgz", + "integrity": "sha1-oNORTzXJKXhzQeApELf/rrfYl6k=", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npm.taobao.org/long/download/long-4.0.0.tgz", + "integrity": "sha1-mntxz7fTYaGU6lVSQckvdGjVvyg=", + "dev": true + } + } +} diff --git a/wamr/assembly-script/package.json b/wamr/assembly-script/package.json new file mode 100644 index 0000000..00aaaaf --- /dev/null +++ b/wamr/assembly-script/package.json @@ -0,0 +1,20 @@ +{ + "name": "assembly_script", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build:request_handler": "asc samples/request_handler.ts -b build/request_handler.wasm -t build/request_handler.wat --sourceMap --validate --optimize --use abort=", + "build:request_sender": "asc samples/request_sender.ts -b build/request_sender.wasm -t build/request_sender.wat --sourceMap --validate --optimize --use abort=", + "build:timer": "asc samples/timer.ts -b build/timer.wasm -t build/timer.wat --sourceMap --validate --optimize --use abort=", + "build:publisher": "asc samples/event_publisher.ts -b build/event_publisher.wasm -t build/event_publisher.wat --sourceMap --validate --optimize --use abort=", + "build:subscriber": "asc samples/event_subscriber.ts -b build/event_subscriber.wasm -t build/event_subscriber.wat --sourceMap --validate --optimize --use abort=", + "build:all": "npm run build:request_handler; npm run build:request_sender; npm run build:timer; npm run build:subscriber; npm run build:publisher" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "assemblyscript": "^0.8.1" + } +} diff --git a/wamr/assembly-script/samples/event_publisher.ts b/wamr/assembly-script/samples/event_publisher.ts new file mode 100644 index 0000000..3ca133f --- /dev/null +++ b/wamr/assembly-script/samples/event_publisher.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +function publish_overheat_event(): void { + var payload = String.UTF8.encode("warning: temperature is over high"); + request.publish_event("alert/overheat", 0, payload, payload.byteLength); +} + +export function on_init() : void { + timer.setInterval(publish_overheat_event, 2000); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/wamr/assembly-script/samples/event_subscriber.ts b/wamr/assembly-script/samples/event_subscriber.ts new file mode 100644 index 0000000..c9aa52a --- /dev/null +++ b/wamr/assembly-script/samples/event_subscriber.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + request.subscribe_event("alert/overheat", (req) => { + console.log("### user over heat event handler called:"); + + console.log(""); + console.log(" " + String.UTF8.decode(req.payload) + "\n"); + }) +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/wamr/assembly-script/samples/request_handler.ts b/wamr/assembly-script/samples/request_handler.ts new file mode 100644 index 0000000..ef9f58c --- /dev/null +++ b/wamr/assembly-script/samples/request_handler.ts @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + // The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + request.register_resource_handler("/test", (req) => { + console.log("### Req: /test " + String.UTF8.decode(req.payload)); + + console.log(" request payload:"); + console.log(" " + String.UTF8.decode(req.payload) + "\n"); + + var resp = request.make_response_for_request(req); + resp.set_payload(String.UTF8.encode("Ok"), 2); + request.api_response_send(resp); + }); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/wamr/assembly-script/samples/request_sender.ts b/wamr/assembly-script/samples/request_sender.ts new file mode 100644 index 0000000..5648985 --- /dev/null +++ b/wamr/assembly-script/samples/request_sender.ts @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from "../wamr_app_lib/console" +import * as timer from "../wamr_app_lib/timer" +import * as request from "../wamr_app_lib/request" + +export function on_init() : void { + var payload = String.UTF8.encode("test message"); + request.post("/test", payload, payload.byteLength, "", (resp) => { + if (resp != null) { + console.log("Post Success"); + + if (resp.payload != null) { + console.log(" response payload:") + console.log(" " + String.UTF8.decode(resp.payload!) + "\n"); + } + } + else + console.log("Post Timeout"); + }); +} + +export function on_destroy() : void { + +} + + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} + +export function _on_request(buffer_offset: i32, size: i32): void { + request.on_request(buffer_offset, size); +} + +export function _on_response(buffer_offset : i32, size: i32): void { + request.on_response(buffer_offset, size); +} \ No newline at end of file diff --git a/wamr/assembly-script/samples/timer.ts b/wamr/assembly-script/samples/timer.ts new file mode 100644 index 0000000..2e3f69d --- /dev/null +++ b/wamr/assembly-script/samples/timer.ts @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +// The entry file of your WebAssembly module. +import * as console from '../wamr_app_lib/console' +import * as timer from '../wamr_app_lib/timer' + +/* clousure is not implemented yet, we need to declare global variables + so that they can be accessed inside a callback function */ +var cnt = 0; +var my_timer: timer.user_timer; + +export function on_init(): void { + /* The callback function will be called every 2 second, + and will stop after 10 calls */ + my_timer = timer.setInterval(() => { + cnt ++; + console.log((cnt * 2).toString() + " seconds passed"); + + if (cnt >= 10) { + timer.timer_cancel(my_timer); + console.log("Stop Timer"); + } + }, 2000); +} + +export function on_destroy(): void { + +} + +/* Function below are requred by wamr runtime, don't remove or modify them */ +export function _on_timer_callback(on_timer_id: i32): void { + timer.on_timer_callback(on_timer_id); +} \ No newline at end of file diff --git a/wamr/assembly-script/samples/tsconfig.json b/wamr/assembly-script/samples/tsconfig.json new file mode 100644 index 0000000..c614e5c --- /dev/null +++ b/wamr/assembly-script/samples/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/wamr/assembly-script/wamr_app_lib/console.ts b/wamr/assembly-script/wamr_app_lib/console.ts new file mode 100644 index 0000000..f20ede9 --- /dev/null +++ b/wamr/assembly-script/wamr_app_lib/console.ts @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +@external("env", "puts") +declare function printf(a: ArrayBuffer): i32; + +export function log(a: string): void { + printf(String.UTF8.encode(a, true)); +} + +export function log_number(a: number): void { + printf(String.UTF8.encode(a.toString())); +} \ No newline at end of file diff --git a/wamr/assembly-script/wamr_app_lib/request.ts b/wamr/assembly-script/wamr_app_lib/request.ts new file mode 100644 index 0000000..2125e04 --- /dev/null +++ b/wamr/assembly-script/wamr_app_lib/request.ts @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +import * as console from './console' +import * as timer from './timer' + +@external("env", "wasm_response_send") +declare function wasm_response_send(buffer: ArrayBuffer, size: i32): void; + +@external("env", "wasm_register_resource") +declare function wasm_register_resource(url: ArrayBuffer): void; + +@external("env", "wasm_post_request") +declare function wasm_post_request(buffer: ArrayBuffer, size: i32): void; + +@external("env", "wasm_sub_event") +declare function wasm_sub_event(url: ArrayBuffer): void; + +var COAP_GET = 1; +var COAP_POST = 2; +var COAP_PUT = 3; +var COAP_DELETE = 4; +var COAP_EVENT = COAP_DELETE + 2; + +/* CoAP response codes */ +export enum CoAP_Status { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, PING_RESPONSE +}; + +var g_mid: i32 = 0; +class wamr_request { + mid: i32 = 0; + url: string = ""; + action: i32 = 0; + fmt: i32 = 0; + payload: ArrayBuffer; + payload_len: i32 = 0; + + sender: i32 = 0; + + constructor(mid: i32, url: string, action: i32, fmt: i32, + payload: ArrayBuffer, payload_len: number) { + this.mid = mid; + this.url = url; + this.action = action; + this.fmt = fmt; + this.payload = payload; + this.payload_len = i32(payload_len); + } +} + +class wamr_response { + mid: i32 = 0; + status: i32 = 0; + fmt: i32 = 0; + payload: ArrayBuffer | null; + payload_len: i32 = 0; + + receiver: i32 = 0; + + constructor(mid: i32, status: i32, fmt: i32, + payload: ArrayBuffer | null, payload_len: i32) { + this.mid = mid; + this.status = status; + this.fmt = fmt; + this.payload = payload; + this.payload_len = payload_len; + } + + set_status(status: number): void { + this.status = i32(status); + } + + set_payload(payload: ArrayBuffer, payload_len: number): void { + this.payload = payload; + this.payload_len = i32(payload_len); + } +} + +class wamr_resource { + url: string; + type: number; + cb: request_handler_f; + + constructor(url: string, type: number, cb: request_handler_f) { + this.url = url; + this.type = type; + this.cb = cb; + } +} + +function is_expire(trans: wamr_transaction, index: i32, array: Array): bool { + var now = timer.now(); + + var elapsed_ms = (now < trans.time) ? + (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); + + return elapsed_ms >= TRANSACTION_TIMEOUT_MS; +} + +function not_expire(trans: wamr_transaction, index: i32, array: Array): bool { + var now = timer.now(); + + var elapsed_ms = (now < trans.time) ? + (now + (0xFFFFFFFF - trans.time) + 1) : (now - trans.time); + + return elapsed_ms >= TRANSACTION_TIMEOUT_MS; +} + +function transaction_timeout_handler(): void { + var now = timer.now(); + + var expired = transaction_list.filter(is_expire); + transaction_list = transaction_list.filter(not_expire); + + expired.forEach(item => { + item.cb(null); + transaction_remove(item); + }) + + if (transaction_list.length > 0) { + var elpased_ms: number, ms_to_expiry: number; + now = timer.now(); + if (now < transaction_list[0].time) { + elpased_ms = now + (0xFFFFFFFF - transaction_list[0].time) + 1; + } else { + elpased_ms = now - transaction_list[0].time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + timer.timer_restart(g_trans_timer, ms_to_expiry); + } else { + timer.timer_cancel(g_trans_timer); + } +} + +function transaction_find(mid: number): wamr_transaction | null { + for (let i = 0; i < transaction_list.length; i++) { + if (transaction_list[i].mid == mid) + return transaction_list[i]; + } + return null; +} + +function transaction_add(trans: wamr_transaction): void { + transaction_list.push(trans); + + if (transaction_list.length == 1) { + g_trans_timer = timer.setTimeout( + transaction_timeout_handler, + TRANSACTION_TIMEOUT_MS + ); + } +} + +function transaction_remove(trans: wamr_transaction): void { + var index = transaction_list.indexOf(trans); + transaction_list.splice(index, 1); +} + +var transaction_list = new Array(); +class wamr_transaction { + mid: number; + time: number; + cb: (resp: wamr_response | null) => void; + + constructor(mid: number, time: number, cb: (resp: wamr_response) => void) { + this.mid = mid; + this.time = time; + this.cb = cb; + } +} + +var REQUEST_PACKET_FIX_PART_LEN = 18; +var RESPONSE_PACKET_FIX_PART_LEN = 16; +var TRANSACTION_TIMEOUT_MS = 5000; +var g_trans_timer: timer.user_timer; + +var Reg_Event = 0; +var Reg_Request = 1; + +function pack_request(req: wamr_request): DataView { + var url_len = req.url.length + 1; + var len = REQUEST_PACKET_FIX_PART_LEN + url_len + req.payload_len + var buf = new ArrayBuffer(len); + + var dataview = new DataView(buf, 0, len); + + dataview.setUint8(0, 1); + dataview.setUint8(1, u8(req.action)); + dataview.setUint16(2, u16(req.fmt)); + dataview.setUint32(4, req.mid); + dataview.setUint32(8, req.sender); + dataview.setUint16(12, u16(url_len)) + dataview.setUint32(14, req.payload_len); + + var i = 0; + for (i = 0; i < url_len - 1; i++) { + dataview.setUint8(i + 18, u8(req.url.codePointAt(i))); + } + dataview.setUint8(i + 18, 0); + + var payload_view = new DataView(req.payload); + for (i = 0; i < req.payload_len; i++) { + dataview.setUint8(i + 18 + url_len, u8(payload_view.getUint8(i))); + } + + return dataview; +} + +function unpack_request(packet: ArrayBuffer, size: i32): wamr_request { + var dataview = new DataView(packet, 0, size); + + if (dataview.getUint8(0) != 1) + throw new Error("packet version mismatch"); + + if (size < REQUEST_PACKET_FIX_PART_LEN) + throw new Error("packet size error"); + + var url_len = dataview.getUint16(12); + var payload_len = dataview.getUint32(14); + + if (size != (REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) + throw new Error("packet size error"); + + var action = dataview.getUint8(1); + var fmt = dataview.getUint16(2); + var mid = dataview.getUint32(4); + var sender = dataview.getUint32(8); + + var url = packet.slice(REQUEST_PACKET_FIX_PART_LEN, REQUEST_PACKET_FIX_PART_LEN + url_len - 1); + var payload = packet.slice(REQUEST_PACKET_FIX_PART_LEN + url_len, REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len); + + var req = new wamr_request(mid, String.UTF8.decode(url), action, fmt, payload, payload_len); + req.sender = sender; + + return req; +} + +function pack_response(resp: wamr_response): DataView { + var len = RESPONSE_PACKET_FIX_PART_LEN + resp.payload_len + var buf = new ArrayBuffer(len); + + var dataview = new DataView(buf, 0, len); + + dataview.setUint8(0, 1); + dataview.setUint8(1, u8(resp.status)); + dataview.setUint16(2, u16(resp.fmt)); + dataview.setUint32(4, resp.mid); + dataview.setUint32(8, resp.receiver); + dataview.setUint32(12, resp.payload_len) + + if (resp.payload != null) { + var payload_view = new DataView(resp.payload!); + for (let i = 0; i < resp.payload_len; i++) { + dataview.setUint8(i + 16, payload_view.getUint8(i)); + } + } + + return dataview; +} + +function unpack_response(packet: ArrayBuffer, size: i32): wamr_response { + var dataview = new DataView(packet, 0, size); + + if (dataview.getUint8(0) != 1) + throw new Error("packet version mismatch"); + + if (size < RESPONSE_PACKET_FIX_PART_LEN) + throw new Error("packet size error"); + + var payload_len = dataview.getUint32(12); + if (size != RESPONSE_PACKET_FIX_PART_LEN + payload_len) + throw new Error("packet size error"); + + var status = dataview.getUint8(1); + var fmt = dataview.getUint16(2); + var mid = dataview.getUint32(4); + var receiver = dataview.getUint32(8); + + var payload = packet.slice(RESPONSE_PACKET_FIX_PART_LEN); + + var resp = new wamr_response(mid, status, fmt, payload, payload_len); + resp.receiver = receiver; + + return resp; +} + +function do_request(req: wamr_request, cb: (resp: wamr_response) => void): void { + var trans = new wamr_transaction(req.mid, timer.now(), cb); + var msg = pack_request(req); + + transaction_add(trans); + + wasm_post_request(msg.buffer, msg.byteLength); +} + +function do_response(resp: wamr_response): void { + var msg = pack_response(resp); + + wasm_response_send(msg.buffer, msg.byteLength); +} + +var resource_list = new Array(); +type request_handler_f = (req: wamr_request) => void; + +function registe_url_handler(url: string, cb: request_handler_f, type: number): void { + for (let i = 0; i < resource_list.length; i++) { + if (resource_list[i].type == type && resource_list[i].url == url) { + resource_list[i].cb = cb; + return; + } + } + + var res = new wamr_resource(url, type, cb); + resource_list.push(res); + + if (type == Reg_Request) + wasm_register_resource(String.UTF8.encode(url)); + else + wasm_sub_event(String.UTF8.encode(url)); +} + +function is_event_type(req: wamr_request): bool { + return req.action == COAP_EVENT; +} + +function check_url_start(url: string, leading_str: string): bool { + return url.split('/')[0] == leading_str.split('/')[0]; +} + +/* User APIs below */ +export function post(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_POST, 0, payload, payload_len); + + do_request(req, cb); +} + +export function get(url: string, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_GET, 0, new ArrayBuffer(0), 0); + + do_request(req, cb); +} + +export function put(url: string, payload: ArrayBuffer, payload_len: number, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_PUT, 0, payload, payload_len); + + do_request(req, cb); +} + +export function del(url: string, tag: string, + cb: (resp: wamr_response) => void): void { + var req = new wamr_request(g_mid++, url, COAP_PUT, 0, new ArrayBuffer(0), 0); + + do_request(req, cb); +} + +export function make_response_for_request(req: wamr_request): wamr_response { + var resp = new wamr_response(req.mid, CoAP_Status.CONTENT_2_05, 0, null, 0); + resp.receiver = req.sender; + + return resp; +} + +export function api_response_send(resp: wamr_response): void { + do_response(resp); +} + +export function register_resource_handler(url: string, + request_handle: request_handler_f): void { + registe_url_handler(url, request_handle, Reg_Request); +} + +export function publish_event(url: string, fmt: number, + payload: ArrayBuffer, payload_len: number): void { + var req = new wamr_request(g_mid++, url, COAP_EVENT, i32(fmt), payload, payload_len); + + var msg = pack_request(req); + + wasm_post_request(msg.buffer, msg.byteLength); +} + +export function subscribe_event(url: string, cb: request_handler_f): void { + registe_url_handler(url, cb, Reg_Event); +} + + +/* These two APIs are required by wamr runtime, + use a wrapper to export them in the entry file + + e.g: + + import * as request from '.wamr_app_lib/request' + + // Your code here ... + + export function _on_request(buffer_offset: i32, size: i32): void { + on_request(buffer_offset, size); + } + + export function _on_response(buffer_offset: i32, size: i32): void { + on_response(buffer_offset, size); + } +*/ +export function on_request(buffer_offset: i32, size: i32): void { + var buffer = new ArrayBuffer(size); + var dataview = new DataView(buffer); + + for (let i = 0; i < size; i++) { + dataview.setUint8(i, load(buffer_offset + i, 0, 1)); + } + + var req = unpack_request(buffer, size); + + var is_event = is_event_type(req); + + for (let i = 0; i < resource_list.length; i++) { + if ((is_event && resource_list[i].type == Reg_Event) + || (!is_event && resource_list[i].type == Reg_Request)) { + if (check_url_start(req.url, resource_list[i].url)) { + resource_list[i].cb(req); + return; + } + } + } + + console.log("on_request: exit. no service handler."); +} + +export function on_response(buffer_offset: i32, size: i32): void { + var buffer = new ArrayBuffer(size); + var dataview = new DataView(buffer); + + for (let i = 0; i < size; i++) { + dataview.setUint8(i, load(buffer_offset + i, 0, 1)); + } + + var resp = unpack_response(buffer, size); + var trans = transaction_find(resp.mid); + + if (trans != null) { + if (transaction_list.indexOf(trans) == 0) { + if (transaction_list.length >= 2) { + var elpased_ms: number, ms_to_expiry: number; + var now = timer.now(); + if (now < transaction_list[1].time) { + elpased_ms = now + (0xFFFFFFFF - transaction_list[1].time) + 1; + } else { + elpased_ms = now - transaction_list[1].time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + timer.timer_restart(g_trans_timer, ms_to_expiry); + } else { + timer.timer_cancel(g_trans_timer); + } + } + + trans.cb(resp); + } +} \ No newline at end of file diff --git a/wamr/assembly-script/wamr_app_lib/timer.ts b/wamr/assembly-script/wamr_app_lib/timer.ts new file mode 100644 index 0000000..ea8363e --- /dev/null +++ b/wamr/assembly-script/wamr_app_lib/timer.ts @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +@external("env", "wasm_create_timer") +declare function wasm_create_timer(a: i32, b: bool, c: bool): i32; + +@external("env", "wasm_timer_cancel") +declare function wasm_timer_cancel(a: i32): void; + +@external("env", "wasm_timer_restart") +declare function wasm_timer_restart(a: i32, b: i32): void; + +@external("env", "wasm_get_sys_tick_ms") +declare function wasm_get_sys_tick_ms(): i32; + +export var timer_list = new Array(); + +export class user_timer { + timer_id: i32 = 0; + timeout: i32; + period: bool = false; + cb: () => void; + + constructor(cb: () => void, timeout: i32, period: bool) { + this.cb = cb; + this.timeout = timeout; + this.period = period + this.timer_id = timer_create(this.timeout, this.period, true); + } +} + +export function timer_create(a: i32, b: bool, c: bool): i32 { + return wasm_create_timer(a, b, c); +} + +export function setTimeout(cb: () => void, timeout: i32): user_timer { + var timer = new user_timer(cb, timeout, false); + timer_list.push(timer); + + return timer; +} + +export function setInterval(cb: () => void, timeout: i32): user_timer { + var timer = new user_timer(cb, timeout, true); + timer_list.push(timer); + + return timer; +} + +export function timer_cancel(timer: user_timer): void { + wasm_timer_cancel(timer.timer_id); + + var i = 0; + for (i = 0; i < timer_list.length; i++) { + if (timer_list[i].timer_id == timer.timer_id) + break; + } + + timer_list.splice(i, 1); +} + +export function timer_restart(timer: user_timer, interval: number): void { + wasm_timer_restart(timer.timer_id, i32(interval)); +} + +export function now(): i32 { + return wasm_get_sys_tick_ms(); +} + +// This export function need to be copied to the top application file +// +export function on_timer_callback(on_timer_id: i32): void { + for (let i = 0; i < timer_list.length; i++) { + if (timer_list[i].timer_id == on_timer_id) { + timer_list[i].cb(); + } + } +} \ No newline at end of file diff --git a/wamr/assembly-script/wamr_app_lib/tsconfig.json b/wamr/assembly-script/wamr_app_lib/tsconfig.json new file mode 100644 index 0000000..c614e5c --- /dev/null +++ b/wamr/assembly-script/wamr_app_lib/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../node_modules/assemblyscript/std/assembly.json", + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/wamr/build-scripts/config_common.cmake b/wamr/build-scripts/config_common.cmake new file mode 100644 index 0000000..f22691d --- /dev/null +++ b/wamr/build-scripts/config_common.cmake @@ -0,0 +1,168 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +string(TOUPPER ${WAMR_BUILD_TARGET} WAMR_BUILD_TARGET) + + +# Add definitions for the build target +if (WAMR_BUILD_TARGET STREQUAL "X86_64") + add_definitions(-DBUILD_TARGET_X86_64) +elseif (WAMR_BUILD_TARGET STREQUAL "AMD_64") + add_definitions(-DBUILD_TARGET_AMD_64) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + add_definitions(-DBUILD_TARGET_X86_32) +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + if (WAMR_BUILD_TARGET MATCHES "(ARM.*)_VFP") + add_definitions(-DBUILD_TARGET_ARM_VFP) + add_definitions(-DBUILD_TARGET="${CMAKE_MATCH_1}") + else () + add_definitions(-DBUILD_TARGET_ARM) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") + endif () +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + if (WAMR_BUILD_TARGET MATCHES "(THUMB.*)_VFP") + add_definitions(-DBUILD_TARGET_THUMB_VFP) + add_definitions(-DBUILD_TARGET="${CMAKE_MATCH_1}") + else () + add_definitions(-DBUILD_TARGET_THUMB) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") + endif () +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + add_definitions(-DBUILD_TARGET_AARCH64) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") +elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") + add_definitions(-DBUILD_TARGET_MIPS) +elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") + add_definitions(-DBUILD_TARGET_XTENSA) +else () + message (FATAL_ERROR "-- WAMR build target isn't set") +endif () + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DBH_DEBUG=1) +endif () + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64" OR WAMR_BUILD_TARGET MATCHES "AARCH64.*") + # Add -fPIC flag if build as 64-bit + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + else () + add_definitions (-m32) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") + endif () +endif () + +if (WAMR_BUILD_TARGET MATCHES "ARM.*") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm") +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb") + set (CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -Wa,-mthumb") +endif () + +if (NOT WAMR_BUILD_INTERP EQUAL 1) +if (NOT WAMR_BUILD_AOT EQUAL 1) + message (FATAL_ERROR "-- WAMR Interpreter and AOT must be enabled at least one") +endif () +endif () + +if (WAMR_BUILD_JIT EQUAL 1) + if (WAMR_BUILD_AOT EQUAL 1) + add_definitions("-DWASM_ENABLE_JIT=1") + set (LLVM_SRC_ROOT "${WAMR_ROOT_DIR}/core/deps/llvm") + if (NOT EXISTS "${LLVM_SRC_ROOT}/build") + message (FATAL_ERROR "Cannot find LLVM dir: ${LLVM_SRC_ROOT}/build") + endif () + set (CMAKE_PREFIX_PATH "${LLVM_SRC_ROOT}/build;${CMAKE_PREFIX_PATH}") + find_package(LLVM REQUIRED CONFIG) + include_directories(${LLVM_INCLUDE_DIRS}) + add_definitions(${LLVM_DEFINITIONS}) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + else () + set (WAMR_BUILD_JIT 0) + message ("-- WAMR JIT disabled due to WAMR AOT is disabled") + endif () +else () + unset (LLVM_AVAILABLE_LIBS) +endif () + +message ("-- Build Configurations:") +message (" Build as target ${WAMR_BUILD_TARGET}") +message (" CMAKE_BUILD_TYPE " ${CMAKE_BUILD_TYPE}) +if (WAMR_BUILD_INTERP EQUAL 1) + message (" WAMR Interpreter enabled") +else () + message (" WAMR Interpreter disabled") +endif () +if (WAMR_BUILD_AOT EQUAL 1) + message (" WAMR AOT enabled") +else () + message (" WAMR AOT disabled") +endif () +if (WAMR_BUILD_JIT EQUAL 1) + message (" WAMR JIT enabled") +else () + message (" WAMR JIT disabled") +endif () +if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) + message (" Libc builtin enabled") +else () + message (" Libc builtin disabled") +endif () +if (WAMR_BUILD_LIBC_WASI EQUAL 1) + message (" Libc WASI enabled") +else () + message (" Libc WASI disabled") +endif () +if (WAMR_BUILD_FAST_INTERP EQUAL 1) + add_definitions (-DWASM_ENABLE_FAST_INTERP=1) + message (" Fast interpreter enabled") +else () + add_definitions (-DWASM_ENABLE_FAST_INTERP=0) + message (" Fast interpreter disabled") +endif () +if (WAMR_BUILD_MULTI_MODULE EQUAL 1) + add_definitions (-DWASM_ENABLE_MULTI_MODULE=1) + message (" Multiple modules enabled") +else () + add_definitions (-DWASM_ENABLE_MULTI_MODULE=0) + message (" Multiple modules disabled") +endif () +if (WAMR_BUILD_SPEC_TEST EQUAL 1) + add_definitions (-DWASM_ENABLE_SPEC_TEST=1) + message (" spec test compatible mode is on") +endif () +if (WAMR_BUILD_BULK_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_BULK_MEMORY=1) + message (" Bulk memory feature enabled") +else () + add_definitions (-DWASM_ENABLE_BULK_MEMORY=0) +endif () +if (WAMR_BUILD_SHARED_MEMORY EQUAL 1) + add_definitions (-DWASM_ENABLE_SHARED_MEMORY=1) + message (" Shared memory enabled") +else () + add_definitions (-DWASM_ENABLE_SHARED_MEMORY=0) +endif () +if (WAMR_BUILD_THREAD_MGR EQUAL 1) + message (" Thread manager enabled") +endif () +if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) + message (" Lib pthread enabled") +endif () +if (WAMR_BUILD_MINI_LOADER EQUAL 1) + add_definitions (-DWASM_ENABLE_MINI_LOADER=1) + message (" WASM mini loader enabled") +else () + add_definitions (-DWASM_ENABLE_MINI_LOADER=0) +endif () +if (WAMR_DISABLE_HW_BOUND_CHECK EQUAL 1) + add_definitions (-DWASM_DISABLE_HW_BOUND_CHECK=1) + message (" Hardware boundary check disabled") +endif () +if (DEFINED WAMR_APP_THREAD_STACK_SIZE_MAX) + add_definitions (-DAPP_THREAD_STACK_SIZE_MAX=${WAMR_APP_THREAD_STACK_SIZE_MAX}) +endif () + diff --git a/wamr/build-scripts/runtime_lib.cmake b/wamr/build-scripts/runtime_lib.cmake new file mode 100644 index 0000000..a87a2ff --- /dev/null +++ b/wamr/build-scripts/runtime_lib.cmake @@ -0,0 +1,125 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + +if (NOT DEFINED WAMR_ROOT_DIR) + set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../) +endif () +if (NOT DEFINED SHARED_DIR) + set (SHARED_DIR ${WAMR_ROOT_DIR}/core/shared) +endif () +if (NOT DEFINED IWASM_DIR) + set (IWASM_DIR ${WAMR_ROOT_DIR}/core/iwasm) +endif () +if (NOT DEFINED APP_MGR_DIR) + set (APP_MGR_DIR ${WAMR_ROOT_DIR}/core/app-mgr) +endif () +if (NOT DEFINED APP_FRAMEWORK_DIR) + set (APP_FRAMEWORK_DIR ${WAMR_ROOT_DIR}/core/app-framework) +endif () +if (NOT DEFINED DEPS_DIR) + set (DEPS_DIR ${WAMR_ROOT_DIR}/core/deps) +endif () + +if (DEFINED EXTRA_SDK_INCLUDE_PATH) + message(STATUS, "EXTRA_SDK_INCLUDE_PATH = ${EXTRA_SDK_INCLUDE_PATH} ") + include_directories ( + ${EXTRA_SDK_INCLUDE_PATH} + ) +endif () + +# Set default options + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +################ optional according to settings ################ +if (WAMR_BUILD_INTERP EQUAL 1 OR WAMR_BUILD_JIT EQUAL 1) + include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) +endif () + +if (WAMR_BUILD_AOT EQUAL 1) + include (${IWASM_DIR}/aot/iwasm_aot.cmake) + if (WAMR_BUILD_JIT EQUAL 1) + include (${IWASM_DIR}/compilation/iwasm_compl.cmake) + endif () +endif () + +if (WAMR_BUILD_APP_FRAMEWORK EQUAL 1) + include (${APP_FRAMEWORK_DIR}/app_framework.cmake) + include (${SHARED_DIR}/coap/lib_coap.cmake) + include (${APP_MGR_DIR}/app-manager/app_mgr.cmake) + include (${APP_MGR_DIR}/app-mgr-shared/app_mgr_shared.cmake) +endif () + +if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) + include (${IWASM_DIR}/libraries/libc-builtin/libc_builtin.cmake) +endif () + +if (WAMR_BUILD_LIBC_WASI EQUAL 1) + include (${IWASM_DIR}/libraries/libc-wasi/libc_wasi.cmake) +endif () + +if (WAMR_BUILD_LIB_PTHREAD EQUAL 1) + include (${IWASM_DIR}/libraries/lib-pthread/lib_pthread.cmake) + # Enable the dependent feature if lib pthread is enabled + set (WAMR_BUILD_THREAD_MGR 1) + set (WAMR_BUILD_BULK_MEMORY 1) + set (WAMR_BUILD_SHARED_MEMORY 1) +endif () + +if (WAMR_BUILD_THREAD_MGR EQUAL 1) + include (${IWASM_DIR}/libraries/thread-mgr/thread_mgr.cmake) +endif () + +####################### Common sources ####################### +if (NOT MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections \ + -Wall -Wno-unused-parameter -Wno-pedantic") +endif () + +# include the build config template file +include (${CMAKE_CURRENT_LIST_DIR}/config_common.cmake) + +include_directories (${IWASM_DIR}/include) + +file (GLOB header + ${IWASM_DIR}/include/*.h +) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) + +enable_language (ASM) + +include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) +include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) +include (${IWASM_DIR}/common/iwasm_common.cmake) +include (${SHARED_DIR}/utils/shared_utils.cmake) + + +set (source_all + ${PLATFORM_SHARED_SOURCE} + ${MEM_ALLOC_SHARED_SOURCE} + ${UTILS_SHARED_SOURCE} + ${LIBC_BUILTIN_SOURCE} + ${LIBC_WASI_SOURCE} + ${IWASM_COMMON_SOURCE} + ${IWASM_INTERP_SOURCE} + ${IWASM_AOT_SOURCE} + ${IWASM_COMPL_SOURCE} + ${WASM_APP_LIB_SOURCE_ALL} + ${NATIVE_INTERFACE_SOURCE} + ${APP_MGR_SOURCE} + ${LIB_PTHREAD_SOURCE} + ${THREAD_MGR_SOURCE} +) + +set (WAMR_RUNTIME_LIB_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/README.md b/wamr/core/app-framework/README.md new file mode 100644 index 0000000..34f0e27 --- /dev/null +++ b/wamr/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/wamr/core/app-framework/app-native-shared/README.md b/wamr/core/app-framework/app-native-shared/README.md new file mode 100644 index 0000000..b166e0b --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/README.md @@ -0,0 +1,11 @@ + Notes: +======= +This folder 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. + + + + + diff --git a/wamr/core/app-framework/app-native-shared/attr_container.c b/wamr/core/app-framework/app-native-shared/attr_container.c new file mode 100644 index 0000000..2cd816c --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/attr_container.c @@ -0,0 +1,827 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bi-inc/attr_container.h" + +typedef union jvalue { + bool z; + int8_t b; + uint16_t c; + int16_t s; + int32_t i; + int64_t j; + float f; + double d; +} jvalue; + + + +static inline int16_t get_int16(const char *buf) +{ + int16_t ret; + bh_memcpy_s(&ret, sizeof(int16_t), buf, sizeof(int16_t)); + return ret; +} + +static inline uint16_t get_uint16(const char *buf) +{ + return get_int16(buf); +} + +static inline int32_t get_int32(const char *buf) +{ + int32_t ret; + bh_memcpy_s(&ret, sizeof(int32_t), buf, sizeof(int32_t)); + return ret; +} + +static inline uint32_t get_uint32(const char *buf) +{ + return get_int32(buf); +} + +static inline int64_t get_int64(const char *buf) +{ + int64_t ret; + bh_memcpy_s(&ret, sizeof(int64_t), buf, sizeof(int64_t)); + return ret; +} + +static inline uint64_t get_uint64(const char *buf) +{ + return get_int64(buf); +} + +static inline void set_int16(char *buf, int16_t v) +{ + bh_memcpy_s(buf, sizeof(int16_t), &v, sizeof(int16_t)); +} + +static inline void set_uint16(char *buf, uint16_t v) +{ + bh_memcpy_s(buf, sizeof(uint16_t), &v, sizeof(uint16_t)); +} + +static inline void set_int32(char *buf, int32_t v) +{ + bh_memcpy_s(buf, sizeof(int32_t), &v, sizeof(int32_t)); +} + +static inline void set_uint32(char *buf, uint32_t v) +{ + bh_memcpy_s(buf, sizeof(uint32_t), &v, sizeof(uint32_t)); +} + +static inline void set_int64(char *buf, int64_t v) +{ + bh_memcpy_s(buf, sizeof(int64_t), &v, sizeof(int64_t)); +} + +static inline void set_uint64(char *buf, uint64_t v) +{ + bh_memcpy_s(buf, sizeof(uint64_t), &v, sizeof(uint64_t)); +} + +char* +attr_container_get_attr_begin(const attr_container_t *attr_cont, + uint32_t *p_total_length, uint16_t *p_attr_num) +{ + char *p = (char*) attr_cont->buf; + uint16_t str_len, attr_num; + uint32_t total_length; + + /* skip total length */ + total_length = get_uint32(p); + p += sizeof(uint32_t); + if (!total_length) + return NULL; + + /* tag length */ + str_len = get_uint16(p); + p += sizeof(uint16_t); + if (!str_len) + return NULL; + + /* tag content */ + p += str_len; + if (p - attr_cont->buf >= total_length) + return NULL; + + /* attribute num */ + attr_num = get_uint16(p); + p += sizeof(uint16_t); + if (p - attr_cont->buf >= total_length) + return NULL; + + if (p_total_length) + *p_total_length = total_length; + + if (p_attr_num) + *p_attr_num = attr_num; + + /* first attribute */ + return p; +} + +static char* +attr_container_get_attr_next(const char *curr_attr) +{ + char *p = (char*) curr_attr; + uint8_t type; + + /* key length and key */ + p += sizeof(uint16_t) + get_uint16(p); + type = *p++; + + /* Short type to Boolean type */ + if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) { + p += 1 << (type & 3); + return p; + } + /* String type */ + else if (type == ATTR_TYPE_STRING) { + p += sizeof(uint16_t) + get_uint16(p); + return p; + } + /* ByteArray type */ + else if (type == ATTR_TYPE_BYTEARRAY) { + p += sizeof(uint32_t) + get_uint32(p); + return p; + } + + return NULL; +} + +static const char* +attr_container_find_attr(const attr_container_t *attr_cont, const char *key) +{ + uint32_t total_length; + uint16_t str_len, attr_num, i; + const char *p = attr_cont->buf; + + if (!key) + return NULL; + + if (!(p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num))) + return NULL; + + for (i = 0; i < attr_num; i++) { + /* key length */ + if (!(str_len = get_uint16(p))) + return NULL; + + if (str_len == strlen(key) + 1 + && memcmp(p + sizeof(uint16_t), key, str_len) == 0) { + if (p + sizeof(uint16_t) + str_len - attr_cont->buf >= total_length) + return NULL; + return p; + } + + if (!(p = attr_container_get_attr_next(p))) + return NULL; + } + + return NULL; +} + +char* +attr_container_get_attr_end(const attr_container_t *attr_cont) +{ + uint32_t total_length; + uint16_t attr_num, i; + char *p; + + if (!(p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num))) + return NULL; + + for (i = 0; i < attr_num; i++) + if (!(p = attr_container_get_attr_next(p))) + return NULL; + + return p; +} + +static char* +attr_container_get_msg_end(attr_container_t *attr_cont) +{ + char *p = attr_cont->buf; + return p + get_uint32(p); +} + +uint16_t attr_container_get_attr_num(const attr_container_t *attr_cont) +{ + uint16_t str_len; + /* skip total length */ + const char *p = attr_cont->buf + sizeof(uint32_t); + + str_len = get_uint16(p); + /* skip tag length and tag */ + p += sizeof(uint16_t) + str_len; + + /* attribute num */ + return get_uint16(p); +} + +static void attr_container_inc_attr_num(attr_container_t *attr_cont) +{ + uint16_t str_len, attr_num; + /* skip total length */ + char *p = attr_cont->buf + sizeof(uint32_t); + + str_len = get_uint16(p); + /* skip tag length and tag */ + p += sizeof(uint16_t) + str_len; + + /* attribute num */ + attr_num = get_uint16(p) + 1; + set_uint16(p, attr_num); +} + +attr_container_t * +attr_container_create(const char *tag) +{ + attr_container_t *attr_cont; + int length, tag_length; + char *p; + + tag_length = tag ? strlen(tag) + 1 : 1; + length = offsetof(attr_container_t, buf) + + /* total length + tag length + tag + reserved 100 bytes */ + sizeof(uint32_t) + sizeof(uint16_t) + tag_length + 100; + + if (!(attr_cont = attr_container_malloc(length))) { + attr_container_printf( + "Create attr_container failed: allocate memory failed.\r\n"); + return NULL; + } + + memset(attr_cont, 0, length); + p = attr_cont->buf; + + /* total length */ + set_uint32(p, length - offsetof(attr_container_t, buf)); + p += 4; + + /* tag length, tag */ + set_uint16(p, tag_length); + p += 2; + if (tag) + bh_memcpy_s(p, tag_length, tag, tag_length); + + return attr_cont; +} + +void attr_container_destroy(const attr_container_t *attr_cont) +{ + if (attr_cont) + attr_container_free((char*) attr_cont); +} + +static bool check_set_attr(attr_container_t **p_attr_cont, const char *key) +{ + uint32_t flags; + + if (!p_attr_cont || !*p_attr_cont || !key || strlen(key) == 0) { + attr_container_printf( + "Set attribute failed: invalid input arguments.\r\n"); + return false; + } + + flags = get_uint32((char*) *p_attr_cont); + if (flags & ATTR_CONT_READONLY_SHIFT) { + attr_container_printf( + "Set attribute failed: attribute container is readonly.\r\n"); + return false; + } + + return true; +} + +bool attr_container_set_attr(attr_container_t **p_attr_cont, const char *key, + int type, const void *value, int value_length) +{ + attr_container_t *attr_cont, *attr_cont1; + uint16_t str_len; + uint32_t total_length, attr_len; + char *p, *p1, *attr_end, *msg_end, *attr_buf; + + if (!check_set_attr(p_attr_cont, key)) { + return false; + } + + attr_cont = *p_attr_cont; + p = attr_cont->buf; + total_length = get_uint32(p); + + if (!(attr_end = attr_container_get_attr_end(attr_cont))) { + attr_container_printf("Set attr failed: get attr end failed.\r\n"); + return false; + } + + msg_end = attr_container_get_msg_end(attr_cont); + + /* key len + key + '\0' + type */ + attr_len = sizeof(uint16_t) + strlen(key) + 1 + 1; + if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) + attr_len += 1 << (type & 3); + else if (type == ATTR_TYPE_STRING) + attr_len += sizeof(uint16_t) + value_length; + else if (type == ATTR_TYPE_BYTEARRAY) + attr_len += sizeof(uint32_t) + value_length; + + if (!(p = attr_buf = attr_container_malloc(attr_len))) { + attr_container_printf("Set attr failed: allocate memory failed.\r\n"); + return false; + } + + /* Set the attr buf */ + str_len = strlen(key) + 1; + set_uint16(p, str_len); + p += sizeof(uint16_t); + bh_memcpy_s(p, str_len, key, str_len); + p += str_len; + + *p++ = type; + if (type >= ATTR_TYPE_SHORT && type <= ATTR_TYPE_BOOLEAN) + bh_memcpy_s(p, 1 << (type & 3), value, 1 << (type & 3)); + else if (type == ATTR_TYPE_STRING) { + set_uint16(p, value_length); + p += sizeof(uint16_t); + bh_memcpy_s(p, value_length, value, value_length); + } else if (type == ATTR_TYPE_BYTEARRAY) { + set_uint32(p, value_length); + p += sizeof(uint32_t); + bh_memcpy_s(p, value_length, value, value_length); + } + + if ((p = (char*) attr_container_find_attr(attr_cont, key))) { + /* key found */ + p1 = attr_container_get_attr_next(p); + + if (p1 - p == attr_len) { + bh_memcpy_s(p, attr_len, attr_buf, attr_len); + attr_container_free(attr_buf); + return true; + } + + if (p1 - p + msg_end - attr_end >= attr_len) { + memmove(p, p1, attr_end - p1); + bh_memcpy_s(p + (attr_end - p1), attr_len, attr_buf, attr_len); + attr_container_free(attr_buf); + return true; + } + + total_length += attr_len + 100; + if (!(attr_cont1 = attr_container_malloc( + offsetof(attr_container_t, buf) + total_length))) { + attr_container_printf( + "Set attr failed: allocate memory failed.\r\n"); + attr_container_free(attr_buf); + return false; + } + + bh_memcpy_s(attr_cont1, p - (char* )attr_cont, attr_cont, + p - (char* )attr_cont); + bh_memcpy_s((char* )attr_cont1 + (unsigned )(p - (char* )attr_cont), + attr_end - p1, p1, attr_end - p1); + bh_memcpy_s( + (char* )attr_cont1 + (unsigned )(p - (char* )attr_cont) + + (unsigned )(attr_end - p1), attr_len, attr_buf, + attr_len); + p = attr_cont1->buf; + set_uint32(p, total_length); + *p_attr_cont = attr_cont1; + /* Free original buffer */ + attr_container_free(attr_cont); + attr_container_free(attr_buf); + return true; + } else { + /* key not found */ + if (msg_end - attr_end >= attr_len) { + bh_memcpy_s(attr_end, msg_end - attr_end, attr_buf, attr_len); + attr_container_inc_attr_num(attr_cont); + attr_container_free(attr_buf); + return true; + } + + total_length += attr_len + 100; + if (!(attr_cont1 = attr_container_malloc( + offsetof(attr_container_t, buf) + total_length))) { + attr_container_printf( + "Set attr failed: allocate memory failed.\r\n"); + attr_container_free(attr_buf); + return false; + } + + bh_memcpy_s(attr_cont1, attr_end - (char* )attr_cont, attr_cont, + attr_end - (char* )attr_cont); + bh_memcpy_s( + (char* )attr_cont1 + (unsigned )(attr_end - (char* )attr_cont), + attr_len, attr_buf, attr_len); + attr_container_inc_attr_num(attr_cont1); + p = attr_cont1->buf; + set_uint32(p, total_length); + *p_attr_cont = attr_cont1; + /* Free original buffer */ + attr_container_free(attr_cont); + attr_container_free(attr_buf); + return true; + } + + return false; +} + +bool attr_container_set_short(attr_container_t **p_attr_cont, const char *key, + short value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_SHORT, &value, 2); +} + +bool attr_container_set_int(attr_container_t **p_attr_cont, const char *key, + int value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT, &value, 4); +} + +bool attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, + int64_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_INT64, &value, 8); +} + +bool attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, + int8_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BYTE, &value, 1); +} + +bool attr_container_set_uint16(attr_container_t **p_attr_cont, const char *key, + uint16_t value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_UINT16, &value, + 2); +} + +bool attr_container_set_float(attr_container_t **p_attr_cont, const char *key, + float value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_FLOAT, &value, 4); +} + +bool attr_container_set_double(attr_container_t **p_attr_cont, const char *key, + double value) +{ + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_DOUBLE, &value, + 8); +} + +bool attr_container_set_bool(attr_container_t **p_attr_cont, const char *key, + bool value) +{ + int8_t value1 = value ? 1 : 0; + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BOOLEAN, &value1, + 1); +} + +bool attr_container_set_string(attr_container_t **p_attr_cont, const char *key, + const char *value) +{ + if (!value) { + attr_container_printf("Set attr failed: invald input arguments.\r\n"); + return false; + } + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_STRING, value, + strlen(value) + 1);; +} + +bool attr_container_set_bytearray(attr_container_t **p_attr_cont, + const char *key, const int8_t *value, unsigned length) +{ + if (!value) { + attr_container_printf("Set attr failed: invald input arguments.\r\n"); + return false; + } + return attr_container_set_attr(p_attr_cont, key, ATTR_TYPE_BYTEARRAY, value, + length);; +} + +static const char* +attr_container_get_attr(const attr_container_t *attr_cont, const char *key) +{ + const char *attr_addr; + + if (!attr_cont || !key) { + attr_container_printf( + "Get attribute failed: invalid input arguments.\r\n"); + return NULL; + } + + if (!(attr_addr = attr_container_find_attr(attr_cont, key))) { + attr_container_printf("Get attribute failed: lookup key failed.\r\n"); + return false; + } + + /* key len + key + '\0' */ + return attr_addr + 2 + strlen(key) + 1; +} + +#define TEMPLATE_ATTR_BUF_TO_VALUE(attr, key, var_name) do {\ + jvalue val; \ + const char *addr = attr_container_get_attr(attr, key); \ + uint8_t type; \ + if (!addr) \ + return 0; \ + val.j = 0; \ + type = *(uint8_t*)addr++; \ + switch (type) { \ + case ATTR_TYPE_SHORT: \ + case ATTR_TYPE_INT: \ + case ATTR_TYPE_INT64: \ + case ATTR_TYPE_BYTE: \ + case ATTR_TYPE_UINT16: \ + case ATTR_TYPE_FLOAT: \ + case ATTR_TYPE_DOUBLE: \ + case ATTR_TYPE_BOOLEAN: \ + bh_memcpy_s(&val, sizeof(val.var_name), addr, 1 << (type & 3)); \ + break; \ + case ATTR_TYPE_STRING: \ + { \ + unsigned len= get_uint16(addr); \ + addr += 2; \ + if (len > sizeof(val.var_name)) \ + len = sizeof(val.var_name); \ + bh_memcpy_s(&val.var_name, sizeof(val.var_name), addr, len); \ + break; \ + } \ + case ATTR_TYPE_BYTEARRAY: \ + { \ + unsigned len= get_uint32(addr); \ + addr += 4; \ + if (len > sizeof(val.var_name)) \ + len = sizeof(val.var_name); \ + bh_memcpy_s(&val.var_name, sizeof(val.var_name), addr, len); \ + break; \ + } \ + } \ + return val.var_name; \ + } while (0) + +short attr_container_get_as_short(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, s); +} + +int attr_container_get_as_int(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, i); +} + +int64_t attr_container_get_as_int64(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, j); +} + +int8_t attr_container_get_as_byte(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, b); +} + +uint16_t attr_container_get_as_uint16(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, s); +} + +float attr_container_get_as_float(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, f); +} + +double attr_container_get_as_double(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, d); +} + +bool attr_container_get_as_bool(const attr_container_t *attr_cont, + const char *key) +{ + TEMPLATE_ATTR_BUF_TO_VALUE(attr_cont, key, z); +} + +const int8_t* +attr_container_get_as_bytearray(const attr_container_t *attr_cont, + const char *key, unsigned *array_length) +{ + const char *addr = attr_container_get_attr(attr_cont, key); + uint8_t type; + uint32_t length; + + if (!addr) + return NULL; + + if (!array_length) { + attr_container_printf("Get attribute failed: invalid input arguments."); + return NULL; + } + + type = *(uint8_t*) addr++; + switch (type) { + case ATTR_TYPE_SHORT: + case ATTR_TYPE_INT: + case ATTR_TYPE_INT64: + case ATTR_TYPE_BYTE: + case ATTR_TYPE_UINT16: + case ATTR_TYPE_FLOAT: + case ATTR_TYPE_DOUBLE: + case ATTR_TYPE_BOOLEAN: + length = 1 << (type & 3); + break; + case ATTR_TYPE_STRING: + length = get_uint16(addr); + addr += 2; + break; + case ATTR_TYPE_BYTEARRAY: + length = get_uint32(addr); + addr += 4; + break; + default: + return NULL; + } + + *array_length = length; + return (const int8_t*) addr; +} + +char* +attr_container_get_as_string(const attr_container_t *attr_cont, const char *key) +{ + unsigned array_length; + return (char*) attr_container_get_as_bytearray(attr_cont, key, + &array_length); +} + +const char* +attr_container_get_tag(const attr_container_t *attr_cont) +{ + return attr_cont ? + attr_cont->buf + sizeof(uint32_t) + sizeof(uint16_t) : NULL; +} + +bool attr_container_contain_key(const attr_container_t *attr_cont, + const char *key) +{ + if (!attr_cont || !key || !strlen(key)) { + attr_container_printf( + "Check contain key failed: invalid input arguments.\r\n"); + return false; + } + return attr_container_find_attr(attr_cont, key) ? true : false; +} + +unsigned int attr_container_get_serialize_length( + const attr_container_t *attr_cont) +{ + const char *p; + + if (!attr_cont) { + attr_container_printf( + "Get container serialize length failed: invalid input arguments.\r\n"); + return 0; + } + + p = attr_cont->buf; + return sizeof(uint16_t) + get_uint32(p); +} + +bool attr_container_serialize(char *buf, const attr_container_t *attr_cont) +{ + const char *p; + uint16_t flags; + uint32_t length; + + if (!buf || !attr_cont) { + attr_container_printf( + "Container serialize failed: invalid input arguments.\r\n"); + return false; + } + + p = attr_cont->buf; + length = sizeof(uint16_t) + get_uint32(p); + bh_memcpy_s(buf, length, attr_cont, length); + /* Set readonly */ + flags = get_uint16((const char*) attr_cont); + set_uint16(buf, flags | (1 << ATTR_CONT_READONLY_SHIFT)); + + return true; +} + +bool attr_container_is_constant(const attr_container_t* attr_cont) +{ + uint16_t flags; + + if (!attr_cont) { + attr_container_printf( + "Container check const: invalid input arguments.\r\n"); + return false; + } + + flags = get_uint16((const char*) attr_cont); + return (flags & (1 << ATTR_CONT_READONLY_SHIFT)) ? true : false; +} + +void attr_container_dump(const attr_container_t *attr_cont) +{ + uint32_t total_length; + uint16_t attr_num, i, type; + const char *p, *tag, *key; + jvalue value; + + if (!attr_cont) + return; + + tag = attr_container_get_tag(attr_cont); + if (!tag) + return; + + attr_container_printf("Attribute container dump:\n"); + attr_container_printf("Tag: %s\n", tag); + + p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num); + if (!p) + return; + + attr_container_printf("Attribute list:\n"); + for (i = 0; i < attr_num; i++) { + key = p + 2; + /* Skip key len and key */ + p += 2 + get_uint16(p); + type = *p++; + attr_container_printf(" key: %s", key); + + switch (type) { + case ATTR_TYPE_SHORT: + bh_memcpy_s(&value.s, sizeof(int16_t), p, sizeof(int16_t)); + attr_container_printf(", type: short, value: 0x%x\n", + value.s & 0xFFFF); + p += 2; + break; + case ATTR_TYPE_INT: + bh_memcpy_s(&value.i, sizeof(int32_t), p, sizeof(int32_t)); + attr_container_printf(", type: int, value: 0x%x\n", value.i); + p += 4; + break; + case ATTR_TYPE_INT64: + bh_memcpy_s(&value.j, sizeof(uint64_t), p, sizeof(uint64_t)); + attr_container_printf(", type: int64, value: 0x%llx\n", (long long unsigned int)(value.j)); + p += 8; + break; + case ATTR_TYPE_BYTE: + bh_memcpy_s(&value.b, 1, p, 1); + attr_container_printf(", type: byte, value: 0x%x\n", + value.b & 0xFF); + p++; + break; + case ATTR_TYPE_UINT16: + bh_memcpy_s(&value.c, sizeof(uint16_t), p, sizeof(uint16_t)); + attr_container_printf(", type: uint16, value: 0x%x\n", value.c); + p += 2; + break; + case ATTR_TYPE_FLOAT: + bh_memcpy_s(&value.f, sizeof(float), p, sizeof(float)); + attr_container_printf(", type: float, value: %f\n", value.f); + p += 4; + break; + case ATTR_TYPE_DOUBLE: + bh_memcpy_s(&value.d, sizeof(double), p, sizeof(double)); + attr_container_printf(", type: double, value: %f\n", value.d); + p += 8; + break; + case ATTR_TYPE_BOOLEAN: + bh_memcpy_s(&value.z, 1, p, 1); + attr_container_printf(", type: bool, value: 0x%x\n", value.z); + p++; + break; + case ATTR_TYPE_STRING: + attr_container_printf(", type: string, value: %s\n", + p + sizeof(uint16_t)); + p += sizeof(uint16_t) + get_uint16(p); + break; + case ATTR_TYPE_BYTEARRAY: + attr_container_printf(", type: byte array, length: %d\n", + get_uint32(p)); + p += sizeof(uint32_t) + get_uint32(p); + break; + } + } + + attr_container_printf("\n"); +} + diff --git a/wamr/core/app-framework/app-native-shared/bi-inc/attr_container.h b/wamr/core/app-framework/app-native-shared/bi-inc/attr_container.h new file mode 100644 index 0000000..e70d0e4 --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/bi-inc/attr_container.h @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _ATTR_CONTAINER_H_ +#define _ATTR_CONTAINER_H_ + +#include +#include +#include +#include +#include +#include +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Attribute type */ +enum { + ATTR_TYPE_BEGIN = 1, + ATTR_TYPE_SHORT = ATTR_TYPE_BEGIN, + ATTR_TYPE_INT, + ATTR_TYPE_INT64, + ATTR_TYPE_BYTE, + ATTR_TYPE_UINT16, + ATTR_TYPE_FLOAT, + ATTR_TYPE_DOUBLE, + ATTR_TYPE_BOOLEAN, + ATTR_TYPE_STRING, + ATTR_TYPE_BYTEARRAY, + ATTR_TYPE_END = ATTR_TYPE_BYTEARRAY +}; + +#define ATTR_CONT_READONLY_SHIFT 2 + +typedef struct attr_container { + /* container flag: + * bit0, bit1 denote the implemenation algorithm, 00: buffer, 01: link list + * bit2 denotes the readonly flag: 1 is readonly and attr cannot be set + */ + char flags[2]; + /** + * Buffer format + * for buffer implementation: + * buf length (4 bytes) + * tag length (2 bytes) + * tag + * attr num (2bytes) + * attr[0..n-1]: + * attr key length (2 bytes) + * attr type (1byte) + * attr value (length depends on attr type) + */ + char buf[1]; +} attr_container_t; + +/** + * Create attribute container + * + * @param tag tag of current attribute container + * + * @return the created attribute container, NULL if failed + */ +attr_container_t * +attr_container_create(const char *tag); + +/** + * Destroy attribute container + * + * @param attr_cont the attribute container to destroy + */ +void +attr_container_destroy(const attr_container_t *attr_cont); + +/** + * Set short attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_short(attr_container_t **p_attr_cont, const char *key, + short value); + +/** + * Set int attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_int(attr_container_t **p_attr_cont, const char *key, + int value); + +/** + * Set int64 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_int64(attr_container_t **p_attr_cont, const char *key, + int64_t value); + +/** + * Set byte attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_byte(attr_container_t **p_attr_cont, const char *key, + int8_t value); + +/** + * Set uint16 attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_uint16(attr_container_t **p_attr_cont, const char *key, + uint16_t value); + +/** + * Set float attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_float(attr_container_t **p_attr_cont, const char *key, + float value); + +/** + * Set double attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_double(attr_container_t **p_attr_cont, const char *key, + double value); + +/** + * Set bool attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_bool(attr_container_t **p_attr_cont, const char *key, + bool value); + +/** + * Set string attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the attribute value + * + * @return true if success, false otherwise + */ +bool +attr_container_set_string(attr_container_t **p_attr_cont, const char *key, + const char *value); + +/** + * Set bytearray attribute in attribute container + * + * @param p_attr_cont pointer to attribute container to set attribute, and + * return the new attribute container if it is re-created + * @param key the attribute key + * @param value the bytearray buffer + * @param length the bytearray length + * + * @return true if success, false otherwise + */ +bool +attr_container_set_bytearray(attr_container_t **p_attr_cont, const char *key, + const int8_t *value, unsigned length); + +/** + * Get tag of current attribute container + * + * @param attr_cont the attribute container + * + * @return tag of current attribute container + */ +const char* +attr_container_get_tag(const attr_container_t *attr_cont); + +/** + * Get attribute number of current attribute container + * + * @param attr_cont the attribute container + * + * @return attribute number of current attribute container + */ +uint16_t +attr_container_get_attr_num(const attr_container_t *attr_cont); + +/** + * Whether the attribute container contains an attribute key. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return true if key is contained in message, false otherwise + */ +bool +attr_container_contain_key(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as short value, + * return 0 if attribute isn't found in message. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the short value of the attribute, 0 if key isn't found + */ +short +attr_container_get_as_short(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as int value, + * return 0 if attribute isn't found in message. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the int value of the attribute, 0 if key isn't found + */ +int +attr_container_get_as_int(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as int64 value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the long value of the attribute, 0 if key isn't found + */ +int64_t +attr_container_get_as_int64(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as byte value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the byte value of the attribute, 0 if key isn't found + */ +int8_t +attr_container_get_as_byte(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as uint16 value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the char value of the attribute, 0 if key isn't found + */ +uint16_t +attr_container_get_as_uint16(const attr_container_t *attr_cont, + const char *key); + +/** + * Get attribute from attribute container and return it as float value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the float value of the attribute, 0 if key isn't found + */ +float +attr_container_get_as_float(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as double value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the double value of the attribute, 0 if key isn't found + */ +double +attr_container_get_as_double(const attr_container_t *attr_cont, + const char *key); + +/** + * Get attribute from attribute container and return it as bool value, + * return false if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the bool value of the attribute, 0 if key isn't found + */ +bool +attr_container_get_as_bool(const attr_container_t *attr_cont, const char *key); + +/** + * Get attribute from attribute container and return it as string value, + * return NULL if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the string value of the attribute, NULL if key isn't found + */ +char* +attr_container_get_as_string(const attr_container_t *attr_cont, + const char *key); + +/** + * Get attribute from attribute container and return it as bytearray value, + * return 0 if attribute isn't found in attribute container. + * + * @param attr_cont the attribute container + * @param key the attribute key + * + * @return the bytearray value of the attribute, NULL if key isn't found + */ +const int8_t* +attr_container_get_as_bytearray(const attr_container_t *attr_cont, + const char *key, unsigned *array_length); + +/** + * Get the buffer size of attribute container + * + * @param attr_cont the attribute container + * + * @return the buffer size of attribute container + */ +unsigned +attr_container_get_serialize_length(const attr_container_t *attr_cont); + +/** + * Serialize attribute container to a buffer + * + * @param buf the buffer to receive the serialized data + * @param attr_cont the attribute container to be serialized + * + * @return true if success, false otherwise + */ +bool +attr_container_serialize(char *buf, const attr_container_t *attr_cont); + +/** + * Whether the attribute container is const, or set attribute isn't supported + * + * @param attr_cont the attribute container + * + * @return true if const, false otherwise + */ +bool +attr_container_is_constant(const attr_container_t* attr_cont); + +void +attr_container_dump(const attr_container_t *attr_cont); + +#ifndef attr_container_malloc +#define attr_container_malloc WA_MALLOC +#endif + +#ifndef attr_container_free +#define attr_container_free WA_FREE +#endif + +#ifndef attr_container_printf +#define attr_container_printf printf +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _ATTR_CONTAINER_H_ */ + diff --git a/wamr/core/app-framework/app-native-shared/bi-inc/shared_utils.h b/wamr/core/app-framework/app-native-shared/bi-inc/shared_utils.h new file mode 100644 index 0000000..3154b4b --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/bi-inc/shared_utils.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SHARED_UTILS_H_ +#define _SHARED_UTILS_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define FMT_ATTR_CONTAINER 99 +#define FMT_APP_RAW_BINARY 98 + +/* the request structure */ +typedef struct request { + // message id + uint32 mid; + + // url of the request + char *url; + + // action of the request, can be PUT/GET/POST/DELETE + int action; + + // payload format, currently only support attr_container_t type + int fmt; + + // payload of the request, currently only support attr_container_t type + void *payload; + + //length in bytes of the payload + int payload_len; + + //sender of the request + unsigned long sender; +} request_t; + +/* the response structure */ +typedef struct response { + // message id + uint32 mid; + + // status of the response + int status; + + // payload format + int fmt; + + // payload of the response, + void *payload; + + //length in bytes of the payload + int payload_len; + + //receiver of the response + unsigned long reciever; +} response_t; + +int +check_url_start(const char* url, int url_len, const char * leading_str); + +bool +match_url(char * pattern, char * matched); + +char * +find_key_value(char * buffer, int buffer_len, char * key, char * value, + int value_len, char delimiter); + +request_t * +clone_request(request_t *request); + +void +request_cleaner(request_t *request); + +response_t * +clone_response(response_t * response); + +void +response_cleaner(response_t * response); + +/** + * @brief Set fields of response. + * + * @param response pointer of the response to be set + * @param status status of response + * @param fmt format of the response payload + * @param payload payload of the response + * @param payload_len length in bytes of the response payload + * + * @return pointer to the response + * + * @warning the response pointer MUST NOT be NULL + */ +response_t * +set_response(response_t * response, int status, int fmt, + const char *payload, int payload_len); + +/** + * @brief Make a response for a request. + * + * @param request pointer of the request + * @param response pointer of the response to be made + * + * @return pointer to the response + * + * @warning the request and response pointers MUST NOT be NULL + */ +response_t * +make_response_for_request(request_t * request, response_t * response); + +/** + * @brief Initialize a request. + * + * @param request pointer of the request to be initialized + * @param url url of the request + * @param action action of the request + * @param fmt format of the request payload + * @param payload payload of the request + * @param payload_len length in bytes of the request payload + * + * @return pointer to the request + * + * @warning the request pointer MUST NOT be NULL + */ +request_t * +init_request(request_t * request, char *url, int action, int fmt, + void *payload, int payload_len); + +char * +pack_request(request_t *request, int * size); + +request_t * +unpack_request(char * packet, int size, request_t * request); + +char * +pack_response(response_t *response, int * size); + +response_t * +unpack_response(char * packet, int size, response_t * response); + +void +free_req_resp_packet(char * packet); + +char * +wa_strdup(const char *str); + + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SHARED_UTILS_H_ */ diff --git a/wamr/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h b/wamr/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h new file mode 100644 index 0000000..7a391a1 --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/bi-inc/wgl_shared_utils.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H +#define WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H + +#include "bh_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + +/* Object native function IDs */ +enum { + OBJ_FUNC_ID_DEL, + OBJ_FUNC_ID_DEL_ASYNC, + OBJ_FUNC_ID_CLEAN, + OBJ_FUNC_ID_SET_EVT_CB, + OBJ_FUNC_ID_ALIGN, + + /* Number of functions */ + _OBJ_FUNC_ID_NUM, +}; + +/* Button native function IDs */ +enum { + BTN_FUNC_ID_CREATE, + BTN_FUNC_ID_SET_TOGGLE, + BTN_FUNC_ID_SET_STATE, + BTN_FUNC_ID_TOGGLE, + BTN_FUNC_ID_SET_INK_IN_TIME, + BTN_FUNC_ID_SET_INK_WAIT_TIME, + BTN_FUNC_ID_SET_INK_OUT_TIME, + BTN_FUNC_ID_GET_STATE, + BTN_FUNC_ID_GET_TOGGLE, + BTN_FUNC_ID_GET_INK_IN_TIME, + BTN_FUNC_ID_GET_INK_WAIT_TIME, + BTN_FUNC_ID_GET_INK_OUT_TIME, + /* Number of functions */ + _BTN_FUNC_ID_NUM, +}; + +/* Check box native function IDs */ +enum { + CB_FUNC_ID_CREATE, + CB_FUNC_ID_SET_TEXT, + CB_FUNC_ID_SET_STATIC_TEXT, + CB_FUNC_ID_GET_TEXT, + CB_FUNC_ID_GET_TEXT_LENGTH, + + /* Number of functions */ + _CB_FUNC_ID_NUM, +}; + +/* List native function IDs */ +enum { + LIST_FUNC_ID_CREATE, + LIST_FUNC_ID_ADD_BTN, + + /* Number of functions */ + _LIST_FUNC_ID_NUM, +}; + +/* Label native function IDs */ +enum { + LABEL_FUNC_ID_CREATE, + LABEL_FUNC_ID_SET_TEXT, + LABEL_FUNC_ID_SET_ARRAY_TEXT, + LABEL_FUNC_ID_SET_STATIC_TEXT, + LABEL_FUNC_ID_SET_LONG_MODE, + LABEL_FUNC_ID_SET_ALIGN, + LABEL_FUNC_ID_SET_RECOLOR, + LABEL_FUNC_ID_SET_BODY_DRAW, + LABEL_FUNC_ID_SET_ANIM_SPEED, + LABEL_FUNC_ID_SET_TEXT_SEL_START, + LABEL_FUNC_ID_SET_TEXT_SEL_END, + LABEL_FUNC_ID_GET_TEXT, + LABEL_FUNC_ID_GET_TEXT_LENGTH, + LABEL_FUNC_ID_GET_LONG_MODE, + LABEL_FUNC_ID_GET_ALIGN, + LABEL_FUNC_ID_GET_RECOLOR, + LABEL_FUNC_ID_GET_BODY_DRAW, + LABEL_FUNC_ID_GET_ANIM_SPEED, + LABEL_FUNC_ID_GET_LETTER_POS, + LABEL_FUNC_ID_GET_TEXT_SEL_START, + LABEL_FUNC_ID_GET_TEXT_SEL_END, + LABEL_FUNC_ID_INS_TEXT, + LABEL_FUNC_ID_CUT_TEXT, + /* Number of functions */ + _LABEL_FUNC_ID_NUM, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* WAMR_GRAPHIC_LIBRARY_SHARED_UTILS_H */ diff --git a/wamr/core/app-framework/app-native-shared/native_interface.cmake b/wamr/core/app-framework/app-native-shared/native_interface.cmake new file mode 100644 index 0000000..48ebe0a --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/native_interface.cmake @@ -0,0 +1,15 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (NATIVE_INTERFACE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${NATIVE_INTERFACE_DIR}) + + +file (GLOB_RECURSE source_all ${NATIVE_INTERFACE_DIR}/*.c) + +set (NATIVE_INTERFACE_SOURCE ${source_all}) + +set (WASM_APP_BI_INC_DIR "${NATIVE_INTERFACE_DIR}/bi-inc") +LIST (APPEND RUNTIME_LIB_HEADER_LIST "${NATIVE_INTERFACE_DIR}/native_interface.h") + diff --git a/wamr/core/app-framework/app-native-shared/native_interface.h b/wamr/core/app-framework/app-native-shared/native_interface.h new file mode 100644 index 0000000..76af444 --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/native_interface.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _NATIVE_INTERFACE_H_ +#define _NATIVE_INTERFACE_H_ + +/* Note: the bh_plaform.h is the only head file separately + implemented by both [app] and [native] worlds */ +#include "bh_platform.h" + + +#endif /* end of _NATIVE_INTERFACE_H */ + diff --git a/wamr/core/app-framework/app-native-shared/restful_utils.c b/wamr/core/app-framework/app-native-shared/restful_utils.c new file mode 100644 index 0000000..5268b22 --- /dev/null +++ b/wamr/core/app-framework/app-native-shared/restful_utils.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +#include "bi-inc/shared_utils.h" + +/* Serialization of request and response message + * + * Choices: + * We considered a few options: + * 1. coap + * 2. flatbuffer + * 3. cbor + * 4. attr-containers of our own + * 5. customized serialization for request/response + * + * Now we choose the #5 mainly because we need to quickly get the URL for dispatching + * and sometimes we want to change the URL in the original packet. the request format: + * fixed part: version: (1 byte), code (1 byte), fmt(2 byte), mid (4 bytes), sender_id(4 bytes), url_len(2 bytes), payload_len(4bytes) + * dynamic part: url (bytes in url_len), payload + * + * response format: + * fixed part: (1 byte), code (1 byte), fmt(2 byte), mid (4 bytes), sender_id(4 bytes), payload_len(4bytes) + * dynamic part: payload + */ +#define REQUES_PACKET_VER 1 +#define REQUEST_PACKET_FIX_PART_LEN 18 +#define REQUEST_PACKET_URL_OFFSET REQUEST_PACKET_FIX_PART_LEN +#define REQUEST_PACKET_URL_LEN *((uint16*)( (char*) buffer + 12))) //!!! to ensure little endian +#define REQUEST_PACKET_PAYLOAD_LEN *((uint32*)( (char*) buffer + 14))) //!!! to ensure little endian +#define REQUEST_PACKET_URL(buffer) ((char*) buffer + REQUEST_PACKET_URL_OFFSET) +#define REQUEST_PACKET_PAYLOAD(buffer) ((char*) buffer + REQUEST_PACKET_URL_OFFSET + REQUEST_PACKET_URL_LEN(buffer)) + +#define RESPONSE_PACKET_FIX_PART_LEN 16 + +char * pack_request(request_t *request, int * size) +{ + int url_len = strlen(request->url) + 1; + int len = REQUEST_PACKET_FIX_PART_LEN + url_len + request->payload_len; + char * packet = (char*) WA_MALLOC(len); + if (packet == NULL) + return NULL; + + // TODO: ensure little endian for words and dwords + *packet = REQUES_PACKET_VER; + *((uint8*) (packet + 1)) = request->action; + *((uint16*) (packet + 2)) = htons(request->fmt); + *((uint32*) (packet + 4)) = htonl(request->mid); + *((uint32*) (packet + 8)) = htonl(request->sender); + *((uint16*) (packet + 12)) = htons(url_len); + *((uint32*) (packet + 14)) = htonl(request->payload_len); + strcpy(packet + REQUEST_PACKET_URL_OFFSET, request->url); + memcpy(packet + REQUEST_PACKET_URL_OFFSET + url_len, request->payload, + request->payload_len); + + *size = len; + return packet; +} + +void free_req_resp_packet(char * packet) +{ + WA_FREE(packet); +} + +request_t * unpack_request(char * packet, int size, request_t * request) +{ + if (*packet != REQUES_PACKET_VER) { + return NULL; + } + if (size < REQUEST_PACKET_FIX_PART_LEN) { + return NULL; + } + uint16 url_len = ntohs(*((uint16*) (packet + 12))); + uint32 payload_len = ntohl(*((uint32*) (packet + 14))); + + if (size != ( REQUEST_PACKET_FIX_PART_LEN + url_len + payload_len)) { + return NULL; + } + if (*(packet + REQUEST_PACKET_FIX_PART_LEN + url_len - 1) != 0) { + return NULL; + } + + request->action = *((uint8*) (packet + 1)); + request->fmt = ntohs(*((uint16*) (packet + 2))); + request->mid = ntohl(*((uint32*) (packet + 4))); + request->sender = ntohl(*((uint32*) (packet + 8))); + request->payload_len = payload_len; + request->url = REQUEST_PACKET_URL(packet); + if (payload_len > 0) + request->payload = packet + REQUEST_PACKET_URL_OFFSET + url_len; + else + request->payload = NULL; + + return request; +} + +char * pack_response(response_t *response, int * size) +{ + int len = RESPONSE_PACKET_FIX_PART_LEN + response->payload_len; + char * packet = (char*) WA_MALLOC(len); + if (packet == NULL) + return NULL; + + // TODO: ensure little endian for words and dwords + *packet = REQUES_PACKET_VER; + *((uint8*) (packet + 1)) = response->status; + *((uint16*) (packet + 2)) = htons(response->fmt); + *((uint32*) (packet + 4)) = htonl(response->mid); + *((uint32*) (packet + 8)) = htonl(response->reciever); + *((uint32*) (packet + 12)) = htonl(response->payload_len); + memcpy(packet + RESPONSE_PACKET_FIX_PART_LEN, response->payload, + response->payload_len); + + *size = len; + return packet; +} + +response_t * unpack_response(char * packet, int size, response_t * response) +{ + if (*packet != REQUES_PACKET_VER) + return NULL; + if (size < RESPONSE_PACKET_FIX_PART_LEN) + return NULL; + uint32 payload_len = ntohl(*((uint32*) (packet + 12))); + if (size != ( RESPONSE_PACKET_FIX_PART_LEN + payload_len)) + return NULL; + + response->status = *((uint8*) (packet + 1)); + response->fmt = ntohs(*((uint16*) (packet + 2))); + response->mid = ntohl(*((uint32*) (packet + 4))); + response->reciever = ntohl(*((uint32*) (packet + 8))); + response->payload_len = payload_len; + if (payload_len > 0) + response->payload = packet + RESPONSE_PACKET_FIX_PART_LEN; + else + response->payload = NULL; + + return response; +} + +request_t *clone_request(request_t *request) +{ + /* deep clone */ + request_t *req = (request_t *) WA_MALLOC(sizeof(request_t)); + if (req == NULL) + return NULL; + + memset(req, 0, sizeof(*req)); + req->action = request->action; + req->fmt = request->fmt; + req->url = wa_strdup(request->url); + req->sender = request->sender; + req->mid = request->mid; + + if (req->url == NULL) + goto fail; + + req->payload_len = request->payload_len; + + if (request->payload_len) { + req->payload = (char *) WA_MALLOC(request->payload_len); + if (!req->payload) + goto fail; + memcpy(req->payload, request->payload, request->payload_len); + } else { + // when payload_len is 0, the payload may be used for carrying some handle or integer + req->payload = request->payload; + } + + return req; + +fail: + request_cleaner(req); + return NULL; +} + +void request_cleaner(request_t *request) +{ + if (request->url != NULL) + WA_FREE(request->url); + if (request->payload != NULL && request->payload_len > 0) + WA_FREE(request->payload); + + WA_FREE(request); +} + +void response_cleaner(response_t * response) +{ + if (response->payload != NULL && response->payload_len > 0) + WA_FREE(response->payload); + + WA_FREE(response); +} + +response_t * clone_response(response_t * response) +{ + response_t *clone = (response_t *) WA_MALLOC(sizeof(response_t)); + if (clone == NULL) + return NULL; + + memset(clone, 0, sizeof(*clone)); + clone->fmt = response->fmt; + clone->mid = response->mid; + clone->status = response->status; + clone->reciever = response->reciever; + clone->payload_len = response->payload_len; + if (clone->payload_len) { + clone->payload = (char *) WA_MALLOC(response->payload_len); + if (!clone->payload) + goto fail; + memcpy(clone->payload, response->payload, response->payload_len); + } else { + // when payload_len is 0, the payload may be used for carrying some handle or integer + clone->payload = response->payload; + } + return clone; + +fail: + response_cleaner(clone); + return NULL; +} + +response_t * set_response(response_t * response, int status, int fmt, + const char *payload, int payload_len) +{ + response->payload = (void *)payload; + response->payload_len = payload_len; + response->status = status; + response->fmt = fmt; + return response; +} + +response_t * make_response_for_request(request_t * request, + response_t * response) +{ + response->mid = request->mid; + response->reciever = request->sender; + + return response; +} + +request_t * init_request(request_t * request, char *url, int action, int fmt, + void *payload, int payload_len) +{ + static unsigned int mid = 0; + request->url = url; + request->action = action; + request->fmt = fmt; + request->payload = payload; + request->payload_len = payload_len; + request->mid = ++mid; + + return request; +} + +/* + check if the "url" is starting with "leading_str" + return: 0 - not match; >0 - the offset of matched url, include any "/" at the end + notes: + 1. it ensures the leading_str "/abc" can pass "/abc/cde" and "/abc/, but fail "/ab" and "/abcd". + leading_str "/abc/" can pass "/abc" + 2. it omit the '/' at the first char + 3. it ensure the leading_str "/abc" can pass "/abc?cde + */ + +int check_url_start(const char* url, int url_len, const char * leading_str) +{ + int offset = 0; + if (*leading_str == '/') + leading_str++; + if (url_len > 0 && *url == '/') { + url_len--; + url++; + offset++; + } + + int len = strlen(leading_str); + if (len == 0) + return 0; + + // ensure leading_str not end with "/" + if (leading_str[len - 1] == '/') { + len--; + if (len == 0) + return 0; + } + + // equal length + if (url_len == len) { + if (memcmp(url, leading_str, url_len) == 0) { + return (offset + len); + } else { + return 0; + } + } + + if (url_len < len) + return 0; + + else if (memcmp(url, leading_str, len) != 0) + return 0; + + else if (url[len] != '/' && url[len] != '?') + return 0; + else + return (offset + len + 1); +} + +// * @pattern: +// * sample 1: /abcd, match /abcd only +// * sample 2: /abcd/ match match "/abcd" and "/abcd/*" +// * sample 3: /abcd*, match any url started with "/abcd" +// * sample 4: /abcd/*, exclude "/abcd" + +bool match_url(char * pattern, char * matched) +{ + if (*pattern == '/') + pattern++; + if (*matched == '/') + matched++; + + int matched_len = strlen(matched); + if (matched_len == 0) + return false; + + if (matched[matched_len - 1] == '/') { + matched_len--; + if (matched_len == 0) + return false; + } + + int len = strlen(pattern); + if (len == 0) + return false; + + if (pattern[len - 1] == '/') { + len--; + if (strncmp(pattern, matched, len) != 0) + return false; + + if (len == matched_len) + return true; + + if (matched_len > len && matched[len] == '/') + return true; + + return false; + + } else if (pattern[len - 1] == '*') { + if (pattern[len - 2] == '/') { + if (strncmp(pattern, matched, len - 1) == 0) + return true; + + else + return false; + } else { + return (strncmp(pattern, matched, len - 1) == 0); + } + } else { + return (strcmp(pattern, matched) == 0); + } +} + +/* + * get the value of the key from following format buffer: + * key1=value1;key2=value2;key3=value3 + */ +char * find_key_value(char * buffer, int buffer_len, char * key, char * value, + int value_len, char delimiter) +{ + char * p = buffer; + int remaining = buffer_len; + int key_len = strlen(key); + + while (*p != 0 && remaining > 0) { + while (*p == ' ' || *p == delimiter) { + p++; + remaining--; + } + + if (remaining <= key_len) + return NULL; + + // find the key + if (0 == strncmp(p, key, key_len) && p[key_len] == '=') { + p += (key_len + 1); + remaining -= (key_len + 1); + char * v = value; + memset(value, 0, value_len); + value_len--; // ensure last char is 0 + while (*p != delimiter && remaining > 0 && value_len > 0) { + *v++ = *p++; + remaining--; + value_len--; + } + return value; + } + + // goto next key + while (*p != delimiter && remaining > 0) { + p++; + remaining--; + } + } + + return NULL; +} diff --git a/wamr/core/app-framework/app_ext_lib_export.c b/wamr/core/app-framework/app_ext_lib_export.c new file mode 100644 index 0000000..6e66fdf --- /dev/null +++ b/wamr/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/wamr/core/app-framework/app_framework.cmake b/wamr/core/app-framework/app_framework.cmake new file mode 100644 index 0000000..8ad43de --- /dev/null +++ b/wamr/core/app-framework/app_framework.cmake @@ -0,0 +1,89 @@ +# 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}) + +MACRO(SUBDIRLIST result curdir) + FILE(GLOB children RELATIVE ${curdir} ${curdir}/*) + SET(dirlist "") + FOREACH(child ${children}) + IF(IS_DIRECTORY ${curdir}/${child}) + LIST(APPEND dirlist ${child}) + ENDIF() + ENDFOREACH() + SET(${result} ${dirlist}) +ENDMACRO() + +function (add_module_native arg) + message ("Add native module ${ARGV0}") + include (${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/wasm_lib.cmake) + + file (GLOB header + ${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/*.h + ${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/native/*.inl + ) + LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) + set (RUNTIME_LIB_HEADER_LIST ${RUNTIME_LIB_HEADER_LIST} PARENT_SCOPE) + + 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) +endfunction () + +function (add_module_app arg) + message ("Add app module ${ARGV0}") + include (${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/app/wasm_app.cmake) + + LIST (APPEND WASM_APP_WA_INC_DIR_LIST "${APP_FRAMEWORK_ROOT_DIR}/${ARGV0}/app/wa-inc") + set (WASM_APP_WA_INC_DIR_LIST ${WASM_APP_WA_INC_DIR_LIST} PARENT_SCOPE) + + LIST (APPEND WASM_APP_NAME ${ARGV0}) + set (WASM_APP_NAME ${WASM_APP_NAME} PARENT_SCOPE) + + LIST (APPEND WASM_APP_SOURCE_ALL ${WASM_APP_CURRENT_SOURCE}) + set (WASM_APP_SOURCE_ALL ${WASM_APP_SOURCE_ALL} PARENT_SCOPE) +endfunction () + +if ("${WAMR_BUILD_APP_LIST}" STREQUAL "WAMR_APP_BUILD_ALL") + # add all modules under this folder + SUBDIRLIST(SUBDIRS ${APP_FRAMEWORK_ROOT_DIR}) + + FOREACH(subdir ${SUBDIRS}) + if ("${subdir}" STREQUAL "app-native-shared") + continue() + endif () + if ("${subdir}" STREQUAL "template") + continue() + endif () + + if ( NOT DEFINED APP_FRAMEWORK_INCLUDE_TYPE ) + add_module_native (${subdir}) + else () + add_module_app (${subdir}) + endif () + ENDFOREACH() + +else () + # add each module in the list + FOREACH (dir IN LISTS WAMR_BUILD_APP_LIST) + string(REPLACE "WAMR_APP_BUILD_" "" dir ${dir}) + string(TOLOWER ${dir} dir) + + if ( NOT DEFINED APP_FRAMEWORK_INCLUDE_TYPE ) + add_module_native (${dir}) + else () + add_module_app (${dir}) + endif () + ENDFOREACH (dir) + +endif() diff --git a/wamr/core/app-framework/base/app/bh_platform.c b/wamr/core/app-framework/base/app/bh_platform.c new file mode 100644 index 0000000..0dbf047 --- /dev/null +++ b/wamr/core/app-framework/base/app/bh_platform.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include +#include +#include + +/* + * + * + */ + +static bool is_little_endian() +{ + long i = 0x01020304; + unsigned char* c = (unsigned char*) &i; + return (*c == 0x04) ? true : false; +} + +static void swap32(uint8* pData) +{ + uint8 value = *pData; + *pData = *(pData + 3); + *(pData + 3) = value; + + value = *(pData + 1); + *(pData + 1) = *(pData + 2); + *(pData + 2) = value; +} + +static void swap16(uint8* pData) +{ + uint8 value = *pData; + *(pData) = *(pData + 1); + *(pData + 1) = value; +} + +uint32 htonl(uint32 value) +{ + uint32 ret; + if (is_little_endian()) { + ret = value; + swap32((uint8*) &ret); + return ret; + } + + return value; +} + +uint32 ntohl(uint32 value) +{ + return htonl(value); +} + +uint16 htons(uint16 value) +{ + uint16 ret; + if (is_little_endian()) { + ret = value; + swap16((uint8 *)&ret); + return ret; + } + + return value; +} + +uint16 ntohs(uint16 value) +{ + return htons(value); +} + +char *wa_strdup(const char *s) +{ + char *s1 = NULL; + if (s && (s1 = WA_MALLOC(strlen(s) + 1))) + memcpy(s1, s, strlen(s) + 1); + return s1; +} diff --git a/wamr/core/app-framework/base/app/bh_platform.h b/wamr/core/app-framework/base/app/bh_platform.h new file mode 100755 index 0000000..2539ec5 --- /dev/null +++ b/wamr/core/app-framework/base/app/bh_platform.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ +#define DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ + +#include + +typedef unsigned char uint8; +typedef char int8; +typedef unsigned short uint16; +typedef short int16; +typedef unsigned int uint32; +typedef int int32; + +#ifndef NULL +# define NULL ((void*) 0) +#endif + +#ifndef __cplusplus +#define true 1 +#define false 0 +#define inline __inline +#endif + +// all wasm-app<->native shared source files should use WA_MALLOC/WA_FREE. +// they will be mapped to different implementations in each side +#ifndef WA_MALLOC +#define WA_MALLOC malloc +#endif + +#ifndef WA_FREE +#define WA_FREE free +#endif + + +uint32 htonl(uint32 value); +uint32 ntohl(uint32 value); +uint16 htons(uint16 value); +uint16 ntohs(uint16 value); + + +// We are not worried for the WASM world since the sandbox will catch it. +#define bh_memcpy_s(dst, dst_len, src, src_len) memcpy(dst, src, src_len) + +#endif /* DEPS_IWASM_APP_LIBS_BASE_BH_PLATFORM_H_ */ diff --git a/wamr/core/app-framework/base/app/req_resp_api.h b/wamr/core/app-framework/base/app/req_resp_api.h new file mode 100644 index 0000000..cbddab5 --- /dev/null +++ b/wamr/core/app-framework/base/app/req_resp_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _REQ_RESP_API_H_ +#define _REQ_RESP_API_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +wasm_response_send(const char *buf, int size); + +void +wasm_register_resource(const char *url); + +void +wasm_post_request(const char *buf, int size); + +void +wasm_sub_event(const char *url); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _REQ_RESP_API_H_ */ + diff --git a/wamr/core/app-framework/base/app/request.c b/wamr/core/app-framework/base/app/request.c new file mode 100644 index 0000000..13fa22a --- /dev/null +++ b/wamr/core/app-framework/base/app/request.c @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bi-inc/attr_container.h" +#include "wa-inc/request.h" +#include "wa-inc/timer_wasm_app.h" +#include "bi-inc/shared_utils.h" +#include "wasm_app.h" +#include "req_resp_api.h" +#include "timer_api.h" + +#define TRANSACTION_TIMEOUT_MS 5000 + +typedef enum { + Reg_Event, Reg_Request +} reg_type_t; + +typedef struct _res_register { + struct _res_register *next; + const char * url; + reg_type_t reg_type; + void (*request_handler)(request_t *); +} res_register_t; + +typedef struct transaction { + struct transaction *next; + int mid; + unsigned int time; /* start time */ + response_handler_f handler; + void *user_data; +} transaction_t; + +static res_register_t * g_resources = NULL; + +static transaction_t *g_transactions = NULL; + +static user_timer_t g_trans_timer = NULL; + +static transaction_t *transaction_find(int mid) +{ + transaction_t *t = g_transactions; + + while (t) { + if (t->mid == mid) + return t; + t = t->next; + } + + return NULL; +} + +/* + * new transaction is added to the tail of the list, so the list + * is sorted by expiry time naturally. + */ +static void transaction_add(transaction_t *trans) +{ + transaction_t *t; + + if (g_transactions == NULL) { + g_transactions = trans; + return; + } + + t = g_transactions; + while (t) { + if (t->next == NULL) { + t->next = trans; + return; + } + } +} + +static void transaction_remove(transaction_t *trans) +{ + transaction_t *prev = NULL, *current = g_transactions; + + while (current) { + if (current == trans) { + if (prev == NULL) { + g_transactions = current->next; + free(current); + return; + } + prev->next = current->next; + free(current); + return; + } + prev = current; + current = current->next; + } +} + +static bool is_event_type(request_t * req) +{ + return req->action == COAP_EVENT; +} + +static bool register_url_handler(const char *url, + request_handler_f request_handler, reg_type_t reg_type) +{ + res_register_t * r = g_resources; + + while (r) { + if (reg_type == r->reg_type && strcmp(r->url, url) == 0) { + r->request_handler = request_handler; + return true; + } + r = r->next; + } + + r = (res_register_t *) malloc(sizeof(res_register_t)); + if (r == NULL) + return false; + + memset(r, 0, sizeof(*r)); + + r->url = strdup(url); + if (!r->url) { + free(r); + return false; + } + + r->request_handler = request_handler; + r->reg_type = reg_type; + r->next = g_resources; + g_resources = r; + + // tell app mgr to route this url to me + if (reg_type == Reg_Request) + wasm_register_resource(url); + else + wasm_sub_event(url); + + return true; +} + +bool api_register_resource_handler(const char *url, + request_handler_f request_handler) +{ + return register_url_handler(url, request_handler, Reg_Request); +} + +static void transaction_timeout_handler(user_timer_t timer) +{ + transaction_t *cur, *expired = NULL; + unsigned int elpased_ms, now = wasm_get_sys_tick_ms(); + + /* + * Since he transaction list is sorted by expiry time naturally, + * we can easily get all expired transactions. + * */ + cur = g_transactions; + while (cur) { + if (now < cur->time) + elpased_ms = now + (0xFFFFFFFF - cur->time) + 1; + else + elpased_ms = now - cur->time; + + if (elpased_ms >= TRANSACTION_TIMEOUT_MS) { + g_transactions = cur->next; + cur->next = expired; + expired = cur; + cur = g_transactions; + } else { + break; + } + } + + /* call each transaction's handler with response set to NULL */ + cur = expired; + while (cur) { + transaction_t *tmp = cur; + cur->handler(NULL, cur->user_data); + cur = cur->next; + free(tmp); + } + + /* + * If the transaction list is not empty, restart the timer according + * to the first transaction. Otherwise, stop the timer. + */ + if (g_transactions != NULL) { + unsigned int elpased_ms, ms_to_expiry, now = wasm_get_sys_tick_ms(); + if (now < g_transactions->time) { + elpased_ms = now + (0xFFFFFFFF - g_transactions->time) + 1; + } else { + elpased_ms = now - g_transactions->time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + api_timer_restart(g_trans_timer, ms_to_expiry); + } else { + api_timer_cancel(g_trans_timer); + g_trans_timer = NULL; + } +} + +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; + + 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); + } + } + + wasm_post_request(buffer, size); + + free_req_resp_packet(buffer); +} + +/* + * + * APIs for the native layers to callback for request/response arrived to this app + * + */ + +void on_response(char * buffer, int size) +{ + response_t response[1]; + transaction_t *trans; + + if (NULL == unpack_response(buffer, size, response)) { + printf("unpack response failed\n"); + return; + } + + if ((trans = transaction_find(response->mid)) == NULL) { + printf("cannot find the transaction\n"); + return; + } + + /* + * When the 1st transaction get response: + * 1. If the 2nd trans exist, restart the timer according to its expiry time; + * 2. Otherwise, stop the timer since there is no more transactions; + */ + if (trans == g_transactions) { + if (trans->next != NULL) { + unsigned int elpased_ms, ms_to_expiry, now = wasm_get_sys_tick_ms(); + if (now < trans->next->time) { + elpased_ms = now + (0xFFFFFFFF - trans->next->time) + 1; + } else { + elpased_ms = now - trans->next->time; + } + ms_to_expiry = TRANSACTION_TIMEOUT_MS - elpased_ms; + api_timer_restart(g_trans_timer, ms_to_expiry); + } else { + api_timer_cancel(g_trans_timer); + g_trans_timer = NULL; + } + } + + trans->handler(response, trans->user_data); + transaction_remove(trans); +} + +void on_request(char *buffer, int size) +{ + request_t request[1]; + bool is_event; + res_register_t *r = g_resources; + + if (NULL == unpack_request(buffer, size, request)) { + printf("unpack request failed\n"); + return; + } + + is_event = is_event_type(request); + + while (r) { + if ((is_event && r->reg_type == Reg_Event) + || (!is_event && r->reg_type == Reg_Request)) { + if (check_url_start(request->url, strlen(request->url), r->url) + > 0) { + r->request_handler(request); + return; + } + } + + r = r->next; + } + + printf("on_request: exit. no service handler\n"); +} + +void api_response_send(response_t *response) +{ + int size; + char * buffer = pack_response(response, &size); + if (buffer == NULL) + return; + + wasm_response_send(buffer, size); + free_req_resp_packet(buffer); +} + +/// event api + +bool api_publish_event(const char *url, int fmt, void *payload, int payload_len) +{ + int size; + request_t request[1]; + init_request(request, (char *)url, COAP_EVENT, fmt, payload, payload_len); + char * buffer = pack_request(request, &size); + if (buffer == NULL) + return false; + wasm_post_request(buffer, size); + + free_req_resp_packet(buffer); + + return true; +} + +bool api_subscribe_event(const char * url, request_handler_f handler) +{ + return register_url_handler(url, handler, Reg_Event); +} + diff --git a/wamr/core/app-framework/base/app/timer.c b/wamr/core/app-framework/base/app/timer.c new file mode 100644 index 0000000..41bd59f --- /dev/null +++ b/wamr/core/app-framework/base/app/timer.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "wa-inc/timer_wasm_app.h" +#include "timer_api.h" + +#if 1 +#include +#else +#define printf (...) +#endif + +struct user_timer { + struct user_timer * next; + int timer_id; + void (*user_timer_callback)(user_timer_t); +}; + +struct user_timer * g_timers = NULL; + +user_timer_t api_timer_create(int interval, bool is_period, bool auto_start, + on_user_timer_update_f on_timer_update) +{ + + int timer_id = wasm_create_timer(interval, is_period, auto_start); + + //TODO + struct user_timer * timer = (struct user_timer *) malloc( + sizeof(struct user_timer)); + if (timer == NULL) { + // TODO: remove the timer_id + printf("### api_timer_create malloc faild!!! \n"); + return NULL; + } + + memset(timer, 0, sizeof(*timer)); + timer->timer_id = timer_id; + timer->user_timer_callback = on_timer_update; + + if (g_timers == NULL) + g_timers = timer; + else { + timer->next = g_timers; + g_timers = timer; + } + + return timer; +} + +void api_timer_cancel(user_timer_t timer) +{ + user_timer_t t = g_timers, prev = NULL; + + wasm_timer_cancel(timer->timer_id); + + while (t) { + if (t == timer) { + if (prev == NULL) { + g_timers = t->next; + free(t); + } else { + prev->next = t->next; + free(t); + } + return; + } else { + prev = t; + t = t->next; + } + } +} + +void api_timer_restart(user_timer_t timer, int interval) +{ + wasm_timer_restart(timer->timer_id, interval); +} + +void on_timer_callback(int timer_id) +{ + struct user_timer * t = g_timers; + + while (t) { + if (t->timer_id == timer_id) { + t->user_timer_callback(t); + break; + } + t = t->next; + } +} + diff --git a/wamr/core/app-framework/base/app/timer_api.h b/wamr/core/app-framework/base/app/timer_api.h new file mode 100644 index 0000000..0881bab --- /dev/null +++ b/wamr/core/app-framework/base/app/timer_api.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _TIMER_API_H_ +#define _TIMER_API_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int timer_id_t; + +timer_id_t +wasm_create_timer(int interval, bool is_period, bool auto_start); + +void +wasm_timer_destroy(timer_id_t timer_id); + +void +wasm_timer_cancel(timer_id_t timer_id); + +void +wasm_timer_restart(timer_id_t timer_id, int interval); + +uint32 +wasm_get_sys_tick_ms(void); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _TIMER_API_H_ */ + diff --git a/wamr/core/app-framework/base/app/wa-inc/request.h b/wamr/core/app-framework/base/app/wa-inc/request.h new file mode 100644 index 0000000..c209fae --- /dev/null +++ b/wamr/core/app-framework/base/app/wa-inc/request.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AEE_REQUEST_H_ +#define _AEE_REQUEST_H_ + +#include "bi-inc/shared_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* CoAP request method codes */ +typedef enum { + COAP_GET = 1, + COAP_POST, + COAP_PUT, + COAP_DELETE, + COAP_EVENT = (COAP_DELETE + 2) +} coap_method_t; + +/* CoAP response codes */ +typedef enum { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, PING_RESPONSE +} coap_status_t; + + +/** + * @typedef request_handler_f + * + * @brief Define the signature of callback function for API + * api_register_resource_handler() to handle request or for API + * api_subscribe_event() to handle event. + * + * @param request pointer of the request to be handled + * + * @see api_register_resource_handler + * @see api_subscribe_event + */ +typedef void (*request_handler_f)(request_t *request); + +/** + * @typedef response_handler_f + * + * @brief Define the signature of callback function for API + * api_send_request() to handle response of a request. + * + * @param response pointer of the response to be handled + * @param user_data user data associated with the request which is set when + * calling api_send_request(). + * + * @see api_send_request + */ +typedef void (*response_handler_f)(response_t *response, void *user_data); + + +/* + ***************** + * Request APIs + ***************** + */ + +/** + * @brief Register resource. + * + * @param url url of the resource + * @param handler callback function to handle the request to the resource + * + * @return true if success, false otherwise + */ +bool api_register_resource_handler(const char *url, request_handler_f handler); + +/** + * @brief Send request asynchronously. + * + * @param request pointer of the request to be sent + * @param response_handler callback function to handle the response + * @param user_data user data + */ +void api_send_request(request_t * request, response_handler_f response_handler, + void * user_data); + +/** + * @brief Send response. + * + * @param response pointer of the response to be sent + * + * @par + * @code + * void res1_handler(request_t *request) + * { + * response_t response[1]; + * make_response_for_request(request, response); + * set_response(response, DELETED_2_02, 0, NULL, 0); + * api_response_send(response); + * } + * @endcode + */ +void api_response_send(response_t *response); + + +/* + ***************** + * Event APIs + ***************** + */ + +/** + * @brief Publish an event. + * + * @param url url of the event + * @param fmt format of the event payload + * @param payload payload of the event + * @param payload_len length in bytes of the event payload + * + * @return true if success, false otherwise + */ +bool api_publish_event(const char *url, int fmt, void *payload, + int payload_len); + + +/** + * @brief Subscribe an event. + * + * @param url url of the event + * @param handler callback function to handle the event. + * + * @return true if success, false otherwise + */ +bool api_subscribe_event(const char * url, request_handler_f handler); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/app-framework/base/app/wa-inc/timer_wasm_app.h b/wamr/core/app-framework/base/app/wa-inc/timer_wasm_app.h new file mode 100644 index 0000000..17e59bc --- /dev/null +++ b/wamr/core/app-framework/base/app/wa-inc/timer_wasm_app.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AEE_TIMER_H_ +#define _AEE_TIMER_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* board producer define user_timer */ +struct user_timer; +typedef struct user_timer * user_timer_t; + +/** + * @typedef on_user_timer_update_f + * + * @brief Define the signature of callback function for API api_timer_create(). + * + * @param timer the timer + * + * @see api_timer_create + */ +typedef void (*on_user_timer_update_f)(user_timer_t timer); + +/* + ***************** + * Timer APIs + ***************** + */ + +/** + * @brief Create timer. + * + * @param interval timer interval + * @param is_period whether the timer is periodic + * @param auto_start whether start the timer immediately after created + * @param on_timer_update callback function called when timer expired + * + * @return the timer created if success, NULL otherwise + */ +user_timer_t api_timer_create(int interval, bool is_period, bool auto_start, + on_user_timer_update_f on_timer_update); + +/** + * @brief Cancel timer. + * + * @param timer the timer to cancel + */ +void api_timer_cancel(user_timer_t timer); + +/** + * @brief Restart timer. + * + * @param timer the timer to cancel + * @param interval the timer interval + */ +void api_timer_restart(user_timer_t timer, int interval); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/app-framework/base/app/wasm_app.cmake b/wamr/core/app-framework/base/app/wasm_app.cmake new file mode 100644 index 0000000..2313df9 --- /dev/null +++ b/wamr/core/app-framework/base/app/wasm_app.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_APP_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${WASM_APP_BASE_DIR}) + +add_definitions (-DWASM_ENABLE_BASE_LIB) + +file (GLOB_RECURSE source_all ${WASM_APP_BASE_DIR}/*.c) + +set (WASM_APP_CURRENT_SOURCE ${source_all}) +set (WASM_APP_BASE_DIR ${WASM_APP_BASE_DIR} PARENT_SCOPE) diff --git a/wamr/core/app-framework/base/app/wasm_app.h b/wamr/core/app-framework/base/app/wasm_app.h new file mode 100644 index 0000000..a22e77e --- /dev/null +++ b/wamr/core/app-framework/base/app/wasm_app.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _LIB_AEE_H_ +#define _LIB_AEE_H_ + +#include "bi-inc/shared_utils.h" +#include "bi-inc/attr_container.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* end of _LIB_AEE_H_ */ diff --git a/wamr/core/app-framework/base/native/base_lib.inl b/wamr/core/app-framework/base/native/base_lib.inl new file mode 100644 index 0000000..3c228cc --- /dev/null +++ b/wamr/core/app-framework/base/native/base_lib.inl @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + 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/wamr/core/app-framework/base/native/base_lib_export.c b/wamr/core/app-framework/base/native/base_lib_export.c new file mode 100644 index 0000000..0b716fc --- /dev/null +++ b/wamr/core/app-framework/base/native/base_lib_export.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include "lib_export.h" +#include "req_resp_native_api.h" +#include "timer_native_api.h" + + +static NativeSymbol extended_native_symbol_defs[] = { + /* TODO: use macro EXPORT_WASM_API() or EXPORT_WASM_API2() to + add functions to register. */ + #include "base_lib.inl" +}; + +uint32 get_base_lib_export_apis(NativeSymbol **p_base_lib_apis) +{ + *p_base_lib_apis = extended_native_symbol_defs; + return sizeof(extended_native_symbol_defs) / sizeof(NativeSymbol); +} + diff --git a/wamr/core/app-framework/base/native/req_resp_native_api.h b/wamr/core/app-framework/base/native/req_resp_native_api.h new file mode 100644 index 0000000..6fe0581 --- /dev/null +++ b/wamr/core/app-framework/base/native/req_resp_native_api.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _REQ_RESP_API_H_ +#define _REQ_RESP_API_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +wasm_response_send(wasm_exec_env_t exec_env, char *buffer, int size); +void +wasm_register_resource(wasm_exec_env_t exec_env, char *url); +void +wasm_post_request(wasm_exec_env_t exec_env, char *buffer, int size); +void +wasm_sub_event(wasm_exec_env_t exec_env, char *url); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _REQ_RESP_API_H_ */ + diff --git a/wamr/core/app-framework/base/native/request_response.c b/wamr/core/app-framework/base/native/request_response.c new file mode 100644 index 0000000..4088c38 --- /dev/null +++ b/wamr/core/app-framework/base/native/request_response.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager_export.h" +#include "coap_ext.h" +#include "wasm_export.h" +#include "bh_assert.h" + +extern void module_request_handler(request_t *request, void *user_data); + +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; +} + +void +wasm_register_resource(wasm_exec_env_t exec_env, char *url) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (url != NULL) { + unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + bh_assert(mod_id != ID_NONE); + am_register_resource(url, module_request_handler, mod_id); + } +} + +void +wasm_post_request(wasm_exec_env_t exec_env, char *buffer, int size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (buffer != NULL) { + request_t req[1]; + + if (!unpack_request(buffer, size, req)) + return; + + // TODO: add permission check, ensure app can't do harm + + // set sender to help dispatch the response to the sender ap + 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); + } +} + +void +wasm_sub_event(wasm_exec_env_t exec_env, char *url) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (url != NULL) { + unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + + bh_assert(mod_id != ID_NONE); + am_register_event(url, mod_id); + } +} + diff --git a/wamr/core/app-framework/base/native/runtime_lib.h b/wamr/core/app-framework/base/native/runtime_lib.h new file mode 100644 index 0000000..8fb9dbb --- /dev/null +++ b/wamr/core/app-framework/base/native/runtime_lib.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef LIB_BASE_RUNTIME_LIB_H_ +#define LIB_BASE_RUNTIME_LIB_H_ + + +#include "runtime_timer.h" + +void init_wasm_timer(); +void exit_wasm_timer(); +timer_ctx_t get_wasm_timer_ctx(); +timer_ctx_t create_wasm_timer_ctx(unsigned int module_id, int prealloc_num); +void destroy_module_timer_ctx(unsigned int module_id); + +#endif /* LIB_BASE_RUNTIME_LIB_H_ */ diff --git a/wamr/core/app-framework/base/native/timer_native_api.h b/wamr/core/app-framework/base/native/timer_native_api.h new file mode 100644 index 0000000..4d1f587 --- /dev/null +++ b/wamr/core/app-framework/base/native/timer_native_api.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _TIMER_API_H_ +#define _TIMER_API_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int timer_id_t; + +/* + * timer interfaces + */ + +typedef unsigned int timer_id_t; + +timer_id_t +wasm_create_timer(wasm_exec_env_t exec_env, + int interval, bool is_period, bool auto_start); +void +wasm_timer_destroy(wasm_exec_env_t exec_env, timer_id_t timer_id); +void +wasm_timer_cancel(wasm_exec_env_t exec_env, timer_id_t timer_id); +void +wasm_timer_restart(wasm_exec_env_t exec_env, + timer_id_t timer_id, int interval); +uint32 +wasm_get_sys_tick_ms(wasm_exec_env_t exec_env); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _TIMER_API_H_ */ + diff --git a/wamr/core/app-framework/base/native/timer_wrapper.c b/wamr/core/app-framework/base/native/timer_wrapper.c new file mode 100644 index 0000000..fd3ef4b --- /dev/null +++ b/wamr/core/app-framework/base/native/timer_wrapper.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "app_manager_export.h" +#include "module_wasm_app.h" +#include "timer_native_api.h" + +static bool timer_thread_run = true; + +bh_list g_timer_ctx_list; +korp_cond g_timer_ctx_list_cond; +korp_mutex g_timer_ctx_list_mutex; +typedef struct { + bh_list_link l; + timer_ctx_t timer_ctx; +} timer_ctx_node_t; + +void wasm_timer_callback(timer_id_t id, unsigned int mod_id) +{ + module_data* module = module_data_list_lookup_id(mod_id); + if (module == NULL) + return; + + // !!! the length parameter must be 0, so the receiver will + // not free the payload pointer. + bh_post_msg(module->queue, TIMER_EVENT_WASM, (char *)(uintptr_t)id, 0); +} + +/// +/// why we create a separate link for module timer contexts +/// rather than traverse the module list? +/// It helps to reduce the lock frequency for the module list. +/// Also when we lock the module list and then call the callback for +/// timer expire, the callback is request the list lock again for lookup +/// the module from module id. It is for avoiding that situation. + +void * thread_modulers_timer_check(void * arg) +{ + int ms_to_expiry; + + while (timer_thread_run) { + ms_to_expiry = -1; + os_mutex_lock(&g_timer_ctx_list_mutex); + timer_ctx_node_t* elem = (timer_ctx_node_t*) + bh_list_first_elem(&g_timer_ctx_list); + while (elem) { + int next = check_app_timers(elem->timer_ctx); + if (next != -1) { + if (ms_to_expiry == -1 || ms_to_expiry > next) + ms_to_expiry = next; + } + + elem = (timer_ctx_node_t*) bh_list_elem_next(elem); + } + os_mutex_unlock(&g_timer_ctx_list_mutex); + + if (ms_to_expiry == -1) + ms_to_expiry = 60 * 1000; + os_mutex_lock(&g_timer_ctx_list_mutex); + os_cond_reltimedwait(&g_timer_ctx_list_cond, &g_timer_ctx_list_mutex, + ms_to_expiry * 1000); + os_mutex_unlock(&g_timer_ctx_list_mutex); + } + + return NULL; +} + +void wakeup_modules_timer_thread(timer_ctx_t ctx) +{ + os_mutex_lock(&g_timer_ctx_list_mutex); + os_cond_signal(&g_timer_ctx_list_cond); + os_mutex_unlock(&g_timer_ctx_list_mutex); +} + +void init_wasm_timer() +{ + korp_tid tm_tid; + bh_list_init(&g_timer_ctx_list); + + os_cond_init(&g_timer_ctx_list_cond); + /* temp solution for: thread_modulers_timer_check thread would recursive lock the mutex */ + os_recursive_mutex_init(&g_timer_ctx_list_mutex); + + os_thread_create(&tm_tid, thread_modulers_timer_check, + NULL, BH_APPLET_PRESERVED_STACK_SIZE); +} + +void exit_wasm_timer() +{ + timer_thread_run = false; +} + +timer_ctx_t create_wasm_timer_ctx(unsigned int module_id, int prealloc_num) +{ + timer_ctx_t ctx = create_timer_ctx(wasm_timer_callback, + wakeup_modules_timer_thread, + prealloc_num, + module_id); + + if (ctx == NULL) + return NULL; + + timer_ctx_node_t * node = (timer_ctx_node_t*) + wasm_runtime_malloc(sizeof(timer_ctx_node_t)); + if (node == NULL) { + destroy_timer_ctx(ctx); + return NULL; + } + memset(node, 0, sizeof(*node)); + node->timer_ctx = ctx; + + os_mutex_lock(&g_timer_ctx_list_mutex); + bh_list_insert(&g_timer_ctx_list, node); + os_mutex_unlock(&g_timer_ctx_list_mutex); + + return ctx; +} + +void destroy_module_timer_ctx(unsigned int module_id) +{ + os_mutex_lock(&g_timer_ctx_list_mutex); + timer_ctx_node_t* elem = (timer_ctx_node_t*) + bh_list_first_elem(&g_timer_ctx_list); + while (elem) { + if (timer_ctx_get_owner(elem->timer_ctx) == module_id) { + bh_list_remove(&g_timer_ctx_list, elem); + destroy_timer_ctx(elem->timer_ctx); + wasm_runtime_free(elem); + break; + } + + elem = (timer_ctx_node_t*) bh_list_elem_next(elem); + } + os_mutex_unlock(&g_timer_ctx_list_mutex); +} + +timer_ctx_t get_wasm_timer_ctx(wasm_module_inst_t module_inst) +{ + module_data * m = app_manager_get_module_data(Module_WASM_App, + module_inst); + if (m == NULL) + return NULL; + return m->timer_ctx; +} + +timer_id_t +wasm_create_timer(wasm_exec_env_t exec_env, + int interval, bool is_period, bool auto_start) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); + bh_assert(timer_ctx); + return sys_create_timer(timer_ctx, interval, is_period, auto_start); +} + +void +wasm_timer_destroy(wasm_exec_env_t exec_env, timer_id_t timer_id) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); + bh_assert(timer_ctx); + sys_timer_destroy(timer_ctx, timer_id); +} + +void +wasm_timer_cancel(wasm_exec_env_t exec_env, timer_id_t timer_id) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); + bh_assert(timer_ctx); + sys_timer_cancel(timer_ctx, timer_id); +} + +void +wasm_timer_restart(wasm_exec_env_t exec_env, + timer_id_t timer_id, int interval) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + timer_ctx_t timer_ctx = get_wasm_timer_ctx(module_inst); + bh_assert(timer_ctx); + sys_timer_restart(timer_ctx, timer_id, interval); +} + +extern uint32 get_sys_tick_ms(); + +uint32 +wasm_get_sys_tick_ms(wasm_exec_env_t exec_env) +{ + return (uint32)bh_get_tick_ms(); +} + diff --git a/wamr/core/app-framework/base/native/wasm_lib.cmake b/wamr/core/app-framework/base/native/wasm_lib.cmake new file mode 100644 index 0000000..223320b --- /dev/null +++ b/wamr/core/app-framework/base/native/wasm_lib.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_LIB_BASE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_BASE_LIB) + +include_directories(${WASM_LIB_BASE_DIR}) + +file (GLOB_RECURSE source_all ${WASM_LIB_BASE_DIR}/*.c) + +set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) + diff --git a/wamr/core/app-framework/connection/app/connection.c b/wamr/core/app-framework/connection/app/connection.c new file mode 100644 index 0000000..775ee16 --- /dev/null +++ b/wamr/core/app-framework/connection/app/connection.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wa-inc/connection.h" +#include "connection_api.h" + +/* Raw connection structure */ +typedef struct _connection { + /* Next connection */ + struct _connection *next; + + /* Handle of the connection */ + uint32 handle; + + /* Callback function called when event on this connection occurs */ + on_connection_event_f on_event; + + /* User data */ + void *user_data; +} connection_t; + +/* Raw connections list */ +static connection_t *g_conns = NULL; + +connection_t *api_open_connection(const char *name, + attr_container_t *args, + on_connection_event_f on_event, + void *user_data) +{ + connection_t *conn; + char *args_buffer = (char *)args; + uint32 handle, args_len = attr_container_get_serialize_length(args); + + handle = wasm_open_connection(name, args_buffer, args_len); + if (handle == -1) + return NULL; + + conn = (connection_t *)malloc(sizeof(*conn)); + if (conn == NULL) { + wasm_close_connection(handle); + return NULL; + } + + memset(conn, 0, sizeof(*conn)); + conn->handle = handle; + conn->on_event = on_event; + conn->user_data = user_data; + + if (g_conns != NULL) { + conn->next = g_conns; + g_conns = conn; + } else { + g_conns = conn; + } + + return conn; +} + +void api_close_connection(connection_t *c) +{ + connection_t *conn = g_conns, *prev = NULL; + + while (conn) { + if (conn == c) { + wasm_close_connection(c->handle); + if (prev != NULL) + prev->next = conn->next; + else + g_conns = conn->next; + free(conn); + return; + } else { + prev = conn; + conn = conn->next; + } + } +} + +int api_send_on_connection(connection_t *conn, const char *data, uint32 len) +{ + return wasm_send_on_connection(conn->handle, data, len); +} + +bool api_config_connection(connection_t *conn, attr_container_t *cfg) +{ + char *cfg_buffer = (char *)cfg; + uint32 cfg_len = attr_container_get_serialize_length(cfg); + + return wasm_config_connection(conn->handle, cfg_buffer, cfg_len); +} + +void on_connection_data(uint32 handle, char *buffer, uint32 len) +{ + connection_t *conn = g_conns; + + while (conn != NULL) { + if (conn->handle == handle) { + if (len == 0) { + conn->on_event(conn, + CONN_EVENT_TYPE_DISCONNECT, + NULL, + 0, + conn->user_data); + } else { + conn->on_event(conn, + CONN_EVENT_TYPE_DATA, + buffer, + len, + conn->user_data); + } + + return; + } + conn = conn->next; + } +} + diff --git a/wamr/core/app-framework/connection/app/connection_api.h b/wamr/core/app-framework/connection/app/connection_api.h new file mode 100644 index 0000000..5697c83 --- /dev/null +++ b/wamr/core/app-framework/connection/app/connection_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONNECTION_API_H_ +#define CONNECTION_API_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint32 +wasm_open_connection(const char *name, char *args_buf, uint32 args_buf_len); + +void +wasm_close_connection(uint32 handle); + +int +wasm_send_on_connection(uint32 handle, const char *data, uint32 data_len); + +bool +wasm_config_connection(uint32 handle, const char *cfg_buf, uint32 cfg_buf_len); + +#ifdef __cplusplus +} +#endif + + +#endif /* end of CONNECTION_API_H_ */ diff --git a/wamr/core/app-framework/connection/app/wa-inc/connection.h b/wamr/core/app-framework/connection/app/wa-inc/connection.h new file mode 100644 index 0000000..675befa --- /dev/null +++ b/wamr/core/app-framework/connection/app/wa-inc/connection.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _CONNECTION_H_ +#define _CONNECTION_H_ + +#include "bi-inc/attr_container.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct _connection; +typedef struct _connection connection_t; + +/* Connection event type */ +typedef enum { + /* Data is received */ + CONN_EVENT_TYPE_DATA = 1, + /* Connection is disconnected */ + CONN_EVENT_TYPE_DISCONNECT +} conn_event_type_t; + +/* + * @typedef on_connection_event_f + * + * @param conn the connection that the event belongs to + * @param type event type + * @param data the data received for CONN_EVENT_TYPE_DATA event + * @param len length of the data in byte + * @param user_data user data + */ +typedef void (*on_connection_event_f)(connection_t *conn, + conn_event_type_t type, + const char *data, + uint32 len, + void *user_data); + +/* + ***************** + * Connection API's + ***************** + */ + +/* + * @brief Open a connection. + * + * @param name name of the connection, "TCP", "UDP" or "UART" + * @param args connection arguments, such as: ip:127.0.0.1, port:8888 + * @param on_event callback function called when event occurs + * @param user_data user data + * + * @return the connection or NULL means fail + */ +connection_t *api_open_connection(const char *name, + attr_container_t *args, + on_connection_event_f on_event, + void *user_data); + +/* + * @brief Close a connection. + * + * @param conn connection + */ +void api_close_connection(connection_t *conn); + +/* + * Send data to the connection in non-blocking manner which returns immediately + * + * @param conn the connection + * @param data data buffer to be sent + * @param len length of the data in byte + * + * @return actual length sent, or -1 if fail(maybe underlying buffer is full) + */ +int api_send_on_connection(connection_t *conn, const char *data, uint32 len); + +/* + * @brief Configure connection. + * + * @param conn the connection + * @param cfg configurations + * + * @return true if success, false otherwise + */ +bool api_config_connection(connection_t *conn, attr_container_t *cfg); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/app-framework/connection/app/wasm_app.cmake b/wamr/core/app-framework/connection/app/wasm_app.cmake new file mode 100644 index 0000000..ca4e025 --- /dev/null +++ b/wamr/core/app-framework/connection/app/wasm_app.cmake @@ -0,0 +1,11 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_APP_CONN_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${WASM_APP_CONN_DIR}) + + +file (GLOB source_all ${WASM_APP_CONN_DIR}/*.c) + +set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/connection/native/connection.inl b/wamr/core/app-framework/connection/native/connection.inl new file mode 100644 index 0000000..b2d01aa --- /dev/null +++ b/wamr/core/app-framework/connection/native/connection.inl @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +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/wamr/core/app-framework/connection/native/connection_lib.h b/wamr/core/app-framework/connection/native/connection_lib.h new file mode 100644 index 0000000..4a69f55 --- /dev/null +++ b/wamr/core/app-framework/connection/native/connection_lib.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONNECTION_LIB_H_ +#define CONNECTION_LIB_H_ + +#include "bi-inc/attr_container.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ***************** + * This file defines connection library which should be implemented by different platforms + ***************** + */ + +/* + * @brief Open a connection. + * + * @param name name of the connection, "TCP", "UDP" or "UART" + * @param args connection arguments, such as: ip:127.0.0.1, port:8888 + * + * @return 0~0xFFFFFFFE means id of the connection, otherwise(-1) means fail + */ +typedef uint32 (*connection_open_f)(wasm_module_inst_t module_inst, + const char *name, attr_container_t *args); + +/* + * @brief Close a connection. + * + * @param handle of the connection + */ +typedef void (*connection_close_f)(uint32 handle); + +/* + * @brief Send data to the connection in non-blocking manner. + * + * @param handle of the connection + * @param data data buffer to be sent + * @param len length of the data in byte + * + * @return actual length sent, -1 if fail + */ +typedef int (*connection_send_f)(uint32 handle, const char *data, int len); + +/* + * @brief Configure connection. + * + * @param handle of the connection + * @param cfg configurations + * + * @return true if success, false otherwise + */ +typedef bool (*connection_config_f)(uint32 handle, attr_container_t *cfg); + +/* Raw connection interface for platform to implement */ +typedef struct _connection_interface { + connection_open_f _open; + connection_close_f _close; + connection_send_f _send; + connection_config_f _config; +} connection_interface_t; + +/* Platform must define this interface */ +extern connection_interface_t connection_impl; + +#ifdef __cplusplus +} +#endif + + +#endif /* CONNECTION_LIB_H_ */ diff --git a/wamr/core/app-framework/connection/native/connection_native_api.h b/wamr/core/app-framework/connection/native/connection_native_api.h new file mode 100644 index 0000000..79e911d --- /dev/null +++ b/wamr/core/app-framework/connection/native/connection_native_api.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONNECTION_API_H_ +#define CONNECTION_API_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * connection interfaces + */ + +uint32 +wasm_open_connection(wasm_exec_env_t exec_env, + 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, char *data, uint32 len); +bool +wasm_config_connection(wasm_exec_env_t exec_env, + uint32 handle, char *cfg_buf, uint32 len); + + + +#ifdef __cplusplus +} +#endif + + +#endif /* end of CONNECTION_API_H_ */ diff --git a/wamr/core/app-framework/connection/native/connection_wrapper.c b/wamr/core/app-framework/connection/native/connection_wrapper.c new file mode 100644 index 0000000..742a45b --- /dev/null +++ b/wamr/core/app-framework/connection/native/connection_wrapper.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "connection_lib.h" +#include "wasm_export.h" +#include "native_interface.h" +#include "connection_native_api.h" + + +/* Note: + * + * This file is the consumer of connection lib which is implemented by different platforms + */ + +uint32 +wasm_open_connection(wasm_exec_env_t exec_env, + char *name, char *args_buf, uint32 len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + attr_container_t *args; + + args = (attr_container_t *)args_buf; + + if (connection_impl._open != NULL) + return connection_impl._open(module_inst, name, args); + + return -1; +} + +void +wasm_close_connection(wasm_exec_env_t exec_env, uint32 handle) +{ + if (connection_impl._close != NULL) + connection_impl._close(handle); +} + +int +wasm_send_on_connection(wasm_exec_env_t exec_env, + uint32 handle, char *data, uint32 len) +{ + if (connection_impl._send != NULL) + return connection_impl._send(handle, data, len); + + return -1; +} + +bool +wasm_config_connection(wasm_exec_env_t exec_env, + uint32 handle, char *cfg_buf, uint32 len) +{ + attr_container_t *cfg; + + cfg = (attr_container_t *)cfg_buf; + + if (connection_impl._config != NULL) + return connection_impl._config(handle, cfg); + + return false; +} diff --git a/wamr/core/app-framework/connection/native/linux/conn_tcp.c b/wamr/core/app-framework/connection/native/linux/conn_tcp.c new file mode 100644 index 0000000..e676e53 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_tcp.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "conn_tcp.h" + +#include +#include +#include +#include +#include + +int tcp_open(char *address, uint16 port) +{ + int sock, ret; + struct sockaddr_in servaddr; + + memset(&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(address); + servaddr.sin_port = htons(port); + + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == -1) + return -1; + + ret = connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)); + if (ret == -1) { + close(sock); + return -1; + } + + /* Put the socket in non-blocking mode */ + if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0) { + close(sock); + return -1; + } + + return sock; +} + +int tcp_send(int sock, const char *data, int size) +{ + return send(sock, data, size, 0); +} + +int tcp_recv(int sock, char *buffer, int buf_size) +{ + return recv(sock, buffer, buf_size, 0); +} diff --git a/wamr/core/app-framework/connection/native/linux/conn_tcp.h b/wamr/core/app-framework/connection/native/linux/conn_tcp.h new file mode 100644 index 0000000..c1b560e --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_tcp.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONN_LINUX_TCP_H_ +#define CONN_LINUX_TCP_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int tcp_open(char *address, uint16 port); + +int tcp_send(int sock, const char *data, int size); + +int tcp_recv(int sock, char *buffer, int buf_size); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/wamr/core/app-framework/connection/native/linux/conn_uart.c b/wamr/core/app-framework/connection/native/linux/conn_uart.c new file mode 100644 index 0000000..1f5c4e7 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_uart.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "conn_uart.h" + +#include +#include +#include + +static int parse_baudrate(int baud) +{ + switch (baud) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} + +int uart_open(char* device, int baudrate) +{ + int uart_fd; + struct termios uart_term; + + uart_fd = open(device, O_RDWR | O_NOCTTY); + + if (uart_fd < 0) + return -1; + + memset(&uart_term, 0, sizeof(uart_term)); + uart_term.c_cflag = parse_baudrate(baudrate) | CS8 | CLOCAL | CREAD; + uart_term.c_iflag = IGNPAR; + uart_term.c_oflag = 0; + + /* set noncanonical mode */ + uart_term.c_lflag = 0; + uart_term.c_cc[VTIME] = 30; + uart_term.c_cc[VMIN] = 1; + tcflush(uart_fd, TCIFLUSH); + + if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { + close(uart_fd); + return -1; + } + + /* Put the fd in non-blocking mode */ + if (fcntl(uart_fd, F_SETFL, fcntl(uart_fd, F_GETFL) | O_NONBLOCK) < 0) { + close(uart_fd); + return -1; + } + + return uart_fd; +} + +int uart_send(int fd, const char *data, int size) +{ + return write(fd, data, size); +} + +int uart_recv(int fd, char *buffer, int buf_size) +{ + return read(fd, buffer, buf_size); +} diff --git a/wamr/core/app-framework/connection/native/linux/conn_uart.h b/wamr/core/app-framework/connection/native/linux/conn_uart.h new file mode 100644 index 0000000..9d40fb0 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_uart.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONN_LINUX_UART_H_ +#define CONN_LINUX_UART_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int uart_open(char* device, int baudrate); + +int uart_send(int fd, const char *data, int size); + +int uart_recv(int fd, char *buffer, int buf_size); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/wamr/core/app-framework/connection/native/linux/conn_udp.c b/wamr/core/app-framework/connection/native/linux/conn_udp.c new file mode 100644 index 0000000..9d37047 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_udp.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "conn_udp.h" + +#include +#include +#include +#include +#include + +int udp_open(uint16 port) +{ + int sock, ret; + struct sockaddr_in addr; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); + + ret = bind(sock, (struct sockaddr*)&addr, sizeof(addr)); + if (ret == -1) { + close(sock); + return -1; + } + + /* Put the socket in non-blocking mode */ + if (fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK) < 0) { + close(sock); + return -1; + } + + return sock; +} + +int udp_send(int sock, struct sockaddr *dest, const char *data, int size) +{ + return sendto(sock, data, size, MSG_CONFIRM, dest, sizeof(*dest)); +} + +int udp_recv(int sock, char *buffer, int buf_size) +{ + struct sockaddr_in remaddr; + socklen_t addrlen = sizeof(remaddr); + + return recvfrom(sock, + buffer, + buf_size, + 0, + (struct sockaddr *)&remaddr, + &addrlen); +} diff --git a/wamr/core/app-framework/connection/native/linux/conn_udp.h b/wamr/core/app-framework/connection/native/linux/conn_udp.h new file mode 100644 index 0000000..37b9db7 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/conn_udp.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef CONN_LINUX_UDP_H_ +#define CONN_LINUX_UDP_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int udp_open(uint16 port); + +int udp_send(int sock, struct sockaddr *dest, const char *data, int size); + +int udp_recv(int sock, char *buffer, int buf_size); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/wamr/core/app-framework/connection/native/linux/connection_mgr.c b/wamr/core/app-framework/connection/native/linux/connection_mgr.c new file mode 100644 index 0000000..df0fc8f --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/connection_mgr.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * Note: + * This file implements the linux version connection library which is + * defined in connection_lib.h. + * It also provides a reference implementation of connections manager. + */ + +#include "connection_lib.h" +#include "bh_platform.h" +#include "app_manager_export.h" +#include "module_wasm_app.h" +#include "conn_tcp.h" +#include "conn_udp.h" +#include "conn_uart.h" + +#include +#include +#include +#include +#include + +#define MAX_EVENTS 10 +#define IO_BUF_SIZE 256 + +static bool polling_thread_run = true; + +/* Connection type */ +typedef enum conn_type { + CONN_TYPE_TCP, + CONN_TYPE_UDP, + CONN_TYPE_UART, + CONN_TYPE_UNKNOWN +} conn_type_t; + +/* Sys connection */ +typedef struct sys_connection { + /* Next connection */ + struct sys_connection *next; + + /* Type */ + conn_type_t type; + + /* Handle to interact with wasm app */ + uint32 handle; + + /* Underlying connection ID, may be socket fd */ + int fd; + + /* Module id that the connection belongs to */ + uint32 module_id; + + /* Argument, such as dest addr for udp */ + void *arg; +} sys_connection_t; + +/* Epoll instance */ +static int epollfd; + +/* Connections list */ +static sys_connection_t *g_connections = NULL; + +/* Max handle */ +static uint32 g_handle_max = 0; + +/* Lock to protect g_connections and g_handle_max */ +static korp_mutex g_lock; + +/* Epoll events */ +static struct epoll_event epoll_events[MAX_EVENTS]; + +/* Buffer to receive data */ +static char io_buf[IO_BUF_SIZE]; + +static uint32 _conn_open(wasm_module_inst_t module_inst, + const char *name, attr_container_t *args); +static void _conn_close(uint32 handle); +static int _conn_send(uint32 handle, const char *data, int len); +static bool _conn_config(uint32 handle, attr_container_t *cfg); + +/* + * Platform implementation of connection library + */ +connection_interface_t connection_impl = { + ._open = _conn_open, + ._close = _conn_close, + ._send = _conn_send, + ._config = _conn_config +}; + +static void add_connection(sys_connection_t *conn) +{ + os_mutex_lock(&g_lock); + + g_handle_max++; + if (g_handle_max == -1) + g_handle_max++; + conn->handle = g_handle_max; + + if (g_connections) { + conn->next = g_connections; + g_connections = conn; + } else { + g_connections = conn; + } + + os_mutex_unlock(&g_lock); +} + +#define FREE_CONNECTION(conn) do { \ + if (conn->arg) \ + wasm_runtime_free(conn->arg); \ + wasm_runtime_free(conn); \ +} while (0) + +static int get_app_conns_num(uint32 module_id) +{ + sys_connection_t *conn; + int num = 0; + + os_mutex_lock(&g_lock); + + conn = g_connections; + while (conn) { + if (conn->module_id == module_id) + num++; + conn = conn->next; + } + + os_mutex_unlock(&g_lock); + + return num; +} + +static sys_connection_t *find_connection(uint32 handle, bool remove_found) +{ + sys_connection_t *conn, *prev = NULL; + + os_mutex_lock(&g_lock); + + conn = g_connections; + while (conn) { + if (conn->handle == handle) { + if (remove_found) { + if (prev != NULL) { + prev->next = conn->next; + } else { + g_connections = conn->next; + } + } + os_mutex_unlock(&g_lock); + return conn; + } else { + prev = conn; + conn = conn->next; + } + } + + os_mutex_unlock(&g_lock); + + return NULL; +} + +static void cleanup_connections(uint32 module_id) +{ + sys_connection_t *conn, *prev = NULL; + + os_mutex_lock(&g_lock); + + conn = g_connections; + while (conn) { + if (conn->module_id == module_id) { + epoll_ctl(epollfd, EPOLL_CTL_DEL, conn->fd, NULL); + close(conn->fd); + + if (prev != NULL) { + prev->next = conn->next; + FREE_CONNECTION(conn); + conn = prev->next; + } else { + g_connections = conn->next; + FREE_CONNECTION(conn); + conn = g_connections; + } + } else { + prev = conn; + conn = conn->next; + } + } + + os_mutex_unlock(&g_lock); +} + +static conn_type_t get_conn_type(const char *name) +{ + if (strcmp(name, "TCP") == 0) + return CONN_TYPE_TCP; + if (strcmp(name, "UDP") == 0) + return CONN_TYPE_UDP; + if (strcmp(name, "UART") == 0) + return CONN_TYPE_UART; + + return CONN_TYPE_UNKNOWN; +} + +/* --- connection lib function --- */ +static uint32 _conn_open(wasm_module_inst_t module_inst, + const char *name, attr_container_t *args) +{ + int fd; + sys_connection_t *conn; + struct epoll_event ev; + uint32 module_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + bh_assert(module_id != ID_NONE); + + if (get_app_conns_num(module_id) >= MAX_CONNECTION_PER_APP) + return -1; + + conn = (sys_connection_t *)wasm_runtime_malloc(sizeof(*conn)); + if (conn == NULL) + return -1; + + memset(conn, 0, sizeof(*conn)); + conn->module_id = module_id; + conn->type = get_conn_type(name); + + /* Generate a handle and add to list */ + add_connection(conn); + + if (conn->type == CONN_TYPE_TCP) { + char *address; + uint16 port; + + /* Check and parse connection parameters */ + if (!attr_container_contain_key(args, "address") || + !attr_container_contain_key(args, "port")) + goto fail; + + address = attr_container_get_as_string(args, "address"); + port = attr_container_get_as_uint16(args, "port"); + + /* Connect to TCP server */ + if (!address || (fd = tcp_open(address, port)) == -1) + goto fail; + + } else if (conn->type == CONN_TYPE_UDP) { + uint16 port; + + /* Check and parse connection parameters */ + if (!attr_container_contain_key(args, "bind port")) + goto fail; + port = attr_container_get_as_uint16(args, "bind port"); + + /* Bind port */ + if ((fd = udp_open(port)) == -1) + goto fail; + + } else if (conn->type == CONN_TYPE_UART) { + char *device; + int baud; + + /* Check and parse connection parameters */ + if (!attr_container_contain_key(args, "device") || + !attr_container_contain_key(args, "baudrate")) + goto fail; + device = attr_container_get_as_string(args, "device"); + baud = attr_container_get_as_int(args, "baudrate"); + + /* Open device */ + if (!device || (fd = uart_open(device, baud)) == -1) + goto fail; + } else { + goto fail; + } + + conn->fd = fd; + + /* Set current connection as event data */ + ev.events = EPOLLIN; + ev.data.ptr = conn; + + /* Monitor incoming data */ + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) { + close(fd); + goto fail; + } + + return conn->handle; + +fail: + find_connection(conn->handle, true); + wasm_runtime_free(conn); + return -1; +} + +/* --- connection lib function --- */ +static void _conn_close(uint32 handle) +{ + sys_connection_t *conn = find_connection(handle, true); + + if (conn != NULL) { + epoll_ctl(epollfd, EPOLL_CTL_DEL, conn->fd, NULL); + close(conn->fd); + FREE_CONNECTION(conn); + } +} + +/* --- connection lib function --- */ +static int _conn_send(uint32 handle, const char *data, int len) +{ + sys_connection_t *conn = find_connection(handle, false); + + if (conn == NULL) + return -1; + + if (conn->type == CONN_TYPE_TCP) + return tcp_send(conn->fd, data, len); + + if (conn->type == CONN_TYPE_UDP) { + struct sockaddr *addr = (struct sockaddr *)conn->arg; + return udp_send(conn->fd, addr, data, len); + } + + if (conn->type == CONN_TYPE_UART) + return uart_send(conn->fd, data, len); + + return -1; +} + +/* --- connection lib function --- */ +static bool _conn_config(uint32 handle, attr_container_t *cfg) +{ + sys_connection_t *conn = find_connection(handle, false); + + if (conn == NULL) + return false; + + if (conn->type == CONN_TYPE_UDP) { + char *address; + uint16_t port; + struct sockaddr_in *addr; + + /* Parse remote address/port */ + if (!attr_container_contain_key(cfg, "address") || + !attr_container_contain_key(cfg, "port")) + return false; + if (!(address = attr_container_get_as_string(cfg, "address"))) + return false; + port = attr_container_get_as_uint16(cfg, "port"); + + if (conn->arg == NULL) { + addr = (struct sockaddr_in *)wasm_runtime_malloc(sizeof(*addr)); + if (addr == NULL) + return false; + + memset(addr, 0, sizeof(*addr)); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(address); + addr->sin_port = htons(port); + + /* Set remote address as connection arg */ + conn->arg = addr; + } else { + addr = (struct sockaddr_in *)conn->arg; + addr->sin_addr.s_addr = inet_addr(address); + addr->sin_port = htons(port); + } + + return true; + } + + return false; +} + +/* --- connection manager reference implementation ---*/ + +typedef struct connection_event { + uint32 handle; + char *data; + uint32 len; +} connection_event_t; + +static void connection_event_cleaner(connection_event_t *conn_event) +{ + if (conn_event->data != NULL) + wasm_runtime_free(conn_event->data); + wasm_runtime_free(conn_event); +} + +static void post_msg_to_module(sys_connection_t *conn, + char *data, + uint32 len) +{ + module_data *module = module_data_list_lookup_id(conn->module_id); + char *data_copy = NULL; + connection_event_t *conn_data_event; + bh_message_t msg; + + if (module == NULL) + return; + + conn_data_event = (connection_event_t *)wasm_runtime_malloc(sizeof(*conn_data_event)); + if (conn_data_event == NULL) + return; + + if (len > 0) { + data_copy = (char *)wasm_runtime_malloc(len); + if (data_copy == NULL) { + wasm_runtime_free(conn_data_event); + return; + } + bh_memcpy_s(data_copy, len, data, len); + } + + memset(conn_data_event, 0, sizeof(*conn_data_event)); + conn_data_event->handle = conn->handle; + conn_data_event->data = data_copy; + conn_data_event->len = len; + + msg = bh_new_msg(CONNECTION_EVENT_WASM, + conn_data_event, + sizeof(*conn_data_event), + connection_event_cleaner); + if (!msg) { + connection_event_cleaner(conn_data_event); + return; + } + + bh_post_msg2(module->queue, msg); +} + +static void* polling_thread_routine (void *arg) +{ + while (polling_thread_run) { + int i, n; + + n = epoll_wait(epollfd, epoll_events, MAX_EVENTS, -1); + + if (n == -1 && errno != EINTR) + continue; + + for (i = 0; i < n; i++) { + sys_connection_t *conn + = (sys_connection_t *)epoll_events[i].data.ptr; + + if (conn->type == CONN_TYPE_TCP) { + int count = tcp_recv(conn->fd, io_buf, IO_BUF_SIZE); + if (count <= 0) { + /* Connection is closed by peer */ + post_msg_to_module(conn, NULL, 0); + _conn_close(conn->handle); + } else { + /* Data is received */ + post_msg_to_module(conn, io_buf, count); + } + } else if (conn->type == CONN_TYPE_UDP) { + int count = udp_recv(conn->fd, io_buf, IO_BUF_SIZE); + if (count > 0) + post_msg_to_module(conn, io_buf, count); + } else if (conn->type == CONN_TYPE_UART) { + int count = uart_recv(conn->fd, io_buf, IO_BUF_SIZE); + if (count > 0) + post_msg_to_module(conn, io_buf, count); + } + } + } + + return NULL; +} + +void app_mgr_connection_event_callback(module_data *m_data, bh_message_t msg) +{ + uint32 argv[3]; + wasm_function_inst_t func_on_conn_data; + bh_assert(CONNECTION_EVENT_WASM == bh_message_type(msg)); + wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; + wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; + connection_event_t *conn_event + = (connection_event_t *)bh_message_payload(msg); + int32 data_offset; + + if (conn_event == NULL) + return; + + func_on_conn_data = wasm_runtime_lookup_function(inst, "_on_connection_data", + "(i32i32i32)"); + if (!func_on_conn_data) + func_on_conn_data = wasm_runtime_lookup_function(inst, "on_connection_data", + "(i32i32i32)"); + if (!func_on_conn_data) { + printf("Cannot find function on_connection_data\n"); + return; + } + + /* 0 len means connection closed */ + if (conn_event->len == 0) { + argv[0] = conn_event->handle; + argv[1] = 0; + argv[2] = 0; + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_conn_data, + 3, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + printf(":Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + return; + } + } else { + data_offset = wasm_runtime_module_dup_data(inst, + conn_event->data, + conn_event->len); + if (data_offset == 0) { + const char *exception = wasm_runtime_get_exception(inst); + if (exception) { + printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + } + return; + } + + argv[0] = conn_event->handle; + argv[1] = (uint32) data_offset; + argv[2] = conn_event->len; + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_conn_data, + 3, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + printf(":Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + wasm_runtime_module_free(inst, data_offset); + return; + } + wasm_runtime_module_free(inst, data_offset); + } +} + +bool init_connection_framework() +{ + korp_tid tid; + + epollfd = epoll_create(MAX_EVENTS); + if (epollfd == -1) + return false; + + if (os_mutex_init(&g_lock) != 0) { + close(epollfd); + return false; + } + + if (!wasm_register_cleanup_callback(cleanup_connections)) { + goto fail; + } + + if (!wasm_register_msg_callback(CONNECTION_EVENT_WASM, + app_mgr_connection_event_callback)) { + goto fail; + } + + if (os_thread_create(&tid, + polling_thread_routine, + NULL, + BH_APPLET_PRESERVED_STACK_SIZE) != 0) { + goto fail; + } + + return true; + +fail: + os_mutex_destroy(&g_lock); + close(epollfd); + return false; +} + +void exit_connection_framework() +{ + polling_thread_run = false; +} diff --git a/wamr/core/app-framework/connection/native/linux/connection_mgr.cmake b/wamr/core/app-framework/connection/native/linux/connection_mgr.cmake new file mode 100644 index 0000000..c8f2b48 --- /dev/null +++ b/wamr/core/app-framework/connection/native/linux/connection_mgr.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_LIB_CONN_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${WASM_LIB_CONN_MGR_DIR}) + + +file (GLOB_RECURSE source_all ${WASM_LIB_CONN_MGR_DIR}/*.c) + +set (WASM_LIB_CONN_MGR_SOURCE ${source_all}) + + diff --git a/wamr/core/app-framework/connection/native/wasm_lib.cmake b/wamr/core/app-framework/connection/native/wasm_lib.cmake new file mode 100644 index 0000000..58db0c1 --- /dev/null +++ b/wamr/core/app-framework/connection/native/wasm_lib.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +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 + ${WASM_LIB_CONN_MGR_SOURCE} + ${WASM_LIB_CONN_DIR}/*.c +) + +set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/connection/native/zephyr/connection_lib_impl.c b/wamr/core/app-framework/connection/native/zephyr/connection_lib_impl.c new file mode 100644 index 0000000..9c30edc --- /dev/null +++ b/wamr/core/app-framework/connection/native/zephyr/connection_lib_impl.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + * Note: + * This file implements the linux version connection library which is + * defined in connection_lib.h. + * It also provides a reference impl of connections manager. + */ + +#include "connection_lib.h" + +/* + * Platform implementation of connection library + */ +connection_interface_t connection_impl = { + ._open = NULL, + ._close = NULL, + ._send = NULL, + ._config = NULL +}; diff --git a/wamr/core/app-framework/connection/native/zephyr/connection_mgr.cmake b/wamr/core/app-framework/connection/native/zephyr/connection_mgr.cmake new file mode 100644 index 0000000..c8f2b48 --- /dev/null +++ b/wamr/core/app-framework/connection/native/zephyr/connection_mgr.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_LIB_CONN_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${WASM_LIB_CONN_MGR_DIR}) + + +file (GLOB_RECURSE source_all ${WASM_LIB_CONN_MGR_DIR}/*.c) + +set (WASM_LIB_CONN_MGR_SOURCE ${source_all}) + + diff --git a/wamr/core/app-framework/sensor/app/sensor.c b/wamr/core/app-framework/sensor/app/sensor.c new file mode 100644 index 0000000..2af23fa --- /dev/null +++ b/wamr/core/app-framework/sensor/app/sensor.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wa-inc/sensor.h" + +#include "sensor_api.h" + +typedef struct _sensor { + struct _sensor * next; + char *name; + uint32 handle; + void (*sensor_callback)(sensor_t, attr_container_t *, void *); + void *user_data; +} sensor; + +static sensor_t g_sensors = NULL; + +sensor_t sensor_open(const char* name, int index, + sensor_event_handler_f sensor_event_handler, + void *user_data) +{ + uint32 id = wasm_sensor_open(name, index); + if (id == -1) + return NULL; + + //create local node for holding the user callback + sensor_t sensor = (sensor_t) malloc(sizeof(struct _sensor)); + if (sensor == NULL) + return NULL; + + memset(sensor, 0, sizeof(struct _sensor)); + sensor->handle = id; + sensor->name = strdup(name); + sensor->user_data = user_data; + sensor->sensor_callback = sensor_event_handler; + + if (!sensor->name) { + free(sensor); + return NULL; + } + + if (g_sensors == NULL) { + g_sensors = sensor; + } else { + sensor->next = g_sensors; + g_sensors = sensor; + } + + return sensor; +} + +bool sensor_config_with_attr_container(sensor_t sensor, attr_container_t *cfg) +{ + char *buffer = (char *)cfg; + int len = attr_container_get_serialize_length(cfg); + + return wasm_sensor_config_with_attr_container(sensor->handle, buffer, len); +} + +bool sensor_config(sensor_t sensor, int interval, int bit_cfg, int delay) +{ + bool ret = wasm_sensor_config(sensor->handle, interval, bit_cfg, delay); + return ret; +} + +bool sensor_close(sensor_t sensor) +{ + wasm_sensor_close(sensor->handle); + + // remove local node + sensor_t s = g_sensors; + sensor_t prev = NULL; + while (s) { + if (s == sensor) { + if (prev == NULL) { + g_sensors = s->next; + } else { + prev->next = s->next; + } + free(s->name); + free(s); + return true; + } else { + prev = s; + s = s->next; + } + } + + return false; +} + +/* + * + * API for native layer to callback for sensor events + * + */ + +void on_sensor_event(uint32 sensor_id, char * buffer, int len) +{ + attr_container_t * sensor_data = (attr_container_t *) buffer; + + // lookup the sensor and call the handlers + sensor_t s = g_sensors; + sensor_t prev = NULL; + while (s) { + if (s->handle == sensor_id) { + s->sensor_callback(s, sensor_data, s->user_data); + break; + } + + s = s->next; + } +} diff --git a/wamr/core/app-framework/sensor/app/sensor_api.h b/wamr/core/app-framework/sensor/app/sensor_api.h new file mode 100644 index 0000000..9f67677 --- /dev/null +++ b/wamr/core/app-framework/sensor/app/sensor_api.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SENSOR_API_H_ +#define _SENSOR_API_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint32 +wasm_sensor_open(const char* name, int instance); + +bool +wasm_sensor_config(uint32 sensor, int interval, int bit_cfg, int delay); + +bool +wasm_sensor_config_with_attr_container(uint32 sensor, char *buffer, int len); + +bool +wasm_sensor_close(uint32 sensor); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SENSOR_API_H_ */ + diff --git a/wamr/core/app-framework/sensor/app/wa-inc/sensor.h b/wamr/core/app-framework/sensor/app/wa-inc/sensor.h new file mode 100644 index 0000000..8abc1ad --- /dev/null +++ b/wamr/core/app-framework/sensor/app/wa-inc/sensor.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AEE_SENSOR_H_ +#define _AEE_SENSOR_H_ + +#include "bi-inc/attr_container.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* board producer define sensor */ +struct _sensor; +typedef struct _sensor *sensor_t; + +/** + * @typedef sensor_event_handler_f + * + * @brief Define the signature of callback function for API + * sensor_open() to handle sensor event. + * + * @param sensor the sensor which the event belong to + * @param sensor_event the sensor event + * @param user_data user data associated with the sensor which is set when + * calling sensor_open(). + * + * @see sensor_open + */ +typedef void (*sensor_event_handler_f)(sensor_t sensor, + attr_container_t *sensor_event, + void *user_data); + +/* + ***************** + * Sensor APIs + ***************** + */ + +/** + * @brief Open sensor. + * + * @param name sensor name + * @param index sensor index + * @param handler callback function to handle the sensor event + * @param user_data user data + * + * @return the sensor opened if success, NULL otherwise + */ +sensor_t sensor_open(const char* name, + int index, + sensor_event_handler_f handler, + void *user_data); + +/** + * @brief Configure sensor with interval/bit_cfg/delay values. + * + * @param sensor the sensor to be configured + * @param interval sensor event interval + * @param bit_cfg sensor bit config + * @param delay sensor delay + * + * @return true if success, false otherwise + */ +bool sensor_config(sensor_t sensor, int interval, int bit_cfg, int delay); + +/** + * @brief Configure sensor with attr_container_t object. + * + * @param sensor the sensor to be configured + * @param cfg the configuration + * + * @return true if success, false otherwise + */ +bool sensor_config_with_attr_container(sensor_t sensor, attr_container_t *cfg); + +/** + * @brief Close sensor. + * + * @param sensor the sensor to be closed + * + * @return true if success, false otherwise + */ +bool sensor_close(sensor_t sensor); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/app-framework/sensor/app/wasm_app.cmake b/wamr/core/app-framework/sensor/app/wasm_app.cmake new file mode 100644 index 0000000..4b14a8b --- /dev/null +++ b/wamr/core/app-framework/sensor/app/wasm_app.cmake @@ -0,0 +1,11 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_APP_SENSOR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${WASM_APP_SENSOR_DIR}) + + +file (GLOB_RECURSE source_all ${WASM_APP_SENSOR_DIR}/*.c) + +set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/sensor/native/runtime_sensor.c b/wamr/core/app-framework/sensor/native/runtime_sensor.c new file mode 100644 index 0000000..89f7ea8 --- /dev/null +++ b/wamr/core/app-framework/sensor/native/runtime_sensor.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "runtime_sensor.h" +#include "app_manager_export.h" +#include "module_wasm_app.h" +#include "bh_platform.h" + +static sys_sensor_t * g_sys_sensors = NULL; +static int g_sensor_id_max = 0; + +static sensor_client_t * +find_sensor_client(sys_sensor_t * sensor, + unsigned int client_id, bool remove_if_found); + +void (*rechedule_sensor_callback)() = NULL; + +/* + * API for the applications to call - don't call it from the runtime + * + */ + +static void +sensor_event_cleaner(sensor_event_data_t *sensor_event) +{ + if (sensor_event->data != NULL) { + if (sensor_event->data_fmt == FMT_ATTR_CONTAINER) + attr_container_destroy(sensor_event->data); + else + wasm_runtime_free(sensor_event->data); + } + + wasm_runtime_free(sensor_event); +} + +static void +wasm_sensor_callback(void *client, uint32 sensor_id, void *user_data) +{ + attr_container_t *sensor_data = (attr_container_t *) user_data; + attr_container_t *sensor_data_clone; + int sensor_data_len; + sensor_event_data_t *sensor_event; + bh_message_t msg; + sensor_client_t *c = (sensor_client_t *) client; + + module_data *module = module_data_list_lookup_id(c->client_id); + if (module == NULL) + return; + + if (sensor_data == NULL) + return; + + sensor_data_len = attr_container_get_serialize_length(sensor_data); + sensor_data_clone = (attr_container_t *)wasm_runtime_malloc(sensor_data_len); + if (sensor_data_clone == NULL) + return; + + /* multiple sensor clients may use/free the sensor data, so make a copy */ + bh_memcpy_s(sensor_data_clone, sensor_data_len, + sensor_data, sensor_data_len); + + sensor_event = (sensor_event_data_t *)wasm_runtime_malloc(sizeof(*sensor_event)); + if (sensor_event == NULL) { + wasm_runtime_free(sensor_data_clone); + return; + } + + memset(sensor_event, 0, sizeof(*sensor_event)); + sensor_event->sensor_id = sensor_id; + sensor_event->data = sensor_data_clone; + sensor_event->data_fmt = FMT_ATTR_CONTAINER; + + msg = bh_new_msg(SENSOR_EVENT_WASM, + sensor_event, + sizeof(*sensor_event), + sensor_event_cleaner); + if (!msg) { + sensor_event_cleaner(sensor_event); + return; + } + + bh_post_msg2(module->queue, msg); +} + +bool +wasm_sensor_config(wasm_exec_env_t exec_env, + uint32 sensor, int interval, + int bit_cfg, int delay) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + attr_container_t * attr_cont; + sensor_client_t * c; + sensor_obj_t s = find_sys_sensor_id(sensor); + if (s == NULL) + return false; + + unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + bh_assert(mod_id != ID_NONE); + + os_mutex_lock(&s->lock); + + c = find_sensor_client(s, mod_id, false); + if (c == NULL) { + os_mutex_unlock(&s->lock); + return false; + } + + c->interval = interval; + c->bit_cfg = bit_cfg; + c->delay = delay; + + os_mutex_unlock(&s->lock); + + if (s->config != NULL) { + attr_cont = attr_container_create("config sensor"); + attr_container_set_int(&attr_cont, "interval", interval); + attr_container_set_int(&attr_cont, "bit_cfg", bit_cfg); + attr_container_set_int(&attr_cont, "delay", delay); + s->config(s, attr_cont); + attr_container_destroy(attr_cont); + } + + refresh_read_interval(s); + + reschedule_sensor_read(); + + return true; +} + +uint32 +wasm_sensor_open(wasm_exec_env_t exec_env, + char *name, int instance) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (name != NULL) { + sensor_client_t *c; + sys_sensor_t *s = find_sys_sensor(name, instance); + if (s == NULL) + return -1; + + unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + bh_assert(mod_id != ID_NONE); + + os_mutex_lock(&s->lock); + + c = find_sensor_client(s, mod_id, false); + if (c) { + // the app already opened this sensor + os_mutex_unlock(&s->lock); + return -1; + } + + sensor_client_t * client = (sensor_client_t*) wasm_runtime_malloc( + sizeof(sensor_client_t)); + if (client == NULL) { + os_mutex_unlock(&s->lock); + return -1; + } + + memset(client, 0, sizeof(sensor_client_t)); + client->client_id = mod_id; + client->client_callback = (void *)wasm_sensor_callback; + client->interval = s->default_interval; + client->next = s->clients; + s->clients = client; + + os_mutex_unlock(&s->lock); + + refresh_read_interval(s); + + reschedule_sensor_read(); + + return s->sensor_id; + } + + return -1; +} + +bool +wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, + uint32 sensor, char *buffer, int len) +{ + if (buffer != NULL) { + attr_container_t *cfg = (attr_container_t *)buffer; + sensor_obj_t s = find_sys_sensor_id(sensor); + if (s == NULL) + return false; + + if (s->config == NULL) + return false; + + return s->config(s, cfg); + } + + return false; +} + +bool +wasm_sensor_close(wasm_exec_env_t exec_env, uint32 sensor) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + unsigned int mod_id = app_manager_get_module_id(Module_WASM_App, + module_inst); + unsigned int client_id = mod_id; + sensor_obj_t s = find_sys_sensor_id(sensor); + sensor_client_t *c; + + bh_assert(mod_id != ID_NONE); + + if (s == NULL) + return false; + + os_mutex_lock(&s->lock); + if ((c = find_sensor_client(s, client_id, true)) != NULL) + wasm_runtime_free(c); + os_mutex_unlock(&s->lock); + + refresh_read_interval(s); + + reschedule_sensor_read(); + + return true; +} + +/* + * + * sensor framework API - don't expose to the applications + * + */ +void set_sensor_reshceduler(void (*callback)()) +{ + rechedule_sensor_callback = callback; +} + +// used for other threads to wakeup the sensor read thread +void reschedule_sensor_read() +{ + if (rechedule_sensor_callback) + rechedule_sensor_callback(); +} + +void refresh_read_interval(sensor_obj_t sensor) +{ + sensor_client_t *c; + uint32 interval = sensor->default_interval; + os_mutex_lock(&sensor->lock); + + c = sensor->clients; + if (c) + interval = c->interval; + + while (c) { + if (c->interval < interval) + interval = c->interval; + c = c->next; + } + + os_mutex_unlock(&sensor->lock); + + sensor->read_interval = interval; +} + +sensor_obj_t +add_sys_sensor(char * name, char * description, int instance, + uint32 default_interval, void * read_func, void * config_func) +{ + sys_sensor_t * s = (sys_sensor_t *) wasm_runtime_malloc(sizeof(sys_sensor_t)); + if (s == NULL) + return NULL; + + memset(s, 0, sizeof(*s)); + s->name = bh_strdup(name); + s->sensor_instance = instance; + s->default_interval = default_interval; + + if (!s->name) { + wasm_runtime_free(s); + return NULL; + } + + if (description) { + s->description = bh_strdup(description); + if (!s->description) { + wasm_runtime_free(s->name); + wasm_runtime_free(s); + return NULL; + } + } + + g_sensor_id_max++; + if (g_sensor_id_max == -1) + g_sensor_id_max++; + s->sensor_id = g_sensor_id_max; + + s->read = read_func; + s->config = config_func; + + if (g_sys_sensors == NULL) { + g_sys_sensors = s; + } else { + s->next = g_sys_sensors; + g_sys_sensors = s; + } + + os_mutex_init(&s->lock); + + return s; +} + +sensor_obj_t find_sys_sensor(const char* name, int instance) +{ + sys_sensor_t * s = g_sys_sensors; + while (s) { + if (strcmp(s->name, name) == 0 && s->sensor_instance == instance) + return s; + + s = s->next; + } + return NULL; +} + +sensor_obj_t find_sys_sensor_id(uint32 sensor_id) +{ + sys_sensor_t * s = g_sys_sensors; + while (s) { + if (s->sensor_id == sensor_id) + return s; + + s = s->next; + } + return NULL; +} + +sensor_client_t *find_sensor_client(sys_sensor_t * sensor, + unsigned int client_id, bool remove_if_found) +{ + sensor_client_t *prev = NULL, *c = sensor->clients; + + while (c) { + sensor_client_t *next = c->next; + if (c->client_id == client_id) { + if (remove_if_found) { + if (prev) + prev->next = next; + else + sensor->clients = next; + } + return c; + } else { + c = c->next; + } + } + + return NULL; +} + +// return the milliseconds to next check +int check_sensor_timers() +{ + int ms_to_next_check = -1; + uint32 now = (uint32)bh_get_tick_ms(); + + sys_sensor_t * s = g_sys_sensors; + while (s) { + uint32 last_read = s->last_read; + uint32 elpased_ms = bh_get_elpased_ms(&last_read); + + if (s->read_interval <= 0 || s->clients == NULL) { + s = s->next; + continue; + } + + if (elpased_ms >= s->read_interval) { + attr_container_t * data = s->read(s); + if (data) { + sensor_client_t * client = s->clients; + while (client) { + client->client_callback(client, s->sensor_id, data); + client = client->next; + } + attr_container_destroy(data); + } + + s->last_read = now; + + if (ms_to_next_check == -1 || (ms_to_next_check < s->read_interval)) + ms_to_next_check = s->read_interval; + } else { + int remaining = s->read_interval - elpased_ms; + if (ms_to_next_check == -1 || (ms_to_next_check < remaining)) + ms_to_next_check = remaining; + + } + + s = s->next; + } + + return ms_to_next_check; +} + +void sensor_cleanup_callback(uint32 module_id) +{ + sys_sensor_t * s = g_sys_sensors; + + while (s) { + sensor_client_t *c; + os_mutex_lock(&s->lock); + if ((c = find_sensor_client(s, module_id, true)) != NULL) { + wasm_runtime_free(c); + } + os_mutex_unlock(&s->lock); + s = s->next; + } +} diff --git a/wamr/core/app-framework/sensor/native/runtime_sensor.h b/wamr/core/app-framework/sensor/native/runtime_sensor.h new file mode 100644 index 0000000..8154d6d --- /dev/null +++ b/wamr/core/app-framework/sensor/native/runtime_sensor.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef LIB_EXTENSION_RUNTIME_SENSOR_H_ +#define LIB_EXTENSION_RUNTIME_SENSOR_H_ + +#include "bh_platform.h" +#include "bi-inc/attr_container.h" +#include "wasm_export.h" +#include "sensor_native_api.h" + +struct _sys_sensor; +typedef struct _sys_sensor* sensor_obj_t; + +typedef struct _sensor_client { + struct _sensor_client * next; + unsigned int client_id; // the app id + int interval; + int bit_cfg; + int delay; + void (*client_callback)(void * client, uint32, attr_container_t *); +} sensor_client_t; + +typedef struct _sys_sensor { + struct _sys_sensor * next; + char * name; + int sensor_instance; + char * description; + uint32 sensor_id; + sensor_client_t * clients; + /* app, sensor mgr and app mgr may access the clients at the same time, + * so need a lock to protect the clients */ + korp_mutex lock; + uint32 last_read; + uint32 read_interval; + uint32 default_interval; + + attr_container_t * (*read)(void *); /* TODO: may support other type return value, such as 'cbor' */ + bool (*config)(void *, void *); + +} sys_sensor_t; + +sensor_obj_t add_sys_sensor(char * name, char * description, int instance, + uint32 default_interval, void * read_func, void * config_func); +sensor_obj_t find_sys_sensor(const char* name, int instance); +sensor_obj_t find_sys_sensor_id(uint32 sensor_id); +void refresh_read_interval(sensor_obj_t sensor); +void sensor_cleanup_callback(uint32 module_id); +int check_sensor_timers(); +void reschedule_sensor_read(); + +void init_sensor_framework(); +void start_sensor_framework(); +void exit_sensor_framework(); + + + +#endif /* LIB_EXTENSION_RUNTIME_SENSOR_H_ */ diff --git a/wamr/core/app-framework/sensor/native/runtime_sensor.inl b/wamr/core/app-framework/sensor/native/runtime_sensor.inl new file mode 100644 index 0000000..a7b9f47 --- /dev/null +++ b/wamr/core/app-framework/sensor/native/runtime_sensor.inl @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +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/wamr/core/app-framework/sensor/native/sensor_mgr_ref.c b/wamr/core/app-framework/sensor/native/sensor_mgr_ref.c new file mode 100644 index 0000000..80646be --- /dev/null +++ b/wamr/core/app-framework/sensor/native/sensor_mgr_ref.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "runtime_sensor.h" +#include "bi-inc/attr_container.h" +#include "module_wasm_app.h" +#include "wasm_export.h" + +/* + * + * One reference implementation for sensor manager + * + * + */ +static korp_cond cond; +static korp_mutex mutex; +static bool sensor_check_thread_run = true; + +void app_mgr_sensor_event_callback(module_data *m_data, bh_message_t msg) +{ + uint32 argv[3]; + wasm_function_inst_t func_onSensorEvent; + + bh_assert(SENSOR_EVENT_WASM == bh_message_type(msg)); + wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; + wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; + + sensor_event_data_t *payload = (sensor_event_data_t*) + bh_message_payload(msg); + if (payload == NULL) + return; + + func_onSensorEvent = wasm_runtime_lookup_function(inst, "_on_sensor_event", + "(i32i32i32)"); + if (!func_onSensorEvent) + func_onSensorEvent = wasm_runtime_lookup_function(inst, "on_sensor_event", + "(i32i32i32)"); + if (!func_onSensorEvent) { + printf("Cannot find function on_sensor_event\n"); + } else { + int32 sensor_data_offset; + uint32 sensor_data_len; + + if (payload->data_fmt == FMT_ATTR_CONTAINER) { + sensor_data_len = attr_container_get_serialize_length(payload->data); + } else { + printf("Unsupported sensor data format: %d\n", payload->data_fmt); + return; + } + + sensor_data_offset = wasm_runtime_module_dup_data(inst, payload->data, + sensor_data_len); + if (sensor_data_offset == 0) { + const char *exception = wasm_runtime_get_exception(inst); + if (exception) { + printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + } + return; + } + + argv[0] = payload->sensor_id; + argv[1] = (uint32) sensor_data_offset; + argv[2] = sensor_data_len; + + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onSensorEvent, + 3, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + printf(":Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + wasm_runtime_module_free(inst, sensor_data_offset); + return; + } + + wasm_runtime_module_free(inst, sensor_data_offset); + } +} + + +static void thread_sensor_check(void * arg) +{ + while (sensor_check_thread_run) { + int ms_to_expiry = check_sensor_timers(); + if (ms_to_expiry == -1) + ms_to_expiry = 5000; + os_mutex_lock(&mutex); + os_cond_reltimedwait(&cond, &mutex, ms_to_expiry * 1000); + os_mutex_unlock(&mutex); + } +} + +static void cb_wakeup_thread() +{ + os_cond_signal(&cond); +} + +void set_sensor_reshceduler(void (*callback)()); + +void init_sensor_framework() +{ + // init the mutext and conditions + os_cond_init(&cond); + os_mutex_init(&mutex); + + + set_sensor_reshceduler(cb_wakeup_thread); + + wasm_register_msg_callback(SENSOR_EVENT_WASM, + app_mgr_sensor_event_callback); + + wasm_register_cleanup_callback(sensor_cleanup_callback); + + +} + +void start_sensor_framework() +{ + korp_tid tid; + + os_thread_create(&tid, + (void *)thread_sensor_check, + NULL, + BH_APPLET_PRESERVED_STACK_SIZE); +} + + +void exit_sensor_framework() +{ + sensor_check_thread_run = false; + reschedule_sensor_read(); + + //todo: wait the sensor thread termination +} + diff --git a/wamr/core/app-framework/sensor/native/sensor_native_api.h b/wamr/core/app-framework/sensor/native/sensor_native_api.h new file mode 100644 index 0000000..ed32317 --- /dev/null +++ b/wamr/core/app-framework/sensor/native/sensor_native_api.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SENSOR_NATIVE_API_H_ +#define _SENSOR_NATIVE_API_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +wasm_sensor_config(wasm_exec_env_t exec_env, + uint32 sensor, int interval, + int bit_cfg, int delay); +uint32 +wasm_sensor_open(wasm_exec_env_t exec_env, + char *name, int instance); + +bool +wasm_sensor_config_with_attr_container(wasm_exec_env_t exec_env, + uint32 sensor, char *buffer, int len); + +bool +wasm_sensor_close(wasm_exec_env_t exec_env, uint32 sensor); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SENSOR_NATIVE_API_H_ */ + diff --git a/wamr/core/app-framework/sensor/native/wasm_lib.cmake b/wamr/core/app-framework/sensor/native/wasm_lib.cmake new file mode 100644 index 0000000..65a83ba --- /dev/null +++ b/wamr/core/app-framework/sensor/native/wasm_lib.cmake @@ -0,0 +1,14 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_LIB_SENSOR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DAPP_FRAMEWORK_SENSOR) + +include_directories(${WASM_LIB_SENSOR_DIR}) + + +file (GLOB_RECURSE source_all ${WASM_LIB_SENSOR_DIR}/*.c) + +set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) + diff --git a/wamr/core/app-framework/template/app/wa-inc/app_xxx.h b/wamr/core/app-framework/template/app/wa-inc/app_xxx.h new file mode 100644 index 0000000..ac30842 --- /dev/null +++ b/wamr/core/app-framework/template/app/wa-inc/app_xxx.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* + header file for wasm application +*/ \ No newline at end of file diff --git a/wamr/core/app-framework/template/app/wasm_app.cmake b/wamr/core/app-framework/template/app/wasm_app.cmake new file mode 100644 index 0000000..16ca237 --- /dev/null +++ b/wamr/core/app-framework/template/app/wasm_app.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_APP_CURRENT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories( + ${WASM_APP_CURRENT_DIR} + # Add your include dir here +) + +file (GLOB_RECURSE source_all + ${WASM_APP_CURRENT_DIR}/*.c + # Add your source file here +) + +set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/template/native/app_xxx.inl b/wamr/core/app-framework/template/native/app_xxx.inl new file mode 100644 index 0000000..2503fe4 --- /dev/null +++ b/wamr/core/app-framework/template/native/app_xxx.inl @@ -0,0 +1,6 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* EXPORT_WASM_API(your_api_here), */ diff --git a/wamr/core/app-framework/template/native/wasm_lib.cmake b/wamr/core/app-framework/template/native/wasm_lib.cmake new file mode 100644 index 0000000..2601c1d --- /dev/null +++ b/wamr/core/app-framework/template/native/wasm_lib.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_LIB_CURRENT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories( + ${WASM_LIB_CURRENT_DIR} + # Add your include dir here +) + +file (GLOB_RECURSE source_all + ${WASM_LIB_CURRENT_DIR}/*.c + # Add your source file here +) + +set (WASM_APP_LIB_CURRENT_SOURCE ${source_all}) + diff --git a/wamr/core/app-framework/wgl/app/gui_api.h b/wamr/core/app-framework/wgl/app/gui_api.h new file mode 100644 index 0000000..a258474 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/gui_api.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GUI_API_H_ +#define _GUI_API_H_ + +#include "bh_platform.h" +#include "bi-inc/wgl_shared_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void +wasm_obj_native_call(int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_btn_native_call(int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_label_native_call(int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_cb_native_call(int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_list_native_call(int32 func_id, uint32 *argv, uint32 argc); + + +#ifdef __cplusplus +} +#endif + + +#endif /* end of _GUI_API_H_ */ diff --git a/wamr/core/app-framework/wgl/app/prepare_headers.sh b/wamr/core/app-framework/wgl/app/prepare_headers.sh new file mode 100755 index 0000000..2612579 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/prepare_headers.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +WGL_ROOT=$(cd "$(dirname "$0")/" && pwd) +LVGL_REPO_DIR=${WGL_ROOT}/../../../deps/lvgl +ls $LVGL_REPO_DIR + +#if [ ! -d "${LVGL_REPO_DIR}" ]; then +# echo "lvgl repo not exist, please git pull the lvgl v6.0 first" +# exit 1 +#fi + +cd ${WGL_ROOT}/wa-inc/lvgl +pwd + +if [ -d src ]; then + rm -rf src + echo "deleted the src folder from previous preparation." +fi + +mkdir src +cd src + +cp ${LVGL_REPO_DIR}/src/*.h ./ + +for folder in lv_core lv_draw lv_hal lv_objx lv_font lv_misc lv_themes +do + echo "Prepare fold $folder...done" + mkdir $folder + cp ${LVGL_REPO_DIR}/src/${folder}/*.h ./${folder}/ +done + +cp -f ../lv_obj.h ./lv_core/lv_obj.h + +echo "test the header files..." +cd .. + +gcc test.c -o test.out +if [ $? != 0 ];then + echo "failed to compile the test.c" + exit 1 +else + echo "okay" + rm test.out +fi + +echo "lvgl header files for WASM application ready." diff --git a/wamr/core/app-framework/wgl/app/src/wgl_btn.c b/wamr/core/app-framework/wgl/app/src/wgl_btn.c new file mode 100644 index 0000000..89dbbca --- /dev/null +++ b/wamr/core/app-framework/wgl/app/src/wgl_btn.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wa-inc/lvgl/lvgl.h" +#include "bh_platform.h" +#include "gui_api.h" + +#define ARGC sizeof(argv)/sizeof(uint32) +#define CALL_BTN_NATIVE_FUNC(id) wasm_btn_native_call(id, argv, ARGC) + +lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy) +{ + uint32 argv[2] = {0}; + + argv[0] = (uint32)par; + argv[1] = (uint32)copy; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_CREATE); + return (lv_obj_t *)argv[0]; +} + +void lv_btn_set_toggle(lv_obj_t * btn, bool tgl) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)btn; + argv[1] = tgl; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_TOGGLE); +} + +void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)btn; + argv[1] = state; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_STATE); +} + +void lv_btn_toggle(lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_TOGGLE); +} + +void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)btn; + argv[1] = time; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_IN_TIME); +} + +void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)btn; + argv[1] = time; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_WAIT_TIME); +} + +void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)btn; + argv[1] = time; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_SET_INK_OUT_TIME); +} + +//void wgl_btn_set_style(wgl_obj_t btn, wgl_btn_style_t type, const wgl_style_t * style) +//{ +// //TODO: pack style +// //wasm_btn_set_style(btn, type, style); +//} +// +lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_STATE); + return (lv_btn_state_t)argv[0]; +} + +bool lv_btn_get_toggle(const lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_TOGGLE); + return (bool)argv[0]; +} + +uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_IN_TIME); + return (uint16_t)argv[0]; +} + +uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_WAIT_TIME); + return (uint16_t)argv[0]; +} + +uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)btn; + CALL_BTN_NATIVE_FUNC(BTN_FUNC_ID_GET_INK_OUT_TIME); + return (uint16_t)argv[0]; +} +// +//const wgl_style_t * wgl_btn_get_style(const wgl_obj_t btn, wgl_btn_style_t type) +//{ +// //TODO: pack style +// //wasm_btn_get_style(btn, type); +// return NULL; +//} diff --git a/wamr/core/app-framework/wgl/app/src/wgl_cb.c b/wamr/core/app-framework/wgl/app/src/wgl_cb.c new file mode 100644 index 0000000..83a541a --- /dev/null +++ b/wamr/core/app-framework/wgl/app/src/wgl_cb.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#include "wa-inc/lvgl/lvgl.h" +#include "gui_api.h" + +#include + +#define ARGC sizeof(argv)/sizeof(uint32) +#define CALL_CB_NATIVE_FUNC(id) wasm_cb_native_call(id, argv, ARGC) + +lv_obj_t * lv_cb_create(lv_obj_t * par, const lv_obj_t * copy) +{ + uint32 argv[2] = {0}; + + argv[0] = (uint32)par; + argv[1] = (uint32)copy; + CALL_CB_NATIVE_FUNC(CB_FUNC_ID_CREATE); + return (lv_obj_t *)argv[0]; +} + +void lv_cb_set_text(lv_obj_t * cb, const char * txt) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)cb; + argv[1] = (uint32)txt; + argv[2] = strlen(txt) + 1; + CALL_CB_NATIVE_FUNC(CB_FUNC_ID_SET_TEXT); +} + +void lv_cb_set_static_text(lv_obj_t * cb, const char * txt) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)cb; + argv[1] = (uint32)txt; + argv[2] = strlen(txt) + 1; + CALL_CB_NATIVE_FUNC(CB_FUNC_ID_SET_STATIC_TEXT); +} + +//void wgl_cb_set_style(wgl_obj_t cb, wgl_cb_style_t type, const wgl_style_t * style) +//{ +// //TODO: +//} +// + +static unsigned int wgl_cb_get_text_length(lv_obj_t * cb) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)cb; + CALL_CB_NATIVE_FUNC(CB_FUNC_ID_GET_TEXT_LENGTH); + return argv[0]; +} + +static char *wgl_cb_get_text(lv_obj_t * cb, char *buffer, int buffer_len) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)cb; + argv[1] = (uint32)buffer; + argv[2] = buffer_len; + CALL_CB_NATIVE_FUNC(CB_FUNC_ID_GET_TEXT); + return (char *)argv[0]; +} + +// TODO: need to use a global data buffer for the returned text +const char * lv_cb_get_text(const lv_obj_t * cb) +{ + + return NULL; +} + + +//const wgl_style_t * wgl_cb_get_style(const wgl_obj_t cb, wgl_cb_style_t type) +//{ +// //TODO +// return NULL; +//} +// + + diff --git a/wamr/core/app-framework/wgl/app/src/wgl_label.c b/wamr/core/app-framework/wgl/app/src/wgl_label.c new file mode 100644 index 0000000..cecd991 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/src/wgl_label.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + + +#include "wa-inc/lvgl/lvgl.h" +#include "gui_api.h" +#include + + +#define ARGC sizeof(argv)/sizeof(uint32) +#define CALL_LABEL_NATIVE_FUNC(id) wasm_label_native_call(id, argv, ARGC) + +lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy) +{ + uint32 argv[2] = {0}; + + argv[0] = (uint32)par; + argv[1] = (uint32)copy; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_CREATE); + return (lv_obj_t *)argv[0]; +} + +void lv_label_set_text(lv_obj_t * label, const char * text) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = (uint32)text; + argv[2] = strlen(text) + 1; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT); +} + + +void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = (uint32)array; + argv[2] = size; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ARRAY_TEXT); +} + + +void lv_label_set_static_text(lv_obj_t * label, const char * text) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = (uint32)text; + argv[2] = strlen(text) + 1; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_STATIC_TEXT); +} + + +void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = long_mode; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_LONG_MODE); +} + + +void lv_label_set_align(lv_obj_t * label, lv_label_align_t align) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = align; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ALIGN); +} + + +void lv_label_set_recolor(lv_obj_t * label, bool en) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = en; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_RECOLOR); +} + + +void lv_label_set_body_draw(lv_obj_t * label, bool en) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = en; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_BODY_DRAW); +} + + +void lv_label_set_anim_speed(lv_obj_t * label, uint16_t anim_speed) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = anim_speed; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_ANIM_SPEED); +} + + +void lv_label_set_text_sel_start(lv_obj_t * label, uint16_t index) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = index; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT_SEL_START); +} + + +void lv_label_set_text_sel_end(lv_obj_t * label, uint16_t index) +{ + uint32 argv[2] = {0}; + argv[0] = (uint32)label; + argv[1] = index; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_SET_TEXT_SEL_END); +} + +unsigned int wgl_label_get_text_length(lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_LENGTH); + return argv[0]; +} + +char * wgl_label_get_text(lv_obj_t * label, char *buffer, int buffer_len) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = (uint32)buffer; + argv[2] = buffer_len; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT); + return (char *)argv[0]; +} + +// TODO: +char * lv_label_get_text(const lv_obj_t * label) +{ + + return NULL; + +} + + +lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LONG_MODE); + return (lv_label_long_mode_t)argv[0]; +} + + +lv_label_align_t lv_label_get_align(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_ALIGN); + return (lv_label_align_t)argv[0]; +} + + +bool lv_label_get_recolor(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_RECOLOR); + return (bool)argv[0]; +} + + +bool lv_label_get_body_draw(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_BODY_DRAW); + return (bool)argv[0]; +} + + +uint16_t lv_label_get_anim_speed(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_ANIM_SPEED); + return (uint16_t)argv[0]; +} + + +void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos) +{ + uint32 argv[4] = {0}; + argv[0] = (uint32)label; + argv[1] = index; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); + pos->x = argv[2]; + pos->y = argv[3]; +} + + +uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = pos->x; + argv[2] = pos->y; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); + return (uint16_t)argv[0]; +} + + +bool lv_label_is_char_under_pos(const lv_obj_t * label, lv_point_t * pos) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = pos->x; + argv[2] = pos->y; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_LETTER_POS); + return (bool)argv[0]; +} + + +uint16_t lv_label_get_text_sel_start(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_SEL_START); + return (uint16_t)argv[0]; +} + + +uint16_t lv_label_get_text_sel_end(const lv_obj_t * label) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)label; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_GET_TEXT_SEL_END); + return (uint16_t)argv[0]; +} + + +void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt) +{ + uint32 argv[4] = {0}; + argv[0] = (uint32)label; + argv[1] = pos; + argv[2] = (uint32)txt; + argv[3] = strlen(txt) + 1; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_INS_TEXT); +} + + +void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt) +{ + uint32 argv[3] = {0}; + argv[0] = (uint32)label; + argv[1] = pos; + argv[2] = cnt; + CALL_LABEL_NATIVE_FUNC(LABEL_FUNC_ID_CUT_TEXT); +} + + diff --git a/wamr/core/app-framework/wgl/app/src/wgl_list.c b/wamr/core/app-framework/wgl/app/src/wgl_list.c new file mode 100644 index 0000000..1ac9b32 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/src/wgl_list.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#include "wa-inc/lvgl/lvgl.h" +#include "gui_api.h" + +#include + +#define ARGC sizeof(argv)/sizeof(uint32) +#define CALL_LIST_NATIVE_FUNC(id) wasm_list_native_call(id, argv, ARGC) + + +lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy) +{ + uint32 argv[2] = {0}; + + argv[0] = (uint32)par; + argv[1] = (uint32)copy; + + CALL_LIST_NATIVE_FUNC(LIST_FUNC_ID_CREATE); + return (lv_obj_t *)argv[0]; +} +// +// +//void wgl_list_clean(wgl_obj_t obj) +//{ +// wasm_list_clean(obj); +//} +// + +lv_obj_t * lv_list_add_btn(lv_obj_t * list, const void * img_src, const char * txt) +{ + uint32 argv[3] = {0}; + + (void)img_src; /* doesn't support img src currently */ + + argv[0] = (uint32)list; + argv[1] = (uint32)txt; + argv[2] = strlen(txt) + 1; + CALL_LIST_NATIVE_FUNC(LIST_FUNC_ID_ADD_BTN); + return (lv_obj_t *)argv[0]; +} +// +// +//bool wgl_list_remove(const wgl_obj_t list, uint16_t index) +//{ +// return wasm_list_remove(list, index); +//} +// +// +//void wgl_list_set_single_mode(wgl_obj_t list, bool mode) +//{ +// wasm_list_set_single_mode(list, mode); +//} +// +//#if LV_USE_GROUP +// +// +//void wgl_list_set_btn_selected(wgl_obj_t list, wgl_obj_t btn) +//{ +// wasm_list_set_btn_selected(list, btn); +//} +//#endif +// +// +//void wgl_list_set_style(wgl_obj_t list, wgl_list_style_t type, const wgl_style_t * style) +//{ +// //TODO +//} +// +// +//bool wgl_list_get_single_mode(wgl_obj_t list) +//{ +// return wasm_list_get_single_mode(list); +//} +// +// +//const char * wgl_list_get_btn_text(const wgl_obj_t btn) +//{ +// return wasm_list_get_btn_text(btn); +//} +// +//wgl_obj_t wgl_list_get_btn_label(const wgl_obj_t btn) +//{ +// return wasm_list_get_btn_label(btn); +//} +// +// +//wgl_obj_t wgl_list_get_btn_img(const wgl_obj_t btn) +//{ +// return wasm_list_get_btn_img(btn); +//} +// +// +//wgl_obj_t wgl_list_get_prev_btn(const wgl_obj_t list, wgl_obj_t prev_btn) +//{ +// return wasm_list_get_prev_btn(list, prev_btn); +//} +// +// +//wgl_obj_t wgl_list_get_next_btn(const wgl_obj_t list, wgl_obj_t prev_btn) +//{ +// return wasm_list_get_next_btn(list, prev_btn); +//} +// +// +//int32_t wgl_list_get_btn_index(const wgl_obj_t list, const wgl_obj_t btn) +//{ +// return wasm_list_get_btn_index(list, btn); +//} +// +// +//uint16_t wgl_list_get_size(const wgl_obj_t list) +//{ +// return wasm_list_get_size(list); +//} +// +//#if LV_USE_GROUP +// +//wgl_obj_t wgl_list_get_btn_selected(const wgl_obj_t list) +//{ +// return wasm_list_get_btn_selected(list); +//} +//#endif +// +// +// +//const wgl_style_t * wgl_list_get_style(const wgl_obj_t list, wgl_list_style_t type) +//{ +// //TODO +// return NULL; +//} +// +// +//void wgl_list_up(const wgl_obj_t list) +//{ +// wasm_list_up(list); +//} +// +//void wgl_list_down(const wgl_obj_t list) +//{ +// wasm_list_down(list); +//} +// +// +//void wgl_list_focus(const wgl_obj_t btn, wgl_anim_enable_t anim) +//{ +// wasm_list_focus(btn, anim); +//} +// + + diff --git a/wamr/core/app-framework/wgl/app/src/wgl_obj.c b/wamr/core/app-framework/wgl/app/src/wgl_obj.c new file mode 100644 index 0000000..ee72beb --- /dev/null +++ b/wamr/core/app-framework/wgl/app/src/wgl_obj.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#include "wa-inc/lvgl/lvgl.h" +#include "gui_api.h" +#include +#include + +#define ARGC sizeof(argv)/sizeof(uint32) +#define CALL_OBJ_NATIVE_FUNC(id) wasm_obj_native_call(id, argv, ARGC) + +typedef struct _obj_evt_cb { + struct _obj_evt_cb *next; + + lv_obj_t * obj; + lv_event_cb_t event_cb; +} obj_evt_cb_t; + +static obj_evt_cb_t *g_obj_evt_cb_list = NULL; + +/* For lvgl compatible */ +char g_widget_text[100]; + +lv_res_t lv_obj_del(lv_obj_t * obj) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)obj; + CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_DEL); + return (lv_res_t)argv[0]; +} + +void lv_obj_del_async(struct _lv_obj_t *obj) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)obj; + CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_DEL_ASYNC); +} + +void lv_obj_clean(lv_obj_t * obj) +{ + uint32 argv[1] = {0}; + argv[0] = (uint32)obj; + CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_CLEAN); +} + +void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod) +{ + uint32 argv[5] = {0}; + argv[0] = (uint32)obj; + argv[1] = (uint32)base; + argv[2] = align; + argv[3] = x_mod; + argv[4] = y_mod; + CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_ALIGN); +} + +lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj) +{ + obj_evt_cb_t *obj_evt_cb = g_obj_evt_cb_list; + while (obj_evt_cb != NULL) { + if (obj_evt_cb->obj == obj) { + return obj_evt_cb->event_cb; + } + obj_evt_cb = obj_evt_cb->next; + } + + return NULL; +} + +void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb) +{ + obj_evt_cb_t *obj_evt_cb; + uint32 argv[1] = {0}; + + obj_evt_cb = g_obj_evt_cb_list; + while (obj_evt_cb) { + if (obj_evt_cb->obj == obj) { + obj_evt_cb->event_cb = event_cb; + return; + } + } + + obj_evt_cb = (obj_evt_cb_t *)malloc(sizeof(*obj_evt_cb)); + if (obj_evt_cb == NULL) + return; + + memset(obj_evt_cb, 0, sizeof(*obj_evt_cb)); + obj_evt_cb->obj = obj; + obj_evt_cb->event_cb = event_cb; + + if (g_obj_evt_cb_list != NULL) { + obj_evt_cb->next = g_obj_evt_cb_list; + g_obj_evt_cb_list = obj_evt_cb; + } else { + g_obj_evt_cb_list = obj_evt_cb; + } + + argv[0] = (uint32)obj; + CALL_OBJ_NATIVE_FUNC(OBJ_FUNC_ID_SET_EVT_CB); +} + +void on_widget_event(lv_obj_t * obj, lv_event_t event) +{ + obj_evt_cb_t *obj_evt_cb = g_obj_evt_cb_list; + + while (obj_evt_cb != NULL) { + if (obj_evt_cb->obj == obj) { + obj_evt_cb->event_cb(obj, event); + return; + } + obj_evt_cb = obj_evt_cb->next; + } +} diff --git a/wamr/core/app-framework/wgl/app/wa-inc/lv_conf.h b/wamr/core/app-framework/wgl/app/wa-inc/lv_conf.h new file mode 100644 index 0000000..b9f3de8 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wa-inc/lv_conf.h @@ -0,0 +1,497 @@ +/** + * @file lv_conf.h + * + */ + +/* + * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER + */ + +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H +/* clang-format off */ + +#include + + + +/*==================== + Graphical settings + *====================*/ + +/* Maximal horizontal and vertical resolution to support by the library.*/ +#define LV_HOR_RES_MAX (480) +#define LV_VER_RES_MAX (320) + +/* Color depth: + * - 1: 1 byte per pixel + * - 8: RGB233 + * - 16: RGB565 + * - 32: ARGB8888 + */ +#define LV_COLOR_DEPTH 16 + +/* Swap the 2 bytes of RGB565 color. + * Useful if the display has a 8 bit interface (e.g. SPI)*/ +#define LV_COLOR_16_SWAP 0 + +/* 1: Enable screen transparency. + * Useful for OSD or other overlapping GUIs. + * Requires `LV_COLOR_DEPTH = 32` colors and the screen's style should be modified: `style.body.opa = ...`*/ +#define LV_COLOR_SCREEN_TRANSP 0 + +/*Images pixels with this color will not be drawn (with chroma keying)*/ +#define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/ + +/* Enable anti-aliasing (lines, and radiuses will be smoothed) */ +#define LV_ANTIALIAS 1 + +/* Default display refresh period. + * Can be changed in the display driver (`lv_disp_drv_t`).*/ +#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ + +/* Dot Per Inch: used to initialize default sizes. + * E.g. a button with width = LV_DPI / 2 -> half inch wide + * (Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI 100 /*[px]*/ + +/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */ +typedef int16_t lv_coord_t; + +/*========================= + Memory manager settings + *=========================*/ + +/* LittelvGL's internal memory manager's settings. + * The graphical objects and other related data are stored here. */ + +/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */ +#define LV_MEM_CUSTOM 0 +#if LV_MEM_CUSTOM == 0 +/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ +# define LV_MEM_SIZE (32U * 1024U) + +/* Complier prefix for a big array declaration */ +# define LV_MEM_ATTR + +/* Set an address for the memory pool instead of allocating it as an array. + * Can be in external SRAM too. */ +# define LV_MEM_ADR 0 + +/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */ +# define LV_MEM_AUTO_DEFRAG 1 +#else /*LV_MEM_CUSTOM*/ +# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ +# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/ +# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/ +#endif /*LV_MEM_CUSTOM*/ + +/* Garbage Collector settings + * Used if lvgl is binded to higher level language and the memory is managed by that language */ +#define LV_ENABLE_GC 0 +#if LV_ENABLE_GC != 0 +# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ +# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/ +# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/ +#endif /* LV_ENABLE_GC */ + +/*======================= + Input device settings + *=======================*/ + +/* Input device default settings. + * Can be changed in the Input device driver (`lv_indev_drv_t`)*/ + +/* Input device read period in milliseconds */ +#define LV_INDEV_DEF_READ_PERIOD 30 + +/* Drag threshold in pixels */ +#define LV_INDEV_DEF_DRAG_LIMIT 10 + +/* Drag throw slow-down in [%]. Greater value -> faster slow-down */ +#define LV_INDEV_DEF_DRAG_THROW 20 + +/* Long press time in milliseconds. + * Time to send `LV_EVENT_LONG_PRESSSED`) */ +#define LV_INDEV_DEF_LONG_PRESS_TIME 400 + +/* Repeated trigger period in long press [ms] + * Time between `LV_EVENT_LONG_PRESSED_REPEAT */ +#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100 + +/*================== + * Feature usage + *==================*/ + +/*1: Enable the Animations */ +#define LV_USE_ANIMATION 1 +#if LV_USE_ANIMATION + +/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_anim_user_data_t; + +#endif + +/* 1: Enable shadow drawing*/ +#define LV_USE_SHADOW 1 + +/* 1: Enable object groups (for keyboard/encoder navigation) */ +#define LV_USE_GROUP 1 +#if LV_USE_GROUP +typedef void * lv_group_user_data_t; +#endif /*LV_USE_GROUP*/ + +/* 1: Enable GPU interface*/ +#define LV_USE_GPU 1 + +/* 1: Enable file system (might be required for images */ +#define LV_USE_FILESYSTEM 1 +#if LV_USE_FILESYSTEM +/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_fs_drv_user_data_t; +#endif + +/*1: Add a `user_data` to drivers and objects*/ +#define LV_USE_USER_DATA 0 + +/*======================== + * Image decoder and cache + *========================*/ + +/* 1: Enable indexed (palette) images */ +#define LV_IMG_CF_INDEXED 1 + +/* 1: Enable alpha indexed images */ +#define LV_IMG_CF_ALPHA 1 + +/* Default image cache size. Image caching keeps the images opened. + * If only the built-in image formats are used there is no real advantage of caching. + * (I.e. no new image decoder is added) + * With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. + * However the opened images might consume additional RAM. + * LV_IMG_CACHE_DEF_SIZE must be >= 1 */ +#define LV_IMG_CACHE_DEF_SIZE 1 + +/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_img_decoder_user_data_t; + +/*===================== + * Compiler settings + *====================*/ +/* Define a custom attribute to `lv_tick_inc` function */ +#define LV_ATTRIBUTE_TICK_INC + +/* Define a custom attribute to `lv_task_handler` function */ +#define LV_ATTRIBUTE_TASK_HANDLER + +/* With size optimization (-Os) the compiler might not align data to + * 4 or 8 byte boundary. This alignment will be explicitly applied where needed. + * E.g. __attribute__((aligned(4))) */ +#define LV_ATTRIBUTE_MEM_ALIGN + +/* Attribute to mark large constant arrays for example + * font's bitmaps */ +#define LV_ATTRIBUTE_LARGE_CONST + +/*=================== + * HAL settings + *==================*/ + +/* 1: use a custom tick source. + * It removes the need to manually update the tick with `lv_tick_inc`) */ +#define LV_TICK_CUSTOM 0 +#if LV_TICK_CUSTOM == 1 +#define LV_TICK_CUSTOM_INCLUDE "something.h" /*Header for the sys time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current systime in ms*/ +#endif /*LV_TICK_CUSTOM*/ + +typedef void * lv_disp_drv_user_data_t; /*Type of user data in the display driver*/ +typedef void * lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/ + +/*================ + * Log settings + *===============*/ + +/*1: Enable the log module*/ +#define LV_USE_LOG 0 +#if LV_USE_LOG +/* How important log should be added: + * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + * LV_LOG_LEVEL_INFO Log important events + * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + * LV_LOG_LEVEL_NONE Do not log anything + */ +# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + +/* 1: Print the log with 'printf'; + * 0: user need to register a callback with `lv_log_register_print`*/ +# define LV_LOG_PRINTF 0 +#endif /*LV_USE_LOG*/ + +/*================ + * THEME USAGE + *================*/ +#define LV_THEME_LIVE_UPDATE 0 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/ + +#define LV_USE_THEME_TEMPL 0 /*Just for test*/ +#define LV_USE_THEME_DEFAULT 0 /*Built mainly from the built-in styles. Consumes very few RAM*/ +#define LV_USE_THEME_ALIEN 0 /*Dark futuristic theme*/ +#define LV_USE_THEME_NIGHT 0 /*Dark elegant theme*/ +#define LV_USE_THEME_MONO 0 /*Mono color theme for monochrome displays*/ +#define LV_USE_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/ +#define LV_USE_THEME_ZEN 0 /*Peaceful, mainly light theme */ +#define LV_USE_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/ + +/*================== + * FONT USAGE + *===================*/ + +/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel. + * The symbols are available via `LV_SYMBOL_...` defines + * More info about fonts: https://docs.littlevgl.com/#Fonts + * To create a new font go to: https://littlevgl.com/ttf-font-to-c-array + */ + +/* Robot fonts with bpp = 4 + * https://fonts.google.com/specimen/Roboto */ +#define LV_FONT_ROBOTO_12 0 +#define LV_FONT_ROBOTO_16 1 +#define LV_FONT_ROBOTO_22 0 +#define LV_FONT_ROBOTO_28 0 + +/*Pixel perfect monospace font + * http://pelulamu.net/unscii/ */ +#define LV_FONT_UNSCII_8 0 + +/* Optionally declare your custom fonts here. + * You can use these fonts as default font too + * and they will be available globally. E.g. + * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \ + * LV_FONT_DECLARE(my_font_2) + */ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font from the built-in fonts*/ +#define LV_FONT_DEFAULT &lv_font_roboto_16 + +/* Enable it if you have fonts with a lot of characters. + * The limit depends on the font size, font face and bpp + * but with > 10,000 characters if you see issues probably you need to enable it.*/ +#define LV_FONT_FMT_TXT_LARGE 0 + +/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_font_user_data_t; + +/*================= + * Text settings + *=================*/ + +/* Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + * */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + + /*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_" + +/*=================== + * LV_OBJ SETTINGS + *==================*/ + +/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_obj_user_data_t; + +/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ +#define LV_USE_OBJ_REALIGN 1 + +/* Enable to make the object clickable on a larger area. + * LV_EXT_CLICK_AREA_OFF or 0: Disable this feature + * LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px) + * LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px) + */ +#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_OFF + +/*================== + * LV OBJ X USAGE + *================*/ +/* + * Documentation of the object types: https://docs.littlevgl.com/#Object-types + */ + +/*Arc (dependencies: -)*/ +#define LV_USE_ARC 1 + +/*Bar (dependencies: -)*/ +#define LV_USE_BAR 1 + +/*Button (dependencies: lv_cont*/ +#define LV_USE_BTN 1 +#if LV_USE_BTN != 0 +/*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/ +# define LV_BTN_INK_EFFECT 0 +#endif + +/*Button matrix (dependencies: -)*/ +#define LV_USE_BTNM 1 + +/*Calendar (dependencies: -)*/ +#define LV_USE_CALENDAR 1 + +/*Canvas (dependencies: lv_img)*/ +#define LV_USE_CANVAS 1 + +/*Check box (dependencies: lv_btn, lv_label)*/ +#define LV_USE_CB 1 + +/*Chart (dependencies: -)*/ +#define LV_USE_CHART 1 +#if LV_USE_CHART +# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 20 +#endif + +/*Container (dependencies: -*/ +#define LV_USE_CONT 1 + +/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ +#define LV_USE_DDLIST 1 +#if LV_USE_DDLIST != 0 +/*Open and close default animation time [ms] (0: no animation)*/ +# define LV_DDLIST_DEF_ANIM_TIME 200 +#endif + +/*Gauge (dependencies:lv_bar, lv_lmeter)*/ +#define LV_USE_GAUGE 1 + +/*Image (dependencies: lv_label*/ +#define LV_USE_IMG 1 + +/*Image Button (dependencies: lv_btn*/ +#define LV_USE_IMGBTN 1 +#if LV_USE_IMGBTN +/*1: The imgbtn requires left, mid and right parts and the width can be set freely*/ +# define LV_IMGBTN_TILED 0 +#endif + +/*Keyboard (dependencies: lv_btnm)*/ +#define LV_USE_KB 1 + +/*Label (dependencies: -*/ +#define LV_USE_LABEL 1 +#if LV_USE_LABEL != 0 +/*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_ROLL/ROLL_CIRC' mode*/ +# define LV_LABEL_DEF_SCROLL_SPEED 25 + +/* Waiting period at beginning/end of animation cycle */ +# define LV_LABEL_WAIT_CHAR_COUNT 3 + +/*Enable selecting text of the label */ +# define LV_LABEL_TEXT_SEL 0 + +/*Store extra some info in labels (12 bytes) to speed up drawing of very long texts*/ +# define LV_LABEL_LONG_TXT_HINT 0 +#endif + +/*LED (dependencies: -)*/ +#define LV_USE_LED 1 + +/*Line (dependencies: -*/ +#define LV_USE_LINE 1 + +/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/ +#define LV_USE_LIST 1 +#if LV_USE_LIST != 0 +/*Default animation time of focusing to a list element [ms] (0: no animation) */ +# define LV_LIST_DEF_ANIM_TIME 100 +#endif + +/*Line meter (dependencies: *;)*/ +#define LV_USE_LMETER 1 + +/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/ +#define LV_USE_MBOX 1 + +/*Page (dependencies: lv_cont)*/ +#define LV_USE_PAGE 1 +#if LV_USE_PAGE != 0 +/*Focus default animation time [ms] (0: no animation)*/ +# define LV_PAGE_DEF_ANIM_TIME 400 +#endif + +/*Preload (dependencies: lv_arc, lv_anim)*/ +#define LV_USE_PRELOAD 1 +#if LV_USE_PRELOAD != 0 +# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ +# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ +# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC +#endif + +/*Roller (dependencies: lv_ddlist)*/ +#define LV_USE_ROLLER 1 +#if LV_USE_ROLLER != 0 +/*Focus animation time [ms] (0: no animation)*/ +# define LV_ROLLER_DEF_ANIM_TIME 200 + +/*Number of extra "pages" when the roller is infinite*/ +# define LV_ROLLER_INF_PAGES 7 +#endif + +/*Slider (dependencies: lv_bar)*/ +#define LV_USE_SLIDER 1 + +/*Spinbox (dependencies: lv_ta)*/ +#define LV_USE_SPINBOX 1 + +/*Switch (dependencies: lv_slider)*/ +#define LV_USE_SW 1 + +/*Text area (dependencies: lv_label, lv_page)*/ +#define LV_USE_TA 1 +#if LV_USE_TA != 0 +# define LV_TA_DEF_CURSOR_BLINK_TIME 400 /*ms*/ +# define LV_TA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +/*Table (dependencies: lv_label)*/ +#define LV_USE_TABLE 1 +#if LV_USE_TABLE +# define LV_TABLE_COL_MAX 12 +#endif + +/*Tab (dependencies: lv_page, lv_btnm)*/ +#define LV_USE_TABVIEW 1 +# if LV_USE_TABVIEW != 0 +/*Time of slide animation [ms] (0: no animation)*/ +# define LV_TABVIEW_DEF_ANIM_TIME 300 +#endif + +/*Tileview (dependencies: lv_page) */ +#define LV_USE_TILEVIEW 1 +#if LV_USE_TILEVIEW +/*Time of slide animation [ms] (0: no animation)*/ +# define LV_TILEVIEW_DEF_ANIM_TIME 300 +#endif + +/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/ +#define LV_USE_WIN 1 + +/*================== + * Non-user section + *==================*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/ +# define _CRT_SECURE_NO_WARNINGS +#endif + +/*--END OF LV_CONF_H--*/ + +/*Be sure every define has a default value*/ +//#include "../lv_conf_checker.h" + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/core/app-framework/wgl/app/wa-inc/lvgl/LICENCE.txt b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/LICENCE.txt new file mode 100644 index 0000000..beaef1d --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/LICENCE.txt @@ -0,0 +1,8 @@ +MIT licence +Copyright (c) 2016 Gábor Kiss-Vámosi + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lv_obj.h b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lv_obj.h new file mode 100644 index 0000000..c79838e --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lv_obj.h @@ -0,0 +1,916 @@ +/** + * @file lv_obj.h + * + */ + +#ifndef LV_OBJ_H +#define LV_OBJ_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../../lv_conf.h" +#endif + +#include +#include +#include "lv_style.h" +#include "../lv_misc/lv_types.h" +#include "../lv_misc/lv_area.h" +#include "../lv_misc/lv_mem.h" +#include "../lv_misc/lv_ll.h" +#include "../lv_misc/lv_color.h" +#include "../lv_misc/lv_log.h" +#include "../lv_hal/lv_hal.h" + +/********************* + * DEFINES + *********************/ + +/*Error check of lv_conf.h*/ +#if LV_HOR_RES_MAX == 0 || LV_VER_RES_MAX == 0 +#error "LittlevGL: LV_HOR_RES_MAX and LV_VER_RES_MAX must be greater than 0" +#endif + +#if LV_ANTIALIAS > 1 +#error "LittlevGL: LV_ANTIALIAS can be only 0 or 1" +#endif + +#define LV_MAX_ANCESTOR_NUM 8 + +#define LV_EXT_CLICK_AREA_OFF 0 +#define LV_EXT_CLICK_AREA_TINY 1 +#define LV_EXT_CLICK_AREA_FULL 2 + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_obj_t; + + +/** Design modes */ +enum { + LV_DESIGN_DRAW_MAIN, /**< Draw the main portion of the object */ + LV_DESIGN_DRAW_POST, /**< Draw extras on the object */ + LV_DESIGN_COVER_CHK, /**< Check if the object fully covers the 'mask_p' area */ +}; +typedef uint8_t lv_design_mode_t; + +/** + * The design callback is used to draw the object on the screen. + * It accepts the object, a mask area, and the mode in which to draw the object. + */ +typedef bool (*lv_design_cb_t)(struct _lv_obj_t * obj, const lv_area_t * mask_p, lv_design_mode_t mode); + +enum { + LV_EVENT_PRESSED, /**< The object has been pressed*/ + LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/ + LV_EVENT_PRESS_LOST, /**< User is still pressing but slid cursor/finger off of the object */ + LV_EVENT_SHORT_CLICKED, /**< User pressed object for a short period of time, then released it. Not called if dragged. */ + LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `LV_INDEV_LONG_PRESS_TIME`. Not called if dragged.*/ + LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `LV_INDEV_LONG_PRESS_TIME` in every + `LV_INDEV_LONG_PRESS_REP_TIME` ms. Not called if dragged.*/ + LV_EVENT_CLICKED, /**< Called on release if not dragged (regardless to long press)*/ + LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/ + LV_EVENT_DRAG_BEGIN, + LV_EVENT_DRAG_END, + LV_EVENT_DRAG_THROW_BEGIN, + LV_EVENT_KEY, + LV_EVENT_FOCUSED, + LV_EVENT_DEFOCUSED, + LV_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved) */ + LV_EVENT_INSERT, + LV_EVENT_REFRESH, + LV_EVENT_APPLY, /**< "Ok", "Apply" or similar specific button has clicked*/ + LV_EVENT_CANCEL, /**< "Close", "Cancel" or similar specific button has clicked*/ + LV_EVENT_DELETE, /**< Object is being deleted */ +}; +typedef uint8_t lv_event_t; /**< Type of event being sent to the object. */ + +/** + * @brief Event callback. + * Events are used to notify the user of some action being taken on the object. + * For details, see ::lv_event_t. + */ +typedef void (*lv_event_cb_t)(struct _lv_obj_t * obj, lv_event_t event); + +/** Signals are for use by the object itself or to extend the object's functionality. + * Applications should use ::lv_obj_set_event_cb to be notified of events that occur + * on the object. */ +enum { + /*General signals*/ + LV_SIGNAL_CLEANUP, /**< Object is being deleted */ + LV_SIGNAL_CHILD_CHG, /**< Child was removed/added */ + LV_SIGNAL_CORD_CHG, /**< Object coordinates/size have changed */ + LV_SIGNAL_PARENT_SIZE_CHG, /**< Parent's size has changed */ + LV_SIGNAL_STYLE_CHG, /**< Object's style has changed */ + LV_SIGNAL_REFR_EXT_DRAW_PAD, /**< Object's extra padding has changed */ + LV_SIGNAL_GET_TYPE, /**< LittlevGL needs to retrieve the object's type */ + + /*Input device related*/ + LV_SIGNAL_PRESSED, /**< The object has been pressed*/ + LV_SIGNAL_PRESSING, /**< The object is being pressed (called continuously while pressing)*/ + LV_SIGNAL_PRESS_LOST, /**< User is still pressing but slid cursor/finger off of the object */ + LV_SIGNAL_RELEASED, /**< User pressed object for a short period of time, then released it. Not called if dragged. */ + LV_SIGNAL_LONG_PRESS, /**< Object has been pressed for at least `LV_INDEV_LONG_PRESS_TIME`. Not called if dragged.*/ + LV_SIGNAL_LONG_PRESS_REP, /**< Called after `LV_INDEV_LONG_PRESS_TIME` in every `LV_INDEV_LONG_PRESS_REP_TIME` ms. Not called if dragged.*/ + LV_SIGNAL_DRAG_BEGIN, + LV_SIGNAL_DRAG_END, + /*Group related*/ + LV_SIGNAL_FOCUS, + LV_SIGNAL_DEFOCUS, + LV_SIGNAL_CONTROL, + LV_SIGNAL_GET_EDITABLE, +}; +typedef uint8_t lv_signal_t; + +typedef lv_res_t (*lv_signal_cb_t)(struct _lv_obj_t * obj, lv_signal_t sign, void * param); + +/** Object alignment. */ +enum { + LV_ALIGN_CENTER = 0, + LV_ALIGN_IN_TOP_LEFT, + LV_ALIGN_IN_TOP_MID, + LV_ALIGN_IN_TOP_RIGHT, + LV_ALIGN_IN_BOTTOM_LEFT, + LV_ALIGN_IN_BOTTOM_MID, + LV_ALIGN_IN_BOTTOM_RIGHT, + LV_ALIGN_IN_LEFT_MID, + LV_ALIGN_IN_RIGHT_MID, + LV_ALIGN_OUT_TOP_LEFT, + LV_ALIGN_OUT_TOP_MID, + LV_ALIGN_OUT_TOP_RIGHT, + LV_ALIGN_OUT_BOTTOM_LEFT, + LV_ALIGN_OUT_BOTTOM_MID, + LV_ALIGN_OUT_BOTTOM_RIGHT, + LV_ALIGN_OUT_LEFT_TOP, + LV_ALIGN_OUT_LEFT_MID, + LV_ALIGN_OUT_LEFT_BOTTOM, + LV_ALIGN_OUT_RIGHT_TOP, + LV_ALIGN_OUT_RIGHT_MID, + LV_ALIGN_OUT_RIGHT_BOTTOM, +}; +typedef uint8_t lv_align_t; + +#if LV_USE_OBJ_REALIGN +typedef struct +{ + const struct _lv_obj_t * base; + lv_coord_t xofs; + lv_coord_t yofs; + lv_align_t align; + uint8_t auto_realign : 1; + uint8_t origo_align : 1; /**< 1: the origo (center of the object) was aligned with + `lv_obj_align_origo`*/ +} lv_reailgn_t; +#endif + +enum { + LV_DRAG_DIR_HOR = 0x1, /**< Object can be dragged horizontally. */ + LV_DRAG_DIR_VER = 0x2, /**< Object can be dragged vertically. */ + LV_DRAG_DIR_ALL = 0x3, /**< Object can be dragged in all directions. */ +}; + +typedef uint8_t lv_drag_dir_t; + +typedef void lv_obj_t; +typedef void lv_obj_type_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Init. the 'lv' library. + */ +void lv_init(void); + +/*-------------------- + * Create and delete + *-------------------*/ + +/** + * Create a basic object + * @param parent pointer to a parent object. + * If NULL then a screen will be created + * @param copy pointer to a base object, if not NULL then the new object will be copied from it + * @return pointer to the new object + */ +lv_obj_t * lv_obj_create(lv_obj_t * parent, const lv_obj_t * copy); + +/** + * Delete 'obj' and all of its children + * @param obj pointer to an object to delete + * @return LV_RES_INV because the object is deleted + */ +lv_res_t lv_obj_del(lv_obj_t * obj); + +/** + * Helper function for asynchronously deleting objects. + * Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent). + * @param obj object to delete + * @see lv_async_call + */ +void lv_obj_del_async(struct _lv_obj_t *obj); + +/** + * Delete all children of an object + * @param obj pointer to an object + */ +void lv_obj_clean(lv_obj_t * obj); + +/** + * Mark the object as invalid therefore its current position will be redrawn by 'lv_refr_task' + * @param obj pointer to an object + */ +void lv_obj_invalidate(const lv_obj_t * obj); + +/*===================== + * Setter functions + *====================*/ + +/*-------------------- + * Parent/children set + *--------------------*/ + +/** + * Set a new parent for an object. Its relative position will be the same. + * @param obj pointer to an object. Can't be a screen. + * @param parent pointer to the new parent object. (Can't be NULL) + */ +void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent); + +/** + * Move and object to the foreground + * @param obj pointer to an object + */ +void lv_obj_move_foreground(lv_obj_t * obj); + +/** + * Move and object to the background + * @param obj pointer to an object + */ +void lv_obj_move_background(lv_obj_t * obj); + +/*-------------------- + * Coordinate set + * ------------------*/ + +/** + * Set relative the position of an object (relative to the parent) + * @param obj pointer to an object + * @param x new distance from the left side of the parent + * @param y new distance from the top of the parent + */ +void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y); + +/** + * Set the x coordinate of a object + * @param obj pointer to an object + * @param x new distance from the left side from the parent + */ +void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x); + +/** + * Set the y coordinate of a object + * @param obj pointer to an object + * @param y new distance from the top of the parent + */ +void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y); + +/** + * Set the size of an object + * @param obj pointer to an object + * @param w new width + * @param h new height + */ +void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h); + +/** + * Set the width of an object + * @param obj pointer to an object + * @param w new width + */ +void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w); + +/** + * Set the height of an object + * @param obj pointer to an object + * @param h new height + */ +void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h); + +/** + * Align an object to an other object. + * @param obj pointer to an object to align + * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it. + * @param align type of alignment (see 'lv_align_t' enum) + * @param x_mod x coordinate shift after alignment + * @param y_mod y coordinate shift after alignment + */ +void lv_obj_align(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod); + +/** + * Align an object to an other object. + * @param obj pointer to an object to align + * @param base pointer to an object (if NULL the parent is used). 'obj' will be aligned to it. + * @param align type of alignment (see 'lv_align_t' enum) + * @param x_mod x coordinate shift after alignment + * @param y_mod y coordinate shift after alignment + */ +void lv_obj_align_origo(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_mod, lv_coord_t y_mod); + +/** + * Realign the object based on the last `lv_obj_align` parameters. + * @param obj pointer to an object + */ +void lv_obj_realign(lv_obj_t * obj); + +/** + * Enable the automatic realign of the object when its size has changed based on the last + * `lv_obj_align` parameters. + * @param obj pointer to an object + * @param en true: enable auto realign; false: disable auto realign + */ +void lv_obj_set_auto_realign(lv_obj_t * obj, bool en); + +/** + * Set the size of an extended clickable area + * @param obj pointer to an object + * @param left extended clickable are on the left [px] + * @param right extended clickable are on the right [px] + * @param top extended clickable are on the top [px] + * @param bottom extended clickable are on the bottom [px] + */ +void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t left, lv_coord_t right, lv_coord_t top, lv_coord_t bottom); + +/*--------------------- + * Appearance set + *--------------------*/ + +/** + * Set a new style for an object + * @param obj pointer to an object + * @param style_p pointer to the new style + */ +void lv_obj_set_style(lv_obj_t * obj, const lv_style_t * style); + +/** + * Notify an object about its style is modified + * @param obj pointer to an object + */ +void lv_obj_refresh_style(lv_obj_t * obj); + +/** + * Notify all object if a style is modified + * @param style pointer to a style. Only the objects with this style will be notified + * (NULL to notify all objects) + */ +void lv_obj_report_style_mod(lv_style_t * style); + +/*----------------- + * Attribute set + *----------------*/ + +/** + * Hide an object. It won't be visible and clickable. + * @param obj pointer to an object + * @param en true: hide the object + */ +void lv_obj_set_hidden(lv_obj_t * obj, bool en); + +/** + * Enable or disable the clicking of an object + * @param obj pointer to an object + * @param en true: make the object clickable + */ +void lv_obj_set_click(lv_obj_t * obj, bool en); + +/** + * Enable to bring this object to the foreground if it + * or any of its children is clicked + * @param obj pointer to an object + * @param en true: enable the auto top feature + */ +void lv_obj_set_top(lv_obj_t * obj, bool en); + +/** + * Enable the dragging of an object + * @param obj pointer to an object + * @param en true: make the object dragable + */ +void lv_obj_set_drag(lv_obj_t * obj, bool en); + +/** + * Set the directions an object can be dragged in + * @param obj pointer to an object + * @param drag_dir bitwise OR of allowed drag directions + */ +void lv_obj_set_drag_dir(lv_obj_t * obj, lv_drag_dir_t drag_dir); + +/** + * Enable the throwing of an object after is is dragged + * @param obj pointer to an object + * @param en true: enable the drag throw + */ +void lv_obj_set_drag_throw(lv_obj_t * obj, bool en); + +/** + * Enable to use parent for drag related operations. + * If trying to drag the object the parent will be moved instead + * @param obj pointer to an object + * @param en true: enable the 'drag parent' for the object + */ +void lv_obj_set_drag_parent(lv_obj_t * obj, bool en); + +/** + * Propagate the events to the parent too + * @param obj pointer to an object + * @param en true: enable the event propagation + */ +void lv_obj_set_parent_event(lv_obj_t * obj, bool en); + +/** + * Set the opa scale enable parameter (required to set opa_scale with `lv_obj_set_opa_scale()`) + * @param obj pointer to an object + * @param en true: opa scaling is enabled for this object and all children; false: no opa scaling + */ +void lv_obj_set_opa_scale_enable(lv_obj_t * obj, bool en); + +/** + * Set the opa scale of an object. + * The opacity of this object and all it's children will be scaled down with this factor. + * `lv_obj_set_opa_scale_enable(obj, true)` needs to be called to enable it. + * (not for all children just for the parent where to start the opa scaling) + * @param obj pointer to an object + * @param opa_scale a factor to scale down opacity [0..255] + */ +void lv_obj_set_opa_scale(lv_obj_t * obj, lv_opa_t opa_scale); + +/** + * Set a bit or bits in the protect filed + * @param obj pointer to an object + * @param prot 'OR'-ed values from `lv_protect_t` + */ +void lv_obj_set_protect(lv_obj_t * obj, uint8_t prot); + +/** + * Clear a bit or bits in the protect filed + * @param obj pointer to an object + * @param prot 'OR'-ed values from `lv_protect_t` + */ +void lv_obj_clear_protect(lv_obj_t * obj, uint8_t prot); + +/** + * Set a an event handler function for an object. + * Used by the user to react on event which happens with the object. + * @param obj pointer to an object + * @param event_cb the new event function + */ +void lv_obj_set_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb); + +/** + * Send an event to the object + * @param obj pointer to an object + * @param event the type of the event from `lv_event_t`. + * @param data arbitrary data depending on the object type and the event. (Usually `NULL`) + * @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event + */ +lv_res_t lv_event_send(lv_obj_t * obj, lv_event_t event, const void * data); + +/** + * Call an event function with an object, event, and data. + * @param event_xcb an event callback function. If `NULL` `LV_RES_OK` will return without any actions. + * (the 'x' in the argument name indicates that its not a fully generic function because it not follows + * the `func_name(object, callback, ...)` convention) + * @param obj pointer to an object to associate with the event (can be `NULL` to simply call the `event_cb`) + * @param event an event + * @param data pointer to a custom data + * @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event + */ +lv_res_t lv_event_send_func(lv_event_cb_t event_xcb, lv_obj_t * obj, lv_event_t event, const void * data); + +/** + * Get the `data` parameter of the current event + * @return the `data` parameter + */ +const void * lv_event_get_data(void); + +/** + * Set the a signal function of an object. Used internally by the library. + * Always call the previous signal function in the new. + * @param obj pointer to an object + * @param signal_cb the new signal function + */ +void lv_obj_set_signal_cb(lv_obj_t * obj, lv_signal_cb_t signal_cb); + +/** + * Send an event to the object + * @param obj pointer to an object + * @param event the type of the event from `lv_event_t`. + */ +void lv_signal_send(lv_obj_t * obj, lv_signal_t signal, void * param); + +/** + * Set a new design function for an object + * @param obj pointer to an object + * @param design_cb the new design function + */ +void lv_obj_set_design_cb(lv_obj_t * obj, lv_design_cb_t design_cb); + +/*---------------- + * Other set + *--------------*/ + +/** + * Allocate a new ext. data for an object + * @param obj pointer to an object + * @param ext_size the size of the new ext. data + * @return pointer to the allocated ext + */ +void * lv_obj_allocate_ext_attr(lv_obj_t * obj, uint16_t ext_size); + +/** + * Send a 'LV_SIGNAL_REFR_EXT_SIZE' signal to the object + * @param obj pointer to an object + */ +void lv_obj_refresh_ext_draw_pad(lv_obj_t * obj); + +/*======================= + * Getter functions + *======================*/ + +/** + * Return with the screen of an object + * @param obj pointer to an object + * @return pointer to a screen + */ +lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj); + +/** + * Get the display of an object + * @param scr pointer to an object + * @return pointer the object's display + */ +lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj); + +/*--------------------- + * Parent/children get + *--------------------*/ + +/** + * Returns with the parent of an object + * @param obj pointer to an object + * @return pointer to the parent of 'obj' + */ +lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj); + +/** + * Iterate through the children of an object (start from the "youngest, lastly created") + * @param obj pointer to an object + * @param child NULL at first call to get the next children + * and the previous return value later + * @return the child after 'act_child' or NULL if no more child + */ +lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, const lv_obj_t * child); + +/** + * Iterate through the children of an object (start from the "oldest", firstly created) + * @param obj pointer to an object + * @param child NULL at first call to get the next children + * and the previous return value later + * @return the child after 'act_child' or NULL if no more child + */ +lv_obj_t * lv_obj_get_child_back(const lv_obj_t * obj, const lv_obj_t * child); + +/** + * Count the children of an object (only children directly on 'obj') + * @param obj pointer to an object + * @return children number of 'obj' + */ +uint16_t lv_obj_count_children(const lv_obj_t * obj); + +/** Recursively count the children of an object + * @param obj pointer to an object + * @return children number of 'obj' + */ +uint16_t lv_obj_count_children_recursive(const lv_obj_t * obj); + +/*--------------------- + * Coordinate get + *--------------------*/ + +/** + * Copy the coordinates of an object to an area + * @param obj pointer to an object + * @param cords_p pointer to an area to store the coordinates + */ +void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * cords_p); + +/** + * Reduce area retried by `lv_obj_get_coords()` the get graphically usable area of an object. + * (Without the size of the border or other extra graphical elements) + * @param coords_p store the result area here + */ +void lv_obj_get_inner_coords(const lv_obj_t * obj, lv_area_t * coords_p); + +/** + * Get the x coordinate of object + * @param obj pointer to an object + * @return distance of 'obj' from the left side of its parent + */ +lv_coord_t lv_obj_get_x(const lv_obj_t * obj); + +/** + * Get the y coordinate of object + * @param obj pointer to an object + * @return distance of 'obj' from the top of its parent + */ +lv_coord_t lv_obj_get_y(const lv_obj_t * obj); + +/** + * Get the width of an object + * @param obj pointer to an object + * @return the width + */ +lv_coord_t lv_obj_get_width(const lv_obj_t * obj); + +/** + * Get the height of an object + * @param obj pointer to an object + * @return the height + */ +lv_coord_t lv_obj_get_height(const lv_obj_t * obj); + +/** + * Get that width reduced by the left and right padding. + * @param obj pointer to an object + * @return the width which still fits into the container + */ +lv_coord_t lv_obj_get_width_fit(lv_obj_t * obj); + +/** + * Get that height reduced by the top an bottom padding. + * @param obj pointer to an object + * @return the height which still fits into the container + */ +lv_coord_t lv_obj_get_height_fit(lv_obj_t * obj); + +/** + * Get the automatic realign property of the object. + * @param obj pointer to an object + * @return true: auto realign is enabled; false: auto realign is disabled + */ +bool lv_obj_get_auto_realign(lv_obj_t * obj); + +/** + * Get the left padding of extended clickable area + * @param obj pointer to an object + * @return the extended left padding + */ +lv_coord_t lv_obj_get_ext_click_pad_left(const lv_obj_t * obj); + +/** + * Get the right padding of extended clickable area + * @param obj pointer to an object + * @return the extended right padding + */ +lv_coord_t lv_obj_get_ext_click_pad_right(const lv_obj_t * obj); + +/** + * Get the top padding of extended clickable area + * @param obj pointer to an object + * @return the extended top padding + */ +lv_coord_t lv_obj_get_ext_click_pad_top(const lv_obj_t * obj); + +/** + * Get the bottom padding of extended clickable area + * @param obj pointer to an object + * @return the extended bottom padding + */ +lv_coord_t lv_obj_get_ext_click_pad_bottom(const lv_obj_t * obj); + +/** + * Get the extended size attribute of an object + * @param obj pointer to an object + * @return the extended size attribute + */ +lv_coord_t lv_obj_get_ext_draw_pad(const lv_obj_t * obj); + +/*----------------- + * Appearance get + *---------------*/ + +/** + * Get the style pointer of an object (if NULL get style of the parent) + * @param obj pointer to an object + * @return pointer to a style + */ +const lv_style_t * lv_obj_get_style(const lv_obj_t * obj); + +/*----------------- + * Attribute get + *----------------*/ + +/** + * Get the hidden attribute of an object + * @param obj pointer to an object + * @return true: the object is hidden + */ +bool lv_obj_get_hidden(const lv_obj_t * obj); + +/** + * Get the click enable attribute of an object + * @param obj pointer to an object + * @return true: the object is clickable + */ +bool lv_obj_get_click(const lv_obj_t * obj); + +/** + * Get the top enable attribute of an object + * @param obj pointer to an object + * @return true: the auto top feature is enabled + */ +bool lv_obj_get_top(const lv_obj_t * obj); + +/** + * Get the drag enable attribute of an object + * @param obj pointer to an object + * @return true: the object is dragable + */ +bool lv_obj_get_drag(const lv_obj_t * obj); + +/** + * Get the directions an object can be dragged + * @param obj pointer to an object + * @return bitwise OR of allowed directions an object can be dragged in + */ +lv_drag_dir_t lv_obj_get_drag_dir(const lv_obj_t * obj); + +/** + * Get the drag throw enable attribute of an object + * @param obj pointer to an object + * @return true: drag throw is enabled + */ +bool lv_obj_get_drag_throw(const lv_obj_t * obj); + +/** + * Get the drag parent attribute of an object + * @param obj pointer to an object + * @return true: drag parent is enabled + */ +bool lv_obj_get_drag_parent(const lv_obj_t * obj); + +/** + * Get the drag parent attribute of an object + * @param obj pointer to an object + * @return true: drag parent is enabled + */ +bool lv_obj_get_parent_event(const lv_obj_t * obj); + +/** + * Get the opa scale enable parameter + * @param obj pointer to an object + * @return true: opa scaling is enabled for this object and all children; false: no opa scaling + */ +lv_opa_t lv_obj_get_opa_scale_enable(const lv_obj_t * obj); + +/** + * Get the opa scale parameter of an object + * @param obj pointer to an object + * @return opa scale [0..255] + */ +lv_opa_t lv_obj_get_opa_scale(const lv_obj_t * obj); + +/** + * Get the protect field of an object + * @param obj pointer to an object + * @return protect field ('OR'ed values of `lv_protect_t`) + */ +uint8_t lv_obj_get_protect(const lv_obj_t * obj); + +/** + * Check at least one bit of a given protect bitfield is set + * @param obj pointer to an object + * @param prot protect bits to test ('OR'ed values of `lv_protect_t`) + * @return false: none of the given bits are set, true: at least one bit is set + */ +bool lv_obj_is_protected(const lv_obj_t * obj, uint8_t prot); + +/** + * Get the signal function of an object + * @param obj pointer to an object + * @return the signal function + */ +lv_signal_cb_t lv_obj_get_signal_cb(const lv_obj_t * obj); + +/** + * Get the design function of an object + * @param obj pointer to an object + * @return the design function + */ +lv_design_cb_t lv_obj_get_design_cb(const lv_obj_t * obj); + +/** + * Get the event function of an object + * @param obj pointer to an object + * @return the event function + */ +lv_event_cb_t lv_obj_get_event_cb(const lv_obj_t * obj); + +/*------------------ + * Other get + *-----------------*/ + +/** + * Get the ext pointer + * @param obj pointer to an object + * @return the ext pointer but not the dynamic version + * Use it as ext->data1, and NOT da(ext)->data1 + */ +void * lv_obj_get_ext_attr(const lv_obj_t * obj); + +/** + * Get object's and its ancestors type. Put their name in `type_buf` starting with the current type. + * E.g. buf.type[0]="lv_btn", buf.type[1]="lv_cont", buf.type[2]="lv_obj" + * @param obj pointer to an object which type should be get + * @param buf pointer to an `lv_obj_type_t` buffer to store the types + */ +void lv_obj_get_type(lv_obj_t * obj, lv_obj_type_t * buf); + +#if LV_USE_USER_DATA +/** + * Get the object's user data + * @param obj pointer to an object + * @return user data + */ +lv_obj_user_data_t lv_obj_get_user_data(lv_obj_t * obj); + +/** + * Get a pointer to the object's user data + * @param obj pointer to an object + * @return pointer to the user data + */ +lv_obj_user_data_t * lv_obj_get_user_data_ptr(lv_obj_t * obj); + +/** + * Set the object's user data. The data will be copied. + * @param obj pointer to an object + * @param data user data + */ +void lv_obj_set_user_data(lv_obj_t * obj, lv_obj_user_data_t data); + +#endif + +#if LV_USE_GROUP +/** + * Get the group of the object + * @param obj pointer to an object + * @return the pointer to group of the object + */ +void * lv_obj_get_group(const lv_obj_t * obj); + +/** + * Tell whether the object is the focused object of a group or not. + * @param obj pointer to an object + * @return true: the object is focused, false: the object is not focused or not in a group + */ +bool lv_obj_is_focused(const lv_obj_t * obj); + +#endif + +/********************** + * MACROS + **********************/ + +/** + * Helps to quickly declare an event callback function. + * Will be expanded to: `void (lv_obj_t * obj, lv_event_t e)` + * + * Examples: + * static LV_EVENT_CB_DECLARE(my_event1); //Protoype declaration + * + * static LV_EVENT_CB_DECLARE(my_event1) + * { + * if(e == LV_EVENT_CLICKED) { + * lv_obj_set_hidden(obj ,true); + * } + * } + */ +#define LV_EVENT_CB_DECLARE(name) void name(lv_obj_t * obj, lv_event_t e) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_OBJ_H*/ diff --git a/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lvgl.h b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lvgl.h new file mode 100644 index 0000000..c70dd49 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/lvgl.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H +#define WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +//#include "bi-inc/wgl_shared_utils.h" /* shared types between app and native */ +/* +#include "lvgl-compatible/lv_types.h" +#include "lvgl-compatible/lv_obj.h" +#include "lvgl-compatible/lv_btn.h" +#include "lvgl-compatible/lv_cb.h" +#include "lvgl-compatible/lv_label.h" +#include "lvgl-compatible/lv_list.h" +*/ + + + + +#include "src/lv_version.h" + +#include "src/lv_misc/lv_log.h" +#include "src/lv_misc/lv_task.h" +#include "src/lv_misc/lv_math.h" +//#include "src/lv_misc/lv_async.h" + +//#include "src/lv_hal/lv_hal.h" + +#include "src/lv_core/lv_obj.h" +#include "src/lv_core/lv_group.h" + +#include "src/lv_core/lv_refr.h" +#include "src/lv_core/lv_disp.h" + +#include "src/lv_themes/lv_theme.h" + +#include "src/lv_font/lv_font.h" +#include "src/lv_font/lv_font_fmt_txt.h" + +#include "src/lv_objx/lv_btn.h" +#include "src/lv_objx/lv_imgbtn.h" +#include "src/lv_objx/lv_img.h" +#include "src/lv_objx/lv_label.h" +#include "src/lv_objx/lv_line.h" +#include "src/lv_objx/lv_page.h" +#include "src/lv_objx/lv_cont.h" +#include "src/lv_objx/lv_list.h" +#include "src/lv_objx/lv_chart.h" +#include "src/lv_objx/lv_table.h" +#include "src/lv_objx/lv_cb.h" +#include "src/lv_objx/lv_bar.h" +#include "src/lv_objx/lv_slider.h" +#include "src/lv_objx/lv_led.h" +#include "src/lv_objx/lv_btnm.h" +#include "src/lv_objx/lv_kb.h" +#include "src/lv_objx/lv_ddlist.h" +#include "src/lv_objx/lv_roller.h" +#include "src/lv_objx/lv_ta.h" +#include "src/lv_objx/lv_canvas.h" +#include "src/lv_objx/lv_win.h" +#include "src/lv_objx/lv_tabview.h" +#include "src/lv_objx/lv_tileview.h" +#include "src/lv_objx/lv_mbox.h" +#include "src/lv_objx/lv_gauge.h" +#include "src/lv_objx/lv_lmeter.h" +#include "src/lv_objx/lv_sw.h" +#include "src/lv_objx/lv_kb.h" +#include "src/lv_objx/lv_arc.h" +#include "src/lv_objx/lv_preload.h" +#include "src/lv_objx/lv_calendar.h" +#include "src/lv_objx/lv_spinbox.h" + +#include "src/lv_draw/lv_img_cache.h" + +#ifdef __cplusplus +} +#endif + +#endif /* WAMR_GRAPHIC_LIBRARY_LVGL_COMPATIBLE_H */ diff --git a/wamr/core/app-framework/wgl/app/wa-inc/lvgl/test.c b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/test.c new file mode 100644 index 0000000..ceafbd5 --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wa-inc/lvgl/test.c @@ -0,0 +1,11 @@ +#include "lvgl.h" + + +int main() + +{ + + + return 0; + +} diff --git a/wamr/core/app-framework/wgl/app/wasm_app.cmake b/wamr/core/app-framework/wgl/app/wasm_app.cmake new file mode 100644 index 0000000..f01be9f --- /dev/null +++ b/wamr/core/app-framework/wgl/app/wasm_app.cmake @@ -0,0 +1,19 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (WASM_APP_GUI_DIR ${CMAKE_CURRENT_LIST_DIR}) + +set (DEPS_DIR ${WASM_APP_GUI_DIR}/../../../deps) + +if (NOT EXISTS "${DEPS_DIR}/lvgl") + message (FATAL_ERROR "Can not find third party dependency: ${DEPS_DIR}/lvgl") +endif () + +include_directories(${WASM_APP_GUI_DIR} + ${DEPS_DIR} + ${DEPS_DIR}/lvgl + ${DEPS_DIR}/lvgl/src) + +file (GLOB_RECURSE source_all ${WASM_APP_GUI_DIR}/src/*.c) + +set (WASM_APP_CURRENT_SOURCE ${source_all}) diff --git a/wamr/core/app-framework/wgl/native/gui_native_api.h b/wamr/core/app-framework/wgl/native/gui_native_api.h new file mode 100644 index 0000000..f61d9b5 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/gui_native_api.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GUI_API_H_ +#define _GUI_API_H_ + +#include "bh_platform.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * gui interfaces + */ + +void +wasm_obj_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_btn_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_label_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_cb_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc); + +void +wasm_list_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc); + +#ifdef __cplusplus +} +#endif + + +#endif /* end of _GUI_API_H_ */ diff --git a/wamr/core/app-framework/wgl/native/wamr_gui.inl b/wamr/core/app-framework/wgl/native/wamr_gui.inl new file mode 100644 index 0000000..c7855b1 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wamr_gui.inl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/* button */ +EXPORT_WASM_API_WITH_SIG(wasm_btn_native_call, "(i*i)"), + +/* obj */ +EXPORT_WASM_API_WITH_SIG(wasm_obj_native_call, "(i*i)"), + +/* label */ +EXPORT_WASM_API_WITH_SIG(wasm_label_native_call, "(i*i)"), + +/* cont */ +//EXPORT_WASM_API_WITH_SIG(wasm_cont_native_call, "(i*i)"), + +/* page */ +//EXPORT_WASM_API_WITH_SIG(wasm_page_native_call, "(i*i)"), + +/* list */ +EXPORT_WASM_API_WITH_SIG(wasm_list_native_call, "(i*i)"), + +/* drop down list */ +//EXPORT_WASM_API_WITH_SIG(wasm_ddlist_native_call, "(i*i)"), + +/* check box */ +EXPORT_WASM_API_WITH_SIG(wasm_cb_native_call, "(i*i)"), diff --git a/wamr/core/app-framework/wgl/native/wasm_lib.cmake b/wamr/core/app-framework/wgl/native/wasm_lib.cmake new file mode 100644 index 0000000..b452fb1 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wasm_lib.cmake @@ -0,0 +1,19 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +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} + ${DEPS_DIR}/lvgl + ${DEPS_DIR}/lvgl/src) + +file (GLOB_RECURSE lvgl_source ${DEPS_DIR}/lvgl/*.c) +file (GLOB_RECURSE wrapper_source ${WASM_LIB_GUI_DIR}/*.c) + +set (WASM_APP_LIB_CURRENT_SOURCE ${wrapper_source} ${lvgl_source}) diff --git a/wamr/core/app-framework/wgl/native/wgl.h b/wamr/core/app-framework/wgl/native/wgl.h new file mode 100644 index 0000000..dc01191 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WAMR_GRAPHIC_LIBRARY_H +#define WAMR_GRAPHIC_LIBRARY_H + +#ifdef __cplusplus +extern "C" { +#endif + +void wgl_init(void); +void wgl_exit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* WAMR_GRAPHIC_LIBRARY_H */ diff --git a/wamr/core/app-framework/wgl/native/wgl_btn_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_btn_wrapper.c new file mode 100644 index 0000000..dbe6224 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_btn_wrapper.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "native_interface.h" +#include "lvgl.h" +#include "module_wasm_app.h" +#include "wgl_native_utils.h" + +/* ------------------------------------------------------------------------- + * Button widget native function wrappers + * -------------------------------------------------------------------------*/ +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_create_wrapper) +{ + int32 res; + wgl_native_return_type(int32); + wgl_native_get_arg(uint32, par_obj_id); + wgl_native_get_arg(uint32, copy_obj_id); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + res = wgl_native_wigdet_create(WIDGET_TYPE_BTN, par_obj_id, copy_obj_id, module_inst); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_set_toggle_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + wgl_native_get_arg(bool, tgl); + + (void)exec_env; + lv_btn_set_toggle(btn, tgl); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_set_state_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + wgl_native_get_arg(lv_btn_state_t, state); + + (void)exec_env; + lv_btn_set_state(btn, state); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_set_ink_in_time_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + wgl_native_get_arg(uint16_t, time); + + (void)exec_env; + lv_btn_set_ink_in_time(btn, time); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_set_ink_out_time_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + wgl_native_get_arg(uint16_t, time); + + (void)exec_env; + lv_btn_set_ink_out_time(btn, time); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_set_ink_wait_time_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + wgl_native_get_arg(uint16_t, time); + + (void)exec_env; + lv_btn_set_ink_wait_time(btn, time); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_get_ink_in_time_wrapper) +{ + uint16_t res; + wgl_native_return_type(uint16_t); + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + res = lv_btn_get_ink_in_time(btn); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_get_ink_out_time_wrapper) +{ + uint16_t res; + wgl_native_return_type(uint16_t); + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + res = lv_btn_get_ink_out_time(btn); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_get_ink_wait_time_wrapper) +{ + uint16_t res; + wgl_native_return_type(uint16_t); + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + res = lv_btn_get_ink_wait_time(btn); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_get_state_wrapper) +{ + lv_btn_state_t res; + wgl_native_return_type(lv_btn_state_t); + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + res = lv_btn_get_state(btn); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_get_toggle_wrapper) +{ + bool res; + wgl_native_return_type(bool); + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + res = lv_btn_get_toggle(btn); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_btn_toggle_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, btn); + + (void)exec_env; + lv_btn_toggle(btn); +} + +static WGLNativeFuncDef btn_native_func_defs[] = { + { BTN_FUNC_ID_CREATE, lv_btn_create_wrapper, 2, false }, + { BTN_FUNC_ID_SET_TOGGLE, lv_btn_set_toggle_wrapper, 2, true }, + { BTN_FUNC_ID_SET_STATE, lv_btn_set_state_wrapper, 2, true }, + { BTN_FUNC_ID_SET_INK_IN_TIME, lv_btn_set_ink_in_time_wrapper, 2, true }, + { BTN_FUNC_ID_SET_INK_OUT_TIME, lv_btn_set_ink_out_time_wrapper, 2, true }, + { BTN_FUNC_ID_SET_INK_WAIT_TIME, lv_btn_set_ink_wait_time_wrapper, 2, true }, + { BTN_FUNC_ID_GET_INK_IN_TIME, lv_btn_get_ink_in_time_wrapper, 1, true }, + { BTN_FUNC_ID_GET_INK_OUT_TIME, lv_btn_get_ink_out_time_wrapper, 1, true }, + { BTN_FUNC_ID_GET_INK_WAIT_TIME, lv_btn_get_ink_wait_time_wrapper, 1, true }, + { BTN_FUNC_ID_GET_STATE, lv_btn_get_state_wrapper, 1, true }, + { BTN_FUNC_ID_GET_TOGGLE, lv_btn_get_toggle_wrapper, 1, true }, + { BTN_FUNC_ID_TOGGLE, lv_btn_toggle_wrapper, 1, true }, + +}; + +/*************** Native Interface to Wasm App ***********/ +void +wasm_btn_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc) +{ + uint32 size = sizeof(btn_native_func_defs) / sizeof(WGLNativeFuncDef); + + wgl_native_func_call(exec_env, + btn_native_func_defs, + size, + func_id, + argv, + argc); +} diff --git a/wamr/core/app-framework/wgl/native/wgl_cb_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_cb_wrapper.c new file mode 100644 index 0000000..e306b10 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_cb_wrapper.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "lvgl.h" +#include "wasm_export.h" +#include "native_interface.h" +#include "module_wasm_app.h" +#include "wgl_native_utils.h" + +/* ------------------------------------------------------------------------- + * Label widget native function wrappers + * -------------------------------------------------------------------------*/ +DEFINE_WGL_NATIVE_WRAPPER(lv_cb_create_wrapper) +{ + int32 res; + wgl_native_return_type(int32); + wgl_native_get_arg(uint32, par_obj_id); + wgl_native_get_arg(uint32, copy_obj_id); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + res = wgl_native_wigdet_create(WIDGET_TYPE_CB, par_obj_id, copy_obj_id, module_inst); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_cb_set_text_wrapper) +{ + char *text; + wgl_native_get_arg(lv_obj_t *, cb); + wgl_native_get_arg(uint32, text_offset); + wgl_native_get_arg(uint32, text_len); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(text_offset, text_len) + || !(text = addr_app_to_native(text_offset))) + return; + + lv_cb_set_text(cb, text); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_cb_set_static_text_wrapper) +{ + char *text; + wgl_native_get_arg(lv_obj_t *, cb); + wgl_native_get_arg(uint32, text_offset); + wgl_native_get_arg(uint32, text_len); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(text_offset, text_len) + || !(text = addr_app_to_native(text_offset))) + return; + + lv_cb_set_static_text(cb, text); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_cb_get_text_length_wrapper) +{ + const char *text; + wgl_native_return_type(int32); + wgl_native_get_arg(lv_obj_t *, cb); + + (void)exec_env; + + text = lv_cb_get_text(cb); + wgl_native_set_return(text ? strlen(text): 0); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_cb_get_text_wrapper) +{ + const char *text; + char *buffer; + wgl_native_return_type(uint32); + wgl_native_get_arg(lv_obj_t *, cb); + wgl_native_get_arg(uint32, buffer_offset); + wgl_native_get_arg(int, buffer_len); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(buffer_offset, buffer_len) + || !(buffer = addr_app_to_native(buffer_offset))) + return; + + if ((text = lv_cb_get_text(cb))) { + strncpy(buffer, text, buffer_len - 1); + buffer[buffer_len - 1] = '\0'; + } + + wgl_native_set_return(buffer_offset); +} + +static WGLNativeFuncDef cb_native_func_defs[] = { + { CB_FUNC_ID_CREATE, lv_cb_create_wrapper, 2, false }, + { CB_FUNC_ID_SET_TEXT, lv_cb_set_text_wrapper, 3, true }, + { CB_FUNC_ID_SET_STATIC_TEXT, lv_cb_set_static_text_wrapper, 3, true }, + { CB_FUNC_ID_GET_TEXT_LENGTH, lv_cb_get_text_length_wrapper, 1, true }, + { CB_FUNC_ID_GET_TEXT, lv_cb_get_text_wrapper, 3, true }, +}; + +/*************** Native Interface to Wasm App ***********/ +void +wasm_cb_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc) +{ + uint32 size = sizeof(cb_native_func_defs) / sizeof(WGLNativeFuncDef); + + wgl_native_func_call(exec_env, + cb_native_func_defs, + size, + func_id, + argv, + argc); +} diff --git a/wamr/core/app-framework/wgl/native/wgl_cont_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_cont_wrapper.c new file mode 100644 index 0000000..70eeb0f --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_cont_wrapper.c @@ -0,0 +1,7 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "lvgl.h" +#include "module_wasm_app.h" diff --git a/wamr/core/app-framework/wgl/native/wgl_label_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_label_wrapper.c new file mode 100644 index 0000000..c1327e4 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_label_wrapper.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "lvgl.h" +#include "wasm_export.h" +#include "native_interface.h" +#include "module_wasm_app.h" +#include "wgl_native_utils.h" + +/* ------------------------------------------------------------------------- + * Label widget native function wrappers + * -------------------------------------------------------------------------*/ +DEFINE_WGL_NATIVE_WRAPPER(lv_label_create_wrapper) +{ + int32 res; + wgl_native_return_type(int32); + wgl_native_get_arg(uint32, par_obj_id); + wgl_native_get_arg(uint32, copy_obj_id); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + res = wgl_native_wigdet_create(WIDGET_TYPE_LABEL, par_obj_id, copy_obj_id, module_inst); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_label_set_text_wrapper) +{ + char *text; + wgl_native_get_arg(lv_obj_t *, label); + wgl_native_get_arg(uint32, text_offset); + wgl_native_get_arg(uint32, text_len); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(text_offset, text_len) + || !(text = addr_app_to_native(text_offset))) + return; + + lv_label_set_text(label, text); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_label_get_text_length_wrapper) +{ + wgl_native_return_type(int32); + wgl_native_get_arg(lv_obj_t *, label); + const char *text; + + (void)exec_env; + + text = lv_label_get_text(label); + wgl_native_set_return(text ? strlen(text) : 0); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_label_get_text_wrapper) +{ + const char *text; + char *buffer; + wgl_native_return_type(uint32); + wgl_native_get_arg(lv_obj_t *, label); + wgl_native_get_arg(uint32, buffer_offset); + wgl_native_get_arg(int, buffer_len); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(buffer_offset, buffer_len) + || !(buffer = addr_app_to_native(buffer_offset))) + return; + + if ((text = lv_label_get_text(label))) { + strncpy(buffer, text, buffer_len - 1); + buffer[buffer_len - 1] = '\0'; + } + + wgl_native_set_return(buffer_offset); +} + +static WGLNativeFuncDef label_native_func_defs[] = { + { LABEL_FUNC_ID_CREATE, lv_label_create_wrapper, 2, false }, + { LABEL_FUNC_ID_SET_TEXT, lv_label_set_text_wrapper, 3, true }, + { LABEL_FUNC_ID_GET_TEXT_LENGTH, lv_label_get_text_length_wrapper, 1, true }, + { LABEL_FUNC_ID_GET_TEXT, lv_label_get_text_wrapper, 3, true }, +}; + +/*************** Native Interface to Wasm App ***********/ +void +wasm_label_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc) +{ + uint32 size = sizeof(label_native_func_defs) / sizeof(WGLNativeFuncDef); + + wgl_native_func_call(exec_env, + label_native_func_defs, + size, + func_id, + argv, + argc); +} diff --git a/wamr/core/app-framework/wgl/native/wgl_list_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_list_wrapper.c new file mode 100644 index 0000000..926573e --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_list_wrapper.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "native_interface.h" +#include "lvgl.h" +#include "module_wasm_app.h" +#include "wgl_native_utils.h" +#include "bh_assert.h" + +/* ------------------------------------------------------------------------- + * List widget native function wrappers + * -------------------------------------------------------------------------*/ +DEFINE_WGL_NATIVE_WRAPPER(lv_list_create_wrapper) +{ + int32 res; + wgl_native_return_type(int32); + wgl_native_get_arg(uint32, par_obj_id); + wgl_native_get_arg(uint32, copy_obj_id); + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + res = wgl_native_wigdet_create(WIDGET_TYPE_LIST, par_obj_id, copy_obj_id, module_inst); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_list_add_btn_wrapper) +{ + wgl_native_return_type(int32); + wgl_native_get_arg(lv_obj_t *, list); + wgl_native_get_arg(uint32, text_offset); + wgl_native_get_arg(uint32, text_len); + uint32 btn_obj_id; + lv_obj_t *btn; + uint32 mod_id; + char *text; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_app_addr(text_offset, text_len) + || !(text = addr_app_to_native(text_offset))) + return; + + if (!(btn = lv_list_add_btn(list, NULL, text))) { + wasm_runtime_set_exception(module_inst, "add button to list fail."); + return; + } + + mod_id = app_manager_get_module_id(Module_WASM_App, module_inst); + bh_assert(mod_id != ID_NONE); + + if (!wgl_native_add_object(btn, mod_id, &btn_obj_id)) { + wasm_runtime_set_exception(module_inst, "add button to object list fail."); + return; + } + + wgl_native_set_return(btn_obj_id); +} + +static WGLNativeFuncDef list_native_func_defs[] = { + { LIST_FUNC_ID_CREATE, lv_list_create_wrapper, 2, false }, + { LIST_FUNC_ID_ADD_BTN, lv_list_add_btn_wrapper, 3, true }, +}; + +/*************** Native Interface to Wasm App ***********/ +void +wasm_list_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc) +{ + uint32 size = sizeof(list_native_func_defs) / sizeof(WGLNativeFuncDef); + + wgl_native_func_call(exec_env, + list_native_func_defs, + size, + func_id, + argv, + argc); +} diff --git a/wamr/core/app-framework/wgl/native/wgl_native_utils.c b/wamr/core/app-framework/wgl/native/wgl_native_utils.c new file mode 100644 index 0000000..081a31a --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_native_utils.c @@ -0,0 +1,131 @@ + + +#include "wgl_native_utils.h" +#include "lvgl.h" +#include "module_wasm_app.h" +#include "wasm_export.h" +#include "bh_assert.h" + +#include + +#define THROW_EXC(msg) wasm_runtime_set_exception(module_inst, msg); + +uint32 wgl_native_wigdet_create(int8 widget_type, + uint32 par_obj_id, + uint32 copy_obj_id, + wasm_module_inst_t module_inst) +{ + uint32 obj_id; + lv_obj_t *wigdet = NULL, *par = NULL, *copy = NULL; + uint32 mod_id; + + //TODO: limit total widget number + + /* validate the parent object id if not equal to 0 */ + if (par_obj_id != 0 && !wgl_native_validate_object(par_obj_id, &par)) { + THROW_EXC("create widget with invalid parent object."); + return 0; + } + /* validate the copy object id if not equal to 0 */ + if (copy_obj_id != 0 && !wgl_native_validate_object(copy_obj_id, ©)) { + THROW_EXC("create widget with invalid copy object."); + return 0; + } + + if (par == NULL) + par = lv_disp_get_scr_act(NULL); + + if (widget_type == WIDGET_TYPE_BTN) + wigdet = lv_btn_create(par, copy); + else if (widget_type == WIDGET_TYPE_LABEL) + wigdet = lv_label_create(par, copy); + else if (widget_type == WIDGET_TYPE_CB) + wigdet = lv_cb_create(par, copy); + else if (widget_type == WIDGET_TYPE_LIST) + wigdet = lv_list_create(par, copy); + else if (widget_type == WIDGET_TYPE_DDLIST) + wigdet = lv_ddlist_create(par, copy); + + if (wigdet == NULL) + return 0; + + mod_id = app_manager_get_module_id(Module_WASM_App, module_inst); + bh_assert(mod_id != ID_NONE); + + if (wgl_native_add_object(wigdet, mod_id, &obj_id)) + return obj_id; /* success return */ + + return 0; +} + +void wgl_native_func_call(wasm_exec_env_t exec_env, + WGLNativeFuncDef *funcs, + uint32 size, + int32 func_id, + uint32 *argv, + uint32 argc) +{ + typedef void (*WGLNativeFuncPtr)(wasm_exec_env_t, uint64*, uint32*); + WGLNativeFuncPtr wglNativeFuncPtr; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + WGLNativeFuncDef *func_def = funcs; + WGLNativeFuncDef *func_def_end = func_def + size; + + /* Note: argv is validated in wasm_runtime_invoke_native() + * with pointer length equals to 1. Here validate the argv + * buffer again but with its total length in bytes */ + if (!wasm_runtime_validate_native_addr(module_inst, argv, argc * sizeof(uint32))) + return; + + while (func_def < func_def_end) { + if (func_def->func_id == func_id + && (uint32)func_def->arg_num == argc) { + uint64 argv_copy_buf[16], size; + uint64 *argv_copy = argv_copy_buf; + int i; + + if (argc > sizeof(argv_copy_buf) / sizeof(uint64)) { + size = sizeof(uint64) * (uint64)argc; + if (size >= UINT32_MAX + || !(argv_copy = wasm_runtime_malloc((uint32)size))) { + THROW_EXC("allocate memory failed."); + return; + } + memset(argv_copy, 0, (uint32)size); + } + + /* Init argv_copy */ + for (i = 0; i < func_def->arg_num; i++) + *(uint32*)&argv_copy[i] = argv[i]; + + /* Validate the first argument which is a lvgl object if needed */ + if (func_def->check_obj) { + lv_obj_t *obj = NULL; + if (!wgl_native_validate_object(argv[0], &obj)) { + THROW_EXC("the object is invalid"); + goto fail; + } + *(lv_obj_t **)&argv_copy[0] = obj; + } + + wglNativeFuncPtr = (WGLNativeFuncPtr)func_def->func_ptr; + wglNativeFuncPtr(exec_env, argv_copy, argv); + + if (argv_copy != argv_copy_buf) + wasm_runtime_free(argv_copy); + + /* success return */ + return; + + fail: + if (argv_copy != argv_copy_buf) + wasm_runtime_free(argv_copy); + return; + } + + func_def++; + } + + THROW_EXC("the native widget function is not found!"); +} + diff --git a/wamr/core/app-framework/wgl/native/wgl_native_utils.h b/wamr/core/app-framework/wgl/native/wgl_native_utils.h new file mode 100644 index 0000000..1150f01 --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_native_utils.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H +#define WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" +#include "lvgl.h" +#include "wasm_export.h" +#include "bi-inc/wgl_shared_utils.h" + +#define wgl_native_return_type(type) type *wgl_ret = (type*)(args_ret) +#define wgl_native_get_arg(type, name) type name = *((type*)(args++)) +#define wgl_native_set_return(val) *wgl_ret = (val) + +#define DEFINE_WGL_NATIVE_WRAPPER(func_name) \ +static void func_name(wasm_exec_env_t exec_env, uint64 *args, uint32 *args_ret) + +enum { + WIDGET_TYPE_BTN, + WIDGET_TYPE_LABEL, + WIDGET_TYPE_CB, + WIDGET_TYPE_LIST, + WIDGET_TYPE_DDLIST, + + _WIDGET_TYPE_NUM, +}; + +typedef struct WGLNativeFuncDef { + /* Function id */ + int32 func_id; + + /* Native function pointer */ + void *func_ptr; + + /* argument number */ + uint8 arg_num; + + /* whether the first argument is lvgl object and needs validate */ + bool check_obj; +} WGLNativeFuncDef; + +bool wgl_native_validate_object(int32 obj_id, lv_obj_t **obj); + +bool wgl_native_add_object(lv_obj_t *obj, uint32 module_id, uint32 *obj_id); + +uint32 wgl_native_wigdet_create(int8 widget_type, + uint32 par_obj_id, + uint32 copy_obj_id, + wasm_module_inst_t module_inst); + +void wgl_native_func_call(wasm_exec_env_t exec_env, + WGLNativeFuncDef *funcs, + uint32 size, + int32 func_id, + uint32 *argv, + uint32 argc); + +#ifdef __cplusplus +} +#endif + +#endif /* WAMR_GRAPHIC_LIBRARY_NATIVE_UTILS_H */ diff --git a/wamr/core/app-framework/wgl/native/wgl_obj_wrapper.c b/wamr/core/app-framework/wgl/native/wgl_obj_wrapper.c new file mode 100644 index 0000000..801c55d --- /dev/null +++ b/wamr/core/app-framework/wgl/native/wgl_obj_wrapper.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "lvgl.h" +#include "app_manager_export.h" +#include "module_wasm_app.h" +#include "bh_platform.h" +#include "wgl_native_utils.h" +#include "wgl.h" + + +typedef struct { + bh_list_link l; + + /* The object id. */ + uint32 obj_id; + + /* The lv object */ + lv_obj_t *obj; + + /* Module id that the obj belongs to */ + uint32 module_id; +} object_node_t; + +typedef struct { + int32 obj_id; + lv_event_t event; +} object_event_t; + +/* Max obj id */ +static uint32 g_obj_id_max = 0; + +static bh_list g_object_list; + +static korp_mutex g_object_list_mutex; + +static bool lv_task_handler_thread_run = true; + +static korp_mutex task_handler_lock; + +static korp_cond task_handler_cond; + +static void app_mgr_object_event_callback(module_data *m_data, bh_message_t msg) +{ + uint32 argv[2]; + wasm_function_inst_t func_on_object_event; + bh_assert(WIDGET_EVENT_WASM == bh_message_type(msg)); + wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; + wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; + object_event_t *object_event + = (object_event_t *)bh_message_payload(msg); + + if (object_event == NULL) + return; + + func_on_object_event = wasm_runtime_lookup_function(inst, "_on_widget_event", + "(i32i32)"); + if (!func_on_object_event) + func_on_object_event = wasm_runtime_lookup_function(inst, "on_widget_event", + "(i32i32)"); + if (!func_on_object_event) { + printf("Cannot find function on_widget_event\n"); + return; + } + + argv[0] = object_event->obj_id; + argv[1] = object_event->event; + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_on_object_event, + 2, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + printf(":Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + return; + } +} + +static void cleanup_object_list(uint32 module_id) +{ + object_node_t *elem; + + os_mutex_lock(&g_object_list_mutex); + + while (true) { + bool found = false; + elem = (object_node_t *)bh_list_first_elem(&g_object_list); + while (elem) { + /* delete the leaf node belongs to the module firstly */ + if (module_id == elem->module_id && + lv_obj_count_children(elem->obj) == 0) { + object_node_t *next = (object_node_t *)bh_list_elem_next(elem); + + found = true; + lv_obj_del(elem->obj); + bh_list_remove(&g_object_list, elem); + wasm_runtime_free(elem); + elem = next; + } else { + elem = (object_node_t *)bh_list_elem_next(elem); + } + } + + if (!found) + break; + } + + os_mutex_unlock(&g_object_list_mutex); +} + +static bool init_object_event_callback_framework() +{ + if (!wasm_register_cleanup_callback(cleanup_object_list)) { + goto fail; + } + + if (!wasm_register_msg_callback(WIDGET_EVENT_WASM, + app_mgr_object_event_callback)) { + goto fail; + } + + return true; + +fail: + return false; +} + +bool wgl_native_validate_object(int32 obj_id, lv_obj_t **obj) +{ + object_node_t *elem; + + os_mutex_lock(&g_object_list_mutex); + + elem = (object_node_t *)bh_list_first_elem(&g_object_list); + while (elem) { + if (obj_id == elem->obj_id) { + if (obj != NULL) + *obj = elem->obj; + os_mutex_unlock(&g_object_list_mutex); + return true; + } + elem = (object_node_t *) bh_list_elem_next(elem); + } + + os_mutex_unlock(&g_object_list_mutex); + + return false; +} + +bool wgl_native_add_object(lv_obj_t *obj, uint32 module_id, uint32 *obj_id) +{ + object_node_t *node; + + node = (object_node_t *) wasm_runtime_malloc(sizeof(object_node_t)); + + if (node == NULL) + return false; + + /* Generate an obj id */ + g_obj_id_max++; + if (g_obj_id_max == -1) + g_obj_id_max = 1; + + memset(node, 0, sizeof(*node)); + node->obj = obj; + node->obj_id = g_obj_id_max; + node->module_id = module_id; + + os_mutex_lock(&g_object_list_mutex); + bh_list_insert(&g_object_list, node); + os_mutex_unlock(&g_object_list_mutex); + + if (obj_id != NULL) + *obj_id = node->obj_id; + + return true; +} + +static void _obj_del_recursive(lv_obj_t *obj) +{ + object_node_t *elem; + lv_obj_t * i; + lv_obj_t * i_next; + + i = lv_ll_get_head(&(obj->child_ll)); + + while (i != NULL) { + /*Get the next object before delete this*/ + i_next = lv_ll_get_next(&(obj->child_ll), i); + + /*Call the recursive del to the child too*/ + _obj_del_recursive(i); + + /*Set i to the next node*/ + i = i_next; + } + + os_mutex_lock(&g_object_list_mutex); + + elem = (object_node_t *)bh_list_first_elem(&g_object_list); + while (elem) { + if (obj == elem->obj) { + bh_list_remove(&g_object_list, elem); + wasm_runtime_free(elem); + os_mutex_unlock(&g_object_list_mutex); + return; + } + elem = (object_node_t *) bh_list_elem_next(elem); + } + + os_mutex_unlock(&g_object_list_mutex); +} + +static void _obj_clean_recursive(lv_obj_t *obj) +{ + lv_obj_t * i; + lv_obj_t * i_next; + + i = lv_ll_get_head(&(obj->child_ll)); + + while (i != NULL) { + /*Get the next object before delete this*/ + i_next = lv_ll_get_next(&(obj->child_ll), i); + + /*Call the recursive del to the child too*/ + _obj_del_recursive(i); + + /*Set i to the next node*/ + i = i_next; + } +} + +static void post_widget_msg_to_module(object_node_t *object_node, lv_event_t event) +{ + module_data *module = module_data_list_lookup_id(object_node->module_id); + object_event_t *object_event; + + if (module == NULL) + return; + + object_event = (object_event_t *)wasm_runtime_malloc(sizeof(*object_event)); + if (object_event == NULL) + return; + + memset(object_event, 0, sizeof(*object_event)); + object_event->obj_id = object_node->obj_id; + object_event->event = event; + + bh_post_msg(module->queue, + WIDGET_EVENT_WASM, + object_event, + sizeof(*object_event)); +} + +static void internal_lv_obj_event_cb(lv_obj_t *obj, lv_event_t event) +{ + object_node_t *elem; + + os_mutex_lock(&g_object_list_mutex); + + elem = (object_node_t *)bh_list_first_elem(&g_object_list); + while (elem) { + if (obj == elem->obj) { + post_widget_msg_to_module(elem, event); + os_mutex_unlock(&g_object_list_mutex); + return; + } + elem = (object_node_t *) bh_list_elem_next(elem); + } + + os_mutex_unlock(&g_object_list_mutex); +} + +static void* lv_task_handler_thread_routine (void *arg) +{ + os_mutex_lock(&task_handler_lock); + + while (lv_task_handler_thread_run) { + os_cond_reltimedwait(&task_handler_cond, &task_handler_lock, + 100 * 1000); + lv_task_handler(); + } + + os_mutex_unlock(&task_handler_lock); + return NULL; +} + +void wgl_init(void) +{ + korp_tid tid; + + if (os_mutex_init(&task_handler_lock) != 0) + return; + + if (os_cond_init(&task_handler_cond) != 0) { + os_mutex_destroy(&task_handler_lock); + return; + } + + lv_init(); + + bh_list_init(&g_object_list); + os_recursive_mutex_init(&g_object_list_mutex); + init_object_event_callback_framework(); + + /* new a thread, call lv_task_handler periodically */ + os_thread_create(&tid, + lv_task_handler_thread_routine, + NULL, + BH_APPLET_PRESERVED_STACK_SIZE); +} + +void wgl_exit(void) +{ + lv_task_handler_thread_run = false; + os_cond_destroy(&task_handler_cond); + os_mutex_destroy(&task_handler_lock); +} + +/* ------------------------------------------------------------------------- + * Obj native function wrappers + * -------------------------------------------------------------------------*/ +DEFINE_WGL_NATIVE_WRAPPER(lv_obj_del_wrapper) +{ + lv_res_t res; + wgl_native_return_type(lv_res_t); + wgl_native_get_arg(lv_obj_t *, obj); + + (void)exec_env; + + /* Recursively delete object node in the list belong to this + * parent object including itself */ + _obj_del_recursive(obj); + res = lv_obj_del(obj); + wgl_native_set_return(res); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_obj_del_async_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, obj); + + (void)exec_env; + lv_obj_del_async(obj); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_obj_clean_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, obj); + + (void)exec_env; + + /* Recursively delete child object node in the list belong to this + * parent object */ + _obj_clean_recursive(obj); + + /* Delete all of its children */ + lv_obj_clean(obj); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_obj_align_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, obj); + wgl_native_get_arg(uint32, base_obj_id); + wgl_native_get_arg(lv_align_t, align); + wgl_native_get_arg(lv_coord_t, x_mod); + wgl_native_get_arg(lv_coord_t, y_mod); + lv_obj_t *base = NULL; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + /* validate the base object id if not equal to 0 */ + if (base_obj_id != 0 && !wgl_native_validate_object(base_obj_id, &base)) { + wasm_runtime_set_exception(module_inst, "align with invalid base object."); + return; + } + + lv_obj_align(obj, base, align, x_mod, y_mod); +} + +DEFINE_WGL_NATIVE_WRAPPER(lv_obj_set_event_cb_wrapper) +{ + wgl_native_get_arg(lv_obj_t *, obj); + (void)exec_env; + lv_obj_set_event_cb(obj, internal_lv_obj_event_cb); +} + +/* ------------------------------------------------------------------------- */ + +static WGLNativeFuncDef obj_native_func_defs[] = { + { OBJ_FUNC_ID_DEL, lv_obj_del_wrapper, 1, true }, + { OBJ_FUNC_ID_DEL_ASYNC, lv_obj_del_async_wrapper, 1, true }, + { OBJ_FUNC_ID_CLEAN, lv_obj_clean_wrapper, 1, true }, + { OBJ_FUNC_ID_ALIGN, lv_obj_align_wrapper, 5, true }, + { OBJ_FUNC_ID_SET_EVT_CB, lv_obj_set_event_cb_wrapper, 1, true }, +}; + +/*************** Native Interface to Wasm App ***********/ +void +wasm_obj_native_call(wasm_exec_env_t exec_env, + int32 func_id, uint32 *argv, uint32 argc) +{ + uint32 size = sizeof(obj_native_func_defs) / sizeof(WGLNativeFuncDef); + + wgl_native_func_call(exec_env, + obj_native_func_defs, + size, + func_id, + argv, + argc); +} diff --git a/wamr/core/app-framework/wgl/readme.MD b/wamr/core/app-framework/wgl/readme.MD new file mode 100644 index 0000000..f929a5c --- /dev/null +++ b/wamr/core/app-framework/wgl/readme.MD @@ -0,0 +1,97 @@ +WASM Graphic Layer (WGL) +======= + +The WGL builds the littlevgl v6.0 into the runtime and exports a API layer for WASM appication programming graphic user interfaces. This approach will make the WASM application small footprint. Another option is to build the whole littlevgl library into WASM, which is how the sample littlevgl is implemented. + +# Challenges + +When the littlevgl library is compiled into the runtime, all the widget data is actually located in the runtime native space. As the WASM sandbox won't allow the WASM application to directly access the native data, it introduced a few problems for the littlevgl API exporting: + +1. Reference to the widget object + + Almost each littlevgl API takes the widget object as the first argument, which leads to either reading or writing the data associated with the object in native space. We have to prevent the WASM app utilize this method to access unauthorized memmory address. + + The solution is to track the objects created by the WASM App in the native layer. Every access to the object must be validated in advance. To simplify implementing native wrapper function, the wgl_native_func_call() will do the validation for the object presented in the first argument. + + When multiple WASM apps are creating their own UI, the object should also validated for its owner module instance. + +2. Access the object properties + + As the data structure of widget objects is no longer visible to the applications, the app has to call APIs to get/set the properties of an object. + +3. Pass function arguments in stucture pointers + + We have to do serialization for the structure passing between the WASM and native. + +4. Callbacks + +# API compatibility with littlevgl +We wish the application continue to use the littlevgl API and keep existing header files inclusion, however it is also a bit challenging since we have to redefine some data types such as lv_obj_t in the APIs exposed to the WASM app. + +''' +typedef void lv_obj_t; +''' + + + +# Prepare the lvgl header files for WASM applicaitons + +Run the below script to setup the lvgl header files for the wasm appliation. + +``` +core/app-framework/wgl/app/prepare_headers.sh +``` + +The script is also automatically executed after downloading the lvgl repo in the wamr-sdk building process. + + + +# How to extend a little vgl wideget +Currently the wgl has exported the API for a few common used widgets such as button, label etc. + +Refer to the implementation of these widgets for extending more widgets. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wamr/core/app-mgr/README.md b/wamr/core/app-mgr/README.md new file mode 100644 index 0000000..d5e0588 --- /dev/null +++ b/wamr/core/app-mgr/README.md @@ -0,0 +1,10 @@ +WASM application management +======= + +## structure + + + + + + diff --git a/wamr/core/app-mgr/app-manager/app_manager.c b/wamr/core/app-mgr/app-manager/app_manager.c new file mode 100644 index 0000000..d688618 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/app_manager.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager.h" +#include "app_manager_host.h" +#include "bh_platform.h" +#include "bi-inc/attr_container.h" +#include "event.h" +#include "watchdog.h" +#include "coap_ext.h" + +/* Queue of app manager */ +static bh_queue *g_app_mgr_queue; + +void* +get_app_manager_queue() +{ + return g_app_mgr_queue; +} + +void app_manager_post_applets_update_event() +{ + module_data *m_data; + attr_container_t *attr_cont; + request_t msg; + int num = 0, i = 0; + char *url = "/applets"; + + if (!event_is_registered(url)) + return; + + if (!(attr_cont = attr_container_create("All Applets"))) { + app_manager_printf( + "Post applets update event failed: allocate memory failed."); + return; + } + + os_mutex_lock(&module_data_list_lock); + + m_data = module_data_list; + while (m_data) { + num++; + m_data = m_data->next; + } + + if (!(attr_container_set_int(&attr_cont, "num", num))) { + app_manager_printf( + "Post applets update event failed: set attr container key failed."); + goto fail; + } + + m_data = module_data_list; + while (m_data) { + char buf[32]; + i++; + snprintf(buf, sizeof(buf), "%s%d", "applet", i); + if (!(attr_container_set_string(&attr_cont, buf, m_data->module_name))) { + app_manager_printf( + "Post applets update event failed: set attr applet name key failed."); + goto fail; + } + snprintf(buf, sizeof(buf), "%s%d", "heap", i); + if (!(attr_container_set_int(&attr_cont, buf, m_data->heap_size))) { + app_manager_printf( + "Post applets update event failed: set attr heap key failed."); + goto fail; + } + m_data = m_data->next; + } + + memset(&msg, 0, sizeof(msg)); + msg.url = url; + msg.action = COAP_EVENT; + msg.payload = (char*) attr_cont; + send_request_to_host(&msg); + + app_manager_printf("Post applets update event success!\n"); + attr_container_dump(attr_cont); + +fail: + os_mutex_unlock(&module_data_list_lock); + attr_container_destroy(attr_cont); +} + +static int get_applets_count() +{ + module_data *m_data; + int num = 0; + + os_mutex_lock(&module_data_list_lock); + + m_data = module_data_list; + while (m_data) { + num++; + m_data = m_data->next; + } + + os_mutex_unlock(&module_data_list_lock); + + return num; +} + +/* Query fw apps info if name = NULL, otherwise query specify app */ +static bool app_manager_query_applets(request_t *msg, const char *name) +{ + module_data *m_data; + attr_container_t *attr_cont; + int num = 0, i = 0, len; + bool ret = false, found = false; + response_t response[1] = { 0 }; + + attr_cont = attr_container_create("Applets Info"); + if (!attr_cont) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applets failed: allocate memory failed."); + return false; + } + + os_mutex_lock(&module_data_list_lock); + + m_data = module_data_list; + while (m_data) { + num++; + m_data = m_data->next; + } + + if (name == NULL && !(attr_container_set_int(&attr_cont, "num", num))) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applets failed: set attr container key failed."); + goto fail; + } + + m_data = module_data_list; + while (m_data) { + char buf[32]; + + if (name == NULL) { + i++; + snprintf(buf, sizeof(buf), "%s%d", "applet", i); + if (!(attr_container_set_string(&attr_cont, buf, + m_data->module_name))) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applets failed: set attr container key failed."); + goto fail; + } + snprintf(buf, sizeof(buf), "%s%d", "heap", i); + if (!(attr_container_set_int(&attr_cont, buf, m_data->heap_size))) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applets failed: set attr container heap key failed."); + goto fail; + } + } else if (!strcmp(name, m_data->module_name)) { + found = true; + if (!(attr_container_set_string(&attr_cont, "name", + m_data->module_name))) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applet failed: set attr container key failed."); + goto fail; + } + if (!(attr_container_set_int(&attr_cont, "heap", m_data->heap_size))) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applet failed: set attr container heap key failed."); + goto fail; + } + } + + m_data = m_data->next; + } + + if (name != NULL && !found) { + SEND_ERR_RESPONSE(msg->mid, + "Query Applet failed: the app is not found."); + goto fail; + } + + len = attr_container_get_serialize_length(attr_cont); + + make_response_for_request(msg, response); + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (char*) attr_cont, len); + send_response_to_host(response); + + ret = true; + app_manager_printf("Query Applets success!\n"); + attr_container_dump(attr_cont); + +fail: + os_mutex_unlock(&module_data_list_lock); + attr_container_destroy(attr_cont); + return ret; +} + +void applet_mgt_reqeust_handler(request_t *request, void *unused) +{ + bh_message_t msg; + /* deep copy, but not use app self heap, but use global heap */ + request_t *req = clone_request(request); + + if (!req) + return; + + msg = bh_new_msg(RESTFUL_REQUEST, req, sizeof(*req), request_cleaner); + if (!msg) { + request_cleaner(req); + return; + } + + bh_post_msg2(get_app_manager_queue(), msg); +} + +/* return -1 for error */ +static int get_module_type(char *kv_str) +{ + int module_type = -1; + char type_str[16] = { 0 }; + + find_key_value(kv_str, strlen(kv_str), "type", type_str, + sizeof(type_str) - 1, '&'); + + if (strlen(type_str) == 0) + module_type = Module_WASM_App; + else if (strcmp(type_str, "jeff") == 0) + module_type = Module_Jeff; + else if (strcmp(type_str, "wasm") == 0) + module_type = Module_WASM_App; + else if (strcmp(type_str, "wasmlib") == 0) + module_type = Module_WASM_Lib; + + return module_type; +} + +#define APP_NAME_MAX_LEN 128 + +/* Queue callback of App Manager */ + +static void app_manager_queue_callback(void *message, void *arg) +{ + request_t *request = (request_t *) bh_message_payload((bh_message_t)message); + int mid = request->mid, module_type, offset; + + (void)arg; + + if ((offset = check_url_start(request->url, strlen(request->url), "/applet")) + > 0) { + module_type = get_module_type(request->url + offset); + + if (module_type == -1) { + SEND_ERR_RESPONSE(mid, + "Applet Management failed: invalid module type."); + goto fail; + } + + /* Install Applet */ + if (request->action == COAP_PUT) { + if (get_applets_count() >= MAX_APP_INSTALLATIONS) { + SEND_ERR_RESPONSE(mid, + "Install Applet failed: exceed max app installations."); + goto fail; + } + + if (!request->payload) { + SEND_ERR_RESPONSE(mid, + "Install Applet failed: invalid payload."); + goto fail; + } + if (g_module_interfaces[module_type] + && g_module_interfaces[module_type]->module_install) { + if (!g_module_interfaces[module_type]->module_install(request)) + goto fail; + } + } + /* Uninstall Applet */ + else if (request->action == COAP_DELETE) { + module_type = get_module_type(request->url + offset); + if (module_type == -1) { + SEND_ERR_RESPONSE(mid, + "Uninstall Applet failed: invalid module type."); + goto fail; + } + + if (g_module_interfaces[module_type] + && g_module_interfaces[module_type]->module_uninstall) { + if (!g_module_interfaces[module_type]->module_uninstall( + request)) + goto fail; + } + } + /* Query Applets installed */ + else if (request->action == COAP_GET) { + char name[APP_NAME_MAX_LEN] = { 0 }; + char *properties = request->url + offset; + find_key_value(properties, strlen(properties), "name", name, + sizeof(name) - 1, '&'); + if (strlen(name) > 0) + app_manager_query_applets(request, name); + else + app_manager_query_applets(request, NULL); + } else { + SEND_ERR_RESPONSE(mid, "Invalid request of applet: invalid action"); + } + } + /* Event Register/Unregister */ + else if ((offset = check_url_start(request->url, strlen(request->url), + "/event/")) > 0) { + char url_buf[256] = { 0 }; + + strncpy(url_buf, request->url + offset, sizeof(url_buf) - 1); + + if (!event_handle_event_request(request->action, url_buf, ID_HOST)) { + SEND_ERR_RESPONSE(mid, "Handle event request failed."); + goto fail; + } + send_error_response_to_host(mid, CONTENT_2_05, NULL); /* OK */ + } else { + int i; + for (i = 0; i < Module_Max; i++) { + if (g_module_interfaces[i] + && g_module_interfaces[i]->module_handle_host_url) { + if (g_module_interfaces[i]->module_handle_host_url(request)) + break; + } + } + + } + + fail: + + return; + +} + +static void module_interfaces_init() +{ + int i; + for (i = 0; i < Module_Max; i++) { + if (g_module_interfaces[i] && g_module_interfaces[i]->module_init) + g_module_interfaces[i]->module_init(); + } +} + +void app_manager_startup(host_interface *interface) +{ + module_interfaces_init(); + + /* Create queue of App Manager */ + g_app_mgr_queue = bh_queue_create(); + if (!g_app_mgr_queue) + return; + + if (!module_data_list_init()) + goto fail1; + + if (!watchdog_startup()) + goto fail2; + + /* Initialize Host */ + app_manager_host_init(interface); + + am_register_resource("/app/", targeted_app_request_handler, ID_APP_MGR); + + /*/app/ and /event/ are both processed by applet_mgt_reqeust_handler*/ + am_register_resource("/applet", applet_mgt_reqeust_handler, ID_APP_MGR); + am_register_resource("/event/", applet_mgt_reqeust_handler, ID_APP_MGR); + + app_manager_printf("App Manager started.\n"); + + /* Enter loop run */ + bh_queue_enter_loop_run(g_app_mgr_queue, app_manager_queue_callback, NULL); + +fail2: + module_data_list_destroy(); + +fail1: + bh_queue_destroy(g_app_mgr_queue); +} + +#include "module_config.h" + +module_interface *g_module_interfaces[Module_Max] = { +#if ENABLE_MODULE_JEFF != 0 + &jeff_module_interface, +#else + NULL, +#endif + +#if ENABLE_MODULE_WASM_APP != 0 + &wasm_app_module_interface, +#else + NULL, +#endif + +#if ENABLE_MODULE_WASM_LIB != 0 + &wasm_lib_module_interface +#else + NULL +#endif + }; diff --git a/wamr/core/app-mgr/app-manager/app_manager.h b/wamr/core/app-mgr/app-manager/app_manager.h new file mode 100644 index 0000000..fb2a42f --- /dev/null +++ b/wamr/core/app-mgr/app-manager/app_manager.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef APP_MANAGER_H +#define APP_MANAGER_H + +#include "bh_platform.h" +#include "app_manager_export.h" +#include "native_interface.h" +#include "bi-inc/shared_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define APP_MGR_MALLOC wasm_runtime_malloc +#define APP_MGR_FREE wasm_runtime_free + +/* os_printf is defined in each platform */ +#define app_manager_printf os_printf + +#define SEND_ERR_RESPONSE(mid, err_msg) do { \ + app_manager_printf("%s\n", err_msg); \ + send_error_response_to_host(mid, INTERNAL_SERVER_ERROR_5_00, err_msg); \ +} while (0) + +extern module_interface *g_module_interfaces[Module_Max]; + +/* Lock of the module data list */ +extern korp_mutex module_data_list_lock; + +/* Module data list */ +extern module_data *module_data_list; + +void +app_manager_add_module_data(module_data *m_data); + +void +app_manager_del_module_data(module_data *m_data); + +bool +module_data_list_init(); + +void +module_data_list_destroy(); + +bool +app_manager_is_interrupting_module(uint32 module_type, void *module_inst); + +void release_module(module_data *m_data); + +void +module_data_list_remove(module_data *m_data); + +void* +app_manager_timer_create(void (*timer_callback)(void*), + watchdog_timer *wd_timer); + +void +app_manager_timer_destroy(void *timer); + +void +app_manager_timer_start(void *timer, int timeout); + +void +app_manager_timer_stop(void *timer); + +watchdog_timer* +app_manager_get_wd_timer_from_timer_handle(void *timer); + +int +app_manager_signature_verify(const uint8_t *file, unsigned int file_len, + const uint8_t *signature, unsigned int sig_size); + +void targeted_app_request_handler(request_t *request, void *unused); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif + diff --git a/wamr/core/app-mgr/app-manager/app_manager_host.c b/wamr/core/app-mgr/app-manager/app_manager_host.c new file mode 100644 index 0000000..a6b13ba --- /dev/null +++ b/wamr/core/app-mgr/app-manager/app_manager_host.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "app_manager_host.h" +#include "app_manager.h" +#include "app_manager_export.h" +#include "coap_ext.h" + +/* host communication interface */ +static host_interface host_commu; + +/* IMRTLink Two leading bytes */ +static unsigned char leadings[] = { (unsigned char) 0x12, (unsigned char) 0x34 }; + +/* IMRTLink Receiving Phase */ +typedef enum recv_phase_t { + Phase_Non_Start, Phase_Leading, Phase_Type, Phase_Size, Phase_Payload +} recv_phase_t; + +/* IMRTLink Receive Context */ +typedef struct recv_context_t { + recv_phase_t phase; + bh_link_msg_t message; + int size_in_phase; +} recv_context_t; + +/* Current IMRTLink receive context */ +static recv_context_t recv_ctx; + +/* Lock for device write */ +static korp_mutex host_lock; + +static bool enable_log = false; + +static bool is_little_endian() +{ + long i = 0x01020304; + unsigned char* c = (unsigned char*) &i; + return (*c == 0x04) ? true : false; +} + +static void exchange32(uint8* pData) +{ + uint8 value = *pData; + *pData = *(pData + 3); + *(pData + 3) = value; + + value = *(pData + 1); + *(pData + 1) = *(pData + 2); + *(pData + 2) = value; +} + +/* return: + * 1: complete message received + * 0: incomplete message received + */ +static int on_imrt_link_byte_arrive(unsigned char ch, recv_context_t *ctx) +{ + if (ctx->phase == Phase_Non_Start) { + ctx->message.payload_size = 0; + + if (ctx->message.payload) { + APP_MGR_FREE(ctx->message.payload); + ctx->message.payload = NULL; + } + + if (ch == leadings[0]) { + if (enable_log) + app_manager_printf("##On byte arrive: got leading 0\n"); + ctx->phase = Phase_Leading; + } + + return 0; + } else if (ctx->phase == Phase_Leading) { + if (ch == leadings[1]) { + if (enable_log) + app_manager_printf("##On byte arrive: got leading 1\n"); + ctx->phase = Phase_Type; + } else + ctx->phase = Phase_Non_Start; + + return 0; + } else if (ctx->phase == Phase_Type) { + if (ctx->size_in_phase++ == 0) { + if (enable_log) + app_manager_printf("##On byte arrive: got type 0\n"); + ctx->message.message_type = ch; + } else { + if (enable_log) + app_manager_printf("##On byte arrive: got type 1\n"); + ctx->message.message_type |= (ch << 8); + ctx->message.message_type = ntohs(ctx->message.message_type); + ctx->phase = Phase_Size; + ctx->size_in_phase = 0; + } + + return 0; + } else if (ctx->phase == Phase_Size) { + unsigned char *p = (unsigned char *) &ctx->message.payload_size; + + if (enable_log) + app_manager_printf("##On byte arrive: got payload_size, byte %d\n", + ctx->size_in_phase); + p[ctx->size_in_phase++] = ch; + + if (ctx->size_in_phase == sizeof(ctx->message.payload_size)) { + ctx->message.payload_size = ntohl(ctx->message.payload_size); + ctx->phase = Phase_Payload; + + if (enable_log) + app_manager_printf("##On byte arrive: payload_size: %d\n", + ctx->message.payload_size); + if (ctx->message.payload) { + APP_MGR_FREE(ctx->message.payload); + ctx->message.payload = NULL; + } + + /* message completion */ + if (ctx->message.payload_size == 0) { + ctx->phase = Phase_Non_Start; + if (enable_log) + app_manager_printf( + "##On byte arrive: receive end, payload_size is 0.\n"); + return 1; + } + + if (ctx->message.payload_size > 1024 * 1024) { + ctx->phase = Phase_Non_Start; + return 0; + } + + if (ctx->message.message_type != INSTALL_WASM_APP) { + ctx->message.payload = + (char *) APP_MGR_MALLOC(ctx->message.payload_size); + if (!ctx->message.payload) { + ctx->phase = Phase_Non_Start; + return 0; + } + } + + ctx->phase = Phase_Payload; + ctx->size_in_phase = 0; + } + + return 0; + } else if (ctx->phase == Phase_Payload) { + if (ctx->message.message_type == INSTALL_WASM_APP) { + int received_size; + module_on_install_request_byte_arrive_func module_on_install = + g_module_interfaces[Module_WASM_App]->module_on_install; + + ctx->size_in_phase++; + + if (module_on_install != NULL) { + if (module_on_install(ch, ctx->message.payload_size, + &received_size)) { + if (received_size == ctx->message.payload_size) { + /* whole wasm app received */ + ctx->phase = Phase_Non_Start; + return 1; + } + } else { + /* receive or handle fail */ + ctx->phase = Phase_Non_Start; + ctx->size_in_phase = 0; + return 0; + } + return 0; + } else { + ctx->phase = Phase_Non_Start; + ctx->size_in_phase = 0; + return 0; + } + } else { + ctx->message.payload[ctx->size_in_phase++] = ch; + + if (ctx->size_in_phase == ctx->message.payload_size) { + ctx->phase = Phase_Non_Start; + if (enable_log) + app_manager_printf("##On byte arrive: receive end, payload_size is %d.\n", + ctx->message.payload_size); + return 1; + } + return 0; + } + } + + return 0; +} + +int aee_host_msg_callback(void *msg, uint16_t msg_len) +{ + unsigned char *p = msg, *p_end = p + msg_len; + + /*app_manager_printf("App Manager receive %d bytes from Host\n", msg_len);*/ + + for (; p < p_end; p++) { + int ret = on_imrt_link_byte_arrive(*p, &recv_ctx); + + if (ret == 1) { + if (recv_ctx.message.payload) { + int msg_type = recv_ctx.message.message_type; + + if (msg_type == REQUEST_PACKET) { + request_t request; + memset(&request, 0, sizeof(request)); + + if (!unpack_request(recv_ctx.message.payload, + recv_ctx.message.payload_size, &request)) + continue; + + request.sender = ID_HOST; + + am_dispatch_request(&request); + } else { + app_manager_printf("unexpected host msg type: %d\n", msg_type); + } + + APP_MGR_FREE(recv_ctx.message.payload); + recv_ctx.message.payload = NULL; + recv_ctx.message.payload_size = 0; + } + + memset(&recv_ctx, 0, sizeof(recv_ctx)); + } + } + + return 0; +} + +bool app_manager_host_init(host_interface *interface) +{ + os_mutex_init(&host_lock); + memset(&recv_ctx, 0, sizeof(recv_ctx)); + + host_commu.init = interface->init; + host_commu.send = interface->send; + host_commu.destroy = interface->destroy; + + if (host_commu.init != NULL) + return host_commu.init(); + + return true; +} + +int app_manager_host_send_msg(int msg_type, const char *buf, int size) +{ + /* send an IMRT LINK message contains the buf as payload */ + if (host_commu.send != NULL) { + int size_s = size, n; + char header[16]; + + os_mutex_lock(&host_lock); + /* leading bytes */ + bh_memcpy_s(header, 2, leadings, 2); + + /* message type */ + // todo: check if use network byte order!!! + *((uint16*) (header + 2)) = htons(msg_type); + + /* payload length */ + if (is_little_endian()) + exchange32((uint8*) &size_s); + + bh_memcpy_s(header + 4, 4, &size_s, 4); + n = host_commu.send(NULL, header, 8); + if (n != 8) { + os_mutex_unlock(&host_lock); + return 0; + } + + /* payload */ + n = host_commu.send(NULL, buf, size); + os_mutex_unlock(&host_lock); + + app_manager_printf("sent %d bytes to host\n", n); + return n; + } else { + app_manager_printf("no send api provided\n"); + } + return 0; +} diff --git a/wamr/core/app-mgr/app-manager/app_manager_host.h b/wamr/core/app-mgr/app-manager/app_manager_host.h new file mode 100644 index 0000000..cc66372 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/app_manager_host.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _APP_MANAGER_HOST_H_ +#define _APP_MANAGER_HOST_H_ + +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define HOST_MODE_AON 1 +#define HOST_MODE_UART 2 +#define HOST_MODE_TEST 3 + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif + diff --git a/wamr/core/app-mgr/app-manager/app_mgr.cmake b/wamr/core/app-mgr/app-manager/app_mgr.cmake new file mode 100644 index 0000000..fd6e690 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/app_mgr.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (__APP_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${__APP_MGR_DIR}) + + +file (GLOB source_all ${__APP_MGR_DIR}/*.c ${__APP_MGR_DIR}/platform/${WAMR_BUILD_PLATFORM}/*.c) + +set (APP_MGR_SOURCE ${source_all}) + +file (GLOB header + ${__APP_MGR_DIR}/module_wasm_app.h +) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) + diff --git a/wamr/core/app-mgr/app-manager/ble_msg.c b/wamr/core/app-mgr/app-manager/ble_msg.c new file mode 100644 index 0000000..179eb78 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/ble_msg.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#if 0 + +#define BLUETOOTH_INTERFACE_ADVERTISMENT_DATA_LENGTH 31 +/* ble_device_info */ +typedef struct ble_device_info { + + /* address type */ + uint8_t address_type; + /* MAC of Device */ + uint8_t mac[6]; + /* security level */ + uint8_t security_level; + /* signal strength */ + int8_t rssi; + /* uuid_16_type */ + int8_t uuid_16_type; + /* uuid_32_type */ + int8_t uuid_32_type; + /* uuid_128_type */ + int8_t uuid_128_type; + /* error code */ + uint8_t error_code; + /* scan response length*/ + uint16_t adv_data_len; + /* advertisement data */ + uint8_t *adv_data; + /* scan response length*/ + uint16_t scan_response_len; + /* scan response */ + uint8_t *scan_response; + /* next device */ + struct ble_device_info *next; + /* private data length */ + int private_data_length; + /* private data */ + uint8_t *private_data; + /* value handle*/ + uint16_t value_handle; + /* ccc handle*/ + uint16_t ccc_handle; + +}ble_device_info; + +/* BLE message sub type */ +typedef enum BLE_SUB_EVENT_TYPE { + BLE_SUB_EVENT_DISCOVERY, + BLE_SUB_EVENT_CONNECTED, + BLE_SUB_EVENT_DISCONNECTED, + BLE_SUB_EVENT_NOTIFICATION, + BLE_SUB_EVENT_INDICATION, + BLE_SUB_EVENT_PASSKEYENTRY, + BLE_SUB_EVENT_SECURITY_LEVEL_CHANGE +}BLE_SUB_EVENT_TYPE; + +/* Queue message, for BLE Event */ +typedef struct bh_queue_ble_sub_msg_t { + /* message type, should be one of QUEUE_MSG_TYPE */ + BLE_SUB_EVENT_TYPE type; + /* payload size */ + /*uint32_t payload_size;*/ + char payload[1]; +}bh_queue_ble_sub_msg_t; + +static void +app_instance_free_ble_msg(char *msg) +{ + bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; + ble_device_info *dev_info; + + dev_info = (ble_device_info *) ble_msg->payload; + + if (dev_info->scan_response != NULL) + APP_MGR_FREE(dev_info->scan_response); + + if (dev_info->private_data != NULL) + APP_MGR_FREE(dev_info->private_data); + + if (dev_info->adv_data != NULL) + APP_MGR_FREE(dev_info->adv_data); + + if (dev_info != NULL) + APP_MGR_FREE(dev_info); +} + +static void +app_instance_queue_free_callback(bh_message_t queue_msg) +{ + + char * payload = (char *)bh_message_payload(queue_msg); + if(payload == NULL) + return; + + switch (bh_message_type(queue_msg)) + { + /* + case SENSOR_EVENT: { + bh_sensor_event_t *sensor_event = (bh_sensor_event_t *) payload; + attr_container_t *event = sensor_event->event; + attr_container_destroy(event); + } + break; + */ + case BLE_EVENT: { + app_instance_free_ble_msg(payload); + break; + } + } +} + +#endif diff --git a/wamr/core/app-mgr/app-manager/coding_rule.txt b/wamr/core/app-mgr/app-manager/coding_rule.txt new file mode 100644 index 0000000..4598872 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/coding_rule.txt @@ -0,0 +1,15 @@ +Coding rules: + +1. module implementation can include the export head files of associated runtime + +2. app manager only call access the module implementation through the interface API + +3. module implementation can call the app manager API from following files: + - util.c + - message.c + +4. platform API: To define it + +5. Any platform dependent implementation of app manager should be implemented in the + platform specific source file, such as app_mgr_zephyr.c + diff --git a/wamr/core/app-mgr/app-manager/event.c b/wamr/core/app-mgr/app-manager/event.c new file mode 100644 index 0000000..f2c49df --- /dev/null +++ b/wamr/core/app-mgr/app-manager/event.c @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +#include "event.h" + +#include "app_manager.h" +#include "coap_ext.h" + +typedef struct _subscribe { + struct _subscribe * next; + uint32 subscriber_id; +} subscribe_t; + +typedef struct _event { + struct _event *next; + int subscriber_size; + subscribe_t * subscribers; + char url[1]; /* event url */ +} event_reg_t; + +event_reg_t *g_events = NULL; + +static bool find_subscriber(event_reg_t * reg, uint32 id, bool remove_found) +{ + subscribe_t* c = reg->subscribers; + subscribe_t * prev = NULL; + while (c) { + subscribe_t * next = c->next; + if (c->subscriber_id == id) { + if (remove_found) { + if (prev) + prev->next = next; + else + reg->subscribers = next; + + APP_MGR_FREE(c); + } + + return true; + } else { + prev = c; + c = next; + } + } + + return false; +} + +static bool check_url(const char *url) +{ + if (*url == 0) + return false; + + return true; +} + +bool am_register_event(const char *url, uint32_t reg_client) +{ + event_reg_t *current = g_events; + + app_manager_printf("am_register_event adding url:(%s)\n", url); + + if (!check_url(url)) { + app_manager_printf("am_register_event: invaild url:(%s)\n", url); + return false; + } + while (current) { + if (strcmp(url, current->url) == 0) + break; + current = current->next; + } + + if (current == NULL) { + if (NULL + == (current = (event_reg_t *) APP_MGR_MALLOC( + offsetof(event_reg_t, url) + strlen(url) + 1))) { + app_manager_printf("am_register_event: malloc fail\n"); + return false; + } + + memset(current, 0, sizeof(event_reg_t)); + bh_strcpy_s(current->url, strlen(url) + 1, url); + current->next = g_events; + g_events = current; + } + + if (find_subscriber(current, reg_client, false)) { + return true; + } else { + subscribe_t * s = (subscribe_t*) APP_MGR_MALLOC(sizeof(subscribe_t)); + if (s == NULL) + return false; + + memset(s, 0, sizeof(subscribe_t)); + s->subscriber_id = reg_client; + s->next = current->subscribers; + current->subscribers = s; + app_manager_printf("client: %d registered event (%s)\n", reg_client, + url); + } + + return true; +} + +// @url: NULL means the client wants to unregister all its subscribed items +bool am_unregister_event(const char *url, uint32_t reg_client) +{ + event_reg_t *current = g_events, *pre = NULL; + + while (current != NULL) { + if (url == NULL || strcmp(current->url, url) == 0) { + event_reg_t * next = current->next; + if (find_subscriber(current, reg_client, true)) { + app_manager_printf("client: %d deregistered event (%s)\n", + reg_client, current->url); + } + + // remove the registration if no client subscribe it + if (current->subscribers == NULL) { + app_manager_printf("unregister for event deleted url:(%s)\n", + current->url); + if (pre) + pre->next = next; + else + g_events = next; + APP_MGR_FREE(current); + current = next; + continue; + } + } + pre = current; + current = current->next; + } + + return true; +} + +bool event_handle_event_request(uint8_t code, const char *event_url, + uint32_t reg_client) +{ + if (code == COAP_PUT) { /* register */ + return am_register_event(event_url, reg_client); + } else if (code == COAP_DELETE) { /* unregister */ + return am_unregister_event(event_url, reg_client); + } else { + /* invalid request */ + return false; + } +} + +void am_publish_event(request_t * event) +{ + bh_assert(event->action == COAP_EVENT); + + event_reg_t *current = g_events; + while (current) { + if (0 == strcmp(event->url, current->url)) { + subscribe_t* c = current->subscribers; + while (c) { + if (c->subscriber_id == ID_HOST) { + send_request_to_host(event); + } else { + module_request_handler + (event, (void *)(uintptr_t)c->subscriber_id); + } + c = c->next; + } + + return; + } + + current = current->next; + } +} + +bool event_is_registered(const char *event_url) +{ + event_reg_t *current = g_events; + + while (current != NULL) { + if (strcmp(current->url, event_url) == 0) { + return true; + } + current = current->next; + } + + return false; +} diff --git a/wamr/core/app-mgr/app-manager/event.h b/wamr/core/app-mgr/app-manager/event.h new file mode 100644 index 0000000..a9c3de3 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/event.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _EVENT_H_ +#define _EVENT_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Handle event request from host agent + * + * @param code the coap packet code + * @param event_url the event url + * + * @return true if success, false otherwise + */ +bool +event_handle_event_request(uint8_t code, const char *event_url, + uint32_t register); + +/** + * Test whether the event is registered + * + * @param event_url the event url + * + * @return true for registered, false for not registered + */ +bool +event_is_registered(const char *event_url); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _EVENT_H_ */ diff --git a/wamr/core/app-mgr/app-manager/message.c b/wamr/core/app-mgr/app-manager/message.c new file mode 100644 index 0000000..2aed147 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/message.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager.h" +#include "app_manager_host.h" +#include "event.h" +#include "bi-inc/attr_container.h" +#include "coap_ext.h" + +#if 0 +bool send_coap_packet_to_host(coap_packet_t * packet) +{ + int size; + uint8_t *buf; + + size = coap_serialize_message_tcp(&packet, &buf); + if (!buf || size == 0) + return false; + + app_manager_host_send_msg(buf, size); + APP_MGR_FREE(buf); + + return true; +} +#endif + +bool send_request_to_host(request_t *msg) +{ + if (COAP_EVENT == msg->action && !event_is_registered(msg->url)) { + app_manager_printf("Event is not registered\n"); + return false; + } + + int size; + char * packet = pack_request(msg, &size); + if (packet == NULL) + return false; + + app_manager_host_send_msg(REQUEST_PACKET, packet, size); + + free_req_resp_packet(packet); + + return true; +} + +bool send_response_to_host(response_t *response) +{ + int size; + char * packet = pack_response(response, &size); + if (packet == NULL) + return false; + + app_manager_host_send_msg(RESPONSE_PACKET, packet, size); + + free_req_resp_packet(packet); + + return true; +} + +bool send_error_response_to_host(int mid, int status, const char *msg) +{ + int payload_len = 0; + attr_container_t *payload = NULL; + response_t response[1] = { 0 }; + + if (msg) { + payload = attr_container_create(""); + if (payload) { + attr_container_set_string(&payload, "error message", msg); + payload_len = attr_container_get_serialize_length(payload); + } + } + + set_response(response, status, + FMT_ATTR_CONTAINER, (const char *)payload, payload_len); + response->mid = mid; + + send_response_to_host(response); + + if (payload) + attr_container_destroy(payload); + return true; +} + diff --git a/wamr/core/app-mgr/app-manager/module_config.h b/wamr/core/app-mgr/app-manager/module_config.h new file mode 100644 index 0000000..b742fed --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_config.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _MODULE_CONFIG_H_ +#define _MODULE_CONFIG_H_ + +#define ENABLE_MODULE_JEFF 0 +#define ENABLE_MODULE_WASM_APP 1 +#define ENABLE_MODULE_WASM_LIB 1 + +#ifdef ENABLE_MODULE_JEFF +#include "module_jeff.h" +#endif +#ifdef ENABLE_MODULE_WASM_APP +#include "module_wasm_app.h" +#endif +#ifdef ENABLE_MODULE_WASM_LIB +#include "module_wasm_lib.h" +#endif + +#endif /* _MODULE_CONFIG_H_ */ diff --git a/wamr/core/app-mgr/app-manager/module_jeff.c b/wamr/core/app-mgr/app-manager/module_jeff.c new file mode 100644 index 0000000..7e1a8be --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_jeff.c @@ -0,0 +1,1733 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifdef ENABLE_JEFF + +#include "module_jeff.h" +#include "jeff_export.h" +#include "../vmcore_jeff/jeff-runtime.h" +#include "../vmcore_jeff/jeff-thread.h" +#include "../vmcore_jeff/jeff-buffer.h" +#include "../vmcore_jeff/jeff-tool.h" +#include "../vmcore_jeff/jeff-tool-priv.h" +#include "app_manager-host.h" +#include "bh_queue.h" +#include "attr-container.h" +#include "attr-container-util.h" +#include "bh_thread.h" +#include "ems_gc.h" +#include "coap_ext.h" +#include "libcore.h" +#include "event.h" +#include "watchdog.h" + +#define DEFAULT_APPLET_TIMEOUT (3 * 60 * 1000) +#define DEFAULT_APPLET_HEAP_SIZE (48 * 1024) +#define MIN_APPLET_HEAP_SIZE (2 * 1024) +#define MAX_APPLET_HEAP_SIZE (1024 * 1024) + +typedef struct jeff_applet_data { + /* Java Applet Object */ + JeffObjectRef applet_obj; + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + /* Whether the applet is in debug mode */ + bool debug_mode; + /* Queue of the tool agent */ + bh_queue *tool_agent_queue; +#endif + + /* VM instance */ + JeffInstanceLocalRoot *vm_instance; + /* Applet Main file */ + JeffFileHeaderLinked *main_file; + /* Permissions of the Java Applet */ + char *perms; +}jeff_applet_data; + +/* Jeff class com.intel.aee.AEEApplet */ +static JeffClassHeaderLinked *class_AEEApplet; +/* Jeff class com.intel.aee.Request */ +static JeffClassHeaderLinked *class_AEERequest; +/* Jeff class com.intel.aee.Timer */ +static JeffClassHeaderLinked *class_Timer; +/* Jeff class com.intel.aee.Sensor */ +static JeffClassHeaderLinked *class_Sensor; +/* Jeff class com.intel.aee.ble.BLEManager */ +static JeffClassHeaderLinked *class_BLEManager; +/* Jeff class com.intel.aee.ble.BLEDevice */ +static JeffClassHeaderLinked *class_BLEDevice; +/* Jeff class com.intel.aee.ble.BLEGattService */ +JeffClassHeaderLinked *class_BLEGattService; +/* Jeff class com.intel.aee.ble.BLEGattCharacteristic */ +JeffClassHeaderLinked *class_BLEGattCharacteristic; +/* Jeff class com.intel.aee.ble.BLEGattDescriptor */ +JeffClassHeaderLinked *class_BLEGattDescriptor; +/* Jeff class com.intel.aee.gpio.GPIOChannel */ +static JeffClassHeaderLinked *class_GPIOChannel; +/* Jeff method void com.intel.aee.AEEApplet.onInit() */ +static JeffMethodLinked *method_AEEApplet_onInit; +/* Jeff method void com.intel.aee.AEEApplet.onDestroy() */ +static JeffMethodLinked *method_AEEApplet_onDestroy; +/* Jeff method void com.intel.aee.AEEApplet.callOnRequest(Request request) */ +static JeffMethodLinked *method_AEEApplet_callOnRequest; +/* Jeff method void com.intel.aee.Timer.callOnTimer() */ +static JeffMethodLinked *method_callOnTimer; +/* Jeff method void com.intel.aee.Sensor.callOnSensorEvent() */ +static JeffMethodLinked *method_callOnSensorEvent; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEStartDiscovery() */ +static JeffMethodLinked *method_callOnBLEStartDiscovery; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEConnected() */ +static JeffMethodLinked *method_callOnBLEConnected; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEDisonnected() */ +static JeffMethodLinked *method_callOnBLEDisconnected; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLENotification() */ +static JeffMethodLinked *method_callOnBLENotification; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEIndication() */ +static JeffMethodLinked *method_callOnBLEIndication; +/* Jeff method void com.intel.aee.ble.BLEManager.callOnBLEPasskeyEntry() */ +static JeffMethodLinked *method_callOnBLEPasskeyEntry; +/* Jeff method void com.intel.aee.gpio.GPIOChannel.callOnGPIOInterrupt() */ +static JeffMethodLinked *method_callOnGPIOInterrupt; +/* Jeff method void com.intel.aee.ble.BLEManager.getBLEDevice() */ +static JeffMethodLinked *method_callOnBLEManagerGetBLEDevice; + +static jeff_applet_data * +app_manager_get_jeff_applet_data() +{ + module_data *m_data = app_manager_get_module_data(Module_Jeff); + return (jeff_applet_data *)m_data->internal_data; +} + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 +void * +app_manager_get_tool_agent_queue() +{ + return app_manager_get_jeff_applet_data()->tool_agent_queue; +} +#endif + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 +static bool +is_tool_agent_running(module_data *m_data) +{ + jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; + return (applet_data->debug_mode + && applet_data->tool_agent_queue + && applet_data->vm_instance->tool_agent); +} +#endif + +static char * +get_class_qname(const JeffString *pname, const JeffString *cname) +{ + unsigned int length = pname->length ? pname->length + 2 + cname->length + : cname->length + 1; + char *buf = APP_MGR_MALLOC(length), *p; + + if (!buf) + return NULL; + + p = buf; + if (pname->length) { + bh_memcpy_s(p, pname->length, pname->value, pname->length); + p += pname->length; + *p++ = '.'; + } + + bh_memcpy_s(p, cname->length, cname->value, cname->length); + p += cname->length; + *p = '\0'; + + return buf; +} + +static void +send_exception_event_to_host(const char *applet_name, const char *exc_name) +{ + attr_container_t *payload; + bh_request_msg_t msg; + char *url; + int url_len; + + payload = attr_container_create("exception detail"); + if (!payload) { + app_manager_printf("Send exception to host fail: allocate memory"); + return; + } + + if (!attr_container_set_string(&payload, "exception name", exc_name) + || !attr_container_set_string(&payload, "stack trace", "TODO") + || !attr_container_set_string(&payload, "applet name", applet_name)) { + app_manager_printf("Send exception to host fail: set attr"); + goto fail; + } + + url_len = strlen("/exception/") + strlen(applet_name); + url = APP_MGR_MALLOC(url_len + 1); + if (!url) { + app_manager_printf("Send exception to host fail: allocate memory"); + goto fail; + } + memset(url, 0, url_len + 1); + bh_strcpy_s(url, url_len + 1, "/exception/"); + bh_strcat_s(url, url_len + 1, applet_name); + + memset(&msg, 0, sizeof(msg)); + msg.url = url; + msg.action = COAP_PUT; + msg.payload = (char *)payload; + + app_send_request_msg_to_host(&msg); + + APP_MGR_FREE(url); + + fail: + attr_container_destroy(payload); +} + +static bool +check_exception() +{ + if (jeff_runtime_get_exception()) { + jeff_printf("V1.Exception thrown when running applet '%s':\n", + app_manager_get_module_name(Module_Jeff)); + jeff_runtime_print_exception(); + jeff_printf("\n"); + jeff_printf(NULL); + + if (!app_manager_is_interrupting_module(Module_Jeff)) { + attr_container_t *payload; + int payload_len; + JeffClassHeaderLinked *exc_class = jeff_object_class_pointer(jeff_runtime_get_exception()); + char *qname_buf = get_class_qname(jeff_get_class_pname(exc_class), + jeff_get_class_cname(exc_class)); + + /* Send exception event to host */ + if (qname_buf) { + send_exception_event_to_host(app_manager_get_module_name(Module_Jeff), qname_buf); + APP_MGR_FREE(qname_buf); + } + + /* Uninstall the applet */ + if ((payload = attr_container_create("uninstall myself"))) { + if (attr_container_set_string(&payload, "name", app_manager_get_module_name(Module_Jeff)) + /* Set special flag to prevent app manager making response since this is an internal message */ + && attr_container_set_bool(&payload, "do not reply me", true)) { + request_t request = {0}; + payload_len = attr_container_get_serialize_length(payload); + + init_request(request, "/applet", COAP_DELETE, (char *)payload, payload_len)); + app_mgr_lookup_resource(&request); + + // TODO: confirm this is right + attr_container_destroy(payload); + } + } + + jeff_runtime_set_exception(NULL); + return true; + } + + return false; + } + + static bool + app_manager_initialize_class(JeffClassHeaderLinked *c) + { + jeff_runtime_initialize_class(c); + return !check_exception(); + } + + static bool + app_manager_initialize_object(JeffObjectRef obj) + { + jeff_runtime_initialize_object(obj); + return !check_exception(); + } + + static bool + app_manager_call_java(JeffMethodLinked *method, + unsigned int argc, uint32 argv[], uint8 argt[]) + { + module_data *m_data = app_manager_get_module_data(Module_Jeff); + watchdog_timer *wd_timer = &m_data->wd_timer; + bool is_wd_started = false; + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + /* Only start watchdog when debugger is not running */ + if (!is_tool_agent_running(m_data)) { +#endif + watchdog_timer_start(wd_timer); + is_wd_started = true; +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + } +#endif + + jeff_runtime_call_java(method, argc, argv, argt); + + if (is_wd_started) { + os_mutex_lock(&wd_timer->lock); + if (!wd_timer->is_interrupting) { + wd_timer->is_stopped = true; + watchdog_timer_stop(wd_timer); + } + os_mutex_unlock(&wd_timer->lock); + } + + return !check_exception(); + } + + static AEEBLEDevice + create_object_BLEDevice(ble_device_info *dev_info) + { + JeffLocalObjectRef ref; + AEEBLEDevice dev_struct; + + jeff_runtime_push_local_object_ref(&ref); + + ref.val = jeff_runtime_new_object(class_BLEDevice); + + if (!ref.val) { + jeff_runtime_pop_local_object_ref(1); + return NULL; + } + + dev_struct = (AEEBLEDevice) (ref.val); + dev_struct->rssi = dev_info->rssi; + dev_struct->mac = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->mac, 6); + + app_manager_printf("adv_data_len:%d,scan_response_len:%d\n", dev_info->adv_data_len, dev_info->scan_response_len); + + dev_struct->advData = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->adv_data, dev_info->adv_data_len); + dev_struct->scanResponse = (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->scan_response, dev_info->scan_response_len); + dev_struct->addressType = dev_info->address_type; + jeff_runtime_initialize_object(ref.val); + jeff_runtime_pop_local_object_ref(1); + if ((dev_struct->mac == NULL) || (dev_struct->advData == NULL) || (dev_struct->scanResponse == NULL)) { + return NULL; + } + return (AEEBLEDevice) ref.val; + } + + static void + app_instance_process_ble_msg(char *msg) + { + bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; + unsigned int argv[5]; + uint8 argt[5]; + + ble_device_info *dev_info; + + dev_info = (ble_device_info *) ble_msg->payload; + AEEBLEDevice ble_dev; + + argv[0] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array((int8 *)dev_info->mac, 6); + argt[0] = 1; + if (!app_manager_call_java(method_callOnBLEManagerGetBLEDevice, 1, argv, argt)) { + app_manager_printf("app_manager_call_java BLEManagerGetBLEDevice fail error\n"); + goto fail; + } + ble_dev = (AEEBLEDevice) argv[0]; + if (ble_dev == NULL) { + ble_dev = create_object_BLEDevice(dev_info); + if (ble_dev == NULL) { + goto fail; + } + } + + switch (ble_msg->type) { + case BLE_SUB_EVENT_DISCOVERY: + { + argv[0] = (unsigned int) ble_dev; + argt[0] = 1; + ble_dev->rssi = dev_info->rssi; + if (!app_manager_call_java(method_callOnBLEStartDiscovery, 1, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLEStartDiscovery fail error\n"); + goto fail; + } + } + break; + + case BLE_SUB_EVENT_CONNECTED: + { + if (ble_dev) { + argv[0] = (unsigned int) ble_dev; + argv[1] = 0; + argt[0] = 1; + argt[1] = 1; + if (!app_manager_call_java(method_callOnBLEConnected, 2, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLEConnected fail error\n"); + goto fail; + } + } + } + break; + + case BLE_SUB_EVENT_DISCONNECTED: + { + app_manager_printf("app instance received disconnected\n"); + + if (ble_dev) { + argv[0] = (unsigned int) ble_dev; + argv[1] = 0; + argt[0] = 1; + argt[1] = 1; + ble_dev->rssi = dev_info->rssi; + if (!app_manager_call_java(method_callOnBLEDisconnected, 2, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLEDisconnected fail error\n"); + goto fail; + } + } + } + break; + + case BLE_SUB_EVENT_NOTIFICATION: + { + if (ble_dev) { + argv[0] = (unsigned int) ble_dev; + argv[1] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array( + (int8 *)dev_info->private_data, dev_info->private_data_length); + argv[2] = dev_info->value_handle; + argv[3] = dev_info->ccc_handle; + argt[1] = 1; + argt[2] = 0; + argt[3] = 0; + ble_dev->rssi = dev_info->rssi; + if (!app_manager_call_java(method_callOnBLENotification, 4, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLENotification fail error\n"); + goto fail; + } + } + } + break; + + case BLE_SUB_EVENT_INDICATION: + { + if (ble_dev) { + argv[0] = (unsigned int) ble_dev; + argv[1] = (unsigned int) (jbyteArray) jeff_runtime_create_byte_array( + (int8 *)dev_info->private_data, dev_info->private_data_length); + argv[2] = dev_info->value_handle; + argv[3] = dev_info->ccc_handle; + argt[0] = 1; + argt[1] = 1; + argt[2] = 0; + argt[3] = 0; + ble_dev->rssi = dev_info->rssi; + if (!app_manager_call_java(method_callOnBLEIndication, 4, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLEIndication fail error\n"); + goto fail; + } + } + } + break; + + case BLE_SUB_EVENT_PASSKEYENTRY: + { + + if (ble_dev) { + argv[0] = (unsigned int) ble_dev; + argt[0] = 1; + argt[1] = 1; + ble_dev->rssi = dev_info->rssi; + if (!app_manager_call_java(method_callOnBLEPasskeyEntry, 1, argv, argt)) { + app_manager_printf("app_manager_call_java method_callOnBLEPasskeyEntry fail error\n"); + goto fail; + } + } + } + break; + + case BLE_SUB_EVENT_SECURITY_LEVEL_CHANGE: + { + if (ble_dev) { + ble_dev->securityLevel = dev_info->security_level; + } + } + break; + + default: + break; + } + + fail: + if (dev_info->scan_response != NULL) { + APP_MGR_FREE(dev_info->scan_response); + } + if (dev_info->private_data != NULL) { + APP_MGR_FREE(dev_info->private_data); + } + + if (dev_info->adv_data != NULL) { + APP_MGR_FREE(dev_info->adv_data); + } + if (dev_info != NULL) { + APP_MGR_FREE(dev_info); + } + + } + + static void + app_instance_free_ble_msg(char *msg) + { + bh_queue_ble_sub_msg_t *ble_msg = (bh_queue_ble_sub_msg_t *)msg; + ble_device_info *dev_info; + + dev_info = (ble_device_info *) ble_msg->payload; + + if (dev_info->scan_response != NULL) + APP_MGR_FREE(dev_info->scan_response); + + if (dev_info->private_data != NULL) + APP_MGR_FREE(dev_info->private_data); + + if (dev_info->adv_data != NULL) + APP_MGR_FREE(dev_info->adv_data); + + if (dev_info != NULL) + APP_MGR_FREE(dev_info); + } + + static void + app_instance_queue_free_callback(void *queue_msg) + { + bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; + + switch (msg->message_type) { + case APPLET_REQUEST: + { + bh_request_msg_t *req_msg = (bh_request_msg_t *)msg->payload; + APP_MGR_FREE(req_msg); + break; + } + + case TIMER_EVENT: + { + break; + } + + case SENSOR_EVENT: + { + if (msg->payload) { + bh_sensor_event_t *sensor_event = (bh_sensor_event_t *)msg->payload; + attr_container_t *event = sensor_event->event; + + attr_container_destroy(event); + APP_MGR_FREE(sensor_event); + } + break; + } + + case BLE_EVENT: + { + if (msg->payload) { + app_instance_free_ble_msg(msg->payload); + APP_MGR_FREE(msg->payload); + } + break; + } + + case GPIO_INTERRUPT_EVENT: + { + break; + } + + default: + { + break; + } + } + + APP_MGR_FREE(msg); + } + + static void + app_instance_queue_callback(void *queue_msg) + { + bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; + unsigned int argv[5]; + uint8 argt[5]; + + if (app_manager_is_interrupting_module(Module_Jeff)) { + app_instance_queue_free_callback(queue_msg); + return; + } + + switch (msg->message_type) { + case APPLET_REQUEST: + { + JeffLocalObjectRef ref; + AEERequest req_obj; + bh_request_msg_t *req_msg = (bh_request_msg_t *)msg->payload; + attr_container_t *attr_cont = (attr_container_t *)req_msg->payload; + module_data *m_data = app_manager_get_module_data(Module_Jeff); + jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; + + app_manager_printf("Applet %s got request, url %s, action %d\n", + m_data->module_name, req_msg->url, req_msg->action); + + /* Create Request object */ + req_obj = (AEERequest)jeff_object_new(m_data->heap, class_AEERequest); + if (!req_obj) { + app_manager_printf("Applet process request failed: create request obj failed.\n"); + goto fail1; + } + + jeff_runtime_push_local_object_ref(&ref); + ref.val = (JeffObjectRef)req_obj; + + req_obj->mid = req_msg->mid; + req_obj->action = req_msg->action; + req_obj->fmt = req_msg->fmt; + + /* Create Java url string */ + if (req_msg->url) { + req_obj->url = (jstring)jeff_runtime_create_java_string(req_msg->url); + if (!req_obj->url) { + app_manager_printf("Applet process request failed: create url string failed.\n"); + goto fail2; + } + } + + /* Create Java AttributeObject payload */ + if (attr_cont + && !attr_container_to_attr_obj(attr_cont, &req_obj->payload)) { + app_manager_printf("Applet process request failed: convert payload failed.\n"); + goto fail2; + } + + /* Call AEEApplet.callOnRequest(Request request) method */ + argv[0] = (unsigned int)applet_data->applet_obj; + argv[1] = (unsigned int)req_obj; + argt[0] = argt[1] = 1; + app_manager_call_java(method_AEEApplet_callOnRequest, 2, argv, argt); + app_manager_printf("Applet process request success.\n"); + + fail2: + jeff_runtime_pop_local_object_ref(1); + fail1: + APP_MGR_FREE(req_msg); + break; + } + + case TIMER_EVENT: + { + if (msg->payload) { + /* Call Timer.callOnTimer() method */ + argv[0] = (unsigned int)msg->payload; + argt[0] = 1; + app_manager_call_java(method_callOnTimer, 1, argv, argt); + } + break; + } + + case SENSOR_EVENT: + { + if (msg->payload) { + bh_sensor_event_t *sensor_event = (bh_sensor_event_t *)msg->payload; + AEESensor sensor = sensor_event->sensor; + attr_container_t *event = sensor_event->event; + bool ret = attr_container_to_attr_obj(event, &sensor->event); + + attr_container_destroy(event); + APP_MGR_FREE(sensor_event); + + if (ret) { + /* Call Sensor.callOnSensorEvent() method */ + argv[0] = (unsigned int)sensor; + argt[0] = 1; + app_manager_call_java(method_callOnSensorEvent, 1, argv, argt); + } + } + break; + } + + case BLE_EVENT: + { + if (msg->payload) { + app_instance_process_ble_msg(msg->payload); + APP_MGR_FREE(msg->payload); + } + break; + } + + case GPIO_INTERRUPT_EVENT: + { + AEEGPIOChannel gpio_ch = (AEEGPIOChannel)msg->payload; + + if ((gpio_ch == NULL) || (gpio_ch->callback == 0) || (gpio_ch->listener == NULL)) { + break; + } + argv[0] = (unsigned int) gpio_ch; + argt[0] = 1; + bool ret_value = app_manager_call_java(method_callOnGPIOInterrupt, 1, argv, argt); + + if (!ret_value) { + app_manager_printf("app_manager_call_java method_method_callOnGPIOInterrupt return false\n"); + } + break; + } + + default: + { + app_manager_printf("Invalid message type of applet queue message.\n"); + break; + } + } + + APP_MGR_FREE(msg); + } + + static JeffClassHeaderLinked* + find_main_class(JeffFileHeaderLinked *main_file) + { + JeffClassHeaderLinked *c = NULL, *ci; + unsigned int i; + + for (i = 0; i < main_file->internal_class_count; i++) { + ci = main_file->class_header[i]; + + if (jeff_is_super_class(class_AEEApplet, ci) + && (ci->access_flag & JEFF_ACC_PUBLIC)) { + if (c) { + jeff_printe_more_than_one_main_class(); + return NULL; + } + + c = ci; + } + } + + if (!c) + jeff_printe_no_main_class(); + + return c; + } + + /* Java applet thread main routine */ + static void* + app_instance_main(void *arg) + { + module_data *m_data = (module_data *)arg; + jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; + JeffClassHeaderLinked *object_class; + JeffMethodLinked *m; + unsigned int argv[1]; + uint8 argt[1]; + + app_manager_printf("Java Applet '%s' started\n", m_data->module_name); + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + if (applet_data->debug_mode) + jeff_tool_suspend_self(); +#endif + + applet_data->vm_instance->applet_object = applet_data->applet_obj; + object_class = jeff_object_class_pointer(applet_data->applet_obj); + m = jeff_select_method_virtual(object_class, method_AEEApplet_onInit); + bh_assert(m != NULL); + /* Initialize applet class which call */ + if (!app_manager_initialize_class(object_class)) { + app_manager_printf("Call fail\n"); + goto fail; + } + + /* Initialize applet object which call */ + if (!app_manager_initialize_object(applet_data->applet_obj)) { + app_manager_printf("Call fail\n"); + goto fail; + } + + /* Call applet's onInit() method */ + argv[0] = (unsigned int)applet_data->applet_obj; + argt[0] = 1; + if (app_manager_call_java(m, 1, argv, argt)) + /* Enter queue loop run to receive and process applet queue message */ + bh_queue_enter_loop_run(m_data->queue, app_instance_queue_callback); + + fail: + applet_data->vm_instance->applet_object = applet_data->applet_obj; + object_class = jeff_object_class_pointer(applet_data->applet_obj); + m = jeff_select_method_virtual(object_class, method_AEEApplet_onDestroy); + bh_assert(m != NULL); + /* Call User Applet or AEEApplet onDestroy() method */ + app_manager_call_java(m, 1, argv, argt); + if (m != method_AEEApplet_onDestroy) { + /*If 'm' is user onDestroy, then Call AEEApplet.onDestroy() method*/ + app_manager_call_java(method_AEEApplet_onDestroy, 1, argv, argt); + } + app_manager_printf("Applet instance main thread exit.\n"); + return NULL; + } + + static bool + verify_signature(JeffFileHeader *file, unsigned size) + { + uint8 *sig; + unsigned sig_size; + +#if BEIHAI_ENABLE_NO_SIGNATURE != 0 + /* no signature */ + if (file->file_signature == 0) + return true; +#endif + + if (file->file_length != size +#if BEIHAI_ENABLE_NO_SIGNATURE == 0 + || file->file_signature == 0 +#endif + || file->file_signature >= file->file_length) + return false; + + sig = (uint8 *)file + file->file_signature; + sig_size = file->file_length - file->file_signature; + + if (0 == app_manager_signature_verify((uint8_t *)file, file->file_signature, + sig, sig_size)) + return false; + + return true; + } + + /* Install Java Applet */ + static bool + jeff_module_install(bh_request_msg_t *msg) + { + unsigned int size, bpk_file_len, main_file_len, heap_size, timeout; + uint8 *bpk_file; + JeffFileHeaderLinked *main_file; + JeffClassHeaderLinked *main_class; + module_data *m_data; + jeff_applet_data *applet_data; + char *applet_name, *applet_perm; + attr_container_t *attr_cont; + bool debug = false; + + /* Check url */ + if (!msg->url + || strcmp(msg->url, "/applet") != 0) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid url."); + return false; + } + + /* Check payload */ + attr_cont = (attr_container_t *)msg->payload; + if (!attr_cont + || !(bpk_file = (uint8 *) + attr_container_get_as_bytearray(attr_cont, "bpk", &bpk_file_len))) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid bpk file."); + return false; + } + + /* Check applet name */ + applet_name = attr_container_get_as_string(attr_cont, "name"); + + if (!applet_name || strlen(applet_name) == 0) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: invalid applet name."); + return false; + } + + if (app_manager_lookup_module_data(applet_name)) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: applet already installed."); + return false; + } + + /* TODO: convert bpk file to Jeff file */ + main_file_len = bpk_file_len; + main_file = APP_MGR_MALLOC(main_file_len); + if (!main_file) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory failed."); + return false; + } + bh_memcpy_s(main_file, main_file_len, bpk_file, main_file_len); + + /* Verify signature */ + if (!verify_signature((JeffFileHeader *)main_file, main_file_len)) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: verify Jeff file signature failed."); + goto fail1; + } + + /* Load Jeff main file */ + if (!jeff_runtime_load(main_file, main_file_len, false, NULL, NULL)) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: load Jeff file failed."); + goto fail1; + } + + /* Find main class */ + main_class = find_main_class(main_file); + if (!main_class) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: find applet class failed."); + goto fail2; + } + + /* Create module data */ + size = offsetof(module_data, module_name) + strlen(applet_name) + 1; + size = align_uint(size, 4); + m_data = APP_MGR_MALLOC(size + sizeof(jeff_applet_data)); + if (!m_data) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory failed."); + goto fail2; + } + + memset(m_data, 0, size + sizeof(jeff_applet_data)); + m_data->module_type = Module_Jeff; + m_data->internal_data = (uint8*)m_data + size; + applet_data = (jeff_applet_data*)m_data->internal_data; + bh_strcpy_s(m_data->module_name, strlen(applet_name) + 1, applet_name); + applet_data->main_file = main_file; + + /* Set applet execution timeout */ + timeout = DEFAULT_APPLET_TIMEOUT; + if (attr_container_contain_key(attr_cont, "execution timeout")) + timeout = attr_container_get_as_int(attr_cont, "execution timeout"); + m_data->timeout = timeout; + + /* Create applet permissions */ + applet_perm = attr_container_get_as_string(attr_cont, "perm"); + if (applet_perm != NULL) { + applet_data->perms = APP_MGR_MALLOC(strlen(applet_perm) + 1); + if (!applet_data->perms) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: allocate memory for applet permissions failed."); + goto fail3; + } + + bh_strcpy_s(applet_data->perms, strlen(applet_perm) + 1, applet_perm); + } + + /* Create applet queue */ + m_data->queue = bh_queue_create(); + if (!m_data->queue) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet queue failed."); + goto fail3_1; + } + + /* Set heap size */ + heap_size = DEFAULT_APPLET_HEAP_SIZE; + if (attr_container_contain_key(attr_cont, "heap size")) { + heap_size = attr_container_get_as_int(attr_cont, "heap size"); + if (heap_size < MIN_APPLET_HEAP_SIZE) + heap_size = MIN_APPLET_HEAP_SIZE; + else if (heap_size > MAX_APPLET_HEAP_SIZE) + heap_size = MAX_APPLET_HEAP_SIZE; + } + + m_data->heap_size = heap_size; + + /* Create applet heap */ + m_data->heap = gc_init_for_instance(heap_size); + if (!m_data->heap) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create heap failed."); + goto fail4; + } + + /* Create applet object */ + applet_data->applet_obj = jeff_object_new(m_data->heap, main_class); + if (!applet_data->applet_obj) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet object failed."); + goto fail5; + } + + /* Initialize watchdog timer */ + if (!watchdog_timer_init(m_data)) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create applet watchdog timer failed."); + goto fail5; + } + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + /* Check whether applet is debuggable */ + if (attr_container_contain_key(attr_cont, "debug")) + debug = attr_container_get_as_bool(attr_cont, "debug"); + + applet_data->debug_mode = debug; + + /* Create tool agent queue */ + if (debug && !(applet_data->tool_agent_queue = bh_queue_create())) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create tool agent queue failed."); + goto fail5_1; + } +#endif + + /* Create applet instance */ + applet_data->vm_instance = + jeff_runtime_create_instance(main_file, m_data->heap, 16, + app_instance_main, m_data, + NULL); + if (!applet_data->vm_instance) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: create Java VM failed"); + goto fail6; + } + + /* Add applet data to applet data list */ + applet_data->vm_instance->applet_object = applet_data->applet_obj; + app_manager_add_module_data(m_data); + app_manager_post_applets_update_event(); + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + /* Start tool agent thread */ + if (debug && !jeff_tool_start_agent(applet_data->vm_instance, applet_data->tool_agent_queue)) { + SEND_ERR_RESPONSE(msg->mid, "Install Applet failed: start tool agent failed"); + goto fail6; + } +#endif + + app_manager_printf("Install Applet success!\n"); + app_send_response_to_host(msg->mid, CREATED_2_01, NULL); /* CREATED */ + return true; + + fail6: +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + if (debug) + bh_queue_destroy(applet_data->tool_agent_queue); +#endif + + fail5_1: + watchdog_timer_destroy(&m_data->wd_timer); + + fail5: + gc_destroy_for_instance(m_data->heap); + + fail4: + bh_queue_destroy(m_data->queue, NULL); + + fail3_1: + APP_MGR_FREE(applet_data->perms); + + fail3: + APP_MGR_FREE(applet_data); + + fail2: + jeff_runtime_unload(main_file); + + fail1: + APP_MGR_FREE(main_file); + + return false; + } + + static void + cleanup_applet_resource(module_data *m_data) + { + jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; + + /* Unload Jeff main file and free it */ + jeff_runtime_unload(applet_data->main_file); + APP_MGR_FREE(applet_data->main_file); + + /* Destroy queue */ + bh_queue_destroy(m_data->queue, app_instance_queue_free_callback); + + /* Destroy heap */ + gc_destroy_for_instance(m_data->heap); + + /* Destroy watchdog timer */ + watchdog_timer_destroy(&m_data->wd_timer); + + /* Remove module data from module data list and free it */ + app_manager_del_module_data(m_data); + APP_MGR_FREE(applet_data->perms); + APP_MGR_FREE(m_data); + } + + /* Uninstall Java Applet */ + static bool + jeff_module_uninstall(bh_request_msg_t *msg) + { + module_data *m_data; + jeff_applet_data *applet_data; + attr_container_t *attr_cont; + char *applet_name; + bool do_not_reply = false; + + /* Check payload and applet name*/ + attr_cont = (attr_container_t *)msg->payload; + + /* Check whether need to reply this request */ + if (attr_container_contain_key(attr_cont, "do not reply me")) + do_not_reply = attr_container_get_as_bool(attr_cont, "do not reply me"); + + /* Check url */ + if (!msg->url + || strcmp(msg->url, "/applet") != 0) { + if (!do_not_reply) + SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invalid url."); + else + app_manager_printf("Uninstall Applet failed: invalid url."); + return false; + } + + if (!attr_cont + || !(applet_name = attr_container_get_as_string(attr_cont, "name")) + || strlen(applet_name) == 0) { + if (!do_not_reply) + SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invalid applet name."); + else + app_manager_printf("Uninstall Applet failed: invalid applet name."); + return false; + } + + m_data = app_manager_lookup_module_data(applet_name); + if (!m_data) { + if (!do_not_reply) + SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: no applet found."); + else + app_manager_printf("Uninstall Applet failed: no applet found."); + return false; + } + + if (m_data->module_type != Module_Jeff) { + if (!do_not_reply) + SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: invlaid module type."); + else + app_manager_printf("Uninstall Applet failed: invalid module type."); + return false; + } + + if (m_data->wd_timer.is_interrupting) { + if (!do_not_reply) + SEND_ERR_RESPONSE(msg->mid, "Uninstall Applet failed: applet is being interrupted by watchdog."); + else + app_manager_printf("Uninstall Applet failed: applet is being interrupted by watchdog."); + return false; + } + + /* Exit applet queue loop run */ + bh_queue_exit_loop_run(m_data->queue); + + applet_data = (jeff_applet_data*)m_data->internal_data; +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + /* Exit tool agent queue loop run */ + if (is_tool_agent_running(m_data)) { + bh_queue_exit_loop_run(applet_data->tool_agent_queue); + } +#endif + + /* Wait the end of the applet instance and then destroy it */ + if (applet_data->vm_instance->main_file) + jeff_runtime_wait_for_instance(applet_data->vm_instance, -1); + jeff_runtime_destroy_instance(applet_data->vm_instance); + + cleanup_applet_resource(m_data); + app_manager_post_applets_update_event(); + + app_manager_printf("Uninstall Applet success!\n"); + + if (!do_not_reply) + app_send_response_to_host(msg->mid, DELETED_2_02, NULL); /* DELETED */ + return true; + } + +#define PERM_PREFIX "AEE.permission." + + static bool + check_permission_format(const char *perm) + { + const char *prefix = PERM_PREFIX; + const char *p; + + if (perm == NULL || strncmp(perm, prefix, strlen(prefix)) != 0 + || *(p = perm + strlen(prefix)) == '\0') + return false; + + do { + if (!(*p == '.' || ('A' <= *p && *p <= 'Z') || ('a' <= *p && *p <= 'z'))) + return false; + }while (*++p != '\0'); + + return true; + } + + static bool + match(const char *haystack, const char *needle, char delim) + { + const char *p = needle; + + if (haystack == NULL || *haystack == '\0' + || needle == NULL || *needle == '\0') + return false; + + while (true) { + while (true) { + if ((*haystack == '\0' || *haystack == delim) && *p == '\0') { + return true; + } else if (*p == *haystack) { + ++p; + ++haystack; + } else { + break; + } + } + while (*haystack != '\0' && *haystack != delim) { + ++haystack; + } + if (*haystack == '\0') { + return false; + } else { + ++haystack; + p = needle; + } + } + } + + bool + bh_applet_check_permission(const char *perm) + { + return check_permission_format(perm) + && match(app_manager_get_jeff_applet_data()->perms, + perm + strlen(PERM_PREFIX), ' '); + } + + static bool + jeff_module_init() + { + JeffDescriptorFull d[] = { {JEFF_TYPE_VOID, 0, NULL}}; + JeffDescriptorFull d1[] = { + { JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, NULL}, + { JEFF_TYPE_VOID, 0, NULL} + }; + + /* Resolve class com.intel.aee.AEEApplet */ + class_AEEApplet = jeff_runtime_resolve_class_full_name("com.intel.aee.AEEApplet"); + if (!class_AEEApplet) { + app_manager_printf("App Manager start failed: resolve class AEEApplet failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.Request */ + class_AEERequest = jeff_runtime_resolve_class_full_name("com.intel.aee.Request"); + if (!class_AEERequest) { + app_manager_printf("App Manager start failed: resolve class Request failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.Timer */ + class_Timer = jeff_runtime_resolve_class_full_name("com.intel.aee.Timer"); + if (!class_Timer) { + app_manager_printf("App Manager start failed: resolve class Timer failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.Sensor */ + class_Sensor = jeff_runtime_resolve_class_full_name("com.intel.aee.Sensor"); + if (!class_Sensor) { + app_manager_printf("App Manager start failed: resolve class Sensor failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.ble.BLEManager */ + class_BLEManager = jeff_runtime_resolve_class_full_name( + "com.intel.aee.ble.BLEManager"); + if (!class_BLEManager) { + app_manager_printf( + "App Manager start failed: resolve class BLEManager failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.ble.BLEDevice */ + class_BLEDevice = jeff_runtime_resolve_class_full_name( + "com.intel.aee.ble.BLEDevice"); + if (!class_BLEDevice) { + app_manager_printf( + "App Manager start failed: resolve class BLEDevice failed.\n"); + return false; + } + /* Resolve class com.intel.aee.ble.BLEDevice */ + class_BLEGattService = jeff_runtime_resolve_class_full_name( + "com.intel.aee.ble.BLEGattService"); + if (!class_BLEGattService) { + app_manager_printf( + "App Manager start failed: resolve class BLEGattService failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.ble.BLEDevice */ + class_BLEGattCharacteristic = jeff_runtime_resolve_class_full_name( + "com.intel.aee.ble.BLEGattCharacteristic"); + if (!class_BLEGattCharacteristic) { + app_manager_printf( + "App Manager start failed: resolve class BLEGattCharacteristic failed.\n"); + return false; + } + + /* Resolve class com.intel.aee.ble.BLEDevice */ + class_BLEGattDescriptor = jeff_runtime_resolve_class_full_name( + "com.intel.aee.ble.BLEGattDescriptor"); + if (!class_BLEGattDescriptor) { + app_manager_printf( + "App Manager start failed: resolve class BLEGattDescriptor failed.\n"); + return false; + } + /* Resolve class com.intel.aee.gpio.GPIOChannel */ + class_GPIOChannel = jeff_runtime_resolve_class_full_name( + "com.intel.aee.gpio.GPIOChannel"); + if (!class_GPIOChannel) { + app_manager_printf( + "App Manager start failed: resolve class GPIOChannel failed.\n"); + return false; + } + + /* Resolve method com.intel.aee.AEEApplet.onInit() */ + method_AEEApplet_onInit = jeff_lookup_method(class_AEEApplet, "onInit", 0, d); + if (!method_AEEApplet_onInit) { + app_manager_printf("App Manager start failed: resolve method Applet.onInit() failed.\n"); + return false; + } + + /* Resolve method com.intel.aee.AEEApplet.onDestroy() */ + method_AEEApplet_onDestroy = jeff_lookup_method(class_AEEApplet, "onDestroy", 0, d); + if (!method_AEEApplet_onDestroy) { + app_manager_printf("App Manager start failed: resolve method AEEApplet.onDestroy() failed.\n"); + return false; + } + + /* Resolve method com.intel.aee.AEEApplet.callOnRequest(Request) */ + d1[0].class_header = class_AEERequest; + method_AEEApplet_callOnRequest = jeff_lookup_method(class_AEEApplet, "callOnRequest", 1, d1); + if (!method_AEEApplet_callOnRequest) { + app_manager_printf("App Manager start failed: resolve method AEEApplet.callOnRequest() failed.\n"); + return false; + } + + /* Resolve method com.intel.aee.Timer.callOnTimer() */ + method_callOnTimer = jeff_lookup_method(class_Timer, "callOnTimer", 0, d); + if (!method_callOnTimer) { + app_manager_printf("App Manager start failed: resolve method Timer.callOnTimer() failed.\n"); + return false; + } + + /* Resolve method com.intel.aee.Sensor.callOnSensorEvent() */ + method_callOnSensorEvent = jeff_lookup_method(class_Sensor, "callOnSensorEvent", 0, d); + if (!method_callOnSensorEvent) { + app_manager_printf("App Manager start failed: resolve method Sensor.callOnSensorEvent() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEStartDiscovery(BLEDevice) */ + d1[0].class_header = class_BLEDevice; + method_callOnBLEStartDiscovery = jeff_lookup_method(class_BLEManager, "callOnBLEStartDiscovery", 1, d1); + if (!method_callOnBLEStartDiscovery) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEStartDiscovery() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ + JeffDescriptorFull d2_1[] = { {JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}, + { JEFF_TYPE_INT, 0, NULL}, + { JEFF_TYPE_VOID, 0, NULL}}; + method_callOnBLEConnected = jeff_lookup_method(class_BLEManager, "callOnBLEConnected", 2, d2_1); + if (!method_callOnBLEConnected) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEConnected() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.method_callOnBLENotification(BLEDevice,byte[]) */ + JeffDescriptorFull d2_2[] = { {JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}, + { JEFF_TYPE_BYTE | JEFF_TYPE_REF | JEFF_TYPE_MONO, 1, NULL}, + { JEFF_TYPE_INT, 0, NULL}, + { JEFF_TYPE_INT, 0, NULL}, + { JEFF_TYPE_VOID, 0, NULL}}; + method_callOnBLENotification = jeff_lookup_method(class_BLEManager, "callOnBLENotification", 4, d2_2); + if (!method_callOnBLENotification) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLENotification() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice,byte[]) */ + method_callOnBLEIndication = jeff_lookup_method(class_BLEManager, "callOnBLEIndication", 4, d2_2); + if (!method_callOnBLEIndication) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEIndication() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ + d1[0].class_header = class_BLEDevice; + method_callOnBLEDisconnected = jeff_lookup_method(class_BLEManager, "callOnBLEDisconnected", 1, d1); + if (!method_callOnBLEDisconnected) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEDisconnected() failed.\n"); + return false; + } + + /* Resovle method com.intel.aee.ble.BLEManager.callOnBLEConnected(BLEDevice) */ + method_callOnBLEPasskeyEntry = jeff_lookup_method(class_BLEManager, "callOnBLEPasskeyEntry", 1, d1); + if (!method_callOnBLEPasskeyEntry) { + app_manager_printf("App Manager start failed: resolve method BLEManager.callOnBLEPasskeyEntry() failed.\n"); + return false; + } + /* Resovle method void com.intel.aee.gpio.GPIOChannel.callOnGPIOInterrupt() */ + method_callOnGPIOInterrupt = jeff_lookup_method(class_GPIOChannel, "callOnGPIOInterrupt", 0, d); + if (!method_callOnGPIOInterrupt) { + app_manager_printf("App Manager start failed: resolve method GPIOChannel.callOnGPIOInterrupt() failed.\n"); + return false; + } + + JeffDescriptorFull d2[] = { {JEFF_TYPE_BYTE | JEFF_TYPE_REF | JEFF_TYPE_MONO, 1, NULL}, + { JEFF_TYPE_OBJECT | JEFF_TYPE_REF, 0, class_BLEDevice}}; + /* Resovle method com.intel.aee.ble.BLEManager.getBLEDevice(byte []) */ + method_callOnBLEManagerGetBLEDevice = jeff_lookup_method(class_BLEManager, + "getBLEDevice", 1, d2); + if (!method_callOnBLEManagerGetBLEDevice) { + app_manager_printf( + "App Manager start failed: resolve method BLEManager.getBLEDevice() failed.\n"); + return false; + } + + return true; + } + + static void + jeff_module_watchdog_kill(module_data *m_data) + { + jeff_applet_data *applet_data = (jeff_applet_data*)m_data->internal_data; + + app_manager_printf("Watchdog interrupt the applet %s\n", m_data->module_name); + + jeff_runtime_interrupt_instance(applet_data->vm_instance, true); + + /* Exit applet queue loop run */ + bh_queue_exit_loop_run(m_data->queue); + + /* Wait the end of the applet instance. If timeout, it means applet + * is busy executing native code, then try to cancle the main thread. */ + if (applet_data->vm_instance->main_file) + jeff_runtime_wait_for_instance(applet_data->vm_instance, 3000); + + if (applet_data->vm_instance->main_file) { + app_manager_printf("Watchdog cancel applet main thread.\n"); + os_thread_cancel(applet_data->vm_instance->main_tlr.handle); + /* k_thread_abort(applet_data->vm_instance->main_tlr.handle); */ + } + + send_exception_event_to_host(m_data->module_name, "java.lang.InterruptedException"); + cleanup_applet_resource(m_data); + app_manager_printf("Watchdog interrupt Jeff applet done.\n"); + } + + static bool + jeff_module_handle_host_url(void *queue_msg) + { +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + bh_queue_msg_t *msg = (bh_queue_msg_t *)queue_msg; + + if (msg->message_type == COAP_PARSED) { + coap_packet_t *packet = (coap_packet_t *)msg->payload; + attr_container_t *attr_cont = (attr_container_t *)packet->payload; + const char *url = NULL; + int url_len = 0, mid; + + bh_memcpy_s(&mid, sizeof(uint32), packet->token, sizeof(uint32)); + url_len = coap_get_header_uri_path(packet, &url); + + /* Send request to tool agent */ + if (url_len >= 12 && memcmp(url, "/tool_agent/", 12) == 0) { + module_data *m_data; + jeff_applet_data *applet_data; + unsigned attr_cont_len = 0, req_msg_len; + bh_queue_msg_t *tool_agent_msg; + bh_request_msg_t *req_msg; + char url_buf[256] = {0}, *p = url_buf; + char applet_name[128] = {0}; + + /* Resolve applet name */ + bh_memcpy_s(url_buf, sizeof(url_buf), url + 12, url_len - 12); + while (*p != '/' && *p != '\0') + p++; + + bh_memcpy_s(applet_name, sizeof(applet_name), url_buf, p - url_buf); + app_manager_printf("Send request to tool agent of applet: %s\n", applet_name); + + /* Check applet name */ + if (!(m_data = app_manager_lookup_module_data(applet_name))) { + SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: invalid applet name"); + return false; + } + + applet_data = (jeff_applet_data*)m_data->internal_data; + /* Attach debug: start the tool agent firstly */ + if (packet->code == COAP_PUT) { + if (is_tool_agent_running(m_data)) { + SEND_ERR_RESPONSE(mid, "Attach debug failed: tool agent is already exist."); + return false; + } + + applet_data->debug_mode = true; + + /* Create tool agent queue */ + if (!(applet_data->tool_agent_queue = bh_queue_create())) { + SEND_ERR_RESPONSE(mid, "Attach debug failed: create tool agent queue failed."); + return false; + } + + /* Start tool agent thread */ + if (!jeff_tool_start_agent(applet_data->vm_instance, applet_data->tool_agent_queue)) { + bh_queue_destroy(applet_data->tool_agent_queue, NULL); + SEND_ERR_RESPONSE(mid, "Attach debug failed: start tool agent failed"); + return false; + } + + app_manager_printf("Attach debug: start tool agent of applet %s success.\n", applet_name); + app_send_response_to_host(mid, CREATED_2_01, NULL); /* OK */ + } else { + /* Check tool agent running */ + if (!is_tool_agent_running(m_data)) { + SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: tool agent is not running"); + return false; + } + + /* Create queue message for tool agent */ + if (!(tool_agent_msg = APP_MGR_MALLOC(sizeof(bh_queue_msg_t)))) { + SEND_ERR_RESPONSE(mid, "Send request to tool agent failed: allocate memory failed"); + return false; + } + + if (attr_cont) + attr_cont_len = attr_container_get_serialize_length(attr_cont); + + req_msg_len = sizeof(bh_request_msg_t) + strlen(p) + 1 + attr_cont_len; + + /* Create request message */ + if (!(req_msg = APP_MGR_MALLOC(req_msg_len))) { + SEND_ERR_RESPONSE(mid, "Send request to applet failed: allocate memory failed"); + APP_MGR_FREE(tool_agent_msg); + return false; + } + + /* Set request message */ + memset(req_msg, 0, req_msg_len); + req_msg->mid = mid; + req_msg->url = (char*)req_msg + sizeof(bh_request_msg_t); + bh_strcpy_s(req_msg->url, strlen(p)+1, p); /* Actual url sent to tool agent */ + req_msg->action = packet->code; + req_msg->fmt = 0; + if (attr_cont) { + req_msg->payload = (char*)req_msg + sizeof(bh_request_msg_t) + + strlen(p) + 1; + attr_container_serialize(req_msg->payload, attr_cont); + } + + /* Set queue message and send to tool agent's queue */ + tool_agent_msg->message_type = JDWP_REQUEST; + tool_agent_msg->payload_size = req_msg_len; + tool_agent_msg->payload = (char*)req_msg; + if (!bh_queue_send_message(applet_data->tool_agent_queue, tool_agent_msg)) { + APP_MGR_FREE(req_msg); + APP_MGR_FREE(tool_agent_msg); + SEND_ERR_RESPONSE + (mid, "Send request to tool agent failed: send queue msg failed."); + return false; + } + + /* app_manager_printf("Send request to tool agent of applet %s success.\n", applet_name); */ + } + + return true; + } + } +#endif /* BEIHAI_ENABLE_TOOL_AGENT != 0 */ + return false; + } + + static module_data* + jeff_module_get_module_data(void) + { + JeffThreadLocalRoot *self = jeff_runtime_get_tlr(); + return (module_data *)self->il_root->start_routine_arg; + } + +#if BEIHAI_ENABLE_TOOL_AGENT != 0 + +#define JDWP_HANDSHAKE_MAGIC "JDWP-Handshake" +#define JDWP_HANDSHAKE_LEN (sizeof (JDWP_HANDSHAKE_MAGIC) - 1) + +#define JDWP_PAYLOAD_KEY "jdwp" + + static bool debug = true; + + static bool + send_msg_to_host (int mid, const char *url, int code, const uint8 *msg, unsigned size) + { + bool ret; + int payload_len = 0; + attr_container_t *payload = NULL; + + if (msg) { + if ((payload = attr_container_create(""))) { + attr_container_set_bytearray(&payload, JDWP_PAYLOAD_KEY, (const int8_t *)msg, size); + payload_len = attr_container_get_serialize_length(payload); + } + } + ret = app_send_msg_to_host(mid, url, code, (char*)payload, payload_len); + + if (payload) + attr_container_destroy(payload); + + return ret; + } + + static bool + send_response(int mid, int code, const uint8 *msg, unsigned size) + { + return send_msg_to_host(mid, NULL, code, msg, size); + } + + static bool + send_packet_response(int mid, int code, JeffBuffer *packet) + { + int size; + + if ((size = jeff_buffer_size(packet)) == 0) + /* No data need to be written, succeed. */ + return true; + + return send_msg_to_host(mid, NULL, code, jeff_buffer_at(packet, 0), size); + } + + void + jeff_tool_event_publish(uint8 *evtbuf, unsigned size) + { + char *prefix = "/jdwp/", *url = NULL; + int url_len; + + url_len = strlen(prefix) + strlen(app_manager_get_module_name(Module_Jeff)); + if (NULL == (url = jeff_runtime_malloc(url_len + 1))) + return; + + bh_strcpy_s(url,url_len + 1, prefix); + bh_strcat_s(url,url_len + 1, app_manager_get_module_name(Module_Jeff)); + + /* Event is sent as request so we set code as COAP_PUT */ + if (event_is_registered(url)) + send_msg_to_host(0, url, COAP_PUT, evtbuf, size); + + jeff_runtime_free(url); + } + +#define SEND_ERROR_RESPONSE(err_msg) do { \ + app_manager_printf("%s\n", err_msg); \ + send_response(req_msg->mid, INTERNAL_SERVER_ERROR_5_00,\ + (uint8 *)err_msg, strlen(err_msg) + 1); \ + } while (0) + + /* Queue callback of tool agent */ + void + tool_agent_queue_callback(void *arg) + { + bh_queue_msg_t *msg = (bh_queue_msg_t*)arg; + + if (msg->message_type == JDWP_REQUEST) { + bh_request_msg_t *req_msg = (bh_request_msg_t*)msg->payload; + attr_container_t *attr_cont = (attr_container_t*)req_msg->payload; + JeffThreadLocalRoot *self = jeff_runtime_get_tlr(); + JeffInstanceLocalRoot *cur_instance = self->il_root; + JeffToolAgent *agent = cur_instance->tool_agent; + bh_queue *queue = (bh_queue *)self->start_routine_arg; + + if (debug) + app_manager_printf("Tool Agent of applet %s got request, url %s, action %d\n", + app_manager_get_module_name(Module_Jeff), req_msg->url, req_msg->action); + + /* Handshake or Process Request */ + if (req_msg->action == COAP_GET) { + uint8 *buf; + unsigned buf_len; + + if (!attr_cont + || !(buf = (uint8*) + attr_container_get_as_bytearray(attr_cont, JDWP_PAYLOAD_KEY, &buf_len))) { + SEND_ERROR_RESPONSE("Tool Agent fail: invalid JDWP payload."); + goto fail; + } + + if (!agent->connected) { + if (buf_len != JDWP_HANDSHAKE_LEN + || memcmp (buf, JDWP_HANDSHAKE_MAGIC, JDWP_HANDSHAKE_LEN)) { + SEND_ERROR_RESPONSE("Tool Agent fail: handshake fail."); + goto fail; + } + + /* Handshake success and response */ + agent->connected = true; + send_response(req_msg->mid, CONTENT_2_05, buf, buf_len); + } else { + /* TODO: tool-agent thread should reuse the request/reply buffer to avoid allocating memory repeatedly */ + JeffBuffer request, reply; + + /* Initialize the package buffers. */ + jeff_buffer_init(&request); + jeff_buffer_init(&reply); + + if (!jeff_buffer_resize(&request, buf_len)) { + SEND_ERROR_RESPONSE("Tool Agent fail: resize buffer fail."); + jeff_buffer_destroy(&request); + jeff_buffer_destroy(&reply); + goto fail; + } + + /* Copy data from request to jeff buffer */ + bh_memcpy_s(jeff_buffer_at(&request, 0), jeff_buffer_size(&request), buf, buf_len); + + /* Handle JDWP request */ + if (!jeff_tool_handle_packet(agent, &request, &reply)) { + SEND_ERROR_RESPONSE("Tool agent fail: handle request fail."); + jeff_buffer_destroy(&request); + jeff_buffer_destroy(&reply); + goto fail; + } + + /* Response JDWP reply */ + send_packet_response(req_msg->mid, CONTENT_2_05, &reply); + + /* Destroy the package buffers. */ + jeff_buffer_destroy(&request); + jeff_buffer_destroy(&reply); + } + } + /* Debugger disconnect */ + else if (req_msg->action == COAP_DELETE) { + send_response(req_msg->mid, DELETED_2_02, NULL, 0); + bh_queue_exit_loop_run(queue); + } + else { + SEND_ERROR_RESPONSE("Tool agent fail: invalid request."); + goto fail; + } + + APP_MGR_FREE(req_msg); + APP_MGR_FREE(msg); + return; + + fail: + bh_queue_exit_loop_run(queue); + APP_MGR_FREE(req_msg); + } + + APP_MGR_FREE(msg); + } + + void + tool_agent_queue_free_callback(void *message) + { + bh_queue_msg_t *msg = (bh_queue_msg_t*)message; + + if (msg->message_type == JDWP_REQUEST) { + bh_request_msg_t *req_msg = (bh_request_msg_t*)msg->payload; + APP_MGR_FREE(req_msg); + } + + APP_MGR_FREE(msg); + } + +#endif /* BEIHAI_ENABLE_TOOL_AGENT != 0 */ + + module_interface jeff_module_interface = { + jeff_module_init, + jeff_module_install, + jeff_module_uninstall, + jeff_module_watchdog_kill, + jeff_module_handle_host_url, + jeff_module_get_module_data, + NULL + }; + +#endif diff --git a/wamr/core/app-mgr/app-manager/module_jeff.h b/wamr/core/app-mgr/app-manager/module_jeff.h new file mode 100644 index 0000000..bb39f27 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_jeff.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _MODULE_JEFF_H_ +#define _MODULE_JEFF_H_ + +#include "app_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern module_interface jeff_module_interface; + +/* sensor event */ +typedef struct bh_sensor_event_t { + /* Java sensor object */ + void *sensor; + /* event of attribute container from context core */ + void *event; +} bh_sensor_event_t; + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _MODULE_JEFF_H_ */ diff --git a/wamr/core/app-mgr/app-manager/module_utils.c b/wamr/core/app-mgr/app-manager/module_utils.c new file mode 100644 index 0000000..637feea --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_utils.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager.h" +#include "app_manager_host.h" +#include "bh_platform.h" +#include "bi-inc/attr_container.h" +#include "event.h" +#include "watchdog.h" +#include "coap_ext.h" + +/* Lock of the module data list */ +korp_mutex module_data_list_lock; + +/* Module data list */ +module_data *module_data_list; + +bool module_data_list_init() +{ + module_data_list = NULL; + return !os_mutex_init(&module_data_list_lock) ? true : false; +} + +void module_data_list_destroy() +{ + + os_mutex_lock(&module_data_list_lock); + if (module_data_list) { + while (module_data_list) { + module_data *p = module_data_list->next; + APP_MGR_FREE(module_data_list); + module_data_list = p; + } + } + os_mutex_unlock(&module_data_list_lock); + os_mutex_destroy(&module_data_list_lock); +} + +static void module_data_list_add(module_data *m_data) +{ + static uint32 module_id_max = 1; + os_mutex_lock(&module_data_list_lock); + // reserve some special ID + // TODO: check the new id is not already occupied! + if (module_id_max == 0xFFFFFFF0) + module_id_max = 1; + m_data->id = module_id_max++; + if (!module_data_list) { + module_data_list = m_data; + } else { + /* Set as head */ + m_data->next = module_data_list; + module_data_list = m_data; + } + os_mutex_unlock(&module_data_list_lock); +} + +void module_data_list_remove(module_data *m_data) +{ + os_mutex_lock(&module_data_list_lock); + if (module_data_list) { + if (module_data_list == m_data) + module_data_list = module_data_list->next; + else { + /* Search and remove it */ + module_data *p = module_data_list; + + while (p && p->next != m_data) + p = p->next; + if (p && p->next == m_data) + p->next = p->next->next; + } + } + os_mutex_unlock(&module_data_list_lock); +} + +module_data* +module_data_list_lookup(const char *module_name) +{ + os_mutex_lock(&module_data_list_lock); + if (module_data_list) { + module_data *p = module_data_list; + + while (p) { + /* Search by module name */ + if (!strcmp(module_name, p->module_name)) { + os_mutex_unlock(&module_data_list_lock); + return p; + } + p = p->next; + } + } + os_mutex_unlock(&module_data_list_lock); + return NULL; +} + +module_data* +module_data_list_lookup_id(unsigned int module_id) +{ + os_mutex_lock(&module_data_list_lock); + if (module_data_list) { + module_data *p = module_data_list; + + while (p) { + /* Search by module name */ + if (module_id == p->id) { + os_mutex_unlock(&module_data_list_lock); + return p; + } + p = p->next; + } + } + os_mutex_unlock(&module_data_list_lock); + return NULL; +} + +module_data * +app_manager_get_module_data(uint32 module_type, void *module_inst) +{ + if (module_type < Module_Max + && g_module_interfaces[module_type] + && g_module_interfaces[module_type]->module_get_module_data) + return g_module_interfaces[module_type]->module_get_module_data(module_inst); + return NULL; +} + +void* +app_manager_get_module_queue(uint32 module_type, void *module_inst) +{ + module_data *m_data = app_manager_get_module_data(module_type, module_inst); + return m_data ? m_data->queue : NULL; +} + +const char* +app_manager_get_module_name(uint32 module_type, void *module_inst) +{ + module_data *m_data = app_manager_get_module_data(module_type, module_inst); + return m_data ? m_data->module_name : NULL; +} + +unsigned int app_manager_get_module_id(uint32 module_type, void *module_inst) +{ + module_data *m_data = app_manager_get_module_data(module_type, module_inst); + return m_data ? m_data->id : ID_NONE; +} + +void* +app_manager_get_module_heap(uint32 module_type, void *module_inst) +{ + module_data *m_data = app_manager_get_module_data(module_type, module_inst); + return m_data ? m_data->heap : NULL; +} + +module_data* +app_manager_lookup_module_data(const char *name) +{ + return module_data_list_lookup(name); +} + +void app_manager_add_module_data(module_data *m_data) +{ + module_data_list_add(m_data); +} + +void app_manager_del_module_data(module_data *m_data) +{ + module_data_list_remove(m_data); + + release_module(m_data); +} + +bool app_manager_is_interrupting_module(uint32 module_type, void *module_inst) +{ + module_data *m_data = app_manager_get_module_data(module_type, module_inst); + return m_data ? m_data->wd_timer.is_interrupting : false; +} + +extern void destroy_module_timer_ctx(unsigned int module_id); + +void release_module(module_data *m_data) +{ + watchdog_timer_destroy(&m_data->wd_timer); + +#ifdef HEAP_ENABLED /* TODO */ + if(m_data->heap) + gc_destroy_for_instance(m_data->heap); +#endif + + if (m_data->queue) + bh_queue_destroy(m_data->queue); + + m_data->timer_ctx = NULL; + + destroy_module_timer_ctx(m_data->id); + + APP_MGR_FREE(m_data); +} + +int check_modules_timer_expiry() +{ + os_mutex_lock(&module_data_list_lock); + module_data *p = module_data_list; + int ms_to_expiry = -1; + + while (p) { + + int next = get_expiry_ms(p->timer_ctx); + if (next != -1) { + if (ms_to_expiry == -1 || ms_to_expiry > next) + ms_to_expiry = next; + } + + p = p->next; + } + os_mutex_unlock(&module_data_list_lock); + return ms_to_expiry; +} + diff --git a/wamr/core/app-mgr/app-manager/module_wasm_app.c b/wamr/core/app-mgr/app-manager/module_wasm_app.c new file mode 100644 index 0000000..a84e760 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_wasm_app.c @@ -0,0 +1,1628 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "module_wasm_app.h" + +#include "native_interface.h" /* for request_t type */ +#include "app_manager_host.h" +#include "bh_platform.h" +#include "bi-inc/attr_container.h" +#include "coap_ext.h" +#include "event.h" +#include "watchdog.h" +#include "runtime_lib.h" +#include "wasm.h" +#if WASM_ENABLE_AOT != 0 +#include "aot_export.h" +#endif + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +/* Wasm bytecode file 4 version bytes */ +static uint8 wasm_bytecode_version[4] = { + (uint8) 0x01, + (uint8) 0x00, + (uint8) 0x00, + (uint8) 0x00 +}; +#endif + +#if WASM_ENABLE_AOT != 0 +/* Wasm aot file 4 version bytes */ +static uint8 wasm_aot_version[4] = { + (uint8) 0x02, + (uint8) 0x00, + (uint8) 0x00, + (uint8) 0x00 +}; +#endif + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) +/* Wasm App Install Request Receiving Phase */ +typedef enum wasm_app_install_req_recv_phase_t { + Phase_Req_Ver, + Phase_Req_Action, + Phase_Req_Fmt, + Phase_Req_Mid, + Phase_Req_Sender, + Phase_Req_Url_Len, + Phase_Req_Payload_Len, /* payload is wasm app binary */ + Phase_Req_Url, + + /* Magic phase */ + Phase_App_Magic, + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + /* Phases of wasm bytecode file */ + Phase_Wasm_Version, + Phase_Wasm_Section_Type, + Phase_Wasm_Section_Size, + Phase_Wasm_Section_Content, +#endif + +#if WASM_ENABLE_AOT != 0 + /* Phases of wasm AOT file */ + Phase_AOT_Version, + Phase_AOT_Section_ID, + Phase_AOT_Section_Size, + Phase_AOT_Section_Content +#endif +} wasm_app_install_req_recv_phase_t; + +/* Message for insall wasm app */ +typedef struct install_wasm_app_msg_t { + uint8 request_version; + uint8 request_action; + uint16 request_fmt; + uint32 request_mid; + uint32 request_sender; + uint16 request_url_len; + uint32 wasm_app_size; /* payload size is just wasm app binary size */ + char *request_url; + wasm_app_file_t app_file; + int app_file_magic; +} install_wasm_app_msg_t; + +/* Wasm App Install Request Receive Context */ +typedef struct wasm_app_install_req_recv_ctx_t { + wasm_app_install_req_recv_phase_t phase; + int size_in_phase; + install_wasm_app_msg_t message; + int total_received_size; +} wasm_app_install_req_recv_ctx_t; + +/* Current wasm app install request receive context */ +static wasm_app_install_req_recv_ctx_t recv_ctx; + +static bool +wasm_app_module_init(void); + +static bool +wasm_app_module_install(request_t *msg); + +static bool +wasm_app_module_uninstall(request_t *msg); + +static void +wasm_app_module_watchdog_kill(module_data *module_data); + +static bool +wasm_app_module_handle_host_url(void *queue_msg); + +static module_data * +wasm_app_module_get_module_data(void *inst); + +static bool +wasm_app_module_on_install_request_byte_arrive(uint8 ch, int request_total_size, + int *received_size); + +static bool +module_wasm_app_handle_install_msg(install_wasm_app_msg_t *message); + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +static void +destroy_all_wasm_sections(wasm_section_list_t sections); + +static void +destroy_part_wasm_sections(wasm_section_list_t *p_sections, + uint8 *section_types, + int section_cnt); +#endif + +#if WASM_ENABLE_AOT != 0 +static void +destroy_all_aot_sections(aot_section_list_t sections); + +static void +destroy_part_aot_sections(aot_section_list_t *p_sections, + uint8 *section_types, + int section_cnt); +#endif + +#define Max_Msg_Callback 10 +int g_msg_type[Max_Msg_Callback] = { 0 }; +message_type_handler_t g_msg_callbacks[Max_Msg_Callback] = { 0 }; + +#define Max_Cleanup_Callback 10 +static resource_cleanup_handler_t +g_cleanup_callbacks[Max_Cleanup_Callback] = { 0 }; + +module_interface wasm_app_module_interface = { + wasm_app_module_init, + wasm_app_module_install, + wasm_app_module_uninstall, + wasm_app_module_watchdog_kill, + wasm_app_module_handle_host_url, + wasm_app_module_get_module_data, + wasm_app_module_on_install_request_byte_arrive +}; + +static void +exchange_uint32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static wasm_function_inst_t +app_manager_lookup_function(const wasm_module_inst_t module_inst, + const char *name, const char *signature) +{ + wasm_function_inst_t func; + + func = wasm_runtime_lookup_function(module_inst, name, signature); + if (!func && name[0] == '_') + func = wasm_runtime_lookup_function(module_inst, name + 1, signature); + return func; +} + + +static void +app_instance_queue_callback(void *queue_msg, void *arg) +{ + uint32 argv[2]; + wasm_function_inst_t func_onRequest, func_onTimer; + + wasm_module_inst_t inst = (wasm_module_inst_t)arg; + module_data *m_data = app_manager_get_module_data(Module_WASM_App, inst); + wasm_data *wasm_app_data = (wasm_data*)m_data->internal_data; + int message_type = bh_message_type(queue_msg); + + bh_assert(m_data); + + if (message_type < BASE_EVENT_MAX) { + switch (message_type) { + case RESTFUL_REQUEST: { + request_t *request = (request_t *)bh_message_payload(queue_msg); + int size; + char *buffer; + int32 buffer_offset; + + app_manager_printf("App %s got request, url %s, action %d\n", + m_data->module_name, + request->url, + request->action); + + func_onRequest = app_manager_lookup_function(inst, + "_on_request", + "(i32i32)"); + if (!func_onRequest) { + app_manager_printf("Cannot find function onRequest\n"); + break; + } + + buffer = pack_request(request, &size); + if (buffer == NULL) + break; + + buffer_offset = wasm_runtime_module_dup_data(inst, buffer, size); + if (buffer_offset == 0) { + const char *exception = wasm_runtime_get_exception(inst); + if (exception) { + app_manager_printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + } + free_req_resp_packet(buffer); + break; + } + + free_req_resp_packet(buffer); + + argv[0] = (uint32) buffer_offset; + argv[1] = (uint32) size; + + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onRequest, + 2, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + app_manager_printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + wasm_runtime_module_free(inst, buffer_offset); + break; + } + + wasm_runtime_module_free(inst, buffer_offset); + app_manager_printf("Wasm app process request success.\n"); + break; + } + case RESTFUL_RESPONSE: { + wasm_function_inst_t func_onResponse; + response_t *response = (response_t *) bh_message_payload(queue_msg); + int size; + char *buffer; + int32 buffer_offset; + + app_manager_printf("App %s got response_t,status %d\n", + m_data->module_name, response->status); + + func_onResponse = + app_manager_lookup_function(inst, "_on_response", "(i32i32)"); + if (!func_onResponse) { + app_manager_printf("Cannot find function on_response\n"); + break; + } + + buffer = pack_response(response, &size); + if (buffer == NULL) + break; + + buffer_offset = wasm_runtime_module_dup_data(inst, buffer, size); + if (buffer_offset == 0) { + const char *exception = wasm_runtime_get_exception(inst); + if (exception) { + app_manager_printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + } + free_req_resp_packet(buffer); + break; + } + + free_req_resp_packet(buffer); + + argv[0] = (uint32) buffer_offset; + argv[1] = (uint32) size; + + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onResponse, + 2, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + app_manager_printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + wasm_runtime_module_free(inst, buffer_offset); + break; + } + + wasm_runtime_module_free(inst, buffer_offset); + app_manager_printf("Wasm app process response success.\n"); + break; + } + default: { + for (int i = 0; i < Max_Msg_Callback; i++) { + if (g_msg_type[i] == message_type) { + g_msg_callbacks[i](m_data, queue_msg); + return; + } + } + app_manager_printf("Invalid message type of WASM app queue message.\n"); + break; + + } + } + } + else { + switch (message_type) { + case TIMER_EVENT_WASM: { + unsigned int timer_id; + if (bh_message_payload(queue_msg)) { + /* Call Timer.callOnTimer() method */ + func_onTimer = + app_manager_lookup_function(inst, + "_on_timer_callback", + "(i32)"); + + if (!func_onTimer) { + app_manager_printf("Cannot find function _on_timer_callback\n"); + break; + } + timer_id = + (unsigned int)(uintptr_t)bh_message_payload(queue_msg); + argv[0] = timer_id; + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onTimer, + 1, argv)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + app_manager_printf("Got exception running wasm code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + } + } + break; + } + default: { + for (int i = 0; i < Max_Msg_Callback; i++) { + if (g_msg_type[i] == message_type) { + g_msg_callbacks[i](m_data, queue_msg); + return; + } + } + app_manager_printf("Invalid message type of WASM app queue message.\n"); + break; + } + + } + } +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static bool +wasm_app_prepare_wasi_dir(wasm_module_t module, const char *module_name, + char *wasi_dir_buf, uint32 buf_size) +{ + const char *wasi_root = wasm_get_wasi_root_dir(); + char *p = wasi_dir_buf; + uint32 module_name_len = strlen(module_name); + uint32 wasi_root_len = strlen(wasi_root); + uint32 total_size; + struct stat st = { 0 }; + + bh_assert(wasi_root); + + /* wasi_dir: wasi_root/module_name */ + total_size = wasi_root_len + 1 + module_name_len + 1; + if (total_size > buf_size) + return false; + memcpy(p, wasi_root, wasi_root_len); + p += wasi_root_len; + *p++ = '/'; + memcpy(p, module_name, module_name_len); + p += module_name_len; + *p++ = '\0'; + + /* Create a wasi dir for the module */ + if (stat(wasi_dir_buf, &st) == 0) { + /* exist, but is a regular file, not a dir */ + if (st.st_mode & S_IFREG) + return false; + } + else { + /* not exist, create it */ + if (mkdir(wasi_dir_buf, 0777) != 0) + return false; + } + + return true; +} +#endif + +/* WASM app thread main routine */ +static void* +wasm_app_routine(void *arg) +{ + wasm_function_inst_t func_onInit; + wasm_function_inst_t func_onDestroy; + + module_data *m_data = (module_data *) arg; + wasm_data *wasm_app_data = (wasm_data*) m_data->internal_data; + wasm_module_inst_t inst = wasm_app_data->wasm_module_inst; + + /* Set m_data to the VM managed instance's custom data */ + wasm_runtime_set_custom_data(inst, m_data); + + app_manager_printf("WASM app '%s' started\n", m_data->module_name); + +#if WASM_ENABLE_LIBC_WASI != 0 + if (wasm_runtime_is_wasi_mode(inst)) { + wasm_function_inst_t func_start; + /* In wasi mode, we should call function named "_start" + which initializes the wasi envrionment. The "_start" function + will call "main" function */ + if ((func_start = wasm_runtime_lookup_wasi_start_function(inst))) { + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_start, + 0, NULL)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + app_manager_printf("Got exception running wasi start function: %s\n", + exception); + wasm_runtime_clear_exception(inst); + goto fail1; + } + } + /* if no start function is found, we execute + the _on_init function as normal */ + } +#endif + + /* Call app's onInit() method */ + func_onInit = app_manager_lookup_function(inst, "_on_init", "()"); + if (!func_onInit) { + app_manager_printf("Cannot find function on_init().\n"); + goto fail1; + } + + if (!wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onInit, + 0, NULL)) { + const char *exception = wasm_runtime_get_exception(inst); + bh_assert(exception); + app_manager_printf("Got exception running WASM code: %s\n", + exception); + wasm_runtime_clear_exception(inst); + /* call on_destroy() in case some resources are opened in on_init() + * and then exception thrown */ + goto fail2; + } + + /* Enter queue loop run to receive and process applet queue message */ + bh_queue_enter_loop_run(m_data->queue, app_instance_queue_callback, inst); + + app_manager_printf("App instance main thread exit.\n"); + +fail2: + /* Call WASM app onDestroy() method if there is */ + func_onDestroy = app_manager_lookup_function(inst, "_on_destroy", "()"); + if (func_onDestroy) + wasm_runtime_call_wasm(wasm_app_data->exec_env, func_onDestroy, 0, NULL); + +fail1: + + return NULL; +} + +static void +cleanup_app_resource(module_data *m_data) +{ + int i; + wasm_data *wasm_app_data = (wasm_data*) m_data->internal_data; + bool is_bytecode = wasm_app_data->is_bytecode; + + am_cleanup_registeration(m_data->id); + + am_unregister_event(NULL, m_data->id); + + for (i = 0; i < Max_Cleanup_Callback; i++) { + if (g_cleanup_callbacks[i] != NULL) + g_cleanup_callbacks[i](m_data->id); + else + break; + } + + wasm_runtime_deinstantiate(wasm_app_data->wasm_module_inst); + + /* Destroy remain sections (i.e. data segment section for bytecode file + * or text section of aot file) from app file's section list. */ + if (is_bytecode) +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + destroy_all_wasm_sections((wasm_section_list_t)(wasm_app_data->sections)); +#else + bh_assert(0); +#endif + else +#if WASM_ENABLE_AOT != 0 + destroy_all_aot_sections((aot_section_list_t)(wasm_app_data->sections)); +#else + bh_assert(0); +#endif + + if (wasm_app_data->wasm_module) + wasm_runtime_unload(wasm_app_data->wasm_module); + + if (wasm_app_data->exec_env) + wasm_runtime_destroy_exec_env(wasm_app_data->exec_env); + + /* Destroy watchdog timer */ + watchdog_timer_destroy(&m_data->wd_timer); + + /* Remove module data from module data list and free it */ + app_manager_del_module_data(m_data); +} + +/************************************************************/ +/* Module specific functions implementation */ +/************************************************************/ + +static bool +wasm_app_module_init(void) +{ + uint32 version; + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + version = WASM_CURRENT_VERSION; + if (!is_little_endian()) + exchange_uint32((uint8 *)&version); + bh_memcpy_s(wasm_bytecode_version, 4, &version, 4); +#endif + +#if WASM_ENABLE_AOT != 0 + version = AOT_CURRENT_VERSION; + if (!is_little_endian()) + exchange_uint32((uint8 *)&version); + bh_memcpy_s(wasm_aot_version, 4, &version, 4); +#endif + return true; +} + +#define APP_NAME_MAX_LEN 128 +#define MAX_INT_STR_LEN 11 + +static bool +wasm_app_module_install(request_t * msg) +{ + unsigned int m_data_size, heap_size, stack_size; + unsigned int timeout, timers, err_size; + char *properties; + int properties_offset; + wasm_app_file_t *wasm_app_file; + wasm_data *wasm_app_data; + package_type_t package_type; + module_data *m_data; + wasm_module_t module = NULL; + wasm_module_inst_t inst = NULL; + wasm_exec_env_t exec_env = NULL; + char m_name[APP_NAME_MAX_LEN] = { 0 }; + char timeout_str[MAX_INT_STR_LEN] = { 0 }; + char heap_size_str[MAX_INT_STR_LEN] = { 0 }; + char timers_str[MAX_INT_STR_LEN] = { 0 }, err[256]; +#if WASM_ENABLE_LIBC_WASI != 0 + char wasi_dir_buf[PATH_MAX] = { 0 }; + const char *wasi_dir_list[] = { wasi_dir_buf }; +#endif + + err_size = sizeof(err); + + /* Check payload */ + if (!msg->payload || msg->payload_len == 0) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: invalid wasm file."); + return false; + } + + /* Check app name */ + properties_offset = check_url_start(msg->url, strlen(msg->url), "/applet"); + bh_assert(properties_offset > 0); + if (properties_offset <= 0) + return false; + properties = msg->url + properties_offset; + find_key_value(properties, strlen(properties), "name", m_name, + sizeof(m_name) - 1, '&'); + + if (strlen(m_name) == 0) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: invalid app name."); + return false; + } + + if (app_manager_lookup_module_data(m_name)) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: app already installed."); + return false; + } + + /* Parse heap size */ + heap_size = APP_HEAP_SIZE_DEFAULT; + find_key_value(properties, strlen(properties), "heap", heap_size_str, + sizeof(heap_size_str) - 1, '&'); + if (strlen(heap_size_str) > 0) { + heap_size = atoi(heap_size_str); + if (heap_size < APP_HEAP_SIZE_MIN) + heap_size = APP_HEAP_SIZE_MIN; + else if (heap_size > APP_HEAP_SIZE_MAX) + heap_size = APP_HEAP_SIZE_MAX; + } + + /* Judge the app type is AOTed or not */ + package_type = get_package_type((uint8 *) msg->payload, msg->payload_len); + + /* Load WASM file and instantiate*/ + switch (package_type) { +#if WASM_ENABLE_AOT != 0 + case Wasm_Module_AoT: + { + wasm_aot_file_t *aot_file; + /* Sections to be released after loading */ + uint8 sections1[] = { + AOT_SECTION_TYPE_TARGET_INFO, + AOT_SECTION_TYPE_INIT_DATA, + AOT_SECTION_TYPE_FUNCTION, + AOT_SECTION_TYPE_EXPORT, + AOT_SECTION_TYPE_RELOCATION, + AOT_SECTION_TYPE_SIGANATURE + }; + + wasm_app_file = (wasm_app_file_t *) msg->payload; + bh_assert(wasm_app_file); + aot_file = &wasm_app_file->u.aot; + + /* Load AOT module from sections */ + module = wasm_runtime_load_from_sections(aot_file->sections, true, + err, err_size); + if (!module) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: load WASM file failed."); + app_manager_printf("error: %s\n", err); + destroy_all_aot_sections(aot_file->sections); + return false; + } + /* Destroy useless sections from list after load */ + destroy_part_aot_sections(&aot_file->sections, + sections1, + sizeof(sections1) / sizeof(uint8)); + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!wasm_app_prepare_wasi_dir(module, m_name, + wasi_dir_buf, sizeof(wasi_dir_buf))) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: prepare wasi env failed."); + wasm_runtime_unload(module); + destroy_all_aot_sections(aot_file->sections); + return false; + } + wasm_runtime_set_wasi_args(module, + wasi_dir_list, 1, + NULL, 0, + NULL, 0, + NULL, 0); +#endif + + /* Instantiate the AOT module */ + inst = wasm_runtime_instantiate(module, 0, heap_size, err, err_size); + if (!inst) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: instantiate wasm runtime failed."); + app_manager_printf("error: %s\n", err); + wasm_runtime_unload(module); + destroy_all_aot_sections(aot_file->sections); + return false; + } + break; + } +#endif /* endof WASM_ENABLE_AOT != 0 */ + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + case Wasm_Module_Bytecode: + { + wasm_bytecode_file_t *bytecode_file; + /* Sections to be released after loading */ + uint8 sections1[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM + }; + /* Sections to be released after instantiating */ + uint8 sections2[] = { SECTION_TYPE_DATA }; + + wasm_app_file = (wasm_app_file_t *) msg->payload; + bh_assert(wasm_app_file); + bytecode_file = &wasm_app_file->u.bytecode; + + /* Load wasm module from sections */ + module = wasm_runtime_load_from_sections(bytecode_file->sections, false, + err, err_size); + if (!module) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: load WASM file failed."); + app_manager_printf("error: %s\n", err); + destroy_all_wasm_sections(bytecode_file->sections); + return false; + } + + /* Destroy useless sections from list after load */ + destroy_part_wasm_sections(&bytecode_file->sections, + sections1, + sizeof(sections1) / sizeof(uint8)); + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!wasm_app_prepare_wasi_dir(module, m_name, + wasi_dir_buf, sizeof(wasi_dir_buf))) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: prepare wasi env failed."); + wasm_runtime_unload(module); + destroy_all_wasm_sections(bytecode_file->sections); + return false; + } + wasm_runtime_set_wasi_args(module, + wasi_dir_list, 1, + NULL, 0, + NULL, 0, + NULL, 0); +#endif + + /* Instantiate the wasm module */ + inst = wasm_runtime_instantiate(module, 0, heap_size, err, err_size); + if (!inst) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: instantiate wasm runtime failed."); + app_manager_printf("error: %s\n", err); + wasm_runtime_unload(module); + destroy_all_wasm_sections(bytecode_file->sections); + return false; + } + + /* Destroy useless sections from list after instantiate */ + destroy_part_wasm_sections(&bytecode_file->sections, + sections2, + sizeof(sections2) / sizeof(uint8)); + break; + } +#endif /* endof WASM_ENALBE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ + default: + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: invalid wasm package type."); + return false; + } + + /* Create module data including the wasm_app_data as its internal_data*/ + m_data_size = offsetof(module_data, module_name) + strlen(m_name) + 1; + m_data_size = align_uint(m_data_size, 4); + m_data = APP_MGR_MALLOC(m_data_size + sizeof(wasm_data)); + if (!m_data) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: allocate memory failed."); + goto fail; + } + memset(m_data, 0, m_data_size + sizeof(wasm_data)); + + m_data->module_type = Module_WASM_App; + m_data->internal_data = (uint8*) m_data + m_data_size; + wasm_app_data = (wasm_data*) m_data->internal_data; + wasm_app_data->wasm_module_inst = inst; + wasm_app_data->wasm_module = module; + wasm_app_data->m_data = m_data; + if (package_type == Wasm_Module_Bytecode) { + wasm_app_data->is_bytecode = true; + wasm_app_data->sections = wasm_app_file->u.bytecode.sections; + } + else { + wasm_app_data->is_bytecode = false; + wasm_app_data->sections = wasm_app_file->u.aot.sections; + } + + if (!(wasm_app_data->exec_env = exec_env = + wasm_runtime_create_exec_env(inst, DEFAULT_WASM_STACK_SIZE))) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: create exec env failed."); + goto fail; + } + + /* Set module data - name and module type */ + bh_strcpy_s(m_data->module_name, strlen(m_name) + 1, m_name); + + /* Set module data - execution timeout */ + timeout = DEFAULT_WATCHDOG_INTERVAL; + find_key_value(properties, strlen(properties), "wd", timeout_str, + sizeof(timeout_str) - 1, '&'); + if (strlen(timeout_str) > 0) + timeout = atoi(timeout_str); + m_data->timeout = timeout; + + /* Set module data - create queue */ + m_data->queue = bh_queue_create(); + if (!m_data->queue) { + SEND_ERR_RESPONSE(msg->mid, "Install WASM app failed: create app queue failed."); + goto fail; + } + + /* Set heap size */ + m_data->heap_size = heap_size; + + /* Set module data - timers number */ + timers = DEFAULT_TIMERS_PER_APP; + find_key_value(properties, strlen(properties), "timers", timers_str, + sizeof(timers_str) - 1, '&'); + if (strlen(timers_str) > 0) { + timers = atoi(timers_str); + if (timers > MAX_TIMERS_PER_APP) + timers = MAX_TIMERS_PER_APP; + } + + /* Attention: must add the module before start the thread! */ + app_manager_add_module_data(m_data); + + m_data->timer_ctx = create_wasm_timer_ctx(m_data->id, timers); + if (!m_data->timer_ctx) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: create app timers failed."); + goto fail; + } + + /* Initialize watchdog timer */ + if (!watchdog_timer_init(m_data)) { + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: create app watchdog timer failed."); + goto fail; + } + + stack_size = APP_THREAD_STACK_SIZE_DEFAULT; +#ifdef OS_ENABLE_HW_BOUND_CHECK + stack_size += 4 * BH_KB; +#endif + /* Create WASM app thread. */ + if (os_thread_create(&wasm_app_data->thread_id, wasm_app_routine, + (void*) m_data, stack_size) != 0) { + module_data_list_remove(m_data); + SEND_ERR_RESPONSE(msg->mid, + "Install WASM app failed: create app thread failed."); + goto fail; + } + + /* only when thread is created it is the flag of installation success */ + app_manager_post_applets_update_event(); + + app_manager_printf("Install WASM app success!\n"); + send_error_response_to_host(msg->mid, CREATED_2_01, NULL); /* CREATED */ + + return true; + +fail: + if (m_data) + release_module(m_data); + wasm_runtime_deinstantiate(inst); + wasm_runtime_unload(module); + if (exec_env) + wasm_runtime_destroy_exec_env(exec_env); + + switch (package_type) { +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + case Wasm_Module_Bytecode: + destroy_all_wasm_sections(wasm_app_file->u.bytecode.sections); + break; +#endif +#if WASM_ENABLE_AOT != 0 + case Wasm_Module_AoT: + destroy_all_aot_sections(wasm_app_file->u.aot.sections); + break; +#endif + default: + break; + } + + return false; +} + +/* For internal use: if defined to 1, the process will + * exit when wasm app is uninstalled. Hence valgrind can + * print memory leak report. */ +#ifndef VALGRIND_CHECK +#define VALGRIND_CHECK 0 +#endif + +/* Uninstall WASM app */ +static bool +wasm_app_module_uninstall(request_t *msg) +{ + module_data *m_data; + wasm_data *wasm_app_data; + char m_name[APP_NAME_MAX_LEN] = { 0 }; + char *properties; + int properties_offset; + + properties_offset = check_url_start(msg->url, strlen(msg->url), "/applet"); + /* TODO: assert(properties_offset > 0) */ + if (properties_offset <= 0) + return false; + properties = msg->url + properties_offset; + find_key_value(properties, strlen(properties), "name", m_name, + sizeof(m_name) - 1, '&'); + + if (strlen(m_name) == 0) { + SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: invalid app name."); + return false; + } + + m_data = app_manager_lookup_module_data(m_name); + if (!m_data) { + SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: no app found."); + return false; + } + + if (m_data->module_type != Module_WASM_App) { + SEND_ERR_RESPONSE(msg->mid, "Uninstall WASM app failed: invalid module type."); + return false; + } + + if (m_data->wd_timer.is_interrupting) { + SEND_ERR_RESPONSE(msg->mid, + "Uninstall WASM app failed: app is being interrupted by watchdog."); + return false; + } + + /* Exit app queue loop run */ + bh_queue_exit_loop_run(m_data->queue); + + /* Wait for wasm app thread to exit */ + wasm_app_data = (wasm_data*) m_data->internal_data; + os_thread_join(wasm_app_data->thread_id, NULL); + + cleanup_app_resource(m_data); + + app_manager_post_applets_update_event(); + + app_manager_printf("Uninstall WASM app successful!\n"); + +#if VALGRIND_CHECK != 0 + bh_queue_exit_loop_run(get_app_manager_queue()); +#endif + + send_error_response_to_host(msg->mid, DELETED_2_02, NULL); /* DELETED */ + return true; +} + +static bool +wasm_app_module_handle_host_url(void *queue_msg) +{ + //todo: implement in future + app_manager_printf("App handles host url address %d\n", + (int)(uintptr_t)queue_msg); + return false; +} + +static module_data* +wasm_app_module_get_module_data(void *inst) +{ + wasm_module_inst_t module_inst = (wasm_module_inst_t)inst; + return (module_data *)wasm_runtime_get_custom_data(module_inst); +} + +static void +wasm_app_module_watchdog_kill(module_data *m_data) +{ + //todo: implement in future + app_manager_printf("Watchdog kills app: %s\n", m_data->module_name); + return; +} + +bool +wasm_register_msg_callback(int message_type, + message_type_handler_t message_handler) +{ + int i; + int freeslot = -1; + for (i = 0; i < Max_Msg_Callback; i++) { + // replace handler for the same event registered + if (g_msg_type[i] == message_type) + break; + + if (g_msg_callbacks[i] == NULL && freeslot == -1) + freeslot = i; + } + + if (i != Max_Msg_Callback) + g_msg_callbacks[i] = message_handler; + else if (freeslot != -1) { + g_msg_callbacks[freeslot] = message_handler; + g_msg_type[freeslot] = message_type; + } else + return false; + + return true; +} + +bool +wasm_register_cleanup_callback(resource_cleanup_handler_t handler) +{ + int i; + + for (i = 0; i < Max_Cleanup_Callback; i++) { + if (g_cleanup_callbacks[i] == NULL) { + g_cleanup_callbacks[i] = handler; + return true; + } + } + + return false; +} + +#define RECV_INTEGER(value, next_phase) do { \ + uint8 *p = (uint8 *)&value; \ + p[recv_ctx.size_in_phase++] = ch; \ + if (recv_ctx.size_in_phase == sizeof(value)) { \ + if (sizeof(value) == 4) \ + value = ntohl(value); \ + else if (sizeof(value) == 2) \ + value = ntohs(value); \ + recv_ctx.phase = next_phase; \ + recv_ctx.size_in_phase = 0; \ + } \ + } while(0) + +/* return: + * 1: whole wasm app arrived + * 0: one valid byte arrived + * -1: fail to process the byte arrived, e.g. allocate memory fail + */ +static bool +wasm_app_module_on_install_request_byte_arrive(uint8 ch, + int request_total_size, + int *received_size) +{ + uint8 *p; + package_type_t package_type = Package_Type_Unknown; + + if (recv_ctx.phase == Phase_Req_Ver) { + recv_ctx.phase = Phase_Req_Ver; + recv_ctx.size_in_phase = 0; + recv_ctx.total_received_size = 0; + } + + recv_ctx.total_received_size++; + *received_size = recv_ctx.total_received_size; + + if (recv_ctx.phase == Phase_Req_Ver) { + if (ch != 1 /* REQUES_PACKET_VER from restful_utils.c */) + return false; + recv_ctx.phase = Phase_Req_Action; + return true; + } + else if (recv_ctx.phase == Phase_Req_Action) { + recv_ctx.message.request_action = ch; + recv_ctx.phase = Phase_Req_Fmt; + recv_ctx.size_in_phase = 0; + return true; + } + else if (recv_ctx.phase == Phase_Req_Fmt) { + RECV_INTEGER(recv_ctx.message.request_fmt, Phase_Req_Mid); + return true; + } + else if (recv_ctx.phase == Phase_Req_Mid) { + RECV_INTEGER(recv_ctx.message.request_mid, Phase_Req_Sender); + return true; + } + else if (recv_ctx.phase == Phase_Req_Sender) { + RECV_INTEGER(recv_ctx.message.request_sender, Phase_Req_Url_Len); + return true; + } + else if (recv_ctx.phase == Phase_Req_Url_Len) { + p = (uint8*)&recv_ctx.message.request_url_len; + + p[recv_ctx.size_in_phase++] = ch; + if (recv_ctx.size_in_phase == + sizeof(recv_ctx.message.request_url_len)) { + recv_ctx.message.request_url_len = + ntohs(recv_ctx.message.request_url_len); + recv_ctx.message.request_url = + APP_MGR_MALLOC(recv_ctx.message.request_url_len + 1); + if (NULL == recv_ctx.message.request_url) { + app_manager_printf("Allocate memory failed!\n"); + goto fail; + } + memset(recv_ctx.message.request_url, 0, + recv_ctx.message.request_url_len + 1); + recv_ctx.phase = Phase_Req_Payload_Len; + recv_ctx.size_in_phase = 0; + } + return true; + } + else if (recv_ctx.phase == Phase_Req_Payload_Len) { + RECV_INTEGER(recv_ctx.message.wasm_app_size, Phase_Req_Url); + return true; + } + else if (recv_ctx.phase == Phase_Req_Url) { + recv_ctx.message.request_url[recv_ctx.size_in_phase++] = ch; + if (recv_ctx.size_in_phase == recv_ctx.message.request_url_len) { + recv_ctx.phase = Phase_App_Magic; + recv_ctx.size_in_phase = 0; + } + return true; + } + else if (recv_ctx.phase == Phase_App_Magic) { + /* start to receive wasm app magic: bytecode or aot */ + p = (uint8*)&recv_ctx.message.app_file_magic; + + p[recv_ctx.size_in_phase++] = ch; + + if (recv_ctx.size_in_phase == + sizeof(recv_ctx.message.app_file_magic)) { + int magic = recv_ctx.message.app_file_magic; + package_type = get_package_type((uint8 *)&magic, sizeof(magic) + 1); + switch (package_type) { +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + case Wasm_Module_Bytecode: + recv_ctx.message.app_file.u.bytecode.magic = + recv_ctx.message.app_file_magic; + recv_ctx.phase = Phase_Wasm_Version; + recv_ctx.size_in_phase = 0; + break; +#endif +#if WASM_ENABLE_AOT != 0 + case Wasm_Module_AoT: + recv_ctx.message.app_file.u.aot.magic = + recv_ctx.message.app_file_magic; + recv_ctx.phase = Phase_AOT_Version; + recv_ctx.size_in_phase = 0; + break; +#endif + default: + SEND_ERR_RESPONSE(recv_ctx.message.request_mid, + "Install WASM app failed: invalid file format."); + goto fail; + } + } + return true; + } +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + else if (recv_ctx.phase == Phase_Wasm_Version) { + p = (uint8*)&recv_ctx.message.app_file.u.bytecode.version; + + if (ch == wasm_bytecode_version[recv_ctx.size_in_phase]) + p[recv_ctx.size_in_phase++] = ch; + else { + app_manager_printf("Invalid WASM version!\n"); + goto fail; + } + + if (recv_ctx.size_in_phase == + sizeof(recv_ctx.message.app_file.u.bytecode.version)) { + recv_ctx.phase = Phase_Wasm_Section_Type; + recv_ctx.size_in_phase = 0; + } + return true; + } + else if (recv_ctx.phase == Phase_Wasm_Section_Type) { + uint8 section_type = ch; + if (section_type <= SECTION_TYPE_DATA) { + wasm_section_t *new_section; + if (!(new_section = (wasm_section_t *) APP_MGR_MALLOC(sizeof(wasm_section_t)))) { + app_manager_printf("Allocate memory failed!\n"); + goto fail; + } + memset(new_section, 0, sizeof(wasm_section_t)); + new_section->section_type = section_type; + new_section->next = NULL; + + /* add the section to tail of link list */ + if (NULL == recv_ctx.message.app_file.u.bytecode.sections) { + recv_ctx.message.app_file.u.bytecode.sections = new_section; + recv_ctx.message.app_file.u.bytecode.section_end = new_section; + } + else { + recv_ctx.message.app_file.u.bytecode.section_end->next = new_section; + recv_ctx.message.app_file.u.bytecode.section_end = new_section; + } + + recv_ctx.phase = Phase_Wasm_Section_Size; + recv_ctx.size_in_phase = 0; + + return true; + } + else { + app_manager_printf("Invalid wasm section type: %d\n", section_type); + goto fail; + } + } + else if (recv_ctx.phase == Phase_Wasm_Section_Size) { + /* the last section is the current receiving one */ + wasm_section_t *section = recv_ctx.message.app_file.u.bytecode.section_end; + uint32 byte; + + bh_assert(section); + + byte = ch; + + section->section_body_size |= + ((byte & 0x7f) << recv_ctx.size_in_phase * 7); + recv_ctx.size_in_phase++; + /* check leab128 overflow for uint32 value */ + if (recv_ctx.size_in_phase > + (sizeof(section->section_body_size) * 8 + 7 - 1) / 7) { + app_manager_printf(" LEB overflow when parsing section size\n"); + goto fail; + } + + if ((byte & 0x80) == 0) { + /* leb128 encoded section size parsed done */ + if (!(section->section_body = APP_MGR_MALLOC(section->section_body_size))) { + app_manager_printf("Allocate memory failed!\n"); + goto fail; + } + recv_ctx.phase = Phase_Wasm_Section_Content; + recv_ctx.size_in_phase = 0; + } + + return true; + } + else if (recv_ctx.phase == Phase_Wasm_Section_Content) { + /* the last section is the current receiving one */ + wasm_section_t *section = recv_ctx.message.app_file.u.bytecode.section_end; + + bh_assert(section); + + section->section_body[recv_ctx.size_in_phase++] = ch; + + if (recv_ctx.size_in_phase == section->section_body_size) { + if (recv_ctx.total_received_size == request_total_size) { + /* whole wasm app received */ + if (module_wasm_app_handle_install_msg(&recv_ctx.message)) { + APP_MGR_FREE(recv_ctx.message.request_url); + recv_ctx.message.request_url = NULL; + memset(&recv_ctx, 0, sizeof(recv_ctx)); + return true; + } + else { + app_manager_printf("Handle install message failed!\n"); + goto fail; + } + } + else { + recv_ctx.phase = Phase_Wasm_Section_Type; + recv_ctx.size_in_phase = 0; + return true; + } + } + + return true; + } +#endif /* end of WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 */ +#if WASM_ENABLE_AOT != 0 + else if (recv_ctx.phase == Phase_AOT_Version) { + p = (uint8*)&recv_ctx.message.app_file.u.aot.version; + + if (ch == wasm_aot_version[recv_ctx.size_in_phase]) + p[recv_ctx.size_in_phase++] = ch; + else { + app_manager_printf("Invalid WASM AOT version!\n"); + goto fail; + } + + if (recv_ctx.size_in_phase == + sizeof(recv_ctx.message.app_file.u.aot.version)) { + recv_ctx.phase = Phase_AOT_Section_ID; + recv_ctx.size_in_phase = 0; + } + return true; + } + else if (recv_ctx.phase == Phase_AOT_Section_ID) { + aot_section_t *cur_section; + uint32 aot_file_cur_offset = recv_ctx.total_received_size - 1 - + 18 /* Request fixed part */ - + recv_ctx.message.request_url_len; + + if (recv_ctx.size_in_phase == 0) { + /* Skip paddings */ + if (aot_file_cur_offset % 4) + return true; + + if (!(cur_section = (aot_section_t *) APP_MGR_MALLOC(sizeof(aot_section_t)))) { + app_manager_printf("Allocate memory failed!\n"); + goto fail; + } + memset(cur_section, 0, sizeof(aot_section_t)); + + /* add the section to tail of link list */ + if (NULL == recv_ctx.message.app_file.u.aot.sections) { + recv_ctx.message.app_file.u.aot.sections = cur_section; + recv_ctx.message.app_file.u.aot.section_end = cur_section; + } + else { + recv_ctx.message.app_file.u.aot.section_end->next = cur_section; + recv_ctx.message.app_file.u.aot.section_end = cur_section; + } + } else { + cur_section = recv_ctx.message.app_file.u.aot.section_end; + bh_assert(cur_section); + } + + p = (uint8 *)&cur_section->section_type; + p[recv_ctx.size_in_phase++] = ch; + if (recv_ctx.size_in_phase == sizeof(cur_section->section_type)) { + /* Notes: integers are always little endian encoded in AOT file */ + if (!is_little_endian()) + exchange_uint32(p); + if (cur_section->section_type < AOT_SECTION_TYPE_SIGANATURE) { + recv_ctx.phase = Phase_AOT_Section_Size; + recv_ctx.size_in_phase = 0; + } + else { + app_manager_printf("Invalid AOT section id: %d\n", + cur_section->section_type); + goto fail; + } + } + + return true; + } + else if (recv_ctx.phase == Phase_AOT_Section_Size) { + /* the last section is the current receiving one */ + aot_section_t *section = recv_ctx.message.app_file.u.aot.section_end; + bh_assert(section); + + p = (uint8*)§ion->section_body_size; + p[recv_ctx.size_in_phase++] = ch; + if (recv_ctx.size_in_phase == sizeof(section->section_body_size)) { + /* Notes: integers are always little endian encoded in AOT file */ + if (!is_little_endian()) + exchange_uint32(p); + /* Allocate memory for section body */ + if (section->section_body_size > 0) { + if (section->section_type == AOT_SECTION_TYPE_TEXT) { + int map_prot = + MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + /* aot code and data in x86_64 must be in range 0 to 2G due to + relocation for R_X86_64_32/32S/PC32 */ + int map_flags = MMAP_MAP_32BIT; +#else + int map_flags = MMAP_MAP_NONE; +#endif + uint64 total_size = (uint64)section->section_body_size + + aot_get_plt_table_size(); + total_size = (total_size + 3) & ~((uint64)3); + if (total_size >= UINT32_MAX + || !(section->section_body = + os_mmap(NULL, (uint32)total_size, + map_prot, map_flags))) { + app_manager_printf("Allocate executable memory failed!\n"); + goto fail; + } +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + /* address must be in the first 2 Gigabytes of + the process address space */ + bh_assert((uintptr_t)section->section_body < INT32_MAX); +#endif + } + else { + if (!(section->section_body = + APP_MGR_MALLOC(section->section_body_size))) { + app_manager_printf("Allocate memory failed!\n"); + goto fail; + } + } + } + + recv_ctx.phase = Phase_AOT_Section_Content; + recv_ctx.size_in_phase = 0; + } + + return true; + } + else if (recv_ctx.phase == Phase_AOT_Section_Content) { + /* the last section is the current receiving one */ + aot_section_t *section = recv_ctx.message.app_file.u.aot.section_end; + bh_assert(section && section->section_body); + + section->section_body[recv_ctx.size_in_phase++] = ch; + + if (recv_ctx.size_in_phase == section->section_body_size) { + if (section->section_type == AOT_SECTION_TYPE_TEXT) { + uint32 total_size = section->section_body_size + + aot_get_plt_table_size(); + total_size = (total_size + 3) & ~3; + if (total_size > section->section_body_size) { + memset(section->section_body + section->section_body_size, + 0, total_size - section->section_body_size); + section->section_body_size = total_size; + } + } + if (recv_ctx.total_received_size == request_total_size) { + /* whole aot file received */ + if (module_wasm_app_handle_install_msg(&recv_ctx.message)) { + APP_MGR_FREE(recv_ctx.message.request_url); + recv_ctx.message.request_url = NULL; + memset(&recv_ctx, 0, sizeof(recv_ctx)); + return true; + } + else { + app_manager_printf("Handle install message failed!\n"); + goto fail; + } + } + else { + recv_ctx.phase = Phase_AOT_Section_ID; + recv_ctx.size_in_phase = 0; + return true; + } + } + + return true; + } +#endif /* end of WASM_ENABLE_AOT != 0 */ + +fail: + switch (package_type) { +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + case Wasm_Module_Bytecode: + destroy_all_wasm_sections(recv_ctx.message.app_file.u.bytecode.sections); + break; +#endif +#if WASM_ENABLE_AOT != 0 + case Wasm_Module_AoT: + destroy_all_aot_sections(recv_ctx.message.app_file.u.aot.sections); + break; +#endif + default: + break; + } + + if (recv_ctx.message.request_url != NULL) { + APP_MGR_FREE(recv_ctx.message.request_url); + recv_ctx.message.request_url = NULL; + } + + recv_ctx.phase = Phase_Req_Ver; + recv_ctx.size_in_phase = 0; + recv_ctx.total_received_size = 0; + + return false; +} + +static bool +module_wasm_app_handle_install_msg(install_wasm_app_msg_t *message) +{ + request_t *request = NULL; + bh_message_t msg; + + request = (request_t *) APP_MGR_MALLOC(sizeof(request_t)); + if (request == NULL) + return false; + + memset(request, 0, sizeof(*request)); + request->action = message->request_action; + request->fmt = message->request_fmt; + request->url = bh_strdup(message->request_url); + request->sender = ID_HOST; + request->mid = message->request_mid; + request->payload_len = sizeof(message->app_file); + request->payload = APP_MGR_MALLOC(request->payload_len); + + if (request->url == NULL || request->payload == NULL) { + request_cleaner(request); + return false; + } + + /* Request payload is set to wasm_app_file_t struct, + * but not whole app buffer */ + bh_memcpy_s(request->payload, request->payload_len, + &message->app_file, request->payload_len); + + /* Since it's a wasm app install request, so directly post to app-mgr's + * queue. The benefit is that section list can be freed when the msg + * failed to post to app-mgr's queue. The defect is missing url check. */ + if (!(msg = bh_new_msg(RESTFUL_REQUEST, request, sizeof(*request), + request_cleaner))) { + request_cleaner(request); + return false; + } + + if (!bh_post_msg2(get_app_manager_queue(), msg)) + return false; + + return true; +} + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 +static void +destroy_all_wasm_sections(wasm_section_list_t sections) +{ + wasm_section_t *cur = sections; + while (cur) { + wasm_section_t *next = cur->next; + if (cur->section_body != NULL) + APP_MGR_FREE(cur->section_body); + APP_MGR_FREE(cur); + cur = next; + } +} + +static void +destroy_part_wasm_sections(wasm_section_list_t *p_sections, + uint8 *section_types, + int section_cnt) +{ + int i; + for (i = 0; i < section_cnt; i++) { + uint8 section_type = section_types[i]; + wasm_section_t *cur = *p_sections, *prev = NULL; + + while (cur) { + wasm_section_t *next = cur->next; + if (cur->section_type == section_type) { + if (prev) + prev->next = next; + else + *p_sections = next; + + if (cur->section_body != NULL) + APP_MGR_FREE(cur->section_body); + APP_MGR_FREE(cur); + break; + } + else { + prev = cur; + cur = next; + } + } + } +} +#endif + +#if WASM_ENABLE_AOT != 0 +static void +destroy_all_aot_sections(aot_section_list_t sections) +{ + aot_section_t *cur = sections; + while (cur) { + aot_section_t *next = cur->next; + if (cur->section_body != NULL) { + if (cur->section_type == AOT_SECTION_TYPE_TEXT) + os_munmap(cur->section_body, cur->section_body_size); + else + APP_MGR_FREE(cur->section_body); + } + APP_MGR_FREE(cur); + cur = next; + } +} + + +static void +destroy_part_aot_sections(aot_section_list_t *p_sections, + uint8 *section_types, + int section_cnt) +{ + int i; + for (i = 0; i < section_cnt; i++) { + uint8 section_type = section_types[i]; + aot_section_t *cur = *p_sections, *prev = NULL; + + while (cur) { + aot_section_t *next = cur->next; + if (cur->section_type == section_type) { + if (prev) + prev->next = next; + else + *p_sections = next; + + if (cur->section_body != NULL) { + if (cur->section_type == AOT_SECTION_TYPE_TEXT) + os_munmap(cur->section_body, cur->section_body_size); + else + APP_MGR_FREE(cur->section_body); + } + APP_MGR_FREE(cur); + break; + } + else { + prev = cur; + cur = next; + } + } + } +} +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 +static char wasi_root_dir[PATH_MAX] = { '.' }; + +bool +wasm_set_wasi_root_dir(const char *root_dir) +{ + char *path, resolved_path[PATH_MAX]; + + if (!(path = realpath(root_dir, resolved_path))) + return false; + + strncpy(wasi_root_dir, path, sizeof(wasi_root_dir)); + return true; +} + +const char * +wasm_get_wasi_root_dir() +{ + return wasi_root_dir; +} +#endif + diff --git a/wamr/core/app-mgr/app-manager/module_wasm_app.h b/wamr/core/app-mgr/app-manager/module_wasm_app.h new file mode 100644 index 0000000..7a967e2 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_wasm_app.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _MODULE_WASM_APP_H_ +#define _MODULE_WASM_APP_H_ + +#include "bh_queue.h" +#include "app_manager_export.h" +#include "wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECTION_TYPE_USER 0 +#define SECTION_TYPE_TYPE 1 +#define SECTION_TYPE_IMPORT 2 +#define SECTION_TYPE_FUNC 3 +#define SECTION_TYPE_TABLE 4 +#define SECTION_TYPE_MEMORY 5 +#define SECTION_TYPE_GLOBAL 6 +#define SECTION_TYPE_EXPORT 7 +#define SECTION_TYPE_START 8 +#define SECTION_TYPE_ELEM 9 +#define SECTION_TYPE_CODE 10 +#define SECTION_TYPE_DATA 11 + +typedef enum AOTSectionType { + AOT_SECTION_TYPE_TARGET_INFO = 0, + AOT_SECTION_TYPE_INIT_DATA, + AOT_SECTION_TYPE_TEXT, + AOT_SECTION_TYPE_FUNCTION, + AOT_SECTION_TYPE_EXPORT, + AOT_SECTION_TYPE_RELOCATION, + AOT_SECTION_TYPE_SIGANATURE +} AOTSectionType; + +enum { + WASM_Msg_Start = BASE_EVENT_MAX, + TIMER_EVENT_WASM, + SENSOR_EVENT_WASM, + CONNECTION_EVENT_WASM, + WIDGET_EVENT_WASM, + WASM_Msg_End = WASM_Msg_Start + 100 +}; + +typedef struct wasm_data { + /* for easily access the containing wasm module */ + wasm_module_t wasm_module; + wasm_module_inst_t wasm_module_inst; + /* Permissions of the WASM app */ + char *perms; + /* thread list mapped with this WASM module */ + korp_tid thread_id; + /* for easily access the containing module data */ + module_data* m_data; + /* is bytecode or aot */ + bool is_bytecode; + /* sections of wasm bytecode or aot file */ + void *sections; + /* execution environment */ + wasm_exec_env_t exec_env; +} wasm_data; + +/* sensor event */ +typedef struct _sensor_event_data { + uint32 sensor_id; + + int data_fmt; + /* event of attribute container from context core */ + void *data; +} sensor_event_data_t; + +/* WASM Bytecode File */ +typedef struct wasm_bytecode_file { + /* magics */ + int magic; + /* current version */ + int version; + /* WASM section list */ + wasm_section_list_t sections; + /* Last WASM section in the list */ + wasm_section_t *section_end; +} wasm_bytecode_file_t; + +/* WASM AOT File */ +typedef struct wasm_aot_file { + /* magics */ + int magic; + /* current version */ + int version; + /* AOT section list */ + aot_section_list_t sections; + /* Last AOT section in the list */ + aot_section_t *section_end; +} wasm_aot_file_t; + +/* WASM App File */ +typedef struct wasm_app_file_t { + union { + wasm_bytecode_file_t bytecode; + wasm_aot_file_t aot; + } u; +} wasm_app_file_t; + +extern module_interface wasm_app_module_interface; + +typedef void (*message_type_handler_t)(module_data *m_data, bh_message_t msg); +extern bool wasm_register_msg_callback(int msg_type, + message_type_handler_t message_handler); + +typedef void (*resource_cleanup_handler_t)(uint32 module_id); +extern bool wasm_register_cleanup_callback(resource_cleanup_handler_t handler); + +/** + * Set WASI root dir for modules. On each wasm app installation, a sub dir named + * with the app's name will be created autamically. That wasm app can only access + * this sub dir. + * + * @param root_dir the root dir to set + * @return true for success, false otherwise + */ +bool +wasm_set_wasi_root_dir(const char *root_dir); + +/** + * Get WASI root dir + * + * @return the WASI root dir + */ +const char * +wasm_get_wasi_root_dir(); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _MODULE_WASM_APP_H_ */ diff --git a/wamr/core/app-mgr/app-manager/module_wasm_lib.c b/wamr/core/app-mgr/app-manager/module_wasm_lib.c new file mode 100644 index 0000000..bdf3215 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_wasm_lib.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#include "module_wasm_lib.h" + +static bool wasm_lib_module_init(void) +{ + return false; +} + +static bool wasm_lib_module_install(request_t *msg) +{ + (void) msg; + return false; +} + +static bool wasm_lib_module_uninstall(request_t *msg) +{ + (void) msg; + return false; +} + +static void wasm_lib_module_watchdog_kill(module_data *m_data) +{ + (void) m_data; +} + +static bool wasm_lib_module_handle_host_url(void *queue_msg) +{ + (void) queue_msg; + return false; +} + +static module_data* +wasm_lib_module_get_module_data(void *inst) +{ + (void) inst; + return NULL; +} + +module_interface wasm_lib_module_interface = { wasm_lib_module_init, + wasm_lib_module_install, wasm_lib_module_uninstall, + wasm_lib_module_watchdog_kill, wasm_lib_module_handle_host_url, + wasm_lib_module_get_module_data, + NULL }; + diff --git a/wamr/core/app-mgr/app-manager/module_wasm_lib.h b/wamr/core/app-mgr/app-manager/module_wasm_lib.h new file mode 100644 index 0000000..63ffd92 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/module_wasm_lib.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _MODULE_WASM_LIB_H_ +#define _MODULE_WASM_LIB_H_ + +#include "app_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern module_interface wasm_lib_module_interface; + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _MODULE_WASM_LIB_H_ */ diff --git a/wamr/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c b/wamr/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c new file mode 100644 index 0000000..45957bc --- /dev/null +++ b/wamr/core/app-mgr/app-manager/platform/linux/app_mgr_linux.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager.h" + +void* +app_manager_timer_create(void (*timer_callback)(void*), + watchdog_timer *wd_timer) +{ + /* TODO */ + return NULL; +} + +void app_manager_timer_destroy(void *timer) +{ + /* TODO */ +} + +void app_manager_timer_start(void *timer, int timeout) +{ + /* TODO */ +} + +void app_manager_timer_stop(void *timer) +{ + /* TODO */ +} + +watchdog_timer * +app_manager_get_wd_timer_from_timer_handle(void *timer) +{ + /* TODO */ + return NULL; +} + +int app_manager_signature_verify(const uint8_t *file, unsigned int file_len, + const uint8_t *signature, unsigned int sig_size) +{ + return 1; +} + diff --git a/wamr/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c b/wamr/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c new file mode 100644 index 0000000..cca6654 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/platform/zephyr/app_mgr_zephyr.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "app_manager.h" +#include "bh_platform.h" +#include +#include +#include +#if 0 +#include +#endif +typedef struct k_timer_watchdog { + struct k_timer timer; + watchdog_timer *wd_timer; +} k_timer_watchdog; + +void* +app_manager_timer_create(void (*timer_callback)(void*), + watchdog_timer *wd_timer) +{ + struct k_timer_watchdog *timer = APP_MGR_MALLOC(sizeof(struct k_timer_watchdog)); + + if (timer) { + k_timer_init(&timer->timer, (void (*)(struct k_timer*)) timer_callback, + NULL); + timer->wd_timer = wd_timer; + } + + return timer; +} + +void app_manager_timer_destroy(void *timer) +{ + APP_MGR_FREE(timer); +} + +void app_manager_timer_start(void *timer, int timeout) +{ + k_timer_start(timer, Z_TIMEOUT_MS(timeout), Z_TIMEOUT_MS(0)); +} + +void app_manager_timer_stop(void *timer) +{ + k_timer_stop(timer); +} + +watchdog_timer * +app_manager_get_wd_timer_from_timer_handle(void *timer) +{ + return ((k_timer_watchdog*) timer)->wd_timer; +} +#if 0 +int app_manager_signature_verify(const uint8_t *file, unsigned int file_len, + const uint8_t *signature, unsigned int sig_size) +{ + return signature_verify(file, file_len, signature, sig_size); +} +#endif diff --git a/wamr/core/app-mgr/app-manager/resource_reg.c b/wamr/core/app-mgr/app-manager/resource_reg.c new file mode 100644 index 0000000..83bc37d --- /dev/null +++ b/wamr/core/app-mgr/app-manager/resource_reg.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#include "native_interface.h" +#include "app_manager.h" +#include "app_manager_export.h" +#include "bi-inc/shared_utils.h" +#include "bi-inc/attr_container.h" +#include "coap_ext.h" + +typedef struct _app_res_register { + struct _app_res_register *next; + char * url; + void (*request_handler)(request_t *, void *); + uint32 register_id; +} app_res_register_t; + +static app_res_register_t * g_resources = NULL; + +void module_request_handler(request_t *request, void *user_data) +{ + unsigned int mod_id = (unsigned int)(uintptr_t)user_data; + bh_message_t msg; + module_data *m_data; + request_t *req; + + /* Check module name */ + m_data = module_data_list_lookup_id(mod_id); + if (!m_data) { + return; + } + + if (m_data->wd_timer.is_interrupting) { + return; + } + + req = clone_request(request); + if (!req) { + return; + } + + /* Set queue message and send to applet's queue */ + msg = bh_new_msg(RESTFUL_REQUEST, req, sizeof(*req), request_cleaner); + if (!msg) { + request_cleaner(req); + return; + } + + if (!bh_post_msg2(m_data->queue, msg)) { + return; + } + + app_manager_printf("Send request to app %s success.\n", + m_data->module_name); +} + +void targeted_app_request_handler(request_t *request, void *unused) +{ + char applet_name[128] = { 0 }; + int offset; + char *url = request->url; + module_data *m_data; + + offset = check_url_start(request->url, strlen(request->url), "/app/"); + + if (offset <= 0) { + return; + } + + strncpy(applet_name, request->url + offset, sizeof(applet_name) - 1); + char *p = strchr(applet_name, '/'); + if (p) { + *p = 0; + } else + return; + app_manager_printf("Send request to applet: %s\n", applet_name); + + request->url = p + 1; + + /* Check module name */ + m_data = module_data_list_lookup(applet_name); + if (!m_data) { + SEND_ERR_RESPONSE(request->mid, + "Send request to applet failed: invalid applet name"); + goto end; + } + + module_request_handler(request, (void *)(uintptr_t)m_data->id); + end: request->url = url; + +} + +void am_send_response(response_t *response) +{ + module_data *m_data; + + // if the receiver is not any of modules, just forward it to the host + m_data = module_data_list_lookup_id(response->reciever); + if (!m_data) { + send_response_to_host(response); + + } else { + response_t * resp_for_send = clone_response(response); + if (!resp_for_send) { + return; + } + + bh_message_t msg = bh_new_msg(RESTFUL_RESPONSE, resp_for_send, + sizeof(*resp_for_send), response_cleaner); + if (!msg) { + response_cleaner(resp_for_send); + return; + } + + if (!bh_post_msg2(m_data->queue, msg)) { + return; + } + } +} + +void * am_dispatch_request(request_t *request) +{ + app_res_register_t *r = g_resources; + + while (r) { + if (check_url_start(request->url, strlen(request->url), r->url) > 0) { + r->request_handler(request, (void *)(uintptr_t)r->register_id); + return r; + } + r = r->next; + } + return NULL; +} + +bool am_register_resource(const char *url, + void (*request_handler)(request_t *, void *), uint32 register_id) +{ + app_res_register_t * r = g_resources; + int register_num = 0; + + while (r) { + if (strcmp(r->url, url) == 0) { + return false; + } + + if (r->register_id == register_id) + register_num++; + + r = r->next; + } + + if (strlen(url) > RESOUCE_EVENT_URL_LEN_MAX) + return false; + + if (register_num >= RESOURCE_REGISTRATION_NUM_MAX) + return false; + + r = (app_res_register_t *) APP_MGR_MALLOC(sizeof(app_res_register_t)); + if (r == NULL) + return false; + + memset(r, 0, sizeof(*r)); + r->url = bh_strdup(url); + if (r->url == NULL) { + APP_MGR_FREE(r); + return false; + } + + r->request_handler = request_handler; + r->next = g_resources; + r->register_id = register_id; + g_resources = r; + + return true; +} + +void am_cleanup_registeration(uint32 register_id) +{ + app_res_register_t * r = g_resources; + app_res_register_t * prev = NULL; + + while (r) { + app_res_register_t *next = r->next; + + if (register_id == r->register_id) { + if (prev) + prev->next = next; + else + g_resources = next; + + APP_MGR_FREE(r->url); + APP_MGR_FREE(r); + } else + /* if r is freed, should not change prev. Only set prev to r + when r isn't freed. */ + prev = r; + + r = next; + } +} diff --git a/wamr/core/app-mgr/app-manager/watchdog.c b/wamr/core/app-mgr/app-manager/watchdog.c new file mode 100644 index 0000000..ccaf5c2 --- /dev/null +++ b/wamr/core/app-mgr/app-manager/watchdog.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "watchdog.h" +#include "bh_platform.h" + +#define WATCHDOG_THREAD_PRIORITY 5 + +/* Queue of watchdog */ +static bh_queue *watchdog_queue; + +#ifdef WATCHDOG_ENABLED /* TODO */ +static void watchdog_timer_callback(void *timer) +{ + watchdog_timer *wd_timer = app_manager_get_wd_timer_from_timer_handle( + timer); + + watchdog_timer_stop(wd_timer); + + os_mutex_lock(&wd_timer->lock); + + if (!wd_timer->is_stopped) { + + wd_timer->is_interrupting = true; + + bh_post_msg(watchdog_queue, WD_TIMEOUT, wd_timer->module_data, + sizeof(module_data)); + } + + os_mutex_unlock(&wd_timer->lock); +} +#endif + +bool watchdog_timer_init(module_data *m_data) +{ +#ifdef WATCHDOG_ENABLED /* TODO */ + watchdog_timer *wd_timer = &m_data->wd_timer; + + if (0 != os_mutex_init(&wd_timer->lock)) + return false; + + if (!(wd_timer->timer_handle = + app_manager_timer_create(watchdog_timer_callback, wd_timer))) { + os_mutex_destroy(&wd_timer->lock); + return false; + } + + wd_timer->module_data = m_data; + wd_timer->is_interrupting = false; + wd_timer->is_stopped = false; +#endif + return true; +} + +void watchdog_timer_destroy(watchdog_timer *wd_timer) +{ +#ifdef WATCHDOG_ENABLED /* TODO */ + app_manager_timer_destroy(wd_timer->timer_handle); + os_mutex_destroy(&wd_timer->lock); +#endif +} + +void watchdog_timer_start(watchdog_timer *wd_timer) +{ + os_mutex_lock(&wd_timer->lock); + + wd_timer->is_interrupting = false; + wd_timer->is_stopped = false; + app_manager_timer_start(wd_timer->timer_handle, + wd_timer->module_data->timeout); + + os_mutex_unlock(&wd_timer->lock); +} + +void watchdog_timer_stop(watchdog_timer *wd_timer) +{ + app_manager_timer_stop(wd_timer->timer_handle); +} + +#ifdef WATCHDOG_ENABLED /* TODO */ +static void watchdog_queue_callback(void *queue_msg) +{ + if (bh_message_type(queue_msg) == WD_TIMEOUT) { + module_data *m_data = (module_data *) bh_message_payload(queue_msg); + if (g_module_interfaces[m_data->module_type] + && g_module_interfaces[m_data->module_type]->module_watchdog_kill) { + g_module_interfaces[m_data->module_type]->module_watchdog_kill( + m_data); + app_manager_post_applets_update_event(); + } + } +} +#endif + +#ifdef WATCHDOG_ENABLED /* TODO */ +static void* +watchdog_thread_routine(void *arg) +{ + /* Enter loop run */ + bh_queue_enter_loop_run(watchdog_queue, watchdog_queue_callback); + + (void) arg; + return NULL; +} +#endif + +bool watchdog_startup() +{ + if (!(watchdog_queue = bh_queue_create())) { + app_manager_printf( + "App Manager start failed: create watchdog queue failed.\n"); + return false; + } +#if 0 +//todo: enable watchdog + /* Start watchdog thread */ + if (!jeff_runtime_create_supervisor_thread_with_prio(watchdog_thread_routine, NULL, + WATCHDOG_THREAD_PRIORITY)) { + bh_queue_destroy(watchdog_queue); + return false; + } +#endif + return true; +} diff --git a/wamr/core/app-mgr/app-manager/watchdog.h b/wamr/core/app-mgr/app-manager/watchdog.h new file mode 100644 index 0000000..a7a508b --- /dev/null +++ b/wamr/core/app-mgr/app-manager/watchdog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#ifndef _WATCHDOG_H_ +#define _WATCHDOG_H_ + +#include "app_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +watchdog_timer_init(module_data *module_data); + +void +watchdog_timer_destroy(watchdog_timer *wd_timer); + +void +watchdog_timer_start(watchdog_timer *wd_timer); + +void +watchdog_timer_stop(watchdog_timer *wd_timer); + +watchdog_timer* +app_manager_get_watchdog_timer(void *timer); + +bool +watchdog_startup(); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* _WATCHDOG_H_ */ diff --git a/wamr/core/app-mgr/app-mgr-shared/app_manager_export.h b/wamr/core/app-mgr/app-mgr-shared/app_manager_export.h new file mode 100644 index 0000000..aa676d2 --- /dev/null +++ b/wamr/core/app-mgr/app-mgr-shared/app_manager_export.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _APP_MANAGER_EXPORT_H_ +#define _APP_MANAGER_EXPORT_H_ + +#include "native_interface.h" +#include "bi-inc/shared_utils.h" +#include "bh_queue.h" +#include "host_link.h" +#include "runtime_timer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Special module IDs */ +#define ID_HOST -3 +#define ID_APP_MGR -2 +/* Invalid module ID */ +#define ID_NONE (uint32)-1 + +struct attr_container_t; + +/* Queue message type */ +typedef enum QUEUE_MSG_TYPE { + COAP_PARSED = LINK_MSG_TYPE_MAX + 1, + RESTFUL_REQUEST, + RESTFUL_RESPONSE, + TIMER_EVENT = 5, + SENSOR_EVENT = 6, + GPIO_INTERRUPT_EVENT = 7, + BLE_EVENT = 8, + JDWP_REQUEST = 9, + WD_TIMEOUT = 10, + BASE_EVENT_MAX = 100 + +} QUEUE_MSG_TYPE; + +typedef enum { + Module_Jeff, Module_WASM_App, Module_WASM_Lib, Module_Max +} Module_Type; + +struct module_data; + +/* Watchdog timer of module */ +typedef struct watchdog_timer { + /* Timer handle of the platform */ + void *timer_handle; + /* Module of the watchdog timer */ + struct module_data *module_data; + /* Lock of the watchdog timer */ + korp_mutex lock; + /* Flag indicates module is being interrupted by watchdog */ + bool is_interrupting; + /* Flag indicates watchdog timer is stopped */ + bool is_stopped; +} watchdog_timer; + +typedef struct module_data { + struct module_data *next; + + /* ID of the module */ + uint32 id; + + /* Type of the module */ + Module_Type module_type; + + /* Heap of the module */ + void *heap; + + /* Heap size of the module */ + int heap_size; + + /* Module execution timeout in millisecond */ + int timeout; + + /* Queue of the module */ + bh_queue *queue; + + /* Watchdog timer of the module*/ + struct watchdog_timer wd_timer; + + timer_ctx_t timer_ctx; + + /* max timers number app can create */ + int timers; + + /* Internal data of the module */ + void *internal_data; + + /* Module name */ + char module_name[1]; +} module_data; + +/* Module function types */ +typedef bool (*module_init_func)(void); +typedef bool (*module_install_func)(request_t *msg); +typedef bool (*module_uninstall_func)(request_t *msg); +typedef void (*module_watchdog_kill_func)(module_data *module_data); +typedef bool (*module_handle_host_url_func)(void *queue_msg); +typedef module_data *(*module_get_module_data_func)(void *inst); + +/** + * @typedef module_on_install_request_byte_arrive_func + * + * @brief Define the signature of function to handle one byte of + * module app install request for struct module_interface. + * + * @param ch the byte to be received and handled + * @param total_size total size of the request + * @param received_total_size currently received total size when the function return + * + * @return true if success, false otherwise + */ +typedef bool (*module_on_install_request_byte_arrive_func) ( + uint8 ch, int total_size, int *received_total_size); + +/* Interfaces of each module */ +typedef struct module_interface { + module_init_func module_init; + module_install_func module_install; + module_uninstall_func module_uninstall; + module_watchdog_kill_func module_watchdog_kill; + module_handle_host_url_func module_handle_host_url; + module_get_module_data_func module_get_module_data; + module_on_install_request_byte_arrive_func module_on_install; +} module_interface; + +/** + * @typedef host_init_func + * @brief Define the host initialize callback function signature for + * struct host_interface. + * + * @return true if success, false if fail + */ +typedef bool (*host_init_func)(void); + +/** + * @typedef host_send_fun + * @brief Define the host send callback function signature for + * struct host_interface. + * + * @param buf data buffer to send. + * @param size size of the data to send. + * + * @return size of the data sent in bytes + */ +typedef int (*host_send_fun)(void * ctx, const char *buf, int size); + +/** + * @typedef host_destroy_fun + * @brief Define the host receive callback function signature for + * struct host_interface. + * + */ +typedef void (*host_destroy_fun)(); + +/* Interfaces of host communication */ +typedef struct host_interface { + host_init_func init; + host_send_fun send; + host_destroy_fun destroy; +} host_interface; + +/** + * Initialize communication with Host + * + * @param interface host communication interface + * + * @return true if success, false otherwise + */ +bool +app_manager_host_init(host_interface *interface); + +/* Startup app manager */ +void +app_manager_startup(host_interface *interface); + +/* Get queue of current applet */ +void * +app_manager_get_module_queue(uint32 module_type, void *module_inst); + +/* Get applet name of current applet */ +const char * +app_manager_get_module_name(uint32 module_type, void *module_inst); + +/* Get heap of current applet */ +void * +app_manager_get_module_heap(uint32 module_type, void *module_inst); + +void* +get_app_manager_queue(); + +module_data* +app_manager_get_module_data(uint32 module_type, void *module_inst); + +unsigned int +app_manager_get_module_id(uint32 module_type, void *module_inst); + +module_data* +app_manager_lookup_module_data(const char *name); + +module_data* +module_data_list_lookup(const char *module_name); + +module_data* +module_data_list_lookup_id(unsigned int module_id); + +void +app_manager_post_applets_update_event(); + +bool +am_register_resource(const char *url, + void (*request_handler)(request_t *, void *), uint32 register_id); + +void am_cleanup_registeration(uint32 register_id); + +bool +am_register_event(const char *url, uint32_t reg_client); + +bool +am_unregister_event(const char *url, uint32_t reg_client); + +void am_publish_event(request_t * event); + +void * am_dispatch_request(request_t *request); + +void am_send_response(response_t *response); + +void module_request_handler(request_t *request, void *user_data); + +/** + * Send request message to host + * + * @param msg the request or event message. + * It is event when msg->action==COAP_EVENT + * + * @return true if success, false otherwise + */ +bool +send_request_to_host(request_t *msg); + +/** + * Send response message to host + * + * @param msg the response message + * + * @return true if success, false otherwise + */ +bool +send_response_to_host(response_t *msg); + +/** + * Send response with mid and code to host + * + * @param mid the message id of response + * @param code the code/status of response + * @param msg the detailed message + * + * @return true if success, false otherwise + */ +bool +send_error_response_to_host(int mid, int code, const char *msg); + +/** + * Check whether the applet has the permission + * + * @param perm the permission needed to check + * + * @return true if success, false otherwise + */ +bool +bh_applet_check_permission(const char *perm); + +/** + * Send message to Host + * + * @param buf buffer to send + * @param size size of buffer + * + * @return size of buffer sent + */ +int +app_manager_host_send_msg(int msg_type, const char *buf, int size); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif + diff --git a/wamr/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake b/wamr/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake new file mode 100644 index 0000000..f370e8b --- /dev/null +++ b/wamr/core/app-mgr/app-mgr-shared/app_mgr_shared.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (APP_MGR_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${APP_MGR_SHARED_DIR}) + + +file (GLOB_RECURSE source_all ${APP_MGR_SHARED_DIR}/*.c) + +set (APP_MGR_SHARED_SOURCE ${source_all}) + +file (GLOB header + ${APP_MGR_SHARED_DIR}/*.h +) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/app-mgr/app-mgr-shared/host_link.h b/wamr/core/app-mgr/app-mgr-shared/host_link.h new file mode 100644 index 0000000..e3a37fb --- /dev/null +++ b/wamr/core/app-mgr/app-mgr-shared/host_link.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ +#define DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ + +typedef enum LINK_MSG_TYPE { + COAP_TCP_RAW = 0, + COAP_UDP_RAW = 1, + REQUEST_PACKET, + RESPONSE_PACKET, + INSTALL_WASM_APP, + CBOR_GENERIC = 30, + + LINK_MSG_TYPE_MAX = 50 +} LINK_MSG_TYPE; + +/* Link message, or message between host and app manager */ +typedef struct bh_link_msg_t { + /* 2 bytes leading */ + uint16_t leading_bytes; + /* message type, must be COAP_TCP or COAP_UDP */ + uint16_t message_type; + /* size of payload */ + uint32_t payload_size; + char *payload; +} bh_link_msg_t; + +#endif /* DEPS_APP_MGR_APP_MGR_SHARED_HOST_LINK_H_ */ diff --git a/wamr/core/app-mgr/module.json b/wamr/core/app-mgr/module.json new file mode 100644 index 0000000..b2faeca --- /dev/null +++ b/wamr/core/app-mgr/module.json @@ -0,0 +1,52 @@ +{ + "name": "aee", + "version": "0.0.1", + "description": "aee", + "type": "source", + "category": "middleware", + "arch": "x86, arc, posix", + "includes": [ + "Beihai/classlib/include", + "Beihai/runtime/include", + "Beihai/runtime/platform/include", + "Beihai/runtime/platform/zephyr", + "Beihai/runtime/utils/coap/er-coap", + "Beihai/runtime/utils/coap/extension", + "iwasm/runtime/include", + "iwasm/runtime/platform/include", + "iwasm/runtime/platform/zephyr", + "iwasm/runtime/vmcore_wasm" + ], + "sources": [ + "Beihai/classlib/native/internal/*.c", + "Beihai/classlib/native/*.c", + "Beihai/runtime/gc/*.c", + "Beihai/runtime/platform/zephyr/*.c", + "Beihai/runtime/utils/*.c", + "Beihai/runtime/utils/coap/er-coap/*.c", + "Beihai/runtime/utils/coap/extension/*.c", + "Beihai/runtime/vmcore_jeff/*.c", + "app-manager/app-manager.c", + "app-manager/app-manager-host.c", + "app-manager/app_mgr_zephyr.c", + "app-manager/event.c", + "app-manager/message.c", + "app-manager/module_jeff.c", + "app-manager/module_wasm_lib.c", + "app-manager/module_wasm_app.c", + "app-manager/watchdog.c", + "Beihai/products/iMRT/*.c", + "iwasm/runtime/utils/*.c", + "iwasm/runtime/platform/zephyr/*.c", + "iwasm/runtime/vmcore_wasm/*.c", + "iwasm/lib/lib-export\.c", + "iwasm/lib/aee/*.c", + "iwasm/products/zephyr/sample/src/*.c" + ], + "compile_definitions": [ + "__JLF__", + "__ZEPHYR__" + ], + "target": "aee", + "dependencies": [] +} diff --git a/wamr/core/config.h b/wamr/core/config.h new file mode 100644 index 0000000..414056b --- /dev/null +++ b/wamr/core/config.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#if !defined(BUILD_TARGET_X86_64) \ + && !defined(BUILD_TARGET_AMD_64) \ + && !defined(BUILD_TARGET_AARCH64) \ + && !defined(BUILD_TARGET_X86_32) \ + && !defined(BUILD_TARGET_ARM) \ + && !defined(BUILD_TARGET_ARM_VFP) \ + && !defined(BUILD_TARGET_THUMB) \ + && !defined(BUILD_TARGET_THUMB_VFP) \ + && !defined(BUILD_TARGET_MIPS) \ + && !defined(BUILD_TARGET_XTENSA) +#if defined(__x86_64__) || defined(__x86_64) +#define BUILD_TARGET_X86_64 +#elif defined(__amd64__) || defined(__amd64) +#define BUILD_TARGET_AMD_64 +#elif defined(__aarch64__) +#define BUILD_TARGET_AARCH64 +#elif defined(__i386__) || defined(__i386) || defined(i386) +#define BUILD_TARGET_X86_32 +#elif defined(__thumb__) +#define BUILD_TARGET_THUMB +#define BUILD_TARGET "THUMBV4T" +#elif defined(__arm__) +#define BUILD_TARGET_ARM +#define BUILD_TARGET "ARMV4T" +#elif defined(__mips__) || defined(__mips) || defined(mips) +#define BUILD_TARGET_MIPS +#elif defined(__XTENSA__) +#define BUILD_TARGET_XTENSA +#else +#error "Build target isn't set" +#endif +#endif + +#ifndef BH_DEBUG +#define BH_DEBUG 0 +#endif + +enum { + /* Memory allocator ems */ + MEM_ALLOCATOR_EMS = 0, + /* Memory allocator tlsf */ + MEM_ALLOCATOR_TLSF +}; + +/* Default memory allocator */ +#define DEFAULT_MEM_ALLOCATOR MEM_ALLOCATOR_EMS + +#ifndef WASM_ENABLE_INTERP +#define WASM_ENABLE_INTERP 0 +#endif + +#ifndef WASM_ENABLE_AOT +#define WASM_ENABLE_AOT 0 +#endif + +#define AOT_MAGIC_NUMBER 0x746f6100 +#define AOT_CURRENT_VERSION 2 + +#ifndef WASM_ENABLE_JIT +#define WASM_ENABLE_JIT 0 +#endif + +#if (WASM_ENABLE_AOT == 0) && (WASM_ENABLE_JIT != 0) +/* JIT can only be enabled when AOT is enabled */ +#undef WASM_ENABLE_JIT +#define WASM_ENABLE_JIT 0 +#endif + +#ifndef WASM_ENABLE_WAMR_COMPILER +#define WASM_ENABLE_WAMR_COMPILER 0 +#endif + +#ifndef WASM_ENABLE_LIBC_BUILTIN +#define WASM_ENABLE_LIBC_BUILTIN 0 +#endif + +#ifndef WASM_ENABLE_LIBC_WASI +#define WASM_ENABLE_LIBC_WASI 0 +#endif + +#ifndef WASM_ENABLE_LIB_PTHREAD +#define WASM_ENABLE_LIB_PTHREAD 0 +#endif + +#ifndef WASM_ENABLE_BASE_LIB +#define WASM_ENABLE_BASE_LIB 0 +#endif + +#ifndef WASM_ENABLE_APP_FRAMEWORK +#define WASM_ENABLE_APP_FRAMEWORK 0 +#endif + +/* Bulk memory operation */ +#ifndef WASM_ENABLE_BULK_MEMORY +#define WASM_ENABLE_BULK_MEMORY 0 +#endif + +/* Shared memory */ +#ifndef WASM_ENABLE_SHARED_MEMORY +#define WASM_ENABLE_SHARED_MEMORY 0 +#endif + +/* Thread manager */ +#ifndef WASM_ENABLE_THREAD_MGR +#define WASM_ENABLE_THREAD_MGR 0 +#endif + +/* WASM log system */ +#ifndef WASM_ENABLE_LOG +#define WASM_ENABLE_LOG 1 +#endif + +#if defined(BUILD_TARGET_X86_32) || defined(BUILD_TARGET_X86_64) +#define WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS 1 +#else +#define WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS 0 +#endif + +/* WASM Interpreter labels-as-values feature */ +#ifdef __GNUC__ +#define WASM_ENABLE_LABELS_AS_VALUES 1 +#else +#define WASM_ENABLE_LABELS_AS_VALUES 0 +#endif + +/* Enable fast interpreter or not */ +#ifndef WASM_ENABLE_FAST_INTERP +#define WASM_ENABLE_FAST_INTERP 0 +#endif + +#if WASM_ENABLE_FAST_INTERP != 0 +#define WASM_ENABLE_ABS_LABEL_ADDR 1 +#define WASM_DEBUG_PREPROCESSOR 0 +#else +#define WASM_ENABLE_ABS_LABEL_ADDR 0 +#endif + +/* Enable opcode counter or not */ +#ifndef WASM_ENABLE_OPCODE_COUNTER +#define WASM_ENABLE_OPCODE_COUNTER 0 +#endif + +/* Support a module with dependency, other modules */ +#ifndef WASM_ENABLE_MULTI_MODULE +#define WASM_ENABLE_MULTI_MODULE 0 +#endif + +/* Enable wasm mini loader or not */ +#ifndef WASM_ENABLE_MINI_LOADER +#define WASM_ENABLE_MINI_LOADER 0 +#endif + +/* Disable boundary check with hardware trap or not, + * enable it by default if it is supported */ +#ifndef WASM_DISABLE_HW_BOUND_CHECK +#define WASM_DISABLE_HW_BOUND_CHECK 0 +#endif + +/* Heap and stack profiling */ +#define BH_ENABLE_MEMORY_PROFILING 0 + +/* Heap verification */ +#define BH_ENABLE_GC_VERIFY 0 + +/* Max app number of all modules */ +#define MAX_APP_INSTALLATIONS 3 + +/* Default timer number in one app */ +#define DEFAULT_TIMERS_PER_APP 20 + +/* Max timer number in one app */ +#define MAX_TIMERS_PER_APP 30 + +/* Max connection number in one app */ +#define MAX_CONNECTION_PER_APP 20 + +/* Max resource registration number in one app */ +#define RESOURCE_REGISTRATION_NUM_MAX 16 + +/* Max length of resource/event url */ +#define RESOUCE_EVENT_URL_LEN_MAX 256 + +/* Default length of queue */ +#define DEFAULT_QUEUE_LENGTH 50 + +/* Default watchdog interval in ms */ +#define DEFAULT_WATCHDOG_INTERVAL (3 * 60 * 1000) + +/* The max percentage of global heap that app memory space can grow */ +#define APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT 1 / 3 + +/* Default base offset of app heap space */ +#define DEFAULT_APP_HEAP_BASE_OFFSET (1 * BH_GB) + +/* Default min/max heap size of each app */ +#define APP_HEAP_SIZE_DEFAULT (8 * 1024) +#define APP_HEAP_SIZE_MIN (2 * 1024) +#define APP_HEAP_SIZE_MAX (512 * 1024 * 1024) + +/* Default wasm stack size of each app */ +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#define DEFAULT_WASM_STACK_SIZE (16 * 1024) +#else +#define DEFAULT_WASM_STACK_SIZE (12 * 1024) +#endif + +/* Default/min/max stack size of each app thread */ +#if !defined(BH_PLATFORM_ZEPHYR) && !defined(BH_PLATFORM_ALIOS_THINGS) +#define APP_THREAD_STACK_SIZE_DEFAULT (32 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (24 * 1024) +#else +#define APP_THREAD_STACK_SIZE_DEFAULT (6 * 1024) +#define APP_THREAD_STACK_SIZE_MIN (4 * 1024) +#endif +#if !defined(APP_THREAD_STACK_SIZE_MAX) +#define APP_THREAD_STACK_SIZE_MAX (8 * 1024 * 1024) +#endif + +/* Reserved bytes to the native thread stack boundary, throw native + stack overflow exception if the guard boudary is reached */ +#define RESERVED_BYTES_TO_NATIVE_STACK_BOUNDARY (512) + +/* Default wasm block address cache size and conflict list size */ +#define BLOCK_ADDR_CACHE_SIZE 64 +#define BLOCK_ADDR_CONFLICT_SIZE 2 + +#ifndef WASM_ENABLE_SPEC_TEST +#define WASM_ENABLE_SPEC_TEST 0 +#endif + +/* Default max thread num per cluster. Can be overwrite by + wasm_runtime_set_max_thread_num */ +#define CLUSTER_MAX_THREAD_NUM 4 + +#endif /* end of _CONFIG_H_ */ + diff --git a/wamr/core/deps/README.md b/wamr/core/deps/README.md new file mode 100644 index 0000000..e69de29 diff --git a/wamr/core/deps/download.sh b/wamr/core/deps/download.sh new file mode 100755 index 0000000..923a9e0 --- /dev/null +++ b/wamr/core/deps/download.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +DEPS_ROOT=$(cd "$(dirname "$0")/" && pwd) +cd ${DEPS_ROOT} + + +if [ ! -d "lvgl" ]; then + echo "git pull lvgl..." + git clone https://github.com/littlevgl/lvgl.git --branch v6.0.1 + [ $? -eq 0 ] || exit $? + + ../app-framework/wgl/app/prepare_headers.sh +fi +if [ ! -d "lv_drivers" ]; then + echo "git pull lv_drivers..." + git clone https://github.com/littlevgl/lv_drivers.git + [ $? -eq 0 ] || exit $? +fi + +if [ ! -d "tlsf" ]; then + echo "git pull tlsf..." + git clone https://github.com/mattconte/tlsf + [ $? -eq 0 ] || exit $? + #cd ${WAMR_DIR}/core/shared/mem-alloc +fi + + +exit 0 diff --git a/wamr/core/iwasm/README.md b/wamr/core/iwasm/README.md new file mode 100644 index 0000000..e69de29 diff --git a/wamr/core/iwasm/aot/aot_loader.c b/wamr/core/iwasm/aot/aot_loader.c new file mode 100644 index 0000000..a17cce0 --- /dev/null +++ b/wamr/core/iwasm/aot/aot_loader.c @@ -0,0 +1,2215 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_runtime.h" +#include "bh_common.h" +#include "bh_log.h" +#include "aot_reloc.h" +#include "../common/wasm_runtime_common.h" +#include "../common/wasm_native.h" +#include "../compilation/aot.h" +#if WASM_ENABLE_JIT != 0 +#include "../compilation/aot_llvm.h" +#include "../interpreter/wasm_loader.h" +#endif + + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "AOT module load failed: %s", string); + } +} + +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, + const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, + "AOT module load failed: %s", buf); + } +} + +#define exchange_uint8(p_data) (void)0 + +static void +exchange_uint16(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 1); + *(p_data + 1) = value; +} + +static void +exchange_uint32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static void +exchange_uint64(uint8 *pData) +{ + exchange_uint32(pData); + exchange_uint32(pData + 4); +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +static bool +check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) +{ + if (buf + length > buf_end) { + set_error_buf(error_buf, error_buf_size, "unexpect end"); + return false; + } + return true; +} + +#define CHECK_BUF(buf, buf_end, length) do { \ + if (!check_buf(buf, buf_end, length, \ + error_buf, error_buf_size)) { \ + goto fail; \ + } \ +} while (0) + +static uint8* +align_ptr(const uint8 *p, uint32 b) +{ + uintptr_t v = (uintptr_t)p; + uintptr_t m = b - 1; + return (uint8*)((v + m) & ~m); +} + +static inline uint64 +GET_U64_FROM_ADDR(uint32 *addr) +{ + union { uint64 val; uint32 parts[2]; } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +#define TEMPLATE_READ(p, p_end, res, type) do { \ + if (sizeof(type) != sizeof(uint64)) \ + p = (uint8*)align_ptr(p, sizeof(type)); \ + else \ + /* align 4 bytes if type is uint64 */ \ + p = (uint8*)align_ptr(p, sizeof(uint32)); \ + CHECK_BUF(p, p_end, sizeof(type)); \ + if (sizeof(type) != sizeof(uint64)) \ + res = *(type*)p; \ + else \ + res = (type)GET_U64_FROM_ADDR((uint32*)p); \ + if (!is_little_endian()) \ + exchange_##type((uint8*)&res); \ + p += sizeof(type); \ + } while (0) + +#define read_uint8(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint8) +#define read_uint16(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint16) +#define read_uint32(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint32) +#define read_uint64(p, p_end, res) TEMPLATE_READ(p, p_end, res, uint64) + +#define read_byte_array(p, p_end, addr, len) do { \ + CHECK_BUF(p, p_end, len); \ + memcpy(addr, p, len); \ + p += len; \ + } while (0) + +#define read_string(p, p_end, str) do { \ + uint16 str_len; \ + read_uint16(p, p_end, str_len); \ + CHECK_BUF(p, p_end, str_len); \ + if (!(str = const_str_set_insert \ + (p, str_len, module, \ + error_buf, error_buf_size))) { \ + goto fail; \ + } \ + p += str_len; \ + } while (0) + +/* Legal values for bin_type */ +#define BIN_TYPE_ELF32L 0 /* 32-bit little endian */ +#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */ +#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */ +#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */ + +/* Legal values for e_type (object file type). */ +#define E_TYPE_NONE 0 /* No file type */ +#define E_TYPE_REL 1 /* Relocatable file */ +#define E_TYPE_EXEC 2 /* Executable file */ +#define E_TYPE_DYN 3 /* Shared object file */ + +/* Legal values for e_machine (architecture). */ +#define E_MACHINE_386 3 /* Intel 80386 */ +#define E_MACHINE_MIPS 8 /* MIPS R3000 big-endian */ +#define E_MACHINE_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define E_MACHINE_ARM 40 /* ARM/Thumb */ +#define E_MACHINE_AARCH64 183 /* AArch64 */ +#define E_MACHINE_ARC 45 /* Argonaut RISC Core */ +#define E_MACHINE_IA_64 50 /* Intel Merced */ +#define E_MACHINE_MIPS_X 51 /* Stanford MIPS-X */ +#define E_MACHINE_X86_64 62 /* AMD x86-64 architecture */ +#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */ + +/* Legal values for e_version */ +#define E_VERSION_CURRENT 1 /* Current version */ + +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static char* +const_str_set_insert(const uint8 *str, int32 len, AOTModule *module, + char* error_buf, uint32 error_buf_size) +{ + HashMap *set = module->const_str_set; + char *c_str, *value; + + if (!(c_str = loader_malloc((uint32)len + 1, + error_buf, error_buf_size))) { + return NULL; + } + + bh_memcpy_s(c_str, (uint32)(len + 1), str, (uint32)len); + c_str[len] = '\0'; + + if ((value = bh_hash_map_find(set, c_str))) { + wasm_runtime_free(c_str); + return value; + } + + if (!bh_hash_map_insert(set, c_str, c_str)) { + set_error_buf(error_buf, error_buf_size, + "insert string to hash map failed"); + wasm_runtime_free(c_str); + return NULL; + } + + return c_str; +} + +static bool +get_aot_file_target(AOTTargetInfo *target_info, + char *target_buf, uint32 target_buf_size, + char *error_buf, uint32 error_buf_size) +{ + char *machine_type = NULL; + switch (target_info->e_machine) { + case E_MACHINE_X86_64: + machine_type = "x86_64"; + break; + case E_MACHINE_386: + machine_type = "i386"; + break; + case E_MACHINE_ARM: + case E_MACHINE_AARCH64: + machine_type = target_info->arch; + break; + case E_MACHINE_MIPS: + machine_type = "mips"; + break; + case E_MACHINE_XTENSA: + machine_type = "xtensa"; + break; + default: + set_error_buf_v(error_buf, error_buf_size, + "unknown machine type %d", + target_info->e_machine); + return false; + } + if (strncmp(target_info->arch, machine_type, strlen(machine_type))) { + set_error_buf_v(error_buf, error_buf_size, + "machine type (%s) isn't consistent with target type (%s)", + machine_type, target_info->arch); + return false; + } + snprintf(target_buf, target_buf_size, "%s", target_info->arch); + return true; +} + +static bool +check_machine_info(AOTTargetInfo *target_info, + char *error_buf, uint32 error_buf_size) +{ + char target_expected[32], target_got[32]; + + get_current_target(target_expected, sizeof(target_expected)); + + if (!get_aot_file_target(target_info, target_got, sizeof(target_got), + error_buf, error_buf_size)) + return false; + + if (strcmp(target_expected, target_got)) { + set_error_buf_v(error_buf, error_buf_size, + "invalid target type, expected %s but got %s", + target_expected, target_got); + return false; + } + + return true; +} + +static bool +load_target_info_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + AOTTargetInfo target_info; + const uint8 *p = buf, *p_end = buf_end; + bool is_target_little_endian, is_target_64_bit; + + read_uint16(p, p_end, target_info.bin_type); + read_uint16(p, p_end, target_info.abi_type); + read_uint16(p, p_end, target_info.e_type); + read_uint16(p, p_end, target_info.e_machine); + read_uint32(p, p_end, target_info.e_version); + read_uint32(p, p_end, target_info.e_flags); + read_uint32(p, p_end, target_info.reserved); + read_byte_array(p, p_end, + target_info.arch, sizeof(target_info.arch)); + + if (p != buf_end) { + set_error_buf(error_buf, error_buf_size, + "invalid section size"); + return false; + } + + /* Check target endian type */ + is_target_little_endian = target_info.bin_type & 1 ? false : true; + if (is_little_endian() != is_target_little_endian) { + set_error_buf_v(error_buf, error_buf_size, + "invalid target endian type, expected %s but got %s", + is_little_endian() ? "little endian" : "big endian", + is_target_little_endian ? "little endian" : "big endian"); + return false; + } + + /* Check target bit width */ + is_target_64_bit = target_info.bin_type & 2 ? true : false; + if ((sizeof(void*) == 8 ? true : false) != is_target_64_bit) { + set_error_buf_v(error_buf, error_buf_size, + "invalid target bit width, expected %s but got %s", + sizeof(void*) == 8 ? "64-bit" : "32-bit", + is_target_64_bit ? "64-bit" : "32-bit"); + return false; + } + + /* Check target elf file type */ + if (target_info.e_type != E_TYPE_REL) { + set_error_buf(error_buf, error_buf_size, + "invalid object file type, " + "expected relocatable file type but got others"); + return false; + } + + /* Check machine info */ + if (!check_machine_info(&target_info, error_buf, error_buf_size)) { + return false; + } + + if (target_info.e_version != E_VERSION_CURRENT) { + set_error_buf(error_buf, error_buf_size, + "invalid elf file version"); + return false; + } + + return true; +fail: + return false; +} + +static void +destroy_import_memories(AOTImportMemory *import_memories, + bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(import_memories); +} + +static void +destroy_mem_init_data_list(AOTMemInitData **data_list, uint32 count, + bool is_jit_mode) +{ + if (!is_jit_mode) { + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) + wasm_runtime_free(data_list[i]); + wasm_runtime_free(data_list); + } +} + +static bool +load_mem_init_data_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTMemInitData **data_list; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTMemInitData *) * (uint64)module->mem_init_data_count; + if (!(module->mem_init_data_list = data_list = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each memory data segment */ + for (i = 0; i < module->mem_init_data_count; i++) { + uint32 init_expr_type, byte_count; + uint64 init_expr_value; + uint32 is_passive; + uint32 memory_index; + + read_uint32(buf, buf_end, is_passive); + read_uint32(buf, buf_end, memory_index); + read_uint32(buf, buf_end, init_expr_type); + read_uint64(buf, buf_end, init_expr_value); + read_uint32(buf, buf_end, byte_count); + size = offsetof(AOTMemInitData, bytes) + (uint64)byte_count; + if (!(data_list[i] = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + /* is_passive and memory_index is only used in bulk memory mode */ + data_list[i]->is_passive = (bool)is_passive; + data_list[i]->memory_index = memory_index; +#endif + data_list[i]->offset.init_expr_type = (uint8)init_expr_type; + data_list[i]->offset.u.i64 = (int64)init_expr_value; + data_list[i]->byte_count = byte_count; + read_byte_array(buf, buf_end, + data_list[i]->bytes, data_list[i]->byte_count); + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_memory_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + uint64 total_size; + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_memory_count); + /* We don't support import_memory_count > 0 currently */ + bh_assert(module->import_memory_count == 0); + + read_uint32(buf, buf_end, module->memory_count); + total_size = sizeof(AOTMemory) * (uint64)module->memory_count; + if (!(module->memories = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->memory_count; i++) { + read_uint32(buf, buf_end, module->memories[i].memory_flags); + read_uint32(buf, buf_end, module->memories[i].num_bytes_per_page); + read_uint32(buf, buf_end, module->memories[i].mem_init_page_count); + read_uint32(buf, buf_end, module->memories[i].mem_max_page_count); + } + + read_uint32(buf, buf_end, module->mem_init_data_count); + + /* load memory init data list */ + if (module->mem_init_data_count > 0 + && !load_mem_init_data_list(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_import_tables(AOTImportTable *import_tables, bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(import_tables); +} + +static void +destroy_tables(AOTTable *tables, bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(tables); +} + +static void +destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count, + bool is_jit_mode) +{ + if (!is_jit_mode) { + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) + wasm_runtime_free(data_list[i]); + wasm_runtime_free(data_list); + } +} + +static bool +load_table_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTTable *table; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTTable) * (uint64)module->table_count; + if (!(module->tables = table = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each table data segment */ + for (i = 0; i < module->table_count; i++, table++) { + read_uint32(buf, buf_end, table->elem_type); + read_uint32(buf, buf_end, table->table_flags); + read_uint32(buf, buf_end, table->table_init_size); + read_uint32(buf, buf_end, table->table_max_size); + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTTableInitData **data_list; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTTableInitData *) * (uint64)module->table_init_data_count; + if (!(module->table_init_data_list = data_list = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each table data segment */ + for (i = 0; i < module->table_init_data_count; i++) { + uint32 table_index, init_expr_type, func_index_count; + uint64 init_expr_value, size1; + + read_uint32(buf, buf_end, table_index); + read_uint32(buf, buf_end, init_expr_type); + read_uint64(buf, buf_end, init_expr_value); + read_uint32(buf, buf_end, func_index_count); + + size1 = sizeof(uint32) * (uint64)func_index_count; + size = offsetof(AOTTableInitData, func_indexes) + size1; + if (!(data_list[i] = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + data_list[i]->table_index = table_index; + data_list[i]->offset.init_expr_type = (uint8)init_expr_type; + data_list[i]->offset.u.i64 = (int64)init_expr_value; + data_list[i]->func_index_count = func_index_count; + read_byte_array(buf, buf_end, data_list[i]->func_indexes, size1); + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_table_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_table_count); + /* We don't support import_table_count > 0 currently */ + bh_assert(module->import_table_count == 0); + + read_uint32(buf, buf_end, module->table_count); + if (module->table_count > 0 + && !load_table_list(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + read_uint32(buf, buf_end, module->table_init_data_count); + + /* load table init data list */ + if (module->table_init_data_count > 0 + && !load_table_init_data_list(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_func_types(AOTFuncType **func_types, uint32 count, bool is_jit_mode) +{ + if (!is_jit_mode) { + uint32 i; + for (i = 0; i < count; i++) + if (func_types[i]) + wasm_runtime_free(func_types[i]); + wasm_runtime_free(func_types); + } +} + +static bool +load_func_types(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTFuncType **func_types; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTFuncType *) * (uint64)module->func_type_count; + if (!(module->func_types = func_types = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + /* Create each function type */ + for (i = 0; i < module->func_type_count; i++) { + uint32 param_count, result_count; + uint32 param_cell_num, ret_cell_num; + uint64 size1; + + read_uint32(buf, buf_end, param_count); + read_uint32(buf, buf_end, result_count); + + if (param_count > UINT16_MAX || result_count > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + return false; + } + + size1 = (uint64)param_count + (uint64)result_count; + size = offsetof(AOTFuncType, types) + size1; + if (!(func_types[i] = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + func_types[i]->param_count = (uint16)param_count; + func_types[i]->result_count = (uint16)result_count; + read_byte_array(buf, buf_end, func_types[i]->types, (uint32)size1); + + param_cell_num = wasm_get_cell_num(func_types[i]->types, param_count); + ret_cell_num = wasm_get_cell_num(func_types[i]->types + param_count, + result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + return false; + } + + func_types[i]->param_cell_num = (uint16)param_cell_num; + func_types[i]->ret_cell_num = (uint16)ret_cell_num; + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_func_type_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->func_type_count); + + /* load function type */ + if (module->func_type_count > 0 + && !load_func_types(&buf, buf_end, module, error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_import_globals(AOTImportGlobal *import_globals, bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(import_globals); +} + +static bool +load_import_globals(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTImportGlobal *import_globals; + uint64 size; + uint32 i, data_offset = 0; + + /* Allocate memory */ + size = sizeof(AOTImportGlobal) * (uint64)module->import_global_count; + if (!(module->import_globals = import_globals = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each import global */ + for (i = 0; i < module->import_global_count; i++) { + buf = (uint8*)align_ptr(buf, 2); + read_uint8(buf, buf_end, import_globals[i].type); + read_uint8(buf, buf_end, import_globals[i].is_mutable); + read_string(buf, buf_end, import_globals[i].module_name); + read_string(buf, buf_end, import_globals[i].global_name); + + import_globals[i].size = wasm_value_type_size(import_globals[i].type); + import_globals[i].data_offset = data_offset; + data_offset += import_globals[i].size; + module->global_data_size += import_globals[i].size; + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_import_global_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_global_count); + + /* load import globals */ + if (module->import_global_count > 0 + && !load_import_globals(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_globals(AOTGlobal *globals, bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(globals); +} + +static bool +load_globals(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTGlobal *globals; + uint64 size; + uint32 i, data_offset = 0; + AOTImportGlobal *last_import_global; + + /* Allocate memory */ + size = sizeof(AOTGlobal) * (uint64)module->global_count; + if (!(module->globals = globals = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + if (module->import_global_count > 0) { + last_import_global = + &module->import_globals[module->import_global_count - 1]; + data_offset = last_import_global->data_offset + + last_import_global->size; + } + + /* Create each global */ + for (i = 0; i < module->global_count; i++) { + uint16 init_expr_type; + uint64 init_expr_value; + + read_uint8(buf, buf_end, globals[i].type); + read_uint8(buf, buf_end, globals[i].is_mutable); + read_uint16(buf, buf_end, init_expr_type); + read_uint64(buf, buf_end, init_expr_value); + globals[i].init_expr.init_expr_type = (uint8)init_expr_type; + globals[i].init_expr.u.i64 = (int64)init_expr_value; + + globals[i].size = wasm_value_type_size(globals[i].type); + globals[i].data_offset = data_offset; + data_offset += globals[i].size; + module->global_data_size += globals[i].size; + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_global_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->global_count); + + /* load globals */ + if (module->global_count > 0 + && !load_globals(&buf, buf_end, module, error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_import_funcs(AOTImportFunc *import_funcs, + bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(import_funcs); +} + +static bool +load_import_funcs(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const char *module_name, *field_name; + const uint8 *buf = *p_buf; + AOTImportFunc *import_funcs; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTImportFunc) * (uint64)module->import_func_count; + if (!(module->import_funcs = import_funcs = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each import func */ + for (i = 0; i < module->import_func_count; i++) { + read_uint16(buf, buf_end, import_funcs[i].func_type_index); + if (import_funcs[i].func_type_index >= module->func_type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + 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 = + wasm_native_resolve_symbol(module_name, field_name, + import_funcs[i].func_type, + &import_funcs[i].signature, + &import_funcs[i].attachment, + &import_funcs[i].call_conv_raw))) { + LOG_WARNING("warning: fail to link import function (%s, %s)\n", + module_name, field_name); + } + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!strcmp(import_funcs[i].module_name, "wasi_unstable") + || !strcmp(import_funcs[i].module_name, "wasi_snapshot_preview1")) + module->is_wasi_module = true; +#endif + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_import_func_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->import_func_count); + + /* load import funcs */ + if (module->import_func_count > 0 + && !load_import_funcs(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static void +destroy_object_data_sections(AOTObjectDataSection *data_sections, + uint32 data_section_count) +{ + uint32 i; + AOTObjectDataSection *data_section = data_sections; + for (i = 0; i < data_section_count; i++, data_section++) + if (data_section->data) + os_munmap(data_section->data, data_section->size); + wasm_runtime_free(data_sections); +} + +static bool +load_object_data_sections(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTObjectDataSection *data_sections; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTObjectDataSection) * (uint64)module->data_section_count; + if (!(module->data_sections = data_sections = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each data section */ + for (i = 0; i < module->data_section_count; i++) { + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + /* aot code and data in x86_64 must be in range 0 to 2G due to + relocation for R_X86_64_32/32S/PC32 */ + int map_flags = MMAP_MAP_32BIT; +#else + int map_flags = MMAP_MAP_NONE; +#endif + + read_string(buf, buf_end, data_sections[i].name); + read_uint32(buf, buf_end, data_sections[i].size); + + /* Allocate memory for data */ + if (!(data_sections[i].data = + os_mmap(NULL, data_sections[i].size, map_prot, map_flags))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return false; + } +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#ifndef BH_PLATFORM_LINUX_SGX + /* address must be in the first 2 Gigabytes of + the process address space */ + bh_assert((uintptr_t)data_sections[i].data < INT32_MAX); +#endif +#endif + + read_byte_array(buf, buf_end, + data_sections[i].data, data_sections[i].size); + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_object_data_sections_info(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + + read_uint32(buf, buf_end, module->data_section_count); + + /* load object data sections */ + if (module->data_section_count > 0 + && !load_object_data_sections(&buf, buf_end, module, + error_buf, error_buf_size)) + return false; + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_init_data_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + + if (!load_memory_info(&p, p_end, module, error_buf, error_buf_size) + || !load_table_info(&p, p_end, module, error_buf, error_buf_size) + || !load_func_type_info(&p, p_end, module, error_buf, error_buf_size) + || !load_import_global_info(&p, p_end, module, error_buf, error_buf_size) + || !load_global_info(&p, p_end, module, error_buf, error_buf_size) + || !load_import_func_info(&p, p_end, module, error_buf, error_buf_size)) + return false; + + /* load function count and start function index */ + read_uint32(p, p_end, module->func_count); + read_uint32(p, p_end, module->start_func_index); + + /* check start function index */ + if (module->start_func_index != (uint32)-1 + && (module->start_func_index >= module->import_func_count + + module->func_count)) { + set_error_buf(error_buf, error_buf_size, + "invalid start function index"); + return false; + } + + read_uint32(p, p_end, module->aux_data_end_global_index); + read_uint32(p, p_end, module->aux_data_end); + read_uint32(p, p_end, module->aux_heap_base_global_index); + read_uint32(p, p_end, module->aux_heap_base); + read_uint32(p, p_end, module->aux_stack_top_global_index); + read_uint32(p, p_end, module->aux_stack_bottom); + read_uint32(p, p_end, module->aux_stack_size); + + if (!load_object_data_sections_info(&p, p_end, module, + error_buf, error_buf_size)) + return false; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, + "invalid init data section size"); + return false; + } + + return true; + +fail: + return false; +} + +static bool +load_text_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint8 *plt_base; + + if (module->func_count > 0 && buf_end == buf) { + set_error_buf(error_buf, error_buf_size, + "invalid code size"); + return false; + } + + read_uint32(buf, buf_end, module->literal_size); + + /* literal data is at begining of the text section */ + module->literal = (uint8*)buf; + module->code = (void*)(buf + module->literal_size); + module->code_size = (uint32)(buf_end - (uint8*)module->code); + + if (module->code_size > 0) { + plt_base = (uint8*)buf_end - get_plt_table_size(); + init_plt_table(plt_base); + } + return true; + +fail: + return false; +} + +static bool +load_function_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 i; + uint64 size, text_offset; + + size = sizeof(void*) * (uint64)module->func_count; + if (!(module->func_ptrs = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->func_count; i++) { + if (sizeof(void*) == 8) { + read_uint64(p, p_end, text_offset); + } + else { + uint32 text_offset32; + read_uint32(p, p_end, text_offset32); + text_offset = text_offset32; + } + if (text_offset >= module->code_size) { + set_error_buf(error_buf, error_buf_size, + "invalid function code offset"); + return false; + } + module->func_ptrs[i] = (uint8*)module->code + text_offset; +#if defined(BUILD_TARGET_THUMB) || defined(BUILD_TARGET_THUMB_VFP) + /* bits[0] of thumb function address must be 1 */ + module->func_ptrs[i] = (void*)((uintptr_t)module->func_ptrs[i] | 1); +#endif + } + + /* Set start function when function pointers are resolved */ + if (module->start_func_index != (uint32)-1) { + if (module->start_func_index >= module->import_func_count) + module->start_function = + module->func_ptrs[module->start_func_index + - module->import_func_count]; + else + /* TODO: fix start function can be import function issue */ + module->start_function = NULL; + } + else { + module->start_function = NULL; + } + + size = sizeof(uint32) * (uint64)module->func_count; + if (!(module->func_type_indexes = loader_malloc + (size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->func_count; i++) { + read_uint32(p, p_end, module->func_type_indexes[i]); + if (module->func_type_indexes[i] >= module->func_type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + return false; + } + } + + if (p != buf_end) { + set_error_buf(error_buf, error_buf_size, + "invalid function section size"); + return false; + } + + return true; +fail: + return false; +} + +static void +destroy_exports(AOTExport *exports, bool is_jit_mode) +{ + if (!is_jit_mode) + wasm_runtime_free(exports); +} + +static bool +load_exports(const uint8 **p_buf, const uint8 *buf_end, + AOTModule *module, char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTExport *exports; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTExport) * (uint64)module->export_count; + if (!(module->exports = exports = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* Create each export */ + for (i = 0; i < module->export_count; i++) { + read_uint32(buf, buf_end, exports[i].index); + read_uint8(buf, buf_end, exports[i].kind); + read_string(buf, buf_end, exports[i].name); +#if 0 /* TODO: check kind and index */ + if (export_funcs[i].index >= + module->func_count + module->import_func_count) { + set_error_buf(error_buf, error_buf_size, + "function index is out of range"); + return false; + } +#endif + } + + *p_buf = buf; + return true; +fail: + return false; +} + +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + + /* load export functions */ + read_uint32(p, p_end, module->export_count); + if (module->export_count > 0 + && !load_exports(&p, p_end, module, error_buf, error_buf_size)) + return false; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, + "invalid export section size"); + return false; + } + + return true; + +fail: + return false; +} + + +static void * +get_data_section_addr(AOTModule *module, const char *section_name, + uint32 *p_data_size) +{ + uint32 i; + AOTObjectDataSection *data_section = module->data_sections; + + for (i = 0; i < module->data_section_count; i++, data_section++) + if (!strcmp(data_section->name, section_name)) { + if (p_data_size) + *p_data_size = data_section->size; + return data_section->data; + } + + return NULL; +} + +static void * +resolve_target_sym(const char *symbol, int32 *p_index) +{ + uint32 i, num = 0; + SymbolMap *target_sym_map; + + if (!(target_sym_map = get_target_symbol_map(&num))) + return NULL; + + for (i = 0; i < num; i++) + if (!strcmp(target_sym_map[i].symbol_name, symbol)) { + *p_index = (int32)i; + return target_sym_map[i].symbol_addr; + } + return NULL; +} + +static bool +is_literal_relocation(const char *reloc_sec_name) +{ + return !strcmp(reloc_sec_name, ".rela.literal"); +} + +static bool +do_text_relocation(AOTModule *module, + AOTRelocationGroup *group, + char *error_buf, uint32 error_buf_size) +{ + bool is_literal = is_literal_relocation(group->section_name); + uint8 *aot_text = is_literal ? module->literal : module->code; + uint32 aot_text_size = is_literal ? module->literal_size : module->code_size; + uint32 i, func_index, symbol_len; + char symbol_buf[128] = { 0 }, *symbol, *p; + void *symbol_addr; + AOTRelocation *relocation = group->relocations; + + if (group->relocation_count > 0 && !aot_text) { + set_error_buf(error_buf, error_buf_size, + "invalid text relocation count"); + return false; + } + + for (i = 0; i < group->relocation_count; i++, relocation++) { + int32 symbol_index = -1; + symbol_len = (uint32)strlen(relocation->symbol_name); + if (symbol_len + 1 <= sizeof(symbol_buf)) + symbol = symbol_buf; + else { + if (!(symbol = loader_malloc(symbol_len + 1, + error_buf, error_buf_size))) { + return false; + } + } + memcpy(symbol, relocation->symbol_name, symbol_len); + symbol[symbol_len] = '\0'; + + if (!strncmp(symbol, AOT_FUNC_PREFIX, strlen(AOT_FUNC_PREFIX))) { + p = symbol + strlen(AOT_FUNC_PREFIX); + if (*p == '\0' + || (func_index = (uint32)atoi(p)) > module->func_count) { + set_error_buf_v(error_buf, error_buf_size, + "invalid import symbol %s", symbol); + goto check_symbol_fail; + } + symbol_addr = module->func_ptrs[func_index]; + } + else if (!strcmp(symbol, ".text")) { + symbol_addr = module->code; + } + else if (!strcmp(symbol, ".data") + || !strcmp(symbol, ".rodata") + /* ".rodata.cst4/8/16/.." */ + || !strncmp(symbol, ".rodata.cst", strlen(".rodata.cst"))) { + symbol_addr = get_data_section_addr(module, symbol, NULL); + if (!symbol_addr) { + set_error_buf_v(error_buf, error_buf_size, + "invalid data section (%s)", symbol); + goto check_symbol_fail; + } + } + else if (!strcmp(symbol, ".literal")) { + symbol_addr = module->literal; + } + else if (!(symbol_addr = resolve_target_sym(symbol, &symbol_index))) { + set_error_buf_v(error_buf, error_buf_size, + "resolve symbol %s failed", symbol); + goto check_symbol_fail; + } + + if (symbol != symbol_buf) + wasm_runtime_free(symbol); + + if (!apply_relocation(module, + aot_text, aot_text_size, + relocation->relocation_offset, + relocation->relocation_addend, + relocation->relocation_type, + symbol_addr, symbol_index, + error_buf, error_buf_size)) + return false; + } + + return true; + +check_symbol_fail: + if (symbol != symbol_buf) + wasm_runtime_free(symbol); + return false; +} + +static bool +do_data_relocation(AOTModule *module, + AOTRelocationGroup *group, + char *error_buf, uint32 error_buf_size) + +{ + uint8 *data_addr; + uint32 data_size = 0, i; + AOTRelocation *relocation = group->relocations; + void *symbol_addr; + char *symbol, *data_section_name; + + if (!strncmp(group->section_name, ".rela.", 6)) { + data_section_name = group->section_name + strlen(".rela"); + } + else if (!strncmp(group->section_name, ".rel.", 5)) { + data_section_name = group->section_name + strlen(".rel"); + } + else { + set_error_buf(error_buf, error_buf_size, + "invalid data relocation section name"); + return false; + } + + data_addr = get_data_section_addr(module, data_section_name, + &data_size); + if (group->relocation_count > 0 && !data_addr) { + set_error_buf(error_buf, error_buf_size, + "invalid data relocation count"); + return false; + } + + for (i = 0; i < group->relocation_count; i++, relocation++) { + symbol = relocation->symbol_name; + if (!strcmp(symbol, ".text")) { + symbol_addr = module->code; + } + else { + set_error_buf_v(error_buf, error_buf_size, + "invalid relocation symbol %s", symbol); + return false; + } + + if (!apply_relocation(module, + data_addr, data_size, + relocation->relocation_offset, + relocation->relocation_addend, + relocation->relocation_type, + symbol_addr, -1, + error_buf, error_buf_size)) + return false; + } + + return true; +} + +static bool +validate_symbol_table(uint8 *buf, uint8 *buf_end, + uint32 *offsets, uint32 count, + char *error_buf, uint32 error_buf_size) +{ + uint32 i, str_len_addr = 0; + uint16 str_len; + + for (i = 0; i < count; i++) { + if (offsets[i] != str_len_addr) + return false; + + read_uint16(buf, buf_end, str_len); + str_len_addr += (uint32)sizeof(uint16) + str_len; + str_len_addr = align_uint(str_len_addr, 2); + buf += str_len; + buf = (uint8*)align_ptr(buf, 2); + } + + if (buf == buf_end) + return true; +fail: + return false; +} + +static bool +load_relocation_section(const uint8 *buf, const uint8 *buf_end, + AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + AOTRelocationGroup *groups = NULL, *group; + uint32 symbol_count = 0; + uint32 group_count = 0, i, j; + uint64 size; + uint32 *symbol_offsets, total_string_len; + uint8 *symbol_buf, *symbol_buf_end; + bool ret = false; + + read_uint32(buf, buf_end, symbol_count); + + symbol_offsets = (uint32 *)buf; + for (i = 0; i < symbol_count; i++) { + CHECK_BUF(buf, buf_end, sizeof(uint32)); + buf += sizeof(uint32); + } + + read_uint32(buf, buf_end, total_string_len); + symbol_buf = (uint8 *)buf; + symbol_buf_end = symbol_buf + total_string_len; + + if (!validate_symbol_table(symbol_buf, symbol_buf_end, + symbol_offsets, symbol_count, + error_buf, error_buf_size)) { + set_error_buf(error_buf, error_buf_size, + "validate symbol table failed"); + goto fail; + } + + buf = symbol_buf_end; + read_uint32(buf, buf_end, group_count); + + /* Allocate memory for relocation groups */ + size = sizeof(AOTRelocationGroup) * (uint64)group_count; + if (!(groups = loader_malloc(size, error_buf, error_buf_size))) { + goto fail; + } + + /* Load each relocation group */ + for (i = 0, group = groups; i < group_count; i++, group++) { + AOTRelocation *relocation; + uint32 name_index; + uint16 str_len; + uint8 *name_addr; + + /* section name address is 4 bytes aligned. */ + buf = (uint8*)align_ptr(buf, sizeof(uint32)); + read_uint32(buf, buf_end, name_index); + + if (name_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + name_addr = symbol_buf + symbol_offsets[name_index]; + str_len = *(uint16 *)name_addr; + + if (!(group->section_name = + const_str_set_insert(name_addr + sizeof(uint16), + (int32)str_len, module, + error_buf, error_buf_size))) { + goto fail; + } + + read_uint32(buf, buf_end, group->relocation_count); + + /* Allocate memory for relocations */ + size = sizeof(AOTRelocation) * (uint64)group->relocation_count; + if (!(group->relocations = relocation = + loader_malloc(size, error_buf, error_buf_size))) { + ret = false; + goto fail; + } + + /* Load each relocation */ + for (j = 0; j < group->relocation_count; j++, relocation++) { + uint32 symbol_index; + uint16 str_len; + uint8 *symbol_addr; + + if (sizeof(void *) == 8) { + read_uint64(buf, buf_end, relocation->relocation_offset); + read_uint64(buf, buf_end, relocation->relocation_addend); + } + else { + uint32 offset32, addend32; + read_uint32(buf, buf_end, offset32); + relocation->relocation_offset = (uint64)offset32; + read_uint32(buf, buf_end, addend32); + relocation->relocation_addend = (uint64)addend32; + } + read_uint32(buf, buf_end, relocation->relocation_type); + read_uint32(buf, buf_end, symbol_index); + + if (symbol_index >= symbol_count) { + set_error_buf(error_buf, error_buf_size, + "symbol index out of range"); + goto fail; + } + + symbol_addr = symbol_buf + symbol_offsets[symbol_index]; + str_len = *(uint16 *)symbol_addr; + + if (!(relocation->symbol_name = + const_str_set_insert(symbol_addr + sizeof(uint16), + (int32)str_len, module, + error_buf, error_buf_size))) { + goto fail; + } + } + + if (!strcmp(group->section_name, ".rel.text") + || !strcmp(group->section_name, ".rela.text") + || !strcmp(group->section_name, ".rela.literal")) { + if (!do_text_relocation(module, group, error_buf, error_buf_size)) + return false; + } + else { + if (!do_data_relocation(module, group, error_buf, error_buf_size)) + return false; + } + } + + ret = true; + +fail: + if (groups) { + for (i = 0, group = groups; i < group_count; i++, group++) + if (group->relocations) + wasm_runtime_free(group->relocations); + wasm_runtime_free(groups); + } + + return ret; +} + +static bool +load_from_sections(AOTModule *module, AOTSection *sections, + char *error_buf, uint32 error_buf_size) +{ + AOTSection *section = sections; + const uint8 *buf, *buf_end; + uint32 last_section_type = (uint32)-1, section_type; + uint32 i, func_index, func_type_index; + AOTFuncType *func_type; + AOTExport *exports; + + while (section) { + buf = section->section_body; + buf_end = buf + section->section_body_size; + /* Check sections */ + section_type = (uint32)section->section_type; + if ((last_section_type == (uint32)-1 + && section_type != AOT_SECTION_TYPE_TARGET_INFO) + || (last_section_type != (uint32)-1 + && section_type != last_section_type + 1)) { + set_error_buf(error_buf, error_buf_size, + "invalid section order"); + return false; + } + last_section_type = section_type; + switch (section_type) { + case AOT_SECTION_TYPE_TARGET_INFO: + if (!load_target_info_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_INIT_DATA: + if (!load_init_data_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_TEXT: + if (!load_text_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_FUNCTION: + if (!load_function_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_EXPORT: + if (!load_export_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case AOT_SECTION_TYPE_RELOCATION: + if (!load_relocation_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + } + + section = section->next; + } + + if (last_section_type != AOT_SECTION_TYPE_RELOCATION) { + set_error_buf(error_buf, error_buf_size, + "section missing"); + return false; + } + + /* Resolve malloc and free function */ + module->malloc_func_index = (uint32)-1; + module->free_func_index = (uint32)-1; + + exports = module->exports; + for (i = 0; i < module->export_count; i++) { + if (exports[i].kind == EXPORT_KIND_FUNC + && exports[i].index >= module->import_func_count) { + if (!strcmp(exports[i].name, "malloc")) { + func_index = exports[i].index - module->import_func_count; + func_type_index = module->func_type_indexes[func_index]; + func_type = module->func_types[func_type_index]; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == VALUE_TYPE_I32 + && func_type->types[1] == VALUE_TYPE_I32) { + module->malloc_func_index = func_index; + LOG_VERBOSE("Found malloc function, index: %u", + exports[i].index); + } + } + else if (!strcmp(exports[i].name, "free")) { + func_index = exports[i].index - module->import_func_count; + func_type_index = module->func_type_indexes[func_index]; + func_type = module->func_types[func_type_index]; + if (func_type->param_count == 1 + && func_type->result_count == 0 + && func_type->types[0] == VALUE_TYPE_I32) { + module->free_func_index = func_index; + LOG_VERBOSE("Found free function, index: %u", + exports[i].index); + } + } + } + } + + /* Flush data cache before executing AOT code, + * otherwise unpredictable behavior can occur. */ + os_dcache_flush(); + + return true; +} + +#if BH_ENABLE_MEMORY_PROFILING != 0 +static void aot_free(void *ptr) +{ + wasm_runtime_free(ptr); +} +#else +#define aot_free wasm_runtime_free +#endif + +static AOTModule* +create_module(char *error_buf, uint32 error_buf_size) +{ + AOTModule *module = + loader_malloc(sizeof(AOTModule), error_buf, error_buf_size); + + if (!module) { + return NULL; + } + + module->module_type = Wasm_Module_AoT; + + if (!(module->const_str_set = + bh_hash_map_create(32, false, + (HashFunc)wasm_string_hash, + (KeyEqualFunc)wasm_string_equal, + NULL, + aot_free))) { + set_error_buf(error_buf, error_buf_size, + "create const string set failed"); + wasm_runtime_free(module); + return NULL; + } + + return module; +} + +AOTModule* +aot_load_from_sections(AOTSection *section_list, + char *error_buf, uint32 error_buf_size) +{ + AOTModule *module = create_module(error_buf, error_buf_size); + + if (!module) + return NULL; + + if (!load_from_sections(module, section_list, + error_buf, error_buf_size)) { + aot_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module from sections success.\n"); + return module; +} + +static void +destroy_sections(AOTSection *section_list, bool destroy_aot_text) +{ + AOTSection *section = section_list, *next; + while (section) { + next = section->next; + if (destroy_aot_text + && section->section_type == AOT_SECTION_TYPE_TEXT + && section->section_body) + os_munmap((uint8*)section->section_body, section->section_body_size); + wasm_runtime_free(section); + section = next; + } +} + +static bool +create_sections(const uint8 *buf, uint32 size, + AOTSection **p_section_list, + char *error_buf, uint32 error_buf_size) +{ + AOTSection *section_list = NULL, *section_list_end = NULL, *section; + const uint8 *p = buf, *p_end = buf + size; + uint32 section_type; + uint32 section_size; + uint64 total_size; + uint8 *aot_text; + + p += 8; + while (p < p_end) { + read_uint32(p, p_end, section_type); + if (section_type < AOT_SECTION_TYPE_SIGANATURE) { + read_uint32(p, p_end, section_size); + CHECK_BUF(p, p_end, section_size); + + if (!(section = + loader_malloc(sizeof(AOTSection), + error_buf, error_buf_size))) { + goto fail; + } + + memset(section, 0, sizeof(AOTSection)); + section->section_type = (int32)section_type; + section->section_body = (uint8*)p; + section->section_body_size = section_size; + + if (section_type == AOT_SECTION_TYPE_TEXT) { + if (section_size > 0) { + int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE + | MMAP_PROT_EXEC; +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) + /* aot code and data in x86_64 must be in range 0 to 2G due to + relocation for R_X86_64_32/32S/PC32 */ + int map_flags = MMAP_MAP_32BIT; +#else + int map_flags = MMAP_MAP_NONE; +#endif + total_size = (uint64)section_size + aot_get_plt_table_size(); + total_size = (total_size + 3) & ~((uint64)3); + if (total_size >= UINT32_MAX + || !(aot_text = os_mmap(NULL, (uint32)total_size, + map_prot, map_flags))) { + wasm_runtime_free(section); + set_error_buf(error_buf, error_buf_size, + "mmap memory failed"); + goto fail; + } +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#ifndef BH_PLATFORM_LINUX_SGX + /* address must be in the first 2 Gigabytes of + the process address space */ + bh_assert((uintptr_t)aot_text < INT32_MAX); +#endif +#endif + bh_memcpy_s(aot_text, (uint32)total_size, + section->section_body, (uint32)section_size); + section->section_body = aot_text; + + if ((uint32)total_size > section->section_body_size) { + memset(aot_text + (uint32)section_size, + 0, (uint32)total_size - section_size); + section->section_body_size = (uint32)total_size; + } + } + else + section->section_body = NULL; + } + + if (!section_list) + section_list = section_list_end = section; + else { + section_list_end->next = section; + section_list_end = section; + } + + p += section_size; + } + else { + set_error_buf(error_buf, error_buf_size, + "invalid section id"); + goto fail; + } + } + + if (!section_list) { + set_error_buf(error_buf, error_buf_size, + "create section list failed"); + return false; + } + + *p_section_list = section_list; + return true; +fail: + if (section_list) + destroy_sections(section_list, true); + return false; +} + +static bool +load(const uint8 *buf, uint32 size, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf_end = buf + size; + const uint8 *p = buf, *p_end = buf_end; + uint32 magic_number, version; + AOTSection *section_list = NULL; + bool ret; + + read_uint32(p, p_end, magic_number); + if (magic_number != AOT_MAGIC_NUMBER) { + set_error_buf(error_buf, error_buf_size, "magic header not detected"); + return false; + } + + read_uint32(p, p_end, version); + if (version != AOT_CURRENT_VERSION) { + set_error_buf(error_buf, error_buf_size, "unknown binary version"); + return false; + } + + if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size)) + return false; + + ret = load_from_sections(module, section_list, error_buf, error_buf_size); + if (!ret) { + /* If load_from_sections() fails, then aot text is destroyed + in destroy_sections() */ + destroy_sections(section_list, true); + /* aot_unload() won't destroy aot text again */ + module->code = NULL; + } + else { + /* If load_from_sections() succeeds, then aot text is set to + module->code and will be destroyed in aot_unload() */ + destroy_sections(section_list, false); + } + return ret; +fail: + return false; +} + +AOTModule* +aot_load_from_aot_file(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size) +{ + AOTModule *module = create_module(error_buf, error_buf_size); + + if (!module) + return NULL; + + if (!load(buf, size, module, error_buf, error_buf_size)) { + aot_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module success.\n"); + return module; +} + +#if WASM_ENABLE_JIT != 0 +static AOTModule* +aot_load_from_comp_data(AOTCompData *comp_data, AOTCompContext *comp_ctx, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + uint64 size; + char func_name[32]; + AOTModule *module; + + /* Allocate memory for module */ + if (!(module = + loader_malloc(sizeof(AOTModule), error_buf, error_buf_size))) { + return NULL; + } + + module->module_type = Wasm_Module_AoT; + + module->import_memory_count = comp_data->import_memory_count; + module->import_memories = comp_data->import_memories; + + module->memory_count = comp_data->memory_count; + if (module->memory_count) { + size = sizeof(AOTMemory) * (uint64)module->memory_count; + if (!(module->memories = + loader_malloc(size, error_buf, error_buf_size))) { + goto fail1; + } + + bh_memcpy_s(module->memories, size, comp_data->memories, size); + } + + module->mem_init_data_list = comp_data->mem_init_data_list; + module->mem_init_data_count = comp_data->mem_init_data_count; + + module->import_table_count = comp_data->import_table_count; + module->import_tables = comp_data->import_tables; + + module->table_count = comp_data->table_count; + module->tables = comp_data->tables; + + module->table_init_data_list = comp_data->table_init_data_list; + module->table_init_data_count = comp_data->table_init_data_count; + + module->func_type_count = comp_data->func_type_count; + module->func_types = comp_data->func_types; + + module->import_global_count = comp_data->import_global_count; + module->import_globals = comp_data->import_globals; + + module->global_count = comp_data->global_count; + module->globals = comp_data->globals; + + module->global_count = comp_data->global_count; + module->globals = comp_data->globals; + + module->global_data_size = comp_data->global_data_size; + + module->import_func_count = comp_data->import_func_count; + module->import_funcs = comp_data->import_funcs; + + module->func_count = comp_data->func_count; + + /* Allocate memory for function pointers */ + size = (uint64)module->func_count * sizeof(void *); + if (!(module->func_ptrs = + loader_malloc(size, error_buf, error_buf_size))) { + goto fail2; + } + + /* Resolve function addresses */ + bh_assert(comp_ctx->exec_engine); + for (i = 0; i < comp_data->func_count; i++) { + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, i); + if (!(module->func_ptrs[i] = + (void *)LLVMGetFunctionAddress(comp_ctx->exec_engine, + func_name))) { + set_error_buf(error_buf, error_buf_size, + "get function address failed"); + goto fail3; + } + } + + /* Allocation memory for function type indexes */ + size = (uint64)module->func_count * sizeof(uint32); + if (!(module->func_type_indexes = + loader_malloc(size, error_buf, error_buf_size))) { + goto fail3; + } + for (i = 0; i < comp_data->func_count; i++) + module->func_type_indexes[i] = comp_data->funcs[i]->func_type_index; + + module->export_count = comp_data->wasm_module->export_count; + module->exports = comp_data->wasm_module->exports; + + module->start_func_index = comp_data->start_func_index; + if (comp_data->start_func_index != (uint32)-1) { + bh_assert(comp_data->start_func_index < module->import_func_count + + module->func_count); + /* TODO: fix issue that start func cannot be import func */ + if (comp_data->start_func_index >= module->import_func_count) { + module->start_function = + module->func_ptrs[comp_data->start_func_index + - module->import_func_count]; + } + } + + module->malloc_func_index = comp_data->malloc_func_index; + module->free_func_index = comp_data->free_func_index; + + module->aux_data_end_global_index = comp_data->aux_data_end_global_index; + module->aux_data_end = comp_data->aux_data_end; + module->aux_heap_base_global_index = comp_data->aux_heap_base_global_index; + module->aux_heap_base = comp_data->aux_heap_base; + module->aux_stack_top_global_index = comp_data->aux_stack_top_global_index; + module->aux_stack_bottom = comp_data->aux_stack_bottom; + module->aux_stack_size = comp_data->aux_stack_size; + + module->code = NULL; + module->code_size = 0; + + module->is_jit_mode = true; + + module->wasm_module = comp_data->wasm_module; + module->comp_ctx = comp_ctx; + module->comp_data = comp_data; + +#if WASM_ENABLE_LIBC_WASI != 0 + module->is_wasi_module = comp_data->wasm_module->is_wasi_module; +#endif + + return module; + +fail3: + wasm_runtime_free(module->func_ptrs); +fail2: + if (module->memory_count > 0) + wasm_runtime_free(module->memories); +fail1: + wasm_runtime_free(module); + return NULL; +} + +AOTModule* +aot_convert_wasm_module(WASMModule *wasm_module, + char *error_buf, uint32 error_buf_size) +{ + AOTCompData *comp_data; + AOTCompContext *comp_ctx; + AOTModule *aot_module; + AOTCompOption option = { 0 }; + char *aot_last_error; + + comp_data = aot_create_comp_data(wasm_module); + if (!comp_data) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + return NULL; + } + + option.is_jit_mode = true; +#if WASM_ENABLE_THREAD_MGR != 0 + option.enable_thread_mgr = true; +#endif + comp_ctx = aot_create_comp_context(comp_data, &option); + if (!comp_ctx) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + goto fail1; + } + + if (!aot_compile_wasm(comp_ctx)) { + aot_last_error = aot_get_last_error(); + bh_assert(aot_last_error != NULL); + set_error_buf(error_buf, error_buf_size, aot_last_error); + goto fail2; + } + + aot_module = aot_load_from_comp_data(comp_data, comp_ctx, + error_buf, error_buf_size); + if (!aot_module) { + goto fail2; + } + + return aot_module; + +fail2: + aot_destroy_comp_context(comp_ctx); +fail1: + aot_destroy_comp_data(comp_data); + return NULL; +} +#endif + +void +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->import_memories) + destroy_import_memories(module->import_memories, + module->is_jit_mode); + + if (module->memories) + wasm_runtime_free(module->memories); + + 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->import_tables) + destroy_import_tables(module->import_tables, + module->is_jit_mode); + + if (module->tables) + destroy_tables(module->tables, 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->exports) + destroy_exports(module->exports, + module->is_jit_mode); + + if (module->func_type_indexes) + wasm_runtime_free(module->func_type_indexes); + + if (module->func_ptrs) + wasm_runtime_free(module->func_ptrs); + + if (module->const_str_set) + bh_hash_map_destroy(module->const_str_set); + + if (module->code) { + uint8 *mmap_addr = module->literal - sizeof(module->literal_size); + uint32 total_size = sizeof(module->literal_size) + module->literal_size + module->code_size; + os_munmap(mmap_addr, total_size); + } + + if (module->data_sections) + destroy_object_data_sections(module->data_sections, + module->data_section_count); + + wasm_runtime_free(module); +} + +uint32 +aot_get_plt_table_size() +{ + return get_plt_table_size(); +} + diff --git a/wamr/core/iwasm/aot/aot_reloc.h b/wamr/core/iwasm/aot/aot_reloc.h new file mode 100644 index 0000000..882780a --- /dev/null +++ b/wamr/core/iwasm/aot/aot_reloc.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_runtime.h" + +typedef struct { + const char *symbol_name; + void *symbol_addr; +} SymbolMap; + +#define REG_SYM(symbol) { #symbol, (void*)symbol } + +#if WASM_ENABLE_BULK_MEMORY != 0 +#define REG_BULK_MEMORY_SYM() \ + REG_SYM(aot_memory_init), \ + REG_SYM(aot_data_drop), +#else +#define REG_BULK_MEMORY_SYM() +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#define REG_ATOMIC_WAIT_SYM() \ + REG_SYM(wasm_runtime_atomic_wait), \ + REG_SYM(wasm_runtime_atomic_notify), +#else +#define REG_ATOMIC_WAIT_SYM() +#endif + +#if (defined(_WIN32) || defined(_WIN32_)) && defined(NDEBUG) +#define REG_COMMON_SYMBOLS \ + REG_SYM(aot_set_exception_with_id), \ + REG_SYM(aot_invoke_native), \ + REG_SYM(aot_call_indirect), \ + REG_SYM(wasm_runtime_enlarge_memory), \ + REG_SYM(wasm_runtime_set_exception), \ + REG_BULK_MEMORY_SYM() \ + REG_ATOMIC_WAIT_SYM() +#else /* else of (defined(_WIN32) || defined(_WIN32_)) && defined(NDEBUG) */ +#define REG_COMMON_SYMBOLS \ + REG_SYM(aot_set_exception_with_id), \ + REG_SYM(aot_invoke_native), \ + REG_SYM(aot_call_indirect), \ + REG_SYM(wasm_runtime_enlarge_memory), \ + REG_SYM(wasm_runtime_set_exception), \ + REG_SYM(fmin), \ + REG_SYM(fminf), \ + REG_SYM(fmax), \ + REG_SYM(fmaxf), \ + REG_SYM(ceil), \ + REG_SYM(ceilf), \ + REG_SYM(floor), \ + REG_SYM(floorf), \ + REG_SYM(trunc), \ + REG_SYM(truncf), \ + REG_SYM(rint), \ + REG_SYM(rintf), \ + REG_BULK_MEMORY_SYM() \ + REG_ATOMIC_WAIT_SYM() +#endif /* end of (defined(_WIN32) || defined(_WIN32_)) && defined(NDEBUG) */ + +#define CHECK_RELOC_OFFSET(data_size) do { \ + if (!check_reloc_offset(target_section_size, reloc_offset, data_size, \ + error_buf, error_buf_size)) \ + return false; \ + } while (0) + +SymbolMap * +get_target_symbol_map(uint32 *sym_num); + +uint32 +get_plt_table_size(); + +void +init_plt_table(uint8 *plt); + +void +get_current_target(char *target_buf, uint32 target_buf_size); + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size); + diff --git a/wamr/core/iwasm/aot/aot_runtime.c b/wamr/core/iwasm/aot/aot_runtime.c new file mode 100644 index 0000000..0b4fbfc --- /dev/null +++ b/wamr/core/iwasm/aot/aot_runtime.c @@ -0,0 +1,1934 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_runtime.h" +#include "bh_log.h" +#include "mem_alloc.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "AOT module instantiate failed: %s", string); + } +} + +static void * +runtime_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static bool +global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + InitializerExpression *init_expr; + uint8 *p = (uint8*)module_inst->global_data.ptr; + AOTImportGlobal *import_global = module->import_globals; + AOTGlobal *global = module->globals; + + /* Initialize import global data */ + for (i = 0; i < module->import_global_count; i++, import_global++) { + bh_assert(import_global->data_offset == + (uint32)(p - (uint8*)module_inst->global_data.ptr)); + memcpy(p, &import_global->global_data_linked, import_global->size); + p += import_global->size; + } + + /* Initialize defined global data */ + for (i = 0; i < module->global_count; i++, global++) { + bh_assert(global->data_offset == + (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: + if (init_expr->u.global_index >= module->import_global_count + i) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + memcpy(p, + &module->import_globals[init_expr->u.global_index].global_data_linked, + global->size); + break; + default: + /* TODO: check whether global type and init_expr type are matching */ + memcpy(p, &init_expr->u, global->size); + break; + } + p += global->size; + } + + bh_assert(module_inst->global_data_size == + (uint32)(p - (uint8*)module_inst->global_data.ptr)); + return true; +} + +static bool +table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i, global_index, global_data_offset, base_offset, length; + AOTTableInitData *table_seg; + + for (i = 0; i < module->table_init_data_count; i++) { + table_seg = module->table_init_data_list[i]; + bh_assert(table_seg->offset.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + || table_seg->offset.init_expr_type == + INIT_EXPR_TYPE_GET_GLOBAL); + + /* Resolve table data base offset */ + if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = table_seg->offset.u.global_index; + bh_assert(global_index < + module->import_global_count + module->global_count); + /* TODO: && globals[table_seg->offset.u.global_index].type == + VALUE_TYPE_I32*/ + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; + else + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; + + base_offset = *(uint32*) + ((uint8*)module_inst->global_data.ptr + global_data_offset); + } + else + base_offset = (uint32)table_seg->offset.u.i32; + + /* Copy table data */ + bh_assert(module_inst->table_data.ptr); + /* base_offset only since length might negative */ + if (base_offset > module_inst->table_size) { + LOG_DEBUG("base_offset(%d) > table_size(%d)", base_offset, + module_inst->table_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + return false; + } + + /* base_offset + length(could be zero) */ + length = table_seg->func_index_count; + if (base_offset + length > module_inst->table_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > table_size(%d)", + base_offset, length, module_inst->table_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + return false; + } + + /** + * Check function index in the current module inst for now. + * will check the linked table inst owner in future + */ + memcpy((uint32 *)module_inst->table_data.ptr + base_offset, + table_seg->func_indexes, + length * sizeof(uint32)); + } + + return true; +} + +static void +memories_deinstantiate(AOTModuleInstance *module_inst) +{ + uint32 i; + AOTMemoryInstance *memory_inst; + + for (i = 0; i < module_inst->memory_count; i++) { + memory_inst = ((AOTMemoryInstance **)module_inst->memories.ptr)[i]; + if (memory_inst) { +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (memory_inst->is_shared) { + int32 ref_count = + shared_memory_dec_reference( + (WASMModuleCommon *)module_inst->aot_module.ptr); + bh_assert(ref_count >= 0); + + /* if the reference count is not zero, + don't free the memory */ + if (ref_count > 0) + continue; + } +#endif + if (memory_inst->heap_handle.ptr) + mem_allocator_destroy(memory_inst->heap_handle.ptr); + + if (memory_inst->heap_data.ptr) { +#ifndef OS_ENABLE_HW_BOUND_CHECK + wasm_runtime_free(memory_inst->memory_data.ptr); +#else + os_munmap((uint8*)memory_inst->memory_data.ptr, + 8 * (uint64)BH_GB); +#endif + } + } + } + wasm_runtime_free(module_inst->memories.ptr); +} + +static AOTMemoryInstance* +memory_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + AOTMemoryInstance *memory_inst, AOTMemory *memory, + uint32 heap_size, char *error_buf, uint32 error_buf_size) +{ + void *heap_handle; + uint32 num_bytes_per_page = memory->num_bytes_per_page; + uint32 init_page_count = memory->mem_init_page_count; + uint32 max_page_count = memory->mem_max_page_count; + uint32 inc_page_count, aux_heap_base, global_idx; + uint32 bytes_of_last_page, bytes_to_page_end; + uint32 heap_offset = num_bytes_per_page *init_page_count; + uint64 total_size; + uint8 *p, *global_addr; +#ifdef OS_ENABLE_HW_BOUND_CHECK + uint8 *mapped_mem; + uint64 map_size = 8 * (uint64)BH_GB; + uint64 page_size = os_getpagesize(); +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = memory->memory_flags & 0x02 ? true : false; + + /* Shared memory */ + if (is_shared_memory) { + AOTMemoryInstance *shared_memory_instance; + WASMSharedMemNode *node = + wasm_module_get_shared_memory((WASMModuleCommon *)module); + /* If the memory of this module has been instantiated, + return the memory instance directly */ + if (node) { + uint32 ref_count; + ref_count = shared_memory_inc_reference( + (WASMModuleCommon *)module); + bh_assert(ref_count > 0); + shared_memory_instance = + (AOTMemoryInstance *)shared_memory_get_memory_inst(node); + bh_assert(shared_memory_instance); + + (void)ref_count; + return shared_memory_instance; + } + } +#endif + + if (heap_size > 0 + && module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1) { + /* Disable app heap, use malloc/free function exported + by wasm app to allocate/free memory instead */ + heap_size = 0; + } + + if (init_page_count == max_page_count && init_page_count == 1) { + /* If only one page and at most one page, we just append + the app heap to the end of linear memory, enlarge the + num_bytes_per_page, and don't change the page count*/ + heap_offset = num_bytes_per_page; + num_bytes_per_page += heap_size; + if (num_bytes_per_page < heap_size) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return NULL; + } + } + else if (heap_size > 0) { + if (module->aux_heap_base_global_index != (uint32)-1 + && module->aux_heap_base < num_bytes_per_page + * init_page_count) { + /* Insert app heap before __heap_base */ + aux_heap_base = module->aux_heap_base; + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + inc_page_count = (heap_size - bytes_to_page_end + + num_bytes_per_page - 1) / num_bytes_per_page; + heap_offset = aux_heap_base; + aux_heap_base += heap_size; + + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + if (bytes_to_page_end < 1 * BH_KB) { + aux_heap_base += 1 * BH_KB; + inc_page_count++; + } + + /* Adjust __heap_base global value */ + global_idx = module->aux_heap_base_global_index + - module->import_global_count; + global_addr = (uint8*)module_inst->global_data.ptr + + module->globals[global_idx].data_offset; + *(uint32 *)global_addr = aux_heap_base; + LOG_VERBOSE("Reset __heap_base global to %u", aux_heap_base); + } + else { + /* Insert app heap before new page */ + inc_page_count = (heap_size + num_bytes_per_page - 1) + / num_bytes_per_page; + heap_offset = num_bytes_per_page * init_page_count; + heap_size = num_bytes_per_page * inc_page_count; + if (heap_size > 0) + heap_size -= 1 * BH_KB; + } + init_page_count += inc_page_count; + max_page_count += inc_page_count; + if (init_page_count > 65536) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return NULL; + } + if (max_page_count > 65536) + max_page_count = 65536; + } + + LOG_VERBOSE("Memory instantiate:"); + LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", + num_bytes_per_page, init_page_count, max_page_count); + LOG_VERBOSE(" heap offset: %u, heap size: %d\n", heap_offset, heap_size); + + total_size = (uint64)num_bytes_per_page * init_page_count; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + /* Allocate max page for shared memory */ + total_size = (uint64)num_bytes_per_page * max_page_count; + } +#endif + +#ifndef OS_ENABLE_HW_BOUND_CHECK + /* Allocate memory */ + if (!(p = runtime_malloc(total_size, error_buf, error_buf_size))) { + return NULL; + } +#else + total_size = (total_size + page_size - 1) & ~(page_size - 1); + + /* Totally 8G is mapped, the opcode load/store address range is 0 to 8G: + * ea = i + memarg.offset + * both i and memarg.offset are u32 in range 0 to 4G + * so the range of ea is 0 to 8G + */ + if (total_size >= UINT32_MAX + || !(p = mapped_mem = os_mmap(NULL, map_size, + MMAP_PROT_NONE, MMAP_MAP_NONE))) { + set_error_buf(error_buf, error_buf_size, "mmap memory failed"); + return NULL; + } + + if (os_mprotect(p, total_size, MMAP_PROT_READ | MMAP_PROT_WRITE) != 0) { + set_error_buf(error_buf, error_buf_size, "mprotec memory failed"); + os_munmap(mapped_mem, map_size); + return NULL; + } + memset(p, 0, (uint32)total_size); +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + + memory_inst->module_type = Wasm_Module_AoT; + memory_inst->num_bytes_per_page = num_bytes_per_page; + memory_inst->cur_page_count = init_page_count; + memory_inst->max_page_count = max_page_count; + + /* Init memory info */ + memory_inst->memory_data.ptr = p; + memory_inst->memory_data_end.ptr = p + (uint32)total_size; + memory_inst->memory_data_size = (uint32)total_size; + + /* Initialize heap info */ + memory_inst->heap_data.ptr = p + heap_offset; + memory_inst->heap_data_end.ptr = p + heap_offset + heap_size; + if (heap_size > 0) { + if (!(heap_handle = mem_allocator_create(memory_inst->heap_data.ptr, + heap_size))) { + set_error_buf(error_buf, error_buf_size, + "init app heap failed"); + goto fail1; + } + memory_inst->heap_handle.ptr = heap_handle; + } + + if (total_size > 0) { + if (sizeof(uintptr_t) == sizeof(uint64)) { + memory_inst->mem_bound_check_1byte.u64 = total_size - 1; + memory_inst->mem_bound_check_2bytes.u64 = total_size - 2; + memory_inst->mem_bound_check_4bytes.u64 = total_size - 4; + memory_inst->mem_bound_check_8bytes.u64 = total_size - 8; + } + else { + memory_inst->mem_bound_check_1byte.u32[0] = (uint32)total_size - 1; + memory_inst->mem_bound_check_2bytes.u32[0] = (uint32)total_size - 2; + memory_inst->mem_bound_check_4bytes.u32[0] = (uint32)total_size - 4; + memory_inst->mem_bound_check_8bytes.u32[0] = (uint32)total_size - 8; + } + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + memory_inst->is_shared = true; + if (!shared_memory_set_memory_inst((WASMModuleCommon *)module, + (WASMMemoryInstanceCommon *)memory_inst)) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail2; + } + } +#endif + + return memory_inst; + +#if WASM_ENABLE_SHARED_MEMORY != 0 +fail2: + if (heap_size > 0) { + mem_allocator_destroy(memory_inst->heap_handle.ptr); + memory_inst->heap_handle.ptr = NULL; + } +#endif +fail1: +#ifndef OS_ENABLE_HW_BOUND_CHECK + wasm_runtime_free(memory_inst->memory_data.ptr); +#else + os_munmap(mapped_mem, map_size); +#endif + memory_inst->memory_data.ptr = NULL; + return NULL; +} + +static AOTMemoryInstance* +aot_get_default_memory(AOTModuleInstance *module_inst) +{ + if (module_inst->memories.ptr) + return ((AOTMemoryInstance **)module_inst->memories.ptr)[0]; + else + return NULL; +} + +static bool +memories_instantiate(AOTModuleInstance *module_inst, AOTModule *module, + uint32 heap_size, char *error_buf, uint32 error_buf_size) +{ + uint32 global_index, global_data_offset, base_offset, length; + uint32 i, memory_count = module->memory_count; + AOTMemoryInstance *memories, *memory_inst; + AOTMemInitData *data_seg; + uint64 total_size; + + module_inst->memory_count = memory_count; + total_size = sizeof(AOTPointer) * (uint64)memory_count; + if (!(module_inst->memories.ptr = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + memories = module_inst->global_table_data.memory_instances; + for (i = 0; i < memory_count; i++, memories++) { + memory_inst = + memory_instantiate(module_inst, module, + memories, &module->memories[i], + heap_size, error_buf, error_buf_size); + if (!memory_inst) { + return false; + } + + ((AOTMemoryInstance **)module_inst->memories.ptr)[i] = memory_inst; + } + + /* Get default memory instance */ + memory_inst = aot_get_default_memory(module_inst); + + for (i = 0; i < module->mem_init_data_count; i++) { + data_seg = module->mem_init_data_list[i]; +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif + + bh_assert(data_seg->offset.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + || data_seg->offset.init_expr_type == + INIT_EXPR_TYPE_GET_GLOBAL); + + /* Resolve memory data base offset */ + if (data_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + global_index = data_seg->offset.u.global_index; + bh_assert(global_index < + module->import_global_count + module->global_count); + /* TODO: && globals[data_seg->offset.u.global_index].type == + VALUE_TYPE_I32*/ + if (global_index < module->import_global_count) + global_data_offset = + module->import_globals[global_index].data_offset; + else + global_data_offset = + module->globals[global_index - module->import_global_count] + .data_offset; + + base_offset = *(uint32*) + ((uint8*)module_inst->global_data.ptr + global_data_offset); + } else { + base_offset = (uint32)data_seg->offset.u.i32; + } + + /* Copy memory data */ + bh_assert(memory_inst->memory_data.ptr); + + /* Check memory data */ + /* check offset since length might negative */ + if (base_offset > memory_inst->memory_data_size) { + LOG_DEBUG("base_offset(%d) > memory_data_size(%d)", base_offset, + memory_inst->memory_data_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + return false; + } + + /* check offset + length(could be zero) */ + length = data_seg->byte_count; + if (base_offset + length > memory_inst->memory_data_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%d)", + base_offset, length, memory_inst->memory_data_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + return false; + } + + bh_memcpy_s((uint8*)memory_inst->memory_data.ptr + base_offset, + memory_inst->memory_data_size - base_offset, + data_seg->bytes, length); + } + + return true; +} + +static bool +init_func_ptrs(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + void **func_ptrs; + uint64 total_size = + ((uint64)module->import_func_count + module->func_count) * sizeof(void*); + + /* Allocate memory */ + if (!(module_inst->func_ptrs.ptr = runtime_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set import function pointers */ + func_ptrs = (void**)module_inst->func_ptrs.ptr; + for (i = 0; i < module->import_func_count; i++, func_ptrs++) + *func_ptrs = (void*)module->import_funcs[i].func_ptr_linked; + + /* Set defined function pointers */ + memcpy(func_ptrs, module->func_ptrs, module->func_count * sizeof(void*)); + return true; +} + +static bool +init_func_type_indexes(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + uint32 i; + uint32 *func_type_index; + uint64 total_size = + ((uint64)module->import_func_count + module->func_count) * sizeof(uint32); + + /* Allocate memory */ + if (!(module_inst->func_type_indexes.ptr = + runtime_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set import function type indexes */ + func_type_index = (uint32*)module_inst->func_type_indexes.ptr; + for (i = 0; i < module->import_func_count; i++, func_type_index++) + *func_type_index = module->import_funcs[i].func_type_index; + + memcpy(func_type_index, module->func_type_indexes, + module->func_count * sizeof(uint32)); + + return true; +} + +static bool +create_export_funcs(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + AOTExport *exports = module->exports; + AOTFunctionInstance *export_func; + uint64 size; + uint32 i, func_index, ftype_index; + + for (i = 0; i < module->export_count; i++) { + if (exports[i].kind == EXPORT_KIND_FUNC) + module_inst->export_func_count++; + } + + if (module_inst->export_func_count > 0) { + /* Allocate memory */ + size = sizeof(AOTFunctionInstance) + * (uint64)module_inst->export_func_count; + if (!(module_inst->export_funcs.ptr = export_func = + runtime_malloc(size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->export_count; i++) { + if (exports[i].kind == EXPORT_KIND_FUNC) { + export_func->func_name = exports[i].name; + export_func->func_index = exports[i].index; + if (export_func->func_index < module->import_func_count) { + export_func->is_import_func = true; + export_func->u.func_import = + &module->import_funcs[export_func->func_index]; + } + else { + export_func->is_import_func = false; + func_index = export_func->func_index + - module->import_func_count; + ftype_index = module->func_type_indexes[func_index]; + export_func->u.func.func_type = + module->func_types[ftype_index]; + export_func->u.func.func_ptr = + module->func_ptrs[func_index]; + } + export_func++; + } + } + } + + return true; +} + +static bool +create_exports(AOTModuleInstance *module_inst, AOTModule *module, + char *error_buf, uint32 error_buf_size) +{ + return create_export_funcs(module_inst, module, + error_buf, error_buf_size); +} + +static bool +execute_post_inst_function(AOTModuleInstance *module_inst) +{ + AOTFunctionInstance *post_inst_func = + aot_lookup_function(module_inst, "__post_instantiate", "()"); + + if (!post_inst_func) + /* Not found */ + return true; + + return aot_create_exec_env_and_call_function(module_inst, post_inst_func, 0, NULL); +} + +static bool +execute_start_function(AOTModuleInstance *module_inst) +{ + AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; + WASMExecEnv *exec_env; + typedef void (*F)(WASMExecEnv*); + union { F f; void *v; } u; + + if (!module->start_function) + return true; + + if (!(exec_env = wasm_exec_env_create((WASMModuleInstanceCommon*)module_inst, + module_inst->default_wasm_stack_size))) { + aot_set_exception(module_inst, "allocate memory failed"); + return false; + } + + u.v = module->start_function; + u.f(exec_env); + + wasm_exec_env_destroy(exec_env); + return !aot_get_exception(module_inst); +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +execute_memory_init_function(AOTModuleInstance *module_inst) +{ + AOTFunctionInstance *memory_init_func = + aot_lookup_function(module_inst, "__wasm_call_ctors", "()"); + + if (!memory_init_func) + /* Not found */ + return true; + + return aot_create_exec_env_and_call_function(module_inst, memory_init_func, + 0, NULL); +} +#endif + +AOTModuleInstance* +aot_instantiate(AOTModule *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size) +{ + AOTModuleInstance *module_inst; + uint32 module_inst_struct_size = + offsetof(AOTModuleInstance, global_table_data.bytes); + uint64 module_inst_mem_inst_size = + (uint64)module->memory_count * sizeof(AOTMemoryInstance); + uint32 table_size = module->table_count > 0 ? + module->tables[0].table_init_size : 0; + uint64 table_data_size = (uint64)table_size * sizeof(uint32); + uint64 total_size = (uint64)module_inst_struct_size + + module_inst_mem_inst_size + + module->global_data_size + + table_data_size; + uint8 *p; + + /* Check heap size */ + heap_size = align_uint(heap_size, 8); + if (heap_size > APP_HEAP_SIZE_MAX) + heap_size = APP_HEAP_SIZE_MAX; + + /* Allocate module instance, global data, table data and heap data */ + if (!(module_inst = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + module_inst->module_type = Wasm_Module_AoT; + module_inst->aot_module.ptr = module; + + /* Initialize global info */ + p = (uint8*)module_inst + module_inst_struct_size + + module_inst_mem_inst_size; + module_inst->global_data.ptr = p; + module_inst->global_data_size = module->global_data_size; + if (!global_instantiate(module_inst, module, error_buf, error_buf_size)) + goto fail; + + /* Initialize table info */ + p += module->global_data_size; + module_inst->table_data.ptr = p; + module_inst->table_size = table_size; + /* Set all elements to -1 to mark them as uninitialized elements */ + memset(module_inst->table_data.ptr, -1, (uint32)table_data_size); + if (!table_instantiate(module_inst, module, error_buf, error_buf_size)) + goto fail; + + /* Initialize memory space */ + if (!memories_instantiate(module_inst, module, heap_size, + error_buf, error_buf_size)) + goto fail; + + /* Initialize function pointers */ + if (!init_func_ptrs(module_inst, module, error_buf, error_buf_size)) + goto fail; + + /* Initialize function type indexes */ + if (!init_func_type_indexes(module_inst, module, error_buf, error_buf_size)) + goto fail; + + if (!create_exports(module_inst, module, error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_LIBC_WASI != 0 + if (!is_sub_inst) { + if (heap_size > 0 + && !wasm_runtime_init_wasi((WASMModuleInstanceCommon*)module_inst, + module->wasi_args.dir_list, + module->wasi_args.dir_count, + module->wasi_args.map_dir_list, + module->wasi_args.map_dir_count, + module->wasi_args.env, + module->wasi_args.env_count, + module->wasi_args.argv, + module->wasi_args.argc, + error_buf, error_buf_size)) + goto fail; + } +#endif + + /* Initialize the thread related data */ + if (stack_size == 0) + stack_size = DEFAULT_WASM_STACK_SIZE; +#if WASM_ENABLE_SPEC_TEST != 0 + if (stack_size < 48 *1024) + stack_size = 48 * 1024; +#endif + module_inst->default_wasm_stack_size = stack_size; + + /* Execute __post_instantiate function and start function*/ + if (!execute_post_inst_function(module_inst) + || !execute_start_function(module_inst)) { + set_error_buf(error_buf, error_buf_size, + module_inst->cur_exception); + goto fail; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 +#if WASM_ENABLE_LIBC_WASI != 0 + if (!module->is_wasi_module) { +#endif + /* Only execute the memory init function for main instance because + the data segments will be dropped once initialized. + */ + if (!is_sub_inst) { + if (!execute_memory_init_function(module_inst)) { + set_error_buf(error_buf, error_buf_size, + module_inst->cur_exception); + goto fail; + } + } +#if WASM_ENABLE_LIBC_WASI != 0 + } +#endif +#endif + + return module_inst; + +fail: + aot_deinstantiate(module_inst, is_sub_inst); + return NULL; +} + +void +aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst) +{ +#if WASM_ENABLE_LIBC_WASI != 0 + /* Destroy wasi resource before freeing app heap, since some fields of + wasi contex are allocated from app heap, and if app heap is freed, + these fields will be set to NULL, we cannot free their internal data + which may allocated from global heap. */ + /* Only destroy wasi ctx in the main module instance */ + if (!is_sub_inst) + wasm_runtime_destroy_wasi((WASMModuleInstanceCommon*)module_inst); +#endif + + if (module_inst->memories.ptr) + memories_deinstantiate(module_inst); + + if (module_inst->export_funcs.ptr) + wasm_runtime_free(module_inst->export_funcs.ptr); + + if (module_inst->func_ptrs.ptr) + wasm_runtime_free(module_inst->func_ptrs.ptr); + + if (module_inst->func_type_indexes.ptr) + wasm_runtime_free(module_inst->func_type_indexes.ptr); + + wasm_runtime_free(module_inst); +} + +AOTFunctionInstance* +aot_lookup_function(const AOTModuleInstance *module_inst, + const char *name, const char *signature) +{ + uint32 i; + AOTFunctionInstance *export_funcs = (AOTFunctionInstance *) + module_inst->export_funcs.ptr; + + for (i = 0; i < module_inst->export_func_count; i++) + if (!strcmp(export_funcs[i].func_name, name)) + return &export_funcs[i]; + (void)signature; + return NULL; +} + +#define PUT_I64_TO_ADDR(addr, value) do { \ + union { int64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + +#define PUT_F64_TO_ADDR(addr, value) do { \ + union { float64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + + +#ifdef OS_ENABLE_HW_BOUND_CHECK + +#define STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT 3 + +static os_thread_local_attribute WASMExecEnv *aot_exec_env = NULL; + +static inline uint8 * +get_stack_min_addr(WASMExecEnv *exec_env, uint32 page_size) +{ + uintptr_t stack_bound = (uintptr_t)exec_env->native_stack_boundary; + return (uint8*)(stack_bound & ~(uintptr_t)(page_size -1 )); +} + +static void +aot_signal_handler(void *sig_addr) +{ + AOTModuleInstance *module_inst; + AOTMemoryInstance *memory_inst; + WASMJmpBuf *jmpbuf_node; + uint8 *mapped_mem_start_addr = NULL; + uint8 *mapped_mem_end_addr = NULL; + uint8 *stack_min_addr; + uint32 page_size; + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + + /* Check whether current thread is running aot function */ + if (aot_exec_env + && aot_exec_env->handle == os_self_thread() + && (jmpbuf_node = aot_exec_env->jmpbuf_stack_top)) { + /* Get mapped mem info of current instance */ + module_inst = (AOTModuleInstance *)aot_exec_env->module_inst; + /* Get the default memory instance */ + memory_inst = aot_get_default_memory(module_inst); + if (memory_inst) { + mapped_mem_start_addr = (uint8*)memory_inst->memory_data.ptr; + mapped_mem_end_addr = (uint8*)memory_inst->memory_data.ptr + + 8 * (uint64)BH_GB; + } + + /* Get stack info of current thread */ + page_size = os_getpagesize(); + stack_min_addr = get_stack_min_addr(aot_exec_env, page_size); + + if (memory_inst + && (mapped_mem_start_addr <= (uint8*)sig_addr + && (uint8*)sig_addr < mapped_mem_end_addr)) { + /* The address which causes segmentation fault is inside + aot instance's guard regions */ + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS); + os_longjmp(jmpbuf_node->jmpbuf, 1); + } + else if (stack_min_addr - page_size <= (uint8*)sig_addr + && (uint8*)sig_addr < stack_min_addr + + page_size * guard_page_count) { + /* The address which causes segmentation fault is inside + native thread's guard page */ + aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + os_longjmp(jmpbuf_node->jmpbuf, 1); + } + } +} + +bool +aot_signal_init() +{ + return os_signal_init(aot_signal_handler) == 0 ? true : false; +} + +void +aot_signal_destroy() +{ + os_signal_destroy(); +} + +#if defined(__GNUC__) +__attribute__((no_sanitize_address)) static uint32 +#else +static uint32 +#endif +touch_pages(uint8 *stack_min_addr, uint32 page_size) +{ + uint8 sum = 0; + while (1) { + volatile uint8 *touch_addr = + (volatile uint8*)os_alloca(page_size / 2); + if (touch_addr < stack_min_addr + page_size) { + sum += *(stack_min_addr + page_size - 1); + break; + } + sum += *touch_addr; + } + return sum; +} + +static bool +invoke_native_with_hw_bound_check(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + uint32 *argv, uint32 argc, uint32 *argv_ret) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance*)exec_env->module_inst; + WASMExecEnv **p_aot_exec_env = &aot_exec_env; + WASMJmpBuf *jmpbuf_node, *jmpbuf_node_pop; + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + uint8 *stack_min_addr = get_stack_min_addr(exec_env, page_size); + bool ret; + + /* Check native stack overflow firstly to ensure we have enough + native stack to run the following codes before actually calling + the aot function in invokeNative function. */ + if ((uint8*)&module_inst < exec_env->native_stack_boundary + + page_size * (guard_page_count + 1)) { + aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + return false; + } + + if (aot_exec_env + && (aot_exec_env != exec_env)) { + aot_set_exception(module_inst, "invalid exec env"); + return false; + } + + if (!exec_env->jmpbuf_stack_top) { + /* Touch each stack page to ensure that it has been mapped: the OS may + lazily grow the stack mapping as a guard page is hit. */ + touch_pages(stack_min_addr, page_size); + /* First time to call aot function, protect one page */ + if (os_mprotect(stack_min_addr, page_size * guard_page_count, + MMAP_PROT_NONE) != 0) { + aot_set_exception(module_inst, "set protected page failed"); + return false; + } + } + + if (!(jmpbuf_node = wasm_runtime_malloc(sizeof(WASMJmpBuf)))) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); + return false; + } + + wasm_exec_env_push_jmpbuf(exec_env, jmpbuf_node); + + aot_exec_env = exec_env; + if (os_setjmp(jmpbuf_node->jmpbuf) == 0) { + ret = wasm_runtime_invoke_native(exec_env, func_ptr, func_type, + signature, attachment, + argv, argc, argv_ret); + } + else { + /* Exception has been set in signal handler before calling longjmp */ + ret = false; + } + + jmpbuf_node_pop = wasm_exec_env_pop_jmpbuf(exec_env); + bh_assert(jmpbuf_node == jmpbuf_node_pop); + wasm_runtime_free(jmpbuf_node); + if (!exec_env->jmpbuf_stack_top) { + /* Unprotect the guard page when the nested call depth is zero */ + os_mprotect(stack_min_addr, page_size * guard_page_count, + MMAP_PROT_READ | MMAP_PROT_WRITE); + *p_aot_exec_env = NULL; + } + os_sigreturn(); + os_signal_unmask(); + (void)jmpbuf_node_pop; + return ret; +} + +#define invoke_native_internal invoke_native_with_hw_bound_check +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ +#define invoke_native_internal wasm_runtime_invoke_native +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + +bool +aot_call_function(WASMExecEnv *exec_env, + AOTFunctionInstance *function, + unsigned argc, uint32 argv[]) +{ + AOTModuleInstance *module_inst = (AOTModuleInstance*)exec_env->module_inst; + AOTFuncType *func_type = function->u.func.func_type; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret; + + if (ext_ret_count > 0) { + uint32 cell_num = 0, i; + uint8 *ext_ret_types = func_type->types + func_type->param_count + 1; + uint32 argv1_buf[32], *argv1 = argv1_buf, *ext_rets = NULL; + uint32 *argv_ret = argv; + uint32 ext_ret_cell = wasm_get_cell_num(ext_ret_types, ext_ret_count); + uint64 size; + + /* Allocate memory all arguments */ + size = sizeof(uint32) * (uint64)argc /* original arguments */ + + sizeof(void*) * (uint64)ext_ret_count /* extra result values' addr */ + + sizeof(uint32) * (uint64)ext_ret_cell; /* extra result values */ + if (size > sizeof(argv1_buf) + && !(argv1 = runtime_malloc(size, module_inst->cur_exception, + sizeof(module_inst->cur_exception)))) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); + return false; + } + + /* Copy original arguments */ + bh_memcpy_s(argv1, (uint32)size, argv, sizeof(uint32) * argc); + + /* Get the extra result value's address */ + ext_rets = argv1 + argc + sizeof(void*)/sizeof(uint32) * ext_ret_count; + + /* Append each extra result value's address to original arguments */ + for (i = 0; i < ext_ret_count; i++) { + *(uintptr_t*)(argv1 + argc + sizeof(void*) / sizeof(uint32) * i) = + (uintptr_t)(ext_rets + cell_num); + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + + ret = invoke_native_internal(exec_env, function->u.func.func_ptr, + func_type, NULL, NULL, argv1, argc, argv); + if (!ret || aot_get_exception(module_inst)) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return false; + } + + /* Get extra result values */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + argv_ret++; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + argv_ret += 2; + break; + default: + bh_assert(0); + break; + } + ext_rets = argv1 + argc + sizeof(void*)/sizeof(uint32) * ext_ret_count; + bh_memcpy_s(argv_ret, sizeof(uint32) * cell_num, + ext_rets, sizeof(uint32) * cell_num); + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + + return true; + } + else { + ret = invoke_native_internal(exec_env, function->u.func.func_ptr, + func_type, NULL, NULL, argv, argc, argv); + return ret && !aot_get_exception(module_inst) ? true : false; + } +} + +bool +aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, + AOTFunctionInstance *func, + unsigned argc, uint32 argv[]) +{ + WASMExecEnv *exec_env; + bool ret; + + if (!(exec_env = wasm_exec_env_create((WASMModuleInstanceCommon*)module_inst, + module_inst->default_wasm_stack_size))) { + aot_set_exception(module_inst, "allocate memory failed"); + return false; + } + + /* set thread handle and stack boundary */ + wasm_exec_env_set_thread_info(exec_env); + + ret = aot_call_function(exec_env, func, argc, argv); + wasm_exec_env_destroy(exec_env); + return ret; +} + +void +aot_set_exception(AOTModuleInstance *module_inst, + const char *exception) +{ + if (exception) + snprintf(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + "Exception: %s", exception); + else + module_inst->cur_exception[0] = '\0'; +} + +void +aot_set_exception_with_id(AOTModuleInstance *module_inst, + uint32 id) +{ + switch (id) { + case EXCE_UNREACHABLE: + aot_set_exception(module_inst, "unreachable"); + break; + case EXCE_OUT_OF_MEMORY: + aot_set_exception(module_inst, "allocate memory failed"); + break; + case EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS: + aot_set_exception(module_inst, "out of bounds memory access"); + break; + case EXCE_INTEGER_OVERFLOW: + aot_set_exception(module_inst, "integer overflow"); + break; + case EXCE_INTEGER_DIVIDE_BY_ZERO: + aot_set_exception(module_inst, "integer divide by zero"); + break; + case EXCE_INVALID_CONVERSION_TO_INTEGER: + aot_set_exception(module_inst, "invalid conversion to integer"); + break; + case EXCE_INVALID_FUNCTION_TYPE_INDEX: + aot_set_exception(module_inst, "indirect call type mismatch"); + break; + case EXCE_INVALID_FUNCTION_INDEX: + aot_set_exception(module_inst, "invalid function index"); + break; + case EXCE_UNDEFINED_ELEMENT: + aot_set_exception(module_inst, "undefined element"); + break; + case EXCE_UNINITIALIZED_ELEMENT: + aot_set_exception(module_inst, "uninitialized element"); + break; + case EXCE_CALL_UNLINKED_IMPORT_FUNC: + aot_set_exception(module_inst, "fail to call unlinked import function"); + break; + case EXCE_NATIVE_STACK_OVERFLOW: + aot_set_exception(module_inst, "native stack overflow"); + break; + case EXCE_UNALIGNED_ATOMIC: + aot_set_exception(module_inst, "unaligned atomic"); + break; + default: + break; + } +} + +const char* +aot_get_exception(AOTModuleInstance *module_inst) +{ + if (module_inst->cur_exception[0] == '\0') + return NULL; + else + return module_inst->cur_exception; +} + +void +aot_clear_exception(AOTModuleInstance *module_inst) +{ + module_inst->cur_exception[0] = '\0'; +} + +static bool +execute_malloc_function(AOTModuleInstance *module_inst, + AOTFunctionInstance *malloc_func, + uint32 size, uint32 *p_result) +{ + uint32 argv[2]; + bool ret; + + argv[0] = size; +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (aot_exec_env != NULL) { + bh_assert(aot_exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + ret = aot_call_function(aot_exec_env, malloc_func, 1, argv); + } + else +#endif + { + ret = aot_create_exec_env_and_call_function + (module_inst, malloc_func, 1, argv); + } + + if (ret) + *p_result = argv[0]; + return ret; +} + +static bool +execute_free_function(AOTModuleInstance *module_inst, + AOTFunctionInstance *free_func, + uint32 offset) +{ + uint32 argv[2]; + + argv[0] = offset; +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (aot_exec_env != NULL) { + bh_assert(aot_exec_env->module_inst + == (WASMModuleInstanceCommon *)module_inst); + return aot_call_function(aot_exec_env, free_func, 1, argv); + } + else +#endif + { + return aot_create_exec_env_and_call_function + (module_inst, free_func, 1, argv); + } +} + +uint32 +aot_module_malloc(AOTModuleInstance *module_inst, uint32 size, + void **p_native_addr) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *module = (AOTModule *)module_inst->aot_module.ptr; + uint8 *addr = NULL; + uint32 offset = 0; + + if (memory_inst->heap_handle.ptr) { + addr = mem_allocator_malloc(memory_inst->heap_handle.ptr, size); + } + else if (module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1) { + AOTFunctionInstance *malloc_func = + aot_lookup_function(module_inst, "malloc", "(i)i"); + + bh_assert(malloc_func); + if (!execute_malloc_function(module_inst, malloc_func, + size, &offset)) { + return 0; + } + addr = offset + ? (uint8*)memory_inst->memory_data.ptr + offset + : NULL; + } + + if (!addr) { + aot_set_exception(module_inst, "out of memory"); + return 0; + } + if (p_native_addr) + *p_native_addr = addr; + return (uint32)(addr - (uint8*)memory_inst->memory_data.ptr); +} + +void +aot_module_free(AOTModuleInstance *module_inst, uint32 ptr) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *module = (AOTModule *)module_inst->aot_module.ptr; + + if (ptr) { + uint8 *addr = (uint8 *)memory_inst->memory_data.ptr + ptr; + if (memory_inst->heap_handle.ptr + &&(uint8 *)memory_inst->heap_data.ptr < addr + && addr < (uint8 *)memory_inst->heap_data_end.ptr) { + mem_allocator_free(memory_inst->heap_handle.ptr, addr); + } + else if (module->malloc_func_index != (uint32)-1 + && module->free_func_index != (uint32)-1 + && (uint8 *)memory_inst->memory_data.ptr <= addr + && addr < (uint8 *)memory_inst->memory_data_end.ptr) { + AOTFunctionInstance *free_func = + aot_lookup_function(module_inst, "free", "(i)i"); + + bh_assert(free_func); + execute_free_function(module_inst, free_func, ptr); + } + } +} + +uint32 +aot_module_dup_data(AOTModuleInstance *module_inst, + const char *src, uint32 size) +{ + char *buffer; + uint32 buffer_offset = aot_module_malloc(module_inst, size, + (void**)&buffer); + + if (buffer_offset != 0) { + buffer = aot_addr_app_to_native(module_inst, buffer_offset); + bh_memcpy_s(buffer, size, src, size); + } + return buffer_offset; +} + +bool +aot_validate_app_addr(AOTModuleInstance *module_inst, + uint32 app_offset, uint32 size) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + + /* integer overflow check */ + if(app_offset + size < app_offset) { + goto fail; + } + if (app_offset + size <= memory_inst->memory_data_size) { + return true; + } +fail: + aot_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +bool +aot_validate_native_addr(AOTModuleInstance *module_inst, + void *native_ptr, uint32 size) +{ + uint8 *addr = (uint8 *)native_ptr; + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + + /* integer overflow check */ + if (addr + size < addr) { + goto fail; + } + + if ((uint8 *)memory_inst->memory_data.ptr <= addr + && addr + size <= (uint8 *)memory_inst->memory_data_end.ptr) + return true; +fail: + aot_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +void * +aot_addr_app_to_native(AOTModuleInstance *module_inst, uint32 app_offset) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + uint8 *addr = (uint8 *)memory_inst->memory_data.ptr + app_offset; + + if ((uint8 *)memory_inst->memory_data.ptr <= addr + && addr < (uint8 *)memory_inst->memory_data_end.ptr) + return addr; + return NULL; +} + +uint32 +aot_addr_native_to_app(AOTModuleInstance *module_inst, void *native_ptr) +{ + uint8 *addr = (uint8 *)native_ptr; + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + + if ((uint8 *)memory_inst->memory_data.ptr <= addr + && addr < (uint8 *)memory_inst->memory_data_end.ptr) + return (uint32)(addr - (uint8 *)memory_inst->memory_data.ptr); + return 0; +} + +bool +aot_get_app_addr_range(AOTModuleInstance *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + uint32 memory_data_size = memory_inst->memory_data_size; + + if (app_offset < memory_data_size) { + if (p_app_start_offset) + *p_app_start_offset = 0; + if (p_app_end_offset) + *p_app_end_offset = memory_data_size; + return true; + } + return false; +} + +bool +aot_get_native_addr_range(AOTModuleInstance *module_inst, + uint8 *native_ptr, + uint8 **p_native_start_addr, + uint8 **p_native_end_addr) +{ + uint8 *addr = (uint8 *)native_ptr; + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + + if ((uint8 *)memory_inst->memory_data.ptr <= addr + && addr < (uint8 *)memory_inst->memory_data_end.ptr) { + if (p_native_start_addr) + *p_native_start_addr = (uint8 *)memory_inst->memory_data.ptr; + if (p_native_end_addr) + *p_native_end_addr = (uint8 *)memory_inst->memory_data_end.ptr; + return true; + } + return false; +} + +#ifndef OS_ENABLE_HW_BOUND_CHECK +bool +aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + uint32 num_bytes_per_page = memory_inst->num_bytes_per_page; + uint32 cur_page_count = memory_inst->cur_page_count; + uint32 max_page_count = memory_inst->max_page_count; + uint32 total_page_count = cur_page_count + inc_page_count; + uint32 total_size_old = memory_inst->memory_data_size; + uint64 total_size = (uint64)num_bytes_per_page * total_page_count; + uint32 heap_size = (uint32)((uint8 *)memory_inst->heap_data_end.ptr + - (uint8 *)memory_inst->heap_data.ptr); + uint8 *memory_data_old = (uint8 *)memory_inst->memory_data.ptr; + uint8 *heap_data_old = (uint8 *)memory_inst->heap_data.ptr; + uint8 *memory_data, *heap_data; + void *heap_handle_old = memory_inst->heap_handle.ptr; + + if (inc_page_count <= 0) + /* No need to enlarge memory */ + return true; + + if (total_page_count < cur_page_count /* integer overflow */ + || total_page_count > max_page_count) { + return false; + } + + if (total_size >= UINT32_MAX) { + return false; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (memory_inst->is_shared) { + /* For shared memory, we have reserved the maximum spaces during + instantiate, only change the cur_page_count here */ + memory_inst->cur_page_count = total_page_count; + return true; + } +#endif + + if (heap_size > 0) { + /* Destroy heap's lock firstly, if its memory is re-allocated, + we cannot access its lock again. */ + mem_allocator_destroy_lock(memory_inst->heap_handle.ptr); + } + if (!(memory_data = wasm_runtime_realloc(memory_data_old, + (uint32)total_size))) { + if (!(memory_data = wasm_runtime_malloc((uint32)total_size))) { + if (heap_size > 0) { + /* Restore heap's lock if memory re-alloc failed */ + mem_allocator_reinit_lock(memory_inst->heap_handle.ptr); + } + return false; + } + bh_memcpy_s(memory_data, (uint32)total_size, + memory_data_old, total_size_old); + wasm_runtime_free(memory_data_old); + } + + memset(memory_data + total_size_old, + 0, (uint32)total_size - total_size_old); + + memory_inst->cur_page_count = total_page_count; + memory_inst->memory_data_size = (uint32)total_size; + memory_inst->memory_data.ptr = memory_data; + memory_inst->memory_data_end.ptr = memory_data + total_size; + + if (heap_size > 0) { + memory_inst->heap_handle.ptr = (uint8 *)heap_handle_old + + (memory_data - memory_data_old); + if (mem_allocator_migrate(memory_inst->heap_handle.ptr, + heap_handle_old) != 0) { + return false; + } + } + + heap_data = heap_data_old + (memory_data - memory_data_old); + memory_inst->heap_data.ptr = heap_data; + memory_inst->heap_data_end.ptr = heap_data + heap_size; + + if (sizeof(uintptr_t) == sizeof(uint64)) { + memory_inst->mem_bound_check_1byte.u64 = total_size - 1; + memory_inst->mem_bound_check_2bytes.u64 = total_size - 2; + memory_inst->mem_bound_check_4bytes.u64 = total_size - 4; + memory_inst->mem_bound_check_8bytes.u64 = total_size - 8; + } + else { + memory_inst->mem_bound_check_1byte.u32[0] = (uint32)total_size - 1; + memory_inst->mem_bound_check_2bytes.u32[0] = (uint32)total_size - 2; + memory_inst->mem_bound_check_4bytes.u32[0] = (uint32)total_size - 4; + memory_inst->mem_bound_check_8bytes.u32[0] = (uint32)total_size - 8; + } + return true; +} +#else /* else of OS_ENABLE_HW_BOUND_CHECK */ +bool +aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + uint32 num_bytes_per_page = memory_inst->num_bytes_per_page; + uint32 cur_page_count = memory_inst->cur_page_count; + uint32 max_page_count = memory_inst->max_page_count; + uint32 total_page_count = cur_page_count + inc_page_count; + uint64 total_size = (uint64)num_bytes_per_page * total_page_count; + + if (inc_page_count <= 0) + /* No need to enlarge memory */ + return true; + + if (total_page_count < cur_page_count /* integer overflow */ + || total_page_count > max_page_count) { + return false; + } + + if (os_mprotect(memory_inst->memory_data_end.ptr, + num_bytes_per_page * inc_page_count, + MMAP_PROT_READ | MMAP_PROT_WRITE) != 0) { + return false; + } + + memset(memory_inst->memory_data_end.ptr, 0, + num_bytes_per_page * inc_page_count); + + memory_inst->cur_page_count = total_page_count; + memory_inst->memory_data_size = (uint32)total_size; + memory_inst->memory_data_end.ptr = (uint8 *)memory_inst->memory_data.ptr + + (uint32)total_size; + + if (sizeof(uintptr_t) == sizeof(uint64)) { + memory_inst->mem_bound_check_1byte.u64 = total_size - 1; + memory_inst->mem_bound_check_2bytes.u64 = total_size - 2; + memory_inst->mem_bound_check_4bytes.u64 = total_size - 4; + memory_inst->mem_bound_check_8bytes.u64 = total_size - 8; + } + else { + memory_inst->mem_bound_check_1byte.u32[0] = (uint32)total_size - 1; + memory_inst->mem_bound_check_2bytes.u32[0] = (uint32)total_size - 2; + memory_inst->mem_bound_check_4bytes.u32[0] = (uint32)total_size - 4; + memory_inst->mem_bound_check_8bytes.u32[0] = (uint32)total_size - 8; + } + return true; +} +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + +bool +aot_is_wasm_type_equal(AOTModuleInstance *module_inst, + uint32 type1_idx, uint32 type2_idx) +{ + WASMType *type1, *type2; + AOTModule *module = (AOTModule*)module_inst->aot_module.ptr; + + if (type1_idx >= module->func_type_count + || type2_idx >= module->func_type_count) { + aot_set_exception(module_inst, "type index out of bounds"); + return false; + } + + if (type1_idx == type2_idx) + return true; + + type1 = module->func_types[type1_idx]; + type2 = module->func_types[type2_idx]; + + return wasm_type_equal(type1, type2); +} + +bool +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + uint32 argc, uint32 *argv) +{ + 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; + void *attachment; + char buf[128]; + +#ifdef OS_ENABLE_HW_BOUND_CHECK + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + /* Check native stack overflow firstly to ensure we have enough + native stack to run the following codes before actually calling + the aot function in invokeNative function. */ + if ((uint8*)&module_inst < exec_env->native_stack_boundary + + page_size * (guard_page_count + 1)) { + aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + return false; + } +#endif + + bh_assert(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 false; + } + + signature = import_func->signature; + attachment = import_func->attachment; + if (!import_func->call_conv_raw) { + return wasm_runtime_invoke_native(exec_env, func_ptr, + func_type, signature, attachment, + argv, argc, argv); + } + else { + return wasm_runtime_invoke_native_raw(exec_env, func_ptr, + func_type, signature, attachment, + argv, argc, argv); + } +} + +bool +aot_call_indirect(WASMExecEnv *exec_env, + bool check_func_type, uint32 func_type_idx, + uint32 table_elem_idx, + uint32 argc, uint32 *argv) +{ + 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 *table_data = (uint32*)module_inst->table_data.ptr; + AOTFuncType *func_type; + void **func_ptrs = (void**)module_inst->func_ptrs.ptr, *func_ptr; + uint32 table_size = module_inst->table_size; + uint32 func_idx, func_type_idx1; + uint32 ext_ret_count; + AOTImportFunc *import_func; + const char *signature = NULL; + void *attachment = NULL; + char buf[128]; + bool ret; + + /* this function is called from native code, so exec_env->handle and + exec_env->native_stack_boundary must have been set, we don't set + it again */ + + if ((uint8*)&module_inst < exec_env->native_stack_boundary) { + aot_set_exception_with_id(module_inst, EXCE_NATIVE_STACK_OVERFLOW); + return false; + } + + if (table_elem_idx >= table_size) { + aot_set_exception_with_id(module_inst, EXCE_UNDEFINED_ELEMENT); + return false; + } + + func_idx = table_data[table_elem_idx]; + if (func_idx == (uint32)-1) { + aot_set_exception_with_id(module_inst, EXCE_UNINITIALIZED_ELEMENT); + return false; + } + + func_type_idx1 = func_type_indexes[func_idx]; + if (check_func_type + && !aot_is_wasm_type_equal(module_inst, func_type_idx, + func_type_idx1)) { + aot_set_exception_with_id(module_inst, + EXCE_INVALID_FUNCTION_TYPE_INDEX); + return false; + } + func_type = aot_module->func_types[func_type_idx1]; + + if (!(func_ptr = func_ptrs[func_idx])) { + bh_assert(func_idx < aot_module->import_func_count); + import_func = aot_module->import_funcs + func_idx; + 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 false; + } + + if (func_idx < aot_module->import_func_count) { + /* Call native function */ + import_func = aot_module->import_funcs + func_idx; + signature = import_func->signature; + if (import_func->call_conv_raw) { + attachment = import_func->attachment; + return wasm_runtime_invoke_native_raw(exec_env, func_ptr, + func_type, signature, + attachment, + argv, argc, argv); + } + } + + ext_ret_count = func_type->result_count > 1 + ? func_type->result_count - 1 : 0; + if (ext_ret_count > 0) { + uint32 argv1_buf[32], *argv1 = argv1_buf; + uint32 *ext_rets = NULL, *argv_ret = argv; + uint32 cell_num = 0, i; + uint8 *ext_ret_types = func_type->types + func_type->param_count + 1; + uint32 ext_ret_cell = wasm_get_cell_num(ext_ret_types, ext_ret_count); + uint64 size; + + /* Allocate memory all arguments */ + size = sizeof(uint32) * (uint64)argc /* original arguments */ + + sizeof(void*) * (uint64)ext_ret_count /* extra result values' addr */ + + sizeof(uint32) * (uint64)ext_ret_cell; /* extra result values */ + if (size > sizeof(argv1_buf) + && !(argv1 = runtime_malloc(size, module_inst->cur_exception, + sizeof(module_inst->cur_exception)))) { + aot_set_exception_with_id(module_inst, EXCE_OUT_OF_MEMORY); + return false; + } + + /* Copy original arguments */ + bh_memcpy_s(argv1, (uint32)size, argv, sizeof(uint32) * argc); + + /* Get the extra result value's address */ + ext_rets = argv1 + argc + sizeof(void*)/sizeof(uint32) * ext_ret_count; + + /* Append each extra result value's address to original arguments */ + for (i = 0; i < ext_ret_count; i++) { + *(uintptr_t*)(argv1 + argc + sizeof(void*) / sizeof(uint32) * i) = + (uintptr_t)(ext_rets + cell_num); + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + + ret = invoke_native_internal(exec_env, func_ptr, + func_type, signature, attachment, + argv1, argc, argv); + if (!ret || aot_get_exception(module_inst)) { + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + return false; + } + + /* Get extra result values */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + argv_ret++; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + argv_ret += 2; + break; + default: + bh_assert(0); + break; + } + ext_rets = argv1 + argc + sizeof(void*)/sizeof(uint32) * ext_ret_count; + bh_memcpy_s(argv_ret, sizeof(uint32) * cell_num, + ext_rets, sizeof(uint32) * cell_num); + + if (argv1 != argv1_buf) + wasm_runtime_free(argv1); + + return true; + } + else { + return invoke_native_internal(exec_env, func_ptr, + func_type, signature, attachment, + argv, argc, argv); + } +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, uint32 dst) +{ + AOTMemoryInstance *memory_inst = aot_get_default_memory(module_inst); + AOTModule *aot_module; + uint8 *data = NULL; + uint8 *maddr; + uint64 seg_len = 0; + + aot_module = (AOTModule *)module_inst->aot_module.ptr; + if (aot_module->is_jit_mode) { +#if WASM_ENABLE_JIT != 0 + seg_len = aot_module->wasm_module->data_segments[seg_index]->data_length; + data = aot_module->wasm_module->data_segments[seg_index]->data; +#endif + } + else { + seg_len = aot_module->mem_init_data_list[seg_index]->byte_count; + data = aot_module->mem_init_data_list[seg_index]->bytes; + } + + if (!aot_validate_app_addr(module_inst, dst, len)) + return false; + + if ((uint64)offset + (uint64)len > seg_len) { + aot_set_exception(module_inst, "out of bounds memory access"); + return false; + } + + maddr = aot_addr_app_to_native(module_inst, dst); + + bh_memcpy_s(maddr, memory_inst->memory_data_size - dst, + data + offset, len); + return true; +} + +bool +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index) +{ + AOTModule *aot_module = (AOTModule *)(module_inst->aot_module.ptr); + + if (aot_module->is_jit_mode) { +#if WASM_ENABLE_JIT != 0 + aot_module->wasm_module->data_segments[seg_index]->data_length = 0; + /* Currently we can't free the dropped data segment + as they are stored in wasm bytecode */ +#endif + } + else { + aot_module->mem_init_data_list[seg_index]->byte_count = 0; + /* Currently we can't free the dropped data segment + as the mem_init_data_count is a continuous array */ + } + return true; +} +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +aot_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size) +{ + AOTModuleInstance *module_inst = + (AOTModuleInstance*)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->aot_module.ptr; + + uint32 stack_top_idx = module->aux_stack_top_global_index; + uint32 data_end = module->aux_data_end; + uint32 stack_bottom = module->aux_stack_bottom; + bool is_stack_before_data = stack_bottom < data_end ? true : false; + + /* Check the aux stack space, currently we don't allocate space in heap */ + if ((is_stack_before_data && (size > start_offset)) + || ((!is_stack_before_data) && (start_offset - data_end < size))) + return false; + + if (stack_top_idx != (uint32)-1) { + /* The aux stack top is a wasm global, + set the initial value for the global */ + uint32 global_offset = + module->globals[stack_top_idx].data_offset; + uint8 *global_addr = (uint8 *)module_inst->global_data.ptr + global_offset; + *(int32*)global_addr = start_offset; + + /* The aux stack boundary is a constant value, + set the value to exec_env */ + exec_env->aux_stack_boundary = start_offset - size; + return true; + } + + return false; +} + +bool +aot_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size) +{ + AOTModuleInstance *module_inst = + (AOTModuleInstance*)exec_env->module_inst; + AOTModule *module = (AOTModule *)module_inst->aot_module.ptr; + + /* The aux stack information is resolved in loader + and store in module */ + uint32 stack_bottom = module->aux_stack_bottom; + uint32 total_aux_stack_size = module->aux_stack_size; + + if (stack_bottom != 0 && total_aux_stack_size != 0) { + if (start_offset) + *start_offset = stack_bottom; + if (size) + *size = total_aux_stack_size; + return true; + } + return false; +} + +#endif diff --git a/wamr/core/iwasm/aot/aot_runtime.h b/wamr/core/iwasm/aot/aot_runtime.h new file mode 100644 index 0000000..1b49d13 --- /dev/null +++ b/wamr/core/iwasm/aot/aot_runtime.h @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_RUNTIME_H_ +#define _AOT_RUNTIME_H_ + +#include "bh_platform.h" +#include "../common/wasm_runtime_common.h" +#include "../interpreter/wasm_runtime.h" +#include "../compilation/aot.h" +#if WASM_ENABLE_JIT != 0 +#include "../compilation/aot_llvm.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum AOTExceptionID { + EXCE_UNREACHABLE = 0, + EXCE_OUT_OF_MEMORY, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + EXCE_INTEGER_OVERFLOW, + EXCE_INTEGER_DIVIDE_BY_ZERO, + EXCE_INVALID_CONVERSION_TO_INTEGER, + EXCE_INVALID_FUNCTION_TYPE_INDEX, + EXCE_INVALID_FUNCTION_INDEX, + EXCE_UNDEFINED_ELEMENT, + EXCE_UNINITIALIZED_ELEMENT, + EXCE_CALL_UNLINKED_IMPORT_FUNC, + EXCE_NATIVE_STACK_OVERFLOW, + EXCE_UNALIGNED_ATOMIC, + EXCE_NUM, +} AOTExceptionID; + +typedef enum AOTSectionType { + AOT_SECTION_TYPE_TARGET_INFO = 0, + AOT_SECTION_TYPE_INIT_DATA, + AOT_SECTION_TYPE_TEXT, + AOT_SECTION_TYPE_FUNCTION, + AOT_SECTION_TYPE_EXPORT, + AOT_SECTION_TYPE_RELOCATION, + AOT_SECTION_TYPE_SIGANATURE +} AOTSectionType; + +typedef struct AOTObjectDataSection { + char *name; + uint8 *data; + uint32 size; +} AOTObjectDataSection; + +/* Relocation info */ +typedef struct AOTRelocation { + uint64 relocation_offset; + uint64 relocation_addend; + uint32 relocation_type; + char *symbol_name; + /* index in the symbol offset field */ + uint32 symbol_index; +} AOTRelocation; + +/* Relocation Group */ +typedef struct AOTRelocationGroup { + char *section_name; + /* index in the symbol offset field */ + uint32 name_index; + uint32 relocation_count; + AOTRelocation *relocations; +} AOTRelocationGroup; + +/* AOT function instance */ +typedef struct AOTFunctionInstance { + char *func_name; + uint32 func_index; + bool is_import_func; + union { + struct { + AOTFuncType *func_type; + /* function pointer linked */ + void *func_ptr; + } func; + AOTImportFunc *func_import; + } u; +} AOTFunctionInstance; + +typedef struct AOTModule { + uint32 module_type; + + /* import memories */ + uint32 import_memory_count; + AOTImportMemory *import_memories; + + /* memory info */ + uint32 memory_count; + AOTMemory *memories; + + /* init data */ + uint32 mem_init_data_count; + AOTMemInitData **mem_init_data_list; + + /* import tables */ + uint32 import_table_count; + AOTImportTable *import_tables; + + /* tables */ + uint32 table_count; + AOTTable *tables; + + /* table init data info */ + uint32 table_init_data_count; + AOTTableInitData **table_init_data_list; + + /* function type info */ + uint32 func_type_count; + AOTFuncType **func_types; + + /* import global varaible info */ + uint32 import_global_count; + AOTImportGlobal *import_globals; + + /* global variable info */ + uint32 global_count; + AOTGlobal *globals; + + /* total global variable size */ + uint32 global_data_size; + + /* import function info */ + uint32 import_func_count; + AOTImportFunc *import_funcs; + + /* function info */ + uint32 func_count; + /* point to AOTed/JITed functions */ + void **func_ptrs; + /* function type indexes */ + uint32 *func_type_indexes; + + /* export info */ + uint32 export_count; + AOTExport *exports; + + /* start function index, -1 denotes no start function */ + uint32 start_func_index; + /* start function, point to AOTed/JITed function */ + void *start_function; + + uint32 malloc_func_index; + uint32 free_func_index; + + /* AOTed code, NULL for JIT mode */ + void *code; + uint32 code_size; + + /* literal for AOTed code, NULL for JIT mode */ + uint8 *literal; + uint32 literal_size; + + /* data sections in AOT object file, including .data, .rodata + * and .rodata.cstN. NULL for JIT mode. */ + AOTObjectDataSection *data_sections; + uint32 data_section_count; + + /* constant string set */ + HashMap *const_str_set; + + /* the index of auxiliary __data_end global, + -1 means unexported */ + uint32 aux_data_end_global_index; + /* auxiliary __data_end exported by wasm app */ + uint32 aux_data_end; + + /* the index of auxiliary __heap_base global, + -1 means unexported */ + uint32 aux_heap_base_global_index; + /* auxiliary __heap_base exported by wasm app */ + uint32 aux_heap_base; + + /* the index of auxiliary stack top global, + -1 means unexported */ + uint32 aux_stack_top_global_index; + /* auxiliary stack bottom resolved */ + uint32 aux_stack_bottom; + /* auxiliary stack size resolved */ + uint32 aux_stack_size; + + /* is jit mode or not */ + bool is_jit_mode; + +#if WASM_ENABLE_JIT != 0 + WASMModule *wasm_module; + AOTCompContext *comp_ctx; + AOTCompData *comp_data; +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + WASIArguments wasi_args; + bool is_wasi_module; +#endif +} AOTModule; + +typedef union { + uint64 _make_it_8_bytes_; + void *ptr; +} AOTPointer; + +typedef union { + uint64 u64; + uint32 u32[2]; +} MemBound; + +typedef struct AOTMemoryInstance { + uint32 module_type; + /* shared memory flag */ + bool is_shared; + + /* memory space info */ + uint32 num_bytes_per_page; + uint32 cur_page_count; + uint32 max_page_count; + uint32 memory_data_size; + AOTPointer memory_data; + AOTPointer memory_data_end; + + /* heap space info */ + AOTPointer heap_data; + AOTPointer heap_data_end; + AOTPointer heap_handle; + + /* boundary check constants for aot code */ + MemBound mem_bound_check_1byte; + MemBound mem_bound_check_2bytes; + MemBound mem_bound_check_4bytes; + MemBound mem_bound_check_8bytes; +} AOTMemoryInstance; + +typedef struct AOTModuleInstance { + uint32 module_type; + + /* memories */ + uint32 memory_count; + AOTPointer memories; + + /* global and table info */ + uint32 global_data_size; + uint32 table_size; + AOTPointer global_data; + AOTPointer table_data; + + /* funciton pointer array */ + AOTPointer func_ptrs; + /* function type indexes */ + AOTPointer func_type_indexes; + + /* export info */ + uint32 export_func_count; + uint32 export_global_count; + uint32 export_mem_count; + uint32 export_tab_count; + AOTPointer export_funcs; + AOTPointer export_globals; + AOTPointer export_memories; + AOTPointer export_tables; + + /* The exception buffer for current thread. */ + char cur_exception[128]; + /* The custom data that can be set/get by + * wasm_runtime_set_custom_data/wasm_runtime_get_custom_data */ + AOTPointer custom_data; + /* The AOT module */ + AOTPointer aot_module; + /* WASI context */ + AOTPointer wasi_ctx; + + /* others */ + uint32 temp_ret; + uint32 llvm_stack; + uint32 default_wasm_stack_size; + + /* reserved */ + uint32 reserved[11]; + + union { + uint64 _make_it_8_byte_aligned_; + AOTMemoryInstance memory_instances[1]; + uint8 bytes[1]; + } global_table_data; +} AOTModuleInstance; + +/* Target info, read from ELF header of object file */ +typedef struct AOTTargetInfo { + /* Binary type, elf32l/elf32b/elf64l/elf64b */ + uint16 bin_type; + /* ABI type */ + uint16 abi_type; + /* Object file type */ + uint16 e_type; + /* Architecture */ + uint16 e_machine; + /* Object file version */ + uint32 e_version; + /* Processor-specific flags */ + uint32 e_flags; + /* Reserved */ + uint32 reserved; + /* Arch name */ + char arch[16]; +} AOTTargetInfo; + +/** + * Load a AOT module from aot file buffer + * @param buf the byte buffer which contains the AOT file data + * @param size the size of the buffer + * @param error_buf output of the error info + * @param error_buf_size the size of the error string + * + * @return return AOT module loaded, NULL if failed + */ +AOTModule* +aot_load_from_aot_file(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size); + +/** + * Load a AOT module from a specified AOT section list. + * + * @param section_list the section list which contains each section data + * @param error_buf output of the error info + * @param error_buf_size the size of the error string + * + * @return return AOT module loaded, NULL if failed + */ +AOTModule* +aot_load_from_sections(AOTSection *section_list, + char *error_buf, uint32 error_buf_size); + +#if WASM_ENABLE_JIT != 0 +/** + * Convert WASM module to AOT module + * + * @param wasm_module the WASM module to convert + * @param error_buf output of the error info + * @param error_buf_size the size of the error string + * + * @return return AOT module loaded, NULL if failed + */ +AOTModule* +aot_convert_wasm_module(WASMModule *wasm_module, + char *error_buf, uint32 error_buf_size); +#endif + +/** + * Unload a AOT module. + * + * @param module the module to be unloaded + */ +void +aot_unload(AOTModule *module); + +/** + * Instantiate a AOT module. + * + * @param module the AOT module to instantiate + * @param is_sub_inst the flag of sub instance + * @param heap_size the default heap size of the module instance, a heap will + * be created besides the app memory space. Both wasm app and native + * function can allocate memory from the heap. If heap_size is 0, the + * default heap size will be used. + * @param error_buf buffer to output the error info if failed + * @param error_buf_size the size of the error buffer + * + * @return return the instantiated AOT module instance, NULL if failed + */ +AOTModuleInstance* +aot_instantiate(AOTModule *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size); + +/** + * Deinstantiate a AOT module instance, destroy the resources. + * + * @param module_inst the AOT module instance to destroy + * @param is_sub_inst the flag of sub instance + */ +void +aot_deinstantiate(AOTModuleInstance *module_inst, bool is_sub_inst); + +/** + * Lookup an exported function in the AOT module instance. + * + * @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" + * + * @return the function instance found + */ +AOTFunctionInstance* +aot_lookup_function(const AOTModuleInstance *module_inst, + const char *name, const char *signature); +/** + * Call the given AOT function of a AOT module instance with + * arguments. + * + * @param exec_env the execution environment + * @param function the function to be called + * @param argc the number of arguments + * @param argv the arguments. If the function method has return value, + * the first (or first two in case 64-bit return value) element of + * argv stores the return value of the called AOT function after this + * function returns. + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call aot_get_exception to get exception info. + */ +bool +aot_call_function(WASMExecEnv *exec_env, + AOTFunctionInstance *function, + unsigned argc, uint32 argv[]); + +bool +aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, + AOTFunctionInstance *function, + unsigned argc, uint32 argv[]); +/** + * Set AOT module instance exception with exception string + * + * @param module the AOT module instance + * + * @param exception current exception string + */ +void +aot_set_exception(AOTModuleInstance *module_inst, + const char *exception); + +void +aot_set_exception_with_id(AOTModuleInstance *module_inst, + uint32 id); + +/** + * Get exception info of the AOT module instance. + * + * @param module_inst the AOT module instance + * + * @return the exception string + */ +const char* +aot_get_exception(AOTModuleInstance *module_inst); + +/** + * Clear exception info of the AOT module instance. + * + * @param module_inst the AOT module instance + */ +void +aot_clear_exception(AOTModuleInstance *module_inst); + +uint32 +aot_module_malloc(AOTModuleInstance *module_inst, uint32 size, + void **p_native_addr); + +void +aot_module_free(AOTModuleInstance *module_inst, uint32 ptr); + +uint32 +aot_module_dup_data(AOTModuleInstance *module_inst, + const char *src, uint32 size); + +bool +aot_validate_app_addr(AOTModuleInstance *module_inst, + uint32 app_offset, uint32 size); + + +bool +aot_validate_native_addr(AOTModuleInstance *module_inst, + void *native_ptr, uint32 size); + +void * +aot_addr_app_to_native(AOTModuleInstance *module_inst, uint32 app_offset); + +uint32 +aot_addr_native_to_app(AOTModuleInstance *module_inst, void *native_ptr); + +bool +aot_get_app_addr_range(AOTModuleInstance *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset); + +bool +aot_get_native_addr_range(AOTModuleInstance *module_inst, + uint8 *native_ptr, + uint8 **p_native_start_addr, + uint8 **p_native_end_addr); + +bool +aot_enlarge_memory(AOTModuleInstance *module_inst, uint32 inc_page_count); + +/** + * Compare whether two wasm types are equal according to the indexs + * + * @param module_inst the AOT module instance + * @param type1_idx index of the first wasm type + * @param type2_idx index of the second wasm type + * + * @return true if equal, false otherwise + */ +bool +aot_is_wasm_type_equal(AOTModuleInstance *module_inst, + uint32 type1_idx, uint32 type2_idx); + +/** + * Invoke native function from aot code + */ +bool +aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, + uint32 argc, uint32 *argv); + +bool +aot_call_indirect(WASMExecEnv *exec_env, + bool check_func_type, uint32 func_type_idx, + uint32 table_elem_idx, + uint32 argc, uint32 *argv); + +uint32 +aot_get_plt_table_size(); + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_memory_init(AOTModuleInstance *module_inst, uint32 seg_index, + uint32 offset, uint32 len, uint32 dst); + +bool +aot_data_drop(AOTModuleInstance *module_inst, uint32 seg_index); +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +aot_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size); + +bool +aot_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size); +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +bool +aot_signal_init(); + +void +aot_signal_destroy(); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_RUNTIME_H_ */ + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_aarch64.c b/wamr/core/iwasm/aot/arch/aot_reloc_aarch64.c new file mode 100644 index 0000000..373ac2d --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_aarch64.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADD_ABS_LO12_NC 277 +#define R_AARCH64_CALL26 283 + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +#define BUILD_TARGET_AARCH64_DEFAULT "aarch64v8" +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char * s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "aarch64v8" by default if sub version isn't specified */ + if (strcmp(s, "AARACH64") == 0) { + s = BUILD_TARGET_AARCH64_DEFAULT; + s_size = sizeof(BUILD_TARGET_AARCH64_DEFAULT); + } + if(target_buf_size < s_size){ + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++ ; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} +#undef BUILD_TARGET_AARCH64_DEFAULT + +static uint32 +get_plt_item_size() +{ + /* 8*4 bytes instructions and 8 bytes symbol address */ + return 40; +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint32 *p = (uint32*)plt; + *p++ = 0xd10023ff; /* sub sp, sp, #0x8 */ + *p++ = 0xf90003fe; /* str x30, [sp] */ + *p++ = 0x100000de; /* adr x30, #24 */ + *p++ = 0xf94003de; /* ldr x30, [x30] */ + *p++ = 0xd63f03c0; /* blr x30 */ + *p++ = 0xf94003fe; /* ldr x30, [sp] */ + *p++ = 0x910023ff; /* add sp, sp, #0x8 */ + *p++ = 0xd61f03c0; /* br x30 */ + /* symbol addr */ + *(uint64*)p = (uint64)(uintptr_t)target_sym_map[i].symbol_addr; + p += 2; + plt += get_plt_item_size(); + } +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +#define SIGN_EXTEND_TO_INT64(val, bits, val_ext) do { \ + int64 m = ((int64)1 << (bits - 1)); \ + val_ext = ((int64)val ^ m) - m; \ +} while (0) + +#define Page(expr) ((expr) & ~0xFFF) + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_AARCH64_CALL26: + { + void *S, *P = (void*)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm26; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32*)P; + imm26 = insn & 0x3FFFFFF; + SIGN_EXTEND_TO_INT64(imm26 << 2, 28, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-128MB relative + * to the relocation address. + */ + S = symbol_addr; + } + else { + uint8 *plt; + if (reloc_addend > 0) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-128MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + S = plt = (uint8*)module->code + module->code_size + - get_plt_table_size() + + get_plt_item_size() * symbol_index; + } + + /* S + A - P */ + X = (int64)S + A - (int64)P; + + /* Check overflow: +-128MB */ + if (X > (128 * BH_MB) || X < (-128 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + /* write the imm26 back to instruction */ + *(int32*)P = (insn & 0xFC000000) | ((int32)((X >> 2) & 0x3FFFFFF)); + break; + } + + case R_AARCH64_ADR_PREL_PG_HI21: + { + void *S = symbol_addr, *P = (void*)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, immhi19, immlo2, imm21; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32*)P; + immhi19 = (insn >> 5) & 0x7FFFF; + immlo2 = (insn >> 29) & 0x3; + imm21 = (immhi19 << 2) | immlo2; + + SIGN_EXTEND_TO_INT64(imm21 << 12, 33, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* Page(S+A) - Page(P) */ + X = Page((int64)S + A) - Page((int64)P); + + /* Check overflow: +-4GB */ + if (X > ((int64)4 * BH_GB) || X < ((int64)-4 * BH_GB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + /* write the imm21 back to instruction */ + immhi19 = (int32)(((X >> 12) >> 2) & 0x7FFFF); + immlo2 = (int32)((X >> 12) & 0x3); + *(int32*)P = (insn & 0x9F00001F) | (immlo2 << 29) | (immhi19 << 5); + + break; + } + + case R_AARCH64_ADD_ABS_LO12_NC: + { + void *S = symbol_addr, *P = (void*)(target_section_addr + reloc_offset); + int64 X, A, initial_addend; + int32 insn, imm12; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + insn = *(int32*)P; + imm12 = (insn >> 10) & 0xFFF; + + SIGN_EXTEND_TO_INT64(imm12, 12, initial_addend); + A = initial_addend; + A += (int64)reloc_addend; + + /* S + A */ + X = (int64)S + A; + + /* No need to check overflow for this reloction type */ + + /* write the imm12 back to instruction */ + *(int32*)P = (insn & 0xFFC003FF) | ((int32)((X & 0xFFF) << 10)); + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_arm.c b/wamr/core/iwasm/aot/arch/aot_reloc_arm.c new file mode 100644 index 0000000..e3e2d1a --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_arm.c @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JMP24 29 /* PC relative 24 bit (B/BL). */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ + +void __divdi3(); +void __udivdi3(); +void __moddi3(); +void __umoddi3(); +void __divsi3(); +void __udivsi3(); +void __modsi3(); +void __udivmoddi4(); +void __clzsi2(); +void __fixsfdi(); +void __fixunssfdi(); +void __fixdfdi(); +void __fixunsdfdi(); +void __floatdisf(); +void __floatundisf(); +void __floatdidf(); +void __floatundidf(); +void __aeabi_l2f(); +void __aeabi_f2lz(); +void __aeabi_ul2f(); +void __aeabi_d2lz(); +void __aeabi_l2d(); +void __aeabi_f2ulz(); +void __aeabi_ul2d(); +void __aeabi_d2ulz(); +void __aeabi_idiv(); +void __aeabi_uidiv(); +void __aeabi_idivmod(); +void __aeabi_uidivmod(); +void __aeabi_ldivmod(); +void __aeabi_uldivmod(); +void __aeabi_i2d(); +void __aeabi_dadd(); +void __aeabi_ddiv(); +void __aeabi_dcmplt(); +void __aeabi_dcmpun(); +void __aeabi_dcmple(); +void __aeabi_dcmpge(); +void __aeabi_d2iz(); +void __aeabi_fcmplt(); +void __aeabi_fcmpun(); +void __aeabi_fcmple(); +void __aeabi_fcmpge(); +void __aeabi_f2iz(); +void __aeabi_f2d(); + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + /* compiler-rt symbols that come from compiler(e.g. gcc) */ + REG_SYM(__divdi3), + REG_SYM(__udivdi3), + REG_SYM(__umoddi3), + REG_SYM(__divsi3), + REG_SYM(__udivsi3), + REG_SYM(__modsi3), + REG_SYM(__udivmoddi4), + REG_SYM(__clzsi2), + REG_SYM(__fixsfdi), + REG_SYM(__fixunssfdi), + REG_SYM(__fixdfdi), + REG_SYM(__fixunsdfdi), + REG_SYM(__floatdisf), + REG_SYM(__floatundisf), + REG_SYM(__floatdidf), + REG_SYM(__floatundidf), + REG_SYM(__aeabi_l2f), + REG_SYM(__aeabi_f2lz), + REG_SYM(__aeabi_ul2f), + REG_SYM(__aeabi_d2lz), + REG_SYM(__aeabi_l2d), + REG_SYM(__aeabi_f2ulz), + REG_SYM(__aeabi_ul2d), + REG_SYM(__aeabi_d2ulz), + REG_SYM(__aeabi_idiv), + REG_SYM(__aeabi_uidiv), + REG_SYM(__aeabi_idivmod), + REG_SYM(__aeabi_uidivmod), + REG_SYM(__aeabi_ldivmod), + REG_SYM(__aeabi_uldivmod), + REG_SYM(__aeabi_i2d), + REG_SYM(__aeabi_dadd), + REG_SYM(__aeabi_ddiv), + REG_SYM(__aeabi_dcmplt), + REG_SYM(__aeabi_dcmpun), + REG_SYM(__aeabi_dcmple), + REG_SYM(__aeabi_dcmpge), + REG_SYM(__aeabi_d2iz), + REG_SYM(__aeabi_fcmplt), + REG_SYM(__aeabi_fcmpun), + REG_SYM(__aeabi_fcmple), + REG_SYM(__aeabi_fcmpge), + REG_SYM(__aeabi_f2iz), + REG_SYM(__aeabi_f2d), +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +#define BUILD_TARGET_ARM_DEFAULT "armv4" +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char * s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "armv4" by default if sub version isn't specified */ + if (strcmp(s, "ARM") == 0) { + s = BUILD_TARGET_ARM_DEFAULT; + s_size = sizeof(BUILD_TARGET_ARM_DEFAULT); + } + if(target_buf_size < s_size){ + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++ ; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} +#undef BUILD_TARGET_ARM_DEFAULT + +uint32 +get_plt_item_size() +{ + /* 8 bytes instructions and 4 bytes symbol address */ + return 12; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint32 *p = (uint32*)plt; + /* ldr pc, [pc] */ + *p++ = 0xe59ff000; + /* nop */ + *p++ = 0xe1a00000; + /* symbol addr */ + *p++ = (uint32)(uintptr_t)target_sym_map[i].symbol_addr; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_ARM_CALL: + case R_ARM_JMP24: + { + intptr_t result; + int32 RESULT_MASK = 0x03FFFFFE; + int32 insn = *(int32*)(target_section_addr + reloc_offset); + /* Initial addend: sign_extend(insn[23:0] << 2) */ + int32 initial_addend = ((insn & 0xFFFFFF) << 2) + | ((insn & 0x800000) ? 0xFC000000 : 0); + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-32MB relative + * to the relocation address. + */ + /* operation: ((S + A) | T) - P where S is symbol address and T is 0 */ + result = (intptr_t) + ((uint8*)symbol_addr + reloc_addend + - (target_section_addr + reloc_offset)); + } + else { + if (reloc_addend > 0) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-32MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + /* operation: ((S + A) | T) - P where S is PLT address and T is 0 */ + uint8 *plt = (uint8*)module->code + module->code_size - get_plt_table_size() + + get_plt_item_size() * symbol_index; + result = (intptr_t) + (plt + reloc_addend + - (target_section_addr + reloc_offset)); + } + + result += initial_addend; + + /* Check overflow: +-32MB */ + if (result > (32 * BH_MB) || result < (-32 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + *(int32*)(target_section_addr + reloc_offset) = + (int32) + ((insn & 0xff000000) + | (((int32)result & RESULT_MASK) >> 2)); + break; + } + case R_ARM_ABS32: + { + intptr_t initial_addend; + /* (S + A) | T where T is 0 */ + CHECK_RELOC_OFFSET(sizeof(void*)); + initial_addend = *(intptr_t*)(target_section_addr + (uint32)reloc_offset); + *(uint8**)(target_section_addr + reloc_offset) + = (uint8*)symbol_addr + initial_addend + reloc_addend; + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_mips.c b/wamr/core/iwasm/aot/arch/aot_reloc_mips.c new file mode 100644 index 0000000..c1b3fa5 --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_mips.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "mips"); +} + +static uint32 +get_plt_item_size() +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + /* TODO: implement relocation for mips */ + case R_MIPS_26: + case R_MIPS_32: + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_thumb.c b/wamr/core/iwasm/aot/arch/aot_reloc_thumb.c new file mode 100644 index 0000000..f4023ff --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_thumb.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_ARM_THM_CALL 10 /* PC relative (Thumb BL and ARMv5 Thumb BLX). */ +#define R_ARM_THM_JMP24 30 /* B.W */ + +void __divdi3(); +void __udivdi3(); +void __moddi3(); +void __umoddi3(); +void __divsi3(); +void __udivsi3(); +void __modsi3(); +void __udivmoddi4(); +void __clzsi2(); +void __fixsfdi(); +void __fixunssfdi(); +void __fixdfdi(); +void __fixunsdfdi(); +void __floatdisf(); +void __floatundisf(); +void __floatdidf(); +void __floatundidf(); +void __aeabi_l2f(); +void __aeabi_f2lz(); +void __aeabi_ul2f(); +void __aeabi_d2lz(); +void __aeabi_l2d(); +void __aeabi_f2ulz(); +void __aeabi_ul2d(); +void __aeabi_d2ulz(); +void __aeabi_idiv(); +void __aeabi_uidiv(); +void __aeabi_idivmod(); +void __aeabi_uidivmod(); +void __aeabi_ldivmod(); +void __aeabi_uldivmod(); +void __aeabi_i2d(); +void __aeabi_dadd(); +void __aeabi_ddiv(); +void __aeabi_dcmplt(); +void __aeabi_dcmpun(); +void __aeabi_dcmple(); +void __aeabi_dcmpge(); +void __aeabi_d2iz(); +void __aeabi_fcmplt(); +void __aeabi_fcmpun(); +void __aeabi_fcmple(); +void __aeabi_fcmpge(); +void __aeabi_f2iz(); +void __aeabi_f2d(); + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + /* compiler-rt symbols that come from compiler(e.g. gcc) */ + REG_SYM(__divdi3), + REG_SYM(__udivdi3), + REG_SYM(__umoddi3), + REG_SYM(__divsi3), + REG_SYM(__udivsi3), + REG_SYM(__modsi3), + REG_SYM(__udivmoddi4), + REG_SYM(__clzsi2), + REG_SYM(__fixsfdi), + REG_SYM(__fixunssfdi), + REG_SYM(__fixdfdi), + REG_SYM(__fixunsdfdi), + REG_SYM(__floatdisf), + REG_SYM(__floatundisf), + REG_SYM(__floatdidf), + REG_SYM(__floatundidf), + REG_SYM(__aeabi_l2f), + REG_SYM(__aeabi_f2lz), + REG_SYM(__aeabi_ul2f), + REG_SYM(__aeabi_d2lz), + REG_SYM(__aeabi_l2d), + REG_SYM(__aeabi_f2ulz), + REG_SYM(__aeabi_ul2d), + REG_SYM(__aeabi_d2ulz), + REG_SYM(__aeabi_idiv), + REG_SYM(__aeabi_uidiv), + REG_SYM(__aeabi_idivmod), + REG_SYM(__aeabi_uidivmod), + REG_SYM(__aeabi_ldivmod), + REG_SYM(__aeabi_uldivmod), + REG_SYM(__aeabi_i2d), + REG_SYM(__aeabi_dadd), + REG_SYM(__aeabi_ddiv), + REG_SYM(__aeabi_dcmplt), + REG_SYM(__aeabi_dcmpun), + REG_SYM(__aeabi_dcmple), + REG_SYM(__aeabi_dcmpge), + REG_SYM(__aeabi_d2iz), + REG_SYM(__aeabi_fcmplt), + REG_SYM(__aeabi_fcmpun), + REG_SYM(__aeabi_fcmple), + REG_SYM(__aeabi_fcmpge), + REG_SYM(__aeabi_f2iz), + REG_SYM(__aeabi_f2d), +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +#define BUILD_TARGET_THUMB_V4T "thumbv4t" +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + const char * s = BUILD_TARGET; + size_t s_size = sizeof(BUILD_TARGET); + char *d = target_buf; + + /* Set to "thumbv4t" by default if sub version isn't specified */ + if (strcmp(s, "THUMB") == 0) { + s = BUILD_TARGET_THUMB_V4T; + s_size = sizeof(BUILD_TARGET_THUMB_V4T); + } + if(target_buf_size < s_size){ + s_size = target_buf_size; + } + while (--s_size) { + if (*s >= 'A' && *s <= 'Z') + *d++ = *s++ + 'a' - 'A'; + else + *d++ = *s++ ; + } + /* Ensure the string is null byte ('\0') terminated */ + *d = '\0'; +} +#undef BUILD_TARGET_THUMB_V4T + +uint32 +get_plt_item_size() +{ + /* 16 bytes instructions and 4 bytes symbol address */ + return 20; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint16 *p = (uint16*)plt; + /* nop */ + *p++ = 0xbf00; + /* push {r4} */ + *p++ = 0xb410; + /* add r4, pc, #8 */ + *p++ = 0xa402; + /* ldr r4, [r4, #0] */ + *p++ = 0x6824; + /* mov ip, r4 */ + *p++ = 0x46a4; + /* pop {r4} */ + *p++ = 0xbc10; + /* mov pc, ip */ + *p++ = 0x46e7; + /* nop */ + *p++ = 0xbf00; + /* symbol addr */ + *(uint32*)p = (uint32)(uintptr_t)target_sym_map[i].symbol_addr; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_ARM_THM_CALL: + case R_ARM_THM_JMP24: + { + int32 RESULT_MASK = 0x01FFFFFE; + int32 result, result_masked; + int16 *reloc_addr; + int32 initial_addend_0, initial_addend_1, initial_addend; + bool sign; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + reloc_addr = (int16*)(target_section_addr + reloc_offset); + initial_addend_0 = (*reloc_addr) & 0x7FF; + initial_addend_1 = (*(reloc_addr + 1)) & 0x7FF; + sign = (initial_addend_0 & 0x400) ? true : false; + initial_addend = (initial_addend_0 << 12) | (initial_addend_1 << 1) + | (sign ? 0xFF800000 : 0); + + if (symbol_index < 0) { + /* Symbol address itself is an AOT function. + * Apply relocation with the symbol directly. + * Suppose the symbol address is in +-4MB relative + * to the relocation address. + */ + /* operation: ((S + A) | T) - P where S is symbol address and T is 1 */ + result = (int32)(((intptr_t)((uint8*)symbol_addr + reloc_addend) | 1) + - (intptr_t)(target_section_addr + reloc_offset)); + } + else { + if (reloc_addend > 0) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: relocate to plt table " + "with reloc addend larger than 0 is unsupported."); + return false; + } + + /* Symbol address is not an AOT function, + * but a function of runtime or native. Its address is + * beyond of the +-4MB space. Apply relocation with + * the PLT which branch to the target symbol address. + */ + /* operation: ((S + A) | T) - P where S is PLT address and T is 1 */ + uint8 *plt = (uint8*)module->code + module->code_size - get_plt_table_size() + + get_plt_item_size() * symbol_index + 1; + result = (int32)(((intptr_t)plt | 1) + - (intptr_t)(target_section_addr + reloc_offset)); + } + + result += initial_addend; + + /* Check overflow: +-4MB */ + if (result > (4 * BH_MB) || result < (-4 * BH_MB)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + result_masked = (int32)result & RESULT_MASK; + initial_addend_0 = (result_masked >> 12) & 0x7FF; + initial_addend_1 = (result_masked >> 1) & 0x7FF; + + *reloc_addr = (*reloc_addr & ~0x7FF) | initial_addend_0; + *(reloc_addr + 1) = (*(reloc_addr + 1) & ~0x7FF) | initial_addend_1; + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_x86_32.c b/wamr/core/iwasm/aot/arch/aot_reloc_x86_32.c new file mode 100644 index 0000000..6cc470e --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_x86_32.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ + +void __divdi3(); +void __udivdi3(); +void __moddi3(); +void __umoddi3(); + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +#if !defined(_WIN32) && !defined(_WIN32_) + /* compiler-rt symbols that come from compiler(e.g. gcc) */ + REG_SYM(__divdi3), + REG_SYM(__udivdi3), + REG_SYM(__moddi3), + REG_SYM(__umoddi3) +#endif +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "i386"); +} + +uint32 +get_plt_table_size() +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_386_32: + { + intptr_t value; + + CHECK_RELOC_OFFSET(sizeof(void*)); + value = *(intptr_t*)(target_section_addr + (uint32)reloc_offset); + *(uint8**)(target_section_addr + reloc_offset) + = (uint8*)symbol_addr + reloc_addend + value; /* S + A */ + break; + } + + case R_386_PC32: + { + int32 value; + + CHECK_RELOC_OFFSET(sizeof(void*)); + value = *(int32*)(target_section_addr + (uint32)reloc_offset); + *(uint32*)(target_section_addr + (uint32)reloc_offset) = (uint32) + ((uint8*)symbol_addr + (uint32)reloc_addend + - (uint8*)(target_section_addr + (uint32)reloc_offset) + + value); /* S + A - P */ + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_x86_64.c b/wamr/core/iwasm/aot/arch/aot_reloc_x86_64.c new file mode 100644 index 0000000..a18b1c3 --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_x86_64.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ + +void __divdi3(); +void __udivdi3(); +void __moddi3(); +void __umoddi3(); + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "x86_64"); +} + +static uint32 +get_plt_item_size() +{ + /* size of mov instruction and jmp instruction */ + return 12; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +void +init_plt_table(uint8 *plt) +{ + uint32 i, num = sizeof(target_sym_map) / sizeof(SymbolMap); + for (i = 0; i < num; i++) { + uint8 *p = plt; + /* mov symbol_addr, rax */ + *p++ = 0x48; + *p++ = 0xB8; + *(uint64*)p = (uint64)(uintptr_t)target_sym_map[i].symbol_addr; + p += sizeof(uint64); + /* jmp rax */ + *p++ = 0xFF; + *p++ = 0xE0; + plt += get_plt_item_size(); + } +} + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_X86_64_64: + { + intptr_t value; + + CHECK_RELOC_OFFSET(sizeof(void*)); + value = *(intptr_t*)(target_section_addr + (uint32)reloc_offset); + *(uint8**)(target_section_addr + reloc_offset) + = (uint8*)symbol_addr + reloc_addend + value; /* S + A */ + break; + } + case R_X86_64_PC32: + { + intptr_t target_addr = (intptr_t) /* S + A - P */ + ((uint8*)symbol_addr + reloc_addend + - (target_section_addr + reloc_offset)); + + CHECK_RELOC_OFFSET(sizeof(int32)); + if ((int32)target_addr != target_addr) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "relocation truncated to fit R_X86_64_PC32 failed. " + "Try using wamrc with --size-level=1 option."); + return false; + } + + *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } + case R_X86_64_32: + case R_X86_64_32S: + { + char buf[128]; + uintptr_t target_addr = (uintptr_t) /* S + A */ + ((uint8*)symbol_addr + reloc_addend); + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if ((reloc_type == R_X86_64_32 + && (uint32)target_addr != (uint64)target_addr) + || (reloc_type == R_X86_64_32S + && (int32)target_addr != (int64)target_addr)) { + snprintf(buf, sizeof(buf), + "AOT module load failed: " + "relocation truncated to fit %s failed. " + "Try using wamrc with --size-level=1 option.", + reloc_type == R_X86_64_32 + ? "R_X86_64_32" : "R_X86_64_32S"); + set_error_buf(error_buf, error_buf_size, buf); + return false; + } + + *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } + case R_X86_64_PLT32: + { + uint8 *plt; + intptr_t target_addr = 0; + + CHECK_RELOC_OFFSET(sizeof(int32)); + + if (symbol_index >= 0) { + plt = (uint8*)module->code + module->code_size - get_plt_table_size() + + get_plt_item_size() * symbol_index; + target_addr = (intptr_t) /* L + A - P */ + (plt + reloc_addend + - (target_section_addr + reloc_offset)); + } + else { + target_addr = (intptr_t) /* L + A - P */ + ((uint8*)symbol_addr + reloc_addend + - (target_section_addr + reloc_offset)); + } + + if ((int32)target_addr != target_addr) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "relocation truncated to fit R_X86_64_PC32 failed. " + "Try using wamrc with --size-level=1 option."); + return false; + } + + *(int32*)(target_section_addr + reloc_offset) = (int32)target_addr; + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/aot/arch/aot_reloc_xtensa.c b/wamr/core/iwasm/aot/arch/aot_reloc_xtensa.c new file mode 100644 index 0000000..91499a1 --- /dev/null +++ b/wamr/core/iwasm/aot/arch/aot_reloc_xtensa.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_reloc.h" + +#define R_XTENSA_32 1 /* Direct 32 bit */ +#define R_XTENSA_SLOT0_OP 20 /* PC relative */ + +/* for soft-float */ +void __floatsidf(); +void __divdf3(); +void __ltdf2(); + +/* for mul32 */ +void __mulsi3(); +void __muldi3(); + +void __modsi3(); + +void __divdi3(); + +static SymbolMap target_sym_map[] = { + REG_COMMON_SYMBOLS + + /* API's for soft-float */ + /* TODO: only register these symbols when Floating-Point Coprocessor + * Option is not enabled */ + REG_SYM(__floatsidf), + REG_SYM(__divdf3), + REG_SYM(__ltdf2), + + /* API's for 32-bit integer multiply */ + /* TODO: only register these symbols when 32-bit Integer Multiply Option + * is not enabled */ + REG_SYM(__mulsi3), + REG_SYM(__muldi3), + + REG_SYM(__modsi3), + + REG_SYM(__divdi3), +}; + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +SymbolMap * +get_target_symbol_map(uint32 *sym_num) +{ + *sym_num = sizeof(target_sym_map) / sizeof(SymbolMap); + return target_sym_map; +} + +void +get_current_target(char *target_buf, uint32 target_buf_size) +{ + snprintf(target_buf, target_buf_size, "xtensa"); +} + +static uint32 +get_plt_item_size() +{ + return 0; +} + +void +init_plt_table(uint8 *plt) +{ + (void)plt; +} + +uint32 +get_plt_table_size() +{ + return get_plt_item_size() * (sizeof(target_sym_map) / sizeof(SymbolMap)); +} + +static bool +check_reloc_offset(uint32 target_section_size, + uint64 reloc_offset, uint32 reloc_data_size, + char *error_buf, uint32 error_buf_size) +{ + if (!(reloc_offset < (uint64)target_section_size + && reloc_offset + reloc_data_size <= (uint64)target_section_size)) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: invalid relocation offset."); + return false; + } + return true; +} + +/* + * CPU like esp32 can read and write data through the instruction bus, but only + * in a word aligned manner; non-word-aligned access will cause a CPU exception. + * This function uses a world aligned manner to write 16bit value to instruction + * addreess. + */ +static void +put_imm16_to_addr(int16 imm16, int16 *addr) +{ + int8 bytes[8]; + int32 *addr_aligned1, *addr_aligned2; + + addr_aligned1 = (int32*)((intptr_t)addr & ~3); + + if ((intptr_t)addr % 4 != 3) { + *(int32*)bytes = *addr_aligned1; + *(int16*)(bytes + ((intptr_t)addr % 4)) = imm16; + memcpy(addr_aligned1, bytes, 4); + } + else { + addr_aligned2 = (int32*)(((intptr_t)addr + 3) & ~3); + *(int32*)bytes = *addr_aligned1; + *(int32*)(bytes + 4) = *addr_aligned2; + *(int16*)(bytes + 3) = imm16; + memcpy(addr_aligned1, bytes, 8); + } +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +typedef union { + struct l32r_le { + int8 other; + int16 imm16; + } __packed l; + + struct l32r_be { + int16 imm16; + int8 other; + } __packed b; +} l32r_insn_t; + +bool +apply_relocation(AOTModule *module, + uint8 *target_section_addr, uint32 target_section_size, + uint64 reloc_offset, uint64 reloc_addend, + uint32 reloc_type, void *symbol_addr, int32 symbol_index, + char *error_buf, uint32 error_buf_size) +{ + switch (reloc_type) { + case R_XTENSA_32: + { + uint8 *insn_addr = target_section_addr + reloc_offset; + int32 initial_addend; + /* (S + A) */ + if ((intptr_t)insn_addr & 3) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "instruction address unaligned."); + return false; + } + CHECK_RELOC_OFFSET(4); + initial_addend = *(int32*)insn_addr; + *(uint8**)insn_addr + = (uint8*)symbol_addr + initial_addend + reloc_addend; + break; + } + + case R_XTENSA_SLOT0_OP: + { + uint8 *insn_addr = target_section_addr + reloc_offset; + /* Currently only l32r instruction generates R_XTENSA_SLOT0_OP relocation */ + l32r_insn_t *l32r_insn = (l32r_insn_t *)insn_addr; + uint8 *reloc_addr; + int32 relative_offset/*, initial_addend */; + int16 imm16; + + CHECK_RELOC_OFFSET(3); /* size of l32r instruction */ + + /* + imm16 = is_little_endian() ? + l32r_insn->l.imm16 : l32r_insn->b.imm16; + initial_addend = (int32)imm16 << 2; + */ + + reloc_addr = (uint8*)symbol_addr + reloc_addend; + + if ((intptr_t)reloc_addr & 3) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "relocation address unaligned."); + return false; + } + + relative_offset = (int32) + ((intptr_t)reloc_addr - + (((intptr_t)insn_addr + 3) & ~(intptr_t)3)); + /* relative_offset += initial_addend; */ + + /* check relative offset boundary */ + if (relative_offset < -256 * BH_KB || relative_offset > -4) { + set_error_buf(error_buf, error_buf_size, + "AOT module load failed: " + "target address out of range."); + return false; + } + + imm16 = (int16)(relative_offset >> 2); + + /* write back the imm16 to the l32r instruction */ + if (is_little_endian()) + put_imm16_to_addr(imm16, &l32r_insn->l.imm16); + else + put_imm16_to_addr(imm16, &l32r_insn->b.imm16); + + break; + } + + default: + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "Load relocation section failed: " + "invalid relocation type %d.", + reloc_type); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/aot/iwasm_aot.cmake b/wamr/core/iwasm/aot/iwasm_aot.cmake new file mode 100644 index 0000000..e7d0549 --- /dev/null +++ b/wamr/core/iwasm/aot/iwasm_aot.cmake @@ -0,0 +1,31 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (IWASM_AOT_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_AOT=1) + +include_directories (${IWASM_AOT_DIR}) + +file (GLOB c_source_all ${IWASM_AOT_DIR}/*.c) + +if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_x86_64.c) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_x86_32.c) +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_aarch64.c) +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_arm.c) +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_thumb.c) +elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_mips.c) +elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") + set (arch_source ${IWASM_AOT_DIR}/arch/aot_reloc_xtensa.c) +else () + message (FATAL_ERROR "Build target isn't set") +endif () + +set (IWASM_AOT_SOURCE ${c_source_all} ${arch_source}) + diff --git a/wamr/core/iwasm/common/arch/invokeNative_aarch64.s b/wamr/core/iwasm/common/arch/invokeNative_aarch64.s new file mode 100644 index 0000000..68390cb --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_aarch64.s @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * x0 function ptr + * x1 argv + * x2 nstacks + */ + +invokeNative: + sub sp, sp, #0x30 + stp x19, x20, [sp, #0x20] /* save the registers */ + stp x21, x22, [sp, #0x10] + stp x23, x24, [sp, #0x0] + + mov x19, x0 /* x19 = function ptr */ + mov x20, x1 /* x20 = argv */ + mov x21, x2 /* x21 = nstacks */ + mov x22, sp /* save the sp before call function */ + + /* Fill in float-point registers */ + ldp d0, d1, [x20], #16 /* d0 = argv[0], d1 = argv[1] */ + ldp d2, d3, [x20], #16 /* d2 = argv[2], d3 = argv[3] */ + ldp d4, d5, [x20], #16 /* d4 = argv[4], d5 = argv[5] */ + ldp d6, d7, [x20], #16 /* d6 = argv[6], d7 = argv[7] */ + + /* Fill inteter registers */ + ldp x0, x1, [x20], #16 /* x0 = argv[8] = exec_env, x1 = argv[9] */ + ldp x2, x3, [x20], #16 /* x2 = argv[10], x3 = argv[11] */ + ldp x4, x5, [x20], #16 /* x4 = argv[12], x5 = argv[13] */ + ldp x6, x7, [x20], #16 /* x6 = argv[14], x7 = argv[15] */ + + /* Now x20 points to stack args */ + + /* Directly call the fucntion if no args in stack */ + cmp x21, #0 + beq call_func + + /* Fill all stack args: reserve stack space and fill one by one */ + mov x23, sp + bic sp, x23, #15 /* Ensure stack is 16 bytes aligned */ + lsl x23, x21, #3 /* x23 = nstacks * 8 */ + add x23, x23, #15 /* x23 = (x23 + 15) & ~15 */ + bic x23, x23, #15 + sub sp, sp, x23 /* reserved stack space for stack arguments */ + mov x23, sp + +loop_stack_args: /* copy stack arguments to stack */ + cmp x21, #0 + beq call_func + ldr x24, [x20], #8 + str x24, [x23], #8 + sub x21, x21, #1 + b loop_stack_args + +call_func: + mov x20, x30 /* save x30(lr) */ + blr x19 + mov sp, x22 /* restore sp which is saved before calling fuction*/ + +return: + mov x30, x20 /* restore x30(lr) */ + ldp x19, x20, [sp, #0x20] /* restore the registers in stack */ + ldp x21, x22, [sp, #0x10] + ldp x23, x24, [sp, #0x0] + add sp, sp, #0x30 /* restore sp */ + ret + diff --git a/wamr/core/iwasm/common/arch/invokeNative_arm.s b/wamr/core/iwasm/common/arch/invokeNative_arm.s new file mode 100644 index 0000000..7231831 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_arm.s @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * r0 function ptr + * r1 argv + * r2 argc + */ + +invokeNative: + stmfd sp!, {r4, r5, r6, r7, lr} + sub sp, sp, #4 /* make sp 8 byte aligned */ + mov ip, r0 /* ip = function ptr */ + mov r4, r1 /* r4 = argv */ + mov r5, r2 /* r5 = argc */ + + cmp r5, #1 /* at least one argument required: exec_env */ + blt return + + mov r6, #0 /* increased stack size */ + + ldr r0, [r4], #4 /* r0 = argv[0] = exec_env */ + cmp r5, #1 + beq call_func + + ldr r1, [r4], #4 /* r1 = argv[1] */ + cmp r5, #2 + beq call_func + + ldr r2, [r4], #4 /* r2 = argv[2] */ + cmp r5, #3 + beq call_func + + ldr r3, [r4], #4 /* r3 = argv[3] */ + cmp r5, #4 + beq call_func + + sub r5, r5, #4 /* argc -= 4, now we have r0 ~ r3 */ + + /* Ensure address is 8 byte aligned */ + mov r6, r5, lsl#2 /* r6 = argc * 4 */ + add r6, r6, #7 /* r6 = (r6 + 7) & ~7 */ + bic r6, r6, #7 + sub sp, sp, r6 /* reserved stack space for left arguments */ + mov r7, sp + +loop_args: /* copy left arguments to stack */ + cmp r5, #0 + beq call_func + ldr lr, [r4], #4 + str lr, [r7], #4 + sub r5, r5, #1 + b loop_args + +call_func: + blx ip + add sp, sp, r6 /* restore sp */ + +return: + add sp, sp, #4 + ldmfd sp!, {r4, r5, r6, r7, lr} + bx lr diff --git a/wamr/core/iwasm/common/arch/invokeNative_arm_vfp.s b/wamr/core/iwasm/common/arch/invokeNative_arm_vfp.s new file mode 100644 index 0000000..679fded --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_arm_vfp.s @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * r0 function ptr + * r1 argv + * r2 nstacks + */ + +invokeNative: + stmfd sp!, {r4, r5, r6, r7, lr} + mov ip, r0 /* ip = function ptr */ + mov r4, r1 /* r4 = argv */ + mov r5, r2 /* r5 = nstacks */ + mov r6, sp + + /* Fill all int args */ + ldr r0, [r4], #4 /* r0 = *(int*)&argv[0] = exec_env */ + ldr r1, [r4], #4 /* r1 = *(int*)&argv[1] */ + ldr r2, [r4], #4 /* r2 = *(int*)&argv[2] */ + ldr r3, [r4], #4 /* r3 = *(int*)&argv[3] */ + + /* Fill all float/double args to 16 single-precision registers, s0-s15, */ + /* which may also be accessed as 8 double-precision registers, d0-d7 (with */ + /* d0 overlapping s0, s1; d1 overlapping s2, s3; etc). */ + vldr s0, [r4, #0] /* s0 = *(float*)&argv[4] */ + vldr s1, [r4, #4] + vldr s2, [r4, #8] + vldr s3, [r4, #12] + vldr s4, [r4, #16] + vldr s5, [r4, #20] + vldr s6, [r4, #24] + vldr s7, [r4, #28] + vldr s8, [r4, #32] + vldr s9, [r4, #36] + vldr s10, [r4, #40] + vldr s11, [r4, #44] + vldr s12, [r4, #48] + vldr s13, [r4, #52] + vldr s14, [r4, #56] + vldr s15, [r4, #60] + /* Directly call the fucntion if no args in stack */ + cmp r5, #0 + beq call_func + + + /* Fill all stack args: reserve stack space and fill one by one */ + add r4, r4, #64 /* r4 points to stack args */ + bic sp, sp, #7 /* Ensure stack is 8 byte aligned */ + mov r7, r5, lsl#2 /* r7 = nstacks * 4 */ + add r7, r7, #7 /* r7 = (r7 + 7) & ~7 */ + bic r7, r7, #7 + sub sp, sp, r7 /* reserved stack space for stack arguments */ + mov r7, sp + +loop_stack_args: /* copy stack arguments to stack */ + cmp r5, #0 + beq call_func + ldr lr, [r4], #4 /* Note: caller should insure int64 and */ + str lr, [r7], #4 /* double are placed in 8 bytes aligned address */ + sub r5, r5, #1 + b loop_stack_args + +call_func: + blx ip + mov sp, r6 /* restore sp */ + +return: + ldmfd sp!, {r4, r5, r6, r7, lr} + bx lr + diff --git a/wamr/core/iwasm/common/arch/invokeNative_em64.asm b/wamr/core/iwasm/common/arch/invokeNative_em64.asm new file mode 100644 index 0000000..8ce4fc0 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_em64.asm @@ -0,0 +1,62 @@ +; +; Copyright (C) 2019 Intel Corporation. All rights reserved. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; + +_TEXT SEGMENT + ; rcx func_ptr + ; rdx argv + ; r8 n_stacks + +invokeNative PROC + push rbp + mov rbp, rsp + + mov r10, rcx ; func_ptr + mov rax, rdx ; argv + mov rcx, r8 ; n_stacks + +; fill all fp args + movsd xmm0, qword ptr [rax + 0] + movsd xmm1, qword ptr [rax + 8] + movsd xmm2, qword ptr [rax + 16] + movsd xmm3, qword ptr [rax + 24] + +; check for stack args + cmp rcx, 0 + jz cycle_end + + mov rdx, rsp + and rdx, 15 + jz no_abort + int 3 +no_abort: + mov rdx, rcx + and rdx, 1 + shl rdx, 3 + sub rsp, rdx + +; store stack args + lea r9, qword ptr [rax + rcx * 8 + 64] + sub r9, rsp ; offset +cycle: + push qword ptr [rsp + r9] + loop cycle + +cycle_end: + mov rcx, [rax + 32] + mov rdx, [rax + 40] + mov r8, [rax + 48] + mov r9, [rax + 56] + + sub rsp, 32 ; shadow space + + call r10 + leave + ret + +invokeNative ENDP + +_TEXT ENDS + +END \ No newline at end of file diff --git a/wamr/core/iwasm/common/arch/invokeNative_em64.s b/wamr/core/iwasm/common/arch/invokeNative_em64.s new file mode 100644 index 0000000..739e84e --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_em64.s @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN +.globl invokeNative + .type invokeNative, @function +invokeNative: +#else +.globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + /* rdi - function ptr */ + /* rsi - argv */ + /* rdx - n_stacks */ + + push %rbp + mov %rsp, %rbp + + mov %rdx, %r10 + mov %rsp, %r11 /* Check that stack is aligned on */ + and $8, %r11 /* 16 bytes. This code may be removed */ + je check_stack_succ /* when we are sure that compiler always */ + int3 /* calls us with aligned stack */ +check_stack_succ: + mov %r10, %r11 /* Align stack on 16 bytes before pushing */ + and $1, %r11 /* stack arguments in case we have an odd */ + shl $3, %r11 /* number of stack arguments */ + sub %r11, %rsp + /* store memory args */ + movq %rdi, %r11 /* func ptr */ + movq %r10, %rcx /* counter */ + lea 64+48-8(%rsi,%rcx,8), %r10 + sub %rsp, %r10 + cmpq $0, %rcx + je push_args_end +push_args: + push 0(%rsp,%r10) + loop push_args +push_args_end: + /* fill all fp args */ + movq 0x00(%rsi), %xmm0 + movq 0x08(%rsi), %xmm1 + movq 0x10(%rsi), %xmm2 + movq 0x18(%rsi), %xmm3 + movq 0x20(%rsi), %xmm4 + movq 0x28(%rsi), %xmm5 + movq 0x30(%rsi), %xmm6 + movq 0x38(%rsi), %xmm7 + + /* fill all int args */ + movq 0x40(%rsi), %rdi + movq 0x50(%rsi), %rdx + movq 0x58(%rsi), %rcx + movq 0x60(%rsi), %r8 + movq 0x68(%rsi), %r9 + movq 0x48(%rsi), %rsi + + call *%r11 + leave + ret + diff --git a/wamr/core/iwasm/common/arch/invokeNative_general.c b/wamr/core/iwasm/common/arch/invokeNative_general.c new file mode 100644 index 0000000..8d6e70f --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_general.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "../wasm_runtime_common.h" +#include "../wasm_exec_env.h" + +void invokeNative(void (*native_code)(), uint32 argv[], uint32 argc) +{ + bh_assert(argc >= sizeof(WASMExecEnv*)/sizeof(uint32)); + + switch(argc) { + case 0: + native_code(); + break; + case 1: + native_code(argv[0]); + break; + case 2: + native_code(argv[0], argv[1]); + break; + case 3: + native_code(argv[0], argv[1], argv[2]); + break; + case 4: + native_code(argv[0], argv[1], argv[2], argv[3]); + break; + case 5: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4]); + break; + case 6: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); + break; + case 7: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); + break; + case 8: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + break; + case 9: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]); + break; + case 10: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]); + break; + case 11: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]); + break; + case 12: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]); + break; + case 13: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]); + break; + case 14: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]); + break; + case 15: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]); + break; + case 16: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15]); + break; + case 17: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16]); + break; + case 18: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17]); + break; + case 19: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18]); + break; + case 20: + native_code(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14], argv[15], argv[16], argv[17], argv[18], argv[19]); + break; + default: + { + /* FIXME: If this happen, add more cases. */ + WASMExecEnv *exec_env = *(WASMExecEnv**)argv; + WASMModuleInstanceCommon *module_inst = exec_env->module_inst; + wasm_runtime_set_exception(module_inst, "the argument number of native function exceeds maximum"); + return; + } + } +} diff --git a/wamr/core/iwasm/common/arch/invokeNative_ia32.asm b/wamr/core/iwasm/common/arch/invokeNative_ia32.asm new file mode 100644 index 0000000..c52c8d6 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_ia32.asm @@ -0,0 +1,27 @@ +; +; Copyright (C) 2019 Intel Corporation. All rights reserved. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +; + + .386 + .model flat + .code +_invokeNative PROC + push ebp + mov ebp,esp + mov ecx, [ebp+16] ; ecx = argc */ + mov edx, [ebp+12] ; edx = argv */ + test ecx, ecx + jz skip_push_args ; if ecx == 0, skip pushing arguments */ + lea edx, [edx+ecx*4-4] ; edx = edx + ecx * 4 - 4 */ + sub edx,esp ; edx = edx - esp */ +loop_push: + push [esp+edx] + loop loop_push ; loop ecx counts */ +skip_push_args: + mov edx, [ebp+8] ; edx = func_ptr */ + call edx + leave + ret +_invokeNative ENDP +END \ No newline at end of file diff --git a/wamr/core/iwasm/common/arch/invokeNative_ia32.s b/wamr/core/iwasm/common/arch/invokeNative_ia32.s new file mode 100644 index 0000000..0056a53 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_ia32.s @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + .text + .align 2 +#ifndef BH_PLATFORM_DARWIN +.globl invokeNative + .type invokeNative, @function +invokeNative: +#else +.globl _invokeNative +_invokeNative: +#endif /* end of BH_PLATFORM_DARWIN */ + push %ebp + movl %esp, %ebp + movl 16(%ebp), %ecx /* ecx = argc */ + movl 12(%ebp), %edx /* edx = argv */ + test %ecx, %ecx + jz skip_push_args /* if ecx == 0, skip pushing arguments */ + leal -4(%edx,%ecx,4), %edx /* edx = edx + ecx * 4 - 4 */ + subl %esp, %edx /* edx = edx - esp */ +1: + push 0(%esp,%edx) + loop 1b /* loop ecx counts */ +skip_push_args: + movl 8(%ebp), %edx /* edx = func_ptr */ + call *%edx + leave + ret + diff --git a/wamr/core/iwasm/common/arch/invokeNative_mips.s b/wamr/core/iwasm/common/arch/invokeNative_mips.s new file mode 100644 index 0000000..645f4f2 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_mips.s @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + .text + .align 2 + .globl invokeNative + .ent invokeNative + .type invokeNative, @function + +/** + * On function entry parameters: + * $4 = func_ptr + * $5 = args + * $6 = arg_num + */ + +invokeNative: + .frame $fp, 8, $0 + .mask 0x00000000, 0 + .fmask 0x00000000, 0 + + /* Fixed part of frame */ + subu $sp, 8 + + /* save registers */ + sw $31, 4($sp) + sw $fp, 0($sp) + + /* set frame pointer to bottom of fixed frame */ + move $fp, $sp + + /* allocate enough stack space */ + sll $11, $6, 2 /* $11 == arg_num * 4 */ + subu $sp, $11 + + /* make 8-byte aligned */ + and $sp, ~7 + + move $9, $sp + move $25, $4 /* $25 = func_ptr */ + +push_args: + beq $6, 0, done /* arg_num == 0 ? */ + lw $8, 0($5) /* $8 = *args */ + sw $8, 0($9) /* store $8 to stack */ + addu $5, 4 /* args++ */ + addu $9, 4 /* sp++ */ + subu $6, 1 /* arg_num-- */ + j push_args + +done: + lw $4, 0($sp) /* Load $4..$7 from stack */ + lw $5, 4($sp) + lw $6, 8($sp) + lw $7, 12($sp) + ldc1 $f12, 0($sp) /* Load $f12, $f13, $f14, $f15 */ + ldc1 $f14, 8($sp) + + jalr $25 /* call function */ + + nop + + /* restore saved registers */ + move $sp, $fp + lw $31, 4($sp) + lw $fp, 0($sp) + + /* pop frame */ + addu $sp, $sp, 8 + + j $31 + .end invokeNative diff --git a/wamr/core/iwasm/common/arch/invokeNative_thumb.s b/wamr/core/iwasm/common/arch/invokeNative_thumb.s new file mode 100644 index 0000000..571c6a2 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_thumb.s @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * r0 function ptr + * r1 argv + * r2 argc + */ + +invokeNative: + push {r4, r5, r6, r7} + push {lr} + mov ip, r0 /* ip = function ptr */ + mov r4, r1 /* r4 = argv */ + mov r5, r2 /* r5 = argc */ + + cmp r5, #1 /* at least one argument required: exec_env */ + blt return + + mov r6, #0 /* increased stack size */ + + ldr r0, [r4] /* r0 = argv[0] = exec_env */ + add r4, r4, #4 /* r4 += 4 */ + cmp r5, #1 + beq call_func + + ldr r1, [r4] /* r1 = argv[1] */ + add r4, r4, #4 + cmp r5, #2 + beq call_func + + ldr r2, [r4] /* r2 = argv[2] */ + add r4, r4, #4 + cmp r5, #3 + beq call_func + + ldr r3, [r4] /* r3 = argv[3] */ + add r4, r4, #4 + cmp r5, #4 + beq call_func + + sub r5, r5, #4 /* argc -= 4, now we have r0 ~ r3 */ + + /* Ensure address is 8 byte aligned */ + lsl r6, r5, #2 /* r6 = argc * 4 */ + mov r7, #7 + add r6, r6, r7 /* r6 = (r6 + 7) & ~7 */ + bic r6, r6, r7 + add r6, r6, #4 /* +4 because odd(5) registers are in stack */ + mov r7, sp + sub r7, r7, r6 /* reserved stack space for left arguments */ + mov sp, r7 + + mov lr, r2 /* save r2 */ +loop_args: /* copy left arguments to stack */ + cmp r5, #0 + beq call_func1 + ldr r2, [r4] + add r4, r4, #4 + str r2, [r7] + add r7, r7, #4 + sub r5, r5, #1 + b loop_args + +call_func1: + mov r2, lr /* restore r2 */ + +call_func: + blx ip + add sp, sp, r6 /* restore sp */ + +return: + pop {r3} + pop {r4, r5, r6, r7} + mov lr, r3 + bx lr diff --git a/wamr/core/iwasm/common/arch/invokeNative_thumb_vfp.s b/wamr/core/iwasm/common/arch/invokeNative_thumb_vfp.s new file mode 100644 index 0000000..faab213 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_thumb_vfp.s @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * r0 function ptr + * r1 argv + * r2 nstacks + */ + +invokeNative: + push {r4, r5, r6, r7} + push {lr} + mov ip, r0 /* ip = function ptr */ + mov r4, r1 /* r4 = argv */ + mov r5, r2 /* r5 = nstacks */ + mov r7, sp + + /* Fill all int args */ + ldr r0, [r4, #0] /* r0 = *(int*)&argv[0] = exec_env */ + ldr r1, [r4, #4] /* r1 = *(int*)&argv[1] */ + ldr r2, [r4, #8] /* r2 = *(int*)&argv[2] */ + ldr r3, [r4, #12] /* r3 = *(int*)&argv[3] */ + add r4, r4, #16 /* r4 points to float args */ + + /* Fill all float/double args to 16 single-precision registers, s0-s15, */ + /* which may also be accessed as 8 double-precision registers, d0-d7 (with */ + /* d0 overlapping s0, s1; d1 overlapping s2, s3; etc). */ + vldr s0, [r4, #0] /* s0 = *(float*)&argv[4] */ + vldr s1, [r4, #4] + vldr s2, [r4, #8] + vldr s3, [r4, #12] + vldr s4, [r4, #16] + vldr s5, [r4, #20] + vldr s6, [r4, #24] + vldr s7, [r4, #28] + vldr s8, [r4, #32] + vldr s9, [r4, #36] + vldr s10, [r4, #40] + vldr s11, [r4, #44] + vldr s12, [r4, #48] + vldr s13, [r4, #52] + vldr s14, [r4, #56] + vldr s15, [r4, #60] + /* Directly call the fucntion if no args in stack */ + cmp r5, #0 + beq call_func + + mov lr, r2 /* save r2 */ + + /* Fill all stack args: reserve stack space and fill ony by one */ + add r4, r4, #64 /* r4 points to stack args */ + mov r6, sp + mov r7, #7 + bic r6, r6, r7 /* Ensure stack is 8 byte aligned */ + lsl r2, r5, #2 /* r2 = nstacks * 4 */ + add r2, r2, #7 /* r2 = (r2 + 7) & ~7 */ + bic r2, r2, r7 + sub r6, r6, r2 /* reserved stack space for stack arguments */ + mov r7, sp + mov sp, r6 + +loop_stack_args: /* copy stack arguments to stack */ + cmp r5, #0 + beq call_func1 + ldr r2, [r4] /* Note: caller should insure int64 and */ + add r4, r4, #4 /* double are placed in 8 bytes aligned address */ + str r2, [r6] + add r6, r6, #4 + + sub r5, r5, #1 + b loop_stack_args + +call_func1: + mov r2, lr /* restore r2 */ + +call_func: + blx ip + mov sp, r7 /* restore sp */ + +return: + pop {r3} + pop {r4, r5, r6, r7} + mov lr, r3 + bx lr + diff --git a/wamr/core/iwasm/common/arch/invokeNative_xtensa.s b/wamr/core/iwasm/common/arch/invokeNative_xtensa.s new file mode 100644 index 0000000..ce03f12 --- /dev/null +++ b/wamr/core/iwasm/common/arch/invokeNative_xtensa.s @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + .text + .align 2 + .global invokeNative + .type invokeNative,function + +/* + * Arguments passed in: + * + * a2 function pntr + * a3 argv + * a4 argc + */ + +invokeNative: + entry a1, 256 + + blti a4, 1, return /* at least one argument required: exec_env */ + + /* register a10 ~ a15 are used to pass first 6 arguments */ + + l32i.n a10, a3, 0 + beqi a4, 1, call_func + + l32i.n a11, a3, 4 + beqi a4, 2, call_func + + l32i.n a12, a3, 8 + beqi a4, 3, call_func + + l32i.n a13, a3, 12 + beqi a4, 4, call_func + + l32i.n a14, a3, 16 + beqi a4, 5, call_func + + l32i.n a15, a3, 20 + beqi a4, 6, call_func + + /* left arguments are passed through stack */ + + addi a4, a4, -6 + addi a3, a3, 24 /* move argv pointer */ + mov.n a6, a1 /* store stack pointer */ + addi a7, a1, 256 /* stack boundary */ + +loop_args: + beqi a4, 0, call_func + bge a6, a7, call_func /* reach stack boundary */ + + l32i.n a5, a3, 0 /* load argument to a5 */ + s32i.n a5, a6, 0 /* push data to stack */ + + addi a4, a4, -1 /* decrease argc */ + addi a3, a3, 4 /* move argv pointer */ + addi a6, a6, 4 /* move stack pointer */ + + j loop_args + +call_func: + mov.n a8, a2 + callx8 a8 + + /* the result returned from callee is stored in a2 + mov the result to a10 so the caller of this function + can receive the value */ + mov.n a2, a10 + mov.n a3, a11 + +return: + retw.n diff --git a/wamr/core/iwasm/common/iwasm_common.cmake b/wamr/core/iwasm/common/iwasm_common.cmake new file mode 100644 index 0000000..458b00a --- /dev/null +++ b/wamr/core/iwasm/common/iwasm_common.cmake @@ -0,0 +1,56 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (IWASM_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories (${IWASM_COMMON_DIR}) + +add_definitions(-DBH_MALLOC=wasm_runtime_malloc) +add_definitions(-DBH_FREE=wasm_runtime_free) + +file (GLOB c_source_all ${IWASM_COMMON_DIR}/*.c) + +if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64.asm) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_em64.s) + endif () +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_ia32.asm) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_ia32.s) + endif () +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + if (WAMR_BUILD_TARGET MATCHES "ARM.*_VFP") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arm_vfp.s) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_arm.s) + endif () +elseif (WAMR_BUILD_TARGET MATCHES "THUMB.*") + if (WAMR_BUILD_TARGET MATCHES "THUMB.*_VFP") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_thumb_vfp.s) + else () + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_thumb.s) + endif () +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_aarch64.s) +elseif (WAMR_BUILD_TARGET STREQUAL "MIPS") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_mips.s) +elseif (WAMR_BUILD_TARGET STREQUAL "XTENSA") + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_xtensa.s) +elseif (WAMR_BUILD_TARGET STREQUAL "GENERAL") + # Use invokeNative_general.c instead of assembly code, + # but the maximum number of native arguments is limited to 20, + # and there are possible issues when passing arguments to + # native function for some cpus, e.g. int64 and double arguments + # in arm and mips need to be 8-bytes aligned, and some arguments + # of x86_64 are passed by registers but not stack + set (source_all ${c_source_all} ${IWASM_COMMON_DIR}/arch/invokeNative_general.c) +else () + message (FATAL_ERROR "Build target isn't set") +endif () + +set (IWASM_COMMON_SOURCE ${source_all}) + diff --git a/wamr/core/iwasm/common/wasm_c_api.c b/wamr/core/iwasm/common/wasm_c_api.c new file mode 100644 index 0000000..21d612c --- /dev/null +++ b/wamr/core/iwasm/common/wasm_c_api.c @@ -0,0 +1,2747 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_c_api_internal.h" +#include "wasm_memory.h" +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +#define NOT_REACHED() bh_assert(!"should not be reached") + +typedef struct wasm_module_ex_t wasm_module_ex_t; + +void +wasm_module_delete_internal(wasm_module_t *); + +void +wasm_instance_delete_internal(wasm_instance_t *); + +static void * +malloc_internal(size_t size) +{ + void *mem = NULL; + + if (size >= UINT32_MAX) { + return NULL; + } + + mem = wasm_runtime_malloc((uint32)size); + if (mem) { + memset(mem, 0, size); + } + + return mem; +} + +#define FREEIF(p) \ + if (p) { \ + wasm_runtime_free(p); \ + } + +/* Vectors */ +#define INIT_VEC(vector_p, func_prefix, size) \ + do { \ + vector_p = malloc_internal(sizeof(*(vector_p))); \ + if (!vector_p) { \ + goto failed; \ + } \ + func_prefix##_new_uninitialized(vector_p, size); \ + if (!(vector_p)->data) { \ + goto failed; \ + } \ + } while (false) + +#define DEINIT_VEC(vector_p, delete_func) \ + if ((vector_p)) { \ + if ((vector_p)->data) { \ + delete_func(vector_p); \ + } \ + wasm_runtime_free(vector_p); \ + vector_p = NULL; \ + } + +#define FREE_VEC_ELEMS(vec, del_func) \ + for (i = 0; i < (vec)->num_elems; ++i) { \ + del_func(*((vec)->data + i)); \ + *((vec)->data + i) = NULL; \ + } + +static inline void +generic_vec_init_data(Vector *out, size_t num_of_elems, size_t size_of_elem) +{ + if (!bh_vector_init(out, num_of_elems, size_of_elem)) { + out->data = NULL; + out->max_elems = 0; + out->num_elems = 0; + } + else { + memset(out->data, 0, num_of_elems * size_of_elem); + } +} + +void +wasm_byte_vec_new_uninitialized(wasm_byte_vec_t *out, size_t size) +{ + bh_assert(out); + generic_vec_init_data((Vector *)out, size, sizeof(wasm_byte_t)); +} + +void +wasm_byte_vec_copy(wasm_byte_vec_t *out, const wasm_byte_vec_t *src) +{ + uint32 len = 0; + + bh_assert(out && src); + + generic_vec_init_data((Vector *)out, src->size, src->size_of_elem); + if (!out->data) { + goto failed; + } + + len = src->size * src->size_of_elem; + bh_memcpy_s(out->data, len, src->data, len); + out->num_elems = src->num_elems; + return; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_byte_vec_delete(out); +} + +void +wasm_byte_vec_new(wasm_byte_vec_t *out, size_t size, const wasm_byte_t *data) +{ + size_t size_in_bytes = 0; + + bh_assert(out && data); + + generic_vec_init_data((Vector *)out, size, sizeof(wasm_byte_t)); + if (!out->data) { + goto failed; + } + + size_in_bytes = size * sizeof(wasm_byte_t); + bh_memcpy_s(out->data, size_in_bytes, data, size_in_bytes); + out->num_elems = size; + return; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_byte_vec_delete(out); +} + +void +wasm_byte_vec_delete(wasm_byte_vec_t *byte_vec) +{ + if (byte_vec && byte_vec->data) { + bh_vector_destroy((Vector *)byte_vec); + } +} + +/* Runtime Environment */ +static void +wasm_engine_delete_internal(wasm_engine_t *engine) +{ + if (engine) { + DEINIT_VEC(engine->stores, wasm_store_vec_delete); + wasm_runtime_free(engine); + } + + wasm_runtime_destroy(); +} + +static wasm_engine_t * +wasm_engine_new_internal(mem_alloc_type_t type, + const MemAllocOption *opts, + runtime_mode_e mode) +{ + wasm_engine_t *engine = NULL; + /* init runtime */ + RuntimeInitArgs init_args = { 0 }; + init_args.mem_alloc_type = type; + + if (type == Alloc_With_Pool) { + init_args.mem_alloc_option.pool.heap_buf = opts->pool.heap_buf; + init_args.mem_alloc_option.pool.heap_size = opts->pool.heap_size; + } + else if (type == Alloc_With_Allocator) { + init_args.mem_alloc_option.allocator.malloc_func = + opts->allocator.malloc_func; + init_args.mem_alloc_option.allocator.free_func = + opts->allocator.free_func; + init_args.mem_alloc_option.allocator.realloc_func = + opts->allocator.realloc_func; + } + else { + init_args.mem_alloc_option.pool.heap_buf = NULL; + init_args.mem_alloc_option.pool.heap_size = 0; + } + + if (!wasm_runtime_full_init(&init_args)) { + goto failed; + } + +#if BH_DEBUG == 1 + bh_log_set_verbose_level(5); +#else + bh_log_set_verbose_level(3); +#endif + + /* create wasm_engine_t */ + engine = malloc_internal(sizeof(wasm_engine_t)); + if (!engine) { + goto failed; + } + + /* set running mode */ + LOG_WARNING("running under mode %d", mode); + engine->mode = mode; + + /* create wasm_store_vec_t */ + INIT_VEC(engine->stores, wasm_store_vec, 1); + + return engine; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_engine_delete_internal(engine); + return NULL; +} + +/* global engine instance */ +static wasm_engine_t *singleton_engine = NULL; + +static inline runtime_mode_e +current_runtime_mode() +{ + bh_assert(singleton_engine); + return singleton_engine->mode; +} + +wasm_engine_t * +wasm_engine_new() +{ + runtime_mode_e mode = INTERP_MODE; +#if WASM_ENABLE_INTERP == 0 && WASM_ENABLE_AOT != 0 + mode = AOT_MODE; +#endif + + if (INTERP_MODE == mode) { +#if WASM_ENABLE_INTERP == 0 + bh_assert(!"does not support INTERP_MODE. Please recompile"); +#endif + } + else { +#if WASM_ENABLE_AOT == 0 + bh_assert(!"does not support AOT_MODE. Please recompile"); +#endif + } + + if (!singleton_engine) { + singleton_engine = + wasm_engine_new_internal(Alloc_With_System_Allocator, NULL, mode); + } + return singleton_engine; +} + +wasm_engine_t * +wasm_engine_new_with_args(mem_alloc_type_t type, + const MemAllocOption *opts, + runtime_mode_e mode) +{ + if (!singleton_engine) { + singleton_engine = wasm_engine_new_internal(type, opts, mode); + } + return singleton_engine; +} + +void +wasm_engine_delete(wasm_engine_t *engine) +{ + if (engine) { + wasm_engine_delete_internal(engine); + singleton_engine = NULL; + } +} + +wasm_store_t * +wasm_store_new(wasm_engine_t *engine) +{ + wasm_store_t *store = NULL; + + bh_assert(engine && singleton_engine == engine); + + /* always return the first store */ + if (bh_vector_size((Vector *)singleton_engine->stores) > 0) { + /* + * although it is a copy of the first store, but still point to + * the wasm_store_t + */ + if (!bh_vector_get((Vector *)singleton_engine->stores, 0, &store)) { + goto failed; + } + return store; + } + + store = malloc_internal(sizeof(wasm_store_t)); + if (!store) { + goto failed; + } + + /* new a vector, and new its data */ + INIT_VEC(store->modules, wasm_module_vec, DEFAULT_VECTOR_INIT_LENGTH); + INIT_VEC(store->instances, wasm_instance_vec, DEFAULT_VECTOR_INIT_LENGTH); + + /* append to a store list of engine */ + if (!bh_vector_append((Vector *)singleton_engine->stores, &store)) { + goto failed; + } + + return store; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_store_delete(store); + return NULL; +} + +void +wasm_store_delete(wasm_store_t *store) +{ + if (!store || singleton_engine->stores->num_elems == 0) { + return; + } + + DEINIT_VEC(store->modules, wasm_module_vec_delete); + DEINIT_VEC(store->instances, wasm_instance_vec_delete); + + wasm_runtime_free(store); + bh_vector_remove((Vector *)singleton_engine->stores, 0, NULL); +} + +void +wasm_store_vec_new_uninitialized(wasm_store_vec_t *out, size_t size) +{ + bh_assert(out); + generic_vec_init_data((Vector *)out, size, sizeof(wasm_store_t *)); +} + +void +wasm_store_vec_delete(wasm_store_vec_t *store_vec) +{ + size_t i = 0; + if (!store_vec || !store_vec->data) { + return; + } + + FREE_VEC_ELEMS(store_vec, wasm_store_delete); + bh_vector_destroy((Vector *)store_vec); +} + +static inline void +check_engine_and_store(wasm_engine_t *engine, wasm_store_t *store) +{ + /* remove it if we are supporting more than one store */ + bh_assert(engine && store && engine->stores->data[0] == store); +} + +/* Type Representations */ +static wasm_valtype_t * +wasm_valtype_new_internal(uint8 val_type_rt) +{ + switch (val_type_rt) { + case VALUE_TYPE_I32: + return wasm_valtype_new_i32(); + case VALUE_TYPE_I64: + return wasm_valtype_new_i64(); + case VALUE_TYPE_F32: + return wasm_valtype_new_f32(); + case VALUE_TYPE_F64: + return wasm_valtype_new_f64(); + case VALUE_TYPE_ANY: + return wasm_valtype_new_anyref(); + default: + return NULL; + } +} + +wasm_valtype_t * +wasm_valtype_new(wasm_valkind_t kind) +{ + wasm_valtype_t *val_type = malloc_internal(sizeof(wasm_valtype_t)); + if (val_type) { + val_type->kind = kind; + } + return val_type; +} + +void +wasm_valtype_delete(wasm_valtype_t *val_type) +{ + bh_assert(val_type); + wasm_runtime_free(val_type); +} + +wasm_valtype_t * +wasm_valtype_copy(wasm_valtype_t *src) +{ + bh_assert(src); + return wasm_valtype_new(src->kind); +} + +wasm_valkind_t +wasm_valtype_kind(const wasm_valtype_t *val_type) +{ + bh_assert(val_type); + return val_type->kind; +} + +bool +wasm_valtype_same(const wasm_valtype_t *vt1, const wasm_valtype_t *vt2) +{ + if (!vt1 && !vt2) { + return true; + } + + if (!vt1 || !vt2) { + return false; + } + + return vt1->kind == vt2->kind; +} + +void +wasm_valtype_vec_new_uninitialized(wasm_valtype_vec_t *out, size_t size) +{ + bh_assert(out); + generic_vec_init_data((Vector *)out, size, sizeof(wasm_valtype_t *)); +} + +void +wasm_valtype_vec_new_empty(wasm_valtype_vec_t *out) +{ + bh_assert(out); + memset(out, 0, sizeof(wasm_valtype_vec_t)); +} + +void +wasm_valtype_vec_new(wasm_valtype_vec_t *out, + size_t size, + wasm_valtype_t *const data[]) +{ + size_t size_in_bytes = 0; + bh_assert(out && data); + generic_vec_init_data((Vector *)out, size, sizeof(wasm_valtype_t *)); + if (!out->data) { + goto failed; + } + + size_in_bytes = size * sizeof(wasm_valtype_t *); + bh_memcpy_s(out->data, size_in_bytes, data, size_in_bytes); + out->num_elems = size; + return; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_valtype_vec_delete(out); +} + +void +wasm_valtype_vec_copy(wasm_valtype_vec_t *out, const wasm_valtype_vec_t *src) +{ + size_t i = 0; + + bh_assert(out && src); + + generic_vec_init_data((Vector *)out, src->size, src->size_of_elem); + if (!out->data) { + goto failed; + } + + /* clone every wasm_valtype_t */ + for (i = 0; i < src->num_elems; i++) { + wasm_valtype_t *cloned = wasm_valtype_copy(*(src->data + i)); + if (!cloned) { + goto failed; + } + if (!bh_vector_append((Vector *)out, &cloned)) { + goto failed; + } + } + + return; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_valtype_vec_delete(out); +} + +void +wasm_valtype_vec_delete(wasm_valtype_vec_t *val_type_vec) +{ + size_t i = 0; + if (!val_type_vec || !val_type_vec->data) { + return; + } + + FREE_VEC_ELEMS(val_type_vec, wasm_valtype_delete); + bh_vector_destroy((Vector *)val_type_vec); +} + +static wasm_functype_t * +wasm_functype_new_internal(WASMType *type_rt) +{ + wasm_functype_t *func_type = NULL; + uint32 i = 0; + + bh_assert(type_rt); + + func_type = malloc_internal(sizeof(wasm_functype_t)); + if (!func_type) { + goto failed; + } + + /* WASMType->types[0 : type_rt->param_count) -> func_type->params */ + INIT_VEC(func_type->params, wasm_valtype_vec, type_rt->param_count); + for (i = 0; i < type_rt->param_count; ++i) { + wasm_valtype_t *param_type = + wasm_valtype_new_internal(*(type_rt->types + i)); + if (!param_type) { + goto failed; + } + + if (!bh_vector_append((Vector *)func_type->params, ¶m_type)) { + goto failed; + } + } + + /* WASMType->types[type_rt->param_count : type_rt->result_count) -> func_type->results */ + INIT_VEC(func_type->results, wasm_valtype_vec, type_rt->result_count); + for (i = type_rt->param_count; i < type_rt->result_count; ++i) { + wasm_valtype_t *result_type = + wasm_valtype_new_internal(*(type_rt->types + i)); + if (!result_type) { + goto failed; + } + + if (!bh_vector_append((Vector *)func_type->results, &result_type)) { + goto failed; + } + } + + return func_type; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_functype_delete(func_type); + return NULL; +} + +wasm_functype_t * +wasm_functype_new(wasm_valtype_vec_t *params, wasm_valtype_vec_t *results) +{ + wasm_functype_t *func_type = NULL; + + bh_assert(params); + bh_assert(results); + + func_type = malloc_internal(sizeof(wasm_functype_t)); + if (!func_type) { + goto failed; + } + + func_type->extern_kind = WASM_EXTERN_FUNC; + + func_type->params = malloc_internal(sizeof(wasm_valtype_vec_t)); + if (!func_type->params) { + goto failed; + } + + /* transfer ownership of contents of params and results */ + bh_memcpy_s(func_type->params, sizeof(wasm_valtype_vec_t), params, + sizeof(wasm_valtype_vec_t)); + + func_type->results = malloc_internal(sizeof(wasm_valtype_vec_t)); + if (!func_type->results) { + goto failed; + } + + bh_memcpy_s(func_type->results, sizeof(wasm_valtype_vec_t), results, + sizeof(wasm_valtype_vec_t)); + + return func_type; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + FREEIF(func_type->params); + FREEIF(func_type->results); + FREEIF(func_type); + return NULL; +} + +wasm_functype_t * +wasm_functype_copy(wasm_functype_t *src) +{ + wasm_functype_t *dst = NULL; + + bh_assert(src); + + dst = malloc_internal(sizeof(wasm_functype_t)); + if (!dst) { + goto failed; + } + + dst->extern_kind = src->extern_kind; + + dst->params = malloc_internal(sizeof(wasm_valtype_vec_t)); + if (!dst->params) { + goto failed; + } + + wasm_valtype_vec_copy(dst->params, src->params); + if (!dst->params) { + goto failed; + } + + dst->results = malloc_internal(sizeof(wasm_valtype_vec_t)); + if (!dst->results) { + goto failed; + } + + wasm_valtype_vec_copy(dst->results, src->results); + if (!dst->results) { + goto failed; + } + + return dst; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_functype_delete(dst); + return NULL; +} + +void +wasm_functype_delete(wasm_functype_t *func_type) +{ + if (!func_type) { + return; + } + + if (func_type->params) { + wasm_valtype_vec_delete(func_type->params); + wasm_runtime_free(func_type->params); + func_type->params = NULL; + } + + if (func_type->results) { + wasm_valtype_vec_delete(func_type->results); + wasm_runtime_free(func_type->results); + func_type->results = NULL; + } + + wasm_runtime_free(func_type); +} + +const wasm_valtype_vec_t * +wasm_functype_params(const wasm_functype_t *func_type) +{ + bh_assert(func_type); + return func_type->params; +} + +const wasm_valtype_vec_t * +wasm_functype_results(const wasm_functype_t *func_type) +{ + bh_assert(func_type); + return func_type->results; +} + +wasm_globaltype_t * +wasm_globaltype_new(wasm_valtype_t *val_type, wasm_mutability_t mut) +{ + wasm_globaltype_t *global_type = NULL; + + bh_assert(val_type); + + global_type = malloc_internal(sizeof(wasm_globaltype_t)); + if (!global_type) { + goto failed; + } + + global_type->val_type = val_type; + global_type->mutability = mut; + + return global_type; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_globaltype_delete(global_type); + return NULL; +} + +wasm_globaltype_t * +wasm_globaltype_new_internal(uint8 val_type_rt, bool is_mutable) +{ + wasm_globaltype_t *global_type = NULL; + + global_type = malloc_internal(sizeof(wasm_globaltype_t)); + if (!global_type) { + goto failed; + } + + global_type->val_type = wasm_valtype_new_internal(val_type_rt); + if (!global_type->val_type) { + goto failed; + } + + global_type->mutability = is_mutable ? WASM_VAR : WASM_CONST; + + return global_type; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_globaltype_delete(global_type); + return NULL; +} + +void +wasm_globaltype_delete(wasm_globaltype_t *global_type) +{ + if (!global_type) { + return; + } + + if (global_type->val_type) { + wasm_valtype_delete(global_type->val_type); + global_type->val_type = NULL; + } + + wasm_runtime_free(global_type); +} + +wasm_globaltype_t * +wasm_globaltype_copy(wasm_globaltype_t *src) +{ + wasm_globaltype_t *dst = NULL; + + bh_assert(src); + + dst = malloc_internal(sizeof(wasm_globaltype_t)); + if (!dst) { + goto failed; + } + + dst->val_type = wasm_valtype_copy(src->val_type); + if (!dst->val_type) { + goto failed; + } + + dst->mutability = src->mutability; + + return dst; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_globaltype_delete(dst); + return NULL; +} + +const wasm_valtype_t * +wasm_globaltype_content(const wasm_globaltype_t *global_type) +{ + bh_assert(global_type); + return global_type->val_type; +} + +wasm_mutability_t +wasm_globaltype_mutability(const wasm_globaltype_t *global_type) +{ + bh_assert(global_type); + return global_type->mutability; +} + +bool +wasm_globaltype_same(const wasm_globaltype_t *gt1, + const wasm_globaltype_t *gt2) +{ + if (!gt1 && !gt2) { + return true; + } + + if (!gt1 || !gt2) { + return false; + } + + return wasm_valtype_same(gt1->val_type, gt2->val_type) + || gt1->mutability == gt2->mutability; +} + +wasm_tabletype_t * +wasm_tabletype_new(wasm_valtype_t *val_type, const wasm_limits_t *limits) +{ + return NULL; +} + +void +wasm_tabletype_delete(wasm_tabletype_t *table_type) +{} + +wasm_memorytype_t * +wasm_memorytype_new(const wasm_limits_t *limits) +{ + return NULL; +} + +void +wasm_memorytype_delete(wasm_memorytype_t *memory_type) +{} + +/* Runtime Objects */ + +void +wasm_val_delete(wasm_val_t *v) +{ + /* do nothing */ +} + +void +wasm_val_copy(wasm_val_t *out, const wasm_val_t *src) +{ + bh_assert(out && src); + bh_memcpy_s(out, sizeof(wasm_val_t), src, sizeof(wasm_val_t)); +} + +bool +wasm_val_same(const wasm_val_t *v1, const wasm_val_t *v2) +{ + if (!v1 && !v2) { + return true; + } + + if (!v1 || !v2) { + return false; + } + + if (v1->kind != v2->kind) { + return false; + } + + switch (v1->kind) { + case WASM_I32: + return v1->of.i32 == v2->of.i32; + case WASM_I64: + return v1->of.i64 == v2->of.i64; + case WASM_F32: + return v1->of.f32 == v2->of.f32; + case WASM_F64: + return v1->of.f64 == v2->of.f64; + case WASM_FUNCREF: + return v1->of.ref == v2->of.ref; + default: + break; + } + return false; +} + +wasm_trap_t * +wasm_trap_new(wasm_store_t *store, const wasm_message_t *message) +{ + wasm_trap_t *trap; + + bh_assert(store && message); + + trap = malloc_internal(sizeof(wasm_trap_t)); + if (!trap) { + goto failed; + } + + wasm_byte_vec_new(trap->message, message->num_elems, message->data); + if (!trap->message->data) { + goto failed; + } + + return trap; + +failed: + wasm_trap_delete(trap); + return NULL; +} + +void +wasm_trap_delete(wasm_trap_t *trap) +{ + if (!trap) { + return; + } + + if (trap->message) { + wasm_byte_vec_delete(trap->message); + } + + wasm_runtime_free(trap); +} + +void +wasm_trap_message(const wasm_trap_t *trap, wasm_message_t *out) +{ + bh_assert(trap && out); + wasm_byte_vec_copy(out, trap->message); +} + +struct wasm_module_ex_t { + struct WASMModuleCommon *module_comm_rt; + wasm_byte_vec_t *binary; +}; + +static inline wasm_module_t * +module_ext_to_module(wasm_module_ex_t *module_ex) +{ + return (wasm_module_t *)module_ex; +} + +static inline wasm_module_ex_t * +module_to_module_ext(wasm_module_t *module) +{ + return (wasm_module_ex_t *)module; +} + +wasm_module_t * +wasm_module_new(wasm_store_t *store, const wasm_byte_vec_t *binary) +{ + char error[128] = { 0 }; + wasm_module_ex_t *module_ex = NULL; + PackageType pkg_type = Package_Type_Unknown; + + check_engine_and_store(singleton_engine, store); + bh_assert(binary && binary->data && binary->size); + + pkg_type = get_package_type((uint8 *)binary->data, binary->size); + if (Package_Type_Unknown == pkg_type + || (Wasm_Module_Bytecode == pkg_type + && INTERP_MODE != current_runtime_mode()) + || (Wasm_Module_AoT == pkg_type + && INTERP_MODE == current_runtime_mode())) { + LOG_WARNING( + "current runtime mode %d doesn\'t support the package type " + "%d", + current_runtime_mode(), pkg_type); + goto failed; + } + + module_ex = malloc_internal(sizeof(wasm_module_ex_t)); + if (!module_ex) { + goto failed; + } + + module_ex->binary = malloc_internal(sizeof(wasm_byte_vec_t)); + if (!module_ex->binary) { + goto failed; + } + + wasm_byte_vec_copy(module_ex->binary, binary); + if (!module_ex->binary->data) { + goto failed; + } + + module_ex->module_comm_rt = + wasm_runtime_load((uint8 *)module_ex->binary->data, + module_ex->binary->size, error, (uint32)sizeof(error)); + if (!(module_ex->module_comm_rt)) { + LOG_ERROR(error); + goto failed; + } + + /* add it to a watching list in store */ + if (!bh_vector_append((Vector *)store->modules, &module_ex)) { + goto failed; + } + + return module_ext_to_module(module_ex); + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_module_delete_internal(module_ext_to_module(module_ex)); + return NULL; +} + +void +wasm_module_delete_internal(wasm_module_t *module) +{ + wasm_module_ex_t *module_ex; + if (!module) { + return; + } + + module_ex = module_to_module_ext(module); + if (module_ex->binary) { + wasm_byte_vec_delete(module_ex->binary); + wasm_runtime_free(module_ex->binary); + module_ex->binary = NULL; + } + + if (module_ex->module_comm_rt) { + wasm_runtime_unload(module_ex->module_comm_rt); + module_ex->module_comm_rt = NULL; + } + + wasm_runtime_free(module_ex); +} + +/* will release module when releasing the store */ +void +wasm_module_delete(wasm_module_t *module) +{ + /* pass */ +} + +void +wasm_module_vec_new_uninitialized(wasm_module_vec_t *out, size_t size) +{ + generic_vec_init_data((Vector *)out, size, sizeof(wasm_module_t *)); +} + +void +wasm_module_vec_delete(wasm_module_vec_t *module_vec) +{ + size_t i = 0; + if (!module_vec || !module_vec->data) { + return; + } + + FREE_VEC_ELEMS(module_vec, wasm_module_delete_internal); + bh_vector_destroy((Vector *)module_vec); +} + +static uint32 +argv_to_params(const uint64 *argv, + const wasm_valtype_vec_t *param_defs, + wasm_val_t *out) +{ + size_t i = 0; + uint32 argc = 0; + void *argv_p = (void *)argv; + + for (i = 0; i < param_defs->num_elems; i++) { + wasm_valtype_t *param_def = param_defs->data[i]; + wasm_val_t *param = out + i; + switch (param_def->kind) { + case WASM_I32: + param->kind = WASM_I32; + param->of.i32 = *(int32 *)argv_p; + argv_p = (uint32 *)argv_p + 1; + argc++; + break; + case WASM_I64: + param->kind = WASM_I64; + param->of.i64 = *(int64 *)argv_p; + argv_p = (uint64 *)argv_p + 1; + argc++; + break; + case WASM_F32: + param->kind = WASM_F32; + param->of.f32 = *(float32 *)argv_p; + argv_p = (float32 *)argv_p + 1; + argc++; + break; + case WASM_F64: + param->kind = WASM_F64; + param->of.f64 = *(float64 *)argv_p; + argv_p = (float64 *)argv_p + 1; + argc++; + break; + default: + NOT_REACHED(); + goto failed; + } + } + + return argc; +failed: + return 0; +} + +static uint32 +results_to_argv(const wasm_val_t *results, + const wasm_valtype_vec_t *result_defs, + uint64 *out) +{ + size_t i = 0; + uint32 argc = 0; + void *argv_p = out; + + for (i = 0; i < result_defs->num_elems; ++i) { + wasm_valtype_t *result_def = result_defs->data[i]; + const wasm_val_t *result = results + i; + switch (result_def->kind) { + case WASM_I32: + *(int32 *)argv_p = result->of.i32; + argv_p = (uint32 *)argv_p + 1; + argc++; + break; + case WASM_I64: + *(int64 *)argv_p = result->of.i64; + argv_p = (uint64 *)argv_p + 1; + argc++; + break; + case WASM_F32: + *(float32 *)argv_p = result->of.f32; + argv_p = (float32 *)argv_p + 1; + argc++; + break; + case WASM_F64: + *(float64 *)argv_p = result->of.f64; + argv_p = (float64 *)argv_p + 1; + argc++; + break; + default: + NOT_REACHED(); + goto failed; + } + } + + return argc; +failed: + return 0; +} + +static void +native_func_trampoline(wasm_exec_env_t exec_env, uint64 *argv) +{ + wasm_val_t *params = NULL; + wasm_val_t *results = NULL; + uint32 argc = 0; + const wasm_func_t *func = NULL; + wasm_trap_t *trap = NULL; + + bh_assert(argv); + + func = wasm_runtime_get_function_attachment(exec_env); + bh_assert(func); + + params = malloc_internal(wasm_func_param_arity(func) * sizeof(wasm_val_t)); + if (!params) { + goto failed; + } + + results = + malloc_internal(wasm_func_result_arity(func) * sizeof(wasm_val_t)); + if (!results) { + goto failed; + } + + /* argv -> const wasm_val_t params[] */ + argc = + argv_to_params(argv, wasm_functype_params(wasm_func_type(func)), params); + if (wasm_func_param_arity(func) && !argc) { + goto failed; + } + + if (func->with_env) { + trap = func->u.cb_env.cb(func->u.cb_env.env, params, results); + } + else { + trap = func->u.cb(params, results); + } + + if (trap) { + wasm_name_t *message = NULL; + wasm_trap_message(trap, message); + LOG_WARNING("got a trap %s", message->data); + wasm_name_delete(message); + } + + /* there is no result or there is an exception */ + if (trap || !wasm_func_result_arity(func)) { + memset(argv, 0, wasm_func_param_arity(func) * sizeof(uint64)); + } + + /* wasm_val_t results[] -> argv */ + argc = results_to_argv(results, + wasm_functype_results(wasm_func_type(func)), argv); + if (wasm_func_result_arity(func) && !argc) { + goto failed; + } + +failed: + FREEIF(params); + FREEIF(results); + return; +} + +wasm_func_t * +wasm_func_new(wasm_store_t *store, + const wasm_functype_t *func_type, + wasm_func_callback_t callback) +{ + wasm_func_t *func = NULL; + + check_engine_and_store(singleton_engine, store); + bh_assert(func_type); + + func = malloc_internal(sizeof(wasm_func_t)); + if (!func) { + goto failed; + } + + func->kind = WASM_EXTERN_FUNC; + func->with_env = false; + func->u.cb = callback; + + func->func_type = wasm_functype_copy((wasm_functype_t *)func_type); + if (!func->func_type) { + goto failed; + } + + return func; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_func_delete(func); + return NULL; +} + +wasm_func_t * +wasm_func_new_with_env(wasm_store_t *store, + const wasm_functype_t *func_type, + wasm_func_callback_with_env_t callback, + void *env, + void (*finalizer)(void *)) +{ + wasm_func_t *func = NULL; + + check_engine_and_store(singleton_engine, store); + bh_assert(func_type); + + func = malloc_internal(sizeof(wasm_func_t)); + if (!func) { + goto failed; + } + + func->kind = WASM_EXTERN_FUNC; + func->with_env = true; + func->u.cb_env.cb = callback; + func->u.cb_env.env = env; + func->u.cb_env.finalizer = finalizer; + + func->func_type = wasm_functype_copy((wasm_functype_t *)func_type); + if (!func->func_type) { + goto failed; + } + + return func; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_func_delete(func); + return NULL; +} + +static wasm_func_t * +wasm_func_new_internal(wasm_store_t *store, + uint16 func_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_func_t *func = NULL; + WASMType *type_rt = NULL; + + check_engine_and_store(singleton_engine, store); + bh_assert(inst_comm_rt); + + func = malloc_internal(sizeof(wasm_func_t)); + if (!func) { + goto failed; + } + + func->kind = WASM_EXTERN_FUNC; + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + bh_assert(func_idx_rt + < ((WASMModuleInstance *)inst_comm_rt)->function_count); + WASMFunctionInstance *func_interp = + ((WASMModuleInstance *)inst_comm_rt)->functions + func_idx_rt; + type_rt = func_interp->is_import_func + ? func_interp->u.func_import->func_type + : func_interp->u.func->func_type; +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + /* use same index to trace the function type in AOTFuncType **func_types */ + AOTModuleInstance *inst_aot = (AOTModuleInstance *)inst_comm_rt; + AOTFunctionInstance *func_aot = + (AOTFunctionInstance *)inst_aot->export_funcs.ptr + func_idx_rt; + type_rt = func_aot->is_import_func ? func_aot->u.func_import->func_type + : func_aot->u.func.func_type; +#endif + } + + if (!type_rt) { + goto failed; + } + + func->func_type = wasm_functype_new_internal(type_rt); + if (!func->func_type) { + goto failed; + } + + /* will add name information when processing "exports" */ + func->module_name = NULL; + func->name = NULL; + func->func_idx_rt = func_idx_rt; + func->inst_comm_rt = inst_comm_rt; + return func; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_func_delete(func); + return NULL; +} + +void +wasm_func_delete(wasm_func_t *func) +{ + if (!func) { + return; + } + + if (func->func_type) { + wasm_functype_delete(func->func_type); + func->func_type = NULL; + } + + if (func->with_env) { + if (func->u.cb_env.finalizer) { + func->u.cb_env.finalizer(func->u.cb_env.env); + func->u.cb_env.finalizer = NULL; + func->u.cb_env.env = NULL; + } + } + + wasm_runtime_free(func); +} + +wasm_func_t * +wasm_func_copy(const wasm_func_t *func) +{ + wasm_func_t *cloned = NULL; + + bh_assert(func); + + cloned = malloc_internal(sizeof(wasm_func_t)); + if (!cloned) { + goto failed; + } + + cloned->kind = func->kind; + cloned->with_env = func->with_env; + if (cloned->with_env) { + cloned->u.cb_env.cb = func->u.cb_env.cb; + cloned->u.cb_env.env = func->u.cb_env.env; + cloned->u.cb_env.finalizer = func->u.cb_env.finalizer; + } + else { + cloned->u.cb = func->u.cb; + } + + cloned->func_idx_rt = func->func_idx_rt; + cloned->inst_comm_rt = func->inst_comm_rt; + cloned->func_type = wasm_functype_copy(func->func_type); + if (!cloned->func_type) { + goto failed; + } + + return cloned; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_func_delete(cloned); + return NULL; +} + +wasm_functype_t * +wasm_func_type(const wasm_func_t *func) +{ + bh_assert(func); + return func->func_type; +} + +static uint32 +params_to_argv(const wasm_val_t *params, + const wasm_valtype_vec_t *param_defs, + size_t param_arity, + uint32 *out) +{ + size_t i = 0; + uint32 argc = 0; + const wasm_val_t *param = NULL; + + if (!param_arity) { + return 0; + } + + bh_assert(params && param_defs && out); + bh_assert(param_defs->num_elems == param_arity); + + for (i = 0; out && i < param_arity; ++i) { + param = params + i; + bh_assert((*(param_defs->data + i))->kind == param->kind); + + switch (param->kind) { + case WASM_I32: + *(int32 *)out = param->of.i32; + out += 1; + argc += 1; + break; + case WASM_I64: + *(int64 *)out = param->of.i64; + out += 2; + argc += 2; + break; + case WASM_F32: + *(float32 *)out = param->of.f32; + out += 1; + argc += 1; + break; + case WASM_F64: + *(float64 *)out = param->of.f64; + out += 2; + argc += 2; + break; + default: + LOG_DEBUG("unexpected parameter val type %d", param->kind); + goto failed; + } + } + + return argc; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return 0; +} + +static uint32 +argv_to_results(const uint32 *results, + const wasm_valtype_vec_t *result_defs, + size_t result_arity, + wasm_val_t *out) +{ + size_t i = 0; + uint32 argc = 0; + const uint32 *result = results; + const wasm_valtype_t *def = NULL; + + if (!result_arity) { + return 0; + } + + bh_assert(results && result_defs && out); + bh_assert(result_arity == result_defs->num_elems); + + for (i = 0; out && i < result_arity; i++) { + def = *(result_defs->data + i); + + switch (def->kind) { + case WASM_I32: + out->kind = WASM_I32; + out->of.i32 = *(int32 *)result; + result += 1; + break; + case WASM_I64: + out->kind = WASM_I64; + out->of.i64 = *(int64 *)result; + result += 2; + break; + case WASM_F32: + out->kind = WASM_F32; + out->of.f32 = *(float32 *)result; + result += 1; + break; + case WASM_F64: + out->kind = WASM_F64; + out->of.f64 = *(float64 *)result; + result += 2; + break; + default: + LOG_DEBUG("unexpected parameter val type %d", def->kind); + goto failed; + } + out++; + argc++; + } + + return argc; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return 0; +} + +wasm_trap_t * +wasm_func_call(const wasm_func_t *func, + const wasm_val_t params[], + wasm_val_t results[]) +{ + /* parameters count as if all are uint32 */ + /* a int64 or float64 parameter means 2 */ + uint32 argc = 0; + /* a parameter list and a return value list */ + uint32 *argv = NULL; + WASMFunctionInstanceCommon *func_comm_rt = NULL; + size_t param_count = 0; + size_t result_count = 0; + + bh_assert(func && func->func_type && func->inst_comm_rt); + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + func_comm_rt = ((WASMModuleInstance *)func->inst_comm_rt)->functions + + func->func_idx_rt; +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + AOTModuleInstance *inst_aot = (AOTModuleInstance *)func->inst_comm_rt; + func_comm_rt = (AOTFunctionInstance *)inst_aot->export_funcs.ptr + + func->func_idx_rt; +#endif + } + if (!func_comm_rt) { + goto failed; + } + + param_count = wasm_func_param_arity(func); + result_count = wasm_func_result_arity(func); + argv = malloc_internal( + sizeof(uint64) + * (param_count > result_count ? param_count : result_count)); + if (!argv) { + goto failed; + } + + /* copy parametes */ + argc = params_to_argv(params, wasm_functype_params(wasm_func_type(func)), + wasm_func_param_arity(func), argv); + if (wasm_func_param_arity(func) && !argc) { + goto failed; + } + + if (!wasm_runtime_create_exec_env_and_call_wasm( + func->inst_comm_rt, func_comm_rt, argc, argv)) { + LOG_DEBUG("wasm_runtime_create_exec_env_and_call_wasm failed"); + goto failed; + } + + /* copy results */ + argc = argv_to_results(argv, wasm_functype_results(wasm_func_type(func)), + wasm_func_result_arity(func), results); + if (wasm_func_result_arity(func) && !argc) { + goto failed; + } + + FREEIF(argv); + /* should return trap */ + return NULL; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + FREEIF(argv); + return NULL; +} + +size_t +wasm_func_param_arity(const wasm_func_t *func) +{ + bh_assert(func && func->func_type && func->func_type->params); + return func->func_type->params->num_elems; +} + +size_t +wasm_func_result_arity(const wasm_func_t *func) +{ + bh_assert(func && func->func_type && func->func_type->results); + return func->func_type->results->num_elems; +} + +wasm_extern_t * +wasm_func_as_extern(wasm_func_t *func) +{ + return (wasm_extern_t *)func; +} + +wasm_global_t * +wasm_global_new(wasm_store_t *store, + const wasm_globaltype_t *global_type, + const wasm_val_t *init) +{ + wasm_global_t *global = NULL; + + check_engine_and_store(singleton_engine, store); + bh_assert(store && global_type && init); + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + global->kind = WASM_EXTERN_GLOBAL; + global->type = wasm_globaltype_copy((wasm_globaltype_t *)global_type); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + + wasm_val_copy(global->init, init); + /* TODO: how to check if above is failed */ + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + +/* almost same with wasm_global_new */ +wasm_global_t * +wasm_global_copy(const wasm_global_t *src) +{ + wasm_global_t *global = NULL; + + bh_assert(src); + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + global->kind = WASM_EXTERN_GLOBAL; + global->type = wasm_globaltype_copy((wasm_globaltype_t *)src->type); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + + wasm_val_copy(global->init, src->init); + + global->global_idx_rt = src->global_idx_rt; + global->inst_comm_rt = src->inst_comm_rt; + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + +void +wasm_global_delete(wasm_global_t *global) +{ + if (!global) { + return; + } + + if (global->init) { + wasm_val_delete(global->init); + wasm_runtime_free(global->init); + global->init = NULL; + } + + if (global->type) { + wasm_globaltype_delete(global->type); + global->type = NULL; + } + + wasm_runtime_free(global); +} + +bool +wasm_global_same(const wasm_global_t *g1, const wasm_global_t *g2) +{ + if (!g1 && !g2) { + return true; + } + + if (!g1 || !g2) { + return false; + } + + return g1->kind == g2->kind && wasm_globaltype_same(g1->type, g2->type) + && wasm_val_same(g1->init, g2->init); +} + +#if WASM_ENABLE_INTERP != 0 +static bool +interp_global_set(const WASMModuleInstance *inst_interp, + uint16 global_idx_rt, + const wasm_val_t *v) +{ + const WASMGlobalInstance *global_interp = + inst_interp->globals + global_idx_rt; + uint8 val_type_rt = global_interp->type; + uint8 *data = inst_interp->global_data + global_interp->data_offset; + bool ret = true; + + switch (val_type_rt) { + case VALUE_TYPE_I32: + bh_assert(WASM_I32 == v->kind); + *((int32 *)data) = v->of.i32; + break; + case VALUE_TYPE_F32: + bh_assert(WASM_F32 == v->kind); + *((float32 *)data) = v->of.f32; + break; + case VALUE_TYPE_I64: + bh_assert(WASM_I64 == v->kind); + *((int64 *)data) = v->of.i64; + break; + case VALUE_TYPE_F64: + bh_assert(WASM_F64 == v->kind); + *((float64 *)data) = v->of.f64; + break; + default: + LOG_DEBUG("unexpected value type %d", val_type_rt); + ret = false; + break; + } + + return ret; +} + +static bool +interp_global_get(const WASMModuleInstance *inst_interp, + uint16 global_idx_rt, + wasm_val_t *out) +{ + WASMGlobalInstance *global_interp = inst_interp->globals + global_idx_rt; + uint8 val_type_rt = global_interp->type; + uint8 *data = inst_interp->global_data + global_interp->data_offset; + bool ret = true; + + switch (val_type_rt) { + case VALUE_TYPE_I32: + out->kind = WASM_I32; + out->of.i32 = *((int32 *)data); + break; + case VALUE_TYPE_F32: + out->kind = WASM_F32; + out->of.f32 = *((float32 *)data); + break; + case VALUE_TYPE_I64: + out->kind = WASM_I64; + out->of.i64 = *((int64 *)data); + break; + case VALUE_TYPE_F64: + out->kind = WASM_F64; + out->of.f64 = *((float64 *)data); + break; + default: + LOG_DEBUG("unexpected value type %d", val_type_rt); + ret = false; + } + return ret; +} +#endif + +#if WASM_ENABLE_AOT != 0 +static bool +aot_global_set(const AOTModuleInstance *inst_aot, + uint16 global_idx_rt, + const wasm_val_t *v) +{ + AOTModule *module_aot = inst_aot->aot_module.ptr; + uint8 val_type_rt = 0; + uint32 data_offset = 0; + void *data = NULL; + bool ret = true; + + if (global_idx_rt < module_aot->import_global_count) { + data_offset = module_aot->import_globals[global_idx_rt].data_offset; + val_type_rt = module_aot->import_globals[global_idx_rt].type; + } + else { + data_offset = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .data_offset; + val_type_rt = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .type; + } + + data = (void *)((uint8 *)inst_aot->global_data.ptr + data_offset); + switch (val_type_rt) { + case VALUE_TYPE_I32: + bh_assert(WASM_I32 == v->kind); + *((int32 *)data) = v->of.i32; + break; + case VALUE_TYPE_F32: + bh_assert(WASM_F32 == v->kind); + *((float32 *)data) = v->of.f32; + break; + case VALUE_TYPE_I64: + bh_assert(WASM_I64 == v->kind); + *((int64 *)data) = v->of.i64; + break; + case VALUE_TYPE_F64: + bh_assert(WASM_F64 == v->kind); + *((float64 *)data) = v->of.f64; + break; + default: + LOG_DEBUG("unexpected value type %d", val_type_rt); + ret = false; + } + return ret; +} + +static bool +aot_global_get(const AOTModuleInstance *inst_aot, + uint16 global_idx_rt, + wasm_val_t *out) +{ + AOTModule *module_aot = inst_aot->aot_module.ptr; + uint8 val_type_rt = 0; + uint32 data_offset = 0; + void *data = NULL; + bool ret = true; + + if (global_idx_rt < module_aot->import_global_count) { + data_offset = module_aot->import_globals[global_idx_rt].data_offset; + val_type_rt = module_aot->import_globals[global_idx_rt].type; + } + else { + data_offset = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .data_offset; + val_type_rt = + module_aot->globals[global_idx_rt - module_aot->import_global_count] + .type; + } + + data = (void *)((uint8 *)inst_aot->global_data.ptr + data_offset); + switch (val_type_rt) { + case VALUE_TYPE_I32: + out->kind = WASM_I32; + out->of.i32 = *((int32 *)data); + break; + case VALUE_TYPE_F32: + out->kind = WASM_F32; + out->of.f32 = *((float32 *)data); + break; + case VALUE_TYPE_I64: + out->kind = WASM_I64; + out->of.i64 = *((int64 *)data); + break; + case VALUE_TYPE_F64: + out->kind = WASM_F64; + out->of.f64 = *((float64 *)data); + break; + default: + LOG_DEBUG("unexpected value type %d", val_type_rt); + ret = false; + } + return ret; +} +#endif + +void +wasm_global_set(wasm_global_t *global, const wasm_val_t *v) +{ + bh_assert(global && v); + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + (void)interp_global_set((WASMModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, v); +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + (void)aot_global_set((AOTModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, v); +#endif + } +} + +void +wasm_global_get(const wasm_global_t *global, wasm_val_t *out) +{ + bh_assert(global && out); + + memset(out, 0, sizeof(wasm_val_t)); + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + (void)interp_global_get((WASMModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, out); +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + (void)aot_global_get((AOTModuleInstance *)global->inst_comm_rt, + global->global_idx_rt, out); +#endif + } + + bh_assert(global->init->kind == out->kind); +} + +static wasm_global_t * +wasm_global_new_internal(wasm_store_t *store, + uint16 global_idx_rt, + WASMModuleInstanceCommon *inst_comm_rt) +{ + wasm_global_t *global = NULL; + uint8 val_type_rt = 0; + bool is_mutable = 0; + + check_engine_and_store(singleton_engine, store); + bh_assert(inst_comm_rt); + + global = malloc_internal(sizeof(wasm_global_t)); + if (!global) { + goto failed; + } + + /* + * global->module_name = NULL; + * global->name = NULL; + */ + global->kind = WASM_EXTERN_GLOBAL; + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + WASMGlobalInstance *global_interp = + ((WASMModuleInstance *)inst_comm_rt)->globals + global_idx_rt; + val_type_rt = global_interp->type; + is_mutable = global_interp->is_mutable; +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + AOTModuleInstance *inst_aot = (AOTModuleInstance *)inst_comm_rt; + AOTModule *module_aot = inst_aot->aot_module.ptr; + if (global_idx_rt < module_aot->import_global_count) { + AOTImportGlobal *global_import_aot = + module_aot->import_globals + global_idx_rt; + val_type_rt = global_import_aot->type; + is_mutable = global_import_aot->is_mutable; + } + else { + AOTGlobal *global_aot = + module_aot->globals + + (global_idx_rt - module_aot->import_global_count); + val_type_rt = global_aot->type; + is_mutable = global_aot->is_mutable; + } +#endif + } + + global->type = wasm_globaltype_new_internal(val_type_rt, is_mutable); + if (!global->type) { + goto failed; + } + + global->init = malloc_internal(sizeof(wasm_val_t)); + if (!global->init) { + goto failed; + } + + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + interp_global_get((WASMModuleInstance *)inst_comm_rt, global_idx_rt, + global->init); +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + aot_global_get((AOTModuleInstance *)inst_comm_rt, global_idx_rt, + global->init); +#endif + } + + global->inst_comm_rt = inst_comm_rt; + global->global_idx_rt = global_idx_rt; + + return global; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_global_delete(global); + return NULL; +} + +wasm_globaltype_t * +wasm_global_type(const wasm_global_t *global) +{ + bh_assert(global); + return global->type; +} + +wasm_extern_t * +wasm_global_as_extern(wasm_global_t *global) +{ + return (wasm_extern_t *)global; +} + +void +wasm_table_delete(wasm_table_t *table) +{} + +void +wasm_memory_delete(wasm_memory_t *memory) +{} + +#if WASM_ENABLE_INTERP != 0 +static bool +interp_link_func(const wasm_instance_t *inst, + const WASMModule *module_interp, + uint16 func_idx_rt, + wasm_func_t *import) +{ + WASMImport *imported_func_interp = NULL; + wasm_func_t *cloned = NULL; + + bh_assert(inst && module_interp && import); + bh_assert(func_idx_rt < module_interp->import_function_count); + bh_assert(WASM_EXTERN_FUNC == import->kind); + + imported_func_interp = module_interp->import_functions + func_idx_rt; + bh_assert(imported_func_interp); + + cloned = wasm_func_copy(import); + if (!cloned || !bh_vector_append((Vector *)inst->imports, &cloned)) { + return false; + } + + /* add native_func_trampoline as a NativeSymbol */ + imported_func_interp->u.function.call_conv_raw = true; + imported_func_interp->u.function.attachment = cloned; + imported_func_interp->u.function.func_ptr_linked = native_func_trampoline; + import->func_idx_rt = func_idx_rt; + + return true; +} + +static bool +interp_link_global(const WASMModule *module_interp, + uint16 global_idx_rt, + wasm_global_t *import) +{ + WASMImport *imported_global_interp = NULL; + + bh_assert(module_interp && import); + bh_assert(global_idx_rt < module_interp->import_global_count); + bh_assert(WASM_EXTERN_GLOBAL == import->kind); + + imported_global_interp = module_interp->import_globals + global_idx_rt; + bh_assert(imported_global_interp); + + /* set init value */ + switch (wasm_valtype_kind(import->type->val_type)) { + case WASM_I32: + bh_assert(VALUE_TYPE_I32 == imported_global_interp->u.global.type); + imported_global_interp->u.global.global_data_linked.i32 = + import->init->of.i32; + break; + case WASM_I64: + bh_assert(VALUE_TYPE_I64 == imported_global_interp->u.global.type); + imported_global_interp->u.global.global_data_linked.i64 = + import->init->of.i64; + break; + case WASM_F32: + bh_assert(VALUE_TYPE_F32 == imported_global_interp->u.global.type); + imported_global_interp->u.global.global_data_linked.f32 = + import->init->of.f32; + break; + case WASM_F64: + bh_assert(VALUE_TYPE_F64 == imported_global_interp->u.global.type); + imported_global_interp->u.global.global_data_linked.f64 = + import->init->of.f64; + break; + default: + return false; + } + + import->global_idx_rt = global_idx_rt; + return true; +} + +static bool +interp_link_memory(const WASMModule *module_interp, + uint16 memory_inst_index, + wasm_memory_t *import) +{ + return false; +} + +static bool +interp_link_table(const WASMModule *module_interp, + uint16 table_inst_index, + wasm_table_t *import) +{ + return false; +} + +static uint32 +interp_link(const wasm_instance_t *inst, + const WASMModule *module_interp, + wasm_extern_t *imports[]) +{ + uint32 i = 0; + uint32 import_func_i = 0; + uint32 import_global_i = 0; + uint32 import_table_i = 0; + uint32 import_memory_i = 0; + wasm_func_t *func = NULL; + wasm_global_t *global = NULL; + + bh_assert(inst && module_interp && imports); + + for (i = 0; i < module_interp->import_count; ++i) { + wasm_extern_t *import = imports[i]; + WASMImport *import_rt = module_interp->imports + i; + + switch (import_rt->kind) { + case IMPORT_KIND_FUNC: + func = wasm_extern_as_func(import); + if (!interp_link_func(inst, module_interp, import_func_i++, + func)) { + goto failed; + } + + break; + case IMPORT_KIND_GLOBAL: + global = wasm_extern_as_global(import); + if (!interp_link_global(module_interp, import_global_i++, + global)) { + goto failed; + } + + break; + case IMPORT_KIND_MEMORY: + if (!interp_link_memory(module_interp, import_memory_i++, + wasm_extern_as_memory(import))) { + goto failed; + }; + break; + case IMPORT_KIND_TABLE: + if (!interp_link_table(module_interp, import_table_i++, + wasm_extern_as_table(import))) { + goto failed; + } + break; + default: + NOT_REACHED(); + break; + } + } + + return module_interp->import_count; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return (uint32)-1; +} + +static bool +interp_process_export(wasm_store_t *store, + const WASMModuleInstance *inst_interp, + wasm_extern_vec_t *externals) +{ + WASMExport *exports = NULL; + WASMExport *export = NULL; + wasm_func_t *func = NULL; + wasm_global_t *global = NULL; + wasm_extern_t *external = NULL; + uint32 export_cnt = 0; + uint32 i = 0; + + bh_assert(store && inst_interp && externals); + + exports = inst_interp->module->exports; + export_cnt = inst_interp->module->export_count; + + for (i = 0; i < export_cnt; ++i) { + export = exports + i; + + switch (export->kind) { + case EXPORT_KIND_FUNC: + func = wasm_func_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp); + if (!func) { + goto failed; + } + + external = wasm_func_as_extern(func); + break; + case EXPORT_KIND_GLOBAL: + global = wasm_global_new_internal( + store, export->index, + (WASMModuleInstanceCommon *)inst_interp); + if (!global) { + goto failed; + } + + external = wasm_global_as_extern(global); + break; + // TODO: + case EXPORT_KIND_MEMORY: + case EXPORT_KIND_TABLE: + break; + default: + goto failed; + } + + if (!bh_vector_append((Vector *)externals, &external)) { + goto failed; + } + } + + return true; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return false; +} +#endif /* WASM_ENABLE_INTERP */ + +#if WASM_ENABLE_AOT != 0 +static bool +aot_link_func(const wasm_instance_t *inst, + const AOTModule *module_aot, + uint32 import_func_idx_rt, + wasm_func_t *import) +{ + AOTImportFunc *import_aot_func = NULL; + wasm_func_t *cloned = NULL; + + bh_assert(inst && module_aot && import); + + import_aot_func = module_aot->import_funcs + import_func_idx_rt; + bh_assert(import_aot_func); + + cloned = wasm_func_copy(import); + if (!cloned || !bh_vector_append((Vector *)inst->imports, &cloned)) { + return false; + } + + import_aot_func->call_conv_raw = true; + import_aot_func->attachment = wasm_func_copy(import); + import_aot_func->func_ptr_linked = native_func_trampoline; + import->func_idx_rt = import_func_idx_rt; + + return true; +} + +static bool +aot_link_global(const AOTModule *module_aot, + uint16 global_idx_rt, + wasm_global_t *import) +{ + AOTImportGlobal *import_aot_global = NULL; + const wasm_valtype_t *val_type = NULL; + + bh_assert(module_aot && import); + + import_aot_global = module_aot->import_globals + global_idx_rt; + bh_assert(import_aot_global); + + val_type = wasm_globaltype_content(wasm_global_type(import)); + bh_assert(val_type); + + switch (wasm_valtype_kind(val_type)) { + case WASM_I32: + bh_assert(VALUE_TYPE_I32 == import_aot_global->type); + import_aot_global->global_data_linked.i32 = import->init->of.i32; + break; + case WASM_I64: + bh_assert(VALUE_TYPE_I64 == import_aot_global->type); + import_aot_global->global_data_linked.i64 = import->init->of.i64; + break; + case WASM_F32: + bh_assert(VALUE_TYPE_F32 == import_aot_global->type); + import_aot_global->global_data_linked.f32 = import->init->of.f32; + break; + case WASM_F64: + bh_assert(VALUE_TYPE_F64 == import_aot_global->type); + import_aot_global->global_data_linked.f64 = import->init->of.f64; + break; + default: + goto failed; + } + + import->global_idx_rt = global_idx_rt; + return true; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return false; +} + +static uint32 +aot_link(const wasm_instance_t *inst, + const AOTModule *module_aot, + wasm_extern_t *imports[]) +{ + uint32 i = 0; + uint32 import_func_i = 0; + uint32 import_global_i = 0; + wasm_extern_t *import = NULL; + wasm_func_t *func = NULL; + wasm_global_t *global = NULL; + + bh_assert(inst && module_aot && imports); + + while (import_func_i < module_aot->import_func_count + || import_global_i < module_aot->import_global_count) { + import = imports[i++]; + + bh_assert(import); + + switch (wasm_extern_kind(import)) { + case WASM_EXTERN_FUNC: + bh_assert(import_func_i < module_aot->import_func_count); + func = wasm_extern_as_func((wasm_extern_t *)import); + if (!aot_link_func(inst, module_aot, import_func_i++, func)) { + goto failed; + } + + break; + case WASM_EXTERN_GLOBAL: + bh_assert(import_global_i < module_aot->import_global_count); + global = wasm_extern_as_global((wasm_extern_t *)import); + if (!aot_link_global(module_aot, import_global_i++, global)) { + goto failed; + } + + break; + case WASM_EXTERN_MEMORY: + break; + case WASM_EXTERN_TABLE: + break; + default: + goto failed; + } + } + + return i; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return (uint32)-1; +} + +static bool +aot_process_export(wasm_store_t *store, + const AOTModuleInstance *inst_aot, + wasm_extern_vec_t *externals) +{ + uint32 i = 0; + uint32 export_func_i = 0; + wasm_extern_t *external = NULL; + AOTModule *module_aot = NULL; + + bh_assert(store && inst_aot && externals); + + module_aot = (AOTModule *)inst_aot->aot_module.ptr; + bh_assert(module_aot); + + for (i = 0; i < module_aot->export_count; ++i) { + AOTExport *export = module_aot->exports + i; + wasm_func_t *func = NULL; + wasm_global_t *global = NULL; + + switch (export->kind) { + case EXPORT_KIND_FUNC: + func = + wasm_func_new_internal(store, export_func_i++, + (WASMModuleInstanceCommon *)inst_aot); + if (!func) { + goto failed; + } + + external = wasm_func_as_extern(func); + break; + case EXPORT_KIND_GLOBAL: + global = wasm_global_new_internal( + store, export->index, (WASMModuleInstanceCommon *)inst_aot); + if (!global) { + goto failed; + } + + external = wasm_global_as_extern(global); + break; + case EXPORT_KIND_MEMORY: + case EXPORT_KIND_TABLE: + break; + default: + NOT_REACHED(); + goto failed; + } + + if (!bh_vector_append((Vector *)externals, &external)) { + goto failed; + } + } + + return true; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + return false; +} +#endif /* WASM_ENABLE_AOT */ + +wasm_instance_t * +wasm_instance_new(wasm_store_t *store, + const wasm_module_t *module, + const wasm_extern_t *const imports[], + wasm_trap_t **traps) +{ + char error[128] = { 0 }; + const uint32 stack_size = 16 * 1024; + const uint32 heap_size = 16 * 1024; + uint32 import_count = 0; + wasm_instance_t *instance = NULL; + uint32 i = 0; + (void)traps; + + check_engine_and_store(singleton_engine, store); + + instance = malloc_internal(sizeof(wasm_instance_t)); + if (!instance) { + goto failed; + } + + /* link module and imports */ + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + import_count = ((WASMModule *)*module)->import_count; + INIT_VEC(instance->imports, wasm_extern_vec, import_count); + if (!instance->imports) { + goto failed; + } + + import_count = interp_link(instance, (WASMModule *)*module, + (wasm_extern_t **)imports); +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + import_count = ((AOTModule *)*module)->import_func_count + + ((AOTModule *)*module)->import_global_count + + ((AOTModule *)*module)->import_memory_count + + ((AOTModule *)*module)->import_table_count; + INIT_VEC(instance->imports, wasm_extern_vec, import_count); + if (!instance->imports) { + goto failed; + } + + import_count = + aot_link(instance, (AOTModule *)*module, (wasm_extern_t **)imports); +#endif + } + if ((int32)import_count < 0) { + goto failed; + } + + instance->inst_comm_rt = wasm_runtime_instantiate( + *module, stack_size, heap_size, error, sizeof(error)); + if (!instance->inst_comm_rt) { + LOG_ERROR(error); + goto failed; + } + + /* fill in inst */ + for (i = 0; i < (uint32)import_count; ++i) { + wasm_extern_t *import = (wasm_extern_t *)imports[i]; + switch (import->kind) { + case WASM_EXTERN_FUNC: + wasm_extern_as_func(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + case WASM_EXTERN_GLOBAL: + wasm_extern_as_global(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + case WASM_EXTERN_MEMORY: + wasm_extern_as_memory(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + case WASM_EXTERN_TABLE: + wasm_extern_as_table(import)->inst_comm_rt = + instance->inst_comm_rt; + break; + default: + goto failed; + } + } + + /* build the exports list */ + if (INTERP_MODE == current_runtime_mode()) { +#if WASM_ENABLE_INTERP != 0 + uint32 export_cnt = + ((WASMModuleInstance *)instance->inst_comm_rt)->module->export_count; + + INIT_VEC(instance->exports, wasm_extern_vec, export_cnt); + + if (!interp_process_export( + store, (WASMModuleInstance *)instance->inst_comm_rt, + instance->exports)) { + goto failed; + } +#endif + } + else { +#if WASM_ENABLE_AOT != 0 + uint32 export_cnt = + ((AOTModuleInstance *)instance->inst_comm_rt)->export_func_count; + + INIT_VEC(instance->exports, wasm_extern_vec, export_cnt); + + if (!aot_process_export(store, + (AOTModuleInstance *)instance->inst_comm_rt, + instance->exports)) { + goto failed; + } +#endif + } + + /* add it to a watching list in store */ + if (!bh_vector_append((Vector *)store->instances, &instance)) { + goto failed; + } + + return instance; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_instance_delete_internal(instance); + return NULL; +} + +void +wasm_instance_delete_internal(wasm_instance_t *instance) +{ + if (!instance) { + return; + } + + DEINIT_VEC(instance->imports, wasm_extern_vec_delete); + DEINIT_VEC(instance->exports, wasm_extern_vec_delete); + + if (instance->inst_comm_rt) { + wasm_runtime_deinstantiate(instance->inst_comm_rt); + instance->inst_comm_rt = NULL; + } + wasm_runtime_free(instance); +} + +/* will release instance when releasing the store */ +void +wasm_instance_delete(wasm_instance_t *module) +{ + /* pass */ +} + +void +wasm_instance_exports(const wasm_instance_t *instance, wasm_extern_vec_t *out) +{ + bh_assert(instance && out); + wasm_extern_vec_copy(out, instance->exports); +} + +void +wasm_instance_vec_new_uninitialized(wasm_instance_vec_t *out, size_t size) +{ + generic_vec_init_data((Vector *)out, size, sizeof(wasm_instance_t)); +} + +void +wasm_instance_vec_delete(wasm_instance_vec_t *instance_vec) +{ + size_t i = 0; + if (!instance_vec || !instance_vec->data) { + return; + } + + FREE_VEC_ELEMS(instance_vec, wasm_instance_delete_internal); + bh_vector_destroy((Vector *)instance_vec); +} + +wasm_extern_t * +wasm_extern_copy(const wasm_extern_t *src) +{ + wasm_extern_t *dst = NULL; + wasm_func_t *func = NULL; + wasm_global_t *global = NULL; + bh_assert(src); + + switch (wasm_extern_kind(src)) { + case WASM_EXTERN_FUNC: + func = wasm_func_copy(wasm_extern_as_func_const(src)); + dst = wasm_func_as_extern(func); + break; + case WASM_EXTERN_GLOBAL: + global = wasm_global_copy(wasm_extern_as_global_const(src)); + dst = wasm_global_as_extern(global); + break; + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + default: + break; + } + + if (!dst) { + goto failed; + } + + return dst; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_extern_delete(dst); + return NULL; +} + +void +wasm_extern_delete(wasm_extern_t *external) +{ + if (!external) { + return; + } + + switch (wasm_extern_kind(external)) { + case WASM_EXTERN_FUNC: + wasm_func_delete(wasm_extern_as_func(external)); + break; + case WASM_EXTERN_GLOBAL: + wasm_global_delete(wasm_extern_as_global(external)); + break; + case WASM_EXTERN_MEMORY: + case WASM_EXTERN_TABLE: + default: + break; + } +} + +wasm_externkind_t +wasm_extern_kind(const wasm_extern_t *extrenal) +{ + return extrenal->kind; +} + +wasm_func_t * +wasm_extern_as_func(wasm_extern_t *external) +{ + return (wasm_func_t *)external; +} + +const wasm_func_t * +wasm_extern_as_func_const(const wasm_extern_t *external) +{ + return (const wasm_func_t *)external; +} + +wasm_global_t * +wasm_extern_as_global(wasm_extern_t *external) +{ + return (wasm_global_t *)external; +} + +const wasm_global_t * +wasm_extern_as_global_const(const wasm_extern_t *external) +{ + return (const wasm_global_t *)external; +} + +wasm_memory_t * +wasm_extern_as_memory(wasm_extern_t *external) +{ + return (wasm_memory_t *)external; +} + +const wasm_memory_t * +wasm_extern_as_memory_const(const wasm_extern_t *external) +{ + return (const wasm_memory_t *)external; +} + +wasm_table_t * +wasm_extern_as_table(wasm_extern_t *external) +{ + return (wasm_table_t *)external; +} + +const wasm_table_t * +wasm_extern_as_table_const(const wasm_extern_t *external) +{ + return (const wasm_table_t *)external; +} + +void +wasm_extern_vec_copy(wasm_extern_vec_t *out, const wasm_extern_vec_t *src) +{ + size_t i = 0; + bh_assert(out && src); + + generic_vec_init_data((Vector *)out, src->size, src->size_of_elem); + if (!out->data) { + goto failed; + } + + for (i = 0; i < src->num_elems; ++i) { + wasm_extern_t *orig = *(src->data + i); + wasm_extern_t *cloned = wasm_extern_copy(orig); + if (!cloned) { + goto failed; + } + + if (!bh_vector_append((Vector *)out, &cloned)) { + goto failed; + } + } + + return; + +failed: + LOG_DEBUG("%s failed", __FUNCTION__); + wasm_extern_vec_delete(out); + return; +} + +void +wasm_extern_vec_new_uninitialized(wasm_extern_vec_t *out, size_t size) +{ + generic_vec_init_data((Vector *)out, size, sizeof(wasm_extern_t *)); +} + +void +wasm_extern_vec_delete(wasm_extern_vec_t *extern_vec) +{ + size_t i = 0; + if (!extern_vec || !extern_vec->data) { + return; + } + + FREE_VEC_ELEMS(extern_vec, wasm_extern_delete); + bh_vector_destroy((Vector *)extern_vec); +} diff --git a/wamr/core/iwasm/common/wasm_c_api_internal.h b/wamr/core/iwasm/common/wasm_c_api_internal.h new file mode 100644 index 0000000..40d049a --- /dev/null +++ b/wamr/core/iwasm/common/wasm_c_api_internal.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_C_API_INTERNAL_H +#define _WASM_C_API_INTERNAL_H + +#include "wasm_c_api.h" +#include "wasm_runtime_common.h" + +#ifndef own +#define own +#endif + +/* Vectors */ +/* we will malloc resource for the vector's data field */ +/* we will release resource of data */ +/* caller needs to take care resource for the vector itself */ +#define DEFAULT_VECTOR_INIT_LENGTH (64) + +WASM_DECLARE_VEC(store, *) +WASM_DECLARE_VEC(module, *) +WASM_DECLARE_VEC(instance, *) + +/* Runtime Environment */ +typedef enum runtime_mode_e { + INTERP_MODE = 0, + JIT_MODE, + AOT_MODE +} runtime_mode_e; + +struct wasm_engine_t { + // support one store for now + wasm_store_vec_t *stores; + // Interpreter by deault + runtime_mode_e mode; +}; + +struct wasm_store_t { + wasm_module_vec_t *modules; + wasm_instance_vec_t *instances; +}; + +/* Type Representations */ +struct wasm_valtype_t { + wasm_valkind_t kind; +}; + +struct wasm_functype_t { + uint32 extern_kind; + // gona to new and delete own + wasm_valtype_vec_t *params; + wasm_valtype_vec_t *results; +}; + +struct wasm_globaltype_t { + uint32 extern_kind; + // gona to new and delete own + wasm_valtype_t *val_type; + wasm_mutability_t mutability; +}; + +struct wasm_tabletype_t { + uint32 extern_kind; + // always be WASM_FUNCREF + wasm_valtype_t *type; + wasm_limits_t *limits; +}; + +struct wasm_memorytype_t { + uint32 extern_kind; + wasm_limits_t *limits; +}; + +struct wasm_externtype_t { + uint32 extern_kind; + uint8 data[1]; +}; + +struct wasm_import_type_t { + uint32 extern_kind; + wasm_name_t *module_name; + wasm_name_t *name; +}; + +struct wasm_export_type_t { + uint32 extern_kind; + wasm_name_t *module_name; + wasm_name_t *name; +}; + +/* Runtime Objects */ +struct wasm_ref_t { + uint32 obj; +}; + +struct wasm_trap_t { + wasm_byte_vec_t *message; +}; + +struct wasm_func_t { + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + wasm_functype_t *func_type; + + bool with_env; + union { + wasm_func_callback_t cb; + struct callback_ext { + void *env; + wasm_func_callback_with_env_t cb; + void (*finalizer)(void *); + } cb_env; + } u; + /* + * an index in both functions runtime instance lists + * of interpreter mode and aot mode + */ + uint16 func_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_global_t { + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + wasm_globaltype_t *type; + wasm_val_t *init; + /* + * an index in both global runtime instance lists + * of interpreter mode and aot mode + */ + uint16 global_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_memory_t { + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + wasm_memorytype_t *type; + /* + * an index in both memory runtime instance lists + * of interpreter mode and aot mode + */ + uint16 memory_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_table_t { + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + + wasm_tabletype_t *type; + /* + * an index in both table runtime instance lists + * of interpreter mode and aot mode + */ + uint16 table_idx_rt; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +struct wasm_extern_t { + wasm_name_t *module_name; + wasm_name_t *name; + uint16 kind; + uint8 data[1]; +}; + +struct wasm_instance_t { + wasm_extern_vec_t *imports; + wasm_extern_vec_t *exports; + WASMModuleInstanceCommon *inst_comm_rt; +}; + +#endif /* _WASM_C_API_INTERNAL_H */ diff --git a/wamr/core/iwasm/common/wasm_exec_env.c b/wamr/core/iwasm/common/wasm_exec_env.c new file mode 100644 index 0000000..631d449 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_exec_env.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_exec_env.h" +#include "wasm_runtime_common.h" + +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif + +WASMExecEnv * +wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size) +{ + uint64 total_size = offsetof(WASMExecEnv, wasm_stack.s.bottom) + + (uint64)stack_size; + WASMExecEnv *exec_env; + + if (total_size >= UINT32_MAX + || !(exec_env = wasm_runtime_malloc((uint32)total_size))) + return NULL; + + memset(exec_env, 0, (uint32)total_size); + +#if WASM_ENABLE_AOT != 0 + if (!(exec_env->argv_buf = wasm_runtime_malloc(sizeof(uint32) * 64))) { + goto fail1; + } +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 + if (os_mutex_init(&exec_env->wait_lock) != 0) + goto fail2; + + if (os_cond_init(&exec_env->wait_cond) != 0) + goto fail3; +#endif + + exec_env->module_inst = module_inst; + exec_env->wasm_stack_size = stack_size; + exec_env->wasm_stack.s.top_boundary = + exec_env->wasm_stack.s.bottom + stack_size; + exec_env->wasm_stack.s.top = exec_env->wasm_stack.s.bottom; + return exec_env; + +#if WASM_ENABLE_THREAD_MGR != 0 +fail3: + os_mutex_destroy(&exec_env->wait_lock); +fail2: +#endif +#if WASM_ENABLE_AOT != 0 + wasm_runtime_free(exec_env->argv_buf); +fail1: +#endif + wasm_runtime_free(exec_env); + return NULL; +} + +void +wasm_exec_env_destroy_internal(WASMExecEnv *exec_env) +{ +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMJmpBuf *jmpbuf = exec_env->jmpbuf_stack_top; + WASMJmpBuf *jmpbuf_prev; + while (jmpbuf) { + jmpbuf_prev = jmpbuf->prev; + wasm_runtime_free(jmpbuf); + jmpbuf = jmpbuf_prev; + } +#endif +#if WASM_ENABLE_THREAD_MGR != 0 + os_mutex_destroy(&exec_env->wait_lock); + os_cond_destroy(&exec_env->wait_cond); +#endif +#if WASM_ENABLE_AOT != 0 + wasm_runtime_free(exec_env->argv_buf); +#endif + wasm_runtime_free(exec_env); +} + +WASMExecEnv * +wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size) +{ + WASMExecEnv *exec_env = + wasm_exec_env_create_internal(module_inst, stack_size); + + if (!exec_env) + return NULL; + + /* Set the aux_stack_boundary to 0 */ + exec_env->aux_stack_boundary = 0; +#if WASM_ENABLE_THREAD_MGR != 0 + WASMCluster *cluster; + + /* Create a new cluster for this exec_env */ + cluster = wasm_cluster_create(exec_env); + if (!cluster) { + wasm_exec_env_destroy_internal(exec_env); + return NULL; + } +#endif + return exec_env; +} + +void +wasm_exec_env_destroy(WASMExecEnv *exec_env) +{ +#if WASM_ENABLE_THREAD_MGR != 0 + /* Terminate all sub-threads */ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + if (cluster) { + wasm_cluster_terminate_all_except_self(cluster, exec_env); + wasm_cluster_del_exec_env(cluster, exec_env); + } +#endif + wasm_exec_env_destroy_internal(exec_env); +} + +WASMModuleInstanceCommon * +wasm_exec_env_get_module_inst(WASMExecEnv *exec_env) +{ + return exec_env->module_inst; +} + +void +wasm_exec_env_set_thread_info(WASMExecEnv *exec_env) +{ + exec_env->handle = os_self_thread(); + exec_env->native_stack_boundary = os_thread_get_stack_boundary() + + RESERVED_BYTES_TO_NATIVE_STACK_BOUNDARY; +} + +#if WASM_ENABLE_THREAD_MGR != 0 +void * +wasm_exec_env_get_thread_arg(WASMExecEnv *exec_env) +{ + return exec_env->thread_arg; +} + +void +wasm_exec_env_set_thread_arg(WASMExecEnv *exec_env, void *thread_arg) +{ + exec_env->thread_arg = thread_arg; +} +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +void +wasm_exec_env_push_jmpbuf(WASMExecEnv *exec_env, WASMJmpBuf *jmpbuf) +{ + jmpbuf->prev = exec_env->jmpbuf_stack_top; + exec_env->jmpbuf_stack_top = jmpbuf; +} + +WASMJmpBuf * +wasm_exec_env_pop_jmpbuf(WASMExecEnv *exec_env) +{ + WASMJmpBuf *stack_top = exec_env->jmpbuf_stack_top; + + if (stack_top) { + exec_env->jmpbuf_stack_top = stack_top->prev; + return stack_top; + } + + return NULL; +} +#endif + diff --git a/wamr/core/iwasm/common/wasm_exec_env.h b/wamr/core/iwasm/common/wasm_exec_env.h new file mode 100644 index 0000000..6256763 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_exec_env.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_EXEC_ENV_H +#define _WASM_EXEC_ENV_H + +#include "bh_assert.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct WASMModuleInstanceCommon; +struct WASMInterpFrame; + +#if WASM_ENABLE_THREAD_MGR != 0 +typedef struct WASMCluster WASMCluster; +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +typedef struct WASMJmpBuf { + struct WASMJmpBuf *prev; + korp_jmpbuf jmpbuf; +} WASMJmpBuf; +#endif + +/* Execution environment */ +typedef struct WASMExecEnv { + /* Next thread's exec env of a WASM module instance. */ + struct WASMExecEnv *next; + + /* Previous thread's exec env of a WASM module instance. */ + struct WASMExecEnv *prev; + + /* Note: field module_inst, argv_buf and native_stack_boundary + are used by AOTed code, don't change the places of them */ + + /* The WASM module instance of current thread */ + struct WASMModuleInstanceCommon *module_inst; + +#if WASM_ENABLE_AOT != 0 + uint32 *argv_buf; +#endif + + /* The boundary of native stack. When runtime detects that native + frame may overrun this boundary, it throws stack overflow + exception. */ + uint8 *native_stack_boundary; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Used to terminate or suspend the interpreter + bit 0: need terminate + bit 1: need suspend + bit 2: need to go into breakpoint + bit 3: return from pthread_exit */ + union { + uint32 flags; + uintptr_t __padding__; + } suspend_flags; + + /* thread return value */ + void *thread_ret_value; + + /* Must be provided by thread library */ + void* (*thread_start_routine)(void *); + void *thread_arg; + + /* pointer to the cluster */ + WASMCluster *cluster; + + /* used to support debugger */ + korp_mutex wait_lock; + korp_cond wait_cond; +#endif + + /* Aux stack boundary */ + uint32 aux_stack_boundary; + + /* attachment for native function */ + void *attachment; + + void *user_data; + + /* Current interpreter frame of current thread */ + struct WASMInterpFrame *cur_frame; + + /* The native thread handle of current thread */ + korp_tid handle; + +#if WASM_ENABLE_INTERP != 0 + BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK + WASMJmpBuf *jmpbuf_stack_top; +#endif + + /* The WASM stack size */ + uint32 wasm_stack_size; + + /* The WASM stack of current thread */ + union { + uint64 __make_it_8_byte_aligned_; + + struct { + /* The top boundary of the stack. */ + uint8 *top_boundary; + + /* Top cell index which is free. */ + uint8 *top; + + /* The WASM stack. */ + uint8 bottom[1]; + } s; + } wasm_stack; +} WASMExecEnv; + +WASMExecEnv * +wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size); + +void +wasm_exec_env_destroy_internal(WASMExecEnv *exec_env); + +WASMExecEnv * +wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst, + uint32 stack_size); + +void +wasm_exec_env_destroy(WASMExecEnv *exec_env); + +/** + * Allocate a WASM frame from the WASM stack. + * + * @param exec_env the current execution environment + * @param size size of the WASM frame, it must be a multiple of 4 + * + * @return the WASM frame if there is enough space in the stack area + * with a protection area, NULL otherwise + */ +static inline void * +wasm_exec_env_alloc_wasm_frame(WASMExecEnv *exec_env, unsigned size) +{ + uint8 *addr = exec_env->wasm_stack.s.top; + + bh_assert(!(size & 3)); + + /* The outs area size cannot be larger than the frame size, so + multiplying by 2 is enough. */ + if (addr + size * 2 > exec_env->wasm_stack.s.top_boundary) { + /* WASM stack overflow. */ + /* When throwing SOE, the preserved space must be enough. */ + /* bh_assert(!exec_env->throwing_soe);*/ + return NULL; + } + + exec_env->wasm_stack.s.top += size; + + return addr; +} + +static inline void +wasm_exec_env_free_wasm_frame(WASMExecEnv *exec_env, void *prev_top) +{ + bh_assert((uint8 *)prev_top >= exec_env->wasm_stack.s.bottom); + exec_env->wasm_stack.s.top = (uint8 *)prev_top; +} + +/** + * Get the current WASM stack top pointer. + * + * @param exec_env the current execution environment + * + * @return the current WASM stack top pointer + */ +static inline void* +wasm_exec_env_wasm_stack_top(WASMExecEnv *exec_env) +{ + return exec_env->wasm_stack.s.top; +} + +/** + * Set the current frame pointer. + * + * @param exec_env the current execution environment + * @param frame the WASM frame to be set for the current exec env + */ +static inline void +wasm_exec_env_set_cur_frame(WASMExecEnv *exec_env, + struct WASMInterpFrame *frame) +{ + exec_env->cur_frame = frame; +} + +/** + * Get the current frame pointer. + * + * @param exec_env the current execution environment + * + * @return the current frame pointer + */ +static inline struct WASMInterpFrame* +wasm_exec_env_get_cur_frame(WASMExecEnv *exec_env) +{ + return exec_env->cur_frame; +} + +struct WASMModuleInstanceCommon * +wasm_exec_env_get_module_inst(WASMExecEnv *exec_env); + +void +wasm_exec_env_set_thread_info(WASMExecEnv *exec_env); + + +#if WASM_ENABLE_THREAD_MGR != 0 +void * +wasm_exec_env_get_thread_arg(WASMExecEnv *exec_env); + +void +wasm_exec_env_set_thread_arg(WASMExecEnv *exec_env, void *thread_arg); +#endif + +#ifdef OS_ENABLE_HW_BOUND_CHECK +void +wasm_exec_env_push_jmpbuf(WASMExecEnv *exec_env, WASMJmpBuf *jmpbuf); + +WASMJmpBuf * +wasm_exec_env_pop_jmpbuf(WASMExecEnv *exec_env); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_EXEC_ENV_H */ diff --git a/wamr/core/iwasm/common/wasm_memory.c b/wamr/core/iwasm/common/wasm_memory.c new file mode 100644 index 0000000..9032bc0 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_memory.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_runtime_common.h" +#include "bh_platform.h" +#include "mem_alloc.h" + +#if BH_ENABLE_MEMORY_PROFILING != 0 + +/* Memory profile data of a function */ +typedef struct memory_profile { + struct memory_profile *next; + const char *function_name; + const char *file_name; + int line_in_file; + int malloc_num; + int free_num; + int total_malloc; + int total_free; +} memory_profile_t; + +/* Memory in use which grows when BH_MALLOC was called + * and decreases when bh_free was called */ +static unsigned int memory_in_use = 0; + +/* Memory profile data list */ +static memory_profile_t *memory_profiles_list = NULL; + +/* Lock of the memory profile list */ +static korp_mutex profile_lock; +#endif /* end of BH_ENABLE_MEMORY_PROFILING */ + +#ifndef MALLOC_MEMORY_FROM_SYSTEM + +typedef enum Memory_Mode { + MEMORY_MODE_UNKNOWN = 0, + MEMORY_MODE_POOL, + MEMORY_MODE_ALLOCATOR +} Memory_Mode; + +static Memory_Mode memory_mode = MEMORY_MODE_UNKNOWN; + +static mem_allocator_t pool_allocator = NULL; + +static void *(*malloc_func)(unsigned int size) = NULL; +static void *(*realloc_func)(void *ptr, unsigned int size) = NULL; +static void (*free_func)(void *ptr) = NULL; + +static unsigned int global_pool_size; + +static bool +wasm_memory_init_with_pool(void *mem, unsigned int bytes) +{ + mem_allocator_t _allocator = mem_allocator_create(mem, bytes); + + if (_allocator) { + memory_mode = MEMORY_MODE_POOL; + pool_allocator = _allocator; +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_mutex_init(&profile_lock); +#endif + global_pool_size = bytes; + return true; + } + LOG_ERROR("Init memory with pool (%p, %u) failed.\n", mem, bytes); + return false; +} + +static bool +wasm_memory_init_with_allocator(void *_malloc_func, + void *_realloc_func, + void *_free_func) +{ + if (_malloc_func && _free_func && _malloc_func != _free_func) { + memory_mode = MEMORY_MODE_ALLOCATOR; + malloc_func = _malloc_func; + realloc_func = _realloc_func; + free_func = _free_func; +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_mutex_init(&profile_lock); +#endif + return true; + } + LOG_ERROR("Init memory with allocator (%p, %p, %p) failed.\n", + _malloc_func, _realloc_func, _free_func); + return false; +} + +bool +wasm_runtime_memory_init(mem_alloc_type_t mem_alloc_type, + const MemAllocOption *alloc_option) +{ + if (mem_alloc_type == Alloc_With_Pool) + return wasm_memory_init_with_pool(alloc_option->pool.heap_buf, + alloc_option->pool.heap_size); + else if (mem_alloc_type == Alloc_With_Allocator) + return wasm_memory_init_with_allocator(alloc_option->allocator.malloc_func, + alloc_option->allocator.realloc_func, + alloc_option->allocator.free_func); + else if (mem_alloc_type == Alloc_With_System_Allocator) + return wasm_memory_init_with_allocator(os_malloc, os_realloc, os_free); + else + return false; +} + +void +wasm_runtime_memory_destroy() +{ +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_mutex_destroy(&profile_lock); +#endif + if (memory_mode == MEMORY_MODE_POOL) + mem_allocator_destroy(pool_allocator); + memory_mode = MEMORY_MODE_UNKNOWN; +} + +unsigned +wasm_runtime_memory_pool_size() +{ + if (memory_mode == MEMORY_MODE_POOL) + return global_pool_size; + else + return 1 * BH_GB; +} + +void * +wasm_runtime_malloc(unsigned int size) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING("wasm_runtime_malloc failed: memory hasn't been initialize.\n"); + return NULL; + } else if (memory_mode == MEMORY_MODE_POOL) { + return mem_allocator_malloc(pool_allocator, size); + } else { + return malloc_func(size); + } +} + +void * +wasm_runtime_realloc(void *ptr, unsigned int size) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING("wasm_runtime_realloc failed: memory hasn't been initialize.\n"); + return NULL; + } else if (memory_mode == MEMORY_MODE_POOL) { + return mem_allocator_realloc(pool_allocator, ptr, size); + } else { + if (realloc_func) + return realloc_func(ptr, size); + else + return NULL; + } +} + +void +wasm_runtime_free(void *ptr) +{ + if (memory_mode == MEMORY_MODE_UNKNOWN) { + LOG_WARNING("wasm_runtime_free failed: memory hasn't been initialize.\n"); + } else if (memory_mode == MEMORY_MODE_POOL) { + mem_allocator_free(pool_allocator, ptr); + } else { + free_func(ptr); + } +} + +#if BH_ENABLE_MEMORY_PROFILING != 0 + +void +memory_profile_print(const char *file, int line, + const char *func, int alloc) +{ + os_printf("location:%s@%d:used:%d:contribution:%d\n", + func, line, memory_in_use, alloc); +} + +void * +wasm_runtime_malloc_profile(const char *file, int line, + const char *func, unsigned int size) +{ + void *p = wasm_runtime_malloc(size + 8); + + if (p) { + memory_profile_t *profile; + + os_mutex_lock(&profile_lock); + + profile = memory_profiles_list; + while (profile) { + if (strcmp(profile->function_name, func) == 0 + && strcmp(profile->file_name, file) == 0) { + break; + } + profile = profile->next; + } + + if (profile) { + profile->total_malloc += size;/* TODO: overflow check */ + profile->malloc_num++; + } else { + profile = wasm_runtime_malloc(sizeof(memory_profile_t)); + if (!profile) { + os_mutex_unlock(&profile_lock); + bh_memcpy_s(p, size + 8, &size, sizeof(size)); + return (char *)p + 8; + } + + memset(profile, 0, sizeof(memory_profile_t)); + profile->file_name = file; + profile->line_in_file = line; + profile->function_name = func; + profile->malloc_num = 1; + profile->total_malloc = size; + profile->next = memory_profiles_list; + memory_profiles_list = profile; + } + + os_mutex_unlock(&profile_lock); + + bh_memcpy_s(p, size + 8, &size, sizeof(size)); + memory_in_use += size; + + memory_profile_print(file, line, func, size); + + return (char *)p + 8; + } + + return NULL; +} + +void +wasm_runtime_free_profile(const char *file, int line, + const char *func, void *ptr) +{ + unsigned int size = *(unsigned int *)((char *)ptr - 8); + memory_profile_t *profile; + + wasm_runtime_free((char *)ptr - 8); + + if (memory_in_use >= size) + memory_in_use -= size; + + os_mutex_lock(&profile_lock); + + profile = memory_profiles_list; + while (profile) { + if (strcmp(profile->function_name, func) == 0 + && strcmp(profile->file_name, file) == 0) { + break; + } + profile = profile->next; + } + + if (profile) { + profile->total_free += size;/* TODO: overflow check */ + profile->free_num++; + } else { + profile = wasm_runtime_malloc(sizeof(memory_profile_t)); + if (!profile) { + os_mutex_unlock(&profile_lock); + return; + } + + memset(profile, 0, sizeof(memory_profile_t)); + profile->file_name = file; + profile->line_in_file = line; + profile->function_name = func; + profile->free_num = 1; + profile->total_free = size; + profile->next = memory_profiles_list; + memory_profiles_list = profile; + } + + os_mutex_unlock(&profile_lock); +} + +/** + * Summarize memory usage and print it out + * Can use awk to analyze the output like below: + * awk -F: '{print $2,$4,$6,$8,$9}' OFS="\t" ./out.txt | sort -n -r -k 1 + */ +void memory_usage_summarize() +{ + memory_profile_t *profile; + + os_mutex_lock(&profile_lock); + + profile = memory_profiles_list; + while (profile) { + os_printf("malloc:%d:malloc_num:%d:free:%d:free_num:%d:%s\n", + profile->total_malloc, + profile->malloc_num, + profile->total_free, + profile->free_num, + profile->function_name); + profile = profile->next; + } + + os_mutex_unlock(&profile_lock); +} + +#endif /* end of BH_ENABLE_MEMORY_PROFILING */ + +#else /* else of MALLOC_MEMORY_FROM_SYSTEM */ + + +void * +wasm_runtime_malloc(unsigned int size) +{ + return malloc(size); +} + +void * +wasm_runtime_realloc(void *ptr, unsigned int size) +{ + return realloc(ptr, size); +} + +void +wasm_runtime_free(void *ptr) +{ + if (ptr) + free(ptr); +} + +#if BH_ENABLE_MEMORY_PROFILING != 0 +void * +wasm_runtime_malloc_profile(const char *file, int line, + const char *func, unsigned int size) +{ + (void)file; + (void)line; + (void)func; + + (void)memory_profiles_list; + (void)profile_lock; + (void)memory_in_use; + + return malloc(size); +} + +void * +wasm_runtime_realloc_profile(const char *file, int line, + const char *func, void *ptr, unsigned int size) +{ + (void)file; + (void)line; + (void)func; + + (void)memory_profiles_list; + (void)profile_lock; + (void)memory_in_use; + + return realloc(ptr, size); +} + +void +wasm_runtime_free_profile(const char *file, int line, + const char *func, void *ptr) +{ + (void)file; + (void)line; + (void)func; + + if (ptr) + free(ptr); +} +#endif /* end of BH_ENABLE_MEMORY_PROFILING */ +#endif /* end of MALLOC_MEMORY_FROM_SYSTEM*/ + diff --git a/wamr/core/iwasm/common/wasm_memory.h b/wamr/core/iwasm/common/wasm_memory.h new file mode 100644 index 0000000..08157ac --- /dev/null +++ b/wamr/core/iwasm/common/wasm_memory.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_MEMORY_H +#define _WASM_MEMORY_H + +#include "bh_common.h" +#include "../include/wasm_export.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +wasm_runtime_memory_init(mem_alloc_type_t mem_alloc_type, + const MemAllocOption *alloc_option); + +void +wasm_runtime_memory_destroy(); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_MEMORY_H */ + diff --git a/wamr/core/iwasm/common/wasm_native.c b/wamr/core/iwasm/common/wasm_native.c new file mode 100644 index 0000000..c900076 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_native.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_native.h" +#include "wasm_runtime_common.h" +#include "bh_log.h" + +#if !defined(BH_PLATFORM_ZEPHYR) && !defined(BH_PLATFORM_ALIOS_THINGS) +#define ENABLE_QUICKSORT 1 +#else +#define ENABLE_QUICKSORT 0 +#endif + +#define ENABLE_SORT_DEBUG 0 + +#if ENABLE_SORT_DEBUG != 0 +#include +#endif + +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); + +#if WASM_ENABLE_SPEC_TEST != 0 +uint32 +get_spectest_export_apis(NativeSymbol **p_libc_builtin_apis); +#endif + +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); + +#if WASM_ENABLE_LIB_PTHREAD != 0 +bool +lib_pthread_init(); + +void +lib_pthread_destroy(); + +uint32 +get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis); +#endif + +static bool +check_symbol_signature(const WASMType *type, const char *signature) +{ + const char *p = signature, *p_end; + char sig_map[] = { 'F', 'f', 'I', 'i' }, sig; + uint32 i = 0; + + if (!p || strlen(p) < 2) + return false; + + p_end = p + strlen(signature); + + if (*p++ != '(') + return false; + + if ((uint32)(p_end - p) < (uint32)(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; +} + +#if ENABLE_QUICKSORT == 0 +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; + } + } + } +} +#else +static void +swap_symbol(NativeSymbol* left, NativeSymbol* right) +{ + NativeSymbol temp = *left; + *left = *right; + *right = temp; +} + +static void +quick_sort_symbols(NativeSymbol* native_symbols, int left, int right) +{ + NativeSymbol base_symbol; + int pin_left = left; + int pin_right = right; + + if (left >= right) { + return; + } + + base_symbol = native_symbols[left]; + while (left < right) { + while (left < right + && strcmp(native_symbols[right].symbol, + base_symbol.symbol) > 0) { + right--; + } + + if (left < right) { + swap_symbol(&native_symbols[left], &native_symbols[right]); + left++; + } + + while (left < right + && strcmp(native_symbols[left].symbol, + base_symbol.symbol) < 0) { + left++; + } + + if (left < right) { + swap_symbol(&native_symbols[left], &native_symbols[right]); + right--; + } + } + native_symbols[left] = base_symbol; + + quick_sort_symbols(native_symbols, pin_left, left - 1); + quick_sort_symbols(native_symbols, left + 1, pin_right); +} +#endif /* end of ENABLE_QUICKSORT */ + +static void * +lookup_symbol(NativeSymbol *native_symbols, uint32 n_native_symbols, + const char *symbol, const char **p_signature, void **p_attachment) +{ + int low = 0, mid, ret; + int high = (int32)n_native_symbols - 1; + + while (low <= high) { + mid = (low + high) / 2; + ret = strcmp(symbol, native_symbols[mid].symbol); + if (ret == 0) { + *p_signature = native_symbols[mid].signature; + *p_attachment = native_symbols[mid].attachment; + return native_symbols[mid].func_ptr; + } + else if (ret < 0) + high = mid - 1; + else + low = mid + 1; + } + + return NULL; +} + +void* +wasm_native_resolve_symbol(const char *module_name, const char *field_name, + const WASMType *func_type, const char **p_signature, + void **p_attachment, bool *p_call_conv_raw) +{ + NativeSymbolsNode *node, *node_next; + const char *signature = NULL; + void *func_ptr = NULL, *attachment; + + 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, &attachment)) + || (field_name[0] == '_' + && (func_ptr = lookup_symbol(node->native_symbols, + node->n_native_symbols, + field_name + 1, + &signature, &attachment)))) + 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)) { +#if WASM_ENABLE_WAMR_COMPILER == 0 /* Output warning except running aot compiler */ + LOG_WARNING("failed to check signature '%s' and resolve " + "pointer params for import function (%s %s)\n", + signature, module_name, field_name); +#endif + return NULL; + } + else + /* Save signature for runtime to do pointer check and + address conversion */ + *p_signature = signature; + } + else + /* signature is empty */ + *p_signature = NULL; + + *p_attachment = attachment; + *p_call_conv_raw = node->call_conv_raw; + } + + return func_ptr; +} + +static bool +register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols, + bool call_conv_raw) +{ + NativeSymbolsNode *node; +#if ENABLE_SORT_DEBUG != 0 + struct timeval start; + struct timeval end; + unsigned long timer; +#endif + + if (!(node = wasm_runtime_malloc(sizeof(NativeSymbolsNode)))) + return false; + + node->module_name = module_name; + node->native_symbols = native_symbols; + node->n_native_symbols = n_native_symbols; + node->call_conv_raw = call_conv_raw; + 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; + } + +#if ENABLE_SORT_DEBUG != 0 + gettimeofday(&start, NULL); +#endif + +#if ENABLE_QUICKSORT == 0 + sort_symbol_ptr(native_symbols, n_native_symbols); +#else + quick_sort_symbols(native_symbols, 0, (int)(n_native_symbols - 1)); +#endif + +#if ENABLE_SORT_DEBUG != 0 + gettimeofday(&end, NULL); + timer = 1000000 * (end.tv_sec - start.tv_sec) + + (end.tv_usec - start.tv_usec); + LOG_ERROR("module_name: %s, nums: %d, sorted used: %ld us", + module_name, n_native_symbols, timer); +#endif + return true; +} + +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return register_natives(module_name, native_symbols, n_native_symbols, false); +} + +bool +wasm_native_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return register_natives(module_name, native_symbols, n_native_symbols, 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; +#endif /* WASM_ENABLE_LIBC_BUILTIN */ + +#if WASM_ENABLE_SPEC_TEST + n_native_symbols = get_spectest_export_apis(&native_symbols); + if (!wasm_native_register_natives("spectest", + native_symbols, n_native_symbols)) + return false; +#endif /* WASM_ENABLE_SPEC_TEST */ + +#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; + if (!wasm_native_register_natives("wasi_snapshot_preview1", + native_symbols, n_native_symbols)) + return false; +#endif + +#if WASM_ENABLE_BASE_LIB != 0 + 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 + +#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 + +#if WASM_ENABLE_LIB_PTHREAD != 0 + if (!lib_pthread_init()) + return false; + + n_native_symbols = get_lib_pthread_export_apis(&native_symbols); + if (n_native_symbols > 0 + && !wasm_native_register_natives("env", + native_symbols, n_native_symbols)) + return false; +#endif + + return true; +} + +void +wasm_native_destroy() +{ + NativeSymbolsNode *node, *node_next; + +#if WASM_ENABLE_LIB_PTHREAD != 0 + lib_pthread_destroy(); +#endif + + node = g_native_symbols_list; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + + g_native_symbols_list = g_native_symbols_list_end = NULL; +} diff --git a/wamr/core/iwasm/common/wasm_native.h b/wamr/core/iwasm/common/wasm_native.h new file mode 100644 index 0000000..d76d238 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_native.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_NATIVE_H +#define _WASM_NATIVE_H + +#include "bh_common.h" +#include "../include/wasm_export.h" +#include "../interpreter/wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NativeSymbolsNode { + struct NativeSymbolsNode *next; + const char *module_name; + NativeSymbol *native_symbols; + uint32 n_native_symbols; + bool call_conv_raw; +} NativeSymbolsNode, *NativeSymbolsList; + +/** + * Lookup global variable of a given import global + * 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 true if success, false otherwise + */ +bool +wasm_native_lookup_libc_builtin_global(const char *module_name, + const char *global_name, + WASMGlobalImport *global); + +/** + * 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 the native function pointer if success, NULL otherwise + */ +void* +wasm_native_resolve_symbol(const char *module_name, const char *field_name, + const WASMType *func_type, const char **p_signature, + void **p_attachment, bool *p_call_conv_raw); + +bool +wasm_native_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +bool +wasm_native_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +bool +wasm_native_init(); + +void +wasm_native_destroy(); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_NATIVE_H */ + diff --git a/wamr/core/iwasm/common/wasm_runtime_common.c b/wamr/core/iwasm/common/wasm_runtime_common.c new file mode 100644 index 0000000..b729751 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_runtime_common.c @@ -0,0 +1,3179 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "bh_common.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_runtime_common.h" +#include "wasm_memory.h" +#if WASM_ENABLE_INTERP != 0 +#include "../interpreter/wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "../aot/aot_runtime.h" +#endif +#if WASM_ENABLE_THREAD_MGR != 0 +#include "../libraries/thread-mgr/thread_manager.h" +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +/* + * a safety insurance to prevent + * circular depencies leading a stack overflow + * try break early + */ +typedef struct LoadingModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; +} LoadingModule; + +static bh_list loading_module_list_head; +static bh_list *const loading_module_list = &loading_module_list_head; +static korp_mutex loading_module_list_lock; + +/* + * a list about all exported functions, globals, memories, tables of every + * fully loaded module + */ +static bh_list registered_module_list_head; +static bh_list *const registered_module_list = ®istered_module_list_head; +static korp_mutex registered_module_list_lock; +static void +wasm_runtime_destroy_registered_module_list(); +#endif /* WASM_ENABLE_MULTI_MODULE */ + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +static void * +runtime_malloc(uint64 size, WASMModuleInstanceCommon *module_inst, + char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + if (module_inst != NULL) { + wasm_runtime_set_exception(module_inst, + "allocate memory failed"); + } + else if (error_buf != NULL) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + } + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static bool +wasm_runtime_env_init() +{ + if (bh_platform_init() != 0) + return false; + + if (wasm_native_init() == false) { + goto fail1; + } + +#if WASM_ENABLE_MULTI_MODULE + if (BHT_OK != os_mutex_init(®istered_module_list_lock)) { + goto fail2; + } + + if (BHT_OK != os_mutex_init(&loading_module_list_lock)) { + goto fail3; + } +#endif + +#if WASM_ENABLE_SHARED_MEMORY + if (!wasm_shared_memory_init()) { + goto fail4; + } +#endif + +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) + if (!thread_manager_init()) { + goto fail5; + } +#endif + +#if WASM_ENABLE_AOT != 0 +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (!aot_signal_init()) { + goto fail6; + } +#endif +#endif + + return true; + +#if WASM_ENABLE_AOT != 0 +#ifdef OS_ENABLE_HW_BOUND_CHECK +fail6: +#endif +#endif +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) + thread_manager_destroy(); +fail5: +#endif +#if WASM_ENABLE_SHARED_MEMORY + wasm_shared_memory_destroy(); +fail4: +#endif +#if WASM_ENABLE_MULTI_MODULE + os_mutex_destroy(&loading_module_list_lock); +fail3: + os_mutex_destroy(®istered_module_list_lock); +fail2: +#endif + wasm_native_destroy(); +fail1: + bh_platform_destroy(); + + return false; +} + +static bool +wasm_runtime_exec_env_check(WASMExecEnv *exec_env) +{ + return exec_env + && exec_env->module_inst + && exec_env->wasm_stack_size > 0 + && exec_env->wasm_stack.s.top_boundary == + exec_env->wasm_stack.s.bottom + exec_env->wasm_stack_size + && exec_env->wasm_stack.s.top <= exec_env->wasm_stack.s.top_boundary; +} + +bool +wasm_runtime_init() +{ + if (!wasm_runtime_memory_init(Alloc_With_System_Allocator, NULL)) + return false; + + if (!wasm_runtime_env_init()) { + wasm_runtime_memory_destroy(); + return false; + } + + return true; +} + +void +wasm_runtime_destroy() +{ +#if WASM_ENABLE_AOT != 0 +#ifdef OS_ENABLE_HW_BOUND_CHECK + aot_signal_destroy(); +#endif +#endif + + /* runtime env destroy */ +#if WASM_ENABLE_MULTI_MODULE + wasm_runtime_destroy_loading_module_list(); + os_mutex_destroy(&loading_module_list_lock); + + wasm_runtime_destroy_registered_module_list(); + os_mutex_destroy(®istered_module_list_lock); +#endif + +#if WASM_ENABLE_SHARED_MEMORY + wasm_shared_memory_destroy(); +#endif + +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0) + thread_manager_destroy(); +#endif + + wasm_native_destroy(); + bh_platform_destroy(); + + wasm_runtime_memory_destroy(); +} + +bool +wasm_runtime_full_init(RuntimeInitArgs *init_args) +{ + if (!wasm_runtime_memory_init(init_args->mem_alloc_type, + &init_args->mem_alloc_option)) + return false; + + if (!wasm_runtime_env_init()) { + wasm_runtime_memory_destroy(); + return false; + } + + if (init_args->n_native_symbols > 0 + && !wasm_runtime_register_natives(init_args->native_module_name, + init_args->native_symbols, + init_args->n_native_symbols)) { + wasm_runtime_destroy(); + return false; + } + +#if WASM_ENABLE_THREAD_MGR != 0 + wasm_cluster_set_max_thread_num(init_args->max_thread_num); +#endif + + return true; +} + +PackageType +get_package_type(const uint8 *buf, uint32 size) +{ + if (buf && size >= 4) { + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 's' && buf[3] == 'm') + return Wasm_Module_Bytecode; + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 'o' && buf[3] == 't') + return Wasm_Module_AoT; + } + return Package_Type_Unknown; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static module_reader reader; +static module_destroyer destroyer; +void +wasm_runtime_set_module_reader(const module_reader reader_cb, + const module_destroyer destroyer_cb) +{ + reader = reader_cb; + destroyer = destroyer_cb; +} + +module_reader +wasm_runtime_get_module_reader() +{ + return reader; +} + +module_destroyer +wasm_runtime_get_module_destroyer() +{ + return destroyer; +} + +static WASMRegisteredModule * +wasm_runtime_find_module_registered_by_reference(WASMModuleCommon *module) +{ + WASMRegisteredModule *reg_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module && module != reg_module->module) { + reg_module = bh_list_elem_next(reg_module); + } + os_mutex_unlock(®istered_module_list_lock); + + return reg_module; +} + +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, + uint32_t error_buf_size) +{ + WASMRegisteredModule *node = NULL; + + node = wasm_runtime_find_module_registered_by_reference(module); + if (node) { /* module has been registered */ + if (node->module_name) { /* module has name */ + if (strcmp(node->module_name, module_name)) { + /* module has different name */ + LOG_DEBUG("module(%p) has been registered with name %s", + module, node->module_name); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "failed to rename the module"); + return false; + } + else { + /* module has the same name */ + LOG_DEBUG("module(%p) has been registered with the same name %s", + module, node->module_name); + return true; + } + } + else { + /* module has empyt name, reset it */ + node->module_name = module_name; + return true; + } + } + + /* module hasn't been registered */ + node = runtime_malloc(sizeof(WASMRegisteredModule), NULL, NULL, 0); + if (!node) { + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ=%d", + sizeof(WASMRegisteredModule)); + return false; + } + + /* share the string and the module */ + node->module_name = module_name; + node->module = module; + node->orig_file_buf = orig_file_buf; + node->orig_file_buf_size = orig_file_buf_size; + + os_mutex_lock(®istered_module_list_lock); + bh_list_status ret = bh_list_insert(registered_module_list, node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(®istered_module_list_lock); + return true; +} + +bool +wasm_runtime_register_module(const char *module_name, WASMModuleCommon *module, + char *error_buf, uint32_t error_buf_size) +{ + if (!error_buf || !error_buf_size) { + LOG_ERROR("error buffer is required"); + return false; + } + + if (!module_name || !module) { + LOG_DEBUG("module_name and module are required"); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "module_name and module are required"); + return false; + } + + if (wasm_runtime_is_built_in_module(module_name)) { + LOG_DEBUG("%s is a built-in module name", module_name); + set_error_buf(error_buf, error_buf_size, + "Register module failed: " + "can not register as a built-in module"); + return false; + } + + return wasm_runtime_register_module_internal( + module_name, module, NULL, 0, + error_buf, error_buf_size); +} + +void +wasm_runtime_unregister_module(const WASMModuleCommon *module) +{ + WASMRegisteredModule *registered_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + registered_module = bh_list_first_elem(registered_module_list); + while (registered_module && module != registered_module->module) { + registered_module = bh_list_elem_next(registered_module); + } + + /* it does not matter if it is not exist. after all, it is gone */ + if (registered_module) { + bh_list_remove(registered_module_list, registered_module); + wasm_runtime_free(registered_module); + } + os_mutex_unlock(®istered_module_list_lock); +} + +WASMModuleCommon * +wasm_runtime_find_module_registered(const char *module_name) +{ + WASMRegisteredModule *module = NULL, *module_next; + + os_mutex_lock(®istered_module_list_lock); + module = bh_list_first_elem(registered_module_list); + while (module) { + module_next = bh_list_elem_next(module); + if (module->module_name + && !strcmp(module_name, module->module_name)) { + break; + } + module = module_next; + } + os_mutex_unlock(®istered_module_list_lock); + + return module ? module->module : NULL; +} + +bool +wasm_runtime_is_module_registered(const char *module_name) +{ + return NULL != wasm_runtime_find_module_registered(module_name); +} + +/* + * simply destroy all + */ +static void +wasm_runtime_destroy_registered_module_list() +{ + WASMRegisteredModule *reg_module = NULL; + + os_mutex_lock(®istered_module_list_lock); + reg_module = bh_list_first_elem(registered_module_list); + while (reg_module) { + WASMRegisteredModule *next_reg_module = bh_list_elem_next(reg_module); + + bh_list_remove(registered_module_list, reg_module); + + /* now, it is time to release every module in the runtime */ +#if WASM_ENABLE_INTERP != 0 + if (reg_module->module->module_type == Wasm_Module_Bytecode) + wasm_unload((WASMModule *)reg_module->module); +#endif +#if WASM_ENABLE_AOT != 0 + if (reg_module->module->module_type == Wasm_Module_AoT) + aot_unload((AOTModule *)reg_module->module); +#endif + + /* destroy the file buffer */ + if (destroyer && reg_module->orig_file_buf) { + destroyer(reg_module->orig_file_buf, + reg_module->orig_file_buf_size); + reg_module->orig_file_buf = NULL; + reg_module->orig_file_buf_size = 0; + } + + wasm_runtime_free(reg_module); + reg_module = next_reg_module; + } + os_mutex_unlock(®istered_module_list_lock); +} + +bool +wasm_runtime_add_loading_module(const char *module_name, + char *error_buf, uint32 error_buf_size) +{ + LOG_DEBUG("add %s into a loading list", module_name); + LoadingModule *loadingModule = + runtime_malloc(sizeof(LoadingModule), NULL, + error_buf, error_buf_size); + + if (!loadingModule) { + return false; + } + + /* share the incoming string */ + loadingModule->module_name = module_name; + + os_mutex_lock(&loading_module_list_lock); + bh_list_status ret = bh_list_insert(loading_module_list, loadingModule); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + os_mutex_unlock(&loading_module_list_lock); + return true; +} + +void +wasm_runtime_delete_loading_module(const char *module_name) +{ + LOG_DEBUG("delete %s from a loading list", module_name); + + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module->module_name, module_name)) { + module = bh_list_elem_next(module); + } + + /* it does not matter if it is not exist. after all, it is gone */ + if (module) { + bh_list_remove(loading_module_list, module); + wasm_runtime_free(module); + } + os_mutex_unlock(&loading_module_list_lock); +} + +bool +wasm_runtime_is_loading_module(const char *module_name) +{ + LOG_DEBUG("find %s in a loading list", module_name); + + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module && strcmp(module_name, module->module_name)) { + module = bh_list_elem_next(module); + } + os_mutex_unlock(&loading_module_list_lock); + + return module != NULL; +} + +void +wasm_runtime_destroy_loading_module_list() +{ + LoadingModule *module = NULL; + + os_mutex_lock(&loading_module_list_lock); + module = bh_list_first_elem(loading_module_list); + while (module) { + LoadingModule *next_module = bh_list_elem_next(module); + + bh_list_remove(loading_module_list, module); + /* + * will not free the module_name since it is + * shared one of the const string pool + */ + wasm_runtime_free(module); + + module = next_module; + } + + os_mutex_unlock(&loading_module_list_lock); +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name) +{ + return (!strcmp("env", module_name) + || !strcmp("wasi_unstable", module_name) + || !strcmp("wasi_snapshot_preview1", module_name) + || !strcmp("spectest", module_name) + ); +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_exec_env_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size) +{ + WASMModuleInstanceCommon *module_inst + = wasm_exec_env_get_module_inst(exec_env); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_set_aux_stack(exec_env, start_offset, size); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_set_aux_stack(exec_env, start_offset, size); + } +#endif + return false; +} + +bool +wasm_exec_env_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size) +{ + WASMModuleInstanceCommon *module_inst + = wasm_exec_env_get_module_inst(exec_env); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_get_aux_stack(exec_env, start_offset, size); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_get_aux_stack(exec_env, start_offset, size); + } +#endif + return false; +} + +void +wasm_runtime_set_max_thread_num(uint32 num) +{ + wasm_cluster_set_max_thread_num(num); +} +#endif /* end of WASM_ENABLE_THREAD_MGR */ + +static WASMModuleCommon * +register_module_with_null_name(WASMModuleCommon *module_common, + char *error_buf, uint32 error_buf_size) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + if (module_common) { + if (!wasm_runtime_register_module_internal(NULL, module_common, + NULL, 0, + error_buf, + error_buf_size)) { + wasm_runtime_unload(module_common); + return NULL; + } + return module_common; + } + else + return NULL; +#else + return module_common; +#endif +} + +WASMModuleCommon * +wasm_runtime_load(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_common = NULL; + + if (get_package_type(buf, size) == Wasm_Module_Bytecode) { +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_JIT != 0 + AOTModule *aot_module; + WASMModule *module = wasm_load(buf, size, error_buf, error_buf_size); + if (!module) + return NULL; + + if (!(aot_module = aot_convert_wasm_module(module, + error_buf, error_buf_size))) { + wasm_unload(module); + return NULL; + } + + module_common = (WASMModuleCommon*)aot_module; + return register_module_with_null_name(module_common, + error_buf, error_buf_size); +#elif WASM_ENABLE_INTERP != 0 + module_common = (WASMModuleCommon*) + wasm_load(buf, size, error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); +#endif + } + else if (get_package_type(buf, size) == Wasm_Module_AoT) { +#if WASM_ENABLE_AOT != 0 + module_common = (WASMModuleCommon*) + aot_load_from_aot_file(buf, size, error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); +#endif + } + + if (size < 4) + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: unexpected end"); + else + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: magic header not detected"); + return NULL; +} + +WASMModuleCommon * +wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, + char *error_buf, uint32_t error_buf_size) +{ + WASMModuleCommon *module_common; + +#if WASM_ENABLE_INTERP != 0 + if (!is_aot) { + module_common = (WASMModuleCommon*) + wasm_load_from_sections(section_list, + error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (is_aot) { + module_common = (WASMModuleCommon*) + aot_load_from_sections(section_list, + error_buf, error_buf_size); + return register_module_with_null_name(module_common, + error_buf, error_buf_size); + } +#endif + + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: invalid section list type"); + return NULL; +} + +void +wasm_runtime_unload(WASMModuleCommon *module) +{ +#if WASM_ENABLE_MULTI_MODULE != 0 + /** + * since we will unload and free all module when runtime_destroy() + * we don't want users to unwillingly disrupt it + */ + return; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + wasm_unload((WASMModule*)module); + return; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + aot_unload((AOTModule*)module); + return; + } +#endif +} + +WASMModuleInstanceCommon * +wasm_runtime_instantiate_internal(WASMModuleCommon *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + return (WASMModuleInstanceCommon*) + wasm_instantiate((WASMModule*)module, is_sub_inst, + stack_size, heap_size, + error_buf, error_buf_size); +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + return (WASMModuleInstanceCommon*) + aot_instantiate((AOTModule*)module, is_sub_inst, + stack_size, heap_size, + error_buf, error_buf_size); +#endif + set_error_buf(error_buf, error_buf_size, + "Instantiate module failed, invalid module type"); + return NULL; +} + +WASMModuleInstanceCommon * +wasm_runtime_instantiate(WASMModuleCommon *module, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size) +{ + return wasm_runtime_instantiate_internal(module, false, + stack_size, heap_size, + error_buf, error_buf_size); +} + +void +wasm_runtime_deinstantiate_internal(WASMModuleInstanceCommon *module_inst, + bool is_sub_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_deinstantiate((WASMModuleInstance*)module_inst, is_sub_inst); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_deinstantiate((AOTModuleInstance*)module_inst, is_sub_inst); + return; + } +#endif +} + +void +wasm_runtime_deinstantiate(WASMModuleInstanceCommon *module_inst) +{ + return wasm_runtime_deinstantiate_internal(module_inst, false); +} + +WASMExecEnv * +wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, + uint32 stack_size) +{ + return wasm_exec_env_create(module_inst, stack_size); +} + +void +wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env) +{ + wasm_exec_env_destroy(exec_env); +} + +WASMModuleInstanceCommon * +wasm_runtime_get_module_inst(WASMExecEnv *exec_env) +{ + return wasm_exec_env_get_module_inst(exec_env); +} + +void * +wasm_runtime_get_function_attachment(WASMExecEnv *exec_env) +{ + return exec_env->attachment; +} + +void +wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data) +{ + exec_env->user_data = user_data; +} + +void * +wasm_runtime_get_user_data(WASMExecEnv *exec_env) +{ + return exec_env->user_data; +} + +WASMFunctionInstanceCommon * +wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, + const char *name, + const char *signature) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return (WASMFunctionInstanceCommon*) + wasm_lookup_function((const WASMModuleInstance*)module_inst, + name, signature); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return (WASMFunctionInstanceCommon*) + aot_lookup_function((const AOTModuleInstance*)module_inst, + name, signature); +#endif + return NULL; +} + +bool +wasm_runtime_call_wasm(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 argc, uint32 argv[]) +{ + if (!wasm_runtime_exec_env_check(exec_env)) { + LOG_ERROR("Invalid exec env stack info."); + return false; + } + + /* set thread handle and stack boundary */ + wasm_exec_env_set_thread_info(exec_env); + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) + return wasm_call_function(exec_env, + (WASMFunctionInstance*)function, + argc, argv); +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) + return aot_call_function(exec_env, + (AOTFunctionInstance*)function, + argc, argv); +#endif + return false; +} + +static uint32 +parse_args_to_uint32_array(WASMType *type, + uint32 num_args, wasm_val_t *args, + uint32 *out_argv) +{ + int i, p; + + for (i = 0, p = 0; i < num_args; i++) { + switch (args[i].kind) { + case WASM_I32: + out_argv[p++] = args[i].of.i32; + break; + case WASM_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.val = args[i].of.i64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + case WASM_F32: + { + union { float32 val; uint32 part; } u; + u.val = args[i].of.f32; + out_argv[p++] = u.part; + break; + } + case WASM_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.val = args[i].of.f64; + out_argv[p++] = u.parts[0]; + out_argv[p++] = u.parts[1]; + break; + } + default: + bh_assert(0); + break; + } + } + return p; +} + +static uint32 +parse_uint32_array_to_results(WASMType *type, + uint32 argc, uint32 *argv, + wasm_val_t *out_results) +{ + int i, p; + + for (i = 0, p = 0; i < type->result_count; i++) { + switch (type->types[type->param_count + i]) { + case VALUE_TYPE_I32: + out_results[i].kind = WASM_I32; + out_results[i].of.i32 = *(int32 *)argv[p++]; + break; + case VALUE_TYPE_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_I64; + out_results[i].of.i64 = u.val; + break; + } + case VALUE_TYPE_F32: + { + union { float32 val; uint32 part; } u; + u.part = argv[p++]; + out_results[i].kind = WASM_F32; + out_results[i].of.f32 = u.val; + break; + } + case VALUE_TYPE_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.parts[0] = argv[p++]; + u.parts[1] = argv[p++]; + out_results[i].kind = WASM_F64; + out_results[i].of.f64 = u.val; + break; + } + default: + bh_assert(0); + break; + } + } + bh_assert(argc == p); + return type->result_count; +} + +bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, wasm_val_t args[]) +{ + uint32 argc, *argv, ret_num, cell_num, total_size; + bool ret = false; + WASMType *type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; + type = wasm_func->u.func->func_type; + argc = wasm_func->param_cell_num; + cell_num = argc > wasm_func->ret_cell_num ? + argc : wasm_func->ret_cell_num; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + type = ((AOTFunctionInstance*)function)->u.func.func_type; + argc = type->param_cell_num; + cell_num = argc > type->ret_cell_num ? + argc : type->ret_cell_num; + } +#endif + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); + goto fail1; + } + + if (num_results != type->result_count) { + LOG_ERROR("The result value number does not match the function declaration."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the function declaration."); + goto fail1; + } + + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); + if (!(argv = runtime_malloc((uint32)total_size, exec_env->module_inst, NULL, 0))) { + wasm_runtime_set_exception(exec_env->module_inst, "allocate memory failed"); + goto fail1; + } + + argc = parse_args_to_uint32_array(type, num_args, args, argv); + if (!(ret = wasm_runtime_call_wasm(exec_env, function, argc, argv))) + goto fail2; + + ret_num = parse_uint32_array_to_results(type, type->ret_cell_num, argv, results); + bh_assert(ret_num == num_results); + +fail2: + wasm_runtime_free(argv); +fail1: + return ret; +} + +bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t results[], + uint32 num_args, ...) +{ + wasm_val_t *args = NULL; + WASMType *type = NULL; + bool ret = false; + int i = 0; + va_list vargs; + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; + type = wasm_func->u.func->func_type; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) { + type = ((AOTFunctionInstance*)function)->u.func.func_type; + } +#endif + if (!type) { + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); + goto fail1; + } + + if (num_args != type->param_count) { + LOG_ERROR("The argument value number does not match the function declaration."); + goto fail1; + } + if (!(args = runtime_malloc(sizeof(wasm_val_t) * num_args, NULL, NULL, 0))) { + wasm_runtime_set_exception(exec_env->module_inst, "allocate memory failed"); + goto fail1; + } + + va_start(vargs, num_args); + for (i = 0; i < num_args; i++) { + switch (type->types[i]) { + case VALUE_TYPE_I32: + args[i].kind = WASM_I32; + args[i].of.i32 = va_arg(vargs, uint32); + break; + case VALUE_TYPE_I64: + args[i].kind = WASM_I64; + args[i].of.i64 = va_arg(vargs, uint64); + break; + case VALUE_TYPE_F32: + args[i].kind = WASM_F32; + args[i].of.f32 = (float32)va_arg(vargs, float64); + break; + case VALUE_TYPE_F64: + args[i].kind = WASM_F64; + args[i].of.f64 = va_arg(vargs, float64);; + break; + default: + bh_assert(0); + break; + } + } + va_end(vargs); + ret = wasm_runtime_call_wasm_a(exec_env, function, num_results, results, num_args, args); + wasm_runtime_free(args); + +fail1: + return ret; +} + +bool +wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, + WASMFunctionInstanceCommon *function, + uint32 argc, uint32 argv[]) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_create_exec_env_and_call_function( + (WASMModuleInstance*)module_inst, + (WASMFunctionInstance*)function, + argc, argv); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_create_exec_env_and_call_function( + (AOTModuleInstance*)module_inst, + (AOTFunctionInstance*)function, + argc, argv); +#endif + return false; +} + +void +wasm_runtime_set_exception(WASMModuleInstanceCommon *module_inst, + const char *exception) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_set_exception((WASMModuleInstance*)module_inst, exception); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_set_exception((AOTModuleInstance*)module_inst, exception); + return; + } +#endif +} + +const char* +wasm_runtime_get_exception(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_get_exception((WASMModuleInstance*)module_inst); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_get_exception((AOTModuleInstance*)module_inst); + } +#endif + return NULL; +} + +void +wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst) +{ + wasm_runtime_set_exception(module_inst, NULL); +} + +void +wasm_runtime_set_custom_data(WASMModuleInstanceCommon *module_inst, + void *custom_data) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + ((WASMModuleInstance*)module_inst)->custom_data = custom_data; + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + ((AOTModuleInstance*)module_inst)->custom_data.ptr = custom_data; + return; + } +#endif +} + +void* +wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return ((WASMModuleInstance*)module_inst)->custom_data; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return ((AOTModuleInstance*)module_inst)->custom_data.ptr; +#endif + return NULL; +} + +uint32 +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, + 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, + p_native_addr); +#endif + return 0; +} + +void +wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, uint32 ptr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + wasm_module_free((WASMModuleInstance*)module_inst, ptr); + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + aot_module_free((AOTModuleInstance*)module_inst, ptr); + return; + } +#endif +} + +uint32 +wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, + const char *src, uint32 size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + return wasm_module_dup_data((WASMModuleInstance*)module_inst, src, size); + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + return aot_module_dup_data((AOTModuleInstance*)module_inst, src, size); + } +#endif + return 0; +} + +bool +wasm_runtime_validate_app_addr(WASMModuleInstanceCommon *module_inst, + uint32 app_offset, uint32 size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_validate_app_addr((WASMModuleInstance*)module_inst, + app_offset, size); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_validate_app_addr((AOTModuleInstance*)module_inst, + app_offset, size); +#endif + return false; +} + +bool +wasm_runtime_validate_app_str_addr(WASMModuleInstanceCommon *module_inst, + uint32 app_str_offset) +{ + uint32 app_end_offset; + char *str, *str_end; + + if (!wasm_runtime_get_app_addr_range(module_inst, app_str_offset, + NULL, &app_end_offset)) + goto fail; + + str = wasm_runtime_addr_app_to_native(module_inst, app_str_offset); + str_end = str + (app_end_offset - app_str_offset); + while (str < str_end && *str != '\0') + str++; + if (str == str_end) + goto fail; + return true; + +fail: + wasm_runtime_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +bool +wasm_runtime_validate_native_addr(WASMModuleInstanceCommon *module_inst, + void *native_ptr, uint32 size) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_validate_native_addr((WASMModuleInstance*)module_inst, + native_ptr, size); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_validate_native_addr((AOTModuleInstance*)module_inst, + native_ptr, size); +#endif + return false; +} + +void * +wasm_runtime_addr_app_to_native(WASMModuleInstanceCommon *module_inst, + uint32 app_offset) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_addr_app_to_native((WASMModuleInstance*)module_inst, + app_offset); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_addr_app_to_native((AOTModuleInstance*)module_inst, + app_offset); +#endif + return NULL; +} + +uint32 +wasm_runtime_addr_native_to_app(WASMModuleInstanceCommon *module_inst, + void *native_ptr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_addr_native_to_app((WASMModuleInstance*)module_inst, + native_ptr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_addr_native_to_app((AOTModuleInstance*)module_inst, + native_ptr); +#endif + return 0; +} + +bool +wasm_runtime_get_app_addr_range(WASMModuleInstanceCommon *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_get_app_addr_range((WASMModuleInstance*)module_inst, + app_offset, p_app_start_offset, + p_app_end_offset); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_get_app_addr_range((AOTModuleInstance*)module_inst, + app_offset, p_app_start_offset, + p_app_end_offset); +#endif + return false; +} + +bool +wasm_runtime_get_native_addr_range(WASMModuleInstanceCommon *module_inst, + uint8_t *native_ptr, + uint8_t **p_native_start_addr, + uint8_t **p_native_end_addr) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return wasm_get_native_addr_range((WASMModuleInstance*)module_inst, + native_ptr, p_native_start_addr, + p_native_end_addr); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return aot_get_native_addr_range((AOTModuleInstance*)module_inst, + native_ptr, p_native_start_addr, + p_native_end_addr); +#endif + return false; +} + +uint32 +wasm_runtime_get_temp_ret(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return ((WASMModuleInstance*)module_inst)->temp_ret; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return ((AOTModuleInstance*)module_inst)->temp_ret; +#endif + return 0; +} + +void +wasm_runtime_set_temp_ret(WASMModuleInstanceCommon *module_inst, + uint32 temp_ret) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + ((WASMModuleInstance*)module_inst)->temp_ret = temp_ret; + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + ((AOTModuleInstance*)module_inst)->temp_ret = temp_ret; + return; + } +#endif +} + +uint32 +wasm_runtime_get_llvm_stack(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return ((WASMModuleInstance*)module_inst)->llvm_stack; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return ((AOTModuleInstance*)module_inst)->llvm_stack; +#endif + return 0; +} + +void +wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst, + uint32 llvm_stack) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + ((WASMModuleInstance*)module_inst)->llvm_stack = llvm_stack; + return; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + ((AOTModuleInstance*)module_inst)->llvm_stack = llvm_stack; + return; + } +#endif +} + +bool +wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module, + uint32 inc_page_count) +{ +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) + return wasm_enlarge_memory((WASMModuleInstance*)module, + inc_page_count); +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + return aot_enlarge_memory((AOTModuleInstance*)module, + inc_page_count); +#endif + return false; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +void +wasm_runtime_set_wasi_args(WASMModuleCommon *module, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env_list[], uint32 env_count, + char *argv[], int argc) +{ + WASIArguments *wasi_args = NULL; + +#if WASM_ENABLE_INTERP != 0 || WASM_ENABLE_JIT != 0 + if (module->module_type == Wasm_Module_Bytecode) + wasi_args = &((WASMModule*)module)->wasi_args; +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) + wasi_args = &((AOTModule*)module)->wasi_args; +#endif + + if (wasi_args) { + wasi_args->dir_list = dir_list; + wasi_args->dir_count = dir_count; + wasi_args->map_dir_list = map_dir_list; + wasi_args->map_dir_count = map_dir_count; + wasi_args->env = env_list; + wasi_args->env_count = env_count; + wasi_args->argv = argv; + wasi_args->argc = (uint32)argc; + } +} + +bool +wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env[], uint32 env_count, + char *argv[], uint32 argc, + char *error_buf, uint32 error_buf_size) +{ + WASIContext *wasi_ctx; + size_t *argv_offsets = NULL; + char *argv_buf = NULL; + size_t *env_offsets = NULL; + char *env_buf = NULL; + uint64 argv_buf_len = 0, env_buf_len = 0; + uint32 argv_buf_offset = 0, env_buf_offset = 0; + struct fd_table *curfds; + struct fd_prestats *prestats; + struct argv_environ_values *argv_environ; + bool fd_table_inited = false, fd_prestats_inited = false; + bool argv_environ_inited = false; + uint32 offset_argv_offsets = 0, offset_env_offsets = 0; + uint32 offset_argv_buf = 0, offset_env_buf = 0; + uint32 offset_curfds = 0; + uint32 offset_prestats = 0; + uint32 offset_argv_environ = 0; + __wasi_fd_t wasm_fd = 3; + int32 raw_fd; + char *path, resolved_path[PATH_MAX]; + uint64 total_size; + uint32 i; + + if (!(wasi_ctx = runtime_malloc(sizeof(WASIContext), NULL, + error_buf, error_buf_size))) { + return false; + } + + wasm_runtime_set_wasi_ctx(module_inst, wasi_ctx); + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode + && !((WASMModuleInstance*)module_inst)->default_memory) + return true; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT + && !((AOTModuleInstance*)module_inst)-> + global_table_data.memory_instances[0].memory_data.ptr) + return true; +#endif + + /* process argv[0], trip the path and suffix, only keep the program name */ + for (i = 0; i < argc; i++) + argv_buf_len += strlen(argv[i]) + 1; + + total_size = sizeof(size_t) * (uint64)argc; + if (total_size >= UINT32_MAX + || !(offset_argv_offsets = wasm_runtime_module_malloc + (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, + (void**)&argv_buf))) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + for (i = 0; i < argc; i++) { + argv_offsets[i] = argv_buf_offset; + bh_strcpy_s(argv_buf + argv_buf_offset, + (uint32)argv_buf_len - argv_buf_offset, argv[i]); + argv_buf_offset += (uint32)(strlen(argv[i]) + 1); + } + + for (i = 0; i < env_count; i++) + env_buf_len += strlen(env[i]) + 1; + + total_size = sizeof(size_t) * (uint64)argc; + if (total_size >= UINT32_MAX + || !(offset_env_offsets = wasm_runtime_module_malloc + (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, + (void**)&env_buf))) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: allocate memory failed"); + goto fail; + } + + for (i = 0; i < env_count; i++) { + env_offsets[i] = env_buf_offset; + bh_strcpy_s(env_buf + env_buf_offset, + (uint32)env_buf_len - env_buf_offset, env[i]); + env_buf_offset += (uint32)(strlen(env[i]) + 1); + } + + if (!(offset_curfds = wasm_runtime_module_malloc + (module_inst, sizeof(struct fd_table), (void**)&curfds)) + || !(offset_prestats = wasm_runtime_module_malloc + (module_inst, sizeof(struct fd_prestats), (void**)&prestats)) + || !(offset_argv_environ = wasm_runtime_module_malloc + (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; + } + + if (!fd_table_init(curfds)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init fd table failed"); + goto fail; + } + fd_table_inited = true; + + if (!fd_prestats_init(prestats)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init fd prestats failed"); + goto fail; + } + fd_prestats_inited = true; + + if (!argv_environ_init(argv_environ, + argv_offsets, argc, + argv_buf, argv_buf_len, + env_offsets, env_count, + env_buf, env_buf_len)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: " + "init argument environment failed"); + goto fail; + } + argv_environ_inited = true; + + /* Prepopulate curfds with stdin, stdout, and stderr file descriptors. */ + if (!fd_table_insert_existing(curfds, 0, 0) + || !fd_table_insert_existing(curfds, 1, 1) + || !fd_table_insert_existing(curfds, 2, 2)) { + set_error_buf(error_buf, error_buf_size, + "Init wasi environment failed: init fd table failed"); + goto fail; + } + + wasm_fd = 3; + for (i = 0; i < dir_count; i++, wasm_fd++) { + path = realpath(dir_list[i], resolved_path); + if (!path) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening directory %s: %d\n", + dir_list[i], errno); + goto fail; + } + + raw_fd = open(path, O_RDONLY | O_DIRECTORY, 0); + if (raw_fd == -1) { + if (error_buf) + snprintf(error_buf, error_buf_size, + "error while pre-opening directory %s: %d\n", + dir_list[i], errno); + goto fail; + } + + fd_table_insert_existing(curfds, wasm_fd, raw_fd); + fd_prestats_insert(prestats, dir_list[i], wasm_fd); + } + + wasi_ctx->curfds_offset = offset_curfds; + wasi_ctx->prestats_offset = offset_prestats; + wasi_ctx->argv_environ_offset = offset_argv_environ; + wasi_ctx->argv_buf_offset = offset_argv_buf; + wasi_ctx->argv_offsets_offset = offset_argv_offsets; + wasi_ctx->env_buf_offset = offset_env_buf; + wasi_ctx->env_offsets_offset = offset_env_offsets; + + return true; + +fail: + if (argv_environ_inited) + argv_environ_destroy(argv_environ); + if (fd_prestats_inited) + fd_prestats_destroy(prestats); + if (fd_table_inited) + fd_table_destroy(curfds); + if (offset_curfds != 0) + wasm_runtime_module_free(module_inst, offset_curfds); + if (offset_prestats != 0) + wasm_runtime_module_free(module_inst, offset_prestats); + if (offset_argv_environ != 0) + wasm_runtime_module_free(module_inst, offset_argv_environ); + if (offset_argv_buf) + wasm_runtime_module_free(module_inst, offset_argv_buf); + if (offset_argv_offsets) + wasm_runtime_module_free(module_inst, offset_argv_offsets); + if (offset_env_buf) + wasm_runtime_module_free(module_inst, offset_env_buf); + if (offset_env_offsets) + wasm_runtime_module_free(module_inst, offset_env_offsets); + return false; +} + +bool +wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode + && ((WASMModuleInstance*)module_inst)->module->is_wasi_module) + return true; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT + && ((AOTModule*)((AOTModuleInstance*)module_inst)->aot_module.ptr) + ->is_wasi_module) + return true; +#endif + return false; +} + +WASMFunctionInstanceCommon * +wasm_runtime_lookup_wasi_start_function(WASMModuleInstanceCommon *module_inst) +{ + uint32 i; + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst; + WASMFunctionInstance *func; + for (i = 0; i < wasm_inst->export_func_count; i++) { + if (!strcmp(wasm_inst->export_functions[i].name, "_start")) { + func = wasm_inst->export_functions[i].function; + if (func->u.func->func_type->param_count != 0 + || func->u.func->func_type->result_count != 0) { + LOG_ERROR("Lookup wasi _start function failed: " + "invalid function type.\n"); + return NULL; + } + return (WASMFunctionInstanceCommon*)func; + } + } + return NULL; + } +#endif + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; + AOTFunctionInstance *export_funcs = (AOTFunctionInstance *) + aot_inst->export_funcs.ptr; + for (i = 0; i < aot_inst->export_func_count; i++) { + if (!strcmp(export_funcs[i].func_name, "_start")) { + AOTFuncType *func_type = export_funcs[i].u.func.func_type; + if (func_type->param_count != 0 + || func_type->result_count != 0) { + LOG_ERROR("Lookup wasi _start function failed: " + "invalid function type.\n"); + return NULL; + } + return (WASMFunctionInstanceCommon*)&export_funcs[i]; + } + } + return NULL; + } +#endif /* end of WASM_ENABLE_AOT */ + + return NULL; +} + +void +wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst) +{ + WASIContext *wasi_ctx = wasm_runtime_get_wasi_ctx(module_inst); + struct argv_environ_values *argv_environ; + struct fd_table *curfds; + struct fd_prestats *prestats; + + if (wasi_ctx) { + if (wasi_ctx->argv_environ_offset) { + argv_environ = (struct argv_environ_values *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->argv_environ_offset); + argv_environ_destroy(argv_environ); + wasm_runtime_module_free(module_inst, wasi_ctx->argv_environ_offset); + } + if (wasi_ctx->curfds_offset) { + curfds = (struct fd_table *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->curfds_offset); + fd_table_destroy(curfds); + wasm_runtime_module_free(module_inst, wasi_ctx->curfds_offset); + } + if (wasi_ctx->prestats_offset) { + prestats = (struct fd_prestats *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->prestats_offset); + fd_prestats_destroy(prestats); + wasm_runtime_module_free(module_inst, wasi_ctx->prestats_offset); + } + if (wasi_ctx->argv_buf_offset) + wasm_runtime_module_free(module_inst, wasi_ctx->argv_buf_offset); + if (wasi_ctx->argv_offsets_offset) + wasm_runtime_module_free(module_inst, wasi_ctx->argv_offsets_offset); + if (wasi_ctx->env_buf_offset) + wasm_runtime_module_free(module_inst, wasi_ctx->env_buf_offset); + if (wasi_ctx->env_offsets_offset) + wasm_runtime_module_free(module_inst, wasi_ctx->env_offsets_offset); + wasm_runtime_free(wasi_ctx); + } +} + +WASIContext * +wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return ((WASMModuleInstance*)module_inst)->wasi_ctx; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return ((AOTModuleInstance*)module_inst)->wasi_ctx.ptr; +#endif + return NULL; +} + +void +wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst, + WASIContext *wasi_ctx) +{ +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + ((WASMModuleInstance*)module_inst)->wasi_ctx = wasi_ctx; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + ((AOTModuleInstance*)module_inst)->wasi_ctx.ptr = wasi_ctx; +#endif +} +#endif /* end of WASM_ENABLE_LIBC_WASI */ + +WASMModuleCommon* +wasm_exec_env_get_module(WASMExecEnv *exec_env) +{ + WASMModuleInstanceCommon *module_inst = + wasm_runtime_get_module_inst(exec_env); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + return (WASMModuleCommon*) + ((WASMModuleInstance*)module_inst)->module; +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + return (WASMModuleCommon*) + ((AOTModuleInstance*)module_inst)->aot_module.ptr; +#endif + return NULL; +} + +/** + * Implementation of wasm_application_execute_main() + */ + +static WASMFunctionInstanceCommon* +resolve_function(const WASMModuleInstanceCommon *module_inst, + const char *name); + +static bool +check_main_func_type(const WASMType *type) +{ + if (!(type->param_count == 0 || type->param_count == 2) + ||type->result_count > 1) { + LOG_ERROR("WASM execute application failed: invalid main function type.\n"); + return false; + } + + if (type->param_count == 2 + && !(type->types[0] == VALUE_TYPE_I32 + && type->types[1] == VALUE_TYPE_I32)) { + LOG_ERROR("WASM execute application failed: invalid main function type.\n"); + return false; + } + + if (type->result_count + && type->types[type->param_count] != VALUE_TYPE_I32) { + LOG_ERROR("WASM execute application failed: invalid main function type.\n"); + return false; + } + + return true; +} + +bool +wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, + int32 argc, char *argv[]) +{ + WASMFunctionInstanceCommon *func; + WASMType *func_type = NULL; + uint32 argc1 = 0, argv1[2] = { 0 }; + uint32 total_argv_size = 0; + uint64 total_size; + uint32 argv_buf_offset; + int32 i; + char *argv_buf, *p, *p_end; + uint32 *argv_offsets; + +#if WASM_ENABLE_LIBC_WASI != 0 + if (wasm_runtime_is_wasi_mode(module_inst)) { + /* In wasi mode, we should call function named "_start" + which initializes the wasi envrionment and then calls + the actual main function. Directly call main function + may cause exception thrown. */ + if ((func = wasm_runtime_lookup_wasi_start_function(module_inst))) + return wasm_runtime_create_exec_env_and_call_wasm( + module_inst, func, 0, NULL); + /* if no start function is found, we execute + the main function as normal */ + } +#endif /* end of WASM_ENABLE_LIBC_WASI */ + + func = resolve_function(module_inst, "_main"); + if (!func) { + func = resolve_function(module_inst, "main"); + } + + if (!func) { + wasm_runtime_set_exception(module_inst, + "lookup main function failed"); + return false; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + if (((WASMFunctionInstance*)func)->is_import_func) { + wasm_runtime_set_exception(module_inst, + "lookup main function failed"); + return false; + } + func_type = ((WASMFunctionInstance*)func)->u.func->func_type; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + func_type = ((AOTFunctionInstance*)func)->u.func.func_type; +#endif + + if (!check_main_func_type(func_type)) { + wasm_runtime_set_exception(module_inst, + "invalid function type of main function"); + return false; + } + + if (func_type->param_count) { + for (i = 0; i < argc; i++) + total_argv_size += (uint32)(strlen(argv[i]) + 1); + total_argv_size = align_uint(total_argv_size, 4); + + total_size = (uint64)total_argv_size + sizeof(int32) * (uint64)argc; + + if (total_size >= UINT32_MAX + || !(argv_buf_offset = + wasm_runtime_module_malloc(module_inst, (uint32)total_size, + (void**)&argv_buf))) { + wasm_runtime_set_exception(module_inst, + "allocate memory failed"); + return false; + } + + p = argv_buf; + argv_offsets = (uint32*)(p + total_argv_size); + p_end = p + total_size; + + for (i = 0; i < argc; i++) { + bh_memcpy_s(p, (uint32)(p_end - p), argv[i], (uint32)(strlen(argv[i]) + 1)); + argv_offsets[i] = argv_buf_offset + (uint32)(p - argv_buf); + p += strlen(argv[i]) + 1; + } + + argc1 = 2; + argv1[0] = (uint32)argc; + argv1[1] = (uint32)wasm_runtime_addr_native_to_app(module_inst, argv_offsets); + } + + return wasm_runtime_create_exec_env_and_call_wasm(module_inst, func, + argc1, argv1); +} + + + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModuleInstance * +get_sub_module_inst(const WASMModuleInstance *parent_module_inst, + const char *sub_module_name) +{ + WASMSubModInstNode *node = + bh_list_first_elem(parent_module_inst->sub_module_inst_list); + + while (node && strcmp(node->module_name, sub_module_name)) { + node = bh_list_elem_next(node); + } + return node ? node->module_inst : NULL; +} + +static bool +parse_function_name(char *orig_function_name, char **p_module_name, + char **p_function_name) +{ + if (orig_function_name[0] != '$') { + *p_module_name = NULL; + *p_function_name = orig_function_name; + return true; + } + + /** + * $module_name$function_name\0 + * ===> + * module_name\0function_name\0 + * ===> + * module_name + * function_name + */ + char *p1 = orig_function_name; + char *p2 = strchr(p1 + 1, '$'); + if (!p2) { + LOG_DEBUG("can not parse the incoming function name"); + return false; + } + + *p_module_name = p1 + 1; + *p2 = '\0'; + *p_function_name = p2 + 1; + return strlen(*p_module_name) && strlen(*p_function_name); +} +#endif + +/** + * Implementation of wasm_application_execute_func() + */ + +static WASMFunctionInstanceCommon* +resolve_function(const WASMModuleInstanceCommon *module_inst, + const char *name) +{ + uint32 i = 0; + WASMFunctionInstanceCommon *ret = NULL; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *sub_module_inst = NULL; + char *orig_name = NULL; + char *sub_module_name = NULL; + char *function_name = NULL; + uint32 length = strlen(name) + 1; + + orig_name = runtime_malloc(sizeof(char) * length, NULL, NULL, 0); + if (!orig_name) { + return NULL; + } + + strncpy(orig_name, name, length); + + if (!parse_function_name(orig_name, &sub_module_name, &function_name)) { + goto LEAVE; + } + + LOG_DEBUG("%s -> %s and %s", name, sub_module_name, function_name); + + if (sub_module_name) { + sub_module_inst = get_sub_module_inst( + (WASMModuleInstance *)module_inst, sub_module_name); + if (!sub_module_inst) { + LOG_DEBUG("can not find a sub module named %s", sub_module_name); + goto LEAVE; + } + } +#else + const char *function_name = name; +#endif + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *wasm_inst = (WASMModuleInstance*)module_inst; + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_inst = sub_module_inst ? sub_module_inst : wasm_inst; +#endif /* WASM_ENABLE_MULTI_MODULE */ + + for (i = 0; i < wasm_inst->export_func_count; i++) { + if (!strcmp(wasm_inst->export_functions[i].name, function_name)) { + ret = wasm_inst->export_functions[i].function; + break; + } + } + } +#endif /* WASM_ENABLE_INTERP */ + +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance*)module_inst; + AOTFunctionInstance *export_funcs = (AOTFunctionInstance *) + aot_inst->export_funcs.ptr; + for (i = 0; i < aot_inst->export_func_count; i++) { + if (!strcmp(export_funcs[i].func_name, function_name)) { + ret = &export_funcs[i]; + break; + } + } + } +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +LEAVE: + wasm_runtime_free(orig_name); +#endif + return ret; +} + +union ieee754_float { + float f; + + /* This is the IEEE 754 single-precision format. */ + union { + struct { + unsigned int negative:1; + unsigned int exponent:8; + unsigned int mantissa:23; + } ieee_big_endian; + struct { + unsigned int mantissa:23; + unsigned int exponent:8; + unsigned int negative:1; + } ieee_little_endian; + } ieee; +}; + +union ieee754_double { + double d; + + /* This is the IEEE 754 double-precision format. */ + union { + struct { + unsigned int negative:1; + unsigned int exponent:11; + /* Together these comprise the mantissa. */ + unsigned int mantissa0:20; + unsigned int mantissa1:32; + } ieee_big_endian; + + struct { + /* Together these comprise the mantissa. */ + unsigned int mantissa1:32; + unsigned int mantissa0:20; + unsigned int exponent:11; + unsigned int negative:1; + } ieee_little_endian; + } ieee; +}; + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +bool +wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, + const char *name, int32 argc, char *argv[]) +{ + WASMFunctionInstanceCommon *func; + WASMType *type = NULL; + uint32 argc1, *argv1 = NULL, cell_num, j, k = 0; + int32 i, p; + uint64 total_size; + const char *exception; + char buf[128]; + + bh_assert(argc >= 0); + LOG_DEBUG("call a function \"%s\" with %d arguments", name, argc); + func = resolve_function(module_inst, name); + + if (!func) { + snprintf(buf, sizeof(buf), "lookup function %s failed", name); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)func; + if (wasm_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !wasm_func->import_func_inst +#endif + ) { + snprintf(buf, sizeof(buf), "lookup function %s failed", name); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + type = wasm_func->u.func->func_type; + argc1 = wasm_func->param_cell_num; + cell_num = argc1 > wasm_func->ret_cell_num ? + argc1 : wasm_func->ret_cell_num; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) { + type = ((AOTFunctionInstance*)func)->u.func.func_type; + argc1 = type->param_cell_num; + cell_num = argc1 > type->ret_cell_num ? + argc1 : type->ret_cell_num; + } +#endif + + if (type->param_count != (uint32)argc) { + wasm_runtime_set_exception(module_inst, + "invalid input argument count"); + goto fail; + } + + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); + if ((!(argv1 = runtime_malloc((uint32)total_size, module_inst, + NULL, 0)))) { + goto fail; + } + + /* Clear errno before parsing arguments */ + errno = 0; + + /* Parse arguments */ + for (i = 0, p = 0; i < argc; i++) { + char *endptr = NULL; + bh_assert(argv[i] != NULL); + if (argv[i][0] == '\0') { + snprintf(buf, sizeof(buf), "invalid input argument %d", i); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + switch (type->types[i]) { + case VALUE_TYPE_I32: + argv1[p++] = (uint32)strtoul(argv[i], &endptr, 0); + break; + case VALUE_TYPE_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.val = strtoull(argv[i], &endptr, 0); + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; + break; + } + case VALUE_TYPE_F32: + { + float32 f32 = strtof(argv[i], &endptr); + if (isnan(f32)) { + if (argv[i][0] == '-') { + union ieee754_float u; + u.f = f32; + if (is_little_endian()) + u.ieee.ieee_little_endian.negative = 1; + else + u.ieee.ieee_big_endian.negative = 1; + memcpy(&f32, &u.f, sizeof(float)); + } + if (endptr[0] == ':') { + uint32 sig; + union ieee754_float u; + sig = (uint32)strtoul(endptr + 1, &endptr, 0); + u.f = f32; + if (is_little_endian()) + u.ieee.ieee_little_endian.mantissa = sig; + else + u.ieee.ieee_big_endian.mantissa = sig; + memcpy(&f32, &u.f, sizeof(float)); + } + } + memcpy(&argv1[p++], &f32, sizeof(float)); + break; + } + case VALUE_TYPE_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.val = strtod(argv[i], &endptr); + if (isnan(u.val)) { + if (argv[i][0] == '-') { + union ieee754_double ud; + ud.d = u.val; + if (is_little_endian()) + ud.ieee.ieee_little_endian.negative = 1; + else + ud.ieee.ieee_big_endian.negative = 1; + memcpy(&u.val, &ud.d, sizeof(double)); + } + if (endptr[0] == ':') { + uint64 sig; + union ieee754_double ud; + sig = strtoull(endptr + 1, &endptr, 0); + ud.d = u.val; + if (is_little_endian()) { + ud.ieee.ieee_little_endian.mantissa0 = sig >> 32; + ud.ieee.ieee_little_endian.mantissa1 = (uint32)sig; + } + else { + ud.ieee.ieee_big_endian.mantissa0 = sig >> 32; + ud.ieee.ieee_big_endian.mantissa1 = (uint32)sig; + } + memcpy(&u.val, &ud.d, sizeof(double)); + } + } + argv1[p++] = u.parts[0]; + argv1[p++] = u.parts[1]; + break; + } + } + if (endptr && *endptr != '\0' && *endptr != '_') { + snprintf(buf, sizeof(buf), "invalid input argument %d: %s", + i, argv[i]); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + if (errno != 0) { + snprintf(buf, sizeof(buf), + "prepare function argument error, errno: %d", errno); + wasm_runtime_set_exception(module_inst, buf); + goto fail; + } + } + bh_assert(p == (int32)argc1); + + wasm_runtime_set_exception(module_inst, NULL); + if (!wasm_runtime_create_exec_env_and_call_wasm(module_inst, func, + argc1, argv1)) { + goto fail; + } + + /* print return value */ + for (j = 0; j < type->result_count; j++) { + switch (type->types[type->param_count + j]) { + case VALUE_TYPE_I32: + os_printf("0x%x:i32", argv1[k]); + k++; + break; + case VALUE_TYPE_I64: + { + union { uint64 val; uint32 parts[2]; } u; + u.parts[0] = argv1[k]; + u.parts[1] = argv1[k + 1]; + k += 2; +#ifdef PRIx64 + os_printf("0x%"PRIx64":i64", u.val); +#else + char buf[16]; + if (sizeof(long) == 4) + snprintf(buf, sizeof(buf), "%s", "0x%llx:i64"); + else + snprintf(buf, sizeof(buf), "%s", "0x%lx:i64"); + os_printf(buf, u.val); +#endif + break; + } + case VALUE_TYPE_F32: + os_printf("%.7g:f32", *(float32*)(argv1 + k)); + k++; + break; + case VALUE_TYPE_F64: + { + union { float64 val; uint32 parts[2]; } u; + u.parts[0] = argv1[k]; + u.parts[1] = argv1[k + 1]; + k += 2; + os_printf("%.7g:f64", u.val); + break; + } + } + if (j < (uint32)(type->result_count - 1)) + os_printf(","); + } + os_printf("\n"); + + wasm_runtime_free(argv1); + return true; + +fail: + if (argv1) + wasm_runtime_free(argv1); + + exception = wasm_runtime_get_exception(module_inst); + bh_assert(exception); + os_printf("%s\n", exception); + 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); +} + +bool +wasm_runtime_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols) +{ + return wasm_native_register_natives_raw(module_name, + native_symbols, n_native_symbols); +} + +bool +wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + uint32 *argv, uint32 argc, uint32 *argv_ret) +{ + WASMModuleInstanceCommon *module = wasm_runtime_get_module_inst(exec_env); + typedef void (*NativeRawFuncPtr)(WASMExecEnv*, uint64*); + NativeRawFuncPtr invokeNativeRaw = (NativeRawFuncPtr)func_ptr; + uint64 argv_buf[16] = { 0 }, *argv1 = argv_buf, *argv_dst, size; + uint32 *argv_src = argv, i, argc1, ptr_len; + uint32 arg_i32; + bool ret = false; + + argc1 = func_type->param_count; + if (argc1 > sizeof(argv_buf) / sizeof(uint64)) { + size = sizeof(uint64) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, + NULL, 0))) { + return false; + } + } + + argv_dst = argv1; + + /* Traverse secondly to fill in each argument */ + for (i = 0; i < func_type->param_count; i++, argv_dst++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + { + *(uint32*)argv_dst = 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; + + *(uintptr_t*)argv_dst = (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; + + *(uintptr_t*)argv_dst = (uintptr_t) + wasm_runtime_addr_app_to_native(module, arg_i32); + } + } + break; + } + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(argv_dst, sizeof(uint64), argv_src, sizeof(uint32) * 2); + argv_src += 2; + break; + case VALUE_TYPE_F32: + *(float32*)argv_dst = *(float32*)argv_src++; + break; + default: + bh_assert(0); + break; + } + } + + exec_env->attachment = attachment; + invokeNativeRaw(exec_env, argv1); + exec_env->attachment = NULL; + + if (func_type->result_count > 0) { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + argv_ret[0] = *(uint32*)argv1; + break; + case VALUE_TYPE_F32: + *(float32*)argv_ret = *(float32*)argv1; + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(argv_ret, sizeof(uint32) * 2, argv1, sizeof(uint64)); + break; + default: + bh_assert(0); + break; + } + } + + ret = true; + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} + +/** + * Implementation of wasm_runtime_invoke_native() + */ + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + for (; num > 0; num--) + *dest++ = *src++; +} + +#define PUT_I64_TO_ADDR(addr, value) do { \ + union { int64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + +#define PUT_F64_TO_ADDR(addr, value) do { \ + union { float64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + +/* The invoke native implementation on ARM platform with VFP co-processor */ +#if defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) +typedef void (*GenericFunctionPointer)(); +int64 invokeNative(GenericFunctionPointer f, uint32 *args, uint32 n_stacks); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint32*, uint32); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint32*, uint32); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint32*,uint32); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint32*, uint32); +typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint32*, uint32); + +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; + +#define MAX_REG_INTS 4 +#define MAX_REG_FLOATS 16 + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + 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; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret = false; + + n_ints++; /* exec env */ + + /* Traverse firstly to calculate stack args count */ + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + break; + case VALUE_TYPE_I64: + if (n_ints < MAX_REG_INTS - 1) { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_ints & 1) + n_ints++; + n_ints += 2; + } + else { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_stacks & 1) + n_stacks++; + n_stacks += 2; + } + break; + case VALUE_TYPE_F32: + if (n_fps < MAX_REG_FLOATS) + n_fps++; + else + n_stacks++; + break; + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS - 1) { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_fps & 1) + n_fps++; + n_fps += 2; + } + else { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_stacks & 1) + n_stacks++; + n_stacks += 2; + } + break; + default: + bh_assert(0); + break; + } + } + + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + n_ints++; + else + n_stacks++; + } + + argc1 = MAX_REG_INTS + MAX_REG_FLOATS + n_stacks; + if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { + size = sizeof(uint32) * (uint32)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, + NULL, 0))) { + return false; + } + } + + ints = argv1; + fps = ints + MAX_REG_INTS; + stacks = fps + MAX_REG_FLOATS; + + n_ints = 0; + n_fps = 0; + n_stacks = 0; + ints[n_ints++] = (uint32)(uintptr_t)exec_env; + + /* Traverse secondly to fill in each argument */ + 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++] = arg_i32; + else + 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 */ + if (n_ints & 1) + n_ints++; + *(uint64*)&ints[n_ints] = *(uint64*)argv_src; + n_ints += 2; + } + else { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_stacks & 1) + n_stacks++; + *(uint64*)&stacks[n_stacks] = *(uint64*)argv_src; + n_stacks += 2; + } + argv_src += 2; + break; + case VALUE_TYPE_F32: + if (n_fps < MAX_REG_FLOATS) + *(float32*)&fps[n_fps++] = *(float32*)argv_src++; + else + *(float32*)&stacks[n_stacks++] = *(float32*)argv_src++; + break; + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS - 1) { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_fps & 1) + n_fps++; + *(float64*)&fps[n_fps] = *(float64*)argv_src; + n_fps += 2; + } + else { + /* 64-bit data must be 8 bytes aligned in arm */ + if (n_stacks & 1) + n_stacks++; + *(float64*)&stacks[n_stacks] = *(float64*)argv_src; + n_stacks += 2; + } + argv_src += 2; + break; + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint32*)argv_src++; + else + stacks[n_stacks++] = *(uint32*)argv_src++; + } + + exec_env->attachment = attachment; + if (func_type->result_count == 0) { + invokeNative_Void(func_ptr, argv1, n_stacks); + } + else { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_I64: + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); + break; + case VALUE_TYPE_F32: + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + break; + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = true; + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} +#endif /* end of defined(BUILD_TARGET_ARM_VFP) || defined(BUILD_TARGET_THUMB_VFP) */ + +#if defined(BUILD_TARGET_X86_32) \ + || defined(BUILD_TARGET_ARM) \ + || defined(BUILD_TARGET_THUMB) \ + || defined(BUILD_TARGET_MIPS) \ + || defined(BUILD_TARGET_XTENSA) +typedef void (*GenericFunctionPointer)(); +int64 invokeNative(GenericFunctionPointer f, uint32 *args, uint32 sz); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer f, uint32*, uint32); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer f, uint32*, uint32); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer f, uint32*, uint32); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer f, uint32*, uint32); +typedef void (*VoidFuncPtr)(GenericFunctionPointer f, uint32*, uint32); + +static Int64FuncPtr invokeNative_Int64 = (Int64FuncPtr)invokeNative; +static Int32FuncPtr invokeNative_Int32 = (Int32FuncPtr)invokeNative; +static Float64FuncPtr invokeNative_Float64 = (Float64FuncPtr)invokeNative; +static Float32FuncPtr invokeNative_Float32 = (Float32FuncPtr)invokeNative; +static VoidFuncPtr invokeNative_Void = (VoidFuncPtr)invokeNative; + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + 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; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + uint64 size; + bool ret = false; + +#if defined(BUILD_TARGET_X86_32) + argc1 = argc + ext_ret_count + 2; +#else + /* arm/thumb/mips/xtensa, 64-bit data must be 8 bytes aligned, + so we need to allocate more memory. */ + argc1 = func_type->param_count * 2 + ext_ret_count + 2; +#endif + + if (argc1 > sizeof(argv_buf) / sizeof(uint32)) { + size = sizeof(uint32) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, + NULL, 0))) { + return false; + } + } + + for (i = 0; i < sizeof(WASMExecEnv*) / sizeof(uint32); i++) + argv1[j++] = ((uint32*)&exec_env)[i]; + + for (i = 0; i < func_type->param_count; i++) { + switch (func_type->types[i]) { + case VALUE_TYPE_I32: + { + 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: +#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; + case VALUE_TYPE_F32: + argv1[j++] = *argv++; + break; + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + word_copy(argv1 + j, argv, ext_ret_count); + + argc1 = j + ext_ret_count; + exec_env->attachment = attachment; + if (func_type->result_count == 0) { + invokeNative_Void(func_ptr, argv1, argc1); + } + else { + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, argc1); + break; + case VALUE_TYPE_I64: + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, argc1)); + break; + case VALUE_TYPE_F32: + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, argc1); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, argc1)); + break; + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = true; + +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + return ret; +} + +#endif /* end of defined(BUILD_TARGET_X86_32) \ + || defined(BUILD_TARGET_ARM) \ + || defined(BUILD_TARGET_THUMB) \ + || defined(BUILD_TARGET_MIPS) \ + || defined(BUILD_TARGET_XTENSA) */ + +#if defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) +typedef void (*GenericFunctionPointer)(); +int64 invokeNative(GenericFunctionPointer f, uint64 *args, uint64 n_stacks); + +typedef float64 (*Float64FuncPtr)(GenericFunctionPointer, uint64*, uint64); +typedef float32 (*Float32FuncPtr)(GenericFunctionPointer, uint64*, uint64); +typedef int64 (*Int64FuncPtr)(GenericFunctionPointer, uint64*,uint64); +typedef int32 (*Int32FuncPtr)(GenericFunctionPointer, uint64*, uint64); +typedef void (*VoidFuncPtr)(GenericFunctionPointer, uint64*, uint64); + +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 +#define MAX_REG_INTS 4 +#else +#define MAX_REG_FLOATS 8 +#if defined(BUILD_TARGET_AARCH64) +#define MAX_REG_INTS 8 +#else +#define MAX_REG_INTS 6 +#endif /* end of defined(BUILD_TARGET_AARCH64 */ +#endif /* end of defined(_WIN32) || defined(_WIN32_) */ + +bool +wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + uint32 *argv, uint32 argc, uint32 *argv_ret) +{ + 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; + uint32 result_count = func_type->result_count; + uint32 ext_ret_count = result_count > 1 ? result_count - 1 : 0; + bool ret = false; + +#if defined(_WIN32) || defined(_WIN32_) + /* important difference in calling conventions */ +#define n_fps n_ints +#else + int n_fps = 0; +#endif + + argc1 = 1 + MAX_REG_FLOATS + (uint32)func_type->param_count + ext_ret_count; + if (argc1 > sizeof(argv_buf) / sizeof(uint64)) { + size = sizeof(uint64) * (uint64)argc1; + if (!(argv1 = runtime_malloc((uint32)size, exec_env->module_inst, + NULL, 0))) { + return false; + } + } + + fps = argv1; + ints = fps + MAX_REG_FLOATS; + stacks = ints + MAX_REG_INTS; + + ints[n_ints++] = (uint64)(uintptr_t)exec_env; + + 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++] = arg_i64; + else + stacks[n_stacks++] = arg_i64; + break; + } + case VALUE_TYPE_I64: + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint64*)argv_src; + else + stacks[n_stacks++] = *(uint64*)argv_src; + argv_src += 2; + break; + case VALUE_TYPE_F32: + if (n_fps < MAX_REG_FLOATS) + *(float32*)&fps[n_fps++] = *(float32*)argv_src++; + else + *(float32*)&stacks[n_stacks++] = *(float32*)argv_src++; + break; + case VALUE_TYPE_F64: + if (n_fps < MAX_REG_FLOATS) + *(float64*)&fps[n_fps++] = *(float64*)argv_src; + else + *(float64*)&stacks[n_stacks++] = *(float64*)argv_src; + argv_src += 2; + break; + default: + bh_assert(0); + break; + } + } + + /* Save extra result values' address to argv1 */ + for (i = 0; i < ext_ret_count; i++) { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *(uint64*)argv_src; + else + stacks[n_stacks++] = *(uint64*)argv_src; + argv_src += 2; + } + + exec_env->attachment = attachment; + if (result_count == 0) { + invokeNative_Void(func_ptr, argv1, n_stacks); + } + else { + /* Invoke the native function and get the first result value */ + switch (func_type->types[func_type->param_count]) { + case VALUE_TYPE_I32: + argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_I64: + PUT_I64_TO_ADDR(argv_ret, invokeNative_Int64(func_ptr, argv1, n_stacks)); + break; + case VALUE_TYPE_F32: + *(float32*)argv_ret = invokeNative_Float32(func_ptr, argv1, n_stacks); + break; + case VALUE_TYPE_F64: + PUT_F64_TO_ADDR(argv_ret, invokeNative_Float64(func_ptr, argv1, n_stacks)); + break; + default: + bh_assert(0); + break; + } + } + exec_env->attachment = NULL; + + ret = true; +fail: + if (argv1 != argv_buf) + wasm_runtime_free(argv1); + + return ret; +} + +#endif /* end of defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) */ + +bool +wasm_runtime_call_indirect(WASMExecEnv *exec_env, + uint32_t element_indices, + uint32_t argc, uint32_t argv[]) +{ + if (!wasm_runtime_exec_env_check(exec_env)) { + LOG_ERROR("Invalid exec env stack info."); + return false; + } + + /* this function is called from native code, so exec_env->handle and + exec_env->native_stack_boundary must have been set, we don't set + it again */ + +#if WASM_ENABLE_INTERP != 0 + if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) + return wasm_call_indirect(exec_env, + element_indices, + argc, argv); +#endif +#if WASM_ENABLE_AOT != 0 + if (exec_env->module_inst->module_type == Wasm_Module_AoT) + return aot_call_indirect(exec_env, false, 0, + element_indices, argc, argv); +#endif + return false; +} + +#if WASM_ENABLE_THREAD_MGR != 0 +typedef struct WASMThreadArg { + WASMExecEnv *new_exec_env; + wasm_thread_callback_t callback; + void *arg; +} WASMThreadArg; + +WASMExecEnv * +wasm_runtime_spawn_exec_env(WASMExecEnv *exec_env) +{ + return wasm_cluster_spawn_exec_env(exec_env); +} + +void +wasm_runtime_destroy_spawned_exec_env(WASMExecEnv *exec_env) +{ + wasm_cluster_destroy_spawned_exec_env(exec_env); +} + +static void* +wasm_runtime_thread_routine(void *arg) +{ + WASMThreadArg *thread_arg = (WASMThreadArg *)arg; + void *ret; + + bh_assert(thread_arg->new_exec_env); + ret = thread_arg->callback(thread_arg->new_exec_env, thread_arg->arg); + + wasm_runtime_destroy_spawned_exec_env(thread_arg->new_exec_env); + wasm_runtime_free(thread_arg); + + os_thread_exit(ret); + return ret; +} + +int32 +wasm_runtime_spawn_thread(WASMExecEnv *exec_env, wasm_thread_t *tid, + wasm_thread_callback_t callback, void *arg) +{ + WASMExecEnv *new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + WASMThreadArg *thread_arg; + int32 ret; + + if (!new_exec_env) + return -1; + + if (!(thread_arg = wasm_runtime_malloc(sizeof(WASMThreadArg)))) { + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + return -1; + } + + thread_arg->new_exec_env = new_exec_env; + thread_arg->callback = callback; + thread_arg->arg = arg; + + ret = os_thread_create((korp_tid *)tid, wasm_runtime_thread_routine, + thread_arg, APP_THREAD_STACK_SIZE_DEFAULT); + + if (ret != 0) { + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + wasm_runtime_free(thread_arg); + } + + return ret; +} + +int32 +wasm_runtime_join_thread(wasm_thread_t tid, void **retval) +{ + return os_thread_join((korp_tid)tid, retval); +} + +#endif diff --git a/wamr/core/iwasm/common/wasm_runtime_common.h b/wamr/core/iwasm/common/wasm_runtime_common.h new file mode 100644 index 0000000..51ef6c7 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_runtime_common.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_COMMON_H +#define _WASM_COMMON_H + +#include "bh_platform.h" +#include "bh_common.h" +#include "wasm_exec_env.h" +#include "wasm_native.h" +#include "../include/wasm_export.h" +#include "../interpreter/wasm.h" +#if WASM_ENABLE_LIBC_WASI != 0 +#include "wasmtime_ssp.h" +#include "posix.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WASMModuleCommon { + /* Module type, for module loaded from WASM bytecode binary, + this field is Wasm_Module_Bytecode, and this structure should + be treated as WASMModule structure; + for module loaded from AOT binary, this field is + Wasm_Module_AoT, and this structure should be treated as + AOTModule structure. */ + uint32 module_type; + uint8 module_data[1]; +} WASMModuleCommon; + +typedef struct WASMModuleInstanceCommon { + /* Module instance type, for module instance loaded from WASM + bytecode binary, this field is Wasm_Module_Bytecode, and this + structure should be treated as WASMModuleInstance structure; + for module instance loaded from AOT binary, this field is + Wasm_Module_AoT, and this structure should be treated as + AOTModuleInstance structure. */ + uint32 module_type; + uint8 module_inst_data[1]; +} WASMModuleInstanceCommon; + +#if WASM_ENABLE_LIBC_WASI != 0 +typedef struct WASIContext { + /* Use offset but not native address, since these fields are + allocated from app's heap, and the heap space may be re-allocated + after memory.grow opcode is executed, the original native address + cannot be accessed again. */ + uint32 curfds_offset; + uint32 prestats_offset; + uint32 argv_environ_offset; + uint32 argv_buf_offset; + uint32 argv_offsets_offset; + uint32 env_buf_offset; + uint32 env_offsets_offset; +} WASIContext; +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMRegisteredModule { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleCommon *module; + /* to store the original module file buffer address */ + uint8 *orig_file_buf; + uint32 orig_file_buf_size; +} WASMRegisteredModule; +#endif + +typedef struct WASMMemoryInstanceCommon { + uint32 module_type; + uint8 memory_inst_data[1]; +} WASMMemoryInstanceCommon; + +typedef package_type_t PackageType; +typedef wasm_section_t WASMSection, AOTSection; + +/* See wasm_export.h for description */ +bool +wasm_runtime_init(void); + +/* See wasm_export.h for description */ +bool +wasm_runtime_full_init(RuntimeInitArgs *init_args); + +/* See wasm_export.h for description */ +void +wasm_runtime_destroy(void); + +/* See wasm_export.h for description */ +PackageType +get_package_type(const uint8 *buf, uint32 size); + + +/* See wasm_export.h for description */ +WASMModuleCommon * +wasm_runtime_load(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size); + +/* See wasm_export.h for description */ +WASMModuleCommon * +wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot, + char *error_buf, uint32 error_buf_size); + +/* See wasm_export.h for description */ +void +wasm_runtime_unload(WASMModuleCommon *module); + +/* Internal API */ +WASMModuleInstanceCommon * +wasm_runtime_instantiate_internal(WASMModuleCommon *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size); + +/* Internal API */ +void +wasm_runtime_deinstantiate_internal(WASMModuleInstanceCommon *module_inst, + bool is_sub_inst); + +/* See wasm_export.h for description */ +WASMModuleInstanceCommon * +wasm_runtime_instantiate(WASMModuleCommon *module, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size); + +/* See wasm_export.h for description */ +void +wasm_runtime_deinstantiate(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +WASMFunctionInstanceCommon * +wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, + const char *name, const char *signature); + +/* See wasm_export.h for description */ +WASMExecEnv * +wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, + uint32 stack_size); + +/* See wasm_export.h for description */ +void +wasm_runtime_destroy_exec_env(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +WASMModuleInstanceCommon * +wasm_runtime_get_module_inst(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +void * +wasm_runtime_get_function_attachment(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +void +wasm_runtime_set_user_data(WASMExecEnv *exec_env, void *user_data); + +/* See wasm_export.h for description */ +void * +wasm_runtime_get_user_data(WASMExecEnv *exec_env); + +/* See wasm_export.h for description */ +bool +wasm_runtime_call_wasm(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 argc, uint32 argv[]); + +bool +wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, wasm_val_t *args); + +bool +wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 num_results, wasm_val_t *results, + uint32 num_args, ...); + +/** + * Call a function reference of a given WASM runtime instance with + * arguments. + * + * @param exec_env the execution environment to call the function + * which must be created from wasm_create_exec_env() + * @param element_indices the function ference indicies, usually + * prvovided by the caller of a registed native function + * @param argc the number of arguments + * @param argv the arguments. If the function method has return value, + * the first (or first two in case 64-bit return value) element of + * argv stores the return value of the called WASM function after this + * function returns. + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get exception info. + */ +bool +wasm_runtime_call_indirect(WASMExecEnv *exec_env, + uint32 element_indices, + uint32 argc, uint32 argv[]); + +bool +wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst, + WASMFunctionInstanceCommon *function, + uint32 argc, uint32 argv[]); + +/* See wasm_export.h for description */ +bool +wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, + int32 argc, char *argv[]); + +/* See wasm_export.h for description */ +bool +wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, + const char *name, int32 argc, char *argv[]); + +/* See wasm_export.h for description */ +void +wasm_runtime_set_exception(WASMModuleInstanceCommon *module, + const char *exception); + +/* See wasm_export.h for description */ +const char * +wasm_runtime_get_exception(WASMModuleInstanceCommon *module); + +/* See wasm_export.h for description */ +void +wasm_runtime_clear_exception(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +void +wasm_runtime_set_custom_data(WASMModuleInstanceCommon *module_inst, + void *custom_data); + +/* See wasm_export.h for description */ +void * +wasm_runtime_get_custom_data(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +uint32 +wasm_runtime_module_malloc(WASMModuleInstanceCommon *module_inst, uint32 size, + void **p_native_addr); + +/* See wasm_export.h for description */ +void +wasm_runtime_module_free(WASMModuleInstanceCommon *module_inst, uint32 ptr); + +/* See wasm_export.h for description */ +uint32 +wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, + const char *src, uint32 size); + +/* See wasm_export.h for description */ +bool +wasm_runtime_validate_app_addr(WASMModuleInstanceCommon *module_inst, + uint32 app_offset, uint32 size); + +/* See wasm_export.h for description */ +bool +wasm_runtime_validate_app_str_addr(WASMModuleInstanceCommon *module_inst, + uint32 app_str_offset); + +/* See wasm_export.h for description */ +bool +wasm_runtime_validate_native_addr(WASMModuleInstanceCommon *module_inst, + void *native_ptr, uint32 size); + +/* See wasm_export.h for description */ +void * +wasm_runtime_addr_app_to_native(WASMModuleInstanceCommon *module_inst, + uint32 app_offset); + +/* See wasm_export.h for description */ +uint32 +wasm_runtime_addr_native_to_app(WASMModuleInstanceCommon *module_inst, + void *native_ptr); + +/* See wasm_export.h for description */ +bool +wasm_runtime_get_app_addr_range(WASMModuleInstanceCommon *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset); + +/* See wasm_export.h for description */ +bool +wasm_runtime_get_native_addr_range(WASMModuleInstanceCommon *module_inst, + uint8 *native_ptr, + uint8 **p_native_start_addr, + uint8 **p_native_end_addr); + +uint32 +wasm_runtime_get_temp_ret(WASMModuleInstanceCommon *module_inst); + +void +wasm_runtime_set_temp_ret(WASMModuleInstanceCommon *module_inst, + uint32 temp_ret); + +uint32 +wasm_runtime_get_llvm_stack(WASMModuleInstanceCommon *module_inst); + +void +wasm_runtime_set_llvm_stack(WASMModuleInstanceCommon *module_inst, + uint32 llvm_stack); + +#if WASM_ENABLE_MULTI_MODULE != 0 +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); + +module_reader +wasm_runtime_get_module_reader(); + +module_destroyer +wasm_runtime_get_module_destroyer(); + +bool +wasm_runtime_register_module_internal(const char *module_name, + WASMModuleCommon *module, + uint8 *orig_file_buf, + uint32 orig_file_buf_size, + char *error_buf, + uint32 error_buf_size); + +void +wasm_runtime_unregister_module(const WASMModuleCommon *module); + +bool +wasm_runtime_is_module_registered(const char *module_name); + +bool +wasm_runtime_add_loading_module(const char *module_name, + char *error_buf, uint32 error_buf_size); + +void +wasm_runtime_delete_loading_module(const char *module_name); + +bool +wasm_runtime_is_loading_module(const char *module_name); + +void +wasm_runtime_destroy_loading_module_list(); +#endif /* WASM_ENALBE_MULTI_MODULE */ + +bool +wasm_runtime_is_built_in_module(const char *module_name); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_exec_env_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size); + +bool +wasm_exec_env_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size); +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 +/* See wasm_export.h for description */ +void +wasm_runtime_set_wasi_args(WASMModuleCommon *module, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env_list[], uint32 env_count, + char *argv[], int argc); + +/* See wasm_export.h for description */ +bool +wasm_runtime_is_wasi_mode(WASMModuleInstanceCommon *module_inst); + +/* See wasm_export.h for description */ +WASMFunctionInstanceCommon * +wasm_runtime_lookup_wasi_start_function(WASMModuleInstanceCommon *module_inst); + +bool +wasm_runtime_init_wasi(WASMModuleInstanceCommon *module_inst, + const char *dir_list[], uint32 dir_count, + const char *map_dir_list[], uint32 map_dir_count, + const char *env[], uint32 env_count, + char *argv[], uint32 argc, + char *error_buf, uint32 error_buf_size); + +void +wasm_runtime_destroy_wasi(WASMModuleInstanceCommon *module_inst); + +void +wasm_runtime_set_wasi_ctx(WASMModuleInstanceCommon *module_inst, + WASIContext *wasi_ctx); + +WASIContext * +wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst); + +#endif /* end of WASM_ENABLE_LIBC_WASI */ + +/* Get module of the current exec_env */ +WASMModuleCommon* +wasm_exec_env_get_module(WASMExecEnv *exec_env); + +/** + * Enlarge wasm memory data space. + * + * @param module the wasm module instance + * @param inc_page_count denote the page number to increase + * @return return true if enlarge successfully, false otherwise + */ +bool +wasm_runtime_enlarge_memory(WASMModuleInstanceCommon *module, uint32 inc_page_count); + +/* See wasm_export.h for description */ +bool +wasm_runtime_register_natives(const char *module_name, + NativeSymbol *native_symbols, + uint32 n_native_symbols); + +/* See wasm_export.h for description */ +bool +wasm_runtime_register_natives_raw(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, + void *attachment, + uint32 *argv, uint32 argc, uint32 *ret); + +bool +wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, + const WASMType *func_type, const char *signature, + void *attachment, + uint32 *argv, uint32 argc, uint32 *ret); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_COMMON_H */ + diff --git a/wamr/core/iwasm/common/wasm_shared_memory.c b/wamr/core/iwasm/common/wasm_shared_memory.c new file mode 100644 index 0000000..a0e2675 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_shared_memory.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" +#include "wasm_shared_memory.h" + +static bh_list shared_memory_list_head; +static bh_list *const shared_memory_list = &shared_memory_list_head; +static korp_mutex shared_memory_list_lock; + +enum { + S_WAITING, S_NOTIFIED +}; + +typedef struct AtomicWaitInfo { + korp_mutex wait_list_lock; + bh_list wait_list_head; + bh_list *wait_list; +} AtomicWaitInfo; + +typedef struct AtomicWaitNode { + bh_list_link l; + uint8 status; + korp_mutex wait_lock; + korp_cond wait_cond; +} AtomicWaitNode; + +/* Atomic wait map */ +static HashMap *wait_map; + +static uint32 +wait_address_hash(void *address); + +static bool +wait_address_equal(void *h1, void *h2); + +static void +destroy_wait_info(void *wait_info); + +bool +wasm_shared_memory_init() +{ + if (os_mutex_init(&shared_memory_list_lock) != 0) + return false; + /* wait map not exists, create new map */ + if (!(wait_map = + bh_hash_map_create(32, true, + (HashFunc)wait_address_hash, + (KeyEqualFunc)wait_address_equal, + NULL, destroy_wait_info))) { + os_mutex_destroy(&shared_memory_list_lock); + return false; + } + + return true; +} + +void +wasm_shared_memory_destroy() +{ + os_mutex_destroy(&shared_memory_list_lock); + if (wait_map) { + bh_hash_map_destroy(wait_map); + } +} + +static WASMSharedMemNode* +search_module(WASMModuleCommon *module) +{ + WASMSharedMemNode *node; + + os_mutex_lock(&shared_memory_list_lock); + node = bh_list_first_elem(shared_memory_list); + + while (node) { + if (module == node->module) { + os_mutex_unlock(&shared_memory_list_lock); + return node; + } + node = bh_list_elem_next(node); + } + + os_mutex_unlock(&shared_memory_list_lock); + return NULL; +} + +WASMSharedMemNode* +wasm_module_get_shared_memory(WASMModuleCommon *module) +{ + return search_module(module); +} + +int32 +shared_memory_inc_reference(WASMModuleCommon *module) +{ + WASMSharedMemNode *node = search_module(module); + if (node) { + os_mutex_lock(&node->lock); + node->ref_count++; + os_mutex_unlock(&node->lock); + return node->ref_count; + } + return -1; +} + +int32 +shared_memory_dec_reference(WASMModuleCommon *module) +{ + WASMSharedMemNode *node = search_module(module); + uint32 ref_count = 0; + if (node) { + os_mutex_lock(&node->lock); + ref_count = --node->ref_count; + os_mutex_unlock(&node->lock); + if (ref_count == 0) { + os_mutex_lock(&shared_memory_list_lock); + bh_list_remove(shared_memory_list, node); + os_mutex_unlock(&shared_memory_list_lock); + + os_mutex_destroy(&node->lock); + wasm_runtime_free(node); + } + return ref_count; + } + + return -1; +} + +WASMMemoryInstanceCommon* +shared_memory_get_memory_inst(WASMSharedMemNode *node) +{ + return node->memory_inst; +} + +WASMSharedMemNode* +shared_memory_set_memory_inst(WASMModuleCommon *module, + WASMMemoryInstanceCommon *memory) +{ + WASMSharedMemNode *node; + bh_list_status ret; + + if (!(node = wasm_runtime_malloc(sizeof(WASMSharedMemNode)))) + return NULL; + + node->module = module; + node->memory_inst = memory; + node->ref_count = 1; + if (os_mutex_init(&node->lock) != 0) { + wasm_runtime_free(node); + return NULL; + } + + os_mutex_lock(&shared_memory_list_lock); + ret = bh_list_insert(shared_memory_list, node); + bh_assert(ret == BH_LIST_SUCCESS); + os_mutex_unlock(&shared_memory_list_lock); + + (void)ret; + return node; +} + +/* Atomics wait && notify APIs */ +static uint32 +wait_address_hash(void *address) +{ + return (uint32)(uintptr_t)address; +} + +static bool +wait_address_equal(void *h1, void *h2) +{ + return h1 == h2 ? true : false; +} + +static bool +is_wait_node_exists(bh_list *wait_list, AtomicWaitNode *node) +{ + AtomicWaitNode *curr; + curr = bh_list_first_elem(wait_list); + + while (curr) { + if (curr == node) { + return true; + } + curr = bh_list_elem_next(curr); + } + + return false; +} + +static uint32 +notify_wait_list(bh_list *wait_list, uint32 count) +{ + AtomicWaitNode *node, *next; + uint32 i, notify_count = count; + + if ((count == UINT32_MAX) || (count > wait_list->len)) + notify_count = wait_list->len; + + node = bh_list_first_elem(wait_list); + + for (i = 0; i < count; i++) { + bh_assert(node); + next = bh_list_elem_next(node); + + node->status = S_NOTIFIED; + /* wakeup */ + os_cond_signal(&node->wait_cond); + + node = next; + } + + return notify_count; +} + +static AtomicWaitInfo * +acquire_wait_info(void *address, bool create) +{ + AtomicWaitInfo *wait_info = NULL; + bh_list_status ret; + + wait_info = (AtomicWaitInfo *) + bh_hash_map_find(wait_map, address); + + if (!create) + return wait_info; + + /* No wait info on this address, create new info */ + if (!wait_info) { + if (!(wait_info = + (AtomicWaitInfo *)wasm_runtime_malloc(sizeof(AtomicWaitInfo)))) + return NULL; + memset(wait_info, 0, sizeof(AtomicWaitInfo)); + + /* init wait list */ + wait_info->wait_list = &wait_info->wait_list_head; + ret = bh_list_init(wait_info->wait_list); + bh_assert(ret == BH_LIST_SUCCESS); + + /* init wait list lock */ + if (0 != os_mutex_init(&wait_info->wait_list_lock)) { + wasm_runtime_free(wait_info); + return NULL; + } + + if (!bh_hash_map_insert(wait_map, address, + (void *)wait_info)) { + os_mutex_destroy(&wait_info->wait_list_lock); + wasm_runtime_free(wait_info); + return NULL; + } + } + + bh_assert(wait_info); + (void)ret; + return wait_info; +} + +static void +destroy_wait_info(void *wait_info) +{ + AtomicWaitNode *node, *next; + + if (wait_info) { + + node = bh_list_first_elem(((AtomicWaitInfo *)wait_info)->wait_list); + + while (node) { + next = bh_list_elem_next(node); + os_mutex_destroy(&node->wait_lock); + os_cond_destroy(&node->wait_cond); + wasm_runtime_free(node); + node = next; + } + + os_mutex_destroy(&((AtomicWaitInfo *)wait_info)->wait_list_lock); + wasm_runtime_free(wait_info); + } +} + +static void +release_wait_info(HashMap *wait_map, + AtomicWaitInfo *wait_info, void *address) +{ + if (wait_info->wait_list->len == 0) { + bh_hash_map_remove(wait_map, address, NULL, NULL); + destroy_wait_info(wait_info); + } +} + +uint32 +wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, + uint64 expect, int64 timeout, bool wait64) +{ + AtomicWaitInfo *wait_info; + AtomicWaitNode *wait_node; + bool check_ret, is_timeout; + +#if WASM_ENABLE_INTERP != 0 + if (module->module_type == Wasm_Module_Bytecode) { + WASMModuleInstance *module_inst = (WASMModuleInstance *)module; + /* Currently we have only one memory instance */ + if (!module_inst->memories[0]->is_shared) { + wasm_runtime_set_exception(module, "wait on unshared memory"); + return -1; + } + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module->module_type == Wasm_Module_AoT) { + AOTModuleInstance *aot_inst = (AOTModuleInstance *)module; + AOTMemoryInstance *aot_memory = + ((AOTMemoryInstance **)aot_inst->memories.ptr)[0]; + /* Currently we have only one memory instance */ + if (!aot_memory->is_shared) { + wasm_runtime_set_exception(module, "wait on unshared memory"); + return -1; + } + } +#endif + + /* acquire the wait info, create new one if not exists */ + wait_info = acquire_wait_info(address, true); + + if (!wait_info) { + wasm_runtime_set_exception(module, "failed to acquire wait_info"); + return -1; + } + + os_mutex_lock(&wait_info->wait_list_lock); + + if ((!wait64 && *(uint32*)address != (uint32)expect) + || (wait64 && *(uint64*)address != expect)) { + os_mutex_unlock(&wait_info->wait_list_lock); + return 1; + } + else { + bh_list_status ret; + + if (!(wait_node = wasm_runtime_malloc(sizeof(AtomicWaitNode)))) { + wasm_runtime_set_exception(module, "failed to create wait node"); + os_mutex_unlock(&wait_info->wait_list_lock); + return -1; + } + memset(wait_node, 0, sizeof(AtomicWaitNode)); + + if (0 != os_mutex_init(&wait_node->wait_lock)) { + wasm_runtime_free(wait_node); + os_mutex_unlock(&wait_info->wait_list_lock); + return -1; + } + + if (0 != os_cond_init(&wait_node->wait_cond)) { + os_mutex_destroy(&wait_node->wait_lock); + wasm_runtime_free(wait_node); + os_mutex_unlock(&wait_info->wait_list_lock); + return -1; + } + + wait_node->status = S_WAITING; + + ret = bh_list_insert(wait_info->wait_list, wait_node); + bh_assert(ret == BH_LIST_SUCCESS); + (void)ret; + } + + os_mutex_unlock(&wait_info->wait_list_lock); + + /* condition wait start */ + os_mutex_lock(&wait_node->wait_lock); + + if (timeout < 0) + timeout = BHT_WAIT_FOREVER; + os_cond_reltimedwait(&wait_node->wait_cond, + &wait_node->wait_lock, timeout); + + os_mutex_unlock(&wait_node->wait_lock); + + /* Check the wait node status */ + os_mutex_lock(&wait_info->wait_list_lock); + check_ret = is_wait_node_exists(wait_info->wait_list, wait_node); + bh_assert(check_ret); + + is_timeout = wait_node->status == S_WAITING ? true : false; + + bh_list_remove(wait_info->wait_list, wait_node); + os_mutex_destroy(&wait_node->wait_lock); + os_cond_destroy(&wait_node->wait_cond); + wasm_runtime_free(wait_node); + os_mutex_unlock(&wait_info->wait_list_lock); + + release_wait_info(wait_map, wait_info, address); + + (void)check_ret; + return is_timeout ? 2 : 0; +} + +uint32 +wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, + void *address, uint32 count) +{ + uint32 notify_result; + AtomicWaitInfo *wait_info; + + /* Nobody wait on this address */ + wait_info = acquire_wait_info(address, false); + if (!wait_info) + return 0; + + os_mutex_lock(&wait_info->wait_list_lock); + notify_result = notify_wait_list(wait_info->wait_list, count); + os_mutex_unlock(&wait_info->wait_list_lock); + + release_wait_info(wait_map, wait_info, address); + + return notify_result; +} diff --git a/wamr/core/iwasm/common/wasm_shared_memory.h b/wamr/core/iwasm/common/wasm_shared_memory.h new file mode 100644 index 0000000..f05e595 --- /dev/null +++ b/wamr/core/iwasm/common/wasm_shared_memory.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_SHARED_MEMORY_H +#define _WASM_SHARED_MEMORY_H + +#include "bh_common.h" +#if WASM_ENABLE_INTERP != 0 +#include "wasm_runtime.h" +#endif +#if WASM_ENABLE_AOT != 0 +#include "aot_runtime.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WASMSharedMemNode { + bh_list_link l; + /* Lock */ + korp_mutex lock; + /* The module reference */ + WASMModuleCommon *module; + /* The memory information */ + WASMMemoryInstanceCommon *memory_inst; + + /* reference count */ + uint32 ref_count; +} WASMSharedMemNode; + +bool +wasm_shared_memory_init(); + +void +wasm_shared_memory_destroy(); + +WASMSharedMemNode* +wasm_module_get_shared_memory(WASMModuleCommon *module); + +int32 +shared_memory_inc_reference(WASMModuleCommon *module); + +int32 +shared_memory_dec_reference(WASMModuleCommon *module); + +WASMMemoryInstanceCommon* +shared_memory_get_memory_inst(WASMSharedMemNode *node); + +WASMSharedMemNode* +shared_memory_set_memory_inst(WASMModuleCommon *module, + WASMMemoryInstanceCommon *memory); + +uint32 +wasm_runtime_atomic_wait(WASMModuleInstanceCommon *module, void *address, + uint64 expect, int64 timeout, bool wait64); + +uint32 +wasm_runtime_atomic_notify(WASMModuleInstanceCommon *module, + void *address, uint32 count); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_SHARED_MEMORY_H */ diff --git a/wamr/core/iwasm/compilation/aot.c b/wamr/core/iwasm/compilation/aot.c new file mode 100644 index 0000000..04a97e2 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot.c @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot.h" + + +static char aot_error[128]; + +char* +aot_get_last_error() +{ + return aot_error[0] == '\0' ? "" : aot_error; +} + +void +aot_set_last_error(const char *error) +{ + if (error) + snprintf(aot_error, sizeof(aot_error), "Error: %s", error); + else + aot_error[0] = '\0'; +} + +static void +aot_destroy_mem_init_data_list(AOTMemInitData **data_list, uint32 count) +{ + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) + wasm_runtime_free(data_list[i]); + wasm_runtime_free(data_list); +} + +static AOTMemInitData ** +aot_create_mem_init_data_list(const WASMModule *module) +{ + AOTMemInitData **data_list; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTMemInitData *) * (uint64)module->data_seg_count; + if (size >= UINT32_MAX + || !(data_list = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(data_list, 0, size); + + /* Create each memory data segment */ + for (i = 0; i < module->data_seg_count; i++) { + size = offsetof(AOTMemInitData, bytes) + + (uint64)module->data_segments[i]->data_length; + if (size >= UINT32_MAX + || !(data_list[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + data_list[i]->is_passive = module->data_segments[i]->is_passive; + data_list[i]->memory_index = module->data_segments[i]->memory_index; +#endif + data_list[i]->offset = module->data_segments[i]->base_offset; + data_list[i]->byte_count = module->data_segments[i]->data_length; + memcpy(data_list[i]->bytes, module->data_segments[i]->data, + module->data_segments[i]->data_length); + } + + return data_list; + +fail: + aot_destroy_mem_init_data_list(data_list, module->data_seg_count); + return NULL; +} + +static void +aot_destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count) +{ + uint32 i; + for (i = 0; i < count; i++) + if (data_list[i]) + wasm_runtime_free(data_list[i]); + wasm_runtime_free(data_list); +} + +static AOTTableInitData ** +aot_create_table_init_data_list(const WASMModule *module) +{ + AOTTableInitData **data_list; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTTableInitData *) * (uint64)module->table_seg_count; + if (size >= UINT32_MAX + || !(data_list = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(data_list, 0, size); + + /* Create each table data segment */ + for (i = 0; i < module->table_seg_count; i++) { + size = offsetof(AOTTableInitData, func_indexes) + + sizeof(uint32) * (uint64)module->table_segments[i].function_count; + if (size >= UINT32_MAX + || !(data_list[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + data_list[i]->offset = module->table_segments[i].base_offset; + data_list[i]->func_index_count = module->table_segments[i].function_count; + memcpy(data_list[i]->func_indexes, module->table_segments[i].func_indexes, + sizeof(uint32) * module->table_segments[i].function_count); + } + + return data_list; + +fail: + aot_destroy_table_init_data_list(data_list, module->table_seg_count); + return NULL; +} + +static AOTImportGlobal * +aot_create_import_globals(const WASMModule *module, + uint32 *p_import_global_data_size) +{ + AOTImportGlobal *import_globals; + uint64 size; + uint32 i, data_offset = 0; + + /* Allocate memory */ + size = sizeof(AOTImportGlobal) * (uint64)module->import_global_count; + if (size >= UINT32_MAX + || !(import_globals = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(import_globals, 0, (uint32)size); + + /* Create each import global */ + for (i = 0; i < module->import_global_count; i++) { + WASMGlobalImport *import_global = &module->import_globals[i].u.global; + import_globals[i].module_name = import_global->module_name; + import_globals[i].global_name = import_global->field_name; + import_globals[i].type = import_global->type; + import_globals[i].is_mutable = import_global->is_mutable; + import_globals[i].global_data_linked = import_global->global_data_linked; + import_globals[i].size = wasm_value_type_size(import_global->type); + /* Calculate data offset */ + import_globals[i].data_offset = data_offset; + data_offset += wasm_value_type_size(import_global->type); + } + + *p_import_global_data_size = data_offset; + return import_globals; +} + +static AOTGlobal * +aot_create_globals(const WASMModule *module, + uint32 global_data_start_offset, + uint32 *p_global_data_size) +{ + AOTGlobal *globals; + uint64 size; + uint32 i, data_offset = global_data_start_offset; + + /* Allocate memory */ + size = sizeof(AOTGlobal) * (uint64)module->global_count; + if (size >= UINT32_MAX + || !(globals = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(globals, 0, (uint32)size); + + /* Create each global */ + for (i = 0; i < module->global_count; i++) { + WASMGlobal *global = &module->globals[i]; + globals[i].type = global->type; + globals[i].is_mutable = global->is_mutable; + globals[i].size = wasm_value_type_size(global->type); + memcpy(&globals[i].init_expr, &global->init_expr, + sizeof(global->init_expr)); + /* Calculate data offset */ + globals[i].data_offset = data_offset; + data_offset += wasm_value_type_size(global->type); + } + + *p_global_data_size = data_offset - global_data_start_offset; + return globals; +} + +static void +aot_destroy_func_types(AOTFuncType **func_types, uint32 count) +{ + uint32 i; + for (i = 0; i < count; i++) + if (func_types[i]) + wasm_runtime_free(func_types[i]); + wasm_runtime_free(func_types); +} + +static AOTFuncType ** +aot_create_func_types(const WASMModule *module) +{ + AOTFuncType **func_types; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTFuncType*) * (uint64)module->type_count; + if (size >= UINT32_MAX + || !(func_types = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(func_types, 0, size); + + /* Create each function type */ + for (i = 0; i < module->type_count; i++) { + size = offsetof(AOTFuncType, types) + + (uint64)module->types[i]->param_count + + (uint64)module->types[i]->result_count; + if (size >= UINT32_MAX + || !(func_types[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + memcpy(func_types[i], module->types[i], size); + } + + return func_types; + +fail: + aot_destroy_func_types(func_types, module->type_count); + return NULL; +} + +static AOTImportFunc * +aot_create_import_funcs(const WASMModule *module) +{ + AOTImportFunc *import_funcs; + uint64 size; + uint32 i, j; + + /* Allocate memory */ + size = sizeof(AOTImportFunc) * (uint64)module->import_function_count; + if (size >= UINT32_MAX + || !(import_funcs = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + /* Create each import function */ + for (i = 0; i < module->import_function_count; i++) { + WASMFunctionImport *import_func = &module->import_functions[i].u.function; + import_funcs[i].module_name = import_func->module_name; + 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; + import_funcs[i].attachment = import_func->attachment; + import_funcs[i].call_conv_raw = import_func->call_conv_raw; + /* Resolve function type index */ + for (j = 0; j < module->type_count; j++) + if (import_func->func_type == module->types[j]) { + import_funcs[i].func_type_index = j; + break; + } + } + + return import_funcs; +} + +static void +aot_destroy_funcs(AOTFunc **funcs, uint32 count) +{ + uint32 i; + + for (i = 0; i < count; i++) + if (funcs[i]) + wasm_runtime_free(funcs[i]); + wasm_runtime_free(funcs); +} + +static AOTFunc ** +aot_create_funcs(const WASMModule *module) +{ + AOTFunc **funcs; + uint64 size; + uint32 i, j; + + /* Allocate memory */ + size = sizeof(AOTFunc*) * (uint64)module->function_count; + if (size >= UINT32_MAX + || !(funcs = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(funcs, 0, size); + + /* Create each function */ + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + size = sizeof (AOTFunc); + if (!(funcs[i] = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + + funcs[i]->func_type = func->func_type; + + /* Resolve function type index */ + for (j = 0; j < module->type_count; j++) + if (func->func_type == module->types[j]) { + funcs[i]->func_type_index = j; + break; + } + + /* Resolve local variable info and code info */ + funcs[i]->local_count = func->local_count; + funcs[i]->local_types = func->local_types; + funcs[i]->param_cell_num = func->param_cell_num; + funcs[i]->local_cell_num = func->local_cell_num; + funcs[i]->code = func->code; + funcs[i]->code_size = func->code_size; + } + + return funcs; + +fail: + aot_destroy_funcs(funcs, module->function_count); + return NULL; +} + +AOTCompData* +aot_create_comp_data(WASMModule *module) +{ + AOTCompData *comp_data; + uint32 import_global_data_size = 0, global_data_size = 0, i, j; + uint64 size; + + /* Allocate memory */ + if (!(comp_data = wasm_runtime_malloc(sizeof(AOTCompData)))) { + aot_set_last_error("create compile data failed.\n"); + return NULL; + } + + memset(comp_data, 0, sizeof(AOTCompData)); + + comp_data->memory_count = module->import_memory_count + module->memory_count; + + /* TODO: create import memories */ + + /* Allocate memory for memory array, reserve one AOTMemory space at least */ + if (!comp_data->memory_count) + comp_data->memory_count = 1; + + size = (uint64)comp_data->memory_count * sizeof(AOTMemory); + if (size >= UINT32_MAX + || !(comp_data->memories = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("create memories array failed.\n"); + goto fail; + } + memset(comp_data->memories, 0, size); + + if (!(module->import_memory_count + module->memory_count)) { + comp_data->memories[0].num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + } + + /* Set memory page count */ + for (i = 0; i < module->import_memory_count + module->memory_count; i++) { + if (i < module->import_memory_count) { + comp_data->memories[i].memory_flags = + module->import_memories[i].u.memory.flags; + comp_data->memories[i].num_bytes_per_page = + module->import_memories[i].u.memory.num_bytes_per_page; + comp_data->memories[i].mem_init_page_count = + module->import_memories[i].u.memory.init_page_count; + comp_data->memories[i].mem_max_page_count = + module->import_memories[i].u.memory.max_page_count; + comp_data->memories[i].num_bytes_per_page = + module->import_memories[i].u.memory.num_bytes_per_page; + } + else { + j = i - module->import_memory_count; + comp_data->memories[i].memory_flags = + module->memories[j].flags; + comp_data->memories[i].num_bytes_per_page = + module->memories[j].num_bytes_per_page; + comp_data->memories[i].mem_init_page_count = + module->memories[j].init_page_count; + comp_data->memories[i].mem_max_page_count = + module->memories[j].max_page_count; + comp_data->memories[i].num_bytes_per_page = + module->memories[j].num_bytes_per_page; + } + } + + /* Create memory data segments */ + comp_data->mem_init_data_count = module->data_seg_count; + if (comp_data->mem_init_data_count > 0 + && !(comp_data->mem_init_data_list = + aot_create_mem_init_data_list(module))) + goto fail; + + /* TODO: create import tables */ + + /* Create tables */ + comp_data->table_count = module->import_table_count + module->table_count; + + if (comp_data->table_count > 0) { + size = sizeof(AOTTable) * (uint64)comp_data->table_count; + if (size >= UINT32_MAX + || !(comp_data->tables = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("create memories array failed.\n"); + goto fail; + } + memset(comp_data->tables, 0, size); + for (i = 0; i < comp_data->table_count; i++) { + if (i < module->import_table_count) { + comp_data->tables[i].elem_type = + module->import_tables[i].u.table.elem_type; + comp_data->tables[i].table_flags = + module->import_tables[i].u.table.flags; + comp_data->tables[i].table_init_size = + module->import_tables[i].u.table.init_size; + comp_data->tables[i].table_max_size = + module->import_tables[i].u.table.max_size; + } + else { + j = i - module->import_table_count; + comp_data->tables[i].elem_type = module->tables[i].elem_type; + comp_data->tables[i].table_flags = module->tables[i].flags; + comp_data->tables[i].table_init_size = module->tables[i].init_size; + comp_data->tables[i].table_max_size = module->tables[i].max_size; + } + } + } + + /* Create table data segments */ + comp_data->table_init_data_count = module->table_seg_count; + if (comp_data->table_init_data_count > 0 + && !(comp_data->table_init_data_list = + aot_create_table_init_data_list(module))) + goto fail; + + /* Create import globals */ + comp_data->import_global_count = module->import_global_count; + if (comp_data->import_global_count > 0 + && !(comp_data->import_globals = + aot_create_import_globals(module, &import_global_data_size))) + goto fail; + + /* Create globals */ + comp_data->global_count = module->global_count; + if (comp_data->global_count + && !(comp_data->globals = aot_create_globals + (module, import_global_data_size, &global_data_size))) + goto fail; + + comp_data->global_data_size = import_global_data_size + + global_data_size; + + /* Create function types */ + comp_data->func_type_count = module->type_count; + if (comp_data->func_type_count + && !(comp_data->func_types = aot_create_func_types(module))) + goto fail; + + /* Create import functions */ + comp_data->import_func_count = module->import_function_count; + if (comp_data->import_func_count + && !(comp_data->import_funcs = aot_create_import_funcs(module))) + goto fail; + + /* Create functions */ + comp_data->func_count = module->function_count; + if (comp_data->func_count + && !(comp_data->funcs = aot_create_funcs(module))) + goto fail; + + /* Create aux data/heap/stack information */ + comp_data->aux_data_end_global_index = module->aux_data_end_global_index; + comp_data->aux_data_end = module->aux_data_end; + comp_data->aux_heap_base_global_index = module->aux_heap_base_global_index; + comp_data->aux_heap_base = module->aux_heap_base; + comp_data->aux_stack_top_global_index = module->aux_stack_top_global_index; + comp_data->aux_stack_bottom = module->aux_stack_bottom; + comp_data->aux_stack_size = module->aux_stack_size; + + comp_data->start_func_index = module->start_function; + comp_data->malloc_func_index = module->malloc_function; + comp_data->free_func_index = module->free_function; + + comp_data->wasm_module = module; + + return comp_data; + +fail: + + aot_destroy_comp_data(comp_data); + return NULL; +} + +void +aot_destroy_comp_data(AOTCompData *comp_data) +{ + if (!comp_data) + return; + + if (comp_data->import_memories) + wasm_runtime_free(comp_data->import_memories); + + if (comp_data->memories) + wasm_runtime_free(comp_data->memories); + + if (comp_data->mem_init_data_list) + aot_destroy_mem_init_data_list(comp_data->mem_init_data_list, + comp_data->mem_init_data_count); + + if (comp_data->import_tables) + wasm_runtime_free(comp_data->import_tables); + + if (comp_data->tables) + wasm_runtime_free(comp_data->tables); + + if (comp_data->table_init_data_list) + aot_destroy_table_init_data_list(comp_data->table_init_data_list, + comp_data->table_init_data_count); + + if (comp_data->import_globals) + wasm_runtime_free(comp_data->import_globals); + + if (comp_data->globals) + wasm_runtime_free(comp_data->globals); + + if (comp_data->func_types) + aot_destroy_func_types(comp_data->func_types, + comp_data->func_type_count); + + if (comp_data->import_funcs) + wasm_runtime_free(comp_data->import_funcs); + + if (comp_data->funcs) + aot_destroy_funcs(comp_data->funcs, comp_data->func_count); + + wasm_runtime_free(comp_data); +} + diff --git a/wamr/core/iwasm/compilation/aot.h b/wamr/core/iwasm/compilation/aot.h new file mode 100644 index 0000000..ccceb75 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot.h @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_H_ +#define _AOT_H_ + +#include "bh_platform.h" +#include "bh_assert.h" +#include "../common/wasm_runtime_common.h" +#include "../interpreter/wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define AOT_FUNC_PREFIX "aot_func#" + +typedef InitializerExpression AOTInitExpr; +typedef WASMType AOTFuncType; +typedef WASMExport AOTExport; + +/** + * Import memory + */ +typedef struct AOTImportMemory { + char *module_name; + char *memory_name; + uint32 memory_flags; + uint32 num_bytes_per_page; + uint32 mem_init_page_count; + uint32 mem_max_page_count; +} AOTImportMemory; + +/** + * Memory information + */ +typedef struct AOTMemory { + /* memory info */ + uint32 memory_flags; + uint32 num_bytes_per_page; + uint32 mem_init_page_count; + uint32 mem_max_page_count; +} AOTMemory; + +/** + * A segment of memory init data + */ +typedef struct AOTMemInitData { +#if WASM_ENABLE_BULK_MEMORY != 0 + /* Passive flag */ + bool is_passive; + /* memory index */ + uint32 memory_index; +#endif + /* Start address of init data */ + AOTInitExpr offset; + /* Byte count */ + uint32 byte_count; + /* Byte array */ + uint8 bytes[1]; +} AOTMemInitData; + +/** + * Import table + */ +typedef struct AOTImportTable { + char *module_name; + char *table_name; + uint32 table_flags; + uint32 table_init_size; + uint32 table_max_size; +} AOTImportTable; + +/** + * Table + */ +typedef struct AOTTable { + uint32 elem_type; + uint32 table_flags; + uint32 table_init_size; + uint32 table_max_size; +} AOTTable; + +/** + * A segment of table init data + */ +typedef struct AOTTableInitData { + uint32 table_index; + /* Start address of init data */ + AOTInitExpr offset; + /* Function index count */ + uint32 func_index_count; + /* Function index array */ + uint32 func_indexes[1]; +} AOTTableInitData; + +/** + * Import global variable + */ +typedef struct AOTImportGlobal { + char *module_name; + char *global_name; + /* VALUE_TYPE_I32/I64/F32/F64 */ + uint8 type; + bool is_mutable; + uint32 size; + /* The data offset of current global in global data */ + uint32 data_offset; + /* global data after linked */ + WASMValue global_data_linked; +} AOTImportGlobal; + +/** + * Global variable + */ +typedef struct AOTGlobal { + /* VALUE_TYPE_I32/I64/F32/F64 */ + uint8 type; + bool is_mutable; + uint32 size; + /* The data offset of current global in global data */ + uint32 data_offset; + AOTInitExpr init_expr; +} AOTGlobal; + +/** + * Import function + */ +typedef struct AOTImportFunc { + char *module_name; + char *func_name; + AOTFuncType *func_type; + uint32 func_type_index; + /* function pointer after linked */ + void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; + /* attachment */ + void *attachment; + bool call_conv_raw; +} AOTImportFunc; + +/** + * Function + */ +typedef struct AOTFunc { + AOTFuncType *func_type; + uint32 func_type_index; + uint32 local_count; + uint8 *local_types; + uint16 param_cell_num; + uint16 local_cell_num; + uint32 code_size; + uint8 *code; +} AOTFunc; + +typedef struct AOTCompData { + /* Import memories */ + uint32 import_memory_count; + AOTImportMemory *import_memories; + + /* Memories */ + uint32 memory_count; + AOTMemory *memories; + + /* Memory init data info */ + uint32 mem_init_data_count; + AOTMemInitData **mem_init_data_list; + + /* Import tables */ + uint32 import_table_count; + AOTImportTable *import_tables; + + /* Tables */ + uint32 table_count; + AOTTable *tables; + + /* Table init data info */ + uint32 table_init_data_count; + AOTTableInitData **table_init_data_list; + + /* Import globals */ + uint32 import_global_count; + AOTImportGlobal *import_globals; + + /* Globals */ + uint32 global_count; + AOTGlobal *globals; + + /* Function types */ + uint32 func_type_count; + AOTFuncType **func_types; + + /* Import functions */ + uint32 import_func_count; + AOTImportFunc *import_funcs; + + /* Functions */ + uint32 func_count; + AOTFunc **funcs; + + uint32 global_data_size; + + uint32 start_func_index; + uint32 malloc_func_index; + uint32 free_func_index; + + uint32 aux_data_end_global_index; + uint32 aux_data_end; + uint32 aux_heap_base_global_index; + uint32 aux_heap_base; + uint32 aux_stack_top_global_index; + uint32 aux_stack_bottom; + uint32 aux_stack_size; + + WASMModule *wasm_module; +} AOTCompData; + +AOTCompData* +aot_create_comp_data(WASMModule *module); + +void +aot_destroy_comp_data(AOTCompData *comp_data); + +char* +aot_get_last_error(); + +void +aot_set_last_error(const char *error); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_compiler.c b/wamr/core/iwasm/compilation/aot_compiler.c new file mode 100644 index 0000000..d813300 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_compiler.c @@ -0,0 +1,1121 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_compiler.h" +#include "aot_emit_compare.h" +#include "aot_emit_conversion.h" +#include "aot_emit_memory.h" +#include "aot_emit_variable.h" +#include "aot_emit_const.h" +#include "aot_emit_exception.h" +#include "aot_emit_numberic.h" +#include "aot_emit_control.h" +#include "aot_emit_function.h" +#include "aot_emit_parametric.h" +#include "../aot/aot_runtime.h" +#include "../interpreter/wasm_opcode.h" +#include + + +#define CHECK_BUF(buf, buf_end, length) do { \ + if (buf + length > buf_end) { \ + aot_set_last_error("read leb failed: unexpected end."); \ + return false; \ + } \ +} while (0) + +static bool +read_leb(const uint8 *buf, const uint8 *buf_end, + uint32 *p_offset, uint32 maxbits, + bool sign, uint64 *p_result) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 bcnt = 0; + uint64 byte; + + while (true) { + CHECK_BUF(buf, buf_end, 1); + byte = buf[*p_offset]; + *p_offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + bcnt += 1; + } + if (bcnt > (((maxbits + 8) >> 3) - (maxbits + 8))) { + aot_set_last_error("read leb failed: unsigned leb overflow."); + return false; + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= (uint64)(- (((uint64)1) << shift)); + } + *p_result = result; + return true; +} + +#define read_leb_uint32(p, p_end, res) do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(p, p_end, &off, 32, false, &res64)) \ + return false; \ + p += off; \ + res = (uint32)res64; \ +} while (0) + +#define read_leb_int32(p, p_end, res) do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(p, p_end, &off, 32, true, &res64)) \ + return false; \ + p += off; \ + res = (int32)res64; \ +} while (0) + +#define read_leb_int64(p, p_end, res) do { \ + uint32 off = 0; \ + uint64 res64; \ + if (!read_leb(p, p_end, &off, 64, true, &res64)) \ + return false; \ + p += off; \ + res = (int64)res64; \ +} while (0) + +#define COMPILE_ATOMIC_RMW(OP, NAME) \ + case WASM_OP_ATOMIC_RMW_I32_##NAME: \ + bytes = 4; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME: \ + bytes = 8; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I32_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I32; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##8_U: \ + bytes = 1; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##16_U: \ + bytes = 2; \ + op_type = VALUE_TYPE_I64; \ + goto OP_ATOMIC_##OP; \ + case WASM_OP_ATOMIC_RMW_I64_##NAME##32_U: \ + bytes = 4; \ + op_type = VALUE_TYPE_I64; \ +OP_ATOMIC_##OP: \ + bin_op = LLVMAtomicRMWBinOp##OP; \ + goto build_atomic_rmw; + +static bool +aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) +{ + AOTFuncContext *func_ctx = comp_ctx->func_ctxes[func_index]; + uint8 *frame_ip = func_ctx->aot_func->code, opcode, *p_f32, *p_f64; + uint8 *frame_ip_end = frame_ip + func_ctx->aot_func->code_size; + uint8 *param_types = NULL; + uint8 *result_types = NULL; + uint8 value_type; + uint16 param_count; + uint16 result_count; + uint32 br_depth, *br_depths, br_count; + uint32 func_idx, type_idx, mem_idx, local_idx, global_idx, i; + uint32 bytes = 4, align, offset; + uint32 type_index; + bool sign = true; + int32 i32_const; + int64 i64_const; + float32 f32_const; + float64 f64_const; + AOTFuncType *func_type = NULL; + + /* Start to translate the opcodes */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, + func_ctx->block_stack.block_list_head + ->llvm_entry_block); + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + switch (opcode) { + case WASM_OP_UNREACHABLE: + if (!aot_compile_op_unreachable(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + value_type = *frame_ip++; + if (value_type == VALUE_TYPE_I32 + || value_type == VALUE_TYPE_I64 + || value_type == VALUE_TYPE_F32 + || value_type == VALUE_TYPE_F64 + || value_type == VALUE_TYPE_VOID) { + param_count = 0; + param_types = NULL; + if (value_type == VALUE_TYPE_VOID) { + result_count = 0; + result_types = NULL; + } + else { + result_count = 1; + result_types = &value_type; + } + } + else { + frame_ip--; + read_leb_uint32(frame_ip, frame_ip_end, type_index); + func_type = comp_ctx->comp_data->func_types[type_index]; + param_count = func_type->param_count; + param_types = func_type->types; + result_count = func_type->result_count; + result_types = func_type->types + param_count; + } + if (!aot_compile_op_block(comp_ctx, func_ctx, + &frame_ip, frame_ip_end, + (uint32)(LABEL_TYPE_BLOCK + opcode - WASM_OP_BLOCK), + param_count, param_types, + result_count, result_types)) + return false; + break; + + case WASM_OP_ELSE: + if (!aot_compile_op_else(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_END: + if (!aot_compile_op_end(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_BR: + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!aot_compile_op_br(comp_ctx, func_ctx, br_depth, &frame_ip)) + return false; + break; + + case WASM_OP_BR_IF: + read_leb_uint32(frame_ip, frame_ip_end, br_depth); + if (!aot_compile_op_br_if(comp_ctx, func_ctx, br_depth, &frame_ip)) + return false; + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(frame_ip, frame_ip_end, br_count); + if (!(br_depths = + wasm_runtime_malloc((uint32)sizeof(uint32) * (br_count + 1)))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (i = 0; i <= br_count; i++) + read_leb_uint32(frame_ip, frame_ip_end, br_depths[i]); + + if (!aot_compile_op_br_table(comp_ctx, func_ctx, + br_depths, br_count, &frame_ip)) { + wasm_runtime_free(br_depths); + return false; + } + + wasm_runtime_free(br_depths); + break; + + case WASM_OP_RETURN: + if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) + return false; + break; + + case WASM_OP_CALL: + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!aot_compile_op_call(comp_ctx, func_ctx, func_idx, &frame_ip)) + return false; + break; + + case WASM_OP_CALL_INDIRECT: + read_leb_uint32(frame_ip, frame_ip_end, type_idx); + frame_ip++; /* skip 0x00 */ + if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx)) + return false; + break; + + case WASM_OP_DROP: + if (!aot_compile_op_drop(comp_ctx, func_ctx, true)) + return false; + break; + + case WASM_OP_DROP_64: + if (!aot_compile_op_drop(comp_ctx, func_ctx, false)) + return false; + break; + + case WASM_OP_SELECT: + if (!aot_compile_op_select(comp_ctx, func_ctx, true)) + return false; + break; + + case WASM_OP_SELECT_64: + if (!aot_compile_op_select(comp_ctx, func_ctx, false)) + return false; + break; + + case WASM_OP_GET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_get_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_SET_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_set_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_TEE_LOCAL: + read_leb_uint32(frame_ip, frame_ip_end, local_idx); + if (!aot_compile_op_tee_local(comp_ctx, func_ctx, local_idx)) + return false; + break; + + case WASM_OP_GET_GLOBAL: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!aot_compile_op_get_global(comp_ctx, func_ctx, global_idx)) + return false; + break; + + case WASM_OP_SET_GLOBAL: + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + if (!aot_compile_op_set_global(comp_ctx, func_ctx, global_idx)) + return false; + break; + + case WASM_OP_I32_LOAD: + bytes = 4; + sign = true; + goto op_i32_load; + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I32_LOAD8_S) ? true : false; + goto op_i32_load; + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I32_LOAD16_S) ? true : false; + op_i32_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i32_load(comp_ctx, func_ctx, align, offset, + bytes, sign, false)) + return false; + break; + + case WASM_OP_I64_LOAD: + bytes = 8; + sign = true; + goto op_i64_load; + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + bytes = 1; + sign = (opcode == WASM_OP_I64_LOAD8_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + bytes = 2; + sign = (opcode == WASM_OP_I64_LOAD16_S) ? true : false; + goto op_i64_load; + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + bytes = 4; + sign = (opcode == WASM_OP_I64_LOAD32_S) ? true : false; + op_i64_load: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i64_load(comp_ctx, func_ctx, align, offset, + bytes, sign, false)) + return false; + break; + + case WASM_OP_F32_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f32_load(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_F64_LOAD: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f64_load(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_I32_STORE: + bytes = 4; + goto op_i32_store; + case WASM_OP_I32_STORE8: + bytes = 1; + goto op_i32_store; + case WASM_OP_I32_STORE16: + bytes = 2; + op_i32_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i32_store(comp_ctx, func_ctx, align, + offset, bytes, false)) + return false; + break; + + case WASM_OP_I64_STORE: + bytes = 8; + goto op_i64_store; + case WASM_OP_I64_STORE8: + bytes = 1; + goto op_i64_store; + case WASM_OP_I64_STORE16: + bytes = 2; + goto op_i64_store; + case WASM_OP_I64_STORE32: + bytes = 4; + op_i64_store: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_i64_store(comp_ctx, func_ctx, align, + offset, bytes, false)) + return false; + break; + + case WASM_OP_F32_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f32_store(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_F64_STORE: + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + if (!aot_compile_op_f64_store(comp_ctx, func_ctx, align, offset)) + return false; + break; + + case WASM_OP_MEMORY_SIZE: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!aot_compile_op_memory_size(comp_ctx, func_ctx)) + return false; + (void)mem_idx; + break; + + case WASM_OP_MEMORY_GROW: + read_leb_uint32(frame_ip, frame_ip_end, mem_idx); + if (!aot_compile_op_memory_grow(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_CONST: + read_leb_int32(frame_ip, frame_ip_end, i32_const); + if (!aot_compile_op_i32_const(comp_ctx, func_ctx, i32_const)) + return false; + break; + + case WASM_OP_I64_CONST: + read_leb_int64(frame_ip, frame_ip_end, i64_const); + if (!aot_compile_op_i64_const(comp_ctx, func_ctx, i64_const)) + return false; + break; + + case WASM_OP_F32_CONST: + p_f32 = (uint8*)&f32_const; + for (i = 0; i < sizeof(float32); i++) + *p_f32++ = *frame_ip++; + if (!aot_compile_op_f32_const(comp_ctx, func_ctx, f32_const)) + return false; + break; + + case WASM_OP_F64_CONST: + p_f64 = (uint8*)&f64_const; + for (i = 0; i < sizeof(float64); i++) + *p_f64++ = *frame_ip++; + if (!aot_compile_op_f64_const(comp_ctx, func_ctx, f64_const)) + return false; + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + if (!aot_compile_op_i32_compare(comp_ctx, func_ctx, + INT_EQZ + opcode - WASM_OP_I32_EQZ)) + return false; + break; + + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + if (!aot_compile_op_i64_compare(comp_ctx, func_ctx, + INT_EQZ + opcode - WASM_OP_I64_EQZ)) + return false; + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + if (!aot_compile_op_f32_compare(comp_ctx, func_ctx, + FLOAT_EQ + opcode - WASM_OP_F32_EQ)) + return false; + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + if (!aot_compile_op_f64_compare(comp_ctx, func_ctx, + FLOAT_EQ + opcode - WASM_OP_F64_EQ)) + return false; + break; + + case WASM_OP_I32_CLZ: + if (!aot_compile_op_i32_clz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_CTZ: + if (!aot_compile_op_i32_ctz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_POPCNT: + if (!aot_compile_op_i32_popcnt(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + if (!aot_compile_op_i32_arithmetic(comp_ctx, func_ctx, + INT_ADD + opcode - WASM_OP_I32_ADD, + &frame_ip)) + return false; + break; + + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + if (!aot_compile_op_i32_bitwise(comp_ctx, func_ctx, + INT_SHL + opcode - WASM_OP_I32_AND)) + return false; + break; + + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + if (!aot_compile_op_i32_shift(comp_ctx, func_ctx, + INT_SHL + opcode - WASM_OP_I32_SHL)) + return false; + break; + + case WASM_OP_I64_CLZ: + if (!aot_compile_op_i64_clz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_CTZ: + if (!aot_compile_op_i64_ctz(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_POPCNT: + if (!aot_compile_op_i64_popcnt(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + if (!aot_compile_op_i64_arithmetic(comp_ctx, func_ctx, + INT_ADD + opcode - WASM_OP_I64_ADD, + &frame_ip)) + return false; + break; + + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + if (!aot_compile_op_i64_bitwise(comp_ctx, func_ctx, + INT_SHL + opcode - WASM_OP_I64_AND)) + return false; + break; + + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + if (!aot_compile_op_i64_shift(comp_ctx, func_ctx, + INT_SHL + opcode - WASM_OP_I64_SHL)) + return false; + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + if (!aot_compile_op_f32_math(comp_ctx, func_ctx, + FLOAT_ABS + opcode - WASM_OP_F32_ABS)) + return false; + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + if (!aot_compile_op_f32_arithmetic(comp_ctx, func_ctx, + FLOAT_ADD + opcode - WASM_OP_F32_ADD)) + return false; + break; + + case WASM_OP_F32_COPYSIGN: + if (!aot_compile_op_f32_copysign(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + if (!aot_compile_op_f64_math(comp_ctx, func_ctx, + FLOAT_ABS + opcode - WASM_OP_F64_ABS)) + return false; + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + if (!aot_compile_op_f64_arithmetic(comp_ctx, func_ctx, + FLOAT_ADD + opcode - WASM_OP_F64_ADD)) + return false; + break; + + case WASM_OP_F64_COPYSIGN: + if (!aot_compile_op_f64_copysign(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_WRAP_I64: + if (!aot_compile_op_i32_wrap_i64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_S_F32) ? true : false; + if (!aot_compile_op_i32_trunc_f32(comp_ctx, func_ctx, sign, false)) + return false; + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_S_F64) ? true : false; + if (!aot_compile_op_i32_trunc_f64(comp_ctx, func_ctx, sign, false)) + return false; + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + sign = (opcode == WASM_OP_I64_EXTEND_S_I32) ? true : false; + if (!aot_compile_op_i64_extend_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_S_F32) ? true : false; + if (!aot_compile_op_i64_trunc_f32(comp_ctx, func_ctx, sign, false)) + return false; + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_S_F64) ? true : false; + if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, false)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + sign = (opcode == WASM_OP_F32_CONVERT_S_I32) ? true : false; + if (!aot_compile_op_f32_convert_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + sign = (opcode == WASM_OP_F32_CONVERT_S_I64) ? true : false; + if (!aot_compile_op_f32_convert_i64(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F32_DEMOTE_F64: + if (!aot_compile_op_f32_demote_f64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + sign = (opcode == WASM_OP_F64_CONVERT_S_I32) ? true : false; + if (!aot_compile_op_f64_convert_i32(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + sign = (opcode == WASM_OP_F64_CONVERT_S_I64) ? true : false; + if (!aot_compile_op_f64_convert_i64(comp_ctx, func_ctx, sign)) + return false; + break; + + case WASM_OP_F64_PROMOTE_F32: + if (!aot_compile_op_f64_promote_f32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_REINTERPRET_F32: + if (!aot_compile_op_i32_reinterpret_f32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I64_REINTERPRET_F64: + if (!aot_compile_op_i64_reinterpret_f64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F32_REINTERPRET_I32: + if (!aot_compile_op_f32_reinterpret_i32(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_F64_REINTERPRET_I64: + if (!aot_compile_op_f64_reinterpret_i64(comp_ctx, func_ctx)) + return false; + break; + + case WASM_OP_I32_EXTEND8_S: + if (!aot_compile_op_i32_extend_i32(comp_ctx, func_ctx, 8)) + return false; + break; + + case WASM_OP_I32_EXTEND16_S: + if (!aot_compile_op_i32_extend_i32(comp_ctx, func_ctx, 16)) + return false; + break; + + case WASM_OP_I64_EXTEND8_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 8)) + return false; + break; + + case WASM_OP_I64_EXTEND16_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 16)) + return false; + break; + + case WASM_OP_I64_EXTEND32_S: + if (!aot_compile_op_i64_extend_i64(comp_ctx, func_ctx, 32)) + return false; + break; + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + opcode = (uint32)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F32) ? true : false; + if (!aot_compile_op_i32_trunc_f32(comp_ctx, func_ctx, sign, true)) + return false; + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I32_TRUNC_SAT_S_F64) ? true : false; + if (!aot_compile_op_i32_trunc_f64(comp_ctx, func_ctx, sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F32) ? true : false; + if (!aot_compile_op_i64_trunc_f32(comp_ctx, func_ctx, sign, true)) + return false; + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + sign = (opcode == WASM_OP_I64_TRUNC_SAT_S_F64) ? true : false; + if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, true)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + frame_ip ++; + if (!aot_compile_op_memory_init(comp_ctx, func_ctx, seg_index)) + return false; + break; + } + case WASM_OP_DATA_DROP: + { + uint32 seg_index; + read_leb_uint32(frame_ip, frame_ip_end, seg_index); + if (!aot_compile_op_data_drop(comp_ctx, func_ctx, seg_index)) + return false; + break; + } + case WASM_OP_MEMORY_COPY: + { + frame_ip += 2; + if (!aot_compile_op_memory_copy(comp_ctx, func_ctx)) + return false; + break; + } + case WASM_OP_MEMORY_FILL: + { + frame_ip ++; + if (!aot_compile_op_memory_fill(comp_ctx, func_ctx)) + return false; + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + default: + break; + } + break; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + uint8 bin_op, op_type; + + if (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + } + if (opcode != WASM_OP_ATOMIC_FENCE) { + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + } + switch (opcode) { + case WASM_OP_ATOMIC_WAIT32: + if (!aot_compile_op_atomic_wait(comp_ctx, func_ctx, VALUE_TYPE_I32, + align, offset, 4)) + return false; + break; + case WASM_OP_ATOMIC_WAIT64: + if (!aot_compile_op_atomic_wait(comp_ctx, func_ctx, VALUE_TYPE_I64, + align, offset, 8)) + return false; + break; + case WASM_OP_ATOMIC_NOTIFY: + if (!aot_compiler_op_atomic_notify(comp_ctx, func_ctx, align, + offset, bytes)) + return false; + break; + case WASM_OP_ATOMIC_I32_LOAD: + bytes = 4; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD8_U: + bytes = 1; + goto op_atomic_i32_load; + case WASM_OP_ATOMIC_I32_LOAD16_U: + bytes = 2; + op_atomic_i32_load: + if (!aot_compile_op_i32_load(comp_ctx, func_ctx, align, + offset, bytes, sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_LOAD: + bytes = 8; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD8_U: + bytes = 1; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD16_U: + bytes = 2; + goto op_atomic_i64_load; + case WASM_OP_ATOMIC_I64_LOAD32_U: + bytes = 4; + op_atomic_i64_load: + if (!aot_compile_op_i64_load(comp_ctx, func_ctx, align, + offset, bytes, sign, true)) + return false; + break; + + case WASM_OP_ATOMIC_I32_STORE: + bytes = 4; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE8: + bytes = 1; + goto op_atomic_i32_store; + case WASM_OP_ATOMIC_I32_STORE16: + bytes = 2; + op_atomic_i32_store: + if (!aot_compile_op_i32_store(comp_ctx, func_ctx, align, + offset, bytes, true)) + return false; + break; + + case WASM_OP_ATOMIC_I64_STORE: + bytes = 8; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE8: + bytes = 1; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE16: + bytes = 2; + goto op_atomic_i64_store; + case WASM_OP_ATOMIC_I64_STORE32: + bytes = 4; + op_atomic_i64_store: + if (!aot_compile_op_i64_store(comp_ctx, func_ctx, align, + offset, bytes, true)) + return false; + break; + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + bytes = 4; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + bytes = 8; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I32; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + bytes = 1; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + bytes = 2; + op_type = VALUE_TYPE_I64; + goto op_atomic_cmpxchg; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + bytes = 4; + op_type = VALUE_TYPE_I64; + op_atomic_cmpxchg: + if (!aot_compile_op_atomic_cmpxchg(comp_ctx, func_ctx, + op_type, align, + offset, bytes)) + return false; + break; + + COMPILE_ATOMIC_RMW(Add, ADD); + COMPILE_ATOMIC_RMW(Sub, SUB); + COMPILE_ATOMIC_RMW(And, AND); + COMPILE_ATOMIC_RMW(Or, OR); + COMPILE_ATOMIC_RMW(Xor, XOR); + COMPILE_ATOMIC_RMW(Xchg, XCHG); + +build_atomic_rmw: + if (!aot_compile_op_atomic_rmw(comp_ctx, func_ctx, + bin_op, op_type, + align, offset, bytes)) + return false; + break; + + default: + break; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + + default: + break; + } + } + + /* Move func_return block to the bottom */ + if (func_ctx->func_return_block) { + LLVMBasicBlockRef last_block = + LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->func_return_block) + LLVMMoveBasicBlockAfter(func_ctx->func_return_block, + last_block); + } + + /* Move got_exception block to the bottom */ + if (func_ctx->got_exception_block) { + LLVMBasicBlockRef last_block = + LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->got_exception_block) + LLVMMoveBasicBlockAfter(func_ctx->got_exception_block, + last_block); + + /* Move all other exception blocks before got_exception block */ + for (i = 0; i < EXCE_NUM; i++) { + if (func_ctx->exception_blocks[i]) + LLVMMoveBasicBlockBefore(func_ctx->exception_blocks[i], + func_ctx->got_exception_block); + } + } + return true; + +fail: + return false; +} + +bool +aot_compile_wasm(AOTCompContext *comp_ctx) +{ + char *msg = NULL; + bool ret; + uint32 i; + + bh_print_time("Begin to compile WASM bytecode to LLVM IR"); + + for (i = 0; i < comp_ctx->func_ctx_count; i++) + if (!aot_compile_func(comp_ctx, i)) { +#if 0 + LLVMDumpModule(comp_ctx->module); + char *err; + LLVMTargetMachineEmitToFile(comp_ctx->target_machine, comp_ctx->module, + "./test.o", LLVMObjectFile, &err); +#endif + return false; + } + +#if 0 + LLVMDumpModule(comp_ctx->module); + /* Clear error no, LLVMDumpModule may set errno */ + errno = 0; +#endif + + bh_print_time("Begin to verify LLVM module"); + + ret = LLVMVerifyModule(comp_ctx->module, LLVMPrintMessageAction, &msg); + if (!ret && msg) { + if (msg[0] != '\0') { + aot_set_last_error(msg); + LLVMDisposeMessage(msg); + return false; + } + LLVMDisposeMessage(msg); + } + + bh_print_time("Begin to run function optimization passes"); + + if (comp_ctx->optimize) { + LLVMInitializeFunctionPassManager(comp_ctx->pass_mgr); + for (i = 0; i < comp_ctx->func_ctx_count; i++) + LLVMRunFunctionPassManager(comp_ctx->pass_mgr, + comp_ctx->func_ctxes[i]->func); + } + + return true; +} + +bool +aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name) +{ + char *err = NULL; + + bh_print_time("Begin to emit LLVM IR file"); + + if (LLVMPrintModuleToFile(comp_ctx->module, file_name, &err) != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("emit llvm ir to file failed."); + return false; + } + + return true; +} + +bool +aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name) +{ + char *err = NULL; + + bh_print_time("Begin to emit object file"); + + if (LLVMTargetMachineEmitToFile(comp_ctx->target_machine, + comp_ctx->module, + file_name, + LLVMObjectFile, + &err) != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("emit elf to memory buffer failed."); + return false; + } + + return true; +} + diff --git a/wamr/core/iwasm/compilation/aot_compiler.h b/wamr/core/iwasm/compilation/aot_compiler.h new file mode 100644 index 0000000..b44ab4d --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_compiler.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_COMPILER_H_ +#define _AOT_COMPILER_H_ + +#include "aot.h" +#include "aot_llvm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum IntCond { + INT_EQZ = 0, + INT_EQ, + INT_NE, + INT_LT_S, + INT_LT_U, + INT_GT_S, + INT_GT_U, + INT_LE_S, + INT_LE_U, + INT_GE_S, + INT_GE_U +} IntCond; + +typedef enum FloatCond { + FLOAT_EQ = 0, + FLOAT_NE, + FLOAT_LT, + FLOAT_GT, + FLOAT_LE, + FLOAT_GE +} FloatCond; + +typedef enum IntArithmetic { + INT_ADD = 0, + INT_SUB, + INT_MUL, + INT_DIV_S, + INT_DIV_U, + INT_REM_S, + INT_REM_U +} IntArithmetic; + +typedef enum IntBitwise { + INT_AND = 0, + INT_OR, + INT_XOR, +} IntBitwise; + +typedef enum IntShift { + INT_SHL = 0, + INT_SHR_S, + INT_SHR_U, + INT_ROTL, + INT_ROTR +} IntShift; + +typedef enum FloatMath { + FLOAT_ABS = 0, + FLOAT_NEG, + FLOAT_CEIL, + FLOAT_FLOOR, + FLOAT_TRUNC, + FLOAT_NEAREST, + FLOAT_SQRT +} FloatMath; + +typedef enum FloatArithmetic { + FLOAT_ADD = 0, + FLOAT_SUB, + FLOAT_MUL, + FLOAT_DIV, + FLOAT_MIN, + FLOAT_MAX +} FloatArithmetic; + +#define CHECK_STACK() do { \ + if (!func_ctx->block_stack.block_list_end) { \ + aot_set_last_error("WASM block stack underflow."); \ + goto fail; \ + } \ + if (!func_ctx->block_stack.block_list_end-> \ + value_stack.value_list_end) { \ + aot_set_last_error("WASM data stack underflow."); \ + goto fail; \ + } \ + } while (0) + +#define POP(llvm_value, value_type) do { \ + AOTValue *aot_value; \ + CHECK_STACK(); \ + aot_value = aot_value_stack_pop \ + (&func_ctx->block_stack.block_list_end->value_stack); \ + if ((value_type != VALUE_TYPE_I32 \ + && aot_value->type != value_type) \ + || (value_type == VALUE_TYPE_I32 \ + && (aot_value->type != VALUE_TYPE_I32 \ + && aot_value->type != VALUE_TYPE_I1))) { \ + aot_set_last_error("invalid WASM stack data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + if (aot_value->type == value_type) \ + llvm_value = aot_value->value; \ + else { \ + bh_assert(aot_value->type == VALUE_TYPE_I1); \ + if (!(llvm_value = LLVMBuildZExt(comp_ctx->builder, \ + aot_value->value, I32_TYPE, "i1toi32"))) { \ + aot_set_last_error("invalid WASM stack data type.");\ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + } \ + wasm_runtime_free(aot_value); \ + } while (0) + +#define POP_I32(v) POP(v, VALUE_TYPE_I32) +#define POP_I64(v) POP(v, VALUE_TYPE_I64) +#define POP_F32(v) POP(v, VALUE_TYPE_F32) +#define POP_F64(v) POP(v, VALUE_TYPE_F64) + +#define POP_COND(llvm_value) do { \ + AOTValue *aot_value; \ + CHECK_STACK(); \ + aot_value = aot_value_stack_pop \ + (&func_ctx->block_stack.block_list_end->value_stack); \ + if (aot_value->type != VALUE_TYPE_I1 \ + && aot_value->type != VALUE_TYPE_I32) { \ + aot_set_last_error("invalid WASM stack data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + if (aot_value->type == VALUE_TYPE_I1) \ + llvm_value = aot_value->value; \ + else { \ + if (!(llvm_value = LLVMBuildICmp(comp_ctx->builder, \ + LLVMIntNE, aot_value->value, I32_ZERO, \ + "i1_cond"))){ \ + aot_set_last_error("llvm build trunc failed."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + } \ + wasm_runtime_free(aot_value); \ + } while (0) + +#define PUSH(llvm_value, value_type) do { \ + AOTValue *aot_value; \ + if (!func_ctx->block_stack.block_list_end) { \ + aot_set_last_error("WASM block stack underflow."); \ + goto fail; \ + } \ + aot_value = wasm_runtime_malloc(sizeof(AOTValue)); \ + memset(aot_value, 0, sizeof(AOTValue)); \ + if (!aot_value) { \ + aot_set_last_error("allocate memory failed."); \ + goto fail; \ + } \ + aot_value->type = value_type; \ + aot_value->value = llvm_value; \ + aot_value_stack_push \ + (&func_ctx->block_stack.block_list_end->value_stack,\ + aot_value); \ + } while (0) + +#define PUSH_I32(v) PUSH(v, VALUE_TYPE_I32) +#define PUSH_I64(v) PUSH(v, VALUE_TYPE_I64) +#define PUSH_F32(v) PUSH(v, VALUE_TYPE_F32) +#define PUSH_F64(v) PUSH(v, VALUE_TYPE_F64) +#define PUSH_COND(v) PUSH(v, VALUE_TYPE_I1) + +#define TO_LLVM_TYPE(wasm_type) \ + wasm_type_to_llvm_type(&comp_ctx->basic_types, wasm_type) + +#define I32_TYPE comp_ctx->basic_types.int32_type +#define I64_TYPE comp_ctx->basic_types.int64_type +#define F32_TYPE comp_ctx->basic_types.float32_type +#define F64_TYPE comp_ctx->basic_types.float64_type +#define VOID_TYPE comp_ctx->basic_types.void_type +#define INT1_TYPE comp_ctx->basic_types.int1_type +#define INT8_TYPE comp_ctx->basic_types.int8_type +#define INT16_TYPE comp_ctx->basic_types.int16_type +#define MD_TYPE comp_ctx->basic_types.meta_data_type +#define INT8_PTR_TYPE comp_ctx->basic_types.int8_ptr_type +#define INT16_PTR_TYPE comp_ctx->basic_types.int16_ptr_type +#define INT32_PTR_TYPE comp_ctx->basic_types.int32_ptr_type +#define INT64_PTR_TYPE comp_ctx->basic_types.int64_ptr_type +#define F32_PTR_TYPE comp_ctx->basic_types.float32_ptr_type +#define F64_PTR_TYPE comp_ctx->basic_types.float64_ptr_type + +#define I32_CONST(v) LLVMConstInt(I32_TYPE, v, true) +#define I64_CONST(v) LLVMConstInt(I64_TYPE, v, true) +#define F32_CONST(v) LLVMConstReal(F32_TYPE, v) +#define F64_CONST(v) LLVMConstReal(F64_TYPE, v) +#define I8_CONST(v) LLVMConstInt(INT8_TYPE, v, true) + +#define I8_ZERO (comp_ctx->llvm_consts.i8_zero) +#define I32_ZERO (comp_ctx->llvm_consts.i32_zero) +#define I64_ZERO (comp_ctx->llvm_consts.i64_zero) +#define F32_ZERO (comp_ctx->llvm_consts.f32_zero) +#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) +#define I64_NEG_ONE (comp_ctx->llvm_consts.i64_neg_one) +#define I32_MIN (comp_ctx->llvm_consts.i32_min) +#define I64_MIN (comp_ctx->llvm_consts.i64_min) +#define I32_31 (comp_ctx->llvm_consts.i32_31) +#define I32_32 (comp_ctx->llvm_consts.i32_32) +#define I64_63 (comp_ctx->llvm_consts.i64_63) +#define I64_64 (comp_ctx->llvm_consts.i64_64) + +#define CHECK_LLVM_CONST(v) do { \ + if (!v) { \ + aot_set_last_error("create llvm const failed."); \ + goto fail; \ + } \ + } while (0) + +bool +aot_compile_wasm(AOTCompContext *comp_ctx); + +bool +aot_emit_llvm_file(AOTCompContext *comp_ctx, const char *file_name); + +bool +aot_emit_aot_file(AOTCompContext *comp_ctx, + AOTCompData *comp_data, + const char *file_name); + +bool +aot_emit_object_file(AOTCompContext *comp_ctx, char *file_name); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_COMPILER_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_aot_file.c b/wamr/core/iwasm/compilation/aot_emit_aot_file.c new file mode 100644 index 0000000..1d27d4f --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_aot_file.c @@ -0,0 +1,2104 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_compiler.h" +#include "../aot/aot_runtime.h" + +#define PUT_U64_TO_ADDR(addr, value) do { \ + union { uint64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + ((uint32*)(addr))[0] = u.parts[0]; \ + ((uint32*)(addr))[1] = u.parts[1]; \ + } while (0) + +#define CHECK_SIZE(size) do { \ + if (size == (uint32)-1) { \ + aot_set_last_error("get symbol size failed."); \ + return (uint32)-1; \ + } \ + } while (0) + +/* Internal function in object file */ +typedef struct AOTObjectFunc { + char *func_name; + uint64 text_offset; +} AOTObjectFunc; + +/* Symbol table list node */ +typedef struct AOTSymbolNode { + struct AOTSymbolNode *next; + uint32 str_len; + char *symbol; +} AOTSymbolNode; + +typedef struct AOTSymbolList { + AOTSymbolNode *head; + AOTSymbolNode *end; + uint32 len; +} AOTSymbolList; + +/* AOT object data */ +typedef struct AOTObjectData { + LLVMMemoryBufferRef mem_buf; + LLVMBinaryRef binary; + + AOTTargetInfo target_info; + + void *text; + uint32 text_size; + + /* literal data and size */ + void *literal; + uint32 literal_size; + + AOTObjectDataSection *data_sections; + uint32 data_sections_count; + + AOTObjectFunc *funcs; + uint32 func_count; + + AOTSymbolList symbol_list; + AOTRelocationGroup *relocation_groups; + uint32 relocation_group_count; +} AOTObjectData; + +#if 0 +static void dump_buf(uint8 *buf, uint32 size, char *title) +{ + int i; + printf("------ %s -------", title); + for (i = 0; i < size; i++) { + if ((i % 16) == 0) + printf("\n"); + printf("%02x ", (unsigned char)buf[i]); + } + printf("\n\n"); +} +#endif + +static bool +is_32bit_binary(LLVMBinaryRef binary) +{ + LLVMBinaryType type = LLVMBinaryGetType(binary); + return (type == LLVMBinaryTypeELF32L || type == LLVMBinaryTypeELF32B); +} + +static bool +is_little_endian_binary(LLVMBinaryRef binary) +{ + LLVMBinaryType type = LLVMBinaryGetType(binary); + return (type == LLVMBinaryTypeELF32L || type == LLVMBinaryTypeELF64L); +} + +static bool +str_starts_with(const char *str, const char *prefix) +{ + size_t len_pre = strlen(prefix), len_str = strlen(str); + return (len_str >= len_pre) && !memcmp(str, prefix, len_pre); +} + +static uint32 +get_file_header_size() +{ + /* magic number (4 bytes) + version (4 bytes) */ + return sizeof(uint32) + sizeof(uint32); +} + +static uint32 +get_string_size(const char *s) +{ + /* string size (2 bytes) + string content without '\0' */ + return (uint32)sizeof(uint16) + (uint32)strlen(s); +} + +static uint32 +get_target_info_section_size() +{ + return sizeof(AOTTargetInfo); +} + +static uint32 +get_mem_init_data_size(AOTMemInitData *mem_init_data) +{ + /* init expr type (4 bytes) + init expr value (8 bytes) + + byte count (4 bytes) + bytes */ + uint32 total_size = + (uint32)(sizeof(uint32) + sizeof(uint64) + + sizeof(uint32) + mem_init_data->byte_count); + + /* bulk_memory enabled: + is_passive (4 bytes) + memory_index (4 bytes) + bulk memory disabled: + placeholder (4 bytes) + placeholder (4 bytes) + */ + total_size += (sizeof(uint32) + sizeof(uint32)); + + return total_size; +} + +static uint32 +get_mem_init_data_list_size(AOTMemInitData **mem_init_data_list, + uint32 mem_init_data_count) +{ + AOTMemInitData **mem_init_data = mem_init_data_list; + uint32 size = 0, i; + + for (i = 0; i < mem_init_data_count; i++, mem_init_data++) { + size = align_uint(size, 4); + size += get_mem_init_data_size(*mem_init_data); + } + return size; +} + +static uint32 +get_import_memory_size(AOTCompData *comp_data) +{ + /* currently we only emit import_memory_count = 0 */ + return sizeof(uint32); +} + +static uint32 +get_memory_size(AOTCompData *comp_data) +{ + /* memory_count + count * (memory_flags + num_bytes_per_page + + init_page_count + max_page_count) */ + return (uint32)(sizeof(uint32) + + comp_data->memory_count * sizeof(uint32) * 4); +} + +static uint32 +get_mem_info_size(AOTCompData *comp_data) +{ + /* import_memory_size + memory_size + + init_data_count + init_data_list */ + return get_import_memory_size(comp_data) + + get_memory_size(comp_data) + + (uint32)sizeof(uint32) + + get_mem_init_data_list_size(comp_data->mem_init_data_list, + comp_data->mem_init_data_count); +} + +static uint32 +get_table_init_data_size(AOTTableInitData *table_init_data) +{ + /* table_index + init expr type (4 bytes) + init expr value (8 bytes) + + func index count (4 bytes) + func indexes */ + return (uint32)(sizeof(uint32) + sizeof(uint32) + + sizeof(uint64) + sizeof(uint32) + + sizeof(uint32) * table_init_data->func_index_count); +} + +static uint32 +get_table_init_data_list_size(AOTTableInitData **table_init_data_list, + uint32 table_init_data_count) +{ + AOTTableInitData **table_init_data = table_init_data_list; + uint32 size = 0, i; + + for (i = 0; i < table_init_data_count; i++, table_init_data++) { + size = align_uint(size, 4); + size += get_table_init_data_size(*table_init_data); + } + return size; +} + +static uint32 +get_import_table_size(AOTCompData *comp_data) +{ + /* currently we only emit import_table_count = 0 */ + return sizeof(uint32); +} + +static uint32 +get_table_size(AOTCompData *comp_data) +{ + /* table_count + table_count * (elem_type + table_flags + * + init_size + max_size) */ + return (uint32)(sizeof(uint32) + + comp_data->table_count * sizeof(uint32) * 4); +} + +static uint32 +get_table_info_size(AOTCompData *comp_data) +{ + /* import_table size + table_size + + init data count + init data list */ + return get_import_table_size(comp_data) + + get_table_size(comp_data) + + (uint32)sizeof(uint32) + + get_table_init_data_list_size(comp_data->table_init_data_list, + comp_data->table_init_data_count); +} + +static uint32 +get_func_type_size(AOTFuncType *func_type) +{ + /* param count + result count + types */ + return (uint32)sizeof(uint32) * 2 + + func_type->param_count + func_type->result_count; +} + +static uint32 +get_func_types_size(AOTFuncType **func_types, uint32 func_type_count) +{ + AOTFuncType **func_type = func_types; + uint32 size = 0, i; + + for (i = 0; i < func_type_count; i++, func_type++) { + size = align_uint(size, 4); + size += get_func_type_size(*func_type); + } + return size; +} + +static uint32 +get_func_type_info_size(AOTCompData *comp_data) +{ + /* func type count + func type list */ + return (uint32)sizeof(uint32) + + get_func_types_size(comp_data->func_types, + comp_data->func_type_count); +} + +static uint32 +get_import_global_size(AOTImportGlobal *import_global) +{ + /* type (1 byte) + is_mutable (1 byte) + module_name + global_name */ + uint32 size = (uint32)sizeof(uint8) * 2 + + get_string_size(import_global->module_name); + size = align_uint(size, 2); + size += get_string_size(import_global->global_name); + return size; +} + +static uint32 +get_import_globals_size(AOTImportGlobal *import_globals, + uint32 import_global_count) +{ + AOTImportGlobal *import_global = import_globals; + uint32 size = 0, i; + + for (i = 0; i < import_global_count; i++, import_global++) { + size = align_uint(size, 2); + size += get_import_global_size(import_global); + } + return size; +} + +static uint32 +get_import_global_info_size(AOTCompData *comp_data) +{ + /* import global count + import globals */ + return (uint32)sizeof(uint32) + + get_import_globals_size(comp_data->import_globals, + comp_data->import_global_count); +} + +static uint32 +get_global_size(AOTGlobal *global) +{ + /* type (1 byte) + is_mutable (1 byte) + + init expr type (2 byes) + init expr value (8 byes) */ + return sizeof(uint8) * 2 + sizeof(uint16) + sizeof(uint64); +} + +static uint32 +get_globals_size(AOTGlobal *globals, uint32 global_count) +{ + AOTGlobal *global = globals; + uint32 size = 0, i; + + for (i = 0; i < global_count; i++, global++) { + size = align_uint(size, 4); + size += get_global_size(global); + } + return size; +} + +static uint32 +get_global_info_size(AOTCompData *comp_data) +{ + /* global count + globals */ + return (uint32)sizeof(uint32) + + get_globals_size(comp_data->globals, + comp_data->global_count); +} + +static uint32 +get_import_func_size(AOTImportFunc *import_func) +{ + /* type index (2 bytes) + module_name + func_name */ + uint32 size = (uint32)sizeof(uint16) + + get_string_size(import_func->module_name); + size = align_uint(size, 2); + size += get_string_size(import_func->func_name); + return size; +} + +static uint32 +get_import_funcs_size(AOTImportFunc *import_funcs, + uint32 import_func_count) +{ + AOTImportFunc *import_func = import_funcs; + uint32 size = 0, i; + + for (i = 0; i < import_func_count; i++, import_func++) { + size = align_uint(size, 2); + size += get_import_func_size(import_func); + } + return size; +} + +static uint32 +get_import_func_info_size(AOTCompData *comp_data) +{ + /* import func count + import funcs */ + return (uint32)sizeof(uint32) + + get_import_funcs_size(comp_data->import_funcs, + comp_data->import_func_count); +} + +static uint32 +get_object_data_section_size(AOTObjectDataSection *data_section) +{ + /* name + size + data */ + uint32 size = get_string_size(data_section->name); + size = align_uint(size, 4); + size += (uint32)sizeof(uint32); + size += data_section->size; + return size; +} + +static uint32 +get_object_data_sections_size(AOTObjectDataSection *data_sections, + uint32 data_sections_count) +{ + AOTObjectDataSection *data_section = data_sections; + uint32 size = 0, i; + + for (i = 0; i < data_sections_count; i++, data_section++) { + size = align_uint(size, 2); + size += get_object_data_section_size(data_section); + } + return size; +} + +static uint32 +get_object_data_section_info_size(AOTObjectData *obj_data) +{ + /* data sections count + data sections */ + return (uint32)sizeof(uint32) + + get_object_data_sections_size(obj_data->data_sections, + obj_data->data_sections_count); +} + +static uint32 +get_init_data_section_size(AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 size = 0; + + size += get_mem_info_size(comp_data); + + size = align_uint(size, 4); + size += get_table_info_size(comp_data); + + size = align_uint(size, 4); + size += get_func_type_info_size(comp_data); + + size = align_uint(size, 4); + size += get_import_global_info_size(comp_data); + + size = align_uint(size, 4); + size += get_global_info_size(comp_data); + + size = align_uint(size, 4); + size += get_import_func_info_size(comp_data); + + /* func count + start func index */ + size = align_uint(size, 4); + size += (uint32)sizeof(uint32) * 2; + + /* aux data/heap/stack data */ + size += sizeof(uint32) * 7; + + size += get_object_data_section_info_size(obj_data); + return size; +} + +static uint32 +get_text_section_size(AOTObjectData *obj_data) +{ + return (sizeof(uint32) + obj_data->literal_size + obj_data->text_size + 3) & ~3; +} + +static uint32 +get_func_section_size(AOTCompData *comp_data, AOTObjectData *obj_data) +{ + /* text offsets + function type indexs */ + uint32 size = 0; + + if (is_32bit_binary(obj_data->binary)) + size = (uint32)sizeof(uint32) * comp_data->func_count; + else + size = (uint32)sizeof(uint64) * comp_data->func_count; + + size += (uint32)sizeof(uint32) * comp_data->func_count; + return size; +} + +static uint32 +get_export_size(AOTExport *export) +{ + /* export index + export kind + 1 byte padding + export name */ + return (uint32)sizeof(uint32) + sizeof(uint8) + 1 + + get_string_size(export->name); +} + +static uint32 +get_exports_size(AOTExport *exports, uint32 export_count) +{ + AOTExport *export = exports; + uint32 size = 0, i; + + for (i = 0; i < export_count; i++, export++) { + size = align_uint(size, 4); + size += get_export_size(export); + } + return size; +} + +static uint32 +get_export_section_size(AOTCompData *comp_data) +{ + /* export count + exports */ + return (uint32)sizeof(uint32) + + get_exports_size(comp_data->wasm_module->exports, + comp_data->wasm_module->export_count); +} + +static uint32 +get_relocation_size(AOTRelocation *relocation, bool is_32bin) +{ + /* offset + addend + relocation type + symbol name */ + uint32 size = 0; + if (is_32bin) + size = sizeof(uint32) * 2; /* offset and addend */ + else + size = sizeof(uint64) * 2; /* offset and addend */ + size += (uint32)sizeof(uint32); /* relocation type */ + size += (uint32)sizeof(uint32); /* symbol name index */ + return size; +} + +static uint32 +get_relocations_size(AOTRelocation *relocations, + uint32 relocation_count, + bool is_32bin) +{ + AOTRelocation *relocation = relocations; + uint32 size = 0, i; + + for (i = 0; i < relocation_count; i++, relocation++) { + size = align_uint(size, 4); + size += get_relocation_size(relocation, is_32bin); + } + return size; +} + +static uint32 +get_relocation_group_size(AOTRelocationGroup *relocation_group, + bool is_32bin) +{ + uint32 size = 0; + /* section name index + relocation count + relocations */ + size += (uint32)sizeof(uint32); + size += (uint32)sizeof(uint32); + size += get_relocations_size(relocation_group->relocations, + relocation_group->relocation_count, + is_32bin); + return size; +} + +static uint32 +get_relocation_groups_size(AOTRelocationGroup *relocation_groups, + uint32 relocation_group_count, + bool is_32bin) +{ + AOTRelocationGroup *relocation_group = relocation_groups; + uint32 size = 0, i; + + for (i = 0; i < relocation_group_count; i++, relocation_group++) { + size = align_uint(size, 4); + size += get_relocation_group_size(relocation_group, is_32bin); + } + return size; +} + +/* return the index (in order of insertion) of the symbol, + create if not exits, -1 if failed */ +static uint32 +get_relocation_symbol_index(const char *symbol_name, + bool *is_new, + AOTSymbolList *symbol_list) +{ + AOTSymbolNode *sym; + uint32 index = 0; + + sym = symbol_list->head; + while (sym) { + if (!strcmp(sym->symbol, symbol_name)) { + if (is_new) + *is_new = false; + return index; + } + + sym = sym->next; + index ++; + } + + /* Not found in symbol_list, add it */ + sym = wasm_runtime_malloc(sizeof(AOTSymbolNode)); + if (!sym) { + return (uint32)-1; + } + + memset(sym, 0, sizeof(AOTSymbolNode)); + sym->symbol = (char *)symbol_name; + sym->str_len = (uint32)strlen(symbol_name); + + if (!symbol_list->head) { + symbol_list->head = symbol_list->end = sym; + } + else { + symbol_list->end->next = sym; + symbol_list->end = sym; + } + symbol_list->len ++; + + if (is_new) + *is_new = true; + return index; +} + +static uint32 +get_relocation_symbol_size(AOTRelocation *relocation, + AOTSymbolList *symbol_list) +{ + uint32 size = 0, index = 0; + bool is_new = false; + + index = get_relocation_symbol_index(relocation->symbol_name, &is_new, symbol_list); + CHECK_SIZE(index); + + if (is_new) { + size += (uint32)sizeof(uint16); + size += (uint32)strlen(relocation->symbol_name); + size = align_uint(size, 2); + } + + relocation->symbol_index = index; + return size; +} + +static uint32 +get_relocations_symbol_size(AOTRelocation *relocations, + uint32 relocation_count, + AOTSymbolList *symbol_list) +{ + AOTRelocation *relocation = relocations; + uint32 size = 0, curr_size, i; + + for (i = 0; i < relocation_count; i++, relocation++) { + curr_size = get_relocation_symbol_size(relocation, symbol_list); + CHECK_SIZE(curr_size); + + size += curr_size; + } + return size; +} + +static uint32 +get_relocation_group_symbol_size(AOTRelocationGroup *relocation_group, + AOTSymbolList *symbol_list) +{ + uint32 size = 0, index = 0, curr_size; + bool is_new = false; + + index = get_relocation_symbol_index(relocation_group->section_name, + &is_new, + symbol_list); + CHECK_SIZE(index); + + if (is_new) { + size += (uint32)sizeof(uint16); + size += (uint32)strlen(relocation_group->section_name); + size = align_uint(size, 2); + } + + relocation_group->name_index = index; + + curr_size = get_relocations_symbol_size(relocation_group->relocations, + relocation_group->relocation_count, + symbol_list); + CHECK_SIZE(curr_size); + size += curr_size; + + return size; +} + +static uint32 +get_relocation_groups_symbol_size(AOTRelocationGroup *relocation_groups, + uint32 relocation_group_count, + AOTSymbolList *symbol_list) +{ + AOTRelocationGroup *relocation_group = relocation_groups; + uint32 size = 0, curr_size, i; + + for (i = 0; i < relocation_group_count; i++, relocation_group++) { + curr_size = get_relocation_group_symbol_size(relocation_group, + symbol_list); + CHECK_SIZE(curr_size); + size += curr_size; + } + return size; +} + +static uint32 +get_symbol_size_from_symbol_list(AOTSymbolList *symbol_list) +{ + AOTSymbolNode *sym; + uint32 size = 0; + + sym = symbol_list->head; + while (sym) { + /* (uint16)str_len + str */ + size += (uint32)sizeof(uint16) + sym->str_len; + size = align_uint(size, 2); + sym = sym->next; + } + + return size; +} + +static uint32 +get_relocation_section_symbol_size(AOTObjectData *obj_data) +{ + AOTRelocationGroup *relocation_groups = obj_data->relocation_groups; + uint32 relocation_group_count = obj_data->relocation_group_count; + uint32 string_count = 0, symbol_table_size = 0; + + /* section size will be calculated twice, + get symbol size from symbol list directly in the second calculation */ + if (obj_data->symbol_list.len > 0) { + symbol_table_size = + get_symbol_size_from_symbol_list(&obj_data->symbol_list); + } + else { + symbol_table_size = + get_relocation_groups_symbol_size(relocation_groups, + relocation_group_count, + &obj_data->symbol_list); + } + CHECK_SIZE(symbol_table_size); + string_count = obj_data->symbol_list.len; + + /* string_count + string_offsets + total_string_len + [str (string_len + str)] */ + return (uint32)(sizeof(uint32) + sizeof(uint32) * string_count + + sizeof(uint32) + symbol_table_size); +} + +static uint32 +get_relocation_section_size(AOTObjectData *obj_data) +{ + AOTRelocationGroup *relocation_groups = obj_data->relocation_groups; + uint32 relocation_group_count = obj_data->relocation_group_count; + uint32 symbol_table_size = 0; + + symbol_table_size = get_relocation_section_symbol_size(obj_data); + CHECK_SIZE(symbol_table_size); + symbol_table_size = align_uint(symbol_table_size, 4); + + /* relocation group count + symbol_table + relocation groups */ + return (uint32)sizeof(uint32) + symbol_table_size + + get_relocation_groups_size(relocation_groups, + relocation_group_count, + is_32bit_binary(obj_data->binary)); +} + +static uint32 +get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) +{ + uint32 size = 0; + + /* aot file header */ + size += get_file_header_size(); + + /* target info section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_target_info_section_size(); + + /* init data section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_init_data_section_size(comp_data, obj_data); + + /* text section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_text_section_size(obj_data); + + /* function section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_func_section_size(comp_data, obj_data); + + /* export section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_export_section_size(comp_data); + + /* relocation section */ + size = align_uint(size, 4); + /* section id + section size */ + size += (uint32)sizeof(uint32) * 2; + size += get_relocation_section_size(obj_data); + + return size; +} + +#define exchange_uint8(p_data) (void)0 + +static void +exchange_uint16(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 1); + *(p_data + 1) = value; +} + +static void +exchange_uint32(uint8 *p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static void +exchange_uint64(uint8 *pData) +{ + exchange_uint32(pData); + exchange_uint32(pData + 4); +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +#define CHECK_BUF(length) do { \ + if (buf + offset + length > buf_end) { \ + aot_set_last_error("buf overflow"); \ + return false; \ + } \ +} while (0) + +#define EMIT_U8(v) do { \ + CHECK_BUF(1); \ + *(uint8*)(buf + offset) = (uint8)v; \ + offset++; \ + } while (0) + +#define EMIT_U16(v) do { \ + uint16 t = (uint16)v; \ + CHECK_BUF(2); \ + if (!is_little_endian()) \ + exchange_uint16((uint8*)&t); \ + *(uint16*)(buf + offset) = t; \ + offset += (uint32)sizeof(uint16); \ + } while (0) + +#define EMIT_U32(v) do { \ + uint32 t = (uint32)v; \ + CHECK_BUF(4); \ + if (!is_little_endian()) \ + exchange_uint32((uint8*)&t); \ + *(uint32*)(buf + offset) = t; \ + offset += (uint32)sizeof(uint32); \ + } while (0) + +#define EMIT_U64(v) do { \ + uint64 t = (uint64)v; \ + CHECK_BUF(8); \ + if (!is_little_endian()) \ + exchange_uint64((uint8*)&t); \ + PUT_U64_TO_ADDR(buf + offset, t); \ + offset += (uint32)sizeof(uint64); \ + } while (0) + +#define EMIT_BUF(v, len) do { \ + CHECK_BUF(len); \ + memcpy(buf + offset, v, len); \ + offset += len; \ + } while (0) + +#define EMIT_STR(s) do { \ + uint32 str_len = (uint32)strlen(s); \ + EMIT_U16(str_len); \ + EMIT_BUF(s, str_len); \ + } while (0) + +static bool +aot_emit_file_header(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset; + uint32 aot_curr_version = AOT_CURRENT_VERSION; + + EMIT_U8('\0'); + EMIT_U8('a'); + EMIT_U8('o'); + EMIT_U8('t'); + + EMIT_U32(aot_curr_version); + + *p_offset = offset; + return true; +} + +static bool +aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset; + uint32 section_size = get_target_info_section_size(); + AOTTargetInfo *target_info = &obj_data->target_info; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_TARGET_INFO); + EMIT_U32(section_size); + + EMIT_U16(target_info->bin_type); + EMIT_U16(target_info->abi_type); + EMIT_U16(target_info->e_type); + EMIT_U16(target_info->e_machine); + EMIT_U32(target_info->e_version); + EMIT_U32(target_info->e_flags); + EMIT_U32(target_info->reserved); + EMIT_BUF(target_info->arch, sizeof(target_info->arch)); + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit target info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTMemInitData **init_datas = comp_data->mem_init_data_list; + + *p_offset = offset = align_uint(offset, 4); + + /* Emit import memory count, only emit 0 currently. + TODO: emit the actual import memory count and + the full import memory info. */ + EMIT_U32(0); + + /* Emit memory count */ + EMIT_U32(comp_data->memory_count); + /* Emit memory items */ + for (i = 0; i < comp_data->memory_count; i++) { + EMIT_U32(comp_data->memories[i].memory_flags); + EMIT_U32(comp_data->memories[i].num_bytes_per_page); + EMIT_U32(comp_data->memories[i].mem_init_page_count); + EMIT_U32(comp_data->memories[i].mem_max_page_count); + } + + /* Emit mem init data count */ + EMIT_U32(comp_data->mem_init_data_count); + /* Emit mem init data items */ + for (i = 0; i < comp_data->mem_init_data_count; i++) { + offset = align_uint(offset, 4); +#if WASM_ENABLE_BULK_MEMORY != 0 + if (comp_ctx->enable_bulk_memory) { + EMIT_U32(init_datas[i]->is_passive); + EMIT_U32(init_datas[i]->memory_index); + } + else +#endif + { + /* emit two placeholder to keep the same size */ + EMIT_U32(0); + EMIT_U32(0); + } + EMIT_U32(init_datas[i]->offset.init_expr_type); + EMIT_U64(init_datas[i]->offset.u.i64); + EMIT_U32(init_datas[i]->byte_count); + EMIT_BUF(init_datas[i]->bytes, init_datas[i]->byte_count); + } + + if (offset - *p_offset != get_mem_info_size(comp_data)) { + aot_set_last_error("emit memory info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i, j; + AOTTableInitData **init_datas = comp_data->table_init_data_list; + + *p_offset = offset = align_uint(offset, 4); + + /* Emit import table count, only emit 0 currently. + TODO: emit the actual import table count and + the full import table info. */ + EMIT_U32(0); + + /* Emit table count */ + EMIT_U32(comp_data->table_count); + /* Emit table items */ + for (i = 0; i < comp_data->table_count; i++) { + EMIT_U32(comp_data->tables[i].elem_type); + EMIT_U32(comp_data->tables[i].table_flags); + EMIT_U32(comp_data->tables[i].table_init_size); + EMIT_U32(comp_data->tables[i].table_max_size); + } + + /* Emit table init data count */ + EMIT_U32(comp_data->table_init_data_count); + /* Emit table init data items */ + for (i = 0; i < comp_data->table_init_data_count; i++) { + offset = align_uint(offset, 4); + EMIT_U32(init_datas[i]->table_index); + EMIT_U32(init_datas[i]->offset.init_expr_type); + EMIT_U64(init_datas[i]->offset.u.i64); + EMIT_U32(init_datas[i]->func_index_count); + for (j = 0; j < init_datas[i]->func_index_count; j++) + EMIT_U32(init_datas[i]->func_indexes[j]); + } + + if (offset - *p_offset != get_table_info_size(comp_data)) { + aot_set_last_error("emit table info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_func_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTFuncType **func_types = comp_data->func_types; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(comp_data->func_type_count); + + for (i = 0; i < comp_data->func_type_count; i++) { + offset = align_uint(offset, 4); + EMIT_U32(func_types[i]->param_count); + EMIT_U32(func_types[i]->result_count); + EMIT_BUF(func_types[i]->types, + func_types[i]->param_count + func_types[i]->result_count); + } + + if (offset - *p_offset != get_func_type_info_size(comp_data)) { + aot_set_last_error("emit function type info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_import_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTImportGlobal *import_global = comp_data->import_globals; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(comp_data->import_global_count); + + for (i = 0; i < comp_data->import_global_count; i++, import_global++) { + offset = align_uint(offset, 2); + EMIT_U8(import_global->type); + EMIT_U8(import_global->is_mutable); + EMIT_STR(import_global->module_name); + offset = align_uint(offset, 2); + EMIT_STR(import_global->global_name); + } + + if (offset - *p_offset != get_import_global_info_size(comp_data)) { + aot_set_last_error("emit import global info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_global_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTGlobal *global = comp_data->globals; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(comp_data->global_count); + + for (i = 0; i < comp_data->global_count; i++, global++) { + offset = align_uint(offset, 4); + EMIT_U8(global->type); + EMIT_U8(global->is_mutable); + EMIT_U16(global->init_expr.init_expr_type); + EMIT_U64(global->init_expr.u.i64); + } + + if (offset - *p_offset != get_global_info_size(comp_data)) { + aot_set_last_error("emit global info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_import_func_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTImportFunc *import_func = comp_data->import_funcs; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(comp_data->import_func_count); + + for (i = 0; i < comp_data->import_func_count; i++, import_func++) { + offset = align_uint(offset, 2); + EMIT_U16(import_func->func_type_index); + EMIT_STR(import_func->module_name); + offset = align_uint(offset, 2); + EMIT_STR(import_func->func_name); + } + + if (offset - *p_offset != get_import_func_info_size(comp_data)) { + aot_set_last_error("emit import function info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTObjectData *obj_data) +{ + uint32 offset = *p_offset, i; + AOTObjectDataSection *data_section = obj_data->data_sections; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(obj_data->data_sections_count); + + for (i = 0; i < obj_data->data_sections_count; i++, data_section++) { + offset = align_uint(offset, 2); + EMIT_STR(data_section->name); + offset = align_uint(offset, 4); + EMIT_U32(data_section->size); + EMIT_BUF(data_section->data, data_section->size); + } + + if (offset - *p_offset != get_object_data_section_info_size(obj_data)) { + aot_set_last_error("emit object data section info failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompContext *comp_ctx, AOTCompData *comp_data, + AOTObjectData *obj_data) +{ + uint32 section_size = get_init_data_section_size(comp_data, obj_data); + uint32 offset = *p_offset; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_INIT_DATA); + EMIT_U32(section_size); + + if (!aot_emit_mem_info(buf, buf_end, &offset, comp_ctx, comp_data, obj_data) + || !aot_emit_table_info(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_func_type_info(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_import_global_info(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_global_info(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_import_func_info(buf, buf_end, &offset, comp_data, obj_data)) + return false; + + offset = align_uint(offset, 4); + EMIT_U32(comp_data->func_count); + EMIT_U32(comp_data->start_func_index); + + EMIT_U32(comp_data->aux_data_end_global_index); + EMIT_U32(comp_data->aux_data_end); + EMIT_U32(comp_data->aux_heap_base_global_index); + EMIT_U32(comp_data->aux_heap_base); + EMIT_U32(comp_data->aux_stack_top_global_index); + EMIT_U32(comp_data->aux_stack_bottom); + EMIT_U32(comp_data->aux_stack_size); + + if (!aot_emit_object_data_section_info(buf, buf_end, &offset, obj_data)) + return false; + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit init data section failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_text_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 section_size = get_text_section_size(obj_data); + uint32 offset = *p_offset; + uint8 placeholder = 0; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_TEXT); + EMIT_U32(section_size); + EMIT_U32(obj_data->literal_size); + if (obj_data->literal_size > 0) + EMIT_BUF(obj_data->literal, obj_data->literal_size); + EMIT_BUF(obj_data->text, obj_data->text_size); + + while (offset & 3) + EMIT_BUF(&placeholder, 1); + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit text section failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 section_size = get_func_section_size(comp_data, obj_data); + uint32 i, offset = *p_offset; + AOTObjectFunc *func = obj_data->funcs; + AOTFunc **funcs = comp_data->funcs; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_FUNCTION); + EMIT_U32(section_size); + + for (i = 0; i < obj_data->func_count; i++, func++) { + if (is_32bit_binary(obj_data->binary)) + EMIT_U32(func->text_offset); + else + EMIT_U64(func->text_offset); + } + + for (i = 0; i < comp_data->func_count; i++) + EMIT_U32(funcs[i]->func_type_index); + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit function section failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_export_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 section_size = get_export_section_size(comp_data); + AOTExport *export = comp_data->wasm_module->exports; + uint32 export_count = comp_data->wasm_module->export_count; + uint32 i, offset = *p_offset; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_EXPORT); + EMIT_U32(section_size); + EMIT_U32(export_count); + + for (i = 0; i < export_count; i++, export++) { + offset = align_uint(offset, 4); + EMIT_U32(export->index); + EMIT_U8(export->kind); + EMIT_U8(0); + EMIT_STR(export->name); + } + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit export section failed."); + return false; + } + + *p_offset = offset; + + return true; +} + +static bool +aot_emit_relocation_symbol_table(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 symbol_offset = 0, total_string_len = 0; + uint32 offset = *p_offset; + AOTSymbolNode *sym; + + EMIT_U32(obj_data->symbol_list.len); + + /* emit symbol offsets */ + sym = (AOTSymbolNode *)(obj_data->symbol_list.head); + while(sym) { + EMIT_U32(symbol_offset); + /* string_len + str[0 .. string_len - 1] */ + symbol_offset += (uint32)sizeof(uint16) + sym->str_len; + symbol_offset = align_uint(symbol_offset, 2); + sym = sym->next; + } + + /* emit total string len */ + total_string_len = symbol_offset; + EMIT_U32(total_string_len); + + /* emit symbols */ + sym = (AOTSymbolNode *)(obj_data->symbol_list.head); + while (sym) { + EMIT_STR(sym->symbol); + offset = align_uint(offset, 2); + sym = sym->next; + } + + *p_offset = offset; + return true; +} + +static bool +aot_emit_relocation_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset, + AOTCompData *comp_data, AOTObjectData *obj_data) +{ + uint32 section_size = get_relocation_section_size(obj_data); + uint32 i, offset = *p_offset; + AOTRelocationGroup *relocation_group = obj_data->relocation_groups; + + if (section_size == (uint32)-1) + return false; + + *p_offset = offset = align_uint(offset, 4); + + EMIT_U32(AOT_SECTION_TYPE_RELOCATION); + EMIT_U32(section_size); + + aot_emit_relocation_symbol_table(buf, buf_end, &offset, comp_data, obj_data); + + offset = align_uint(offset, 4); + EMIT_U32(obj_data->relocation_group_count); + + /* emit each relocation group */ + for (i = 0; i < obj_data->relocation_group_count; i++, relocation_group++) { + AOTRelocation *relocation = relocation_group->relocations; + uint32 j; + + offset = align_uint(offset, 4); + EMIT_U32(relocation_group->name_index); + offset = align_uint(offset, 4); + EMIT_U32(relocation_group->relocation_count); + + /* emit each relocation */ + for (j = 0; j < relocation_group->relocation_count; j++, relocation++) { + offset = align_uint(offset, 4); + if (is_32bit_binary(obj_data->binary)) { + EMIT_U32(relocation->relocation_offset); + EMIT_U32(relocation->relocation_addend); + } + else { + EMIT_U64(relocation->relocation_offset); + EMIT_U64(relocation->relocation_addend); + } + EMIT_U32(relocation->relocation_type); + EMIT_U32(relocation->symbol_index); + } + } + + if (offset - *p_offset != section_size + sizeof(uint32) * 2) { + aot_set_last_error("emit relocation section failed."); + return false; + } + + *p_offset = offset; + return true; +} + +#define EI_NIDENT 16 + +typedef uint32 elf32_word; +typedef int32 elf32_sword; +typedef uint16 elf32_half; +typedef uint32 elf32_off; +typedef uint32 elf32_addr; + +struct elf32_ehdr { + unsigned char e_ident[EI_NIDENT]; /* ident bytes */ + elf32_half e_type; /* file type */ + elf32_half e_machine; /* target machine */ + elf32_word e_version; /* file version */ + elf32_addr e_entry; /* start address */ + elf32_off e_phoff; /* phdr file offset */ + elf32_off e_shoff; /* shdr file offset */ + elf32_word e_flags; /* file flags */ + elf32_half e_ehsize; /* sizeof ehdr */ + elf32_half e_phentsize; /* sizeof phdr */ + elf32_half e_phnum; /* number phdrs */ + elf32_half e_shentsize; /* sizeof shdr */ + elf32_half e_shnum; /* number shdrs */ + elf32_half e_shstrndx; /* shdr string index */ +}; + +struct elf32_rel { + elf32_addr r_offset; + elf32_word r_info; +} elf32_rel; + +struct elf32_rela { + elf32_addr r_offset; + elf32_word r_info; + elf32_sword r_addend; +} elf32_rela; + +typedef uint32 elf64_word; +typedef int32 elf64_sword; +typedef uint64 elf64_xword; +typedef int64 elf64_sxword; +typedef uint16 elf64_half; +typedef uint64 elf64_off; +typedef uint64 elf64_addr; + +struct elf64_ehdr { + unsigned char e_ident[EI_NIDENT]; /* ident bytes */ + elf64_half e_type; /* file type */ + elf64_half e_machine; /* target machine */ + elf64_word e_version; /* file version */ + elf64_addr e_entry; /* start address */ + elf64_off e_phoff; /* phdr file offset */ + elf64_off e_shoff; /* shdr file offset */ + elf64_word e_flags; /* file flags */ + elf64_half e_ehsize; /* sizeof ehdr */ + elf64_half e_phentsize; /* sizeof phdr */ + elf64_half e_phnum; /* number phdrs */ + elf64_half e_shentsize; /* sizeof shdr */ + elf64_half e_shnum; /* number shdrs */ + elf64_half e_shstrndx; /* shdr string index */ +}; + +typedef struct elf64_rel { + elf64_addr r_offset; + elf64_xword r_info; +} elf64_rel; + +typedef struct elf64_rela { + elf64_addr r_offset; + elf64_xword r_info; + elf64_sxword r_addend; +} elf64_rela; + + +#define SET_TARGET_INFO(f, v, type, little) do { \ + type tmp = elf_header->v; \ + if ((little && !is_little_endian()) \ + || (!little && is_little_endian())) \ + exchange_##type((uint8*)&tmp); \ + obj_data->target_info.f = tmp; \ + } while (0) + +static bool +aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data) +{ + LLVMBinaryType bin_type = LLVMBinaryGetType(obj_data->binary); + const uint8 *elf_buf = (uint8 *)LLVMGetBufferStart(obj_data->mem_buf); + uint32 elf_size = (uint32)LLVMGetBufferSize(obj_data->mem_buf); + + if (bin_type != LLVMBinaryTypeELF32L + && bin_type != LLVMBinaryTypeELF32B + && bin_type != LLVMBinaryTypeELF64L + && bin_type != LLVMBinaryTypeELF64B + && bin_type != LLVMBinaryTypeMachO32L + && bin_type != LLVMBinaryTypeMachO32B + && bin_type != LLVMBinaryTypeMachO64L + && bin_type != LLVMBinaryTypeMachO64B) { + aot_set_last_error("invaid llvm binary bin_type."); + return false; + } + + obj_data->target_info.bin_type = bin_type - LLVMBinaryTypeELF32L; + + if (bin_type == LLVMBinaryTypeELF32L + || bin_type == LLVMBinaryTypeELF32B) { + struct elf32_ehdr *elf_header; + bool is_little_bin = bin_type == LLVMBinaryTypeELF32L; + + if (!elf_buf || elf_size < sizeof(struct elf32_ehdr)) { + aot_set_last_error("invalid elf32 buffer."); + return false; + } + + elf_header = (struct elf32_ehdr *)elf_buf; + SET_TARGET_INFO(e_type, e_type, uint16, is_little_bin); + SET_TARGET_INFO(e_machine, e_machine, uint16, is_little_bin); + SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); + SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); + } + else if (bin_type == LLVMBinaryTypeELF64L + || bin_type == LLVMBinaryTypeELF64B) { + struct elf64_ehdr *elf_header; + bool is_little_bin = bin_type == LLVMBinaryTypeELF64L; + + if (!elf_buf || elf_size < sizeof(struct elf64_ehdr)) { + aot_set_last_error("invalid elf64 buffer."); + return false; + } + + elf_header = (struct elf64_ehdr *)elf_buf; + SET_TARGET_INFO(e_type, e_type, uint16, is_little_bin); + SET_TARGET_INFO(e_machine, e_machine, uint16, is_little_bin); + SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin); + SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin); + } + else if (bin_type == LLVMBinaryTypeMachO32L + || bin_type == LLVMBinaryTypeMachO32B) { + /* TODO: parse file type of Mach-O 32 */ + aot_set_last_error("invaid llvm binary bin_type."); + return false; + } + else if (bin_type == LLVMBinaryTypeMachO64L + || bin_type == LLVMBinaryTypeMachO64B) { + /* TODO: parse file type of Mach-O 64 */ + aot_set_last_error("invaid llvm binary bin_type."); + return false; + } + + + strncpy(obj_data->target_info.arch, comp_ctx->target_arch, + sizeof(obj_data->target_info.arch)); + + return true; +} + +static bool +aot_resolve_text(AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr; + char *name; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) && !strcmp(name, ".text")) { + obj_data->text = (char *)LLVMGetSectionContents(sec_itr); + obj_data->text_size = (uint32)LLVMGetSectionSize(sec_itr); + break; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + return true; +} + +static bool +aot_resolve_literal(AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr; + char *name; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) && !strcmp(name, ".literal")) { + obj_data->literal = (char *)LLVMGetSectionContents(sec_itr); + obj_data->literal_size = (uint32)LLVMGetSectionSize(sec_itr); + break; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + return true; +} + +static bool +is_data_section(char *section_name) +{ + return (!strcmp(section_name, ".data") + || !strcmp(section_name, ".rodata") + /* ".rodata.cst4/8/16/.." */ + || !strncmp(section_name, ".rodata.cst", strlen(".rodata.cst"))); +} + +static uint32 +get_object_data_sections_count(AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr; + char *name; + uint32 count = 0; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return 0; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) + && (is_data_section(name))) { + count++; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + return count; +} + +static bool +aot_resolve_object_data_sections(AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr; + char *name; + AOTObjectDataSection *data_section; + uint32 sections_count = get_object_data_sections_count(obj_data); + uint32 size; + + if (sections_count > 0) { + size = (uint32)sizeof(AOTObjectDataSection) * sections_count; + if (!(data_section = obj_data->data_sections = wasm_runtime_malloc(size))) { + aot_set_last_error("allocate memory for data sections failed."); + return false; + } + memset(obj_data->data_sections, 0, size); + obj_data->data_sections_count = sections_count; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, + sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) + && (is_data_section(name))) { + data_section->name = name; + data_section->data = (uint8 *)LLVMGetSectionContents(sec_itr); + data_section->size = (uint32)LLVMGetSectionSize(sec_itr); + data_section++; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + } + + return true; +} + +static bool +aot_resolve_functions(AOTCompContext *comp_ctx, AOTObjectData *obj_data) +{ + AOTObjectFunc *func; + LLVMSymbolIteratorRef sym_itr; + char *name, *prefix = AOT_FUNC_PREFIX; + uint32 func_index; + + /* allocate memory for aot function */ + obj_data->func_count = comp_ctx->comp_data->func_count; + if (!(obj_data->funcs + = wasm_runtime_malloc((uint32)sizeof(AOTObjectFunc) * obj_data->func_count))) { + aot_set_last_error("allocate memory for functions failed."); + return false; + } + memset(obj_data->funcs, 0, sizeof(AOTObjectFunc) * obj_data->func_count); + + if (!(sym_itr = LLVMObjectFileCopySymbolIterator(obj_data->binary))) { + aot_set_last_error("llvm get symbol iterator failed."); + return false; + } + + while (!LLVMObjectFileIsSymbolIteratorAtEnd(obj_data->binary, sym_itr)) { + if ((name = (char *)LLVMGetSymbolName(sym_itr)) + && str_starts_with(name, prefix)) { + func_index = (uint32)atoi(name + strlen(prefix)); + if (func_index < obj_data->func_count) { + func = obj_data->funcs + func_index; + func->func_name = name; + func->text_offset = LLVMGetSymbolAddress(sym_itr); + } + } + LLVMMoveToNextSymbol(sym_itr); + } + LLVMDisposeSymbolIterator(sym_itr); + + return true; +} + +static bool +get_relocations_count(LLVMSectionIteratorRef sec_itr, uint32 *p_count) +{ + uint32 relocation_count = 0; + LLVMRelocationIteratorRef rel_itr; + + if (!(rel_itr = LLVMGetRelocations(sec_itr))) { + aot_set_last_error("llvm get relocations failed."); + LLVMDisposeSectionIterator(sec_itr); + return false; + } + + while (!LLVMIsRelocationIteratorAtEnd(sec_itr, rel_itr)) { + relocation_count++; + LLVMMoveToNextRelocation(rel_itr); + } + LLVMDisposeRelocationIterator(rel_itr); + + *p_count = relocation_count; + return true; +} + +static bool +aot_resolve_object_relocation_group(AOTObjectData *obj_data, + AOTRelocationGroup *group, + LLVMSectionIteratorRef rel_sec) +{ + LLVMRelocationIteratorRef rel_itr; + AOTRelocation *relocation = group->relocations; + uint32 size; + bool is_binary_32bit = is_32bit_binary(obj_data->binary); + bool is_binary_little_endian = is_little_endian_binary(obj_data->binary); + bool has_addend = str_starts_with(group->section_name, ".rela"); + uint8 *rela_content = NULL; + + /* calculate relocations count and allcate memory */ + if (!get_relocations_count(rel_sec, &group->relocation_count)) + return false; + if (group->relocation_count == 0) { + aot_set_last_error("invalid relocations count"); + return false; + } + size = (uint32)sizeof(AOTRelocation) * group->relocation_count; + if (!(relocation = group->relocations = wasm_runtime_malloc(size))) { + aot_set_last_error("allocate memory for relocations failed."); + return false; + } + memset(group->relocations, 0, size); + + if (has_addend) { + uint64 rela_content_size; + /* LLVM doesn't provide C API to get relocation addend. So we have to + * parse it manually. */ + rela_content = (uint8 *)LLVMGetSectionContents(rel_sec); + rela_content_size = LLVMGetSectionSize(rel_sec); + if (is_binary_32bit) + size = (uint32)sizeof(struct elf32_rela) * group->relocation_count; + else + size = (uint32)sizeof(struct elf64_rela) * group->relocation_count; + if (rela_content_size != (uint64)size) { + aot_set_last_error("invalid relocation section content."); + return false; + } + } + + /* pares each relocation */ + if (!(rel_itr = LLVMGetRelocations(rel_sec))) { + aot_set_last_error("llvm get relocations failed."); + return false; + } + while (!LLVMIsRelocationIteratorAtEnd(rel_sec, rel_itr)) { + uint64 offset = LLVMGetRelocationOffset(rel_itr); + uint64 type = LLVMGetRelocationType(rel_itr); + LLVMSymbolIteratorRef rel_sym = LLVMGetRelocationSymbol(rel_itr); + + if (!rel_sym) { + aot_set_last_error("llvm get relocation symbol failed."); + goto fail; + } + + /* parse relocation addend from reloction content */ + if (has_addend) { + if (is_binary_32bit) { + uint32 addend = (uint32)(((struct elf32_rela *)rela_content)->r_addend); + if (is_binary_little_endian != is_little_endian()) + exchange_uint32((uint8 *)&addend); + relocation->relocation_addend = (uint64)addend; + rela_content += sizeof(struct elf32_rela); + } + else { + uint64 addend = (uint64)(((struct elf64_rela *)rela_content)->r_addend); + if (is_binary_little_endian != is_little_endian()) + exchange_uint64((uint8 *)&addend); + relocation->relocation_addend = addend; + rela_content += sizeof(struct elf64_rela); + } + } + + /* set relocation fields */ + relocation->relocation_offset = offset; + relocation->relocation_type = (uint32)type; + relocation->symbol_name = (char *)LLVMGetSymbolName(rel_sym); + + /* for ".LCPIxxx" relocation, transform the symbol name to real + * section name and set addend to the symbol address */ + if (relocation->symbol_name + && str_starts_with(relocation->symbol_name, ".LCPI")) { + /* change relocation->relocation_addend and relocation->symbol_name */ + LLVMSectionIteratorRef contain_section; + if (!(contain_section + = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + goto fail; + } + LLVMMoveToContainingSection(contain_section, rel_sym); + if (LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, contain_section)) { + LLVMDisposeSectionIterator(contain_section); + aot_set_last_error("llvm get containing section failed."); + goto fail; + } + relocation->relocation_addend += LLVMGetSymbolAddress(rel_sym); + relocation->symbol_name = (char *)LLVMGetSectionName(contain_section); + LLVMDisposeSectionIterator(contain_section); + } + + LLVMDisposeSymbolIterator(rel_sym); + LLVMMoveToNextRelocation(rel_itr); + relocation++; + } + LLVMDisposeRelocationIterator(rel_itr); + return true; + +fail: + LLVMDisposeRelocationIterator(rel_itr); + return false; +} + +static bool +is_relocation_section(char *section_name) +{ + return (!strcmp(section_name, ".rela.text") + || !strcmp(section_name, ".rel.text") + || !strcmp(section_name, ".rela.literal") + || !strcmp(section_name, ".rela.data") + || !strcmp(section_name, ".rel.data") + || !strcmp(section_name, ".rela.rodata") + || !strcmp(section_name, ".rel.rodata") + /* ".rela.rodata.cst4/8/16/.." */ + || !strncmp(section_name, ".rela.rodata.cst", + strlen(".rela.rodata.cst")) + /* ".rel.rodata.cst4/8/16/.." */ + || !strncmp(section_name, ".rel.rodata.cst", + strlen(".rel.rodata.cst"))); +} + +static bool +get_relocation_groups_count(AOTObjectData *obj_data, uint32 *p_count) +{ + uint32 count = 0; + LLVMSectionIteratorRef sec_itr; + char *name; + + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) + && is_relocation_section(name)) { + count++; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + *p_count = count; + return true; +} + +static bool +aot_resolve_object_relocation_groups(AOTObjectData *obj_data) +{ + LLVMSectionIteratorRef sec_itr; + AOTRelocationGroup *relocation_group; + uint32 group_count; + char *name; + uint32 size; + + /* calculate relocation groups count and allcate memory */ + if (!get_relocation_groups_count(obj_data, &group_count)) + return false; + + if (0 == (obj_data->relocation_group_count = group_count)) + return true; + + size = (uint32)sizeof(AOTRelocationGroup) * group_count; + if (!(relocation_group = obj_data->relocation_groups = wasm_runtime_malloc(size))) { + aot_set_last_error("allocate memory for relocation groups failed."); + return false; + } + + memset(obj_data->relocation_groups, 0, size); + + /* resolve each relocation group */ + if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) { + aot_set_last_error("llvm get section iterator failed."); + return false; + } + while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) { + if ((name = (char *)LLVMGetSectionName(sec_itr)) + && is_relocation_section(name)) { + relocation_group->section_name = name; + if (!aot_resolve_object_relocation_group( + obj_data, + relocation_group, + sec_itr)) { + LLVMDisposeSectionIterator(sec_itr); + return false; + } + relocation_group++; + } + LLVMMoveToNextSection(sec_itr); + } + LLVMDisposeSectionIterator(sec_itr); + + return true; +} + +static void +destroy_relocation_groups(AOTRelocationGroup *relocation_groups, + uint32 relocation_group_count) +{ + uint32 i; + AOTRelocationGroup *relocation_group = relocation_groups; + + for (i = 0; i < relocation_group_count; i++, relocation_group++) + if (relocation_group->relocations) + wasm_runtime_free(relocation_group->relocations); + wasm_runtime_free(relocation_groups); +} + +static void +destroy_relocation_symbol_list(AOTSymbolList *symbol_list) +{ + AOTSymbolNode *elem; + + elem = symbol_list->head; + while (elem) { + AOTSymbolNode *next = elem->next; + wasm_runtime_free(elem); + elem = next; + } +} + +static void +aot_obj_data_destroy(AOTObjectData *obj_data) +{ + if (obj_data->binary) + LLVMDisposeBinary(obj_data->binary); + if (obj_data->mem_buf) + LLVMDisposeMemoryBuffer(obj_data->mem_buf); + if (obj_data->funcs) + wasm_runtime_free(obj_data->funcs); + if (obj_data->data_sections) + wasm_runtime_free(obj_data->data_sections); + if (obj_data->relocation_groups) + destroy_relocation_groups(obj_data->relocation_groups, + obj_data->relocation_group_count); + if (obj_data->symbol_list.len) + destroy_relocation_symbol_list(&obj_data->symbol_list); + wasm_runtime_free(obj_data); +} + +static AOTObjectData * +aot_obj_data_create(AOTCompContext *comp_ctx) +{ + char *err = NULL; + AOTObjectData *obj_data; + + bh_print_time("Begin to emit object file to buffer"); + + if (!(obj_data = wasm_runtime_malloc(sizeof(AOTObjectData)))) { + aot_set_last_error("allocate memory failed."); + return false; + } + memset(obj_data, 0, sizeof(AOTObjectData)); + + if (LLVMTargetMachineEmitToMemoryBuffer(comp_ctx->target_machine, + comp_ctx->module, + LLVMObjectFile, + &err, + &obj_data->mem_buf) != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("llvm emit to memory buffer failed."); + goto fail; + } + + if (!(obj_data->binary = + LLVMCreateBinary(obj_data->mem_buf, NULL, &err))) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("llvm create binary failed."); + goto fail; + } + + bh_print_time("Begin to resolve object file info"); + + /* resolve target info/text/relocations/functions */ + if (!aot_resolve_target_info(comp_ctx, obj_data) + || !aot_resolve_text(obj_data) + || !aot_resolve_literal(obj_data) + || !aot_resolve_object_data_sections(obj_data) + || !aot_resolve_object_relocation_groups(obj_data) + || !aot_resolve_functions(comp_ctx, obj_data)) + goto fail; + + return obj_data; + +fail: + aot_obj_data_destroy(obj_data); + return NULL; +} + +bool +aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data, + const char *file_name) +{ + AOTObjectData *obj_data = aot_obj_data_create(comp_ctx); + uint8 *aot_file_buf, *buf, *buf_end; + uint32 aot_file_size, offset = 0; + bool ret = false; + FILE *file; + + if (!obj_data) + return false; + + bh_print_time("Begin to emit AOT file"); + + aot_file_size = get_aot_file_size(comp_ctx, comp_data, obj_data); + + if (!(buf = aot_file_buf = wasm_runtime_malloc(aot_file_size))) { + aot_set_last_error("allocate memory failed."); + goto fail1; + } + + memset(aot_file_buf, 0, aot_file_size); + buf_end = buf + aot_file_size; + + if (!aot_emit_file_header(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_init_data_section(buf, buf_end, &offset, comp_ctx, comp_data, obj_data) + || !aot_emit_text_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_func_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_export_section(buf, buf_end, &offset, comp_data, obj_data) + || !aot_emit_relocation_section(buf, buf_end, &offset, comp_data, obj_data)) + goto fail2; + +#if 0 + dump_buf(buf, offset, "sections"); +#endif + + if (offset != aot_file_size) { + aot_set_last_error("emit aot file failed."); + goto fail2; + } + + /* write buffer to file */ + if (!(file = fopen(file_name, "wb"))) { + aot_set_last_error("open or create aot file failed."); + goto fail2; + } + if (!fwrite(aot_file_buf, aot_file_size, 1, file)) { + aot_set_last_error("write to aot file failed."); + goto fail3; + } + + ret = true; + +fail3: + fclose(file); + +fail2: + wasm_runtime_free(aot_file_buf); + +fail1: + aot_obj_data_destroy(obj_data); + return ret; +} diff --git a/wamr/core/iwasm/compilation/aot_emit_compare.c b/wamr/core/iwasm/compilation/aot_emit_compare.c new file mode 100644 index 0000000..94c96a5 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_compare.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_compare.h" + +static bool +int_cond_to_llvm_op(IntCond cond, LLVMIntPredicate *op) +{ + if (cond < INT_EQZ || cond > INT_GE_U) + return false; + + switch (cond) { + case INT_EQZ: + case INT_EQ: + *op = LLVMIntEQ; + break; + case INT_NE: + *op = LLVMIntNE; + break; + case INT_LT_S: + *op = LLVMIntSLT; + break; + case INT_LT_U: + *op = LLVMIntULT; + break; + case INT_GT_S: + *op = LLVMIntSGT; + break; + case INT_GT_U: + *op = LLVMIntUGT; + break; + case INT_LE_S: + *op = LLVMIntSLE; + break; + case INT_LE_U: + *op = LLVMIntULE; + break; + case INT_GE_S: + *op = LLVMIntSGE; + break; + case INT_GE_U: + *op = LLVMIntUGE; + break; + default: + return false; + } + + return true; +} + +static bool +float_cond_to_llvm_op(FloatCond cond, LLVMRealPredicate *op) +{ + if (cond < FLOAT_EQ || cond > FLOAT_GE) + return false; + + switch (cond) { + case FLOAT_EQ: + *op = LLVMRealOEQ; + break; + case FLOAT_NE: + *op = LLVMRealUNE; + break; + case FLOAT_LT: + *op = LLVMRealOLT; + break; + case FLOAT_GT: + *op = LLVMRealOGT; + break; + case FLOAT_LE: + *op = LLVMRealOLE; + break; + case FLOAT_GE: + *op = LLVMRealOGE; + break; + default: + return false; + } + + return true; +} + +bool +aot_compile_op_i32_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntCond cond) +{ + LLVMIntPredicate op; + LLVMValueRef lhs, rhs, res; + + if (!int_cond_to_llvm_op(cond, &op)) { + aot_set_last_error("invalid WASM condition opcode"); + return false; + } + + if (cond == INT_EQZ) + rhs = I32_ZERO; + else + POP_I32(rhs); + + POP_I32(lhs); + + if (!(res = LLVMBuildICmp(comp_ctx->builder, op, lhs, rhs, "i32_cmp"))) { + aot_set_last_error("llvm build compare failed."); + return false; + } + + PUSH_COND(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntCond cond) +{ + LLVMIntPredicate op; + LLVMValueRef lhs, rhs, res; + + if (!int_cond_to_llvm_op(cond, &op)) { + aot_set_last_error("invalid WASM condition opcode"); + return false; + } + + if (cond == INT_EQZ) + rhs = I64_CONST(0); + else + POP_I64(rhs); + + POP_I64(lhs); + + if (!(res = LLVMBuildICmp(comp_ctx->builder, op, lhs, rhs, "i64_cmp"))) { + aot_set_last_error("llvm build compare failed."); + return false; + } + + PUSH_COND(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatCond cond) +{ + LLVMRealPredicate op; + LLVMValueRef lhs, rhs, res; + + if (!float_cond_to_llvm_op(cond, &op)) { + aot_set_last_error("invalid WASM condition opcode"); + return false; + } + + POP_F32(rhs); + POP_F32(lhs); + + if (!(res = LLVMBuildFCmp(comp_ctx->builder, op, lhs, rhs, "f32_cmp"))) { + aot_set_last_error("llvm build compare failed."); + return false; + } + + PUSH_COND(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatCond cond) +{ + LLVMRealPredicate op; + LLVMValueRef lhs, rhs, res; + + if (!float_cond_to_llvm_op(cond, &op)) { + aot_set_last_error("invalid WASM condition opcode"); + return false; + } + + POP_F64(rhs); + POP_F64(lhs); + + if (!(res = LLVMBuildFCmp(comp_ctx->builder, op, lhs, rhs, "f64_cmp"))) { + aot_set_last_error("llvm build compare failed."); + return false; + } + + PUSH_COND(res); + return true; +fail: + return false; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_compare.h b/wamr/core/iwasm/compilation/aot_emit_compare.h new file mode 100644 index 0000000..6b06f5f --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_compare.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_COMPARE_H_ +#define _AOT_EMIT_COMPARE_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_i32_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntCond cond); + +bool +aot_compile_op_i64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntCond cond); + +bool +aot_compile_op_f32_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatCond cond); + +bool +aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatCond cond); + + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_COMPARE_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_const.c b/wamr/core/iwasm/compilation/aot_emit_const.c new file mode 100644 index 0000000..a862a7d --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_const.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_const.h" + + +bool +aot_compile_op_i32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 i32_const) +{ + LLVMValueRef value = I32_CONST((uint32)i32_const); + CHECK_LLVM_CONST(value); + PUSH_I32(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int64 i64_const) +{ + LLVMValueRef value = I64_CONST((uint64)i64_const); + CHECK_LLVM_CONST(value); + PUSH_I64(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + float32 f32_const) +{ + LLVMValueRef alloca, value; + + if (!isnan(f32_const)) { + value = F32_CONST(f32_const); + CHECK_LLVM_CONST(value); + PUSH_F32(value); + } + else { + int32 i32_const; + memcpy(&i32_const, &f32_const, sizeof(int32)); + if (!(alloca = LLVMBuildAlloca(comp_ctx->builder, + I32_TYPE, "i32_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + if (!LLVMBuildStore(comp_ctx->builder, + I32_CONST((uint32)i32_const), alloca)) { + aot_set_last_error("llvm build store failed."); + return false; + } + if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, + alloca, F32_PTR_TYPE, "f32_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + return false; + } + if (!(value = LLVMBuildLoad(comp_ctx->builder, alloca, ""))) { + aot_set_last_error("llvm build load failed."); + return false; + } + PUSH_F32(value); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + float64 f64_const) +{ + LLVMValueRef alloca, value; + + if (!isnan(f64_const)) { + value = F64_CONST(f64_const); + CHECK_LLVM_CONST(value); + PUSH_F64(value); + } + else { + int64 i64_const; + memcpy(&i64_const, &f64_const, sizeof(int64)); + if (!(alloca = LLVMBuildAlloca(comp_ctx->builder, + I64_TYPE, "i64_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + return false; + } + value = I64_CONST((uint64)i64_const); + CHECK_LLVM_CONST(value); + if (!LLVMBuildStore(comp_ctx->builder, value, alloca)) { + aot_set_last_error("llvm build store failed."); + return false; + } + if (!(alloca = LLVMBuildBitCast(comp_ctx->builder, + alloca, F64_PTR_TYPE, "f64_ptr"))) { + aot_set_last_error("llvm build bitcast failed."); + return false; + } + if (!(value = LLVMBuildLoad(comp_ctx->builder, alloca, ""))) { + aot_set_last_error("llvm build load failed."); + return false; + } + PUSH_F64(value); + } + + return true; +fail: + return false; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_const.h b/wamr/core/iwasm/compilation/aot_emit_const.h new file mode 100644 index 0000000..1d20a7a --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_const.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_CONST_H_ +#define _AOT_EMIT_CONST_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_i32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 i32_const); + +bool +aot_compile_op_i64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int64 i64_const); + +bool +aot_compile_op_f32_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + float32 f32_const); + +bool +aot_compile_op_f64_const(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + float64 f64_const); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_CONST_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_control.c b/wamr/core/iwasm/compilation/aot_emit_control.c new file mode 100644 index 0000000..a2799a7 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_control.c @@ -0,0 +1,1096 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_control.h" +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" +#include "../interpreter/wasm_loader.h" + +static char *block_name_prefix[] = { "block", "loop", "if" }; +static char *block_name_suffix[] = { "begin", "else", "end" }; + +enum { + LABEL_BEGIN = 0, + LABEL_ELSE, + LABEL_END +}; + +static void +format_block_name(char *name, uint32 name_size, + uint32 block_index, uint32 label_type, + uint32 label_id) +{ + if (label_type != LABEL_TYPE_FUNCTION) + snprintf(name, name_size, "%s%d%s%s", + block_name_prefix[label_type], block_index, + "_", block_name_suffix[label_id]); + else + snprintf(name, name_size, "%s", "func_end"); +} + +#define CREATE_BLOCK(new_llvm_block, name) do { \ + if (!(new_llvm_block = \ + LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, \ + name))) { \ + aot_set_last_error("add LLVM basic block failed.");\ + goto fail; \ + } \ + } while (0) + +#define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder) + +#define MOVE_BLOCK_AFTER(llvm_block, llvm_block_after) \ + LLVMMoveBasicBlockAfter(llvm_block, llvm_block_after) + +#define MOVE_BLOCK_AFTER_CURR(llvm_block) \ + LLVMMoveBasicBlockAfter(llvm_block, CURR_BLOCK()) + +#define MOVE_BLOCK_BEFORE(llvm_block, llvm_block_before) \ + LLVMMoveBasicBlockBefore(llvm_block, llvm_block_before) + +#define BUILD_BR(llvm_block) do { \ + if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \ + aot_set_last_error("llvm build br failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_COND_BR(value_if, block_then, block_else) do {\ + if (!LLVMBuildCondBr(comp_ctx->builder, value_if, \ + block_then, block_else)) { \ + aot_set_last_error("llvm build cond br failed."); \ + goto fail; \ + } \ + } while (0) + +#define SET_BUILDER_POS(llvm_block) \ + LLVMPositionBuilderAtEnd(comp_ctx->builder, llvm_block) + +#define CREATE_RESULT_VALUE_PHIS(block) do { \ + if (block->result_count && !block->result_phis) { \ + uint32 i; \ + uint64 size; \ + LLVMBasicBlockRef block_curr = CURR_BLOCK(); \ + /* Allocate memory */ \ + size = sizeof(LLVMValueRef) * (uint64)block->result_count; \ + if (size >= UINT32_MAX \ + || !(block->result_phis = \ + wasm_runtime_malloc((uint32)size))) { \ + aot_set_last_error("allocate memory failed."); \ + goto fail; \ + } \ + SET_BUILDER_POS(block->llvm_end_block); \ + for (i = 0; i < block->result_count; i++) { \ + if (!(block->result_phis[i] = \ + LLVMBuildPhi(comp_ctx->builder, \ + TO_LLVM_TYPE(block->result_types[i]), \ + "phi"))) { \ + aot_set_last_error("llvm build phi failed."); \ + goto fail; \ + } \ + } \ + SET_BUILDER_POS(block_curr); \ + } \ + } while (0) + +#define ADD_TO_RESULT_PHIS(block, value, idx) do { \ + LLVMBasicBlockRef block_curr = CURR_BLOCK(); \ + LLVMAddIncoming(block->result_phis[idx], \ + &value, &block_curr, 1); \ + } while (0) + +#define BUILD_ICMP(op, left, right, res, name) do { \ + if (!(res = LLVMBuildICmp(comp_ctx->builder, op, \ + left, right, name))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_TO_PARAM_PHIS(block, value, idx) do { \ + LLVMBasicBlockRef block_curr = CURR_BLOCK(); \ + LLVMAddIncoming(block->param_phis[idx], \ + &value, &block_curr, 1); \ + } while (0) + +static LLVMBasicBlockRef +find_next_llvm_end_block(AOTBlock *block) +{ + block = block->prev; + while (block && !block->llvm_end_block) + block = block->prev; + return block ? block->llvm_end_block : NULL; +} + +static AOTBlock* +get_target_block(AOTFuncContext *func_ctx, uint32 br_depth) +{ + uint32 i = br_depth; + AOTBlock *block = func_ctx->block_stack.block_list_end; + + while (i-- > 0 && block) { + block = block->prev; + } + + if (!block) { + aot_set_last_error("WASM block stack underflow."); + return NULL; + } + return block; +} + +static bool +handle_next_reachable_block(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + AOTBlock *block = func_ctx->block_stack.block_list_end; + AOTBlock *block_prev; + uint8 *frame_ip; + uint32 i; + AOTFuncType *func_type; + + aot_checked_addr_list_destroy(func_ctx); + + if (block->label_type == LABEL_TYPE_IF + && block->llvm_else_block + && !block->skip_wasm_code_else + && *p_frame_ip <= block->wasm_code_else) { + /* Clear value stack and start to translate else branch */ + aot_value_stack_destroy(&block->value_stack); + /* Recover parameters of else branch */ + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); + SET_BUILDER_POS(block->llvm_else_block); + *p_frame_ip = block->wasm_code_else + 1; + return true; + } + + while (block && !block->is_reachable) { + block_prev = block->prev; + block = aot_block_stack_pop(&func_ctx->block_stack); + + if (block->label_type == LABEL_TYPE_IF) { + if (block->llvm_else_block + && !block->skip_wasm_code_else + && *p_frame_ip <= block->wasm_code_else) { + /* Clear value stack and start to translate else branch */ + aot_value_stack_destroy(&block->value_stack); + SET_BUILDER_POS(block->llvm_else_block); + *p_frame_ip = block->wasm_code_else + 1; + /* Push back the block */ + aot_block_stack_push(&func_ctx->block_stack, block); + return true; + } + else if (block->llvm_end_block) { + /* Remove unreachable basic block */ + LLVMDeleteBasicBlock(block->llvm_end_block); + block->llvm_end_block = NULL; + } + } + + frame_ip = block->wasm_code_end; + aot_block_destroy(block); + block = block_prev; + } + + if (!block) { + *p_frame_ip = frame_ip + 1; + return true; + } + + *p_frame_ip = block->wasm_code_end + 1; + SET_BUILDER_POS(block->llvm_end_block); + + /* Pop block, push its return value, and destroy the block */ + block = aot_block_stack_pop(&func_ctx->block_stack); + func_type = func_ctx->aot_func->func_type; + for (i = 0; i < block->result_count; i++) { + bh_assert(block->result_phis[i]); + if (block->label_type != LABEL_TYPE_FUNCTION) { + PUSH(block->result_phis[i], block->result_types[i]); + } + else { + /* Store extra return values to function parameters */ + if (i != 0) { + uint32 param_index = func_type->param_count + i; + if (!LLVMBuildStore(comp_ctx->builder, + block->result_phis[i], + LLVMGetParam(func_ctx->func, param_index))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + } + } + } + if (block->label_type == LABEL_TYPE_FUNCTION) { + if (block->result_count) { + /* Return the first return value */ + if (!LLVMBuildRet(comp_ctx->builder, block->result_phis[0])) { + aot_set_last_error("llvm build return failed."); + goto fail; + } + } + else { + if (!LLVMBuildRetVoid(comp_ctx->builder)) { + aot_set_last_error("llvm build return void failed."); + goto fail; + } + } + } + aot_block_destroy(block); + return true; +fail: + return false; +} + +static bool +push_aot_block_to_stack_and_pass_params(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + AOTBlock *block) +{ + uint32 i, param_index; + LLVMValueRef value; + uint64 size; + char name[32]; + LLVMBasicBlockRef block_curr = CURR_BLOCK(); + + if (block->param_count) { + size = sizeof(LLVMValueRef) * (uint64)block->param_count; + if (size >= UINT32_MAX + || !(block->param_phis = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return false; + } + + if (block->label_type == LABEL_TYPE_IF + && !block->skip_wasm_code_else + && !(block->else_param_phis = wasm_runtime_malloc((uint32)size))) { + wasm_runtime_free(block->param_phis); + block->param_phis = NULL; + aot_set_last_error("allocate memory failed."); + return false; + } + + /* Create param phis */ + for (i = 0; i < block->param_count; i++) { + SET_BUILDER_POS(block->llvm_entry_block); + snprintf(name, sizeof(name), "%s%d_phi%d", + block_name_prefix[block->label_type], + block->block_index, i); + if (!(block->param_phis[i] = + LLVMBuildPhi(comp_ctx->builder, + TO_LLVM_TYPE(block->param_types[i]), + name))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + + if (block->label_type == LABEL_TYPE_IF + && !block->skip_wasm_code_else + && block->llvm_else_block) { + /* Build else param phis */ + SET_BUILDER_POS(block->llvm_else_block); + snprintf(name, sizeof(name), "else%d_phi%d", + block->block_index, i); + if (!(block->else_param_phis[i] = + LLVMBuildPhi(comp_ctx->builder, + TO_LLVM_TYPE(block->param_types[i]), + name))) { + aot_set_last_error("llvm build phi failed."); + goto fail; + } + } + } + SET_BUILDER_POS(block_curr); + + /* Pop param values from current block's + * value stack and add to param phis. + */ + for (i = 0; i < block->param_count; i++) { + param_index = block->param_count - 1 - i; + POP(value, block->param_types[param_index]); + ADD_TO_PARAM_PHIS(block, value, param_index); + if (block->label_type == LABEL_TYPE_IF + && !block->skip_wasm_code_else) { + if (block->llvm_else_block) { + /* has else branch, add to else param phis */ + LLVMAddIncoming(block->else_param_phis[param_index], + &value, &block_curr, 1); + } + else { + /* no else branch, add to result phis */ + CREATE_RESULT_VALUE_PHIS(block); + ADD_TO_RESULT_PHIS(block, value, param_index); + } + } + } + } + + /* Push the new block to block stack */ + aot_block_stack_push(&func_ctx->block_stack, block); + + /* Push param phis to the new block */ + for (i = 0; i < block->param_count; i++) { + PUSH(block->param_phis[i], block->param_types[i]); + } + + return true; + +fail: + if (block->param_phis) { + wasm_runtime_free(block->param_phis); + block->param_phis = NULL; + } + if (block->else_param_phis) { + wasm_runtime_free(block->else_param_phis); + block->else_param_phis = NULL; + } + return false; +} + +bool +aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip, uint8 *frame_ip_end, + uint32 label_type, uint32 param_count, uint8 *param_types, + uint32 result_count, uint8 *result_types) +{ + BlockAddr block_addr_cache[BLOCK_ADDR_CACHE_SIZE][BLOCK_ADDR_CONFLICT_SIZE]; + AOTBlock *block; + uint8 *else_addr, *end_addr; + LLVMValueRef value; + char name[32]; + + /* Check block stack */ + if (!func_ctx->block_stack.block_list_end) { + aot_set_last_error("WASM block stack underflow."); + return false; + } + + memset(block_addr_cache, 0, sizeof(block_addr_cache)); + + /* Get block info */ + if (!(wasm_loader_find_block_addr((BlockAddr*)block_addr_cache, + *p_frame_ip, frame_ip_end, (uint8)label_type, + &else_addr, &end_addr, NULL, 0))) { + aot_set_last_error("find block end addr failed."); + return false; + } + + /* Allocate memory */ + if (!(block = wasm_runtime_malloc(sizeof(AOTBlock)))) { + aot_set_last_error("allocate memory failed."); + return false; + } + memset(block, 0, sizeof(AOTBlock)); + if (param_count + && !(block->param_types = wasm_runtime_malloc(param_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + if (result_count) { + if (!(block->result_types = wasm_runtime_malloc(result_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + } + + /* Init aot block data */ + block->label_type = label_type; + block->param_count = param_count; + memcpy(block->param_types, param_types, param_count); + block->result_count = result_count; + memcpy(block->result_types, result_types, result_count); + block->wasm_code_else = else_addr; + block->wasm_code_end = end_addr; + block->block_index = func_ctx->block_stack.block_index[label_type]; + func_ctx->block_stack.block_index[label_type]++; + + if (label_type == LABEL_TYPE_BLOCK + || label_type == LABEL_TYPE_LOOP) { + /* Create block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_BEGIN); + CREATE_BLOCK(block->llvm_entry_block, name); + MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); + /* Jump to the entry block */ + BUILD_BR(block->llvm_entry_block); + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, block)) + goto fail; + /* Start to translate the block */ + SET_BUILDER_POS(block->llvm_entry_block); + if (label_type == LABEL_TYPE_LOOP) + aot_checked_addr_list_destroy(func_ctx); + } + else if (label_type == LABEL_TYPE_IF) { + POP_COND(value); + if (!LLVMIsConstant(value)) { + /* Compare value is not constant, create condition br IR */ + /* Create entry block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_BEGIN); + CREATE_BLOCK(block->llvm_entry_block, name); + MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); + + /* Create end block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_END); + CREATE_BLOCK(block->llvm_end_block, name); + MOVE_BLOCK_AFTER(block->llvm_end_block, block->llvm_entry_block); + + if (else_addr) { + /* Create else block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_ELSE); + CREATE_BLOCK(block->llvm_else_block, name); + MOVE_BLOCK_AFTER(block->llvm_else_block, block->llvm_entry_block); + /* Create condition br IR */ + BUILD_COND_BR(value, block->llvm_entry_block, + block->llvm_else_block); + } + else { + /* Create condition br IR */ + BUILD_COND_BR(value, block->llvm_entry_block, + block->llvm_end_block); + block->is_reachable = true; + } + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, block)) + goto fail; + /* Start to translate if branch of BLOCK if */ + SET_BUILDER_POS(block->llvm_entry_block); + } + else { + if ((int32)LLVMConstIntGetZExtValue(value) != 0) { + /* Compare value is not 0, condition is true, else branch of + BLOCK if cannot be reached */ + block->skip_wasm_code_else = true; + /* Create entry block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_BEGIN); + CREATE_BLOCK(block->llvm_entry_block, name); + MOVE_BLOCK_AFTER_CURR(block->llvm_entry_block); + /* Jump to the entry block */ + BUILD_BR(block->llvm_entry_block); + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, block)) + goto fail; + /* Start to translate the if branch */ + SET_BUILDER_POS(block->llvm_entry_block); + } + else { + /* Compare value is not 0, condition is false, if branch of + BLOCK if cannot be reached */ + if (else_addr) { + /* Create else block */ + format_block_name(name, sizeof(name), + block->block_index, label_type, LABEL_ELSE); + CREATE_BLOCK(block->llvm_else_block, name); + MOVE_BLOCK_AFTER_CURR(block->llvm_else_block); + /* Jump to the else block */ + BUILD_BR(block->llvm_else_block); + if (!push_aot_block_to_stack_and_pass_params(comp_ctx, func_ctx, block)) + goto fail; + /* Start to translate the else branch */ + SET_BUILDER_POS(block->llvm_else_block); + *p_frame_ip = else_addr + 1; + } + else { + /* skip the block */ + aot_block_destroy(block); + *p_frame_ip = end_addr + 1; + } + } + } + } + else { + aot_set_last_error("Invalid block type."); + goto fail; + } + + return true; +fail: + aot_block_destroy(block); + return false; +} + +bool +aot_compile_op_else(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + AOTBlock *block = func_ctx->block_stack.block_list_end; + LLVMValueRef value; + char name[32]; + uint32 i, result_index; + + /* Check block */ + if (!block) { + aot_set_last_error("WASM block stack underflow."); + return false; + } + if (block->label_type != LABEL_TYPE_IF + || (!block->skip_wasm_code_else + && !block->llvm_else_block)) { + aot_set_last_error("Invalid WASM block type."); + return false; + } + + /* Create end block if needed */ + if (!block->llvm_end_block) { + format_block_name(name, sizeof(name), + block->block_index, block->label_type, LABEL_END); + CREATE_BLOCK(block->llvm_end_block, name); + if (block->llvm_else_block) + MOVE_BLOCK_AFTER(block->llvm_end_block, block->llvm_else_block); + else + MOVE_BLOCK_AFTER_CURR(block->llvm_end_block); + } + + block->is_reachable = true; + + /* Comes from the if branch of BLOCK if */ + CREATE_RESULT_VALUE_PHIS(block); + for (i = 0; i < block->result_count; i++) { + result_index = block->result_count - 1 - i; + POP(value, block->result_types[result_index]); + ADD_TO_RESULT_PHIS(block, value, result_index); + } + + /* Jump to end block */ + BUILD_BR(block->llvm_end_block); + + if (!block->skip_wasm_code_else + && block->llvm_else_block) { + /* Clear value stack, recover param values + * and start to translate else branch. + */ + aot_value_stack_destroy(&block->value_stack); + for (i = 0; i < block->param_count; i++) + PUSH(block->else_param_phis[i], block->param_types[i]); + SET_BUILDER_POS(block->llvm_else_block); + aot_checked_addr_list_destroy(func_ctx); + return true; + } + + /* No else branch or no need to translate else branch */ + block->is_reachable = true; + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +fail: + return false; +} + +bool +aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + AOTBlock *block; + LLVMValueRef value; + LLVMBasicBlockRef next_llvm_end_block; + char name[32]; + uint32 i, result_index; + + /* Check block stack */ + if (!(block = func_ctx->block_stack.block_list_end)) { + aot_set_last_error("WASM block stack underflow."); + return false; + } + + /* Create the end block */ + if (!block->llvm_end_block) { + format_block_name(name, sizeof(name), + block->block_index, block->label_type, LABEL_END); + CREATE_BLOCK(block->llvm_end_block, name); + if ((next_llvm_end_block = find_next_llvm_end_block(block))) + MOVE_BLOCK_BEFORE(block->llvm_end_block, next_llvm_end_block); + } + + /* Handle block result values */ + CREATE_RESULT_VALUE_PHIS(block); + for (i = 0; i < block->result_count; i++) { + result_index = block->result_count - 1 - i; + POP(value, block->result_types[result_index]); + ADD_TO_RESULT_PHIS(block, value, result_index); + } + + /* Jump to the end block */ + BUILD_BR(block->llvm_end_block); + + block->is_reachable = true; + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +fail: + return false; +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef terminate_addr, terminate_flags, flag, offset, res; + LLVMBasicBlockRef terminate_check_block, non_terminate_block; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + LLVMBasicBlockRef terminate_block; + + /* Offset of suspend_flags */ + offset = I32_CONST(5); + CHECK_LLVM_CONST(offset); + + if (!(terminate_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->exec_env, + &offset, 1, "terminate_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(terminate_addr = + LLVMBuildBitCast(comp_ctx->builder, + terminate_addr, + INT32_PTR_TYPE, "terminate_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + + if (!(terminate_flags = + LLVMBuildLoad(comp_ctx->builder, + terminate_addr, "terminate_flags"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + /* Set terminate_flags memory accecc to volatile, so that the value + will always be loaded from memory rather than register */ + LLVMSetVolatile(terminate_flags, true); + + CREATE_BLOCK(terminate_check_block, "terminate_check"); + MOVE_BLOCK_AFTER_CURR(terminate_check_block); + + CREATE_BLOCK(non_terminate_block, "non_terminate"); + MOVE_BLOCK_AFTER_CURR(non_terminate_block); + + BUILD_ICMP(LLVMIntSGT, terminate_flags, I32_ZERO, res, "need_terminate"); + BUILD_COND_BR(res, terminate_check_block, non_terminate_block); + + /* Move builder to terminate check block */ + SET_BUILDER_POS(terminate_check_block); + + CREATE_BLOCK(terminate_block, "terminate"); + MOVE_BLOCK_AFTER_CURR(terminate_block); + + if (!(flag = + LLVMBuildAnd(comp_ctx->builder, terminate_flags, + I32_ONE, "termination_flag"))) { + aot_set_last_error("llvm build AND failed"); + return false; + } + + BUILD_ICMP(LLVMIntSGT, flag, I32_ZERO, res, "need_terminate"); + BUILD_COND_BR(res, terminate_block, non_terminate_block); + + /* Move builder to terminate block */ + SET_BUILDER_POS(terminate_block); + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + + /* Move builder to terminate block */ + SET_BUILDER_POS(non_terminate_block); + return true; + +fail: + return false; +} +#endif /* End of WASM_ENABLE_THREAD_MGR */ + +bool +aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip) +{ + AOTBlock *block_dst; + LLVMValueRef value_ret, value_param; + LLVMBasicBlockRef next_llvm_end_block; + char name[32]; + uint32 i, param_index, result_index; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + + if (!(block_dst = get_target_block(func_ctx, br_depth))) { + return false; + } + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + /* Dest block is Loop block */ + /* Handle Loop parameters */ + for (i = 0; i < block_dst->param_count; i++) { + param_index = block_dst->param_count - 1 - i; + POP(value_param, block_dst->param_types[param_index]); + ADD_TO_PARAM_PHIS(block_dst, value_param, param_index); + } + BUILD_BR(block_dst->llvm_entry_block); + } + else { + /* Dest block is Block/If/Function block */ + /* Create the end block */ + if (!block_dst->llvm_end_block) { + format_block_name(name, sizeof(name), + block_dst->block_index, block_dst->label_type, + LABEL_END); + CREATE_BLOCK(block_dst->llvm_end_block, name); + if ((next_llvm_end_block = find_next_llvm_end_block(block_dst))) + MOVE_BLOCK_BEFORE(block_dst->llvm_end_block, + next_llvm_end_block); + } + + block_dst->is_reachable = true; + + /* Handle result values */ + CREATE_RESULT_VALUE_PHIS(block_dst); + for (i = 0; i < block_dst->result_count; i++) { + result_index = block_dst->result_count - 1 - i; + POP(value_ret, block_dst->result_types[result_index]); + ADD_TO_RESULT_PHIS(block_dst, value_ret, result_index); + } + /* Jump to the end block */ + BUILD_BR(block_dst->llvm_end_block); + } + + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +fail: + return false; +} + +bool +aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip) +{ + AOTBlock *block_dst; + LLVMValueRef value_cmp, value, *values = NULL; + LLVMBasicBlockRef llvm_else_block, next_llvm_end_block; + char name[32]; + uint32 i, param_index, result_index; + uint64 size; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + + POP_COND(value_cmp); + if (!LLVMIsConstant(value_cmp)) { + /* Compare value is not constant, create condition br IR */ + if (!(block_dst = get_target_block(func_ctx, br_depth))) { + return false; + } + + /* Create llvm else block */ + CREATE_BLOCK(llvm_else_block, "br_if_else"); + MOVE_BLOCK_AFTER_CURR(llvm_else_block); + + if (block_dst->label_type == LABEL_TYPE_LOOP) { + /* Dest block is Loop block */ + /* Handle Loop parameters */ + if (block_dst->param_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->param_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (i = 0; i < block_dst->param_count; i++) { + param_index = block_dst->param_count - 1 - i; + POP(value, block_dst->param_types[param_index]); + ADD_TO_PARAM_PHIS(block_dst, value, param_index); + values[param_index] = value; + } + for (i = 0; i < block_dst->param_count; i++) { + PUSH(values[i], block_dst->param_types[i]); + } + wasm_runtime_free(values); + values = NULL; + } + + BUILD_COND_BR(value_cmp, block_dst->llvm_entry_block, + llvm_else_block); + + /* Move builder to else block */ + SET_BUILDER_POS(llvm_else_block); + } + else { + /* Dest block is Block/If/Function block */ + /* Create the end block */ + if (!block_dst->llvm_end_block) { + format_block_name(name, sizeof(name), + block_dst->block_index, block_dst->label_type, + LABEL_END); + CREATE_BLOCK(block_dst->llvm_end_block, name); + if ((next_llvm_end_block = find_next_llvm_end_block(block_dst))) + MOVE_BLOCK_BEFORE(block_dst->llvm_end_block, + next_llvm_end_block); + } + + /* Set reachable flag and create condition br IR */ + block_dst->is_reachable = true; + + /* Handle result values */ + if (block_dst->result_count) { + size = sizeof(LLVMValueRef) * (uint64)block_dst->result_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + CREATE_RESULT_VALUE_PHIS(block_dst); + for (i = 0; i < block_dst->result_count; i++) { + result_index = block_dst->result_count - 1 - i; + POP(value, block_dst->result_types[result_index]); + values[result_index] = value; + ADD_TO_RESULT_PHIS(block_dst, value, result_index); + } + for (i = 0; i < block_dst->result_count; i++) { + PUSH(values[i], block_dst->result_types[i]); + } + wasm_runtime_free(values); + values = NULL; + } + + /* Condition jump to end block */ + BUILD_COND_BR(value_cmp, block_dst->llvm_end_block, + llvm_else_block); + + /* Move builder to else block */ + SET_BUILDER_POS(llvm_else_block); + } + } + else { + if ((int32)LLVMConstIntGetZExtValue(value_cmp) != 0) { + /* Compare value is not 0, condition is true, same as op_br */ + return aot_compile_op_br(comp_ctx, func_ctx, br_depth, p_frame_ip); + } + else { + /* Compare value is not 0, condition is false, skip br_if */ + return true; + } + } + return true; +fail: + if (values) + wasm_runtime_free(values); + return false; +} + +bool +aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 *br_depths, uint32 br_count, + uint8 **p_frame_ip) +{ + uint32 i, j; + LLVMValueRef value_switch, value_cmp, value_case, value, *values = NULL; + LLVMBasicBlockRef default_llvm_block = NULL, target_llvm_block; + LLVMBasicBlockRef next_llvm_end_block; + AOTBlock *target_block; + uint32 br_depth, depth_idx; + uint32 param_index, result_index; + uint64 size; + char name[32]; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + + POP_I32(value_cmp); + if (!LLVMIsConstant(value_cmp)) { + /* Compare value is not constant, create switch IR */ + for (i = 0; i <= br_count; i++) { + target_block = get_target_block(func_ctx, br_depths[i]); + if (!target_block) + return false; + + if (target_block->label_type != LABEL_TYPE_LOOP) { + /* Dest block is Block/If/Function block */ + /* Create the end block */ + if (!target_block->llvm_end_block) { + format_block_name(name, sizeof(name), + target_block->block_index, + target_block->label_type, + LABEL_END); + CREATE_BLOCK(target_block->llvm_end_block, name); + if ((next_llvm_end_block = + find_next_llvm_end_block(target_block))) + MOVE_BLOCK_BEFORE(target_block->llvm_end_block, + next_llvm_end_block); + } + /* Handle result values */ + if (target_block->result_count) { + size = sizeof(LLVMValueRef) * (uint64)target_block->result_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + CREATE_RESULT_VALUE_PHIS(target_block); + for (j = 0; j < target_block->result_count; j++) { + result_index = target_block->result_count - 1 - j; + POP(value, target_block->result_types[result_index]); + values[result_index] = value; + ADD_TO_RESULT_PHIS(target_block, value, result_index); + } + for (j = 0; j < target_block->result_count; j++) { + PUSH(values[j], target_block->result_types[j]); + } + wasm_runtime_free(values); + } + target_block->is_reachable = true; + if (i == br_count) + default_llvm_block = target_block->llvm_end_block; + } + else { + /* Handle Loop parameters */ + if (target_block->param_count) { + size = sizeof(LLVMValueRef) * (uint64)target_block->param_count; + if (size >= UINT32_MAX + || !(values = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + for (j = 0; j < target_block->param_count; j++) { + param_index = target_block->param_count - 1 - j; + POP(value, target_block->param_types[param_index]); + values[param_index] = value; + ADD_TO_PARAM_PHIS(target_block, value, param_index); + } + for (j = 0; j < target_block->param_count; j++) { + PUSH(values[j], target_block->param_types[j]); + } + wasm_runtime_free(values); + } + if (i == br_count) + default_llvm_block = target_block->llvm_entry_block; + } + } + + /* Create switch IR */ + if (!(value_switch = LLVMBuildSwitch(comp_ctx->builder, value_cmp, + default_llvm_block, br_count))) { + aot_set_last_error("llvm build switch failed."); + return false; + } + + /* Add each case for switch IR */ + for (i = 0; i < br_count; i++) { + value_case = I32_CONST(i); + CHECK_LLVM_CONST(value_case); + target_block = get_target_block(func_ctx, br_depths[i]); + if (!target_block) + return false; + target_llvm_block = target_block->label_type != LABEL_TYPE_LOOP + ? target_block->llvm_end_block + : target_block->llvm_entry_block; + LLVMAddCase(value_switch, value_case, target_llvm_block); + } + + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + } + else { + /* Compare value is constant, create br IR */ + depth_idx = (uint32)LLVMConstIntGetZExtValue(value_cmp); + br_depth = br_depths[br_count]; + if (depth_idx < br_count) { + br_depth = br_depths[depth_idx]; + } + return aot_compile_op_br(comp_ctx, func_ctx, br_depth, p_frame_ip); + } +fail: + if (values) + wasm_runtime_free(values); + return false; +} + +bool +aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + AOTBlock *block_func = func_ctx->block_stack.block_list_head; + LLVMValueRef value; + AOTFuncType *func_type; + uint32 i, param_index, result_index; + + bh_assert(block_func); + func_type = func_ctx->aot_func->func_type; + + if (block_func->result_count) { + /* Store extra result values to function parameters */ + for (i = 0; i < block_func->result_count - 1; i++) { + result_index = block_func->result_count - 1 - i; + POP(value, block_func->result_types[result_index]); + param_index = func_type->param_count + result_index; + if (!LLVMBuildStore(comp_ctx->builder, + value, + LLVMGetParam(func_ctx->func, param_index))) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + } + /* Return the first result value */ + POP(value, block_func->result_types[0]); + if (!LLVMBuildRet(comp_ctx->builder, value)) { + aot_set_last_error("llvm build return failed."); + goto fail; + } + } + else { + if (!LLVMBuildRetVoid(comp_ctx->builder)) { + aot_set_last_error("llvm build return void failed."); + goto fail; + } + } + + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +fail: + return false; +} + +bool +aot_compile_op_unreachable(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_UNREACHABLE, + false, NULL, NULL)) + return false; + + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +} + +bool +aot_handle_next_reachable_block(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 **p_frame_ip) +{ + return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); +} diff --git a/wamr/core/iwasm/compilation/aot_emit_control.h b/wamr/core/iwasm/compilation/aot_emit_control.h new file mode 100644 index 0000000..c0c0290 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_control.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_CONTROL_H_ +#define _AOT_EMIT_CONTROL_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip, uint8 *frame_ip_end, + uint32 label_type, uint32 param_count, uint8 *param_types, + uint32 result_count, uint8 *result_types); + +bool +aot_compile_op_else(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip); + +bool +aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip); + +bool +aot_compile_op_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip); + +bool +aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 br_depth, uint8 **p_frame_ip); + +bool +aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 *br_depths, uint32 br_count, + uint8 **p_frame_ip); + +bool +aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 **p_frame_ip); + +bool +aot_compile_op_unreachable(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 **p_frame_ip); + +bool +aot_handle_next_reachable_block(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 **p_frame_ip); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_CONTROL_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_conversion.c b/wamr/core/iwasm/compilation/aot_emit_conversion.c new file mode 100644 index 0000000..7dcb928 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_conversion.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_conversion.h" +#include "aot_emit_exception.h" +#include "aot_emit_numberic.h" +#include "../aot/aot_runtime.h" + +static bool +trunc_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef operand, LLVMTypeRef dest_type, + LLVMValueRef min_value, LLVMValueRef max_value, + char *name, bool sign) +{ + LLVMBasicBlockRef check_nan_succ, check_overflow_succ; + LLVMValueRef is_less, is_greater, res; + + if (!(res = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, + operand, operand, "fcmp_is_nan"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + + if (!(check_nan_succ = + LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "check_nan_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_nan_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INVALID_CONVERSION_TO_INTEGER, + true, res, check_nan_succ))) + goto fail; + + if (!(is_less = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLE, operand, + min_value, "fcmp_min_value"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + + if (!(is_greater = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGE, operand, + max_value, "fcmp_max_value"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + + if (!(res = LLVMBuildOr(comp_ctx->builder, is_less, is_greater, "is_overflow"))) { + aot_set_last_error("llvm build logic and failed."); + goto fail; + } + + /* Check if float value out of range */ + if (!(check_overflow_succ = + LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "check_overflow_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_overflow_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_INTEGER_OVERFLOW, + true, res, check_overflow_succ))) + goto fail; + + if (sign) + res = LLVMBuildFPToSI(comp_ctx->builder, operand, dest_type, name); + else + res = LLVMBuildFPToUI(comp_ctx->builder, operand, dest_type, name); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + if (dest_type == I32_TYPE) + PUSH_I32(res); + else if (dest_type == I64_TYPE) + PUSH_I64(res); + return true; +fail: + return false; +} + +#define ADD_BASIC_BLOCK(block, name) do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, \ + name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + \ + LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \ +} while (0) + +static bool +trunc_sat_float_to_int(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef operand, LLVMTypeRef dest_type, + LLVMValueRef min_value, LLVMValueRef max_value, + char *name, bool sign) +{ + LLVMBasicBlockRef check_nan_succ, check_less_succ, check_greater_succ; + LLVMBasicBlockRef is_nan_block, is_less_block, is_greater_block, res_block; + LLVMValueRef is_less, is_greater, res, phi; + LLVMValueRef zero = (dest_type == I32_TYPE) ? I32_ZERO : I64_ZERO; + LLVMValueRef vmin, vmax; + + if (!(res = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, + operand, operand, "fcmp_is_nan"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + + ADD_BASIC_BLOCK(check_nan_succ, "check_nan_succ"); + ADD_BASIC_BLOCK(is_nan_block, "is_nan_block"); + ADD_BASIC_BLOCK(check_less_succ, "check_less_succ"); + ADD_BASIC_BLOCK(is_less_block, "is_less_block"); + ADD_BASIC_BLOCK(check_greater_succ, "check_greater_succ"); + ADD_BASIC_BLOCK(is_greater_block, "is_greater_block"); + ADD_BASIC_BLOCK(res_block, "res_block"); + + if (!LLVMBuildCondBr(comp_ctx->builder, res, + is_nan_block, check_nan_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_nan block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_nan_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_nan_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_nan_succ); + if (!(is_less = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOLE, operand, + min_value, "fcmp_min_value"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + if (!LLVMBuildCondBr(comp_ctx->builder, is_less, + is_less_block, check_less_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_less block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_less_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_less_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_less_succ); + if (!(is_greater = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOGE, operand, + max_value, "fcmp_max_value"))) { + aot_set_last_error("llvm build fcmp failed."); + goto fail; + } + if (!LLVMBuildCondBr(comp_ctx->builder, is_greater, + is_greater_block, check_greater_succ)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* Start to translate is_greater block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, is_greater_block); + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate check_greater_succ block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_greater_succ); + if (sign) + res = LLVMBuildFPToSI(comp_ctx->builder, operand, dest_type, name); + else + res = LLVMBuildFPToUI(comp_ctx->builder, operand, dest_type, name); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + if (!LLVMBuildBr(comp_ctx->builder, res_block)) { + aot_set_last_error("llvm build br failed."); + goto fail; + } + + /* Start to translate res_block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, res_block); + /* Create result phi */ + if (!(phi = LLVMBuildPhi(comp_ctx->builder, + dest_type, + "trunc_sat_result_phi"))) { + aot_set_last_error("llvm build phi failed."); + return false; + } + + /* Add phi incoming values */ + if (dest_type == I32_TYPE) { + if (sign) { + vmin = I32_CONST(INT32_MIN); + vmax = I32_CONST(INT32_MAX); + } + else { + vmin = I32_CONST(0); + vmax = I32_CONST(UINT32_MAX); + } + } + else if (dest_type == I64_TYPE) { + if (sign) { + vmin = I64_CONST(INT64_MIN); + vmax = I64_CONST(INT64_MAX); + } + else { + vmin = I64_CONST(0); + vmax = I64_CONST(UINT64_MAX); + } + } + LLVMAddIncoming(phi, &zero, &is_nan_block, 1); + LLVMAddIncoming(phi, &vmin, &is_less_block, 1); + LLVMAddIncoming(phi, &vmax, &is_greater_block, 1); + LLVMAddIncoming(phi, &res, &check_greater_succ, 1); + + if (dest_type == I32_TYPE) + PUSH_I32(phi); + else if (dest_type == I64_TYPE) + PUSH_I64(phi); + return true; +fail: + return false; +} + +bool +aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef value, res; + + POP_I64(value); + + if (!(res = LLVMBuildTrunc(comp_ctx->builder, value, I32_TYPE, "i32_wrap_i64"))) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i32_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating) +{ + LLVMValueRef value; + LLVMValueRef min_value, max_value; + + POP_F32(value); + + if (sign) { + min_value = F32_CONST(-2147483904.0f); + max_value = F32_CONST(2147483648.0f); + } + else { + min_value = F32_CONST(-1.0f); + max_value = F32_CONST(4294967296.0f); + } + + if (!saturating) + return trunc_float_to_int(comp_ctx, func_ctx, value, + I32_TYPE, min_value, max_value, + sign ? "i32_trunc_f32_s" : "i32_trunc_f32_u", sign); + else + return trunc_sat_float_to_int(comp_ctx, func_ctx, value, + I32_TYPE, min_value, max_value, + sign ? "i32_trunc_sat_f32_s" : + "i32_trunc_sat_f32_u", sign); +fail: + return false; +} + +bool +aot_compile_op_i32_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating) +{ + LLVMValueRef value; + LLVMValueRef min_value, max_value; + + POP_F64(value); + + if (sign) { + min_value = F64_CONST(-2147483649.0); + max_value = F64_CONST(2147483648.0); + } + else { + min_value = F64_CONST(-1.0); + max_value = F64_CONST(4294967296.0); + } + + if (!saturating) + return trunc_float_to_int(comp_ctx, func_ctx, value, + I32_TYPE, min_value, max_value, + sign ? "i32_trunc_f64_s" : "i32_trunc_f64_u", sign); + else + return trunc_sat_float_to_int(comp_ctx, func_ctx, value, + I32_TYPE, min_value, max_value, + sign ? "i32_trunc_sat_f64_s" : + "i32_trunc_sat_f64_u", sign); +fail: + return false; +} + +bool +aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef value, res; + + POP_I32(value); + + if (sign) + res = LLVMBuildSExt(comp_ctx->builder, value, I64_TYPE, "i64_extend_i32_s"); + else + res = LLVMBuildZExt(comp_ctx->builder, value, I64_TYPE, "i64_extend_i32_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I64(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_extend_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int8 bitwidth) +{ + LLVMValueRef value, res, cast_value = NULL; + + POP_I64(value); + + if (bitwidth == 8) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, + INT8_TYPE, true, "i8_intcast_i64"); + } + else if (bitwidth == 16) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, + INT16_TYPE, true, "i16_intcast_i64"); + } + else if (bitwidth == 32) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, + I32_TYPE, true, "i32_intcast_i64"); + } + + if (!cast_value) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + res = LLVMBuildSExt(comp_ctx->builder, cast_value, I64_TYPE, "i64_extend_i64_s"); + + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I64(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i32_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int8 bitwidth) +{ + LLVMValueRef value, res, cast_value = NULL; + + POP_I32(value); + + if (bitwidth == 8) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, + INT8_TYPE, true, "i8_intcast_i32"); + } + else if (bitwidth == 16) { + cast_value = LLVMBuildIntCast2(comp_ctx->builder, value, + INT16_TYPE, true, "i16_intcast_i32"); + } + + if (!cast_value) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + res = LLVMBuildSExt(comp_ctx->builder, cast_value, I32_TYPE, "i32_extend_i32_s"); + + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_I32(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating) +{ + LLVMValueRef value; + LLVMValueRef min_value, max_value; + + POP_F32(value); + + if (sign) { + min_value = F32_CONST(-9223373136366403584.0f); + max_value = F32_CONST(9223372036854775808.0f); + } + else { + min_value = F32_CONST(-1.0f); + max_value = F32_CONST(18446744073709551616.0f); + } + + if (!saturating) + return trunc_float_to_int(comp_ctx, func_ctx, value, + I64_TYPE, min_value, max_value, + sign ? "i64_trunc_f32_s" : "i64_trunc_f32_u", sign); + else + return trunc_sat_float_to_int(comp_ctx, func_ctx, value, + I64_TYPE, min_value, max_value, + sign ? "i64_trunc_sat_f32_s" : + "i64_trunc_sat_f32_u", sign); +fail: + return false; +} + +bool +aot_compile_op_i64_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating) +{ + LLVMValueRef value; + LLVMValueRef min_value, max_value; + + POP_F64(value); + + if (sign) { + min_value = F64_CONST(-9223372036854777856.0); + max_value = F64_CONST(9223372036854775808.0); + } + else { + min_value = F64_CONST(-1.0); + max_value = F64_CONST(18446744073709551616.0); + } + + if (!saturating) + return trunc_float_to_int(comp_ctx, func_ctx, value, + I64_TYPE, min_value, max_value, + sign ? "i64_trunc_f64_s" : "i64_trunc_f64_u", sign); + else + return trunc_sat_float_to_int(comp_ctx, func_ctx, value, + I64_TYPE, min_value, max_value, + sign ? "i64_trunc_sat_f64_s" : + "i64_trunc_sat_f64_u", sign); + +fail: + return false; +} + +bool +aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef value, res; + + POP_I32(value); + + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i32_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i32_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F32(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef value, res; + + POP_I64(value); + + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i64_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F32_TYPE, "f32_convert_i64_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F32(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef value, res; + + POP_F64(value); + + if (!(res = LLVMBuildFPTrunc(comp_ctx->builder, value, F32_TYPE, "f32_demote_f64"))) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F32(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef value, res; + + POP_I32(value); + + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i32_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i32_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F64(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign) +{ + LLVMValueRef value, res; + + POP_I64(value); + + if (sign) + res = LLVMBuildSIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i64_s"); + else + res = LLVMBuildUIToFP(comp_ctx->builder, value, F64_TYPE, "f64_convert_i64_u"); + if (!res) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F64(res); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef value, res; + + POP_F32(value); + + if (!(res = LLVMBuildFPExt(comp_ctx->builder, value, F64_TYPE, "f64_promote_f32"))) { + aot_set_last_error("llvm build conversion failed."); + return false; + } + + PUSH_F64(res); + + /* Avoid the promote being optimized away */ + PUSH_F64(F64_CONST(1.0)); + return aot_compile_op_f64_arithmetic(comp_ctx, func_ctx, FLOAT_MUL); +fail: + return false; +} + +bool +aot_compile_op_i64_reinterpret_f64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef value; + POP_F64(value); + if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, + I64_TYPE, "i64"))) { + aot_set_last_error("llvm build fp to si failed."); + return false; + } + PUSH_I64(value); + return true; +fail: + return false; +} + + +bool +aot_compile_op_i32_reinterpret_f32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef value; + POP_F32(value); + if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, + I32_TYPE, "i32"))) { + aot_set_last_error("llvm build fp to si failed."); + return false; + } + PUSH_I32(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_reinterpret_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef value; + POP_I64(value); + if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, + F64_TYPE, "f64"))) { + aot_set_last_error("llvm build si to fp failed."); + return false; + } + PUSH_F64(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_reinterpret_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef value; + POP_I32(value); + if (!(value = LLVMBuildBitCast(comp_ctx->builder, value, + F32_TYPE, "f32"))) { + aot_set_last_error("llvm build si to fp failed."); + return false; + } + PUSH_F32(value); + return true; +fail: + return false; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_conversion.h b/wamr/core/iwasm/compilation/aot_emit_conversion.h new file mode 100644 index 0000000..da80231 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_conversion.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_CONVERSION_H_ +#define _AOT_EMIT_CONVERSION_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_i32_wrap_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i32_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating); + +bool +aot_compile_op_i32_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating); + +bool +aot_compile_op_i64_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_i64_extend_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int8 bitwidth); + +bool +aot_compile_op_i32_extend_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int8 bitwidth); + +bool +aot_compile_op_i64_trunc_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating); + +bool +aot_compile_op_i64_trunc_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign, bool saturating); + +bool +aot_compile_op_f32_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_f32_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_f32_demote_f64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_f64_convert_i32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_f64_convert_i64(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool sign); + +bool +aot_compile_op_f64_promote_f32(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i64_reinterpret_f64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_i32_reinterpret_f32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_f64_reinterpret_i64(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +bool +aot_compile_op_f32_reinterpret_i32(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_CONVERSION_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_exception.c b/wamr/core/iwasm/compilation/aot_emit_exception.c new file mode 100644 index 0000000..6291118 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_exception.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" + +static char *exce_block_names[] = { + "exce_unreachable", /* EXCE_UNREACHABLE */ + "exce_out_of_memory", /* EXCE_OUT_OF_MEMORY */ + "exce_out_of_bounds_mem_access",/* EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS */ + "exce_integer_overflow", /* EXCE_INTEGER_OVERFLOW */ + "exce_divide_by_zero", /* EXCE_INTEGER_DIVIDE_BY_ZERO */ + "exce_invalid_convert_to_int", /* EXCE_INVALID_CONVERSION_TO_INTEGER */ + "exce_invalid_func_type_idx", /* EXCE_INVALID_FUNCTION_TYPE_INDEX */ + "exce_invalid_func_idx", /* EXCE_INVALID_FUNCTION_INDEX */ + "exce_undefined_element", /* EXCE_UNDEFINED_ELEMENT */ + "exce_uninit_element", /* EXCE_UNINITIALIZED_ELEMENT */ + "exce_call_unlinked", /* EXCE_CALL_UNLINKED_IMPORT_FUNC */ + "exce_native_stack_overflow", /* EXCE_NATIVE_STACK_OVERFLOW */ + "exce_unaligned_atomic" /* EXCE_UNALIGNED_ATOMIC */ +}; + +bool +aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 exception_id, + bool is_cond_br, + LLVMValueRef cond_br_if, + LLVMBasicBlockRef cond_br_else_block) +{ + LLVMBasicBlockRef exce_block; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMValueRef exce_id = I32_CONST((uint32)exception_id), func_const, func; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + LLVMValueRef param_values[2]; + + bh_assert(exception_id >= 0 && exception_id < EXCE_NUM); + + CHECK_LLVM_CONST(exce_id); + + /* Create got_exception block if needed */ + if (!func_ctx->got_exception_block) { + if (!(func_ctx->got_exception_block = + LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "got_exception"))) { + aot_set_last_error("add LLVM basic block failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, + func_ctx->got_exception_block); + + /* Create exection id phi */ + if (!(func_ctx->exception_id_phi = + LLVMBuildPhi(comp_ctx->builder, + comp_ctx->basic_types.int32_type, + "exception_id_phi"))) { + aot_set_last_error("llvm build phi failed."); + return false; + } + + /* Call aot_set_exception_with_id() to throw exception */ + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = VOID_TYPE; + + /* Create function type */ + if (!(func_type = LLVMFunctionType(ret_type, param_types, + 2, false))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + /* Create function type */ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("create LLVM function type failed."); + return false; + } + /* Create LLVM function with const function pointer */ + if (!(func_const = + I64_CONST((uint64)(uintptr_t)aot_set_exception_with_id)) + || !(func = LLVMConstIntToPtr(func_const, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else { + /* Create LLVM function with external function pointer */ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, + "aot_set_exception_with_id")) + && !(func = LLVMAddFunction(comp_ctx->module, + "aot_set_exception_with_id", + func_type))) { + aot_set_last_error("add LLVM function failed."); + return false; + } + } + + /* Call the aot_set_exception_with_id() function */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = func_ctx->exception_id_phi; + if (!LLVMBuildCall(comp_ctx->builder, func, param_values, + 2, "")) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* Create return IR */ + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + + /* Resume the builder position */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + } + + /* Create exception block if needed */ + if (!(exce_block = func_ctx->exception_blocks[exception_id])) { + if (!(func_ctx->exception_blocks[exception_id] = exce_block = + LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + exce_block_names[exception_id]))) { + aot_set_last_error("add LLVM basic block failed."); + return false; + } + + /* Move before got_exception block */ + LLVMMoveBasicBlockBefore(exce_block, func_ctx->got_exception_block); + + /* Add phi incoming value to got_exception block */ + LLVMAddIncoming(func_ctx->exception_id_phi, &exce_id, &exce_block, 1); + + /* Jump to got exception block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, exce_block); + if (!LLVMBuildBr(comp_ctx->builder, func_ctx->got_exception_block)) { + aot_set_last_error("llvm build br failed."); + return false; + } + } + + /* Resume builder position */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + + if (!is_cond_br) { + /* not condition br, create br IR */ + if (!LLVMBuildBr(comp_ctx->builder, exce_block)) { + aot_set_last_error("llvm build br failed."); + return false; + } + } + else { + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, cond_br_if, + exce_block, cond_br_else_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + /* Start to translate the else block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, cond_br_else_block); + } + + return true; +fail: + return false; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_exception.h b/wamr/core/iwasm/compilation/aot_emit_exception.h new file mode 100644 index 0000000..e86faf0 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_exception.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_EXCEPTION_H_ +#define _AOT_EMIT_EXCEPTION_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + int32 exception_id, + bool is_cond_br, + LLVMValueRef cond_br_if, + LLVMBasicBlockRef cond_br_else_block); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_EXCEPTION_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_function.c b/wamr/core/iwasm/compilation/aot_emit_function.c new file mode 100644 index 0000000..af7362f --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_function.c @@ -0,0 +1,742 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_function.h" +#include "aot_emit_exception.h" +#include "aot_emit_control.h" +#include "../aot/aot_runtime.h" + +static bool +create_func_return_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + + /* Create function return block if it isn't created */ + if (!func_ctx->func_return_block) { + if (!(func_ctx->func_return_block = + LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, "func_ret"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + /* Create return IR */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, func_ctx->func_return_block); + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + return true; +} + +/* Check whether there was exception thrown, if yes, return directly */ +static bool +check_exception_thrown(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMBasicBlockRef block_curr, check_exce_succ; + LLVMValueRef value, cmp; + + /* Create function return block if it isn't created */ + if (!create_func_return_block(comp_ctx, func_ctx)) + return false; + + /* Load the first byte of aot_module_inst->cur_exception, and check + whether it is '\0'. If yes, no exception was thrown. */ + if (!(value = LLVMBuildLoad(comp_ctx->builder, func_ctx->cur_exception, + "exce_value")) + || !(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, + value, I8_ZERO, "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + /* Add check exection success block */ + if (!(check_exce_succ = LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "check_exce_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMMoveBasicBlockAfter(check_exce_succ, block_curr); + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, + check_exce_succ, func_ctx->func_return_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_exce_succ); + return true; +} + +/* Check whether there was exception thrown, if yes, return directly */ +static bool +check_call_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef res) +{ + LLVMBasicBlockRef block_curr, check_call_succ; + LLVMValueRef cmp; + + /* Create function return block if it isn't created */ + if (!create_func_return_block(comp_ctx, func_ctx)) + return false; + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, + res, I8_ZERO, "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + /* Add check exection success block */ + if (!(check_call_succ = LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "check_exce_succ"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMMoveBasicBlockAfter(check_call_succ, block_curr); + + LLVMPositionBuilderAtEnd(comp_ctx->builder, block_curr); + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, + check_call_succ, func_ctx->func_return_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_call_succ); + 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, LLVMValueRef *p_res) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[4]; + LLVMTypeRef ret_ptr_type, elem_ptr_type; + LLVMValueRef func, elem_idx, elem_ptr; + LLVMValueRef func_param_values[4], value_ret = NULL, 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] = I32_TYPE; /* argc */ + func_param_types[3] = INT32_PTR_TYPE; /* argv */ + if (!(func_type = LLVMFunctionType(INT8_TYPE, func_param_types, 4, 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_cell_num > 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]); + } + + func_param_values[0] = func_ctx->exec_env; + func_param_values[1] = func_idx; + func_param_values[2] = I32_CONST(param_cell_num); + func_param_values[3] = func_ctx->argv_buf; + + if (!func_param_values[2]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_invoke_native() function */ + if (!(res = LLVMBuildCall(comp_ctx->builder, func, + func_param_values, 4, "res"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* get function return value */ + 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; + } + if (!(*p_value_ret = LLVMBuildLoad(comp_ctx->builder, value_ret, + "value_ret"))) { + aot_set_last_error("llvm build load failed."); + return false; + } + } + + *p_res = res; + return true; +} + +static bool +check_stack_boundary(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 callee_cell_num) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_stack; + LLVMValueRef callee_local_size, stack_bound, cmp; + + if (!(callee_local_size = I32_CONST(callee_cell_num * 4))) { + aot_set_last_error("llvm build const failed."); + return false; + } + + if (!(stack_bound = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->native_stack_bound, + &callee_local_size, 1, + "stack_bound"))) { + aot_set_last_error("llvm build inbound gep failed."); + return false; + } + + if (!(check_stack = LLVMAppendBasicBlockInContext(comp_ctx->context, + func_ctx->func, + "check_stack"))) { + aot_set_last_error("llvm add basic block failed."); + return false; + } + + LLVMMoveBasicBlockAfter(check_stack, block_curr); + + if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, + func_ctx->last_alloca, stack_bound, + "cmp"))) { + aot_set_last_error("llvm build icmp failed."); + return false; + } + + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_NATIVE_STACK_OVERFLOW, + true, cmp, check_stack)) { + return false; + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, check_stack); + 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, param_cell_num = 0; + uint32 ext_ret_cell_num = 0, cell_num = 0; + AOTFuncContext **func_ctxes = comp_ctx->func_ctxes; + AOTFuncType *func_type; + AOTFunc *aot_func; + LLVMTypeRef *param_types = NULL, ret_type; + LLVMTypeRef ext_ret_ptr_type; + LLVMValueRef *param_values = NULL, value_ret = NULL, func; + LLVMValueRef import_func_idx, res; + LLVMValueRef ext_ret, ext_ret_ptr, ext_ret_idx; + int32 i, j = 0, param_count, result_count, ext_ret_count; + uint64 total_size; + uint32 callee_cell_num; + uint8 wasm_ret_type; + uint8 *ext_ret_types = NULL; + bool ret = false; + char buf[32]; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + + /* Check function index */ + if (func_idx >= import_func_count + func_count) { + aot_set_last_error("Function index out of range."); + return false; + } + + /* Get function type */ + if (func_idx < import_func_count) + func_type = import_funcs[func_idx].func_type; + else + func_type = func_ctxes[func_idx - import_func_count]-> + aot_func->func_type; + + /* Get param cell number */ + param_cell_num = func_type->param_cell_num; + + /* Allocate memory for parameters. + * Parameters layout: + * - exec env + * - wasm function's parameters + * - extra results'(except the first one) addresses + */ + param_count = (int32)func_type->param_count; + result_count = (int32)func_type->result_count; + ext_ret_count = result_count > 1 ? result_count - 1 : 0; + total_size = sizeof(LLVMValueRef) * (uint64)(param_count + 1 + + ext_ret_count); + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("Allocate memory failed."); + return false; + } + + /* First parameter is exec env */ + param_values[j++] = func_ctx->exec_env; + + /* Pop parameters from stack */ + for (i = param_count - 1; i >= 0; i--) + POP(param_values[i + j], func_type->types[i]); + + if (func_idx < import_func_count) { + 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); + if (total_size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("Allocate memory failed."); + goto fail; + } + + j = 0; + param_types[j++] = comp_ctx->exec_env_type; + + for (i = 0; i < param_count; i++) + param_types[j++] = TO_LLVM_TYPE(func_type->types[i]); + + if (func_type->result_count) { + wasm_ret_type = func_type->types[func_type->param_count]; + ret_type = TO_LLVM_TYPE(wasm_ret_type); + } + else { + 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, &res)) + goto fail; + + /* Check whether there was exception thrown when executing the function */ + if (!check_call_return(comp_ctx, func_ctx, res)) + goto fail; + } + else { + func = func_ctxes[func_idx - import_func_count]->func; + aot_func = func_ctxes[func_idx - import_func_count]->aot_func; + callee_cell_num = aot_func->param_cell_num + aot_func->local_cell_num + 1; + + if (comp_ctx->enable_bound_check + && !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num)) + goto fail; + + /* Prepare parameters for extra results */ + if (ext_ret_count > 0) { + ext_ret_types = func_type->types + param_count + 1; + ext_ret_cell_num = + wasm_get_cell_num(ext_ret_types, ext_ret_count); + if (ext_ret_cell_num > 64) { + aot_set_last_error("prepare extra results's return " + "address arguments failed: " + "maximum 64 parameter cell number supported."); + goto fail; + } + + for (i = 0; i < ext_ret_count; i++) { + if (!(ext_ret_idx = I32_CONST(cell_num)) + || !(ext_ret_ptr_type = + LLVMPointerType(TO_LLVM_TYPE(ext_ret_types[i]), 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + goto fail; + } + + snprintf(buf, sizeof(buf), "func%d_ext_ret%d_ptr", func_idx, i); + if (!(ext_ret_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->argv_buf, + &ext_ret_idx, 1, buf))) { + aot_set_last_error("llvm build GEP failed."); + goto fail; + } + snprintf(buf, sizeof(buf), "func%d_ext_ret%d_ptr_cast", func_idx, i); + if (!(ext_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, + ext_ret_ptr, + ext_ret_ptr_type, + buf))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } + param_values[1 + param_count + i] = ext_ret_ptr; + cell_num += wasm_value_type_cell_num(ext_ret_types[i]); + } + } + + /* Call the function */ + if (!(value_ret = LLVMBuildCall(comp_ctx->builder, func, + param_values, + (uint32)param_count + 1 + ext_ret_count, + (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)); + + /* Check whether there was exception thrown when executing the function */ + if (!check_exception_thrown(comp_ctx, func_ctx)) + goto fail; + } + + if (func_type->result_count > 0) { + /* Push the first result to stack */ + PUSH(value_ret, func_type->types[func_type->param_count]); + /* Load extra result from its address and push to stack */ + for (i = 0; i < ext_ret_count; i++) { + snprintf(buf, sizeof(buf), "func%d_ext_ret%d", func_idx, i); + if (!(ext_ret = LLVMBuildLoad(comp_ctx->builder, + param_values[1 + param_count + i], + buf))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + PUSH(ext_ret, ext_ret_types[i]); + } + } + + ret = true; +fail: + if (param_types) + wasm_runtime_free(param_types); + if (param_values) + wasm_runtime_free(param_values); + return ret; +} + +static bool +call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + AOTFuncType *aot_func_type, + LLVMValueRef func_type_idx, LLVMValueRef table_elem_idx, + LLVMTypeRef *param_types, LLVMValueRef *param_values, + uint32 param_count, uint32 param_cell_num, + uint32 result_count, uint8 *wasm_ret_types, + LLVMValueRef *value_rets, LLVMValueRef *p_res) +{ + LLVMTypeRef func_type, func_ptr_type, func_param_types[6]; + LLVMTypeRef ret_type, ret_ptr_type, elem_ptr_type; + LLVMValueRef func, ret_idx, ret_ptr, elem_idx, elem_ptr; + LLVMValueRef func_param_values[6], res = NULL; + char buf[32], *func_name = "aot_call_indirect"; + uint32 i, cell_num = 0, ret_cell_num, argv_cell_num; + +#if WASM_ENABLE_THREAD_MGR != 0 + /* Insert suspend check point */ + if (comp_ctx->enable_thread_mgr) { + if (!check_suspend_flags(comp_ctx, func_ctx)) + return false; + } +#endif + + /* prepare function type of aot_call_indirect */ + func_param_types[0] = comp_ctx->exec_env_type; /* exec_env */ + func_param_types[1] = INT8_TYPE; /* check_func_type */ + func_param_types[2] = I32_TYPE; /* func_type_idx */ + func_param_types[3] = I32_TYPE; /* table_elem_idx */ + func_param_types[4] = I32_TYPE; /* argc */ + func_param_types[5] = INT32_PTR_TYPE; /* argv */ + if (!(func_type = LLVMFunctionType(INT8_TYPE, func_param_types, 6, 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_call_indirect)) + || !(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; + } + } + + ret_cell_num = wasm_get_cell_num(wasm_ret_types, result_count); + argv_cell_num = param_cell_num > ret_cell_num ? param_cell_num : ret_cell_num; + if (argv_cell_num > 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]); + } + + func_param_values[0] = func_ctx->exec_env; + func_param_values[1] = I8_CONST(true); + func_param_values[2] = func_type_idx; + func_param_values[3] = table_elem_idx; + func_param_values[4] = I32_CONST(param_cell_num); + func_param_values[5] = func_ctx->argv_buf; + + if (!func_param_values[1] || !func_param_values[4]) { + aot_set_last_error("llvm create const failed."); + return false; + } + + /* call aot_call_indirect() function */ + if (!(res = LLVMBuildCall(comp_ctx->builder, func, + func_param_values, 6, "res"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + /* get function result values */ + cell_num = 0; + for (i = 0; i < result_count; i++) { + ret_type = TO_LLVM_TYPE(wasm_ret_types[i]); + if (!(ret_idx = I32_CONST(cell_num)) + || !(ret_ptr_type = LLVMPointerType(ret_type, 0))) { + aot_set_last_error("llvm add const or pointer type failed."); + return false; + } + + snprintf(buf, sizeof(buf), "argv_ret%d", i); + if (!(ret_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->argv_buf, &ret_idx, 1, buf)) + || !(ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ret_ptr, + ret_ptr_type, buf))) { + aot_set_last_error("llvm build GEP or bit cast failed."); + return false; + } + + snprintf(buf, sizeof(buf), "ret%d", i); + if (!(value_rets[i] = LLVMBuildLoad(comp_ctx->builder, ret_ptr, buf))) { + aot_set_last_error("llvm build load failed."); + return false; + } + cell_num += wasm_value_type_cell_num(wasm_ret_types[i]); + } + + *p_res = res; + return true; +} + +bool +aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_idx) +{ + AOTFuncType *func_type; + LLVMValueRef elem_idx, ftype_idx; + LLVMValueRef *param_values = NULL, *value_rets = NULL, res = NULL; + LLVMTypeRef *param_types = NULL; + int32 i, param_count, result_count; + uint32 param_cell_num; + uint64 total_size; + uint8 *wasm_ret_types = NULL; + bool ret; + + /* Check function type index */ + if (type_idx >= comp_ctx->comp_data->func_type_count) { + aot_set_last_error("type index is overflow"); + return false; + } + + ftype_idx = I32_CONST(type_idx); + CHECK_LLVM_CONST(ftype_idx); + + func_type = comp_ctx->comp_data->func_types[type_idx]; + + param_cell_num = func_type->param_cell_num; + result_count = func_type->result_count; + wasm_ret_types = func_type->types + func_type->param_count; + + POP_I32(elem_idx); + + /* Initialize parameter types of the LLVM function */ + param_count = (int32)func_type->param_count; + total_size = sizeof(LLVMTypeRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("Allocate memory failed."); + goto fail; + } + + for (i = 0; i < param_count; i++) + param_types[i] = TO_LLVM_TYPE(func_type->types[i]); + + /* Allocate memory for parameters */ + total_size = sizeof(LLVMValueRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("Allocate memory failed."); + goto fail; + } + + /* Pop parameters from stack */ + for (i = param_count - 1; i >= 0; i--) + POP(param_values[i], func_type->types[i]); + + /* Allocate memory for result values */ + total_size = sizeof(LLVMValueRef) * (uint64)result_count; + if (total_size >= UINT32_MAX + || !(value_rets = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("Allocate memory failed."); + goto fail; + } + memset(value_rets, 0, total_size); + + if (!call_aot_call_indirect_func(comp_ctx, func_ctx, + func_type, ftype_idx, elem_idx, + param_types, param_values, + param_count, param_cell_num, + result_count, wasm_ret_types, + value_rets, &res)) + goto fail; + + for (i = 0; i < func_type->result_count; i++) + PUSH(value_rets[i], func_type->types[func_type->param_count + i]); + + /* Check whether there was exception thrown when executing the function */ + if (!check_call_return(comp_ctx, func_ctx, res)) + goto fail; + + ret = true; + +fail: + if (value_rets) + wasm_runtime_free(value_rets); + if (param_values) + wasm_runtime_free(param_values); + if (param_types) + wasm_runtime_free(param_types); + return ret; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_function.h b/wamr/core/iwasm/compilation/aot_emit_function.h new file mode 100644 index 0000000..28d7bff --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_function.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_FUNCTION_H_ +#define _AOT_EMIT_FUNCTION_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 func_idx, uint8 **p_frame_ip); + +bool +aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 type_idx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_FUNCTION_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_memory.c b/wamr/core/iwasm/compilation/aot_emit_memory.c new file mode 100644 index 0000000..68ed1ef --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_memory.c @@ -0,0 +1,1292 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_memory.h" +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" + +#define BUILD_ICMP(op, left, right, res, name) do { \ + if (!(res = LLVMBuildICmp(comp_ctx->builder, op, \ + left, right, name))) { \ + aot_set_last_error("llvm build icmp failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_OP(Op, left, right, res, name) do { \ + if (!(res = LLVMBuild##Op(comp_ctx->builder, \ + left, right, name))) { \ + aot_set_last_error("llvm build " #Op " fail."); \ + goto fail; \ + } \ + } while (0) + +#define ADD_BASIC_BLOCK(block, name) do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, \ + name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + } while (0) + +#define SET_BUILD_POS(block) \ + LLVMPositionBuilderAtEnd(comp_ctx->builder, block) + +static LLVMValueRef +get_memory_check_bound(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 bytes) +{ + LLVMValueRef mem_check_bound = NULL; + switch (bytes) { + case 1: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_1byte; + break; + case 2: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_2bytes; + break; + case 4: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_4bytes; + break; + case 8: + mem_check_bound = func_ctx->mem_info[0].mem_bound_check_8bytes; + break; + default: + bh_assert(0); + return NULL; + } + + if (func_ctx->mem_space_unchanged) + return mem_check_bound; + + if (!(mem_check_bound = LLVMBuildLoad(comp_ctx->builder, + mem_check_bound, + "mem_check_bound"))) { + aot_set_last_error("llvm build load failed."); + return NULL; + } + return mem_check_bound; +} + +static LLVMValueRef +get_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +static LLVMValueRef +check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 offset, uint32 bytes) +{ + LLVMValueRef offset_const = I32_CONST(offset); + LLVMValueRef addr, maddr, offset1, cmp1, cmp2, cmp; + LLVMValueRef mem_base_addr, mem_check_bound; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_succ; + AOTValue *aot_value; + bool is_target_64bit; +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = + comp_ctx->comp_data->memories[0].memory_flags & 0x02; +#endif + + is_target_64bit = (comp_ctx->pointer_size == sizeof(uint64)) + ? true : false; + + CHECK_LLVM_CONST(offset_const); + + /* Get memory base address and memory data size */ + if (func_ctx->mem_space_unchanged +#if WASM_ENABLE_SHARED_MEMORY != 0 + || is_shared_memory +#endif + ) { + mem_base_addr = func_ctx->mem_info[0].mem_base_addr; + } + else { + if (!(mem_base_addr = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_base_addr, + "mem_base"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + aot_value = func_ctx->block_stack.block_list_end->value_stack.value_list_end; + + POP_I32(addr); + + /* return addres directly if constant offset and inside memory space */ + if (LLVMIsConstant(addr)) { + uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(addr) + + (uint64)offset; + uint32 num_bytes_per_page = + comp_ctx->comp_data->memories[0].num_bytes_per_page; + uint32 init_page_count = + comp_ctx->comp_data->memories[0].mem_init_page_count; + uint64 mem_data_size = num_bytes_per_page * init_page_count; + + if (mem_offset + bytes <= mem_data_size) { + /* inside memory space */ + offset1 = I32_CONST((uint32)mem_offset); + CHECK_LLVM_CONST(offset1); + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr, + &offset1, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; + } + } + + if (is_target_64bit) { + if (!(offset_const = LLVMBuildZExt(comp_ctx->builder, offset_const, + I64_TYPE, "offset_i64")) + || !(addr = LLVMBuildZExt(comp_ctx->builder, addr, + I64_TYPE, "addr_i64"))) { + aot_set_last_error("llvm build zero extend failed."); + goto fail; + } + } + + /* offset1 = offset + addr; */ + BUILD_OP(Add, offset_const, addr, offset1, "offset1"); + + if (comp_ctx->enable_bound_check + && !(aot_value->is_local + && aot_checked_addr_list_find(func_ctx, aot_value->local_idx, + offset, bytes))) { + uint32 init_page_count = + comp_ctx->comp_data->memories[0].mem_init_page_count; + if (init_page_count == 0) { + LLVMValueRef mem_size; + + if (!(mem_size = get_memory_size(comp_ctx, func_ctx))) { + goto fail; + } + BUILD_ICMP(LLVMIntEQ, mem_size, I32_ZERO, cmp, "is_zero"); + ADD_BASIC_BLOCK(check_succ, "check_mem_size_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + true, cmp, check_succ)) { + goto fail; + } + + SET_BUILD_POS(check_succ); + block_curr = check_succ; + } + + if (!(mem_check_bound = + get_memory_check_bound(comp_ctx, func_ctx, bytes))) { + goto fail; + } + + if (is_target_64bit) { + BUILD_ICMP(LLVMIntUGT, offset1, mem_check_bound, cmp, "cmp"); + } + else { + /* Check integer overflow */ + BUILD_ICMP(LLVMIntULT, offset1, addr, cmp1, "cmp1"); + BUILD_ICMP(LLVMIntUGT, offset1, mem_check_bound, cmp2, "cmp2"); + BUILD_OP(Or, cmp1, cmp2, cmp, "cmp"); + } + + /* Add basic blocks */ + ADD_BASIC_BLOCK(check_succ, "check_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + true, cmp, check_succ)) { + goto fail; + } + + SET_BUILD_POS(check_succ); + + if (aot_value->is_local) { + if (!aot_checked_addr_list_add(func_ctx, aot_value->local_idx, + offset, bytes)) + goto fail; + } + } + + /* maddr = mem_base_addr + offset1 */ + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr, + &offset1, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; +fail: + return NULL; +} + +#define BUILD_PTR_CAST(ptr_type) do { \ + if (!(maddr = LLVMBuildBitCast(comp_ctx->builder, maddr,\ + ptr_type, "data_ptr"))) {\ + aot_set_last_error("llvm build bit cast failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_LOAD() do { \ + if (!(value = LLVMBuildLoad(comp_ctx->builder, maddr, \ + "data"))) { \ + aot_set_last_error("llvm build load failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(value, 1); \ + } while (0) + +#define BUILD_TRUNC(value, data_type) do { \ + if (!(value = LLVMBuildTrunc(comp_ctx->builder, value, \ + data_type, "val_trunc"))){ \ + aot_set_last_error("llvm build trunc failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_STORE() do { \ + LLVMValueRef res; \ + if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ + aot_set_last_error("llvm build store failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(res, 1); \ + } while (0) + +#define BUILD_SIGN_EXT(dst_type) do { \ + if (!(value = LLVMBuildSExt(comp_ctx->builder, value, \ + dst_type, "data_s_ext"))) { \ + aot_set_last_error("llvm build sign ext failed."); \ + goto fail; \ + } \ + } while (0) + +#define BUILD_ZERO_EXT(dst_type) do { \ + if (!(value = LLVMBuildZExt(comp_ctx->builder, value, \ + dst_type, "data_z_ext"))) { \ + aot_set_last_error("llvm build zero ext failed."); \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_SHARED_MEMORY != 0 +bool +check_memory_alignment(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef addr, uint32 align) +{ + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_align_succ; + LLVMValueRef align_mask = I32_CONST(((uint32)1 << align) - 1); + LLVMValueRef res; + + CHECK_LLVM_CONST(align_mask); + + /* Convert pointer to int */ + if (!(addr = LLVMBuildPtrToInt(comp_ctx->builder, addr, + I32_TYPE, "address"))) { + aot_set_last_error("llvm build ptr to int failed."); + goto fail; + } + + /* The memory address should be aligned */ + BUILD_OP(And, addr, align_mask, res, "and"); + BUILD_ICMP(LLVMIntNE, res, I32_ZERO, res, "cmp"); + + /* Add basic blocks */ + ADD_BASIC_BLOCK(check_align_succ, "check_align_succ"); + LLVMMoveBasicBlockAfter(check_align_succ, block_curr); + + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_UNALIGNED_ATOMIC, + true, res, check_align_succ)) { + goto fail; + } + + SET_BUILD_POS(check_align_succ); + + return true; +fail: + return false; +} + +#define BUILD_ATOMIC_LOAD(align) do { \ + if (!(check_memory_alignment(comp_ctx, func_ctx, maddr, align))) { \ + goto fail; \ + } \ + if (!(value = LLVMBuildLoad(comp_ctx->builder, maddr, \ + "data"))) { \ + aot_set_last_error("llvm build load failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(value, 1 << align); \ + LLVMSetVolatile(value, true); \ + LLVMSetOrdering(value, LLVMAtomicOrderingSequentiallyConsistent); \ + } while (0) + +#define BUILD_ATOMIC_STORE(align) do { \ + LLVMValueRef res; \ + if (!(check_memory_alignment(comp_ctx, func_ctx, maddr, align))) { \ + goto fail; \ + } \ + if (!(res = LLVMBuildStore(comp_ctx->builder, value, maddr))) { \ + aot_set_last_error("llvm build store failed."); \ + goto fail; \ + } \ + LLVMSetAlignment(res, 1 << align); \ + LLVMSetVolatile(res, true); \ + LLVMSetOrdering(res, LLVMAtomicOrderingSequentiallyConsistent); \ + } while (0) +#endif + +bool +aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, + bool sign, bool atomic) +{ + LLVMValueRef maddr, value = NULL; + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + switch (bytes) { + case 4: + BUILD_PTR_CAST(INT32_PTR_TYPE); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_LOAD(align); + else +#endif + BUILD_LOAD(); + break; + case 2: + case 1: + if (bytes == 2) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + BUILD_ATOMIC_LOAD(align); + BUILD_ZERO_EXT(I32_TYPE); + } + else +#endif + { + BUILD_LOAD(); + if (sign) + BUILD_SIGN_EXT(I32_TYPE); + else + BUILD_ZERO_EXT(I32_TYPE); + } + break; + default: + bh_assert(0); + break; + } + + PUSH_I32(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, + bool sign, bool atomic) +{ + LLVMValueRef maddr, value = NULL; + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + switch (bytes) { + case 8: + BUILD_PTR_CAST(INT64_PTR_TYPE); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_LOAD(align); + else +#endif + BUILD_LOAD(); + break; + case 4: + case 2: + case 1: + if (bytes == 4) + BUILD_PTR_CAST(INT32_PTR_TYPE); + else if (bytes == 2) + BUILD_PTR_CAST(INT16_PTR_TYPE); + else + BUILD_PTR_CAST(INT8_PTR_TYPE); +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) { + BUILD_ATOMIC_LOAD(align); + BUILD_ZERO_EXT(I64_TYPE); + } + else +#endif + { + BUILD_LOAD(); + if (sign) + BUILD_SIGN_EXT(I64_TYPE); + else + BUILD_ZERO_EXT(I64_TYPE); + } + break; + default: + bh_assert(0); + break; + } + + PUSH_I64(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset) +{ + LLVMValueRef maddr, value; + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 4))) + return false; + + BUILD_PTR_CAST(F32_PTR_TYPE); + BUILD_LOAD(); + PUSH_F32(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset) +{ + LLVMValueRef maddr, value; + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 8))) + return false; + + BUILD_PTR_CAST(F64_PTR_TYPE); + BUILD_LOAD(); + PUSH_F64(value); + return true; +fail: + return false; +} + +bool +aot_compile_op_i32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, bool atomic) +{ + LLVMValueRef maddr, value; + + POP_I32(value); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + switch (bytes) { + case 4: + BUILD_PTR_CAST(INT32_PTR_TYPE); + break; + case 2: + BUILD_PTR_CAST(INT16_PTR_TYPE); + BUILD_TRUNC(value, INT16_TYPE); + break; + case 1: + BUILD_PTR_CAST(INT8_PTR_TYPE); + BUILD_TRUNC(value, INT8_TYPE); + break; + default: + bh_assert(0); + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_STORE(align); + else +#endif + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_i64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, bool atomic) +{ + LLVMValueRef maddr, value; + + POP_I64(value); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + switch (bytes) { + case 8: + BUILD_PTR_CAST(INT64_PTR_TYPE); + break; + case 4: + BUILD_PTR_CAST(INT32_PTR_TYPE); + BUILD_TRUNC(value, I32_TYPE); + break; + case 2: + BUILD_PTR_CAST(INT16_PTR_TYPE); + BUILD_TRUNC(value, INT16_TYPE); + break; + case 1: + BUILD_PTR_CAST(INT8_PTR_TYPE); + BUILD_TRUNC(value, INT8_TYPE); + break; + default: + bh_assert(0); + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (atomic) + BUILD_ATOMIC_STORE(align); + else +#endif + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_f32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset) +{ + LLVMValueRef maddr, value; + + POP_F32(value); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 4))) + return false; + + BUILD_PTR_CAST(F32_PTR_TYPE); + BUILD_STORE(); + return true; +fail: + return false; +} + +bool +aot_compile_op_f64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset) +{ + LLVMValueRef maddr, value; + + POP_F64(value); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, 8))) + return false; + + BUILD_PTR_CAST(F64_PTR_TYPE); + BUILD_STORE(); + return true; +fail: + return false; +} + +static LLVMValueRef +get_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size; + + if (func_ctx->mem_space_unchanged) { + mem_size = func_ctx->mem_info[0].mem_cur_page_count_addr; + } + else { + if (!(mem_size = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_cur_page_count_addr, + "mem_size"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + return mem_size; +fail: + return NULL; +} + +bool +aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size = get_memory_size(comp_ctx, func_ctx); + + if (mem_size) + PUSH_I32(mem_size); + return mem_size ? true : false; +fail: + return false; +} + +bool +aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef mem_size = get_memory_size(comp_ctx, func_ctx); + LLVMValueRef delta, param_values[2], ret_value, func, value; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + if (!mem_size) + return false; + + POP_I32(delta); + + /* Function type of wasm_runtime_enlarge_memory() */ + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + if (!(func_type = LLVMFunctionType(ret_type, param_types, 2, false))) { + aot_set_last_error("llvm add function type failed."); + return false; + } + + if (comp_ctx->is_jit_mode) { + /* JIT mode, call the function directly */ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { + aot_set_last_error("llvm add pointer type failed."); + return false; + } + if (!(value = I64_CONST((uint64)(uintptr_t)wasm_runtime_enlarge_memory)) + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { + aot_set_last_error("create LLVM value failed."); + return false; + } + } + else { + char *func_name = "wasm_runtime_enlarge_memory"; + /* AOT mode, delcare the function */ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) + && !(func = LLVMAddFunction(comp_ctx->module, + func_name, func_type))) { + aot_set_last_error("llvm add function failed."); + return false; + } + } + + /* Call function wasm_runtime_enlarge_memory() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = delta; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 2, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_grow_ret"); + + /* ret_value = ret_value == true ? delta : pre_page_count */ + if (!(ret_value = LLVMBuildSelect(comp_ctx->builder, ret_value, + mem_size, I32_NEG_ONE, + "mem_grow_ret"))) { + aot_set_last_error("llvm build select failed."); + return false; + } + + PUSH_I32(ret_value); + return true; +fail: + return false; +} + +#define GET_AOT_FUNCTION(name, argc) do { \ + if (!(func_type = LLVMFunctionType(ret_type, param_types, \ + argc, false))) { \ + aot_set_last_error("llvm add function type failed."); \ + return false; \ + } \ + if (comp_ctx->is_jit_mode) { \ + /* JIT mode, call the function directly */ \ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ + aot_set_last_error("llvm add pointer type failed."); \ + return false; \ + } \ + if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ + aot_set_last_error("create LLVM value failed."); \ + return false; \ + } \ + } \ + else { \ + char *func_name = #name; \ + /* AOT mode, delcare the function */ \ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \ + && !(func = LLVMAddFunction(comp_ctx->module, \ + func_name, func_type))) { \ + aot_set_last_error("llvm add function failed."); \ + return false; \ + } \ + } \ + } while (0) + +#if WASM_ENABLE_BULK_MEMORY != 0 + +static LLVMValueRef +check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef offset, LLVMValueRef bytes) +{ + LLVMValueRef maddr, max_addr, cmp; + LLVMValueRef mem_base_addr; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef check_succ; + LLVMValueRef mem_size; + + /* Get memory base address and memory data size */ +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = + comp_ctx->comp_data->memories[0].memory_flags & 0x02; + + if (func_ctx->mem_space_unchanged || is_shared_memory) { +#else + if (func_ctx->mem_space_unchanged) { +#endif + mem_base_addr = func_ctx->mem_info[0].mem_base_addr; + } + else { + if (!(mem_base_addr = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_base_addr, + "mem_base"))) { + aot_set_last_error("llvm build load failed."); + goto fail; + } + } + + /* return addres directly if constant offset and inside memory space */ + if (LLVMIsConstant(offset) && LLVMIsConstant(bytes)) { + uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(offset); + uint64 mem_len = (uint64)LLVMConstIntGetZExtValue(bytes); + uint32 num_bytes_per_page = + comp_ctx->comp_data->memories[0].num_bytes_per_page; + uint32 init_page_count = + comp_ctx->comp_data->memories[0].mem_init_page_count; + uint32 mem_data_size = num_bytes_per_page * init_page_count; + if (mem_data_size > 0 + && mem_offset + mem_len <= mem_data_size) { + /* inside memory space */ + /* maddr = mem_base_addr + moffset */ + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, + mem_base_addr, + &offset, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; + } + } + + /* mem_size_offset = aot_inst + off */ + if (!(mem_size = get_memory_size(comp_ctx, func_ctx))) { + goto fail; + } + + ADD_BASIC_BLOCK(check_succ, "check_succ"); + LLVMMoveBasicBlockAfter(check_succ, block_curr); + + offset = LLVMBuildZExt(comp_ctx->builder, offset, I64_TYPE, "extend_offset"); + bytes = LLVMBuildZExt(comp_ctx->builder, bytes, I64_TYPE, "extend_len"); + mem_size = LLVMBuildZExt(comp_ctx->builder, mem_size, I64_TYPE, "extend_size"); + + BUILD_OP(Add, offset, bytes, max_addr, "max_addr"); + BUILD_ICMP(LLVMIntUGT, max_addr, mem_size, cmp, + "cmp_max_mem_addr"); + if (!aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS, + true, cmp, check_succ)) { + goto fail; + } + + /* maddr = mem_base_addr + offset */ + if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr, + &offset, 1, "maddr"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + return maddr; +fail: + return NULL; +} + +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, offset, dst, len, param_values[5], ret_value, func, value; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + LLVMBasicBlockRef mem_init_fail, init_success; + + seg = I32_CONST(seg_index); + + POP_I32(len); + POP_I32(offset); + POP_I32(dst); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(aot_memory_init, 5); + + /* Call function aot_memory_init() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + param_values[2] = offset; + param_values[3] = len; + param_values[4] = dst; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_init_ret"); + + ADD_BASIC_BLOCK(mem_init_fail, "mem_init_fail"); + ADD_BASIC_BLOCK(init_success, "init_success"); + + LLVMMoveBasicBlockAfter(mem_init_fail, block_curr); + LLVMMoveBasicBlockAfter(init_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, ret_value, + init_success, mem_init_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* If memory.init failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail); + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, init_success); + + return true; +fail: + return false; +} + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index) +{ + LLVMValueRef seg, param_values[2], ret_value, func, value; + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + + seg = I32_CONST(seg_index); + CHECK_LLVM_CONST(seg); + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = INT8_TYPE; + + GET_AOT_FUNCTION(aot_data_drop, 2); + + /* Call function aot_data_drop() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = seg; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 2, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef src, dst, src_addr, dst_addr, len, res; + + POP_I32(len); + POP_I32(src); + POP_I32(dst); + + if (!(src_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, src, len))) + return false; + + if (!(dst_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) + return false; + + if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1, + src_addr, 1, len))) { + aot_set_last_error("llvm build memmove failed."); + return false; + } + return true; +fail: + return false; +} + +bool +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef val, dst, dst_addr, len, res; + + POP_I32(len); + POP_I32(val); + POP_I32(dst); + + if (!(dst_addr = + check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len))) + return false; + + val = LLVMBuildIntCast2(comp_ctx->builder, val, INT8_TYPE, true, "mem_set_value"); + + if (!(res = LLVMBuildMemSet(comp_ctx->builder, dst_addr, + val, len, 1))) { + aot_set_last_error("llvm build memset failed."); + return false; + } + return true; +fail: + return false; +} +#endif /* end of WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_SHARED_MEMORY != 0 +bool +aot_compile_op_atomic_rmw(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 atomic_op, uint8 op_type, + uint32 align, uint32 offset, + uint32 bytes) +{ + LLVMValueRef maddr, value, result; + + if (op_type == VALUE_TYPE_I32) + POP_I32(value); + else + POP_I64(value); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; + + switch (bytes) { + case 8: + BUILD_PTR_CAST(INT64_PTR_TYPE); + break; + case 4: + BUILD_PTR_CAST(INT32_PTR_TYPE); + if (op_type == VALUE_TYPE_I64) + BUILD_TRUNC(value, I32_TYPE); + break; + case 2: + BUILD_PTR_CAST(INT16_PTR_TYPE); + BUILD_TRUNC(value, INT16_TYPE); + break; + case 1: + BUILD_PTR_CAST(INT8_PTR_TYPE); + BUILD_TRUNC(value, INT8_TYPE); + break; + default: + bh_assert(0); + break; + } + + if (!(result = + LLVMBuildAtomicRMW(comp_ctx->builder, + atomic_op, maddr, value, + LLVMAtomicOrderingSequentiallyConsistent, false))) { + goto fail; + } + + LLVMSetVolatile(result, true); + + if (op_type == VALUE_TYPE_I32) { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, + I32_TYPE, "result_i32"))) { + goto fail; + } + PUSH_I32(result); + } + else { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, + I64_TYPE, "result_i64"))) { + goto fail; + } + PUSH_I64(result); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_atomic_cmpxchg(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, + uint32 offset, uint32 bytes) +{ + LLVMValueRef maddr, value, expect, result; + + if (op_type == VALUE_TYPE_I32) { + POP_I32(value); + POP_I32(expect); + } + else { + POP_I64(value); + POP_I64(expect); + } + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; + + switch (bytes) { + case 8: + BUILD_PTR_CAST(INT64_PTR_TYPE); + break; + case 4: + BUILD_PTR_CAST(INT32_PTR_TYPE); + if (op_type == VALUE_TYPE_I64) { + BUILD_TRUNC(value, I32_TYPE); + BUILD_TRUNC(expect, I32_TYPE); + } + break; + case 2: + BUILD_PTR_CAST(INT16_PTR_TYPE); + BUILD_TRUNC(value, INT16_TYPE); + BUILD_TRUNC(expect, INT16_TYPE); + break; + case 1: + BUILD_PTR_CAST(INT8_PTR_TYPE); + BUILD_TRUNC(value, INT8_TYPE); + BUILD_TRUNC(expect, INT8_TYPE); + break; + default: + bh_assert(0); + break; + } + + if (!(result = + LLVMBuildAtomicCmpXchg(comp_ctx->builder, maddr, expect, value, + LLVMAtomicOrderingSequentiallyConsistent, + LLVMAtomicOrderingSequentiallyConsistent, + false))) { + goto fail; + } + + LLVMSetVolatile(result, true); + + /* CmpXchg return {i32, i1} structure, + we need to extrack the previous_value from the structure */ + if (!(result = + LLVMBuildExtractValue(comp_ctx->builder, + result, 0, "previous_value"))) { + goto fail; + } + + if (op_type == VALUE_TYPE_I32) { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, + I32_TYPE, "result_i32"))) { + goto fail; + } + PUSH_I32(result); + } + else { + if (!(result = LLVMBuildZExt(comp_ctx->builder, result, + I64_TYPE, "result_i64"))) { + goto fail; + } + PUSH_I64(result); + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, + uint32 offset, uint32 bytes) +{ + LLVMValueRef maddr, value, timeout, expect, cmp; + LLVMValueRef param_values[5], ret_value, func, is_wait64; + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + LLVMBasicBlockRef wait_fail, wait_success; + LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder); + AOTFuncType *aot_func_type = func_ctx->aot_func->func_type; + + POP_I64(timeout); + if (op_type == VALUE_TYPE_I32) { + POP_I32(expect); + is_wait64 = I8_CONST(false); + if (!(expect = + LLVMBuildZExt(comp_ctx->builder, expect, + I64_TYPE, "expect_i64"))) { + goto fail; + } + } + else { + POP_I64(expect); + is_wait64 = I8_CONST(true); + } + + CHECK_LLVM_CONST(is_wait64); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I64_TYPE; + param_types[3] = I64_TYPE; + param_types[4] = INT8_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_runtime_atomic_wait, 5); + + /* Call function wasm_runtime_atomic_wait() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = maddr; + param_values[2] = expect; + param_values[3] = timeout; + param_values[4] = is_wait64; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 5, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + BUILD_ICMP(LLVMIntSGT, ret_value, I32_ZERO, cmp, "atomic_wait_ret"); + + ADD_BASIC_BLOCK(wait_fail, "atomic_wait_fail"); + ADD_BASIC_BLOCK(wait_success, "wait_success"); + + LLVMMoveBasicBlockAfter(wait_fail, block_curr); + LLVMMoveBasicBlockAfter(wait_success, block_curr); + + if (!LLVMBuildCondBr(comp_ctx->builder, cmp, + wait_success, wait_fail)) { + aot_set_last_error("llvm build cond br failed."); + goto fail; + } + + /* If atomic wait failed, return this function + so the runtime can catch the exception */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, wait_fail); + if (aot_func_type->result_count) { + switch (aot_func_type->types[aot_func_type->param_count]) { + case VALUE_TYPE_I32: + LLVMBuildRet(comp_ctx->builder, I32_ZERO); + break; + case VALUE_TYPE_I64: + LLVMBuildRet(comp_ctx->builder, I64_ZERO); + break; + case VALUE_TYPE_F32: + LLVMBuildRet(comp_ctx->builder, F32_ZERO); + break; + case VALUE_TYPE_F64: + LLVMBuildRet(comp_ctx->builder, F64_ZERO); + break; + } + } + else { + LLVMBuildRetVoid(comp_ctx->builder); + } + + LLVMPositionBuilderAtEnd(comp_ctx->builder, wait_success); + + PUSH_I32(ret_value); + + return true; +fail: + return false; +} + +bool +aot_compiler_op_atomic_notify(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes) +{ + LLVMValueRef maddr, value, count; + LLVMValueRef param_values[3], ret_value, func; + LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type; + + POP_I32(count); + + if (!(maddr = check_memory_overflow(comp_ctx, func_ctx, offset, bytes))) + return false; + + if (!check_memory_alignment(comp_ctx, func_ctx, maddr, align)) + return false; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = INT8_PTR_TYPE; + param_types[2] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(wasm_runtime_atomic_notify, 3); + + /* Call function wasm_runtime_atomic_notify() */ + param_values[0] = func_ctx->aot_inst; + param_values[1] = maddr; + param_values[2] = count; + if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, + param_values, 3, "call"))) { + aot_set_last_error("llvm build call failed."); + return false; + } + + PUSH_I32(ret_value); + + return true; +fail: + return false; +} + +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ diff --git a/wamr/core/iwasm/compilation/aot_emit_memory.h b/wamr/core/iwasm/compilation/aot_emit_memory.h new file mode 100644 index 0000000..82465ae --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_memory.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_MEMORY_H_ +#define _AOT_EMIT_MEMORY_H_ + +#include "aot_compiler.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "wasm_shared_memory.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_i32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, + bool sign, bool atomic); + +bool +aot_compile_op_i64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, + bool sign, bool atomic); + +bool +aot_compile_op_f32_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset); + +bool +aot_compile_op_f64_load(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset); + +bool +aot_compile_op_i32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, bool atomic); + +bool +aot_compile_op_i64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes, bool atomic); + +bool +aot_compile_op_f32_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset); + +bool +aot_compile_op_f64_store(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 align, uint32 offset); + +bool +aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +#if WASM_ENABLE_BULK_MEMORY != 0 +bool +aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); + +bool +aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 seg_index); + +bool +aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 +bool +aot_compile_op_atomic_rmw(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 atomic_op, uint8 op_type, + uint32 align, uint32 offset, + uint32 bytes); + +bool +aot_compile_op_atomic_cmpxchg(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, + uint32 offset, uint32 bytes); + +bool +aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint8 op_type, uint32 align, + uint32 offset, uint32 bytes); + +bool +aot_compiler_op_atomic_notify(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 align, uint32 offset, uint32 bytes); +#endif + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_MEMORY_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_numberic.c b/wamr/core/iwasm/compilation/aot_emit_numberic.c new file mode 100644 index 0000000..fc6a513 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_numberic.c @@ -0,0 +1,1270 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_numberic.h" +#include "aot_emit_exception.h" +#include "aot_emit_control.h" +#include "../aot/aot_runtime.h" + +#include + +#define LLVM_BUILD_ICMP(op, left, right, res, name) do { \ + if (!(res = LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \ + aot_set_last_error("llvm build "name" fail."); \ + return false; \ + } \ +} while (0) + +#define LLVM_BUILD_OP(Op, left, right, res, name, err_ret) do { \ + if (!(res = LLVMBuild##Op(comp_ctx->builder, left, right, name))) { \ + aot_set_last_error("llvm build " #name " fail."); \ + return err_ret; \ + } \ +} while (0) + +#define ADD_BASIC_BLOCK(block, name) do { \ + if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ + func_ctx->func, \ + name))) { \ + aot_set_last_error("llvm add basic block failed."); \ + goto fail; \ + } \ + \ + LLVMMoveBasicBlockAfter(block, LLVMGetInsertBlock(comp_ctx->builder)); \ +} while (0) + +#define IS_CONST_ZERO(val) \ + (LLVMIsConstant(val) \ + && ((is_i32 && (int32)LLVMConstIntGetZExtValue(val) == 0) \ + || (!is_i32 && (int64)LLVMConstIntGetSExtValue(val) == 0))) + +#define CHECK_INT_OVERFLOW(type) do { \ + LLVMValueRef cmp_min_int, cmp_neg_one; \ + LLVM_BUILD_ICMP(LLVMIntEQ, left, type##_MIN, \ + cmp_min_int, "cmp_min_int"); \ + LLVM_BUILD_ICMP(LLVMIntEQ, right, type##_NEG_ONE, \ + cmp_neg_one, "cmp_neg_one"); \ + LLVM_BUILD_OP(And, cmp_min_int, cmp_neg_one, \ + overflow, "overflow", false); \ +} while (0) + +#define PUSH_INT(v) do { \ + if (is_i32) \ + PUSH_I32(v); \ + else \ + PUSH_I64(v); \ +} while (0) + +#define POP_INT(v) do { \ + if (is_i32) \ + POP_I32(v); \ + else \ + POP_I64(v); \ +} while (0) + +#define PUSH_FLOAT(v) do { \ + if (is_f32) \ + PUSH_F32(v); \ + else \ + PUSH_F64(v); \ +} while (0) + +#define POP_FLOAT(v) do { \ + if (is_f32) \ + POP_F32(v); \ + else \ + POP_F64(v); \ +} while (0) + +#define DEF_INT_UNARY_OP(op, err) do { \ + LLVMValueRef res, operand; \ + POP_INT(operand); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_INT(res); \ +} while (0) + +#define DEF_INT_BINARY_OP(op, err) do { \ + LLVMValueRef res, left, right; \ + POP_INT(right); \ + POP_INT(left); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_INT(res); \ +} while (0) + +#define DEF_FP_UNARY_OP(op, err) do { \ + LLVMValueRef res, operand; \ + POP_FLOAT(operand); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_FLOAT(res); \ +} while (0) + +#define DEF_FP_BINARY_OP(op, err) do { \ + LLVMValueRef res, left, right; \ + POP_FLOAT(right); \ + POP_FLOAT(left); \ + if (!(res = op)) { \ + if (err) \ + aot_set_last_error(err); \ + return false; \ + } \ + PUSH_FLOAT(res); \ +} while (0) + +#define SHIFT_COUNT_MASK do { \ + /* LLVM has undefined behavior if shift count is greater than bits count \ + * while Webassembly spec requires the shift count be wrapped. */ \ + LLVMValueRef shift_count_mask, bits_minus_one; \ + bits_minus_one = is_i32 ? I32_31 : I64_63; \ + LLVM_BUILD_OP(And, right, bits_minus_one, \ + shift_count_mask, "shift_count_mask", NULL); \ + right = shift_count_mask; \ +} while (0) + + +static LLVMValueRef +__call_llvm_intrinsic(AOTCompContext *comp_ctx, + const char *name, + LLVMTypeRef ret_type, + LLVMTypeRef *param_types, + int param_count, + LLVMValueRef *param_values) +{ + LLVMValueRef func, ret; + LLVMTypeRef func_type; + + /* Declare llvm intrinsic function if necessary */ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, name))) { + if (!(func_type = + LLVMFunctionType(ret_type, param_types, (uint32)param_count, false))) { + aot_set_last_error("create LLVM function type failed."); + return NULL; + } + + if (!(func = LLVMAddFunction(comp_ctx->module, name, func_type))) { + aot_set_last_error("add LLVM function failed."); + return NULL; + } + } + + /* Call the LLVM intrinsic function */ + if (!(ret = LLVMBuildCall(comp_ctx->builder, func, param_values, + (uint32)param_count, "call"))) { + aot_set_last_error("llvm build call failed."); + return NULL; + } + + return ret; +} + +static LLVMValueRef +call_llvm_intrinsic(AOTCompContext *comp_ctx, + const char *name, + LLVMTypeRef ret_type, + LLVMTypeRef *param_types, + int param_count, + ...) +{ + LLVMValueRef *param_values, ret; + va_list argptr; + uint64 total_size; + int i = 0; + + /* Create param values */ + total_size = sizeof(LLVMValueRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory for param values failed."); + return false; + } + + /* Load each param value */ + va_start(argptr, param_count); + while (i < param_count) + param_values[i++] = va_arg(argptr, LLVMValueRef); + va_end(argptr); + + ret = __call_llvm_intrinsic(comp_ctx, name, ret_type, + param_types, param_count, + param_values); + + wasm_runtime_free(param_values); + + return ret; +} + +static LLVMValueRef +call_llvm_intrinsic_v(AOTCompContext *comp_ctx, + const char *name, + LLVMTypeRef ret_type, + LLVMTypeRef *param_types, + int param_count, + va_list param_value_list) +{ + LLVMValueRef *param_values, ret; + uint64 total_size; + int i = 0; + + /* Create param values */ + total_size = sizeof(LLVMValueRef) * (uint64)param_count; + if (total_size >= UINT32_MAX + || !(param_values = wasm_runtime_malloc((uint32)total_size))) { + aot_set_last_error("allocate memory for param values failed."); + return false; + } + + /* Load each param value */ + while (i < param_count) + param_values[i++] = va_arg(param_value_list, LLVMValueRef); + + ret = __call_llvm_intrinsic(comp_ctx, name, ret_type, + param_types, param_count, + param_values); + + wasm_runtime_free(param_values); + + return ret; +} + +/* Call llvm constrained floating-point intrinsic */ +static LLVMValueRef +call_llvm_float_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, + const char *intrinsic, + bool is_f32, + ...) +{ + va_list param_value_list; + LLVMValueRef ret; + LLVMTypeRef param_types[4], ret_type = is_f32 ? F32_TYPE : F64_TYPE; + + param_types[0] = param_types[1] = ret_type; + param_types[2] = param_types[3] = MD_TYPE; + + va_start(param_value_list, is_f32); + + ret = call_llvm_intrinsic_v(comp_ctx, + intrinsic, + ret_type, + param_types, + 4, + param_value_list); + + va_end(param_value_list); + + return ret; +} + +/* Call llvm constrained libm-equivalent intrinsic */ +static LLVMValueRef +call_llvm_libm_expermental_constrained_intrinsic(AOTCompContext *comp_ctx, + const char *intrinsic, + bool is_f32, + ...) +{ + va_list param_value_list; + LLVMValueRef ret; + LLVMTypeRef param_types[3], ret_type = is_f32 ? F32_TYPE : F64_TYPE; + + param_types[0] = ret_type; + param_types[1] = param_types[2] = MD_TYPE; + + va_start(param_value_list, is_f32); + + ret = call_llvm_intrinsic_v(comp_ctx, + intrinsic, + ret_type, + param_types, + 3, + param_value_list); + + va_end(param_value_list); + + return ret; +} + +static LLVMValueRef +compile_op_float_min_max(AOTCompContext *comp_ctx, + bool is_f32, + LLVMValueRef left, + LLVMValueRef right, + bool is_min) +{ + LLVMTypeRef param_types[2], ret_type = is_f32 ? F32_TYPE : F64_TYPE, + int_type = is_f32 ? I32_TYPE : I64_TYPE; + LLVMValueRef cmp, is_eq, is_nan, ret, left_int, right_int, tmp, + nan = LLVMConstRealOfString(ret_type, "NaN"); + char *intrinsic = is_min ? + (is_f32 ? "llvm.minnum.f32" : "llvm.minnum.f64") : + (is_f32 ? "llvm.maxnum.f32" : "llvm.maxnum.f64"); + + CHECK_LLVM_CONST(nan); + + param_types[0] = param_types[1] = ret_type; + + if (!(is_nan = LLVMBuildFCmp(comp_ctx->builder, LLVMRealUNO, + left, right, "is_nan")) + || !(is_eq = LLVMBuildFCmp(comp_ctx->builder, LLVMRealOEQ, + left, right, "is_eq"))) { + aot_set_last_error("llvm build fcmp fail."); + return NULL; + } + + /* If left and right are equal, they may be zero with different sign. + * Webassembly spec assert -0 < +0. So do a bitwise here. */ + if (!(left_int = LLVMBuildBitCast(comp_ctx->builder, left, + int_type, "left_int")) + || !(right_int = LLVMBuildBitCast(comp_ctx->builder, right, + int_type, "right_int"))) { + aot_set_last_error("llvm build bitcast fail."); + return NULL; + } + + if (is_min) + LLVM_BUILD_OP(Or, left_int, right_int, tmp, "tmp_int", NULL); + else + LLVM_BUILD_OP(And, left_int, right_int, tmp, "tmp_int", NULL); + + if (!(tmp = LLVMBuildBitCast(comp_ctx->builder, tmp, ret_type, "tmp"))) { + aot_set_last_error("llvm build bitcast fail."); + return NULL; + } + + if (!(cmp = call_llvm_intrinsic(comp_ctx, + intrinsic, + ret_type, + param_types, + 2, + left, + right))) + return NULL; + + if (!(cmp = LLVMBuildSelect(comp_ctx->builder, + is_eq, + tmp, + cmp, + "cmp"))) { + aot_set_last_error("llvm build select fail."); + return NULL; + } + + if (!(ret = LLVMBuildSelect(comp_ctx->builder, + is_nan, + nan, + cmp, + is_min ? "min" : "max"))) { + aot_set_last_error("llvm build select fail."); + return NULL; + } + + return ret; +fail: + return NULL; +} + +typedef enum BitCountType { + CLZ32 = 0, + CLZ64, + CTZ32, + CTZ64, + POP_CNT32, + POP_CNT64 +} BitCountType; + +static char *bit_cnt_llvm_intrinsic[] = { "llvm.ctlz.i32", + "llvm.ctlz.i64", + "llvm.cttz.i32", + "llvm.cttz.i64", + "llvm.ctpop.i32", + "llvm.ctpop.i64", + }; + +static bool +aot_compile_int_bit_count(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + BitCountType type, bool is_i32) +{ + LLVMValueRef zero_undef; + LLVMTypeRef ret_type, param_types[2]; + + param_types[0] = ret_type = is_i32 ? I32_TYPE : I64_TYPE; + param_types[1] = LLVMInt1TypeInContext(comp_ctx->context); + + zero_undef = LLVMConstInt(param_types[1], false, true); + CHECK_LLVM_CONST(zero_undef); + + /* Call the LLVM intrinsic function */ + if (type < POP_CNT32) + DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, + bit_cnt_llvm_intrinsic[type], + ret_type, + param_types, + 2, + operand, + zero_undef), + NULL); + else + DEF_INT_UNARY_OP(call_llvm_intrinsic(comp_ctx, + bit_cnt_llvm_intrinsic[type], + ret_type, + param_types, + 1, + operand), + NULL); + + return true; + +fail: + return false; +} + +static bool +compile_rems(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef left, LLVMValueRef right, + LLVMValueRef overflow_cond, bool is_i32) +{ + LLVMValueRef phi, no_overflow_value, zero = is_i32 ? I32_ZERO : I64_ZERO; + LLVMBasicBlockRef block_curr, no_overflow_block, rems_end_block; + + block_curr = LLVMGetInsertBlock(comp_ctx->builder); + + /* Add 2 blocks: no_overflow_block and rems_end block */ + ADD_BASIC_BLOCK(rems_end_block, "rems_end"); + ADD_BASIC_BLOCK(no_overflow_block, "rems_no_overflow"); + + /* Create condition br */ + if (!LLVMBuildCondBr(comp_ctx->builder, overflow_cond, + rems_end_block, no_overflow_block)) { + aot_set_last_error("llvm build cond br failed."); + return false; + } + + /* Translate no_overflow_block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, no_overflow_block); + + /* Calculate the rem value */ + LLVM_BUILD_OP(SRem, left, right, no_overflow_value, "rem_s", false); + + /* Jump to rems_end block */ + if (!LLVMBuildBr(comp_ctx->builder, rems_end_block)) { + aot_set_last_error("llvm build br failed."); + return false; + } + + /* Translate rems_end_block */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, rems_end_block); + + /* Create result phi */ + if (!(phi = LLVMBuildPhi(comp_ctx->builder, + is_i32 ? I32_TYPE : I64_TYPE, + "rems_result_phi"))) { + aot_set_last_error("llvm build phi failed."); + return false; + } + + /* Add phi incoming values */ + LLVMAddIncoming(phi, &no_overflow_value, &no_overflow_block, 1); + LLVMAddIncoming(phi, &zero, &block_curr, 1); + + if (is_i32) + PUSH_I32(phi); + else + PUSH_I64(phi); + + return true; + +fail: + return false; +} + +static bool +compile_int_div(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, bool is_i32, uint8 **p_frame_ip) +{ + LLVMValueRef left, right, cmp_div_zero, overflow, res; + LLVMBasicBlockRef check_div_zero_succ, check_overflow_succ; + + bh_assert(arith_op == INT_DIV_S + || arith_op == INT_DIV_U + || arith_op == INT_REM_S + || arith_op == INT_REM_U); + + POP_INT(right); + POP_INT(left); + + if (LLVMIsConstant(right)) { + int64 right_val = (int64)LLVMConstIntGetSExtValue(right); + switch (right_val) { + case 0: + /* Directly throw exception if divided by zero */ + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INTEGER_DIVIDE_BY_ZERO, + false, NULL, NULL))) + goto fail; + + return aot_handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip); + case 1: + if (arith_op == INT_DIV_S || arith_op == INT_DIV_U) + PUSH_INT(left); + else + PUSH_INT(is_i32 ? I32_ZERO : I64_ZERO); + return true; + case -1: + if (arith_op == INT_DIV_S) { + LLVM_BUILD_ICMP(LLVMIntEQ, left, + is_i32 ? I32_MIN : I64_MIN, + overflow, "overflow"); + ADD_BASIC_BLOCK(check_overflow_succ, + "check_overflow_success"); + + /* Throw conditional exception if overflow */ + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INTEGER_OVERFLOW, + true, overflow, + check_overflow_succ))) + goto fail; + + /* Push -(left) to stack */ + if (!(res = LLVMBuildNeg(comp_ctx->builder, left, "neg"))) { + aot_set_last_error("llvm build neg fail."); + return false; + } + PUSH_INT(res); + return true; + } + else if (arith_op == INT_REM_S) { + PUSH_INT(is_i32 ? I32_ZERO : I64_ZERO); + return true; + } + else { + /* fall to default */ + goto handle_default; + } +handle_default: + default: + /* Build div */ + switch (arith_op) { + case INT_DIV_S: + LLVM_BUILD_OP(SDiv, left, right, res, "div_s", false); + break; + case INT_DIV_U: + LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false); + break; + case INT_REM_S: + LLVM_BUILD_OP(SRem, left, right, res, "rem_s", false); + break; + case INT_REM_U: + LLVM_BUILD_OP(URem, left, right, res, "rem_u", false); + break; + default: + bh_assert(0); + return false; + } + + PUSH_INT(res); + return true; + } + } + else { + /* Check divied by zero */ + LLVM_BUILD_ICMP(LLVMIntEQ, right, is_i32 ? I32_ZERO : I64_ZERO, + cmp_div_zero, "cmp_div_zero"); + ADD_BASIC_BLOCK(check_div_zero_succ, "check_div_zero_success"); + + /* Throw conditional exception if divided by zero */ + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INTEGER_DIVIDE_BY_ZERO, true, + cmp_div_zero, check_div_zero_succ))) + goto fail; + + switch (arith_op) { + case INT_DIV_S: + /* Check integer overflow */ + if (is_i32) + CHECK_INT_OVERFLOW(I32); + else + CHECK_INT_OVERFLOW(I64); + + ADD_BASIC_BLOCK(check_overflow_succ, "check_overflow_success"); + + /* Throw conditional exception if integer overflow */ + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_INTEGER_OVERFLOW, + true, overflow, check_overflow_succ))) + goto fail; + + LLVM_BUILD_OP(SDiv, left, right, res, "div_s", false); + PUSH_INT(res); + return true; + case INT_DIV_U: + LLVM_BUILD_OP(UDiv, left, right, res, "div_u", false); + PUSH_INT(res); + return true; + case INT_REM_S: + /* Webassembly spec requires it return 0 */ + if (is_i32) + CHECK_INT_OVERFLOW(I32); + else + CHECK_INT_OVERFLOW(I64); + return compile_rems(comp_ctx, func_ctx, + left, right, overflow, + is_i32); + case INT_REM_U: + LLVM_BUILD_OP(URem, left, right, res, "rem_u", false); + PUSH_INT(res); + return true; + default: + bh_assert(0); + return false;; + } + } + +fail: + return false; +} + +static LLVMValueRef +compile_int_add(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + /* If one of the operands is 0, just return the other */ + if (IS_CONST_ZERO(left)) + return right; + if (IS_CONST_ZERO(right)) + return left; + + /* Build add */ + return LLVMBuildAdd(comp_ctx->builder, left, right, "add"); +} + +static LLVMValueRef +compile_int_sub(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + /* If the right operand is 0, just return the left */ + if (IS_CONST_ZERO(right)) + return left; + + /* Build sub */ + return LLVMBuildSub(comp_ctx->builder, left, right, "sub"); +} + +static LLVMValueRef +compile_int_mul(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + /* If one of the operands is 0, just return constant 0 */ + if (IS_CONST_ZERO(left) || IS_CONST_ZERO(right)) + return is_i32 ? I32_ZERO : I64_ZERO; + + /* Build mul */ + return LLVMBuildMul(comp_ctx->builder, left, right, "mul"); +} + +static bool +compile_op_int_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, bool is_i32, uint8 **p_frame_ip) +{ + switch (arith_op) { + case INT_ADD: + DEF_INT_BINARY_OP(compile_int_add(comp_ctx, left, right, is_i32), + "compile int add fail."); + return true; + case INT_SUB: + DEF_INT_BINARY_OP(compile_int_sub(comp_ctx, left, right, is_i32), + "compile int sub fail."); + return true; + case INT_MUL: + DEF_INT_BINARY_OP(compile_int_mul(comp_ctx, left, right, is_i32), + "compile int mul fail."); + return true; + case INT_DIV_S: + case INT_DIV_U: + case INT_REM_S: + case INT_REM_U: + return compile_int_div(comp_ctx, func_ctx, arith_op, is_i32, p_frame_ip); + default: + bh_assert(0); + return false; + } + +fail: + return false; +} + +static bool +compile_op_int_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntBitwise bitwise_op, bool is_i32) +{ + switch (bitwise_op) { + case INT_AND: + DEF_INT_BINARY_OP(LLVMBuildAnd(comp_ctx->builder, + left, right, "and"), + "llvm build and fail."); + return true; + case INT_OR: + DEF_INT_BINARY_OP(LLVMBuildOr(comp_ctx->builder, + left, right, "or"), + "llvm build or fail."); + return true; + case INT_XOR: + DEF_INT_BINARY_OP(LLVMBuildXor(comp_ctx->builder, + left, right, "xor"), + "llvm build xor fail."); + return true; + default: + bh_assert(0); + return false; + } + +fail: + return false; +} + +static LLVMValueRef +compile_int_shl(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + LLVMValueRef res; + + if (strcmp(comp_ctx->target_arch, "x86_64") != 0 + && strcmp(comp_ctx->target_arch, "i386") != 0) + SHIFT_COUNT_MASK; + + /* Build shl */ + LLVM_BUILD_OP(Shl, left, right, res, "shl", NULL); + + return res; +} + +static LLVMValueRef +compile_int_shr_s(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + LLVMValueRef res; + + if (strcmp(comp_ctx->target_arch, "x86_64") != 0 + && strcmp(comp_ctx->target_arch, "i386") != 0) + SHIFT_COUNT_MASK; + + /* Build shl */ + LLVM_BUILD_OP(AShr, left, right, res, "shr_s", NULL); + + return res; +} + +static LLVMValueRef +compile_int_shr_u(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_i32) +{ + LLVMValueRef res; + + if (strcmp(comp_ctx->target_arch, "x86_64") != 0 + && strcmp(comp_ctx->target_arch, "i386") != 0) + SHIFT_COUNT_MASK; + + /* Build shl */ + LLVM_BUILD_OP(LShr, left, right, res, "shr_u", NULL); + + return res; +} + +static LLVMValueRef +compile_int_rot(AOTCompContext *comp_ctx, + LLVMValueRef left, LLVMValueRef right, + bool is_rotl, + bool is_i32) +{ + LLVMValueRef bits_minus_shift_count, res, tmp_l, tmp_r; + char *name = is_rotl ? "rotl" : "rotr"; + + SHIFT_COUNT_MASK; + + /* Calculate (bits - shif_count) */ + LLVM_BUILD_OP(Sub, + is_i32 ? I32_32 : I64_64, + right, + bits_minus_shift_count, + "bits_minus_shift_count", + NULL); + + if (is_rotl) { + /* left<>(BITS-count) */ + LLVM_BUILD_OP(Shl, left, right, tmp_l, "tmp_l", NULL); + LLVM_BUILD_OP(LShr, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL); + } + else { + /* left>>count | left<<(BITS-count) */ + LLVM_BUILD_OP(LShr, left, right, tmp_l, "tmp_l", NULL); + LLVM_BUILD_OP(Shl, left, bits_minus_shift_count, tmp_r, "tmp_r", NULL); + } + + LLVM_BUILD_OP(Or, tmp_l, tmp_r, res, name, NULL); + + return res; +} + +static bool +compile_op_int_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op, bool is_i32) +{ + switch (shift_op) { + case INT_SHL: + DEF_INT_BINARY_OP(compile_int_shl(comp_ctx, left, right, is_i32), + NULL); + return true; + case INT_SHR_S: + DEF_INT_BINARY_OP(compile_int_shr_s(comp_ctx, left, right, is_i32), + NULL); + return true; + case INT_SHR_U: + DEF_INT_BINARY_OP(compile_int_shr_u(comp_ctx, left, right, is_i32), + NULL); + return true; + case INT_ROTL: + DEF_INT_BINARY_OP(compile_int_rot(comp_ctx, left, right, + true, is_i32), + NULL); + return true; + case INT_ROTR: + DEF_INT_BINARY_OP(compile_int_rot(comp_ctx, left, right, + false, is_i32), + NULL); + return true; + default: + bh_assert(0); + return false; + } + +fail: + return false; +} + +static bool +is_target_arm(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "arm", 3) || + !strncmp(comp_ctx->target_arch, "thumb", 5); +} + +static bool +is_target_x86(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "x86_64", 6) || + !strncmp(comp_ctx->target_arch, "i386", 4); +} + +static bool +is_target_xtensa(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "xtensa", 6); +} + +static bool +is_target_mips(AOTCompContext *comp_ctx) +{ + return !strncmp(comp_ctx->target_arch, "mips", 4); +} + +static bool +is_targeting_soft_float(AOTCompContext *comp_ctx, bool is_f32) +{ + bool ret = false; + char *feature_string; + + if (!(feature_string = + LLVMGetTargetMachineFeatureString(comp_ctx->target_machine))) { + aot_set_last_error("llvm get target machine feature string fail."); + return false; + } + + /* Note: + * LLVM CodeGen uses FPU Coprocessor registers by default, + * so user must specify '--cpu-features=+soft-float' to wamrc if the target + * doesn't have or enable FPU on arm, x86 or mips. */ + if (is_target_arm(comp_ctx) || + is_target_x86(comp_ctx) || + is_target_mips(comp_ctx)) + ret = strstr(feature_string, "+soft-float") ? true : false; + else if (is_target_xtensa(comp_ctx)) + /* Note: + * 1. The Floating-Point Coprocessor Option of xtensa only support + * single-precision floating-point operations, so must use soft-float + * for f64(i.e. double). + * 2. LLVM CodeGen uses Floating-Point Coprocessor registers by default, + * so user must specify '--cpu-features=-fp' to wamrc if the target + * doesn't have or enable Floating-Point Coprocessor Option on xtensa. */ + ret = (!is_f32 || strstr(feature_string, "-fp")) ? true : false; + else + ret = true; + + LLVMDisposeMessage(feature_string); + return ret; +} + +static bool +compile_op_float_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op, bool is_f32) +{ + switch (arith_op) { + case FLOAT_ADD: + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP(LLVMBuildFAdd(comp_ctx->builder, left, right, "fadd"), + "llvm build fadd fail."); + else + DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( + comp_ctx, + (is_f32 + ? "llvm.experimental.constrained.fadd.f32" + : "llvm.experimental.constrained.fadd.f64"), + is_f32, + left, + right, + comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); + return true; + case FLOAT_SUB: + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP(LLVMBuildFSub(comp_ctx->builder, left, right, "fsub"), + "llvm build fsub fail."); + else + DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( + comp_ctx, + (is_f32 + ? "llvm.experimental.constrained.fsub.f32" + : "llvm.experimental.constrained.fsub.f64"), + is_f32, + left, + right, + comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); + return true; + case FLOAT_MUL: + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP(LLVMBuildFMul(comp_ctx->builder, left, right, "fmul"), + "llvm build fmul fail."); + else + DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( + comp_ctx, + (is_f32 + ? "llvm.experimental.constrained.fmul.f32" + : "llvm.experimental.constrained.fmul.f64"), + is_f32, + left, + right, + comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); + return true; + case FLOAT_DIV: + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_BINARY_OP(LLVMBuildFDiv(comp_ctx->builder, left, right, "fdiv"), + "llvm build fdiv fail."); + else + DEF_FP_BINARY_OP(call_llvm_float_expermental_constrained_intrinsic( + comp_ctx, + (is_f32 + ? "llvm.experimental.constrained.fdiv.f32" + : "llvm.experimental.constrained.fdiv.f64"), + is_f32, + left, + right, + comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); + return true; + case FLOAT_MIN: + DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, + is_f32, + left, + right, + true), + NULL); + return true; + case FLOAT_MAX: + DEF_FP_BINARY_OP(compile_op_float_min_max(comp_ctx, + is_f32, + left, + right, + false), + NULL); + + return true; + default: + bh_assert(0); + return false; + } + +fail: + return false; +} + +static LLVMValueRef +call_llvm_float_math_intrinsic(AOTCompContext *comp_ctx, + const char *intrinsic, + bool is_f32, + ...) +{ + va_list param_value_list; + LLVMValueRef ret; + LLVMTypeRef param_type, ret_type = is_f32 ? F32_TYPE : F64_TYPE; + + param_type = ret_type; + + va_start(param_value_list, is_f32); + + ret = call_llvm_intrinsic_v(comp_ctx, + intrinsic, + ret_type, + ¶m_type, + 1, + param_value_list); + + va_end(param_value_list); + + return ret; +} + +static bool +compile_op_float_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatMath math_op, bool is_f32) +{ + switch (math_op) { + case FLOAT_ABS: + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.fabs.f32" : + "llvm.fabs.f64", + is_f32, + operand), + NULL); + return true; + case FLOAT_NEG: + DEF_FP_UNARY_OP(LLVMBuildFNeg(comp_ctx->builder, operand, "fneg"), + "llvm build fneg fail."); + return true; + + case FLOAT_CEIL: + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.ceil.f32" : + "llvm.ceil.f64", + is_f32, + operand), + NULL); + return true; + case FLOAT_FLOOR: + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.floor.f32" : + "llvm.floor.f64", + is_f32, + operand), + NULL); + return true; + case FLOAT_TRUNC: + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.trunc.f32" : + "llvm.trunc.f64", + is_f32, + operand), + NULL); + return true; + case FLOAT_NEAREST: + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.rint.f32" : + "llvm.rint.f64", + is_f32, + operand), + NULL); + return true; + case FLOAT_SQRT: + if (is_targeting_soft_float(comp_ctx, is_f32)) + DEF_FP_UNARY_OP(call_llvm_float_math_intrinsic(comp_ctx, + is_f32 ? "llvm.sqrt.f32" : + "llvm.sqrt.f64", + is_f32, + operand), + NULL); + else + DEF_FP_UNARY_OP(call_llvm_libm_expermental_constrained_intrinsic( + comp_ctx, + (is_f32 + ? "llvm.experimental.constrained.sqrt.f32" + : "llvm.experimental.constrained.sqrt.f64"), + is_f32, + operand, + comp_ctx->fp_rounding_mode, + comp_ctx->fp_exception_behavior), + NULL); + return true; + default: + bh_assert(0); + return false; + } + + return true; + +fail: + return false; +} + +static bool +compile_float_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_f32) +{ + LLVMTypeRef ret_type, param_types[2]; + + param_types[0] = param_types[1] = ret_type = is_f32 ? F32_TYPE : F64_TYPE; + + DEF_FP_BINARY_OP(call_llvm_intrinsic(comp_ctx, + is_f32 ? "llvm.copysign.f32" : + "llvm.copysign.f64", + ret_type, + param_types, + 2, + left, + right), + NULL); + return true; + +fail: + return false; +} + +bool +aot_compile_op_i32_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, CLZ32, true); +} + +bool +aot_compile_op_i32_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, CTZ32, true); +} + +bool +aot_compile_op_i32_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, POP_CNT32, true); +} + +bool +aot_compile_op_i64_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, CLZ64, false); +} + +bool +aot_compile_op_i64_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, CTZ64, false); +} + +bool +aot_compile_op_i64_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return aot_compile_int_bit_count(comp_ctx, func_ctx, POP_CNT64, false); +} + +bool +aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, uint8 **p_frame_ip) +{ + return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, true, p_frame_ip); +} + +bool +aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, uint8 **p_frame_ip) +{ + return compile_op_int_arithmetic(comp_ctx, func_ctx, arith_op, false, p_frame_ip); +} + +bool +aot_compile_op_i32_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntBitwise bitwise_op) +{ + return compile_op_int_bitwise(comp_ctx, func_ctx, bitwise_op, true); +} + +bool +aot_compile_op_i64_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntBitwise bitwise_op) +{ + return compile_op_int_bitwise(comp_ctx, func_ctx, bitwise_op, false); +} + +bool +aot_compile_op_i32_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return compile_op_int_shift(comp_ctx, func_ctx, shift_op, true); +} + +bool +aot_compile_op_i64_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op) +{ + return compile_op_int_shift(comp_ctx, func_ctx, shift_op, false); +} + +bool +aot_compile_op_f32_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatMath math_op) +{ + return compile_op_float_math(comp_ctx, func_ctx, math_op, true); +} + +bool +aot_compile_op_f64_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatMath math_op) +{ + return compile_op_float_math(comp_ctx, func_ctx, math_op, false); +} + +bool +aot_compile_op_f32_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + FloatArithmetic arith_op) +{ + return compile_op_float_arithmetic(comp_ctx, func_ctx, arith_op, true); +} + +bool +aot_compile_op_f64_arithmetic(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + FloatArithmetic arith_op) +{ + return compile_op_float_arithmetic(comp_ctx, func_ctx, arith_op, false); +} + +bool +aot_compile_op_f32_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return compile_float_copysign(comp_ctx, func_ctx, true); +} + +bool +aot_compile_op_f64_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + return compile_float_copysign(comp_ctx, func_ctx, false); +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_numberic.h b/wamr/core/iwasm/compilation/aot_emit_numberic.h new file mode 100644 index 0000000..c9dd307 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_numberic.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_NUMBERIC_H_ +#define _AOT_EMIT_NUMBERIC_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_i32_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i32_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i32_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i64_clz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i64_ctz(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i64_popcnt(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_i32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, uint8 **p_frame_ip); + +bool +aot_compile_op_i64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntArithmetic arith_op, uint8 **p_frame_ip); + +bool +aot_compile_op_i32_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntBitwise bitwise_op); + +bool +aot_compile_op_i64_bitwise(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntBitwise bitwise_op); + +bool +aot_compile_op_i32_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +bool +aot_compile_op_i64_shift(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + IntShift shift_op); + +bool +aot_compile_op_f32_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatMath math_op); + +bool +aot_compile_op_f64_math(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatMath math_op); + +bool +aot_compile_op_f32_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op); + +bool +aot_compile_op_f64_arithmetic(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + FloatArithmetic arith_op); + +bool +aot_compile_op_f32_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_f64_copysign(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_NUMBERIC_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_parametric.c b/wamr/core/iwasm/compilation/aot_emit_parametric.c new file mode 100644 index 0000000..dc81ad5 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_parametric.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_parametric.h" + +static bool +pop_value_from_wasm_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMValueRef *p_value, + bool is_32, uint8 *p_type) +{ + AOTValue *aot_value; + uint8 type; + + if (!func_ctx->block_stack.block_list_end) { + aot_set_last_error("WASM block stack underflow."); + return false; + } + if (!func_ctx->block_stack.block_list_end->value_stack.value_list_end) { + aot_set_last_error("WASM data stack underflow."); + return false; + } + + aot_value = aot_value_stack_pop + (&func_ctx->block_stack.block_list_end->value_stack); + type = aot_value->type; + + if (aot_value->type == VALUE_TYPE_I1) { + if (!(aot_value->value = + LLVMBuildZExt(comp_ctx->builder, aot_value->value, + I32_TYPE, "val_s_ext"))) { + aot_set_last_error("llvm build sign ext failed."); + return false; + } + type = aot_value->type = VALUE_TYPE_I32; + } + + if (p_type != NULL) { + *p_type = aot_value->type; + } + if (p_value != NULL) { + *p_value = aot_value->value; + } + + wasm_runtime_free(aot_value); + + if ((is_32 + && (type != VALUE_TYPE_I32 && type != VALUE_TYPE_F32)) + || (!is_32 + && (type != VALUE_TYPE_I64 && type != VALUE_TYPE_F64))) { + aot_set_last_error("invalid WASM stack data type."); + return false; + } + + return true; +} + + +bool +aot_compile_op_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_drop_32) +{ + if (!pop_value_from_wasm_stack(comp_ctx, func_ctx, NULL, is_drop_32, NULL)) + return false; + + return true; +} + +bool +aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_select_32) +{ + LLVMValueRef val1, val2, cond, selected; + uint8 val1_type, val2_type; + + POP_COND(cond); + + if (!pop_value_from_wasm_stack(comp_ctx, func_ctx, &val2, is_select_32, &val2_type) + || !pop_value_from_wasm_stack(comp_ctx, func_ctx, &val1, is_select_32, &val1_type)) + return false; + + if (val1_type != val2_type) { + aot_set_last_error("invalid stack values with different type"); + return false; + } + + if (!(selected = LLVMBuildSelect(comp_ctx->builder, + cond, val1, val2, + "select"))) { + aot_set_last_error("llvm build select failed."); + return false; + } + + PUSH(selected, val1_type); + + return true; + +fail: + return false; +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_parametric.h b/wamr/core/iwasm/compilation/aot_emit_parametric.h new file mode 100644 index 0000000..9587cb1 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_parametric.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_PARAMETRIC_H_ +#define _AOT_EMIT_PARAMETRIC_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_drop_32); + +bool +aot_compile_op_select(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + bool is_select_32); + + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_PARAMETRIC_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_emit_variable.c b/wamr/core/iwasm/compilation/aot_emit_variable.c new file mode 100644 index 0000000..f9e18a5 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_variable.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_variable.h" +#include "../aot/aot_runtime.h" + +#define CHECK_LOCAL(idx) do { \ + if (idx >= func_ctx->aot_func->func_type->param_count \ + + func_ctx->aot_func->local_count) { \ + aot_set_last_error("local index out of range"); \ + return false; \ + } \ + } while (0) + +static uint8 +get_local_type(AOTFuncContext *func_ctx, uint32 local_idx) +{ + AOTFunc *aot_func = func_ctx->aot_func; + uint32 param_count = aot_func->func_type->param_count; + return local_idx < param_count + ? aot_func->func_type->types[local_idx] + : aot_func->local_types[local_idx - param_count]; +} + +bool +aot_compile_op_get_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx) +{ + char name[32]; + LLVMValueRef value; + AOTValue *aot_value; + + CHECK_LOCAL(local_idx); + + snprintf(name, sizeof(name), "%s%d%s", "local", local_idx, "#"); + if (!(value = LLVMBuildLoad(comp_ctx->builder, + func_ctx->locals[local_idx], + name))) { + aot_set_last_error("llvm build load fail"); + return false; + } + + PUSH(value, get_local_type(func_ctx, local_idx)); + + aot_value = func_ctx->block_stack.block_list_end->value_stack.value_list_end; + aot_value->is_local = true; + aot_value->local_idx = local_idx; + return true; + +fail: + return false; +} + +bool +aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx) +{ + LLVMValueRef value; + + CHECK_LOCAL(local_idx); + + POP(value, get_local_type(func_ctx, local_idx)); + + if (!LLVMBuildStore(comp_ctx->builder, + value, + func_ctx->locals[local_idx])) { + aot_set_last_error("llvm build store fail"); + return false; + } + + aot_checked_addr_list_del(func_ctx, local_idx); + return true; + +fail: + return false; +} + +bool +aot_compile_op_tee_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx) +{ + LLVMValueRef value; + uint8 type; + + CHECK_LOCAL(local_idx); + + type = get_local_type(func_ctx, local_idx); + + POP(value, type); + + if (!LLVMBuildStore(comp_ctx->builder, + value, + func_ctx->locals[local_idx])) { + aot_set_last_error("llvm build store fail"); + return false; + } + + PUSH(value, type); + aot_checked_addr_list_del(func_ctx, local_idx); + return true; + +fail: + return false; +} + +static bool +compile_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 global_idx, bool is_set) +{ + AOTCompData *comp_data = comp_ctx->comp_data; + uint32 import_global_count = comp_data->import_global_count; + uint32 global_base_offset = + offsetof(AOTModuleInstance, global_table_data.bytes) + + sizeof(AOTMemoryInstance) * comp_ctx->comp_data->memory_count; + uint32 global_offset; + uint8 global_type; + LLVMValueRef offset, global_ptr, global; + LLVMTypeRef ptr_type = NULL; + + bh_assert(global_idx < import_global_count + comp_data->global_count); + + if (global_idx < import_global_count) { + global_offset = global_base_offset + + comp_data->import_globals[global_idx].data_offset; + global_type = comp_data->import_globals[global_idx].type; + } + else { + global_offset = global_base_offset + + comp_data->globals[global_idx - import_global_count].data_offset; + global_type = + comp_data->globals[global_idx - import_global_count].type; + } + + offset = I32_CONST(global_offset); + if (!(global_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "global_ptr_tmp"))) { + aot_set_last_error("llvm build in bounds gep failed."); + return false; + } + + switch (global_type) { + case VALUE_TYPE_I32: + ptr_type = comp_ctx->basic_types.int32_ptr_type; + break; + case VALUE_TYPE_I64: + ptr_type = comp_ctx->basic_types.int64_ptr_type; + break; + case VALUE_TYPE_F32: + ptr_type = comp_ctx->basic_types.float32_ptr_type; + break; + case VALUE_TYPE_F64: + ptr_type = comp_ctx->basic_types.float64_ptr_type; + break; + default: + bh_assert(0); + break; + } + + if (!(global_ptr = LLVMBuildBitCast(comp_ctx->builder, global_ptr, + ptr_type, "global_ptr"))) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + if (!is_set) { + if (!(global = LLVMBuildLoad(comp_ctx->builder, + global_ptr, "global"))) { + aot_set_last_error("llvm build load failed."); + return false; + } + PUSH(global, global_type); + } + else { + POP(global, global_type); + if (!LLVMBuildStore(comp_ctx->builder, global, global_ptr)) { + aot_set_last_error("llvm build store failed."); + return false; + } + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_get_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 global_idx) +{ + return compile_global(comp_ctx, func_ctx, global_idx, false); +} + +bool +aot_compile_op_set_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 global_idx) +{ + return compile_global(comp_ctx, func_ctx, global_idx, true); +} + diff --git a/wamr/core/iwasm/compilation/aot_emit_variable.h b/wamr/core/iwasm/compilation/aot_emit_variable.h new file mode 100644 index 0000000..de2b35f --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_emit_variable.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_VARIABLE_H_ +#define _AOT_EMIT_VARIABLE_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_get_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx); + +bool +aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx); + +bool +aot_compile_op_tee_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 local_idx); + +bool +aot_compile_op_get_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 global_idx); + +bool +aot_compile_op_set_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + uint32 global_idx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_EMIT_VARIABLE_H_ */ + diff --git a/wamr/core/iwasm/compilation/aot_llvm.c b/wamr/core/iwasm/compilation/aot_llvm.c new file mode 100644 index 0000000..cb024e5 --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_llvm.c @@ -0,0 +1,1547 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_llvm.h" +#include "aot_compiler.h" +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" + + +LLVMTypeRef +wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type) +{ + switch (wasm_type) { + case VALUE_TYPE_I32: + return llvm_types->int32_type; + case VALUE_TYPE_I64: + return llvm_types->int64_type; + case VALUE_TYPE_F32: + return llvm_types->float32_type; + case VALUE_TYPE_F64: + return llvm_types->float64_type; + case VALUE_TYPE_VOID: + return llvm_types->void_type; + } + return NULL; +} + +/** + * Add LLVM function + */ +static LLVMValueRef +aot_add_llvm_func(AOTCompContext *comp_ctx, AOTFuncType *aot_func_type, + uint32 func_index) +{ + LLVMValueRef func = NULL; + LLVMTypeRef *param_types, ret_type, func_type; + LLVMValueRef local_value; + char func_name[32]; + uint64 size; + uint32 i, j = 0, param_count = (uint64)aot_func_type->param_count; + + /* exec env as first parameter */ + param_count++; + + /* Extra wasm function results(except the first one)'s address are + * appended to aot function parameters. */ + if (aot_func_type->result_count > 1) + param_count += aot_func_type->result_count - 1; + + /* Initialize parameter types of the LLVM function */ + size = sizeof(LLVMTypeRef) * ((uint64)param_count); + if (size >= UINT32_MAX + || !(param_types = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + /* exec env as first parameter */ + param_types[j++] = comp_ctx->exec_env_type; + for (i = 0; i < aot_func_type->param_count; i++) + param_types[j++] = TO_LLVM_TYPE(aot_func_type->types[i]); + /* Extra results' address */ + for (i = 1; i < aot_func_type->result_count; i++, j++) { + param_types[j] = + TO_LLVM_TYPE(aot_func_type->types[aot_func_type->param_count + i]); + if (!(param_types[j] = LLVMPointerType(param_types[j], 0))) { + aot_set_last_error("llvm get pointer type failed."); + goto fail; + } + } + + /* Resolve return type of the LLVM function */ + if (aot_func_type->result_count) + ret_type = TO_LLVM_TYPE(aot_func_type->types[aot_func_type->param_count]); + else + ret_type = VOID_TYPE; + + /* Resolve function prototype */ + if (!(func_type = LLVMFunctionType(ret_type, param_types, + param_count, false))) { + aot_set_last_error("create LLVM function type failed."); + goto fail; + } + + /* Add LLVM function */ + snprintf(func_name, sizeof(func_name), "%s%d", AOT_FUNC_PREFIX, func_index); + if (!(func = LLVMAddFunction(comp_ctx->module, func_name, func_type))) { + aot_set_last_error("add LLVM function failed."); + goto fail; + } + + j = 0; + local_value = LLVMGetParam(func, j++); + LLVMSetValueName(local_value, "exec_env"); + + /* Set parameter names */ + for (i = 0; i < aot_func_type->param_count; i++) { + local_value = LLVMGetParam(func, j++); + LLVMSetValueName(local_value, ""); + } + +fail: + wasm_runtime_free(param_types); + return func; +} + +static void +free_block_memory(AOTBlock *block) +{ + if (block->param_types) + wasm_runtime_free(block->param_types); + if (block->result_types) + wasm_runtime_free(block->result_types); + wasm_runtime_free(block); +} + +/** + * Create first AOTBlock, or function block for the function + */ +static AOTBlock * +aot_create_func_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + AOTFunc *func, AOTFuncType *aot_func_type) +{ + AOTBlock *aot_block; + uint32 param_count = aot_func_type->param_count, + result_count = aot_func_type->result_count; + + /* Allocate memory */ + if (!(aot_block = wasm_runtime_malloc(sizeof(AOTBlock)))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + memset(aot_block, 0, sizeof(AOTBlock)); + if (param_count + && !(aot_block->param_types = wasm_runtime_malloc(param_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + if (result_count) { + if (!(aot_block->result_types = wasm_runtime_malloc(result_count))) { + aot_set_last_error("allocate memory failed."); + goto fail; + } + } + + /* Set block data */ + aot_block->label_type = LABEL_TYPE_FUNCTION; + aot_block->param_count = param_count; + memcpy(aot_block->param_types, aot_func_type->types, param_count); + aot_block->result_count = result_count; + memcpy(aot_block->result_types, aot_func_type->types + param_count, result_count); + aot_block->wasm_code_end = func->code + func->code_size; + + /* Add function entry block */ + if (!(aot_block->llvm_entry_block = + LLVMAppendBasicBlockInContext(comp_ctx->context, func_ctx->func, + "func_begin"))) { + aot_set_last_error("add LLVM basic block failed."); + goto fail; + } + + return aot_block; + +fail: + free_block_memory(aot_block); + return NULL; +} + +static bool +create_exception_blocks(AOTFuncContext *func_ctx) +{ + if (!(func_ctx->exception_blocks = + wasm_runtime_malloc(sizeof(LLVMBasicBlockRef) * EXCE_NUM))) { + aot_set_last_error("allocate memory failed."); + return false;; + } + + memset(func_ctx->exception_blocks, 0, + sizeof(LLVMBasicBlockRef) * EXCE_NUM); + return true; +} + +static bool +create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, + LLVMTypeRef int8_ptr_type, uint32 func_index) +{ + LLVMValueRef offset, mem_info_base; + uint32 memory_count; + WASMModule *module = comp_ctx->comp_data->wasm_module; + WASMFunction *func = module->functions[func_index]; + LLVMTypeRef bound_check_type; + bool mem_space_unchanged = (!func->has_op_memory_grow && !func->has_op_func_call) + || (!module->possible_memory_grow); +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory; +#endif + + func_ctx->mem_space_unchanged = mem_space_unchanged; + + memory_count = module->memory_count + module->import_memory_count; + /* If the module dosen't have memory, reserve + one mem_info space with empty content */ + if (memory_count == 0) + memory_count = 1; + + if (!(func_ctx->mem_info = + wasm_runtime_malloc(sizeof(AOTMemInfo) * memory_count))) { + return false; + } + memset(func_ctx->mem_info, 0, sizeof(AOTMemInfo)); + + /* Currently we only create memory info for memory 0 */ + /* Load memory base address */ +#if WASM_ENABLE_SHARED_MEMORY != 0 + is_shared_memory = comp_ctx->comp_data->memories[0].memory_flags & 0x02 + ? true : false; + if (is_shared_memory) { + LLVMValueRef shared_mem_addr; + offset = I32_CONST(offsetof(AOTModuleInstance, memories)); + if (!offset) { + aot_set_last_error("create llvm const failed."); + return false; + } + + /* aot_inst->memories */ + if (!(shared_mem_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "shared_mem_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildBitCast(comp_ctx->builder, + shared_mem_addr, int8_ptr_type, + "shared_mem_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + /* aot_inst->memories[0] */ + if (!(shared_mem_addr = + LLVMBuildLoad(comp_ctx->builder, + shared_mem_addr, "shared_mem_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildBitCast(comp_ctx->builder, + shared_mem_addr, int8_ptr_type, + "shared_mem_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (!(shared_mem_addr = + LLVMBuildLoad(comp_ctx->builder, + shared_mem_addr, "shared_mem_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + offset = I32_CONST(offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_base_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, shared_mem_addr, + &offset, 1, "mem_base_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + offset = I32_CONST(offsetof(AOTMemoryInstance, cur_page_count)); + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, shared_mem_addr, + &offset, 1, "mem_cur_page_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + else +#endif + { + offset = I32_CONST(offsetof(AOTModuleInstance, global_table_data) + + offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_base_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "mem_base_addr_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + offset = I32_CONST(offsetof(AOTModuleInstance, global_table_data) + + offsetof(AOTMemoryInstance, cur_page_count)); + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "mem_cur_page_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + } + /* Store mem info base address before cast */ + mem_info_base = func_ctx->mem_info[0].mem_base_addr; + + if (!(func_ctx->mem_info[0].mem_base_addr = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_base_addr, + int8_ptr_type, "mem_base_addr_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_cur_page_count_addr, + INT32_PTR_TYPE, "mem_cur_page_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_base_addr = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_base_addr, + "mem_base_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_cur_page_count_addr = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_cur_page_count_addr, + "mem_cur_page_count_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + else if (is_shared_memory) { + /* The base address for shared memory will never changed, + we can load the value here */ + if (!(func_ctx->mem_info[0].mem_base_addr = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_base_addr, + "mem_base_addr"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } +#endif + + bound_check_type = (comp_ctx->pointer_size == sizeof(uint64)) + ? INT64_PTR_TYPE : INT32_PTR_TYPE; + + /* Load memory bound check constants */ + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_1byte) + - offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = + LLVMBuildInBoundsGEP(comp_ctx->builder, mem_info_base, + &offset, 1, "bound_check_1byte_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_1byte, + bound_check_type, "bound_check_1byte_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_1byte = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_1byte, + "bound_check_1byte"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } + + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_2bytes) + - offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = + LLVMBuildInBoundsGEP(comp_ctx->builder, mem_info_base, + &offset, 1, "bound_check_2bytes_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_2bytes, + bound_check_type, "bound_check_2bytes_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_2bytes = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_2bytes, + "bound_check_2bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } + + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_4bytes) + - offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = + LLVMBuildInBoundsGEP(comp_ctx->builder, mem_info_base, + &offset, 1, "bound_check_4bytes_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_4bytes, + bound_check_type, "bound_check_4bytes_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_4bytes = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_4bytes, + "bound_check_4bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } + + offset = I32_CONST(offsetof(AOTMemoryInstance, mem_bound_check_8bytes) + - offsetof(AOTMemoryInstance, memory_data.ptr)); + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = + LLVMBuildInBoundsGEP(comp_ctx->builder, mem_info_base, + &offset, 1, "bound_check_8bytes_offset"))) { + aot_set_last_error("llvm build in bounds gep failed"); + return false; + } + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = + LLVMBuildBitCast(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_8bytes, + bound_check_type, "bound_check_8bytes_ptr"))) { + aot_set_last_error("llvm build bit cast failed"); + return false; + } + if (mem_space_unchanged) { + if (!(func_ctx->mem_info[0].mem_bound_check_8bytes = + LLVMBuildLoad(comp_ctx->builder, + func_ctx->mem_info[0].mem_bound_check_8bytes, + "bound_check_8bytes"))) { + aot_set_last_error("llvm build load failed"); + return false; + } + } + + return true; +} + +static bool +create_table_base(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef offset; + + offset = I32_CONST(offsetof(AOTModuleInstance, global_table_data.bytes) + + comp_ctx->comp_data->global_data_size); + func_ctx->table_base = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->aot_inst, + &offset, 1, + "table_base_tmp"); + if (!func_ctx->table_base) { + aot_set_last_error("llvm build in bounds gep failed."); + return false; + } + func_ctx->table_base = LLVMBuildBitCast(comp_ctx->builder, func_ctx->table_base, + INT32_PTR_TYPE, "table_base"); + if (!func_ctx->table_base) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + return true; +} + +static bool +create_cur_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef offset; + + offset = I32_CONST(offsetof(AOTModuleInstance, cur_exception)); + func_ctx->cur_exception = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->aot_inst, + &offset, 1, + "cur_execption"); + if (!func_ctx->cur_exception) { + aot_set_last_error("llvm build in bounds gep failed."); + return false; + } + return true; +} + +static bool +create_func_type_indexes(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx) +{ + LLVMValueRef offset, func_type_indexes_ptr; + LLVMTypeRef int32_ptr_type; + + offset = I32_CONST(offsetof(AOTModuleInstance, func_type_indexes.ptr)); + func_type_indexes_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder, + func_ctx->aot_inst, + &offset, 1, + "func_type_indexes_ptr"); + if (!func_type_indexes_ptr) { + aot_set_last_error("llvm build add failed."); + return false; + } + + if (!(int32_ptr_type = LLVMPointerType(INT32_PTR_TYPE, 0))) { + aot_set_last_error("llvm get pointer type failed."); + return false; + } + + func_ctx->func_type_indexes = LLVMBuildBitCast(comp_ctx->builder, + func_type_indexes_ptr, + int32_ptr_type, + "func_type_indexes_tmp"); + if (!func_ctx->func_type_indexes) { + aot_set_last_error("llvm build bit cast failed."); + return false; + } + + func_ctx->func_type_indexes = LLVMBuildLoad(comp_ctx->builder, + func_ctx->func_type_indexes, + "func_type_indexes"); + if (!func_ctx->func_type_indexes) { + aot_set_last_error("llvm build load failed."); + return false; + } + return true; +} + +/** + * Create function compiler context + */ +static AOTFuncContext * +aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, + AOTFunc *func, uint32 func_index) +{ + AOTFuncContext *func_ctx; + AOTFuncType *aot_func_type = comp_data->func_types[func->func_type_index]; + AOTBlock *aot_block; + 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; + LLVMValueRef stack_bound_offset = I32_FOUR, stack_bound_addr; + char local_name[32]; + uint64 size; + uint32 i, j = 0; + + /* Allocate memory for the function context */ + size = offsetof(AOTFuncContext, locals) + sizeof(LLVMValueRef) * + ((uint64)aot_func_type->param_count + func->local_count); + if (size >= UINT32_MAX + || !(func_ctx = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(func_ctx, 0, (uint32)size); + func_ctx->aot_func = func; + + /* Add LLVM function */ + if (!(func_ctx->func = aot_add_llvm_func(comp_ctx, aot_func_type, func_index))) + goto fail; + + /* Create function's first AOTBlock */ + if (!(aot_block = aot_create_func_block(comp_ctx, func_ctx, + func, aot_func_type))) + goto fail; + + aot_block_stack_push(&func_ctx->block_stack, aot_block); + + /* Add local variables */ + LLVMPositionBuilderAtEnd(comp_ctx->builder, aot_block->llvm_entry_block); + + /* Save the pameters for fast access */ + 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, 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"))) { + aot_set_last_error("llvm build in bounds gep failed"); + goto fail; + } + + /* Load aot inst */ + if (!(func_ctx->aot_inst = LLVMBuildLoad(comp_ctx->builder, + aot_inst_addr, "aot_inst"))) { + aot_set_last_error("llvm build load failed"); + 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; + } + + /* Get native stack boundary address */ + if (!(stack_bound_addr = + LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->exec_env, + &stack_bound_offset, 1, "stack_bound_addr"))) { + aot_set_last_error("llvm build in bounds gep failed"); + goto fail; + } + + if (!(func_ctx->native_stack_bound = + LLVMBuildLoad(comp_ctx->builder, + stack_bound_addr, "native_stack_bound"))) { + 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] = + LLVMBuildAlloca(comp_ctx->builder, + TO_LLVM_TYPE(aot_func_type->types[i]), + local_name); + if (!func_ctx->locals[i]) { + aot_set_last_error("llvm build alloca failed."); + goto fail; + } + if (!LLVMBuildStore(comp_ctx->builder, + LLVMGetParam(func_ctx->func, j), + func_ctx->locals[i])) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + } + + for (i = 0; i < func->local_count; i++) { + LLVMTypeRef local_type; + LLVMValueRef local_value = NULL; + snprintf(local_name, sizeof(local_name), "l%d", + aot_func_type->param_count + i); + local_type = TO_LLVM_TYPE(func->local_types[i]); + func_ctx->locals[aot_func_type->param_count + i] = + LLVMBuildAlloca(comp_ctx->builder, local_type, local_name); + if (!func_ctx->locals[aot_func_type->param_count + i]) { + aot_set_last_error("llvm build alloca failed."); + goto fail; + } + switch (func->local_types[i]) { + case VALUE_TYPE_I32: + local_value = I32_ZERO; + break; + case VALUE_TYPE_I64: + local_value = I64_ZERO; + break; + case VALUE_TYPE_F32: + local_value = F32_ZERO; + break; + case VALUE_TYPE_F64: + local_value = F64_ZERO; + break; + default: + bh_assert(0); + break; + } + if (!LLVMBuildStore(comp_ctx->builder, local_value, + func_ctx->locals[aot_func_type->param_count + i])) { + aot_set_last_error("llvm build store failed."); + goto fail; + } + } + + if (aot_func_type->param_count + func->local_count > 0) { + func_ctx->last_alloca = func_ctx->locals[aot_func_type->param_count + + func->local_count - 1]; + if (!(func_ctx->last_alloca = + LLVMBuildBitCast(comp_ctx->builder, func_ctx->last_alloca, + INT8_PTR_TYPE, "stack_ptr"))) { + aot_set_last_error("llvm build bit cast failed."); + goto fail; + } + } + else { + if (!(func_ctx->last_alloca = LLVMBuildAlloca(comp_ctx->builder, INT8_TYPE, + "stack_ptr"))) { + aot_set_last_error("llvm build alloca failed."); + goto fail; + } + } + + if (!(int8_ptr_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { + aot_set_last_error("llvm add pointer type failed."); + goto fail; + } + + /* Create exception blocks */ + if (!create_exception_blocks(func_ctx)) + goto fail; + + /* Create base addr, end addr, data size of mem, heap */ + if (!create_memory_info(comp_ctx, func_ctx, int8_ptr_type, func_index)) + goto fail; + + /* Load table base */ + if (!create_table_base(comp_ctx, func_ctx)) + goto fail; + + /* Load current exception */ + if (!create_cur_exception(comp_ctx, func_ctx)) + goto fail; + + /* Load function type indexes */ + if (!create_func_type_indexes(comp_ctx, func_ctx)) + goto fail; + + return func_ctx; + +fail: + if (func_ctx->mem_info) + wasm_runtime_free(func_ctx->mem_info); + if (func_ctx->exception_blocks) + wasm_runtime_free(func_ctx->exception_blocks); + aot_block_stack_destroy(&func_ctx->block_stack); + wasm_runtime_free(func_ctx); + return NULL; +} + +static void +aot_destroy_func_contexts(AOTFuncContext **func_ctxes, uint32 count) +{ + uint32 i; + + for (i = 0; i < count; i++) + if (func_ctxes[i]) { + if (func_ctxes[i]->mem_info) + wasm_runtime_free(func_ctxes[i]->mem_info); + if (func_ctxes[i]->exception_blocks) + wasm_runtime_free(func_ctxes[i]->exception_blocks); + aot_block_stack_destroy(&func_ctxes[i]->block_stack); + aot_checked_addr_list_destroy(func_ctxes[i]); + wasm_runtime_free(func_ctxes[i]); + } + wasm_runtime_free(func_ctxes); +} + +/** + * Create function compiler contexts + */ +static AOTFuncContext ** +aot_create_func_contexts(AOTCompData *comp_data, AOTCompContext *comp_ctx) +{ + AOTFuncContext **func_ctxes; + uint64 size; + uint32 i; + + /* Allocate memory */ + size = sizeof(AOTFuncContext*) * (uint64)comp_data->func_count; + if (size >= UINT32_MAX + || !(func_ctxes = wasm_runtime_malloc((uint32)size))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(func_ctxes, 0, size); + + /* Create each function context */ + for (i = 0; i < comp_data->func_count; i++) { + AOTFunc *func = comp_data->funcs[i]; + if (!(func_ctxes[i] = aot_create_func_context(comp_data, comp_ctx, + func, i))) { + aot_destroy_func_contexts(func_ctxes, comp_data->func_count); + return NULL; + } + } + + return func_ctxes; +} + +static bool +aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) +{ + basic_types->int1_type = LLVMInt1TypeInContext(context); + basic_types->int8_type = LLVMInt8TypeInContext(context); + basic_types->int16_type = LLVMInt16TypeInContext(context); + basic_types->int32_type = LLVMInt32TypeInContext(context); + basic_types->int64_type = LLVMInt64TypeInContext(context); + basic_types->float32_type = LLVMFloatTypeInContext(context); + basic_types->float64_type = LLVMDoubleTypeInContext(context); + basic_types->void_type = LLVMVoidTypeInContext(context); + + basic_types->meta_data_type = LLVMMetadataTypeInContext(context); + + basic_types->int8_ptr_type = LLVMPointerType(basic_types->int8_type, 0); + basic_types->int16_ptr_type = LLVMPointerType(basic_types->int16_type, 0); + basic_types->int32_ptr_type = LLVMPointerType(basic_types->int32_type, 0); + basic_types->int64_ptr_type = LLVMPointerType(basic_types->int64_type, 0); + basic_types->float32_ptr_type = LLVMPointerType(basic_types->float32_type, 0); + basic_types->float64_ptr_type = LLVMPointerType(basic_types->float64_type, 0); + + return (basic_types->int8_ptr_type + && basic_types->int16_ptr_type + && basic_types->int32_ptr_type + && basic_types->int64_ptr_type + && basic_types->float32_ptr_type + && basic_types->float64_ptr_type + && basic_types->meta_data_type) ? true : false; +} + +static bool +aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) +{ + consts->i8_zero = I8_CONST(0); + consts->i32_zero = I32_CONST(0); + consts->i64_zero = I64_CONST(0); + consts->f32_zero = F32_CONST(0); + 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); + consts->i64_neg_one = I64_CONST((uint64)-1); + consts->i32_min = I32_CONST((uint32)INT32_MIN); + consts->i64_min = I64_CONST((uint64)INT64_MIN); + consts->i32_31 = I32_CONST(31); + consts->i32_32 = I32_CONST(32); + consts->i64_63 = I64_CONST(63); + consts->i64_64 = I64_CONST(64); + + return (consts->i8_zero + && consts->i32_zero + && consts->i64_zero + && consts->f32_zero + && consts->f64_zero + && consts->i32_one + && consts->i32_two + && consts->i32_three + && consts->i32_four + && consts->i32_eight + && consts->i32_neg_one + && consts->i64_neg_one + && consts->i32_min + && consts->i64_min + && consts->i32_31 + && consts->i32_32 + && consts->i64_63 + && consts->i64_64) ? true : false; +} + +typedef struct ArchItem { + char *arch; + bool support_eb; +} ArchItem; + +static ArchItem valid_archs[] = { + { "x86_64", false }, + { "i386", false }, + { "xtensa", false}, + { "mips", true }, + { "aarch64v8", false }, + { "aarch64v8.1", false }, + { "aarch64v8.2", false }, + { "aarch64v8.3", false }, + { "aarch64v8.4", false }, + { "aarch64v8.5", false }, + { "aarch64_bev8", false }, /* big endian */ + { "aarch64_bev8.1", false }, + { "aarch64_bev8.2", false }, + { "aarch64_bev8.3", false }, + { "aarch64_bev8.4", false }, + { "aarch64_bev8.5", false }, + { "armv4", true }, + { "armv4t", true }, + { "armv5t", true }, + { "armv5te", true }, + { "armv5tej", true }, + { "armv6", true }, + { "armv6kz", true }, + { "armv6t2", true }, + { "armv6k", true }, + { "armv7", true }, + { "armv6m", true }, + { "armv6sm", true }, + { "armv7em", true }, + { "armv8a", true }, + { "armv8r", true }, + { "armv8m.base", true }, + { "armv8m.main", true }, + { "armv8.1m.main", true }, + { "thumbv4", true }, + { "thumbv4t", true }, + { "thumbv5t", true }, + { "thumbv5te", true }, + { "thumbv5tej", true }, + { "thumbv6", true }, + { "thumbv6kz", true }, + { "thumbv6t2", true }, + { "thumbv6k", true }, + { "thumbv7", true }, + { "thumbv6m", true }, + { "thumbv6sm", true }, + { "thumbv7em", true }, + { "thumbv8a", true }, + { "thumbv8r", true }, + { "thumbv8m.base", true }, + { "thumbv8m.main", true }, + { "thumbv8.1m.main", true } +}; + +static const char *valid_abis[] = { + "gnu", + "eabi", + "gnueabihf" +}; + +static void +print_supported_targets() +{ + uint32 i; + os_printf("Supported targets:\n"); + for (i = 0; i < sizeof(valid_archs) / sizeof(ArchItem); i++) { + os_printf("%s ", valid_archs[i].arch); + if (valid_archs[i].support_eb) + os_printf("%seb ", valid_archs[i].arch); + } + os_printf("\n"); +} + +static void +print_supported_abis() +{ + uint32 i; + os_printf("Supported ABI: "); + for (i = 0; i < sizeof(valid_abis) / sizeof(const char *); i++) + os_printf("%s ", valid_abis[i]); + os_printf("\n"); +} + +static bool +check_target_arch(const char *target_arch) +{ + uint32 i; + char *arch; + bool support_eb; + + for (i = 0; i < sizeof(valid_archs) / sizeof(ArchItem); i++) { + arch = valid_archs[i].arch; + support_eb = valid_archs[i].support_eb; + + if (!strncmp(target_arch, arch, strlen(arch)) + && ((support_eb && (!strcmp(target_arch + strlen(arch), "eb") + || !strcmp(target_arch + strlen(arch), ""))) + || (!support_eb && !strcmp(target_arch + strlen(arch), "")))) { + return true; + } + } + return false; +} + +static bool +check_target_abi(const char *target_abi) +{ + uint32 i; + for (i = 0; i < sizeof(valid_abis) / sizeof(char *); i++) { + if (!strcmp(target_abi, valid_abis[i])) + return true; + } + return false; +} + + +static void +get_target_arch_from_triple(const char *triple, char *arch_buf, uint32 buf_size) +{ + uint32 i = 0; + while (*triple != '-' && *triple != '\0' && i < buf_size - 1) + arch_buf[i++] = *triple++; + /* Make sure buffer is long enough */ + bh_assert(*triple == '-' || *triple == '\0'); +} + +void LLVMAddPromoteMemoryToRegisterPass(LLVMPassManagerRef PM); + +AOTCompContext * +aot_create_comp_context(AOTCompData *comp_data, + aot_comp_option_t option) +{ + AOTCompContext *comp_ctx, *ret = NULL; + /*LLVMTypeRef elem_types[8];*/ + struct LLVMMCJITCompilerOptions jit_options; + LLVMTargetRef target; + char *triple = NULL, *triple_norm, *arch, *abi, *cpu, *features, buf[128]; + char *triple_norm_new = NULL, *cpu_new = NULL; + char *err = NULL, *fp_round= "round.tonearest", *fp_exce = "fpexcept.strict"; + char triple_buf[32] = {0}; + uint32 opt_level, size_level; + LLVMCodeModel code_model; + LLVMTargetDataRef target_data_ref; + + /* Initialize LLVM environment */ + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargets(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllAsmPrinters(); + LLVMLinkInMCJIT(); + + /* Allocate memory */ + if (!(comp_ctx = wasm_runtime_malloc(sizeof(AOTCompContext)))) { + aot_set_last_error("allocate memory failed."); + return NULL; + } + + memset(comp_ctx, 0, sizeof(AOTCompContext)); + comp_ctx->comp_data = comp_data; + + /* Create LLVM context, module and builder */ + if (!(comp_ctx->context = LLVMContextCreate())) { + aot_set_last_error("create LLVM context failed."); + goto fail; + } + + if (!(comp_ctx->builder = LLVMCreateBuilderInContext(comp_ctx->context))) { + aot_set_last_error("create LLVM builder failed."); + goto fail; + } + + if (!(comp_ctx->module = + LLVMModuleCreateWithNameInContext("WASM Module", comp_ctx->context))) { + aot_set_last_error("create LLVM module failed."); + goto fail; + } + + if (option->enable_bulk_memory) + comp_ctx->enable_bulk_memory = true; + + if (option->enable_thread_mgr) + comp_ctx->enable_thread_mgr = true; + + if (option->is_jit_mode) { + /* Create LLVM execution engine */ + LLVMInitializeMCJITCompilerOptions(&jit_options, sizeof(jit_options)); + jit_options.OptLevel = LLVMCodeGenLevelAggressive; + jit_options.EnableFastISel = true; + /*jit_options.CodeModel = LLVMCodeModelSmall;*/ + if (LLVMCreateMCJITCompilerForModule + (&comp_ctx->exec_engine, comp_ctx->module, + &jit_options, sizeof(jit_options), &err) != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + aot_set_last_error("create LLVM JIT compiler failed."); + goto fail; + } + comp_ctx->is_jit_mode = true; + comp_ctx->target_machine = + LLVMGetExecutionEngineTargetMachine(comp_ctx->exec_engine); +#ifndef OS_ENABLE_HW_BOUND_CHECK + comp_ctx->enable_bound_check = true; +#else + comp_ctx->enable_bound_check = false; +#endif + } + else { + /* Create LLVM target machine */ + arch = option->target_arch; + abi = option->target_abi; + cpu = option->target_cpu; + features = option->cpu_features; + opt_level = option->opt_level; + size_level = option->size_level; + + if (arch) { + /* Add default sub-arch if not specified */ + if (!strcmp(arch, "arm")) + arch = "armv4"; + else if (!strcmp(arch, "armeb")) + arch = "armv4eb"; + else if (!strcmp(arch, "thumb")) + arch = "thumbv4t"; + else if (!strcmp(arch, "thumbeb")) + arch = "thumbv4teb"; + else if (!strcmp(arch, "aarch64")) + arch = "aarch64v8"; + else if (!strcmp(arch, "aarch64_be")) + arch = "aarch64_bev8"; + } + + /* Check target arch */ + if (arch && !check_target_arch(arch)) { + if (!strcmp(arch, "help")) + print_supported_targets(); + else + aot_set_last_error("Invalid target. " + "Use --target=help to list all supported targets"); + goto fail; + } + + /* Check target ABI */ + if (abi && !check_target_abi(abi)) { + if (!strcmp(abi, "help")) + print_supported_abis(); + else + aot_set_last_error("Invalid target ABI. " + "Use --target-abi=help to list all supported ABI"); + goto fail; + } + + if (arch) { + /* Construct target triple: --- */ + const char *vendor_sys = "-pc-linux-"; + if (!abi) + abi = "gnu"; + bh_assert(strlen(arch) + strlen(vendor_sys) + strlen(abi) < sizeof(triple_buf)); + memcpy(triple_buf, arch, strlen(arch)); + memcpy(triple_buf + strlen(arch), vendor_sys, strlen(vendor_sys)); + memcpy(triple_buf + strlen(arch) + strlen(vendor_sys), abi, strlen(abi)); + triple = triple_buf; + } + + if (!cpu && features) { + aot_set_last_error("cpu isn't specified for cpu features."); + goto fail; + } + + if (!triple && !cpu) { + /* Get a triple for the host machine */ + if (!(triple_norm = triple_norm_new = LLVMGetDefaultTargetTriple())) { + aot_set_last_error("llvm get default target triple failed."); + goto fail; + } + /* Get CPU name of the host machine */ + if (!(cpu = cpu_new = LLVMGetHostCPUName())) { + aot_set_last_error("llvm get host cpu name failed."); + goto fail; + } + } + else if (triple) { + /* Normalize a target triple */ + if (!(triple_norm = triple_norm_new = LLVMNormalizeTargetTriple(triple))) { + snprintf(buf, sizeof(buf), + "llvm normlalize target triple (%s) failed.", triple); + aot_set_last_error(buf); + goto fail; + } + if (!cpu) + cpu = ""; + } + else { /* triple is NULL, cpu isn't NULL */ + snprintf(buf, sizeof(buf), + "target isn't specified for cpu %s.", cpu); + aot_set_last_error(buf); + goto fail; + } + + if (!features) + features = ""; + + /* Get target with triple, note that LLVMGetTargetFromTriple() + return 0 when success, but not true. */ + if (LLVMGetTargetFromTriple(triple_norm, &target, &err) != 0) { + if (err) { + LLVMDisposeMessage(err); + err = NULL; + } + snprintf(buf, sizeof(buf), + "llvm get target from triple (%s) failed", triple_norm); + aot_set_last_error(buf); + goto fail; + } + + /* Save target arch */ + get_target_arch_from_triple(triple_norm, comp_ctx->target_arch, + sizeof(comp_ctx->target_arch)); + + if (option->bounds_checks == 1 || option->bounds_checks == 0) { + /* Set by user */ + comp_ctx->enable_bound_check = + (option->bounds_checks == 1) ? true : false; + } + else { + /* Unset by user, use default value */ + if (strstr(comp_ctx->target_arch, "64") && !option->is_sgx_platform) { + comp_ctx->enable_bound_check = false; + } + else { + comp_ctx->enable_bound_check = true; + } + } + + os_printf("Create AoT compiler with:\n"); + os_printf(" target: %s\n", comp_ctx->target_arch); + os_printf(" target cpu: %s\n", cpu); + os_printf(" cpu features: %s\n", features); + os_printf(" opt level: %d\n", opt_level); + os_printf(" size level: %d\n", size_level); + switch (option->output_format) { + case AOT_LLVMIR_UNOPT_FILE: + os_printf(" output format: unoptimized LLVM IR\n"); + break; + case AOT_LLVMIR_OPT_FILE: + os_printf(" output format: optimized LLVM IR\n"); + break; + case AOT_FORMAT_FILE: + os_printf(" output format: AoT file\n"); + break; + case AOT_OBJECT_FILE: + os_printf(" output format: native object file\n"); + break; + } + + if (!LLVMTargetHasTargetMachine(target)) { + snprintf(buf, sizeof(buf), + "no target machine for this target (%s).", triple_norm); + aot_set_last_error(buf); + goto fail; + } + + if (!LLVMTargetHasAsmBackend(target)) { + snprintf(buf, sizeof(buf), + "no asm backend for this target (%s).", LLVMGetTargetName(target)); + aot_set_last_error(buf); + goto fail; + } + + /* Set code model */ + if (size_level == 0) + code_model = LLVMCodeModelLarge; + else if (size_level == 1) + code_model = LLVMCodeModelMedium; + else if (size_level == 2) + code_model = LLVMCodeModelKernel; + else + code_model = LLVMCodeModelSmall; + + /* Create the target machine */ + if (!(comp_ctx->target_machine = + LLVMCreateTargetMachine(target, triple_norm, cpu, features, + opt_level, LLVMRelocStatic, + code_model))) { + aot_set_last_error("create LLVM target machine failed."); + goto fail; + } + } + + if (!(target_data_ref = + LLVMCreateTargetDataLayout(comp_ctx->target_machine))) { + aot_set_last_error("create LLVM target data layout failed."); + goto fail; + } + comp_ctx->pointer_size = LLVMPointerSize(target_data_ref); + LLVMDisposeTargetData(target_data_ref); + + comp_ctx->optimize = true; + if (option->output_format == AOT_LLVMIR_UNOPT_FILE) + comp_ctx->optimize = false; + + if (!(comp_ctx->pass_mgr = LLVMCreateFunctionPassManagerForModule + (comp_ctx->module))) { + aot_set_last_error("create LLVM pass manager failed."); + goto fail; + } + + LLVMAddPromoteMemoryToRegisterPass(comp_ctx->pass_mgr); + LLVMAddInstructionCombiningPass(comp_ctx->pass_mgr); + LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr); + LLVMAddJumpThreadingPass(comp_ctx->pass_mgr); + LLVMAddConstantPropagationPass(comp_ctx->pass_mgr); + + /* Create metadata for llvm float experimental constrained intrinsics */ + if (!(comp_ctx->fp_rounding_mode = + LLVMMDStringInContext(comp_ctx->context, + fp_round, + (uint32)strlen(fp_round))) + || !(comp_ctx->fp_exception_behavior = + LLVMMDStringInContext(comp_ctx->context, + fp_exce, + (uint32)strlen(fp_exce)))) { + aot_set_last_error("create float llvm metadata failed."); + goto fail; + } + + if (!aot_set_llvm_basic_types(&comp_ctx->basic_types, comp_ctx->context)) { + aot_set_last_error("create LLVM basic types failed."); + goto fail; + } + + if (!aot_create_llvm_consts(&comp_ctx->llvm_consts, comp_ctx)) { + aot_set_last_error("create LLVM const values failed."); + goto fail; + } + + /* set exec_env data type to int8** */ + if (!(comp_ctx->exec_env_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { + aot_set_last_error("llvm get pointer type failed."); + goto fail; + } + + /* set aot_inst data type to int8* */ + comp_ctx->aot_inst_type = INT8_PTR_TYPE; + + /* Create function context for each function */ + comp_ctx->func_ctx_count = comp_data->func_count; + if (!(comp_ctx->func_ctxes = aot_create_func_contexts(comp_data, comp_ctx))) + goto fail; + + ret = comp_ctx; + +fail: + if (triple_norm_new) + LLVMDisposeMessage(triple_norm_new); + if (cpu_new) + LLVMDisposeMessage(cpu_new); + + if (!ret) + aot_destroy_comp_context(comp_ctx); + return ret; +} + +void +aot_destroy_comp_context(AOTCompContext *comp_ctx) +{ + if (!comp_ctx) + return; + + if (comp_ctx->pass_mgr) + LLVMDisposePassManager(comp_ctx->pass_mgr); + + if (comp_ctx->target_machine && !comp_ctx->is_jit_mode) + LLVMDisposeTargetMachine(comp_ctx->target_machine); + + if (comp_ctx->builder) + LLVMDisposeBuilder(comp_ctx->builder); + + if (comp_ctx->exec_engine) { + LLVMDisposeExecutionEngine(comp_ctx->exec_engine); + /* The LLVM module is freed when disposing execution engine, + no need to dispose it again. */ + } + else if (comp_ctx->module) + LLVMDisposeModule(comp_ctx->module); + + if (comp_ctx->context) + LLVMContextDispose(comp_ctx->context); + + if (comp_ctx->func_ctxes) + aot_destroy_func_contexts(comp_ctx->func_ctxes, comp_ctx->func_ctx_count); + + wasm_runtime_free(comp_ctx); +} + +void +aot_value_stack_push(AOTValueStack *stack, AOTValue *value) +{ + if (!stack->value_list_head) + stack->value_list_head = stack->value_list_end = value; + else { + stack->value_list_end->next = value; + value->prev = stack->value_list_end; + stack->value_list_end = value; + } +} + +AOTValue * +aot_value_stack_pop(AOTValueStack *stack) +{ + AOTValue *value = stack->value_list_end; + + bh_assert(stack->value_list_end); + + if (stack->value_list_head == stack->value_list_end) + stack->value_list_head = stack->value_list_end = NULL; + else { + stack->value_list_end = stack->value_list_end->prev; + stack->value_list_end->next = NULL; + value->prev = NULL; + } + + return value; +} + +void +aot_value_stack_destroy(AOTValueStack *stack) +{ + AOTValue *value = stack->value_list_head, *p; + + while (value) { + p = value->next; + wasm_runtime_free(value); + value = p; + } +} + +void +aot_block_stack_push(AOTBlockStack *stack, AOTBlock *block) +{ + if (!stack->block_list_head) + stack->block_list_head = stack->block_list_end = block; + else { + stack->block_list_end->next = block; + block->prev = stack->block_list_end; + stack->block_list_end = block; + } +} + +AOTBlock * +aot_block_stack_pop(AOTBlockStack *stack) +{ + AOTBlock *block = stack->block_list_end; + + bh_assert(stack->block_list_end); + + if (stack->block_list_head == stack->block_list_end) + stack->block_list_head = stack->block_list_end = NULL; + else { + stack->block_list_end = stack->block_list_end->prev; + stack->block_list_end->next = NULL; + block->prev = NULL; + } + + return block; +} + +void +aot_block_stack_destroy(AOTBlockStack *stack) +{ + AOTBlock *block = stack->block_list_head, *p; + + while (block) { + p = block->next; + aot_value_stack_destroy(&block->value_stack); + wasm_runtime_free(block); + block = p; + } +} + +void +aot_block_destroy(AOTBlock *block) +{ + aot_value_stack_destroy(&block->value_stack); + if (block->param_types) + wasm_runtime_free(block->param_types); + if (block->param_phis) + wasm_runtime_free(block->param_phis); + if (block->else_param_phis) + wasm_runtime_free(block->else_param_phis); + if (block->result_types) + wasm_runtime_free(block->result_types); + if (block->result_phis) + wasm_runtime_free(block->result_phis); + wasm_runtime_free(block); +} + +bool +aot_checked_addr_list_add(AOTFuncContext *func_ctx, + uint32 local_idx, uint32 offset, uint32 bytes) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + + if (!(node = wasm_runtime_malloc(sizeof(AOTCheckedAddr)))) { + aot_set_last_error("allocate memory failed."); + return false; + } + + node->local_idx = local_idx; + node->offset = offset; + node->bytes = bytes; + + node->next = func_ctx->checked_addr_list; + func_ctx->checked_addr_list = node; + return true; +} + +void +aot_checked_addr_list_del(AOTFuncContext *func_ctx, uint32 local_idx) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + AOTCheckedAddr *node_prev = NULL, *node_next; + + while (node) { + node_next = node->next; + + if (node->local_idx == local_idx) { + if (!node_prev) + func_ctx->checked_addr_list = node_next; + else + node_prev->next = node_next; + wasm_runtime_free(node); + } + else { + node_prev = node; + } + + node = node_next; + } +} + +bool +aot_checked_addr_list_find(AOTFuncContext *func_ctx, + uint32 local_idx, uint32 offset, uint32 bytes) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list; + + while (node) { + if (node->local_idx == local_idx + && node->offset == offset + && node->bytes >= bytes) { + return true; + } + node = node->next; + } + + return false; +} + +void +aot_checked_addr_list_destroy(AOTFuncContext *func_ctx) +{ + AOTCheckedAddr *node = func_ctx->checked_addr_list, *node_next; + + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + + func_ctx->checked_addr_list = NULL; +} + diff --git a/wamr/core/iwasm/compilation/aot_llvm.h b/wamr/core/iwasm/compilation/aot_llvm.h new file mode 100644 index 0000000..3658b5f --- /dev/null +++ b/wamr/core/iwasm/compilation/aot_llvm.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_LLVM_H_ +#define _AOT_LLVM_H_ + +#include "aot.h" +#include "llvm-c/Types.h" +#include "llvm-c/Target.h" +#include "llvm-c/Core.h" +#include "llvm-c/Object.h" +#include "llvm-c/ExecutionEngine.h" +#include "llvm-c/Analysis.h" +#include "llvm-c/Transforms/Scalar.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Value in the WASM operation stack, each stack element + * is an LLVM value + */ +typedef struct AOTValue { + struct AOTValue *next; + struct AOTValue *prev; + LLVMValueRef value; + /* VALUE_TYPE_I32/I64/F32/F64/VOID */ + uint8 type; + bool is_local; + uint32 local_idx; +} AOTValue; + +/** + * Value stack, represents stack elements in a WASM block + */ +typedef struct AOTValueStack { + AOTValue *value_list_head; + AOTValue *value_list_end; +} AOTValueStack; + +typedef struct AOTBlock { + struct AOTBlock *next; + struct AOTBlock *prev; + + /* Block index */ + uint32 block_index; + /* LABEL_TYPE_BLOCK/LOOP/IF/FUNCTION */ + uint32 label_type; + /* Whether it is reachable */ + bool is_reachable; + /* Whether skip translation of wasm else branch */ + bool skip_wasm_code_else; + + /* code of else opcode of this block, if it is a IF block */ + uint8 *wasm_code_else; + /* code end of this block */ + uint8 *wasm_code_end; + + /* LLVM label points to code begin */ + LLVMBasicBlockRef llvm_entry_block; + /* LLVM label points to code else */ + LLVMBasicBlockRef llvm_else_block; + /* LLVM label points to code end */ + LLVMBasicBlockRef llvm_end_block; + + /* WASM operation stack */ + AOTValueStack value_stack; + + /* Param count/types/PHIs of this block */ + uint32 param_count; + uint8 *param_types; + LLVMValueRef *param_phis; + LLVMValueRef *else_param_phis; + + /* Result count/types/PHIs of this block */ + uint32 result_count; + uint8 *result_types; + LLVMValueRef *result_phis; +} AOTBlock; + +/** + * Block stack, represents WASM block stack elements + */ +typedef struct AOTBlockStack { + AOTBlock *block_list_head; + AOTBlock *block_list_end; + /* Current block index of each block type */ + uint32 block_index[3]; +} AOTBlockStack; + +typedef struct AOTCheckedAddr { + struct AOTCheckedAddr *next; + uint32 local_idx; + uint32 offset; + uint32 bytes; +} AOTCheckedAddr, *AOTCheckedAddrList; + +typedef struct AOTMemInfo { + LLVMValueRef mem_base_addr; + LLVMValueRef mem_cur_page_count_addr; + LLVMValueRef mem_bound_check_1byte; + LLVMValueRef mem_bound_check_2bytes; + LLVMValueRef mem_bound_check_4bytes; + LLVMValueRef mem_bound_check_8bytes; +} AOTMemInfo; + +typedef struct AOTFuncContext { + AOTFunc *aot_func; + LLVMValueRef func; + AOTBlockStack block_stack; + + LLVMValueRef exec_env; + LLVMValueRef aot_inst; + LLVMValueRef table_base; + LLVMValueRef argv_buf; + LLVMValueRef native_stack_bound; + LLVMValueRef last_alloca; + + AOTMemInfo *mem_info; + + LLVMValueRef cur_exception; + + bool mem_space_unchanged; + AOTCheckedAddrList checked_addr_list; + + LLVMBasicBlockRef *exception_blocks; + LLVMBasicBlockRef got_exception_block; + LLVMBasicBlockRef func_return_block; + LLVMValueRef exception_id_phi; + LLVMValueRef func_type_indexes; + LLVMValueRef locals[1]; +} AOTFuncContext; + +typedef struct AOTLLVMTypes { + LLVMTypeRef int1_type; + LLVMTypeRef int8_type; + LLVMTypeRef int16_type; + LLVMTypeRef int32_type; + LLVMTypeRef int64_type; + LLVMTypeRef float32_type; + LLVMTypeRef float64_type; + LLVMTypeRef void_type; + + LLVMTypeRef int8_ptr_type; + LLVMTypeRef int16_ptr_type; + LLVMTypeRef int32_ptr_type; + LLVMTypeRef int64_ptr_type; + LLVMTypeRef float32_ptr_type; + LLVMTypeRef float64_ptr_type; + + LLVMTypeRef meta_data_type; +} AOTLLVMTypes; + +typedef struct AOTLLVMConsts { + LLVMValueRef i8_zero; + LLVMValueRef i32_zero; + LLVMValueRef i64_zero; + LLVMValueRef f32_zero; + LLVMValueRef f64_zero; + LLVMValueRef i32_one; + LLVMValueRef i32_two; + LLVMValueRef i32_three; + LLVMValueRef i32_four; + LLVMValueRef i32_eight; + LLVMValueRef i32_neg_one; + LLVMValueRef i64_neg_one; + LLVMValueRef i32_min; + LLVMValueRef i64_min; + LLVMValueRef i32_31; + LLVMValueRef i32_32; + LLVMValueRef i64_63; + LLVMValueRef i64_64; +} AOTLLVMConsts; + +/** + * Compiler context + */ +typedef struct AOTCompContext { + AOTCompData *comp_data; + + /* LLVM variables required to emit LLVM IR */ + LLVMContextRef context; + LLVMModuleRef module; + LLVMBuilderRef builder; + LLVMTargetMachineRef target_machine; + char *target_cpu; + char target_arch[16]; + unsigned pointer_size; + + /* LLVM execution engine required by JIT */ + LLVMExecutionEngineRef exec_engine; + bool is_jit_mode; + + /* Bulk memory feature */ + bool enable_bulk_memory; + + /* Bounday Check */ + bool enable_bound_check; + + /* Thread Manager */ + bool enable_thread_mgr; + + /* Whether optimize the JITed code */ + bool optimize; + + /* LLVM pass manager to optimize the JITed code */ + LLVMPassManagerRef pass_mgr; + + /* LLVM floating-point rounding mode metadata */ + LLVMValueRef fp_rounding_mode; + + /* LLVM floating-point exception behavior metadata */ + LLVMValueRef fp_exception_behavior; + + /* LLVM data types */ + AOTLLVMTypes basic_types; + LLVMTypeRef exec_env_type; + LLVMTypeRef aot_inst_type; + + /* LLVM const values */ + AOTLLVMConsts llvm_consts; + + /* Function contexts */ + AOTFuncContext **func_ctxes; + uint32 func_ctx_count; +} AOTCompContext; + +enum { + AOT_FORMAT_FILE, + AOT_OBJECT_FILE, + AOT_LLVMIR_UNOPT_FILE, + AOT_LLVMIR_OPT_FILE, +}; + +typedef struct AOTCompOption{ + bool is_jit_mode; + char *target_arch; + char *target_abi; + char *target_cpu; + char *cpu_features; + bool enable_bulk_memory; + bool enable_thread_mgr; + bool is_sgx_platform; + uint32 opt_level; + uint32 size_level; + uint32 output_format; + uint32 bounds_checks; +} AOTCompOption, *aot_comp_option_t; + +AOTCompContext * +aot_create_comp_context(AOTCompData *comp_data, + aot_comp_option_t option); + +void +aot_destroy_comp_context(AOTCompContext *comp_ctx); + +bool +aot_compile_wasm(AOTCompContext *comp_ctx); + +uint8* +aot_emit_elf_file(AOTCompContext *comp_ctx, uint32 *p_elf_file_size); + +void +aot_destroy_elf_file(uint8 *elf_file); + +void +aot_value_stack_push(AOTValueStack *stack, AOTValue *value); + +AOTValue * +aot_value_stack_pop(AOTValueStack *stack); + +void +aot_value_stack_destroy(AOTValueStack *stack); + +void +aot_block_stack_push(AOTBlockStack *stack, AOTBlock *block); + +AOTBlock * +aot_block_stack_pop(AOTBlockStack *stack); + +void +aot_block_stack_destroy(AOTBlockStack *stack); + +void +aot_block_destroy(AOTBlock *block); + +LLVMTypeRef +wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type); + +bool +aot_checked_addr_list_add(AOTFuncContext *func_ctx, + uint32 local_idx, uint32 offset, uint32 bytes); + +void +aot_checked_addr_list_del(AOTFuncContext *func_ctx, uint32 local_idx); + +bool +aot_checked_addr_list_find(AOTFuncContext *func_ctx, + uint32 local_idx, uint32 offset, uint32 bytes); + +void +aot_checked_addr_list_destroy(AOTFuncContext *func_ctx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _AOT_LLVM_H_ */ + diff --git a/wamr/core/iwasm/compilation/iwasm_compl.cmake b/wamr/core/iwasm/compilation/iwasm_compl.cmake new file mode 100644 index 0000000..36efd33 --- /dev/null +++ b/wamr/core/iwasm/compilation/iwasm_compl.cmake @@ -0,0 +1,8 @@ +set (IWASM_COMPL_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${IWASM_COMPL_DIR}) + +file (GLOB_RECURSE source_all ${IWASM_COMPL_DIR}/*.c) + +set (IWASM_COMPL_SOURCE ${source_all}) + diff --git a/wamr/core/iwasm/include/aot_export.h b/wamr/core/iwasm/include/aot_export.h new file mode 100644 index 0000000..778400d --- /dev/null +++ b/wamr/core/iwasm/include/aot_export.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EXPORT_H +#define _AOT_EXPORT_H + +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +struct AOTCompData; +typedef struct AOTCompData *aot_comp_data_t; + +struct AOTCompContext; +typedef struct AOTCompContext *aot_comp_context_t; + +aot_comp_data_t +aot_create_comp_data(void *wasm_module); + +void +aot_destroy_comp_data(aot_comp_data_t comp_data); + +enum { + AOT_FORMAT_FILE, + AOT_OBJECT_FILE, + AOT_LLVMIR_UNOPT_FILE, + AOT_LLVMIR_OPT_FILE, +}; + +typedef struct AOTCompOption{ + bool is_jit_mode; + char *target_arch; + char *target_abi; + char *target_cpu; + char *cpu_features; + bool enable_bulk_memory; + bool enable_thread_mgr; + bool is_sgx_platform; + uint32_t opt_level; + uint32_t size_level; + uint32_t output_format; + uint32_t bounds_checks; +} AOTCompOption, *aot_comp_option_t; + +aot_comp_context_t +aot_create_comp_context(aot_comp_data_t comp_data, + aot_comp_option_t option); + +void +aot_destroy_comp_context(aot_comp_context_t comp_ctx); + +bool +aot_compile_wasm(aot_comp_context_t comp_ctx); + +bool +aot_emit_llvm_file(aot_comp_context_t comp_ctx, const char *file_name); + +bool +aot_emit_object_file(aot_comp_context_t comp_ctx, const char *file_name); + +bool +aot_emit_aot_file(aot_comp_context_t comp_ctx, + aot_comp_data_t comp_data, + const char *file_name); + +void +aot_destroy_aot_file(uint8_t *aot_file); + +char* +aot_get_last_error(); + +uint32_t +aot_get_plt_table_size(); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _AOT_EXPORT_H */ diff --git a/wamr/core/iwasm/include/lib_export.h b/wamr/core/iwasm/include/lib_export.h new file mode 100644 index 0000000..3a4c02d --- /dev/null +++ b/wamr/core/iwasm/include/lib_export.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _LIB_EXPORT_H_ +#define _LIB_EXPORT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct NativeSymbol { + const char *symbol; + void *func_ptr; + const char *signature; + /* attachment which can be retrieved in native API by + calling wasm_runtime_get_function_attachment(exec_env) */ + void *attachment; +} NativeSymbol; + +#define EXPORT_WASM_API(symbol) {#symbol, (void*)symbol, NULL, NULL} +#define EXPORT_WASM_API2(symbol) {#symbol, (void*)symbol##_wrapper, NULL, NULL} + +#define EXPORT_WASM_API_WITH_SIG(symbol, signature) \ + {#symbol, (void*)symbol, signature, NULL} +#define EXPORT_WASM_API_WITH_SIG2(symbol, signature) \ + {#symbol, (void*)symbol##_wrapper, signature, NULL} + +#define EXPORT_WASM_API_WITH_ATT(symbol, signature, attachment) \ + {#symbol, (void*)symbol, signature, attachment} +#define EXPORT_WASM_API_WITH_ATT2(symbol, signature, attachment) \ + {#symbol, (void*)symbol##_wrapper, signature, attachment} + +/** + * Get the exported APIs of base lib + * + * @param p_base_lib_apis return the exported API array of base lib + * + * @return the number of the exported API + */ +uint32_t +get_base_lib_export_apis(NativeSymbol **p_base_lib_apis); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/wamr/core/iwasm/include/wasm_c_api.h b/wamr/core/iwasm/include/wasm_c_api.h new file mode 100644 index 0000000..fdc0723 --- /dev/null +++ b/wamr/core/iwasm/include/wasm_c_api.h @@ -0,0 +1,685 @@ +// WebAssembly C API + +#ifndef WASM_H +#define WASM_H + +#include +#include +#include +#include +#include + +#ifndef WASM_API_EXTERN +#ifdef _WIN32 +#define WASM_API_EXTERN __declspec(dllimport) +#else +#define WASM_API_EXTERN +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Auxiliaries + +// Machine types +#if (__STDC_VERSION__ > 199901L) +inline void assertions() { + static_assert(sizeof(float) == sizeof(uint32_t), "incompatible float type"); + static_assert(sizeof(double) == sizeof(uint64_t), "incompatible double type"); + static_assert(sizeof(intptr_t) == sizeof(uint32_t) || + sizeof(intptr_t) == sizeof(uint64_t), + "incompatible pointer type"); +} +#endif /* __STDC_VERSION__ > 199901L */ + +typedef char byte_t; +typedef float float32_t; +typedef double float64_t; + + +// Ownership + +#define own + +// The qualifier `own` is used to indicate ownership of data in this API. +// It is intended to be interpreted similar to a `const` qualifier: +// +// - `own wasm_xxx_t*` owns the pointed-to data +// - `own wasm_xxx_t` distributes to all fields of a struct or union `xxx` +// - `own wasm_xxx_vec_t` owns the vector as well as its elements(!) +// - an `own` function parameter passes ownership from caller to callee +// - an `own` function result passes ownership from callee to caller +// - an exception are `own` pointer parameters named `out`, which are copy-back +// output parameters passing back ownership from callee to caller +// +// Own data is created by `wasm_xxx_new` functions and some others. +// It must be released with the corresponding `wasm_xxx_delete` function. +// +// Deleting a reference does not necessarily delete the underlying object, +// it merely indicates that this owner no longer uses it. +// +// For vectors, `const wasm_xxx_vec_t` is used informally to indicate that +// neither the vector nor its elements should be modified. +// TODO: introduce proper `wasm_xxx_const_vec_t`? + + +#define WASM_DECLARE_OWN(name) \ + typedef struct wasm_##name##_t wasm_##name##_t; \ + \ + WASM_API_EXTERN void wasm_##name##_delete(own wasm_##name##_t*); + + +// Vectors +// size: capacity +// num_elems: current number of elements +// size_of_elem: size of one element +#define WASM_DECLARE_VEC(name, ptr_or_none) \ + typedef struct wasm_##name##_vec_t { \ + size_t size; \ + wasm_##name##_t ptr_or_none* data; \ + size_t num_elems; \ + size_t size_of_elem; \ + } wasm_##name##_vec_t; \ + \ + WASM_API_EXTERN void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out); \ + WASM_API_EXTERN void wasm_##name##_vec_new_uninitialized( \ + own wasm_##name##_vec_t* out, size_t); \ + WASM_API_EXTERN void wasm_##name##_vec_new( \ + own wasm_##name##_vec_t* out, \ + size_t, own wasm_##name##_t ptr_or_none const[]); \ + WASM_API_EXTERN void wasm_##name##_vec_copy( \ + own wasm_##name##_vec_t* out, const wasm_##name##_vec_t*); \ + WASM_API_EXTERN void wasm_##name##_vec_delete(own wasm_##name##_vec_t*); + + +// Byte vectors + +typedef byte_t wasm_byte_t; +WASM_DECLARE_VEC(byte, ) + +typedef wasm_byte_vec_t wasm_name_t; + +#define wasm_name wasm_byte_vec +#define wasm_name_new wasm_byte_vec_new +#define wasm_name_new_empty wasm_byte_vec_new_empty +#define wasm_name_new_new_uninitialized wasm_byte_vec_new_uninitialized +#define wasm_name_copy wasm_byte_vec_copy +#define wasm_name_delete wasm_byte_vec_delete + +static inline void wasm_name_new_from_string( + own wasm_name_t* out, const char* s +) { + wasm_name_new(out, strlen(s) + 1, s); +} + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Environment + +// Engine + +WASM_DECLARE_OWN(engine) + +WASM_API_EXTERN own wasm_engine_t* wasm_engine_new(); + + +// Store + +WASM_DECLARE_OWN(store) + +WASM_API_EXTERN own wasm_store_t* wasm_store_new(wasm_engine_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Type Representations + +// Type attributes + +typedef uint8_t wasm_mutability_t; +enum wasm_mutability_enum { + WASM_CONST, + WASM_VAR, +}; + +typedef struct wasm_limits_t { + uint32_t min; + uint32_t max; +} wasm_limits_t; + +static const uint32_t wasm_limits_max_default = 0xffffffff; + + +// Generic + +#define WASM_DECLARE_TYPE(name) \ + WASM_DECLARE_OWN(name) \ + WASM_DECLARE_VEC(name, *) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t*); + + +// Value Types + +WASM_DECLARE_TYPE(valtype) + +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_ANYREF = 128, + WASM_FUNCREF, +}; +#endif + +WASM_API_EXTERN own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t); + +WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*); + +static inline bool wasm_valkind_is_num(wasm_valkind_t k) { + return k < WASM_ANYREF; +} +static inline bool wasm_valkind_is_ref(wasm_valkind_t k) { + return k >= WASM_ANYREF; +} + +static inline bool wasm_valtype_is_num(const wasm_valtype_t* t) { + return wasm_valkind_is_num(wasm_valtype_kind(t)); +} +static inline bool wasm_valtype_is_ref(const wasm_valtype_t* t) { + return wasm_valkind_is_ref(wasm_valtype_kind(t)); +} + + +// Function Types + +WASM_DECLARE_TYPE(functype) + +WASM_API_EXTERN own wasm_functype_t* wasm_functype_new( + own wasm_valtype_vec_t* params, own wasm_valtype_vec_t* results); + +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*); +WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*); + + +// Global Types + +WASM_DECLARE_TYPE(globaltype) + +WASM_API_EXTERN own wasm_globaltype_t* wasm_globaltype_new( + own wasm_valtype_t*, wasm_mutability_t); + +WASM_API_EXTERN const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t*); +WASM_API_EXTERN wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t*); + + +// Table Types + +WASM_DECLARE_TYPE(tabletype) + +WASM_API_EXTERN own wasm_tabletype_t* wasm_tabletype_new( + own wasm_valtype_t*, const wasm_limits_t*); + +WASM_API_EXTERN const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t*); + + +// Memory Types + +WASM_DECLARE_TYPE(memorytype) + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t*); + +WASM_API_EXTERN const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t*); + + +// Extern Types + +WASM_DECLARE_TYPE(externtype) + +typedef uint8_t wasm_externkind_t; +enum wasm_externkind_enum { + WASM_EXTERN_FUNC, + WASM_EXTERN_GLOBAL, + WASM_EXTERN_TABLE, + WASM_EXTERN_MEMORY, +}; + +WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*); + +WASM_API_EXTERN wasm_externtype_t* wasm_functype_as_externtype(wasm_functype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_globaltype_as_externtype(wasm_globaltype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_tabletype_as_externtype(wasm_tabletype_t*); +WASM_API_EXTERN wasm_externtype_t* wasm_memorytype_as_externtype(wasm_memorytype_t*); + +WASM_API_EXTERN wasm_functype_t* wasm_externtype_as_functype(wasm_externtype_t*); +WASM_API_EXTERN wasm_globaltype_t* wasm_externtype_as_globaltype(wasm_externtype_t*); +WASM_API_EXTERN wasm_tabletype_t* wasm_externtype_as_tabletype(wasm_externtype_t*); +WASM_API_EXTERN wasm_memorytype_t* wasm_externtype_as_memorytype(wasm_externtype_t*); + +WASM_API_EXTERN const wasm_externtype_t* wasm_functype_as_externtype_const(const wasm_functype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_globaltype_as_externtype_const(const wasm_globaltype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_tabletype_as_externtype_const(const wasm_tabletype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_memorytype_as_externtype_const(const wasm_memorytype_t*); + +WASM_API_EXTERN const wasm_functype_t* wasm_externtype_as_functype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_globaltype_t* wasm_externtype_as_globaltype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_tabletype_t* wasm_externtype_as_tabletype_const(const wasm_externtype_t*); +WASM_API_EXTERN const wasm_memorytype_t* wasm_externtype_as_memorytype_const(const wasm_externtype_t*); + + +// Import Types + +WASM_DECLARE_TYPE(importtype) + +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_importtype_module(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_name_t* wasm_importtype_name(const wasm_importtype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_importtype_type(const wasm_importtype_t*); + + +// Export Types + +WASM_DECLARE_TYPE(exporttype) + +WASM_API_EXTERN own wasm_exporttype_t* wasm_exporttype_new( + own wasm_name_t*, own wasm_externtype_t*); + +WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*); +WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*); + + +/////////////////////////////////////////////////////////////////////////////// +// Runtime Objects + +// Values + +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float32_t f32; + float64_t f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; +#endif + +WASM_API_EXTERN void wasm_val_delete(own wasm_val_t* v); +WASM_API_EXTERN void wasm_val_copy(own wasm_val_t* out, const wasm_val_t*); + +WASM_DECLARE_VEC(val, ) + + +// References + +#define WASM_DECLARE_REF_BASE(name) \ + WASM_DECLARE_OWN(name) \ + \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t*); \ + WASM_API_EXTERN bool wasm_##name##_same(const wasm_##name##_t*, const wasm_##name##_t*); + +#define WASM_DECLARE_REF(name) \ + WASM_DECLARE_REF_BASE(name) \ + \ + WASM_API_EXTERN wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t*); \ + WASM_API_EXTERN wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t*); \ + WASM_API_EXTERN const wasm_ref_t* wasm_##name##_as_ref_const(const wasm_##name##_t*); \ + WASM_API_EXTERN const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t*); + +#define WASM_DECLARE_SHARABLE_REF(name) \ + WASM_DECLARE_REF(name) \ + WASM_DECLARE_OWN(shared_##name) \ + \ + WASM_API_EXTERN own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t*); \ + WASM_API_EXTERN own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, const wasm_shared_##name##_t*); + + +WASM_DECLARE_REF_BASE(ref) + +// Traps + +typedef wasm_name_t wasm_message_t; // null terminated + +WASM_DECLARE_REF_BASE(trap) + +WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*); + +WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out); + +// Modules +#ifndef WASM_MODULE_T_DEFINED +#define WASM_MODULE_T_DEFINED +struct WASMModuleCommon; +typedef struct WASMModuleCommon *wasm_module_t; +#endif + +WASM_API_EXTERN own wasm_module_t* wasm_module_new( + wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); + +WASM_API_EXTERN void wasm_module_delete(own wasm_module_t*); + +WASM_API_EXTERN own wasm_module_t* wasm_module_copy(const wasm_module_t*); +WASM_API_EXTERN bool wasm_module_same(const wasm_module_t*, const wasm_module_t*); + +WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +WASM_API_EXTERN void wasm_module_exports(const wasm_module_t*, own wasm_exporttype_vec_t* out); + +WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); + + +// Function Instances + +WASM_DECLARE_REF(func) + +typedef own wasm_trap_t* (*wasm_func_callback_t)( + const wasm_val_t args[], wasm_val_t results[]); +typedef own wasm_trap_t* (*wasm_func_callback_with_env_t)( + void* env, const wasm_val_t args[], wasm_val_t results[]); + +WASM_API_EXTERN own wasm_func_t* wasm_func_new( + wasm_store_t*, const wasm_functype_t*, wasm_func_callback_t); +WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env( + wasm_store_t*, const wasm_functype_t* type, wasm_func_callback_with_env_t, + void* env, void (*finalizer)(void*)); + +WASM_API_EXTERN own wasm_functype_t* wasm_func_type(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_param_arity(const wasm_func_t*); +WASM_API_EXTERN size_t wasm_func_result_arity(const wasm_func_t*); + +WASM_API_EXTERN own wasm_trap_t* wasm_func_call( + const wasm_func_t*, const wasm_val_t args[], wasm_val_t results[]); + + +// Global Instances + +WASM_DECLARE_REF_BASE(global) + +WASM_API_EXTERN own wasm_global_t* wasm_global_new( + wasm_store_t*, const wasm_globaltype_t*, const wasm_val_t*); + +WASM_API_EXTERN own wasm_globaltype_t* wasm_global_type(const wasm_global_t*); + +WASM_API_EXTERN void wasm_global_get(const wasm_global_t*, own wasm_val_t* out); +WASM_API_EXTERN void wasm_global_set(wasm_global_t*, const wasm_val_t*); + + +// Table Instances + +WASM_DECLARE_REF_BASE(table) + +typedef uint32_t wasm_table_size_t; + +WASM_API_EXTERN own wasm_table_t* wasm_table_new( + wasm_store_t*, const wasm_tabletype_t*, wasm_ref_t* init); + +WASM_API_EXTERN own wasm_tabletype_t* wasm_table_type(const wasm_table_t*); + +WASM_API_EXTERN own wasm_ref_t* wasm_table_get(const wasm_table_t*, wasm_table_size_t index); +WASM_API_EXTERN bool wasm_table_set(wasm_table_t*, wasm_table_size_t index, wasm_ref_t*); + +WASM_API_EXTERN wasm_table_size_t wasm_table_size(const wasm_table_t*); +WASM_API_EXTERN bool wasm_table_grow(wasm_table_t*, wasm_table_size_t delta, wasm_ref_t* init); + + +// Memory Instances + +WASM_DECLARE_REF_BASE(memory) + +typedef uint32_t wasm_memory_pages_t; + +static const size_t MEMORY_PAGE_SIZE = 0x10000; + +WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*); + +WASM_API_EXTERN own wasm_memorytype_t* wasm_memory_type(const wasm_memory_t*); + +WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*); +WASM_API_EXTERN size_t wasm_memory_data_size(const wasm_memory_t*); + +WASM_API_EXTERN wasm_memory_pages_t wasm_memory_size(const wasm_memory_t*); +WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta); + + +// Externals + +WASM_DECLARE_REF_BASE(extern) +WASM_DECLARE_VEC(extern, *) + +WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*); +WASM_API_EXTERN own wasm_externtype_t* wasm_extern_type(const wasm_extern_t*); + +WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*); +WASM_API_EXTERN wasm_extern_t* wasm_global_as_extern(wasm_global_t*); +WASM_API_EXTERN wasm_extern_t* wasm_table_as_extern(wasm_table_t*); +WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*); + +WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*); +WASM_API_EXTERN wasm_global_t* wasm_extern_as_global(wasm_extern_t*); +WASM_API_EXTERN wasm_table_t* wasm_extern_as_table(wasm_extern_t*); +WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*); + +WASM_API_EXTERN const wasm_extern_t* wasm_func_as_extern_const(const wasm_func_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_global_as_extern_const(const wasm_global_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_table_as_extern_const(const wasm_table_t*); +WASM_API_EXTERN const wasm_extern_t* wasm_memory_as_extern_const(const wasm_memory_t*); + +WASM_API_EXTERN const wasm_func_t* wasm_extern_as_func_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_global_t* wasm_extern_as_global_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_table_t* wasm_extern_as_table_const(const wasm_extern_t*); +WASM_API_EXTERN const wasm_memory_t* wasm_extern_as_memory_const(const wasm_extern_t*); + + +// Module Instances + +WASM_DECLARE_REF_BASE(instance) + +WASM_API_EXTERN own wasm_instance_t* wasm_instance_new( + wasm_store_t*, const wasm_module_t*, const wasm_extern_t* const imports[], + own wasm_trap_t** +); + +WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out); + + +/////////////////////////////////////////////////////////////////////////////// +// Convenience + +// Value Type construction short-hands + +static inline own wasm_valtype_t* wasm_valtype_new_i32() { + return wasm_valtype_new(WASM_I32); +} +static inline own wasm_valtype_t* wasm_valtype_new_i64() { + return wasm_valtype_new(WASM_I64); +} +static inline own wasm_valtype_t* wasm_valtype_new_f32() { + return wasm_valtype_new(WASM_F32); +} +static inline own wasm_valtype_t* wasm_valtype_new_f64() { + return wasm_valtype_new(WASM_F64); +} + +static inline own wasm_valtype_t* wasm_valtype_new_anyref() { + return wasm_valtype_new(WASM_ANYREF); +} +static inline own wasm_valtype_t* wasm_valtype_new_funcref() { + return wasm_valtype_new(WASM_FUNCREF); +} + + +// Function Types construction short-hands + +static inline own wasm_functype_t* wasm_functype_new_0_0() { + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_0( + own wasm_valtype_t* p +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_0( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new_empty(&results); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_1( + own wasm_valtype_t* r +) { + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_1( + own wasm_valtype_t* p, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* r +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_1( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[1] = {r}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 1, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_0_2( + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new_empty(¶ms); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_1_2( + own wasm_valtype_t* p, own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[1] = {p}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 1, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_2_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[2] = {p1, p2}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 2, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + +static inline own wasm_functype_t* wasm_functype_new_3_2( + own wasm_valtype_t* p1, own wasm_valtype_t* p2, own wasm_valtype_t* p3, + own wasm_valtype_t* r1, own wasm_valtype_t* r2 +) { + wasm_valtype_t* ps[3] = {p1, p2, p3}; + wasm_valtype_t* rs[2] = {r1, r2}; + wasm_valtype_vec_t params, results; + wasm_valtype_vec_new(¶ms, 3, ps); + wasm_valtype_vec_new(&results, 2, rs); + return wasm_functype_new(¶ms, &results); +} + + +// Value construction short-hands + +static inline void wasm_val_init_ptr(own wasm_val_t* out, void* p) { +#if UINTPTR_MAX == UINT32_MAX + out->kind = WASM_I32; + out->of.i32 = (intptr_t)p; +#elif UINTPTR_MAX == UINT64_MAX + out->kind = WASM_I64; + out->of.i64 = (intptr_t)p; +#endif +} + +static inline void* wasm_val_ptr(const wasm_val_t* val) { +#if UINTPTR_MAX == UINT32_MAX + return (void*)(intptr_t)val->of.i32; +#elif UINTPTR_MAX == UINT64_MAX + return (void*)(intptr_t)val->of.i64; +#endif +} + + +/////////////////////////////////////////////////////////////////////////////// + +#undef own + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef WASM_H diff --git a/wamr/core/iwasm/include/wasm_export.h b/wamr/core/iwasm/include/wasm_export.h new file mode 100644 index 0000000..0f392e2 --- /dev/null +++ b/wamr/core/iwasm/include/wasm_export.h @@ -0,0 +1,826 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_EXPORT_H +#define _WASM_EXPORT_H + +#include +#include +#include "lib_export.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#define validate_app_str_addr(offset) \ + wasm_runtime_validate_app_str_addr(module_inst, offset) + +#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, 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 native_raw_return_type(type, args) type *raw_ret = (type*)(args) + +#define native_raw_get_arg(type, name, args) type name = *((type*)(args++)) + +#define native_raw_set_return(val) *raw_ret = (val) + + +#ifndef WASM_MODULE_T_DEFINED +#define WASM_MODULE_T_DEFINED +/* Uninstantiated WASM module loaded from WASM binary file + or AoT binary file*/ +struct WASMModuleCommon; +typedef struct WASMModuleCommon *wasm_module_t; +#endif + +/* Instantiated WASM module */ +struct WASMModuleInstanceCommon; +typedef struct WASMModuleInstanceCommon *wasm_module_inst_t; + +/* Function instance */ +typedef void WASMFunctionInstanceCommon; +typedef WASMFunctionInstanceCommon *wasm_function_inst_t; + +/* WASM section */ +typedef struct wasm_section_t { + struct wasm_section_t *next; + /* section type */ + int section_type; + /* section body, not include type and size */ + uint8_t *section_body; + /* section body size */ + uint32_t section_body_size; +} wasm_section_t, aot_section_t, *wasm_section_list_t, *aot_section_list_t; + +/* Execution environment, e.g. stack info */ +struct WASMExecEnv; +typedef struct WASMExecEnv *wasm_exec_env_t; + +/* Package Type */ +typedef enum { + Wasm_Module_Bytecode = 0, + Wasm_Module_AoT, + Package_Type_Unknown = 0xFFFF +} package_type_t; + +/* Memory allocator type */ +typedef enum { + /* pool mode, allocate memory from user defined heap buffer */ + Alloc_With_Pool = 0, + /* user allocator mode, allocate memory from user defined + malloc function */ + Alloc_With_Allocator, + /* system allocator mode, allocate memory from system allocator, + or, platform's os_malloc function */ + Alloc_With_System_Allocator, +} mem_alloc_type_t; + +/* Memory allocator option */ +typedef union MemAllocOption { + struct { + void *heap_buf; + uint32_t heap_size; + } pool; + struct { + void *malloc_func; + void *realloc_func; + void *free_func; + } allocator; +} MemAllocOption; + +/* WASM runtime initialize arguments */ +typedef struct RuntimeInitArgs { + mem_alloc_type_t mem_alloc_type; + MemAllocOption mem_alloc_option; + + const char *native_module_name; + NativeSymbol *native_symbols; + uint32_t n_native_symbols; + + /* maximum thread number, only used when + WASM_ENABLE_THREAD_MGR is defined */ + uint32_t max_thread_num; +} RuntimeInitArgs; + +#ifndef WASM_VALKIND_T_DEFINED +#define WASM_VALKIND_T_DEFINED +typedef uint8_t wasm_valkind_t; +enum wasm_valkind_enum { + WASM_I32, + WASM_I64, + WASM_F32, + WASM_F64, + WASM_ANYREF = 128, + WASM_FUNCREF, +}; +#endif + +#ifndef WASM_VAL_T_DEFINED +#define WASM_VAL_T_DEFINED +struct wasm_ref_t; + +typedef struct wasm_val_t { + wasm_valkind_t kind; + union { + int32_t i32; + int64_t i64; + float f32; + double f64; + struct wasm_ref_t* ref; + } of; +} wasm_val_t; +#endif + +/** + * Initialize the WASM runtime environment, and also initialize + * the memory allocator with system allocator, which calls os_malloc + * to allocate memory + * + * @return true if success, false otherwise + */ +bool +wasm_runtime_init(void); + +/** + * Initialize the WASM runtime environment, and also initialize + * the memory allocator and register native symbols, which are specified + * with init arguments + * + * @param init_args specifies the init arguments + * + * @return return true if success, false otherwise + */ +bool +wasm_runtime_full_init(RuntimeInitArgs *init_args); + +/** + * Destroy the WASM runtime environment. + */ +void +wasm_runtime_destroy(void); + +/** + * Allocate memory from runtime memory environment. + * + * @param size bytes need to allocate + * + * @return the pointer to memory allocated + */ +void * +wasm_runtime_malloc(unsigned int size); + +/** + * Reallocate memory from runtime memory environment + * + * @param ptr the original memory + * @param size bytes need to reallocate + * + * @return the pointer to memory reallocated + */ +void * +wasm_runtime_realloc(void *ptr, unsigned int size); + +/* + * Free memory to runtime memory environment. + */ +void wasm_runtime_free(void *ptr); + +/** + * Get the package type of a buffer. + * + * @param buf the package buffer + * @param size the package buffer size + * + * @return the package type, return Package_Type_Unknown if the type is unknown + */ +package_type_t +get_package_type(const uint8_t *buf, uint32_t size); + +#if WASM_ENABLE_MULTI_MODULE != 0 +/** + * It is a callback for WAMR providing by embedding to load a module file + * into a buffer + */ +typedef bool (*module_reader)(const char *module_name, + uint8_t **p_buffer, uint32_t *p_size); + +/** + * It is a callback for WAMR providing by embedding to release the buffer which + * is used by loading a module file + */ +typedef void (*module_destroyer)(uint8_t *buffer, uint32_t size); + +/** + * To setup callbacks for reading and releasing a buffer about a module file + * + * @param reader a callback to read a module file into a buffer + * @param destroyer a callback to release above buffer + */ +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); +/** + * Give the "module" a name "module_name". + * can not assign a new name to a module if it already has a name + * + * @param module_name indicate a name + * @param module the target module + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return true means success, false means failed + */ +bool +wasm_runtime_register_module(const char *module_name, wasm_module_t module, + char *error_buf, uint32_t error_buf_size); + +/** + * To check if there is already a loaded module named module_name in the + * runtime. you will not want to load repeately + * + * @param module_name indicate a name + * + * @return return WASM module loaded, NULL if failed + */ +wasm_module_t +wasm_runtime_find_module_registered(const char *module_name); +#endif /* WASM_ENABLE_MULTI_MODULE */ + +/** + * Load a WASM module from a specified byte buffer. The byte buffer can be + * WASM binary data when interpreter or JIT is enabled, or AOT binary data + * when AOT is enabled. If it is AOT binary data, it must be 4-byte aligned. + * + * @param buf the byte buffer which contains the WASM binary data + * @param size the size of the buffer + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return return WASM module loaded, NULL if failed + */ +wasm_module_t +wasm_runtime_load(const uint8_t *buf, uint32_t size, + char *error_buf, uint32_t error_buf_size); + +/** + * Load a WASM module from a specified WASM or AOT section list. + * + * @param section_list the section list which contains each section data + * @param is_aot whether the section list is AOT section list + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return return WASM module loaded, NULL if failed + */ +wasm_module_t +wasm_runtime_load_from_sections(wasm_section_list_t section_list, bool is_aot, + char *error_buf, uint32_t error_buf_size); + +/** + * Unload a WASM module. + * + * @param module the module to be unloaded + */ +void +wasm_runtime_unload(wasm_module_t module); + +void +wasm_runtime_set_wasi_args(wasm_module_t module, + const char *dir_list[], uint32_t dir_count, + const char *map_dir_list[], uint32_t map_dir_count, + const char *env[], uint32_t env_count, + char *argv[], int argc); + +/** + * Instantiate a WASM module. + * + * @param module the WASM module to instantiate + * @param stack_size the default stack size of the module instance when the + * exec env's operation stack isn't created by user, e.g. API + * wasm_application_execute_main() and wasm_application_execute_func() + * create the operation stack internally with the stack size specified + * here. And API wasm_runtime_create_exec_env() creates the operation + * stack with stack size specified by its parameter, the stack size + * specified here is ignored. + * @param heap_size the default heap size of the module instance, a heap will + * be created besides the app memory space. Both wasm app and native + * function can allocate memory from the heap. If heap_size is 0, the + * default heap size will be used. + * @param error_buf buffer to output the error info if failed + * @param error_buf_size the size of the error buffer + * + * @return return the instantiated WASM module instance, NULL if failed + */ +wasm_module_inst_t +wasm_runtime_instantiate(const wasm_module_t module, + uint32_t stack_size, uint32_t heap_size, + char *error_buf, uint32_t error_buf_size); + +/** + * Deinstantiate a WASM module instance, destroy the resources. + * + * @param module_inst the WASM module instance to destroy + */ +void +wasm_runtime_deinstantiate(wasm_module_inst_t module_inst); + +bool +wasm_runtime_is_wasi_mode(wasm_module_inst_t module_inst); + +wasm_function_inst_t +wasm_runtime_lookup_wasi_start_function(wasm_module_inst_t module_inst); + +/** + * Lookup an exported function in the WASM module instance. + * + * @param module_inst the module instance + * @param name the name of the function + * @param signature the signature of the function, ignored currently + * + * @return the function instance found, NULL if not found + */ +wasm_function_inst_t +wasm_runtime_lookup_function(wasm_module_inst_t const module_inst, + const char *name, const char *signature); + +/** + * Create execution environment for a WASM module instance. + * + * @param module_inst the module instance + * @param stack_size the stack size to execute a WASM function + * + * @return the execution environment, NULL if failed, e.g. invalid + * stack size is passed + */ +wasm_exec_env_t +wasm_runtime_create_exec_env(wasm_module_inst_t module_inst, + uint32_t stack_size); + +/** + * Destroy the execution environment. + * + * @param exec_env the execution environment to destroy + */ +void +wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env); + +/** + * Get WASM module instance from execution environment + * + * @param exec_env the execution environment to retrieve + * + * @return the WASM module instance + */ +wasm_module_inst_t +wasm_runtime_get_module_inst(wasm_exec_env_t exec_env); + +/** + * Call the given WASM function of a WASM module instance with + * arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param argc the number of arguments + * @param argv the arguments. If the function has return value, + * the first (or first two in case 64-bit return value) element of + * argv stores the return value of the called WASM function after this + * function returns. + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +bool +wasm_runtime_call_wasm(wasm_exec_env_t exec_env, + wasm_function_inst_t function, + uint32_t argc, uint32_t argv[]); + +/** + * Call the given WASM function of a WASM module instance with + * provided results space and arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param args the arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +bool +wasm_runtime_call_wasm_a(wasm_exec_env_t exec_env, + wasm_function_inst_t function, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, wasm_val_t *args); + +/** + * Call the given WASM function of a WASM module instance with + * provided results space and variant arguments (bytecode and AoT). + * + * @param exec_env the execution environment to call the function, + * which must be created from wasm_create_exec_env() + * @param function the function to call + * @param num_results the number of results + * @param results the pre-alloced pointer to get the results + * @param num_args the number of arguments + * @param ... the variant arguments + * + * @return true if success, false otherwise and exception will be thrown, + * the caller can call wasm_runtime_get_exception to get the exception + * info. + */ +bool +wasm_runtime_call_wasm_v(wasm_exec_env_t exec_env, + wasm_function_inst_t function, + uint32_t num_results, wasm_val_t results[], + uint32_t num_args, ...); + +/** + * Find the unique main function from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the main function is called, false otherwise and exception + * will be thrown, the caller can call wasm_runtime_get_exception to get + * the exception info. + */ +bool +wasm_application_execute_main(wasm_module_inst_t module_inst, + int32_t argc, char *argv[]); + +/** + * Find the specified function in argv[0] from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param name the name of the function to execute. + * to indicate the module name via: $module_name$function_name + * or just a function name: function_name + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the specified function is called, false otherwise and + * exception will be thrown, the caller can call wasm_runtime_get_exception + * to get the exception info. + */ +bool +wasm_application_execute_func(wasm_module_inst_t module_inst, + const char *name, int32_t argc, char *argv[]); +/** + * Get exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + * + * @return the exception string + */ +const char * +wasm_runtime_get_exception(wasm_module_inst_t module_inst); + +/** + * Set exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + * + * @param exception the exception string + */ +void +wasm_runtime_set_exception(wasm_module_inst_t module_inst, + const char *exception); + +/** + * Clear exception info of the WASM module instance. + * + * @param module_inst the WASM module instance + */ +void +wasm_runtime_clear_exception(wasm_module_inst_t module_inst); + +/** + * Set custom data to WASM module instance. + * + * @param module_inst the WASM module instance + * @param custom_data the custom data to be set + */ +void +wasm_runtime_set_custom_data(wasm_module_inst_t module_inst, + void *custom_data); +/** + * Get the custom data within a WASM module instance. + * + * @param module_inst the WASM module instance + * + * @return the custom data (NULL if not set yet) + */ +void * +wasm_runtime_get_custom_data(wasm_module_inst_t module_inst); + +/** + * Allocate memory from the heap of WASM module instance + * + * @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. Note that + * it is not an absolute address. + * Return non-zero if success, zero if failed. + */ +uint32_t +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 + * + * @param module_inst the WASM module instance which contains heap + * @param ptr the pointer to free + */ +void +wasm_runtime_module_free(wasm_module_inst_t module_inst, uint32_t ptr); + +/** + * Allocate memory from the heap of WASM module instance and initialize + * the memory with src + * + * @param module_inst the WASM module instance which contains heap + * @param src the source data to copy + * @param size the size of the source data + * + * @return the allocated memory address, which is a relative offset to the + * base address of the module instance's memory space. Note that + * it is not an absolute address. + * Return non-zero if success, zero if failed. + */ +uint32_t +wasm_runtime_module_dup_data(wasm_module_inst_t module_inst, + const char *src, uint32_t size); + +/** + * Validate the app address, check whether it belongs to WASM module + * instance's address space, or in its heap space or memory space. + * + * @param module_inst the WASM module instance + * @param app_offset the app address to validate, which is a relative address + * @param size the size bytes of the app address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +bool +wasm_runtime_validate_app_addr(wasm_module_inst_t module_inst, + uint32_t app_offset, uint32_t size); + +/** + * Similar to wasm_runtime_validate_app_addr(), except that the size parameter + * is not provided. This function validates the app string address, check whether it + * belongs to WASM module instance's address space, or in its heap space or + * memory space. Moreover, it checks whether it is the offset of a string that + * is end with '\0'. + * @param module_inst the WASM module instance + * @param app_str_offset the app address of the string to validate, which is a + * relative address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +bool +wasm_runtime_validate_app_str_addr(wasm_module_inst_t module_inst, + uint32_t app_str_offset); + +/** + * Validate the native address, check whether it belongs to WASM module + * instance's address space, or in its heap space or memory space. + * + * @param module_inst the WASM module instance + * @param native_ptr the native address to validate, which is an absolute + * address + * @param size the size bytes of the app address + * + * @return true if success, false otherwise. If failed, an exception will + * be thrown. + */ +bool +wasm_runtime_validate_native_addr(wasm_module_inst_t module_inst, + void *native_ptr, uint32_t size); + +/** + * Convert app address(relative address) to native address(absolute address) + * + * @param module_inst the WASM module instance + * @param app_offset the app adress + * + * @return the native address converted + */ +void* +wasm_runtime_addr_app_to_native(wasm_module_inst_t module_inst, + uint32_t app_offset); + +/** + * Convert native address(absolute address) to app address(relative address) + * + * @param module_inst the WASM module instance + * @param native_ptr the native address + * + * @return the app address converted + */ +uint32_t +wasm_runtime_addr_native_to_app(wasm_module_inst_t module_inst, + void *native_ptr); + +/** + * Get the app address range (relative address) that a app address belongs to + * + * @param module_inst the WASM module instance + * @param app_offset the app address to retrieve + * @param p_app_start_offset buffer to output the app start offset if not NULL + * @param p_app_end_offset buffer to output the app end offset if not NULL + * + * @return true if success, false otherwise. + */ +bool +wasm_runtime_get_app_addr_range(wasm_module_inst_t module_inst, + uint32_t app_offset, + uint32_t *p_app_start_offset, + uint32_t *p_app_end_offset); + +/** + * Get the native address range (absolute address) that a native address belongs to + * + * @param module_inst the WASM module instance + * @param native_ptr the native address to retrieve + * @param p_native_start_addr buffer to output the native start address if not NULL + * @param p_native_end_addr buffer to output the native end address if not NULL + * + * @return true if success, false otherwise. + */ +bool +wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst, + uint8_t *native_ptr, + 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); + +/** + * Register native functions with same module name, similar to + * wasm_runtime_register_natives, the difference is that runtime passes raw + * arguments to native API, which means that the native API should be defined as: + * void foo(wasm_exec_env_t exec_env, uint64 *args); + * and native API should extract arguments one by one from args array with macro + * native_raw_get_arg + * and write the return value back to args[0] with macro + * native_raw_return_type and native_raw_set_return + */ +bool wasm_runtime_register_natives_raw(const char *module_name, + NativeSymbol *native_symbols, + uint32_t n_native_symbols); + +/** + * Get attachment of native function from execution environment + * + * @param exec_env the execution environment to retrieve + * + * @return the attachment of native function + */ +void * +wasm_runtime_get_function_attachment(wasm_exec_env_t exec_env); + +/** + * Set user data to execution environment. + * + * @param exec_env the execution environment + * @param user_data the user data to be set + */ +void +wasm_runtime_set_user_data(wasm_exec_env_t exec_env, + void *user_data); +/** + * Get the user data within execution environment. + * + * @param exec_env the execution environment + * + * @return the user data (NULL if not set yet) + */ +void * +wasm_runtime_get_user_data(wasm_exec_env_t exec_env); + +#if WASM_ENABLE_THREAD_MGR != 0 +/* wasm thread callback function type */ +typedef void* (*wasm_thread_callback_t)(wasm_exec_env_t, void *); +/* wasm thread type */ +typedef uintptr_t wasm_thread_t; + +/** + * Set the max thread num per cluster. + * + * @param num maximum thread num + */ +void +wasm_runtime_set_max_thread_num(uint32_t num); + +/** + * spawn a new exec_env, the spawned exec_env + * can be used in other threads + * + * @param num the original exec_env + * + * @return the spawned exec_env if success, NULL otherwise + */ +wasm_exec_env_t +wasm_runtime_spawn_exec_env(wasm_exec_env_t exec_env); + +/** + * Destroy the spawned exec_env + * + * @param exec_env the spawned exec_env + */ +void +wasm_runtime_destroy_spawned_exec_env(wasm_exec_env_t exec_env); + +/** + * spawn a thread from the given exec_env + * + * @param exec_env the original exec_env + * @param tid thread id to be returned to the caller + * @param callback the callback function provided by the user + * @param arg the arguments passed to the callback + * + * @return 0 if success, -1 otherwise + */ +int32_t +wasm_runtime_spawn_thread(wasm_exec_env_t exec_env, wasm_thread_t *tid, + wasm_thread_callback_t callback, void *arg); + +/** + * waits a spawned thread to terminate + * + * @param tid thread id + * @param retval if not NULL, output the return value of the thread + * + * @return 0 if success, error number otherwise + */ +int32_t +wasm_runtime_join_thread(wasm_thread_t tid, void **retval); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_EXPORT_H */ diff --git a/wamr/core/iwasm/interpreter/iwasm_interp.cmake b/wamr/core/iwasm/interpreter/iwasm_interp.cmake new file mode 100644 index 0000000..e6e52e4 --- /dev/null +++ b/wamr/core/iwasm/interpreter/iwasm_interp.cmake @@ -0,0 +1,29 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (IWASM_INTERP_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_INTERP=1) + +include_directories(${IWASM_INTERP_DIR}) + +if (WAMR_BUILD_FAST_INTERP EQUAL 1) + set (INTERPRETER "wasm_interp_fast.c") +else () + set (INTERPRETER "wasm_interp_classic.c") +endif () + +if (WAMR_BUILD_MINI_LOADER EQUAL 1) + set (LOADER "wasm_mini_loader.c") +else () + set (LOADER "wasm_loader.c") +endif () + +file (GLOB_RECURSE source_all + ${IWASM_INTERP_DIR}/${LOADER} + ${IWASM_INTERP_DIR}/wasm_runtime.c + ${IWASM_INTERP_DIR}/${INTERPRETER} +) + +set (IWASM_INTERP_SOURCE ${source_all}) + diff --git a/wamr/core/iwasm/interpreter/wasm.h b/wamr/core/iwasm/interpreter/wasm.h new file mode 100644 index 0000000..3696c2e --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm.h @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_H_ +#define _WASM_H_ + +#include "bh_platform.h" +#include "bh_hashmap.h" +#include "bh_assert.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Value Type */ +#define VALUE_TYPE_I32 0x7F +#define VALUE_TYPE_I64 0X7E +#define VALUE_TYPE_F32 0x7D +#define VALUE_TYPE_F64 0x7C +#define VALUE_TYPE_VOID 0x40 +/* Used by AOT */ +#define VALUE_TYPE_I1 0x41 +/* Used by loader to represent any type of i32/i64/f32/f64 */ +#define VALUE_TYPE_ANY 0x42 + +/* Table Element Type */ +#define TABLE_ELEM_TYPE_ANY_FUNC 0x70 + +#define DEFAULT_NUM_BYTES_PER_PAGE 65536 + +#define INIT_EXPR_TYPE_I32_CONST 0x41 +#define INIT_EXPR_TYPE_I64_CONST 0x42 +#define INIT_EXPR_TYPE_F32_CONST 0x43 +#define INIT_EXPR_TYPE_F64_CONST 0x44 +#define INIT_EXPR_TYPE_GET_GLOBAL 0x23 +#define INIT_EXPR_TYPE_ERROR 0xff + +#define WASM_MAGIC_NUMBER 0x6d736100 +#define WASM_CURRENT_VERSION 1 + +#define SECTION_TYPE_USER 0 +#define SECTION_TYPE_TYPE 1 +#define SECTION_TYPE_IMPORT 2 +#define SECTION_TYPE_FUNC 3 +#define SECTION_TYPE_TABLE 4 +#define SECTION_TYPE_MEMORY 5 +#define SECTION_TYPE_GLOBAL 6 +#define SECTION_TYPE_EXPORT 7 +#define SECTION_TYPE_START 8 +#define SECTION_TYPE_ELEM 9 +#define SECTION_TYPE_CODE 10 +#define SECTION_TYPE_DATA 11 +#if WASM_ENABLE_BULK_MEMORY != 0 +#define SECTION_TYPE_DATACOUNT 12 +#endif + +#define IMPORT_KIND_FUNC 0 +#define IMPORT_KIND_TABLE 1 +#define IMPORT_KIND_MEMORY 2 +#define IMPORT_KIND_GLOBAL 3 + +#define EXPORT_KIND_FUNC 0 +#define EXPORT_KIND_TABLE 1 +#define EXPORT_KIND_MEMORY 2 +#define EXPORT_KIND_GLOBAL 3 + +#define LABEL_TYPE_BLOCK 0 +#define LABEL_TYPE_LOOP 1 +#define LABEL_TYPE_IF 2 +#define LABEL_TYPE_FUNCTION 3 + +typedef struct WASMModule WASMModule; +typedef struct WASMFunction WASMFunction; +typedef struct WASMGlobal WASMGlobal; + +typedef union WASMValue { + int32 i32; + uint32 u32; + int64 i64; + uint64 u64; + float32 f32; + float64 f64; + uintptr_t addr; +} WASMValue; + +typedef struct InitializerExpression { + /* type of INIT_EXPR_TYPE_XXX */ + uint8 init_expr_type; + union { + int32 i32; + int64 i64; + float32 f32; + float64 f64; + uint32 global_index; + } u; +} InitializerExpression; + +typedef struct WASMType { + uint16 param_count; + uint16 result_count; + uint16 param_cell_num; + uint16 ret_cell_num; + /* types of params and results */ + uint8 types[1]; +} WASMType; + +typedef struct WASMTable { + uint8 elem_type; + uint32 flags; + uint32 init_size; + /* specified if (flags & 1), else it is 0x10000 */ + uint32 max_size; +} WASMTable; + +typedef struct WASMMemory { + uint32 flags; + uint32 num_bytes_per_page; + uint32 init_page_count; + uint32 max_page_count; +} WASMMemory; + +typedef struct WASMTableImport { + char *module_name; + char *field_name; + uint8 elem_type; + uint32 flags; + uint32 init_size; + /* specified if (flags & 1), else it is 0x10000 */ + uint32 max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMTable *import_table_linked; +#endif +} WASMTableImport; + +typedef struct WASMMemoryImport { + char *module_name; + char *field_name; + uint32 flags; + uint32 num_bytes_per_page; + uint32 init_page_count; + uint32 max_page_count; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMMemory *import_memory_linked; +#endif +} WASMMemoryImport; + +typedef struct WASMFunctionImport { + char *module_name; + char *field_name; + /* function type */ + WASMType *func_type; + /* native function pointer after linked */ + void *func_ptr_linked; + /* signature from registered native symbols */ + const char *signature; + /* attachment */ + void *attachment; + bool call_conv_raw; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModule *import_module; + WASMFunction *import_func_linked; +#endif +} WASMFunctionImport; + +typedef struct WASMGlobalImport { + char *module_name; + char *field_name; + uint8 type; + bool is_mutable; + /* global data after linked */ + WASMValue global_data_linked; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* imported function pointer after linked */ + // TODO: remove if not necessary + WASMModule *import_module; + WASMGlobal *import_global_linked; +#endif +} WASMGlobalImport; + +typedef struct WASMImport { + uint8 kind; + union { + WASMFunctionImport function; + WASMTableImport table; + WASMMemoryImport memory; + WASMGlobalImport global; + struct { + char *module_name; + char *field_name; + } names; + } u; +} WASMImport; + +typedef struct WASMFunction { + /* the type of function */ + WASMType *func_type; + uint32 local_count; + uint8 *local_types; + + /* cell num of parameters */ + uint16 param_cell_num; + /* cell num of return type */ + uint16 ret_cell_num; + /* cell num of local variables */ + uint16 local_cell_num; + /* offset of each local, including function parameters + and local variables */ + uint16 *local_offsets; + + uint32 max_stack_cell_num; + uint32 max_block_num; + /* Whether function has opcode memory.grow */ + bool has_op_memory_grow; + /* Whether function has opcode call or + call_indirect */ + bool has_op_func_call; + uint32 code_size; + uint8 *code; +#if WASM_ENABLE_FAST_INTERP != 0 + uint32 code_compiled_size; + uint8 *code_compiled; + uint8 *consts; + uint32 const_cell_num; +#endif +} WASMFunction; + +typedef struct WASMGlobal { + uint8 type; + bool is_mutable; + InitializerExpression init_expr; +} WASMGlobal; + +typedef struct WASMExport { + char *name; + uint8 kind; + uint32 index; +} WASMExport; + +typedef struct WASMTableSeg { + uint32 table_index; + InitializerExpression base_offset; + uint32 function_count; + uint32 *func_indexes; +} WASMTableSeg; + +typedef struct WASMDataSeg { + uint32 memory_index; + InitializerExpression base_offset; + uint32 data_length; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive; +#endif + uint8 *data; +} WASMDataSeg; + +typedef struct BlockAddr { + const uint8 *start_addr; + uint8 *else_addr; + uint8 *end_addr; +} BlockAddr; + +#if WASM_ENABLE_LIBC_WASI != 0 +typedef struct WASIArguments { + const char **dir_list; + uint32 dir_count; + const char **map_dir_list; + uint32 map_dir_count; + const char **env; + uint32 env_count; + char **argv; + uint32 argc; +} WASIArguments; +#endif + +typedef struct StringNode { + struct StringNode *next; + char *str; +} StringNode, *StringList; + +typedef struct WASMModule { + /* Module type, for module loaded from WASM bytecode binary, + this field is Wasm_Module_Bytecode; + for module loaded from AOT file, this field is + Wasm_Module_AoT, and this structure should be treated as + AOTModule structure. */ + uint32 module_type; + + uint32 type_count; + uint32 import_count; + uint32 function_count; + uint32 table_count; + uint32 memory_count; + uint32 global_count; + uint32 export_count; + uint32 table_seg_count; + /* data seg count read from data segment section */ + uint32 data_seg_count; +#if WASM_ENABLE_BULK_MEMORY != 0 + /* data count read from datacount section */ + uint32 data_seg_count1; +#endif + + uint32 import_function_count; + uint32 import_table_count; + uint32 import_memory_count; + uint32 import_global_count; + + WASMImport *import_functions; + WASMImport *import_tables; + WASMImport *import_memories; + WASMImport *import_globals; + + WASMType **types; + WASMImport *imports; + WASMFunction **functions; + WASMTable *tables; + WASMMemory *memories; + WASMGlobal *globals; + WASMExport *exports; + WASMTableSeg *table_segments; + WASMDataSeg **data_segments; + uint32 start_function; + + /* the index of auxiliary __data_end global, + -1 means unexported */ + uint32 aux_data_end_global_index; + /* auxiliary __data_end exported by wasm app */ + uint32 aux_data_end; + + /* the index of auxiliary __heap_base global, + -1 means unexported */ + uint32 aux_heap_base_global_index; + /* auxiliary __heap_base exported by wasm app */ + uint32 aux_heap_base; + + /* the index of auxiliary stack top global, + -1 means unexported */ + uint32 aux_stack_top_global_index; + /* auxiliary stack bottom resolved */ + uint32 aux_stack_bottom; + /* auxiliary stack size resolved */ + uint32 aux_stack_size; + + /* the index of malloc/free function, + -1 means unexported */ + uint32 malloc_function; + uint32 free_function; + + /* Whether there is possible memory grow, e.g. memory.grow opcode */ + bool possible_memory_grow; + + StringList const_str_list; + +#if WASM_ENABLE_LIBC_WASI != 0 + WASIArguments wasi_args; + bool is_wasi_module; +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 + // TODO: mutex ? mutli-threads ? + bh_list import_module_list_head; + bh_list *import_module_list; +#endif +} WASMModule; + +typedef struct BlockType { + /* Block type may be expressed in one of two forms: + * either by the type of the single return value or + * by a type index of module. + */ + union { + uint8 value_type; + WASMType *type; + } u; + bool is_value_type; +} BlockType; + +typedef struct WASMBranchBlock { + uint8 label_type; + uint32 cell_num; + uint8 *target_addr; + uint32 *frame_sp; +} WASMBranchBlock; + +/* Execution environment, e.g. stack info */ +/** + * Align an unsigned value on a alignment boundary. + * + * @param v the value to be aligned + * @param b the alignment boundary (2, 4, 8, ...) + * + * @return the aligned value + */ +inline static unsigned +align_uint (unsigned v, unsigned b) +{ + unsigned m = b - 1; + return (v + m) & ~m; +} + +/** + * Return the hash value of c string. + */ +inline static uint32 +wasm_string_hash(const char *str) +{ + unsigned h = (unsigned)strlen(str); + const uint8 *p = (uint8*)str; + const uint8 *end = p + h; + + while (p != end) + h = ((h << 5) - h) + *p++; + return h; +} + +/** + * Whether two c strings are equal. + */ +inline static bool +wasm_string_equal(const char *s1, const char *s2) +{ + return strcmp(s1, s2) == 0 ? true : false; +} + +/** + * Return the byte size of value type. + * + */ +inline static uint32 +wasm_value_type_size(uint8 value_type) +{ + switch (value_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + return sizeof(int32); + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + return sizeof(int64); + default: + bh_assert(0); + } + return 0; +} + +inline static uint16 +wasm_value_type_cell_num(uint8 value_type) +{ + if (value_type == VALUE_TYPE_VOID) + return 0; + else if (value_type == VALUE_TYPE_I32 + || value_type == VALUE_TYPE_F32) + return 1; + else if (value_type == VALUE_TYPE_I64 + || value_type == VALUE_TYPE_F64) + return 2; + else { + bh_assert(0); + } + return 0; +} + +inline static uint32 +wasm_get_cell_num(const uint8 *types, uint32 type_count) +{ + uint32 cell_num = 0; + uint32 i; + for (i = 0; i < type_count; i++) + cell_num += wasm_value_type_cell_num(types[i]); + return cell_num; +} + +inline static bool +wasm_type_equal(const WASMType *type1, const WASMType *type2) +{ + return (type1->param_count == type2->param_count + && type1->result_count == type2->result_count + && memcmp(type1->types, type2->types, + (uint32)(type1->param_count + + type1->result_count)) == 0) + ? true : false; +} + +static inline uint32 +block_type_get_param_types(BlockType *block_type, + uint8 **p_param_types) +{ + uint32 param_count = 0; + if (!block_type->is_value_type) { + WASMType *wasm_type = block_type->u.type; + *p_param_types = wasm_type->types; + param_count = wasm_type->param_count; + } + else { + *p_param_types = NULL; + param_count = 0; + } + + return param_count; +} + +static inline uint32 +block_type_get_result_types(BlockType *block_type, + uint8 **p_result_types) +{ + uint32 result_count = 0; + if (block_type->is_value_type) { + if (block_type->u.value_type != VALUE_TYPE_VOID) { + *p_result_types = &block_type->u.value_type; + result_count = 1; + } + } + else { + WASMType *wasm_type = block_type->u.type; + *p_result_types = wasm_type->types + wasm_type->param_count; + result_count = wasm_type->result_count; + } + return result_count; +} + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* end of _WASM_H_ */ diff --git a/wamr/core/iwasm/interpreter/wasm_interp.h b/wamr/core/iwasm/interpreter/wasm_interp.h new file mode 100644 index 0000000..c3c2c1b --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_interp.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_INTERP_H +#define _WASM_INTERP_H + +#include "wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct WASMModuleInstance; +struct WASMFunctionInstance; +struct WASMExecEnv; + +typedef struct WASMInterpFrame { + /* The frame of the caller that are calling the current function. */ + struct WASMInterpFrame *prev_frame; + + /* The current WASM function. */ + struct WASMFunctionInstance *function; + + /* Instruction pointer of the bytecode array. */ + uint8 *ip; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* return offset of the first return value of current frame. + the callee will put return values here continuously */ + uint32 ret_offset; + uint32 *lp; + uint32 operand[1]; +#else + /* Operand stack top pointer of the current frame. The bottom of + the stack is the next cell after the last local variable. */ + uint32 *sp_bottom; + uint32 *sp_boundary; + uint32 *sp; + + WASMBranchBlock *csp_bottom; + WASMBranchBlock *csp_boundary; + WASMBranchBlock *csp; + + /* Frame data, the layout is: + lp: param_cell_count + local_cell_count + sp_bottom to sp_boundary: stack of data + csp_bottom to csp_boundary: stack of block + ref to frame end: data types of local vairables and stack data + */ + uint32 lp[1]; +#endif +} WASMInterpFrame; + +/** + * Calculate the size of interpreter area of frame of a function. + * + * @param all_cell_num number of all cells including local variables + * and the working stack slots + * + * @return the size of interpreter area of the frame + */ +static inline unsigned +wasm_interp_interp_frame_size(unsigned all_cell_num) +{ + return align_uint((uint32)offsetof(WASMInterpFrame, lp) + + all_cell_num * 5, 4); +} + +void +wasm_interp_call_wasm(struct WASMModuleInstance *module_inst, + struct WASMExecEnv *exec_env, + struct WASMFunctionInstance *function, + uint32 argc, uint32 argv[]); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_INTERP_H */ diff --git a/wamr/core/iwasm/interpreter/wasm_interp_classic.c b/wamr/core/iwasm/interpreter/wasm_interp_classic.c new file mode 100644 index 0000000..ec9a102 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_interp_classic.c @@ -0,0 +1,3344 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_interp.h" +#include "bh_log.h" +#include "wasm_runtime.h" +#include "wasm_opcode.h" +#include "wasm_loader.h" +#include "../common/wasm_exec_env.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +typedef int32 CellType_I32; +typedef int64 CellType_I64; +typedef float32 CellType_F32; +typedef float64 CellType_F64; + +#define BR_TABLE_TMP_BUF_LEN 32 + +/* 64-bit Memory accessors. */ +#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 +#define PUT_I64_TO_ADDR(addr, value) do { \ + *(int64*)(addr) = (int64)(value); \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) do { \ + *(float64*)(addr) = (float64)(value); \ + } while (0) + +#define GET_I64_FROM_ADDR(addr) (*(int64*)(addr)) +#define GET_F64_FROM_ADDR(addr) (*(float64*)(addr)) + +/* For STORE opcodes */ +#define STORE_I64 PUT_I64_TO_ADDR +#define STORE_U32(addr, value) do { \ + *(uint32*)(addr) = (uint32)(value); \ + } while (0) +#define STORE_U16(addr, value) do { \ + *(uint16*)(addr) = (uint16)(value); \ + } while (0) + +/* For LOAD opcodes */ +#define LOAD_I64(addr) (*(int64*)(addr)) +#define LOAD_F64(addr) (*(float64*)(addr)) +#define LOAD_I32(addr) (*(int32*)(addr)) +#define LOAD_U32(addr) (*(uint32*)(addr)) +#define LOAD_I16(addr) (*(int16*)(addr)) +#define LOAD_U16(addr) (*(uint16*)(addr)) + +#else /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ +#define PUT_I64_TO_ADDR(addr, value) do { \ + union { int64 val; uint32 parts[2]; } u; \ + u.val = (int64)(value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) do { \ + union { float64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + +static inline int64 +GET_I64_FROM_ADDR(uint32 *addr) +{ + union { int64 val; uint32 parts[2]; } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +static inline float64 +GET_F64_FROM_ADDR (uint32 *addr) +{ + union { float64 val; uint32 parts[2]; } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +/* For STORE opcodes */ +#define STORE_I64(addr, value) do { \ + uintptr_t addr1 = (uintptr_t)(addr); \ + union { int64 val; uint32 u32[2]; \ + uint16 u16[4]; uint8 u8[8]; } u; \ + if ((addr1 & (uintptr_t)7) == 0) \ + *(int64*)(addr) = (int64)(value); \ + else { \ + u.val = (int64)(value); \ + if ((addr1 & (uintptr_t)3) == 0) { \ + ((uint32*)(addr))[0] = u.u32[0]; \ + ((uint32*)(addr))[1] = u.u32[1]; \ + } \ + else if ((addr1 & (uintptr_t)1) == 0) { \ + ((uint16*)(addr))[0] = u.u16[0]; \ + ((uint16*)(addr))[1] = u.u16[1]; \ + ((uint16*)(addr))[2] = u.u16[2]; \ + ((uint16*)(addr))[3] = u.u16[3]; \ + } \ + else { \ + int32 t; \ + for (t = 0; t < 8; t++) \ + ((uint8*)(addr))[t] = u.u8[t]; \ + } \ + } \ + } while (0) + +#define STORE_U32(addr, value) do { \ + uintptr_t addr1 = (uintptr_t)(addr); \ + union { uint32 val; \ + uint16 u16[2]; uint8 u8[4]; } u; \ + if ((addr1 & (uintptr_t)3) == 0) \ + *(uint32*)(addr) = (uint32)(value); \ + else { \ + u.val = (uint32)(value); \ + if ((addr1 & (uintptr_t)1) == 0) { \ + ((uint16*)(addr))[0] = u.u16[0]; \ + ((uint16*)(addr))[1] = u.u16[1]; \ + } \ + else { \ + ((uint8*)(addr))[0] = u.u8[0]; \ + ((uint8*)(addr))[1] = u.u8[1]; \ + ((uint8*)(addr))[2] = u.u8[2]; \ + ((uint8*)(addr))[3] = u.u8[3]; \ + } \ + } \ + } while (0) + +#define STORE_U16(addr, value) do { \ + union { uint16 val; uint8 u8[2]; } u; \ + u.val = (uint16)(value); \ + ((uint8*)(addr))[0] = u.u8[0]; \ + ((uint8*)(addr))[1] = u.u8[1]; \ + } while (0) + +/* For LOAD opcodes */ +static inline int64 +LOAD_I64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { int64 val; uint32 u32[2]; + uint16 u16[4]; uint8 u8[8]; } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(int64*)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32*)addr)[0]; + u.u32[1] = ((uint32*)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + u.u16[2] = ((uint16*)addr)[2]; + u.u16[3] = ((uint16*)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8*)addr)[t]; + } + return u.val; +} + +static inline float64 +LOAD_F64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { float64 val; uint32 u32[2]; + uint16 u16[4]; uint8 u8[8]; } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(float64*)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32*)addr)[0]; + u.u32[1] = ((uint32*)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + u.u16[2] = ((uint16*)addr)[2]; + u.u16[3] = ((uint16*)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8*)addr)[t]; + } + return u.val; +} + +static inline int32 +LOAD_I32(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { int32 val; uint16 u16[2]; uint8 u8[4]; } u; + if ((addr1 & (uintptr_t)3) == 0) + return *(int32*)addr; + + if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + } + else { + u.u8[0] = ((uint8*)addr)[0]; + u.u8[1] = ((uint8*)addr)[1]; + u.u8[2] = ((uint8*)addr)[2]; + u.u8[3] = ((uint8*)addr)[3]; + } + return u.val; +} + +static inline int16 +LOAD_I16(void *addr) +{ + union { int16 val; uint8 u8[2]; } u; + u.u8[0] = ((uint8*)addr)[0]; + u.u8[1] = ((uint8*)addr)[1]; + return u.val; +} + +#define LOAD_U32(addr) ((uint32)LOAD_I32(addr)) +#define LOAD_U16(addr) ((uint16)LOAD_I16(addr)) + +#endif /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ + +#define CHECK_MEMORY_OVERFLOW(bytes) do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + if (offset1 + bytes <= (uint64)linear_mem_size) \ + /* If offset1 is in valid range, maddr must also be in valid range, \ + no need to check it again. */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do { \ + uint64 offset1 = (uint32)(start); \ + if (offset1 + bytes <= (uint64)linear_mem_size) \ + /* App heap space is not valid space for bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_ATOMIC_MEMORY_ACCESS() do { \ + if (((uintptr_t)maddr & ((1 << align) - 1)) != 0) \ + goto unaligned_atomic; \ + } while (0) + +static inline uint32 +rotl32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n<>( (-c)&mask )); +} + +static inline uint32 +rotr32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n>>c) | (n<<( (-c)&mask )); +} + +static inline uint64 +rotl64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n<>( (-c)&mask )); +} + +static inline uint64 +rotr64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n>>c) | (n<<( (-c)&mask )); +} + +static inline double +wa_fmax(double a, double b) +{ + double c = fmax(a, b); + if (c==0 && a==b) + return signbit(a) ? b : a; + return c; +} + +static inline double +wa_fmin(double a, double b) +{ + double c = fmin(a, b); + if (c==0 && a==b) + return signbit(a) ? a : b; + return c; +} + +static inline uint32 +clz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +ctz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +ctz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +popcount32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static inline uint32 +popcount64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static uint64 +read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 bcnt = 0; + uint64 byte; + + while (true) { + byte = buf[*p_offset]; + *p_offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + bcnt += 1; + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= - ((uint64)1 << shift); + } + return result; +} + +#define PUSH_I32(value) do { \ + *(int32*)frame_sp++ = (int32)(value); \ + } while (0) + +#define PUSH_F32(value) do { \ + *(float32*)frame_sp++ = (float32)(value); \ + } while (0) + +#define PUSH_I64(value) do { \ + PUT_I64_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } while (0) + +#define PUSH_F64(value) do { \ + PUT_F64_TO_ADDR(frame_sp, value); \ + frame_sp += 2; \ + } while (0) + +#define PUSH_CSP(_label_type, cell_num, _target_addr) do { \ + bh_assert(frame_csp < frame->csp_boundary); \ + frame_csp->label_type = _label_type; \ + frame_csp->cell_num = cell_num; \ + frame_csp->target_addr = _target_addr; \ + frame_csp->frame_sp = frame_sp; \ + frame_csp++; \ + } while (0) + +#define POP_I32() (--frame_sp, *(int32*)frame_sp) + +#define POP_F32() (--frame_sp, *(float32*)frame_sp) + +#define POP_I64() (frame_sp -= 2, GET_I64_FROM_ADDR(frame_sp)) + +#define POP_F64() (frame_sp -= 2, GET_F64_FROM_ADDR(frame_sp)) + +#define POP_CSP_CHECK_OVERFLOW(n) do { \ + bh_assert(frame_csp - n >= frame->csp_bottom); \ + } while (0) + +#define POP_CSP() do { \ + POP_CSP_CHECK_OVERFLOW(1); \ + --frame_csp; \ + } while (0) + +#define POP_CSP_N(n) do { \ + uint32 *frame_sp_old = frame_sp; \ + uint32 cell_num = 0; \ + POP_CSP_CHECK_OVERFLOW(n + 1); \ + frame_csp -= n; \ + frame_ip = (frame_csp - 1)->target_addr; \ + /* copy arity values of block */ \ + frame_sp = (frame_csp - 1)->frame_sp; \ + cell_num = (frame_csp - 1)->cell_num; \ + word_copy(frame_sp, frame_sp_old - cell_num, cell_num); \ + frame_sp += cell_num; \ + } while (0) + +/* Pop the given number of elements from the given frame's stack. */ +#define POP(N) do { \ + int n = (N); \ + frame_sp -= n; \ + } while (0) + +#define SYNC_ALL_TO_FRAME() do { \ + frame->sp = frame_sp; \ + frame->ip = frame_ip; \ + frame->csp = frame_csp; \ + } while (0) + +#define UPDATE_ALL_FROM_FRAME() do { \ + frame_sp = frame->sp; \ + frame_ip = frame->ip; \ + frame_csp = frame->csp; \ + } while (0) + +#define read_leb_int64(p, p_end, res) do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int64)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFFFFFFFFFF80LL; \ + p++; \ + break; \ + } \ + uint32 _off = 0; \ + res = (int64)read_leb(p, &_off, 64, true); \ + p += _off; \ +} while (0) + +#define read_leb_uint32(p, p_end, res) do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = _val; \ + p++; \ + break; \ + } \ + uint32 _off = 0; \ + res = (uint32)read_leb(p, &_off, 32, false); \ + p += _off; \ +} while (0) + +#define read_leb_int32(p, p_end, res) do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = (int32)_val; \ + if (_val & 0x40) \ + /* sign extend */ \ + res |= 0xFFFFFF80; \ + p++; \ + break; \ + } \ + uint32 _off = 0; \ + res = (int32)read_leb(p, &_off, 32, true); \ + p += _off; \ +} while (0) + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 +#define RECOVER_FRAME_IP_END() \ + frame_ip_end = wasm_get_func_code_end(cur_func) +#else +#define RECOVER_FRAME_IP_END() (void)0 +#endif + +#define RECOVER_CONTEXT(new_frame) do { \ + frame = (new_frame); \ + cur_func = frame->function; \ + prev_frame = frame->prev_frame; \ + frame_ip = frame->ip; \ + RECOVER_FRAME_IP_END(); \ + frame_lp = frame->lp; \ + frame_sp = frame->sp; \ + frame_csp = frame->csp; \ + } while (0) + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define GET_OPCODE() opcode = *(frame_ip - 1); +#else +#define GET_OPCODE() (void)0 +#endif + +#define DEF_OP_I_CONST(ctype, src_op_type) do { \ + ctype cval; \ + read_leb_##ctype(frame_ip, frame_ip_end, cval); \ + PUSH_##src_op_type(cval); \ + } while (0) + +#define DEF_OP_EQZ(src_op_type) do { \ + int32 val; \ + val = POP_##src_op_type() == 0; \ + PUSH_I32(val); \ + } while (0) + +#define DEF_OP_CMP(src_type, src_op_type, cond) do { \ + uint32 res; \ + src_type val1, val2; \ + val2 = (src_type)POP_##src_op_type(); \ + val1 = (src_type)POP_##src_op_type(); \ + res = val1 cond val2; \ + PUSH_I32(res); \ + } while (0) + +#define DEF_OP_BIT_COUNT(src_type, src_op_type, operation) do { \ + src_type val1, val2; \ + val1 = (src_type)POP_##src_op_type(); \ + val2 = (src_type)operation(val1); \ + PUSH_##src_op_type(val2); \ + } while (0) + +#define DEF_OP_NUMERIC(src_type1, src_type2, src_op_type, operation) do { \ + frame_sp -= sizeof(src_type2)/sizeof(uint32); \ + *(src_type1*)(frame_sp - sizeof(src_type1)/sizeof(uint32)) operation##= \ + *(src_type2*)(frame_sp); \ + } while (0) + +#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 +#define DEF_OP_NUMERIC_64 DEF_OP_NUMERIC +#else +#define DEF_OP_NUMERIC_64(src_type1, src_type2, src_op_type, operation) do {\ + src_type1 val1; \ + src_type2 val2; \ + frame_sp -= 2; \ + val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ + val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ + val1 operation##= val2; \ + PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ + } while (0) +#endif + +#define DEF_OP_NUMERIC2(src_type1, src_type2, src_op_type, operation) do { \ + frame_sp -= sizeof(src_type2)/sizeof(uint32); \ + *(src_type1*)(frame_sp - sizeof(src_type1)/sizeof(uint32)) operation##= \ + (*(src_type2*)(frame_sp) % 32); \ + } while (0) + +#define DEF_OP_NUMERIC2_64(src_type1, src_type2, src_op_type, operation) do { \ + src_type1 val1; \ + src_type2 val2; \ + frame_sp -= 2; \ + val1 = (src_type1)GET_##src_op_type##_FROM_ADDR(frame_sp - 2); \ + val2 = (src_type2)GET_##src_op_type##_FROM_ADDR(frame_sp); \ + val1 operation##= (val2 % 64); \ + PUT_##src_op_type##_TO_ADDR(frame_sp - 2, val1); \ + } while (0) + +#define DEF_OP_MATH(src_type, src_op_type, method) do { \ + src_type val; \ + val = POP_##src_op_type(); \ + PUSH_##src_op_type(method(val)); \ + } while (0) + +#define TRUNC_FUNCTION(func_name, src_type, dst_type, signed_type) \ +static dst_type \ +func_name(src_type src_value, src_type src_min, src_type src_max, \ + dst_type dst_min, dst_type dst_max, bool is_sign) \ +{ \ + dst_type dst_value = 0; \ + if (!isnan(src_value)) { \ + if (src_value <= src_min) \ + dst_value = dst_min; \ + else if (src_value >= src_max) \ + dst_value = dst_max; \ + else { \ + if (is_sign) \ + dst_value = (dst_type)(signed_type)src_value; \ + else \ + dst_value = (dst_type)src_value; \ + } \ + } \ + return dst_value; \ +} + +TRUNC_FUNCTION(trunc_f32_to_i32, float32, uint32, int32) +TRUNC_FUNCTION(trunc_f32_to_i64, float32, uint64, int64) +TRUNC_FUNCTION(trunc_f64_to_i32, float64, uint32, int32) +TRUNC_FUNCTION(trunc_f64_to_i64, float64, uint64, int64) + +static bool +trunc_f32_to_int(WASMModuleInstance *module, + uint32 *frame_sp, + float32 src_min, float32 src_max, + bool saturating, bool is_i32, bool is_sign) +{ + float32 src_value = POP_F32(); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return true; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return true; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f32_to_i32(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + PUSH_I32(dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f32_to_i64(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + PUSH_I64(dst_value_i64); + } + return false; +} + +static bool +trunc_f64_to_int(WASMModuleInstance *module, + uint32 *frame_sp, + float64 src_min, float64 src_max, + bool saturating, bool is_i32, bool is_sign) +{ + float64 src_value = POP_F64(); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return true; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return true; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f64_to_i32(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + PUSH_I32(dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f64_to_i64(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + PUSH_I64(dst_value_i64); + } + return false; +} + +#define DEF_OP_TRUNC_F32(min, max, is_i32, is_sign) do { \ + if (trunc_f32_to_int(module, frame_sp, min, max, \ + false, is_i32, is_sign)) \ + goto got_exception; \ + } while (0) + +#define DEF_OP_TRUNC_F64(min, max, is_i32, is_sign) do { \ + if (trunc_f64_to_int(module, frame_sp, min, max, \ + false, is_i32, is_sign)) \ + goto got_exception; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F32(min, max, is_i32, is_sign) do { \ + (void)trunc_f32_to_int(module, frame_sp, min, max, \ + true, is_i32, is_sign); \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F64(min, max, is_i32, is_sign) do { \ + (void)trunc_f64_to_int(module, frame_sp, min, max, \ + true, is_i32, is_sign); \ + } while (0) + +#define DEF_OP_CONVERT(dst_type, dst_op_type, \ + src_type, src_op_type) do { \ + dst_type value = (dst_type)(src_type)POP_##src_op_type(); \ + PUSH_##dst_op_type(value); \ + } while (0) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ + uint32 param_count = cur_func->param_count; \ + read_leb_uint32(frame_ip, frame_ip_end, local_idx); \ + bh_assert(local_idx < param_count + cur_func->local_count); \ + local_offset = cur_func->local_offsets[local_idx]; \ + if (local_idx < param_count) \ + local_type = cur_func->param_types[local_idx]; \ + else \ + local_type = cur_func->local_types[local_idx - param_count]; \ + } while (0) + +#define DEF_ATOMIC_RMW_OPCODE(OP_NAME, op) \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U: \ + { \ + uint32 readv, sval; \ + \ + sval = POP_I32(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint32)(*(uint8*)maddr); \ + *(uint8*)maddr = (uint8)(readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint32)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = LOAD_I32(maddr); \ + STORE_U32(maddr, readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + PUSH_I32(readv); \ + break; \ + } \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U: \ + { \ + uint64 readv, sval; \ + \ + sval = (uint64)POP_I64(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)(*(uint8*)maddr); \ + *(uint8*)maddr = (uint8)(readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_U32(maddr); \ + STORE_U32(maddr, (uint32)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else { \ + uint64 op_result; \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_I64(maddr); \ + op_result = readv op sval; \ + STORE_I64(maddr, op_result); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + PUSH_I64(readv); \ + break; \ + } + +static inline int32 +sign_ext_8_32(int8 val) +{ + if (val & 0x80) + return (int32)val | (int32)0xffffff00; + return val; +} + +static inline int32 +sign_ext_16_32(int16 val) +{ + if (val & 0x8000) + return (int32)val | (int32)0xffff0000; + return val; +} + +static inline int64 +sign_ext_8_64(int8 val) +{ + if (val & 0x80) + return (int64)val | (int64)0xffffffffffffff00; + return val; +} + +static inline int64 +sign_ext_16_64(int16 val) +{ + if (val & 0x8000) + return (int64)val | (int64)0xffffffffffff0000; + return val; +} + +static inline int64 +sign_ext_32_64(int32 val) +{ + if (val & (int32)0x80000000) + return (int64)val | (int64)0xffffffff00000000; + return val; +} + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + for (; num > 0; num--) + *dest++ = *src++; +} + +static inline WASMInterpFrame* +ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame) +{ + WASMInterpFrame *frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); + + if (frame) + frame->prev_frame = prev_frame; + else { + wasm_set_exception((WASMModuleInstance*)exec_env->module_inst, + "stack overflow"); + } + + return frame; +} + +static inline void +FREE_FRAME(WASMExecEnv *exec_env, WASMInterpFrame *frame) +{ + wasm_exec_env_free_wasm_frame(exec_env, frame); +} + +static void +wasm_interp_call_func_native(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + 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, + wasm_interp_interp_frame_size(local_cell_num), + prev_frame))) + return; + + frame->function = cur_func; + frame->ip = NULL; + frame->sp = frame->lp + local_cell_num; + + wasm_exec_env_set_cur_frame(exec_env, frame); + + 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; + } + + if (!func_import->call_conv_raw) { + ret = wasm_runtime_invoke_native(exec_env, func_import->func_ptr_linked, + func_import->func_type, func_import->signature, + func_import->attachment, + frame->lp, cur_func->param_cell_num, argv_ret); + } + else { + ret = wasm_runtime_invoke_native_raw(exec_env, func_import->func_ptr_linked, + func_import->func_type, func_import->signature, + func_import->attachment, + frame->lp, cur_func->param_cell_num, argv_ret); + } + + if (!ret) + return; + + if (cur_func->ret_cell_num == 1) { + prev_frame->sp[0] = argv_ret[0]; + prev_frame->sp++; + } + else if (cur_func->ret_cell_num == 2) { + prev_frame->sp[0] = argv_ret[0]; + prev_frame->sp[1] = argv_ret[1]; + prev_frame->sp += 2; + } + + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "fail to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* replace exec_env's module_inst with sub_module_inst so we can + call it */ + exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, + sub_func_inst, prev_frame); + + /* restore ip and module_inst */ + prev_frame->ip = ip; + exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; + + /* transfer exception if it is thrown */ + if (wasm_get_exception(sub_module_inst)) { + bh_memcpy_s(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + sub_module_inst->cur_exception, + sizeof(sub_module_inst->cur_exception)); + } +} +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#define CHECK_SUSPEND_FLAGS() do { \ + if (exec_env->suspend_flags.flags != 0) { \ + if (exec_env->suspend_flags.flags & 0x01) { \ + /* terminate current thread */ \ + return; \ + } \ + /* TODO: support suspend and breakpoint */ \ + } \ + } while (0) +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + +#define HANDLE_OP(opcode) HANDLE_##opcode +#define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++] +#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() + +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ + +#define HANDLE_OP(opcode) case opcode +#define HANDLE_OP_END() continue + +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMMemoryInstance *memory = module->default_memory; + uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; + uint8 *global_data = module->global_data; + uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; + WASMTableInstance *table = module->default_table; + WASMType **wasm_types = module->module->types; + WASMGlobalInstance *globals = module->globals, *global; + uint8 opcode_IMPDEP = WASM_OP_IMPDEP; + WASMInterpFrame *frame = NULL; + /* Points to this special opcode so as to jump to the call_method_from_entry. */ + register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */ + register uint32 *frame_lp = NULL; /* cache of frame->lp */ + register uint32 *frame_sp = NULL; /* cache of frame->sp */ + WASMBranchBlock *frame_csp = NULL; + BlockAddr *cache_items; + uint8 *frame_ip_end = frame_ip + 1; + uint8 opcode; + uint32 *depths = NULL; + uint32 depth_buf[BR_TABLE_TMP_BUF_LEN]; + uint32 i, depth, cond, count, fidx, tidx, frame_size = 0; + uint64 all_cell_num = 0; + int32 didx, val; + uint8 *else_addr, *end_addr, *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + uint8 local_type, *global_addr; + uint32 cache_index, type_index, cell_num; + uint8 value_type; + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + #define HANDLE_OPCODE(op) &&HANDLE_##op + DEFINE_GOTO_TABLE (const void *, handle_table); + #undef HANDLE_OPCODE +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + switch (opcode) { +#else + FETCH_OPCODE_AND_DISPATCH (); +#endif + /* control instructions */ + HANDLE_OP (WASM_OP_UNREACHABLE): + wasm_set_exception(module, "unreachable"); + goto got_exception; + + HANDLE_OP (WASM_OP_NOP): + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_BLOCK): + read_leb_uint32(frame_ip, frame_ip_end, type_index); + cell_num = wasm_types[type_index]->ret_cell_num; + goto handle_op_block; + + HANDLE_OP (WASM_OP_BLOCK): + value_type = *frame_ip++; + cell_num = wasm_value_type_cell_num(value_type); +handle_op_block: + cache_index = ((uintptr_t)frame_ip) & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + end_addr = cache_items[0].end_addr; + } + else if (cache_items[1].start_addr == frame_ip) { + end_addr = cache_items[1].end_addr; + } + else if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache, + frame_ip, (uint8*)-1, + LABEL_TYPE_BLOCK, + &else_addr, &end_addr, + NULL, 0)) { + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + + PUSH_CSP(LABEL_TYPE_BLOCK, cell_num, end_addr); + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_LOOP): + read_leb_uint32(frame_ip, frame_ip_end, type_index); + cell_num = wasm_types[type_index]->param_cell_num; + goto handle_op_loop; + + HANDLE_OP (WASM_OP_LOOP): + value_type = *frame_ip++; + cell_num = wasm_value_type_cell_num(value_type); +handle_op_loop: + PUSH_CSP(LABEL_TYPE_LOOP, cell_num, frame_ip); + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_IF): + read_leb_uint32(frame_ip, frame_ip_end, type_index); + cell_num = wasm_types[type_index]->ret_cell_num; + goto handle_op_if; + + HANDLE_OP (WASM_OP_IF): + value_type = *frame_ip++; + cell_num = wasm_value_type_cell_num(value_type); +handle_op_if: + cache_index = ((uintptr_t)frame_ip) & (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1); + cache_items = exec_env->block_addr_cache[cache_index]; + if (cache_items[0].start_addr == frame_ip) { + else_addr = cache_items[0].else_addr; + end_addr = cache_items[0].end_addr; + } + else if (cache_items[1].start_addr == frame_ip) { + else_addr = cache_items[1].else_addr; + end_addr = cache_items[1].end_addr; + } + else if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache, + frame_ip, (uint8*)-1, + LABEL_TYPE_IF, + &else_addr, &end_addr, + NULL, 0)) { + wasm_set_exception(module, "find block address failed"); + goto got_exception; + } + + cond = (uint32)POP_I32(); + + PUSH_CSP(LABEL_TYPE_IF, cell_num, end_addr); + + /* condition of the if branch is false, else condition is met */ + if (cond == 0) { + /* if there is no else branch, go to the end addr */ + if (else_addr == NULL) { + POP_CSP(); + frame_ip = end_addr + 1; + } + /* if there is an else branch, go to the else addr */ + else + frame_ip = else_addr + 1; + } + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_ELSE): + /* comes from the if branch in WASM_OP_IF */ + frame_ip = (frame_csp - 1)->target_addr; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_END): + if (frame_csp > frame->csp_bottom + 1) { + POP_CSP(); + } + else { /* end of function, treat as WASM_OP_RETURN */ + frame_sp -= cur_func->ret_cell_num; + for (i = 0; i < cur_func->ret_cell_num; i++) { + *prev_frame->sp++ = frame_sp[i]; + } + goto return_func; + } + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); +label_pop_csp_n: + POP_CSP_N(depth); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR_IF): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, depth); + cond = (uint32)POP_I32(); + if (cond) + goto label_pop_csp_n; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR_TABLE): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, count); + if (count <= BR_TABLE_TMP_BUF_LEN) + depths = depth_buf; + else { + uint64 total_size = sizeof(uint32) * (uint64)count; + if (total_size >= UINT32_MAX + || !(depths = wasm_runtime_malloc((uint32)total_size))) { + wasm_set_exception(module, "allocate memory failed"); + goto got_exception; + } + } + for (i = 0; i < count; i++) { + read_leb_uint32(frame_ip, frame_ip_end, depths[i]); + } + read_leb_uint32(frame_ip, frame_ip_end, depth); + didx = POP_I32(); + if (didx >= 0 && (uint32)didx < count) { + depth = depths[didx]; + } + if (depths != depth_buf) { + wasm_runtime_free(depths); + depths = NULL; + } + goto label_pop_csp_n; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_RETURN): + frame_sp -= cur_func->ret_cell_num; + for (i = 0; i < cur_func->ret_cell_num; i++) { + *prev_frame->sp++ = frame_sp[i]; + } + goto return_func; + + HANDLE_OP (WASM_OP_CALL): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + read_leb_uint32(frame_ip, frame_ip_end, fidx); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + cur_func = module->functions + fidx; + goto call_func_from_interp; + + HANDLE_OP (WASM_OP_CALL_INDIRECT): + { + WASMType *cur_type, *cur_func_type; + WASMTableInstance *cur_table_inst; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + + /** + * type check. compiler will make sure all like + * (call_indirect (type $x) (i32.const 1)) + * the function type has to be defined in the module also + * no matter it is used or not + */ + read_leb_uint32(frame_ip, frame_ip_end, tidx); + if (tidx >= module->module->type_count) { + wasm_set_exception(module, "unknown type"); + goto got_exception; + } + cur_type = wasm_types[tidx]; + + /* to skip 0x00 here */ + frame_ip++; + val = POP_I32(); + + /* careful, it might be a table in another module */ + cur_table_inst = table; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table->table_inst_linked) { + cur_table_inst = table->table_inst_linked; + } +#endif + + if (val < 0 || val >= (int32)cur_table_inst->cur_size) { + wasm_set_exception(module, "undefined element"); + goto got_exception; + } + + fidx = ((uint32*)cur_table_inst->base_addr)[val]; + if (fidx == (uint32)-1) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + /* always call module own functions */ + cur_func = module->functions + fidx; + + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + cur_func_type = cur_func->u.func_import->func_type; + else + cur_func_type = cur_func->u.func->func_type; + if (!wasm_type_equal(cur_type, cur_func_type)) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } + + goto call_func_from_interp; + } + + /* parametric instructions */ + HANDLE_OP (WASM_OP_DROP): + { + frame_sp--; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_DROP_64): + { + frame_sp -= 2; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SELECT): + { + cond = (uint32)POP_I32(); + frame_sp--; + if (!cond) + *(frame_sp - 1) = *frame_sp; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SELECT_64): + { + cond = (uint32)POP_I32(); + frame_sp -= 2; + if (!cond) { + *(frame_sp - 2) = *frame_sp; + *(frame_sp - 1) = *(frame_sp + 1); + } + HANDLE_OP_END (); + } + + /* variable instructions */ + HANDLE_OP (WASM_OP_GET_LOCAL): + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + PUSH_I32(*(int32*)(frame_lp + local_offset)); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUSH_I64(GET_I64_FROM_ADDR(frame_lp + local_offset)); + break; + default: + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + + HANDLE_OP_END (); + } + + HANDLE_OP (EXT_OP_GET_LOCAL_FAST): + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUSH_I64(GET_I64_FROM_ADDR(frame_lp + (local_offset & 0x7F))); + else + PUSH_I32(*(int32*)(frame_lp + local_offset)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_LOCAL): + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *(int32*)(frame_lp + local_offset) = POP_I32(); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), POP_I64()); + break; + default: + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + + HANDLE_OP_END (); + } + + HANDLE_OP (EXT_OP_SET_LOCAL_FAST): + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUT_I64_TO_ADDR((uint32*)(frame_lp + (local_offset & 0x7F)), POP_I64()); + else + *(int32*)(frame_lp + local_offset) = POP_I32(); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_TEE_LOCAL): + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + + switch (local_type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *(int32*)(frame_lp + local_offset) = *(int32*)(frame_sp - 1); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), + GET_I64_FROM_ADDR(frame_sp - 2)); + break; + default: + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + + HANDLE_OP_END (); + } + + HANDLE_OP (EXT_OP_TEE_LOCAL_FAST): + { + local_offset = *frame_ip++; + if (local_offset & 0x80) + PUT_I64_TO_ADDR((uint32*)(frame_lp + (local_offset & 0x7F)), + GET_I64_FROM_ADDR(frame_sp - 2)); + else + *(int32*)(frame_lp + local_offset) = *(int32*)(frame_sp - 1); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_GET_GLOBAL): + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + PUSH_I32(*(uint32*)global_addr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_GET_GLOBAL_64): + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + PUSH_I64(GET_I64_FROM_ADDR((uint32*)global_addr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL): + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + *(int32*)global_addr = POP_I32(); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL_AUX_STACK): + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + if (*(uint32*)(frame_sp - 1) < exec_env->aux_stack_boundary) + goto out_of_bounds; + *(int32*)global_addr = POP_I32(); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL_64): + { + read_leb_uint32(frame_ip, frame_ip_end, global_idx); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + PUT_I64_TO_ADDR((uint32*)global_addr, POP_I64()); + HANDLE_OP_END (); + } + + /* memory load instructions */ + HANDLE_OP (WASM_OP_I32_LOAD): + HANDLE_OP (WASM_OP_F32_LOAD): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I32(LOAD_I32(maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD): + HANDLE_OP (WASM_OP_F64_LOAD): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(8); + PUSH_I64(LOAD_I64(maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I32_LOAD8_S): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I32(sign_ext_8_32(*(int8*)maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I32_LOAD8_U): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I32((uint32)(*(uint8*)maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I32_LOAD16_S): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I32(sign_ext_16_32(LOAD_I16(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I32_LOAD16_U): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I32((uint32)(LOAD_U16(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD8_S): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I64(sign_ext_8_64(*(int8*)maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD8_U): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(1); + PUSH_I64((uint64)(*(uint8*)maddr)); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD16_S): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I64(sign_ext_16_64(LOAD_I16(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD16_U): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(2); + PUSH_I64((uint64)(LOAD_U16(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD32_S): + { + uint32 offset, flags, addr; + + opcode = *(frame_ip - 1); + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I64(sign_ext_32_64(LOAD_I32(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_I64_LOAD32_U): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + PUSH_I64((uint64)(LOAD_U32(maddr))); + (void)flags; + HANDLE_OP_END(); + } + + /* memory store instructions */ + HANDLE_OP (WASM_OP_I32_STORE): + HANDLE_OP (WASM_OP_F32_STORE): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + frame_sp--; + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, frame_sp[1]); + (void)flags; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE): + HANDLE_OP (WASM_OP_F64_STORE): + { + uint32 offset, flags, addr; + + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + frame_sp -= 2; + addr = POP_I32(); + CHECK_MEMORY_OVERFLOW(8); + STORE_U32(maddr, frame_sp[1]); + STORE_U32(maddr + 4, frame_sp[2]); + (void)flags; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_STORE8): + HANDLE_OP (WASM_OP_I32_STORE16): + { + uint32 offset, flags, addr; + uint32 sval; + + opcode = *(frame_ip - 1); + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + sval = (uint32)POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_I32_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + *(uint8*)maddr = (uint8)sval; + } + else { + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + } + + (void)flags; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE8): + HANDLE_OP (WASM_OP_I64_STORE16): + HANDLE_OP (WASM_OP_I64_STORE32): + { + uint32 offset, flags, addr; + uint64 sval; + + opcode = *(frame_ip - 1); + read_leb_uint32(frame_ip, frame_ip_end, flags); + read_leb_uint32(frame_ip, frame_ip_end, offset); + sval = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_I64_STORE8) { + CHECK_MEMORY_OVERFLOW(1); + *(uint8*)maddr = (uint8)sval; + } + else if(opcode == WASM_OP_I64_STORE16) { + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + } + else { + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, (uint32)sval); + } + (void)flags; + HANDLE_OP_END (); + } + + /* memory size and memory grow instructions */ + HANDLE_OP (WASM_OP_MEMORY_SIZE): + { + uint32 reserved; + read_leb_uint32(frame_ip, frame_ip_end, reserved); + PUSH_I32(memory->cur_page_count); + (void)reserved; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_MEMORY_GROW): + { + uint32 reserved, delta, prev_page_count = memory->cur_page_count; + + read_leb_uint32(frame_ip, frame_ip_end, reserved); + delta = (uint32)POP_I32(); + + if (!wasm_enlarge_memory(module, delta)) { + /* fail to memory.grow, return -1 */ + PUSH_I32(-1); + } + else { + /* success, return previous page count */ + PUSH_I32(prev_page_count); + /* update the memory instance ptr */ + memory = module->default_memory; + linear_mem_size = num_bytes_per_page * memory->cur_page_count; + } + + (void)reserved; + HANDLE_OP_END (); + } + + /* constant instructions */ + HANDLE_OP (WASM_OP_I32_CONST): + DEF_OP_I_CONST(int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_CONST): + DEF_OP_I_CONST(int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONST): + { + uint8 *p_float = (uint8*)frame_sp++; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *frame_ip++; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_CONST): + { + uint8 *p_float = (uint8*)frame_sp++; + frame_sp++; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *frame_ip++; + HANDLE_OP_END (); + } + + /* comparison instructions of i32 */ + HANDLE_OP (WASM_OP_I32_EQZ): + DEF_OP_EQZ(I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_EQ): + DEF_OP_CMP(uint32, I32, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_NE): + DEF_OP_CMP(uint32, I32, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LT_S): + DEF_OP_CMP(int32, I32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LT_U): + DEF_OP_CMP(uint32, I32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GT_S): + DEF_OP_CMP(int32, I32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GT_U): + DEF_OP_CMP(uint32, I32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LE_S): + DEF_OP_CMP(int32, I32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LE_U): + DEF_OP_CMP(uint32, I32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GE_S): + DEF_OP_CMP(int32, I32, >=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GE_U): + DEF_OP_CMP(uint32, I32, >=); + HANDLE_OP_END (); + + /* comparison instructions of i64 */ + HANDLE_OP (WASM_OP_I64_EQZ): + DEF_OP_EQZ(I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EQ): + DEF_OP_CMP(uint64, I64, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_NE): + DEF_OP_CMP(uint64, I64, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LT_S): + DEF_OP_CMP(int64, I64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LT_U): + DEF_OP_CMP(uint64, I64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GT_S): + DEF_OP_CMP(int64, I64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GT_U): + DEF_OP_CMP(uint64, I64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LE_S): + DEF_OP_CMP(int64, I64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LE_U): + DEF_OP_CMP(uint64, I64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GE_S): + DEF_OP_CMP(int64, I64, >=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GE_U): + DEF_OP_CMP(uint64, I64, >=); + HANDLE_OP_END (); + + /* comparison instructions of f32 */ + HANDLE_OP (WASM_OP_F32_EQ): + DEF_OP_CMP(float32, F32, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NE): + DEF_OP_CMP(float32, F32, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_LT): + DEF_OP_CMP(float32, F32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_GT): + DEF_OP_CMP(float32, F32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_LE): + DEF_OP_CMP(float32, F32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_GE): + DEF_OP_CMP(float32, F32, >=); + HANDLE_OP_END (); + + /* comparison instructions of f64 */ + HANDLE_OP (WASM_OP_F64_EQ): + DEF_OP_CMP(float64, F64, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NE): + DEF_OP_CMP(float64, F64, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_LT): + DEF_OP_CMP(float64, F64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_GT): + DEF_OP_CMP(float64, F64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_LE): + DEF_OP_CMP(float64, F64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_GE): + DEF_OP_CMP(float64, F64, >=); + HANDLE_OP_END (); + + /* numberic instructions of i32 */ + HANDLE_OP (WASM_OP_I32_CLZ): + DEF_OP_BIT_COUNT(uint32, I32, clz32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_CTZ): + DEF_OP_BIT_COUNT(uint32, I32, ctz32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_POPCNT): + DEF_OP_BIT_COUNT(uint32, I32, popcount32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_ADD): + DEF_OP_NUMERIC(uint32, uint32, I32, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_SUB): + DEF_OP_NUMERIC(uint32, uint32, I32, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_MUL): + DEF_OP_NUMERIC(uint32, uint32, I32, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_DIV_S): + { + int32 a, b; + + b = POP_I32(); + a = POP_I32(); + if (a == (int32)0x80000000 && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_DIV_U): + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_REM_S): + { + int32 a, b; + + b = POP_I32(); + a = POP_I32(); + if (a == (int32)0x80000000 && b == -1) { + PUSH_I32(0); + HANDLE_OP_END (); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_REM_U): + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I32(a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_AND): + DEF_OP_NUMERIC(uint32, uint32, I32, &); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_OR): + DEF_OP_NUMERIC(uint32, uint32, I32, |); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_XOR): + DEF_OP_NUMERIC(uint32, uint32, I32, ^); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_SHL): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(uint32, uint32, I32, <<); +#else + DEF_OP_NUMERIC2(uint32, uint32, I32, <<); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_SHR_S): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(int32, uint32, I32, >>); +#else + DEF_OP_NUMERIC2(int32, uint32, I32, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_SHR_U): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(uint32, uint32, I32, >>); +#else + DEF_OP_NUMERIC2(uint32, uint32, I32, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_ROTL): + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + PUSH_I32(rotl32(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_ROTR): + { + uint32 a, b; + + b = (uint32)POP_I32(); + a = (uint32)POP_I32(); + PUSH_I32(rotr32(a, b)); + HANDLE_OP_END (); + } + + /* numberic instructions of i64 */ + HANDLE_OP (WASM_OP_I64_CLZ): + DEF_OP_BIT_COUNT(uint64, I64, clz64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_CTZ): + DEF_OP_BIT_COUNT(uint64, I64, ctz64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_POPCNT): + DEF_OP_BIT_COUNT(uint64, I64, popcount64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_ADD): + DEF_OP_NUMERIC_64(uint64, uint64, I64, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_SUB): + DEF_OP_NUMERIC_64(uint64, uint64, I64, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_MUL): + DEF_OP_NUMERIC_64(uint64, uint64, I64, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_DIV_S): + { + int64 a, b; + + b = POP_I64(); + a = POP_I64(); + if (a == (int64)0x8000000000000000LL && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_DIV_U): + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_REM_S): + { + int64 a, b; + + b = POP_I64(); + a = POP_I64(); + if (a == (int64)0x8000000000000000LL && b == -1) { + PUSH_I64(0); + HANDLE_OP_END (); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_REM_U): + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + PUSH_I64(a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_AND): + DEF_OP_NUMERIC_64(uint64, uint64, I64, &); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_OR): + DEF_OP_NUMERIC_64(uint64, uint64, I64, |); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_XOR): + DEF_OP_NUMERIC_64(uint64, uint64, I64, ^); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_SHL): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(uint64, uint64, I64, <<); +#else + DEF_OP_NUMERIC2_64(uint64, uint64, I64, <<); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_SHR_S): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(int64, uint64, I64, >>); +#else + DEF_OP_NUMERIC2_64(int64, uint64, I64, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_SHR_U): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(uint64, uint64, I64, >>); +#else + DEF_OP_NUMERIC2_64(uint64, uint64, I64, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_ROTL): + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + PUSH_I64(rotl64(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_ROTR): + { + uint64 a, b; + + b = (uint64)POP_I64(); + a = (uint64)POP_I64(); + PUSH_I64(rotr64(a, b)); + HANDLE_OP_END (); + } + + /* numberic instructions of f32 */ + HANDLE_OP (WASM_OP_F32_ABS): + DEF_OP_MATH(float32, F32, fabs); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NEG): + { + int32 i32 = (int32)frame_sp[-1]; + int32 sign_bit = i32 & (1 << 31); + if (sign_bit) + frame_sp[-1] = i32 & ~(1 << 31); + else + frame_sp[-1] = (uint32)(i32 | (1 << 31)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_CEIL): + DEF_OP_MATH(float32, F32, ceil); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_FLOOR): + DEF_OP_MATH(float32, F32, floor); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_TRUNC): + DEF_OP_MATH(float32, F32, trunc); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NEAREST): + DEF_OP_MATH(float32, F32, rint); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_SQRT): + DEF_OP_MATH(float32, F32, sqrt); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_ADD): + DEF_OP_NUMERIC(float32, float32, F32, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_SUB): + DEF_OP_NUMERIC(float32, float32, F32, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_MUL): + DEF_OP_NUMERIC(float32, float32, F32, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_DIV): + DEF_OP_NUMERIC(float32, float32, F32, /); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_MIN): + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + + if (isnan(a)) + PUSH_F32(a); + else if (isnan(b)) + PUSH_F32(b); + else + PUSH_F32(wa_fmin(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_MAX): + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + + if (isnan(a)) + PUSH_F32(a); + else if (isnan(b)) + PUSH_F32(b); + else + PUSH_F32(wa_fmax(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_COPYSIGN): + { + float32 a, b; + + b = POP_F32(); + a = POP_F32(); + PUSH_F32(signbit(b) ? -fabs(a) : fabs(a)); + HANDLE_OP_END (); + } + + /* numberic instructions of f64 */ + HANDLE_OP (WASM_OP_F64_ABS): + DEF_OP_MATH(float64, F64, fabs); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NEG): + { + int64 i64 = GET_I64_FROM_ADDR(frame_sp - 2); + int64 sign_bit = i64 & (((int64)1) << 63); + if (sign_bit) + PUT_I64_TO_ADDR(frame_sp - 2, ((uint64)i64 & ~(((uint64)1) << 63))); + else + PUT_I64_TO_ADDR(frame_sp - 2, ((uint64)i64 | (((uint64)1) << 63))); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_CEIL): + DEF_OP_MATH(float64, F64, ceil); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_FLOOR): + DEF_OP_MATH(float64, F64, floor); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_TRUNC): + DEF_OP_MATH(float64, F64, trunc); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NEAREST): + DEF_OP_MATH(float64, F64, rint); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_SQRT): + DEF_OP_MATH(float64, F64, sqrt); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_ADD): + DEF_OP_NUMERIC_64(float64, float64, F64, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_SUB): + DEF_OP_NUMERIC_64(float64, float64, F64, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_MUL): + DEF_OP_NUMERIC_64(float64, float64, F64, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_DIV): + DEF_OP_NUMERIC_64(float64, float64, F64, /); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_MIN): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + if (isnan(a)) + PUSH_F64(a); + else if (isnan(b)) + PUSH_F64(b); + else + PUSH_F64(wa_fmin(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_MAX): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + if (isnan(a)) + PUSH_F64(a); + else if (isnan(b)) + PUSH_F64(b); + else + PUSH_F64(wa_fmax(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_COPYSIGN): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + PUSH_F64(signbit(b) ? -fabs(a) : fabs(a)); + HANDLE_OP_END (); + } + + /* conversions of i32 */ + HANDLE_OP (WASM_OP_I32_WRAP_I64): + { + int32 value = (int32)(POP_I64() & 0xFFFFFFFFLL); + PUSH_I32(value); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_TRUNC_S_F32): + /* We don't use INT32_MIN/INT32_MAX/UINT32_MIN/UINT32_MAX, + since float/double values of ieee754 cannot precisely represent + all int32/uint32/int64/uint64 values, e.g.: + UINT32_MAX is 4294967295, but (float32)4294967295 is 4294967296.0f, + but not 4294967295.0f. */ + DEF_OP_TRUNC_F32(-2147483904.0f, 2147483648.0f, true, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_U_F32): + DEF_OP_TRUNC_F32(-1.0f, 4294967296.0f, true, false); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_S_F64): + DEF_OP_TRUNC_F64(-2147483649.0, 2147483648.0, true, true); + /* frame_sp can't be moved in trunc function, we need to manually adjust + it if src and dst op's cell num is different */ + frame_sp--; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_U_F64): + DEF_OP_TRUNC_F64(-1.0, 4294967296.0, true, false); + frame_sp--; + HANDLE_OP_END (); + + /* conversions of i64 */ + HANDLE_OP (WASM_OP_I64_EXTEND_S_I32): + DEF_OP_CONVERT(int64, I64, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND_U_I32): + DEF_OP_CONVERT(int64, I64, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_S_F32): + DEF_OP_TRUNC_F32(-9223373136366403584.0f, 9223372036854775808.0f, + false, true); + frame_sp++; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_U_F32): + DEF_OP_TRUNC_F32(-1.0f, 18446744073709551616.0f, + false, false); + frame_sp++; + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_S_F64): + DEF_OP_TRUNC_F64(-9223372036854777856.0, 9223372036854775808.0, + false, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_U_F64): + DEF_OP_TRUNC_F64(-1.0, 18446744073709551616.0, + false, false); + HANDLE_OP_END (); + + /* conversions of f32 */ + HANDLE_OP (WASM_OP_F32_CONVERT_S_I32): + DEF_OP_CONVERT(float32, F32, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_U_I32): + DEF_OP_CONVERT(float32, F32, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_S_I64): + DEF_OP_CONVERT(float32, F32, int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_U_I64): + DEF_OP_CONVERT(float32, F32, uint64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_DEMOTE_F64): + DEF_OP_CONVERT(float32, F32, float64, F64); + HANDLE_OP_END (); + + /* conversions of f64 */ + HANDLE_OP (WASM_OP_F64_CONVERT_S_I32): + DEF_OP_CONVERT(float64, F64, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_U_I32): + DEF_OP_CONVERT(float64, F64, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_S_I64): + DEF_OP_CONVERT(float64, F64, int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_U_I64): + DEF_OP_CONVERT(float64, F64, uint64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_PROMOTE_F32): + DEF_OP_CONVERT(float64, F64, float32, F32); + HANDLE_OP_END (); + + /* reinterpretations */ + HANDLE_OP (WASM_OP_I32_REINTERPRET_F32): + HANDLE_OP (WASM_OP_I64_REINTERPRET_F64): + HANDLE_OP (WASM_OP_F32_REINTERPRET_I32): + HANDLE_OP (WASM_OP_F64_REINTERPRET_I64): + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_EXTEND8_S): + DEF_OP_CONVERT(int32, I32, int8, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_EXTEND16_S): + DEF_OP_CONVERT(int32, I32, int16, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND8_S): + DEF_OP_CONVERT(int64, I64, int8, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND16_S): + DEF_OP_CONVERT(int64, I64, int16, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND32_S): + DEF_OP_CONVERT(int64, I64, int32, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_MISC_PREFIX): + { + uint32 opcode1; + + read_leb_uint32(frame_ip, frame_ip_end, opcode1); + opcode = (uint8)opcode1; + + switch (opcode) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-2147483904.0f, 2147483648.0f, + true, true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 4294967296.0f, + true, false); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-2147483649.0, 2147483648.0, + true, true); + frame_sp--; + break; + case WASM_OP_I32_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 4294967296.0, + true, false); + frame_sp--; + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-9223373136366403584.0f, 9223372036854775808.0f, + false, true); + frame_sp++; + break; + case WASM_OP_I64_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 18446744073709551616.0f, + false, false); + frame_sp++; + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-9223372036854777856.0, 9223372036854775808.0, + false, true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0f, 18446744073709551616.0, + false, false); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 addr, segment; + uint64 bytes, offset, seg_len; + uint8* data; + + read_leb_uint32(frame_ip, frame_ip_end, segment); + /* skip memory index */ + frame_ip++; + + bytes = (uint64)(uint32)POP_I32(); + offset = (uint64)(uint32)POP_I32(); + addr = (uint32)POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); + + seg_len = (uint64)module->module->data_segments[segment]->data_length; + data = module->module->data_segments[segment]->data; + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, linear_mem_size - addr, + data + offset, bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + read_leb_uint32(frame_ip, frame_ip_end, segment); + module->module->data_segments[segment]->data_length = 0; + + break; + } + case WASM_OP_MEMORY_COPY: + { + uint32 dst, src, len; + uint8 *mdst, *msrc; + + frame_ip += 2; + + len = POP_I32(); + src = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + /* allowing the destination and source to overlap */ + bh_memmove_s(mdst, linear_mem_size - dst, + msrc, len); + + break; + } + case WASM_OP_MEMORY_FILL: + { + uint32 dst, len; + uint8 val, *mdst; + frame_ip++; + + len = POP_I32(); + val = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + memset(mdst, val, len); + + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + break; + } + HANDLE_OP_END (); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + HANDLE_OP (WASM_OP_ATOMIC_PREFIX): + { + uint32 offset, align, addr; + + opcode = *frame_ip++; + + read_leb_uint32(frame_ip, frame_ip_end, align); + read_leb_uint32(frame_ip, frame_ip_end, offset); + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + { + uint32 count, ret; + + count = POP_I32(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_notify((WASMModuleInstanceCommon*)module, + maddr, count); + bh_assert((int32)ret >= 0); + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT32: + { + uint64 timeout; + uint32 expect, addr, ret; + + timeout = POP_I64(); + expect = POP_I32(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_wait((WASMModuleInstanceCommon*)module, maddr, + (uint64)expect, timeout, false); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT64: + { + uint64 timeout, expect; + uint32 ret; + + timeout = POP_I64(); + expect = POP_I64(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + ret = wasm_runtime_atomic_wait((WASMModuleInstanceCommon*)module, + maddr, expect, timeout, true); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + { + uint32 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = (uint32)(*(uint8*)maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = (uint32)LOAD_U16(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I32(maddr); + os_mutex_unlock(&memory->mem_lock); + } + + PUSH_I32(readv); + break; + } + + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + { + uint64 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)(*(uint8*)maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U16(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U32(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I64(maddr); + os_mutex_unlock(&memory->mem_lock); + } + + PUSH_I64(readv); + break; + } + + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + { + uint32 sval; + + sval = (uint32)POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_STORE8) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + *(uint8*)maddr = (uint8)sval; + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + STORE_U16(maddr, (uint16)sval); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + STORE_U32(maddr, frame_sp[1]); + os_mutex_unlock(&memory->mem_lock); + } + break; + } + + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + { + uint64 sval; + + sval = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_STORE8) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + *(uint8*)maddr = (uint8)sval; + os_mutex_unlock(&memory->mem_lock); + } + else if(opcode == WASM_OP_ATOMIC_I64_STORE16) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + STORE_U16(maddr, (uint16)sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + STORE_U32(maddr, (uint32)sval); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + os_mutex_lock(&memory->mem_lock); + STORE_U32(maddr, frame_sp[1]); + STORE_U32(maddr + 4, frame_sp[2]); + os_mutex_unlock(&memory->mem_lock); + } + break; + } + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + { + uint32 readv, sval, expect; + + sval = POP_I32(); + expect = POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint32)(*(uint8*)maddr); + if (readv == expect) + *(uint8*)maddr = (uint8)(sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint32)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I32(maddr); + if (readv == expect) + STORE_U32(maddr, sval); + os_mutex_unlock(&memory->mem_lock); + } + PUSH_I32(readv); + break; + } + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + { + uint64 readv, sval, expect; + + sval = (uint64)POP_I64(); + expect = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)(*(uint8*)maddr); + if (readv == expect) + *(uint8*)maddr = (uint8)(sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U32(maddr); + if (readv == expect) + STORE_U32(maddr, (uint32)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_I64(maddr); + if (readv == expect) { + STORE_I64(maddr, sval); + } + os_mutex_unlock(&memory->mem_lock); + } + PUSH_I64(readv); + break; + } + + DEF_ATOMIC_RMW_OPCODE(ADD, +); + DEF_ATOMIC_RMW_OPCODE(SUB, -); + DEF_ATOMIC_RMW_OPCODE(AND, &); + DEF_ATOMIC_RMW_OPCODE(OR, |); + DEF_ATOMIC_RMW_OPCODE(XOR, ^); + /* xchg, ignore the read value, and store the given value: + readv * 0 + sval */ + DEF_ATOMIC_RMW_OPCODE(XCHG, *0 +); + } + + HANDLE_OP_END (); + } +#endif + + HANDLE_OP (WASM_OP_IMPDEP): + frame = prev_frame; + frame_ip = frame->ip; + frame_sp = frame->sp; + frame_csp = frame->csp; + goto call_func_from_entry; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + HANDLE_OP (WASM_OP_UNUSED_0x06): + HANDLE_OP (WASM_OP_UNUSED_0x07): + HANDLE_OP (WASM_OP_UNUSED_0x08): + HANDLE_OP (WASM_OP_UNUSED_0x09): + HANDLE_OP (WASM_OP_UNUSED_0x0a): + HANDLE_OP (WASM_OP_UNUSED_0x12): + HANDLE_OP (WASM_OP_UNUSED_0x13): + HANDLE_OP (WASM_OP_UNUSED_0x14): + HANDLE_OP (WASM_OP_UNUSED_0x15): + HANDLE_OP (WASM_OP_UNUSED_0x16): + HANDLE_OP (WASM_OP_UNUSED_0x17): + HANDLE_OP (WASM_OP_UNUSED_0x18): + HANDLE_OP (WASM_OP_UNUSED_0x19): + HANDLE_OP (WASM_OP_UNUSED_0x1c): + HANDLE_OP (WASM_OP_UNUSED_0x1d): + HANDLE_OP (WASM_OP_UNUSED_0x1e): + HANDLE_OP (WASM_OP_UNUSED_0x1f): + /* Used by fast interpreter */ + HANDLE_OP (EXT_OP_SET_LOCAL_FAST_I64): + HANDLE_OP (EXT_OP_TEE_LOCAL_FAST_I64): + HANDLE_OP (EXT_OP_COPY_STACK_TOP): + HANDLE_OP (EXT_OP_COPY_STACK_TOP_I64): + HANDLE_OP (EXT_OP_COPY_STACK_VALUES): + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + continue; +#else + FETCH_OPCODE_AND_DISPATCH (); +#endif + + call_func_from_interp: + /* Only do the copy when it's called from interpreter. */ + { + WASMInterpFrame *outs_area = wasm_exec_env_wasm_stack_top(exec_env); + POP(cur_func->param_cell_num); + SYNC_ALL_TO_FRAME(); + word_copy(outs_area->lp, frame_sp, cur_func->param_cell_num); + prev_frame = frame; + } + + call_func_from_entry: + { + if (cur_func->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); + } + else +#endif + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); + } + + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + + memory = module->default_memory; + if (wasm_get_exception(module)) + goto got_exception; + } + else { + WASMFunction *cur_wasm_func = cur_func->u.func; + WASMType *func_type; + + func_type = cur_wasm_func->func_type; + + all_cell_num = (uint64)cur_func->param_cell_num + + (uint64)cur_func->local_cell_num + + (uint64)cur_wasm_func->max_stack_cell_num + + ((uint64)cur_wasm_func->max_block_num) * sizeof(WASMBranchBlock) / 4; + if (all_cell_num >= UINT32_MAX) { + wasm_set_exception(module, "stack overflow"); + goto got_exception; + } + + frame_size = wasm_interp_interp_frame_size((uint32)all_cell_num); + if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) { + frame = prev_frame; + goto got_exception; + } + + /* Initialize the interpreter context. */ + frame->function = cur_func; + frame_ip = wasm_get_func_code(cur_func); + frame_ip_end = wasm_get_func_code_end(cur_func); + frame_lp = frame->lp; + + frame_sp = frame->sp_bottom = frame_lp + cur_func->param_cell_num + + cur_func->local_cell_num; + frame->sp_boundary = frame->sp_bottom + cur_wasm_func->max_stack_cell_num; + + frame_csp = frame->csp_bottom = (WASMBranchBlock*)frame->sp_boundary; + frame->csp_boundary = frame->csp_bottom + cur_wasm_func->max_block_num; + + /* Initialize the local varialbes */ + memset(frame_lp + cur_func->param_cell_num, 0, + (uint32)(cur_func->local_cell_num * 4)); + + /* Push function block as first block */ + cell_num = func_type->ret_cell_num; + PUSH_CSP(LABEL_TYPE_FUNCTION, cell_num, frame_ip_end - 1); + + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)frame); + } + HANDLE_OP_END (); + } + + return_func: + { + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)prev_frame); + + if (!prev_frame->ip) + /* Called from native. */ + return; + + RECOVER_CONTEXT(prev_frame); + HANDLE_OP_END (); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + unaligned_atomic: + wasm_set_exception(module, "unaligned atomic"); + goto got_exception; +#endif + + out_of_bounds: + wasm_set_exception(module, "out of bounds memory access"); + + got_exception: + return; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + } +#else + FETCH_OPCODE_AND_DISPATCH (); +#endif +} + +void +wasm_interp_call_wasm(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *function, + uint32 argc, uint32 argv[]) +{ + // TODO: since module_inst = exec_env->module_inst, shall we remove the 1st arg? + WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); + WASMInterpFrame *frame, *outs_area; + + /* Allocate sufficient cells for all kinds of return values. */ + unsigned all_cell_num = function->ret_cell_num > 2 ? + function->ret_cell_num : 2, i; + /* This frame won't be used by JITed code, so only allocate interp + frame here. */ + unsigned frame_size = wasm_interp_interp_frame_size(all_cell_num); + + if (argc != function->param_cell_num) { + char buf[128]; + snprintf(buf, sizeof(buf), + "invalid argument count %d, expected %d", + argc, function->param_cell_num); + wasm_set_exception(module_inst, buf); + return; + } + + if ((uint8*)&prev_frame < exec_env->native_stack_boundary) { + wasm_set_exception((WASMModuleInstance*)exec_env->module_inst, + "native stack overflow"); + return; + } + + if (!(frame = ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame*)prev_frame))) + return; + + outs_area = wasm_exec_env_wasm_stack_top(exec_env); + frame->function = NULL; + frame->ip = NULL; + /* There is no local variable. */ + frame->sp = frame->lp + 0; + + if (argc > 0) + word_copy(outs_area->lp, argv, argc); + + wasm_exec_env_set_cur_frame(exec_env, frame); + + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + LOG_DEBUG("it is a function of a sub module"); + wasm_interp_call_func_import(module_inst, exec_env, + function, frame); + } + else +#endif + { + LOG_DEBUG("it is an native function"); + /* it is a native function */ + wasm_interp_call_func_native(module_inst, exec_env, + function, frame); + } + } + else { + LOG_DEBUG("it is a function of the module itself"); + wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); + } + + /* Output the return value to the caller */ + if (!wasm_get_exception(module_inst)) { + for (i = 0; i < function->ret_cell_num; i++) { + argv[i] = *(frame->sp + i - function->ret_cell_num); + } + + if (function->ret_cell_num) { + LOG_DEBUG("first return value argv[0]=%d", argv[0]); + } + else { + LOG_DEBUG("no return value"); + } + } + else { + LOG_DEBUG("meet an exception %s", wasm_get_exception(module_inst)); + } + + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + FREE_FRAME(exec_env, frame); +} diff --git a/wamr/core/iwasm/interpreter/wasm_interp_fast.c b/wamr/core/iwasm/interpreter/wasm_interp_fast.c new file mode 100644 index 0000000..2a19d8a --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_interp_fast.c @@ -0,0 +1,3383 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_interp.h" +#include "bh_log.h" +#include "wasm_runtime.h" +#include "wasm_opcode.h" +#include "wasm_loader.h" +#include "../common/wasm_exec_env.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +typedef int32 CellType_I32; +typedef int64 CellType_I64; +typedef float32 CellType_F32; +typedef float64 CellType_F64; + +#define BR_TABLE_TMP_BUF_LEN 32 + +/* 64-bit Memory accessors. */ +#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 +#define PUT_I64_TO_ADDR(addr, value) do { \ + *(int64*)(addr) = (int64)(value); \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) do { \ + *(float64*)(addr) = (float64)(value); \ + } while (0) + +#define GET_I64_FROM_ADDR(addr) (*(int64*)(addr)) +#define GET_F64_FROM_ADDR(addr) (*(float64*)(addr)) + +/* For STORE opcodes */ +#define STORE_I64 PUT_I64_TO_ADDR +#define STORE_U32(addr, value) do { \ + *(uint32*)(addr) = (uint32)(value); \ + } while (0) +#define STORE_U16(addr, value) do { \ + *(uint16*)(addr) = (uint16)(value); \ + } while (0) + +/* For LOAD opcodes */ +#define LOAD_I64(addr) (*(int64*)(addr)) +#define LOAD_F64(addr) (*(float64*)(addr)) +#define LOAD_F32(addr) (*(float32*)(addr)) +#define LOAD_I32(addr) (*(int32*)(addr)) +#define LOAD_U32(addr) (*(uint32*)(addr)) +#define LOAD_I16(addr) (*(int16*)(addr)) +#define LOAD_U16(addr) (*(uint16*)(addr)) + +#else /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ +#define PUT_I64_TO_ADDR(addr, value) do { \ + union { int64 val; uint32 parts[2]; } u; \ + u.val = (int64)(value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) +#define PUT_F64_TO_ADDR(addr, value) do { \ + union { float64 val; uint32 parts[2]; } u; \ + u.val = (value); \ + (addr)[0] = u.parts[0]; \ + (addr)[1] = u.parts[1]; \ + } while (0) + +static inline int64 +GET_I64_FROM_ADDR(uint32 *addr) +{ + union { int64 val; uint32 parts[2]; } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +static inline float64 +GET_F64_FROM_ADDR (uint32 *addr) +{ + union { float64 val; uint32 parts[2]; } u; + u.parts[0] = addr[0]; + u.parts[1] = addr[1]; + return u.val; +} + +/* For STORE opcodes */ +#define STORE_I64(addr, value) do { \ + uintptr_t addr1 = (uintptr_t)(addr); \ + union { int64 val; uint32 u32[2]; \ + uint16 u16[4]; uint8 u8[8]; } u; \ + if ((addr1 & (uintptr_t)7) == 0) \ + *(int64*)(addr) = (int64)(value); \ + else { \ + u.val = (int64)(value); \ + if ((addr1 & (uintptr_t)3) == 0) { \ + ((uint32*)(addr))[0] = u.u32[0]; \ + ((uint32*)(addr))[1] = u.u32[1]; \ + } \ + else if ((addr1 & (uintptr_t)1) == 0) { \ + ((uint16*)(addr))[0] = u.u16[0]; \ + ((uint16*)(addr))[1] = u.u16[1]; \ + ((uint16*)(addr))[2] = u.u16[2]; \ + ((uint16*)(addr))[3] = u.u16[3]; \ + } \ + else { \ + int32 t; \ + for (t = 0; t < 8; t++) \ + ((uint8*)(addr))[t] = u.u8[t]; \ + } \ + } \ + } while (0) + +#define STORE_U32(addr, value) do { \ + uintptr_t addr1 = (uintptr_t)(addr); \ + union { uint32 val; \ + uint16 u16[2]; uint8 u8[4]; } u; \ + if ((addr1 & (uintptr_t)3) == 0) \ + *(uint32*)(addr) = (uint32)(value); \ + else { \ + u.val = (uint32)(value); \ + if ((addr1 & (uintptr_t)1) == 0) { \ + ((uint16*)(addr))[0] = u.u16[0]; \ + ((uint16*)(addr))[1] = u.u16[1]; \ + } \ + else { \ + ((uint8*)(addr))[0] = u.u8[0]; \ + ((uint8*)(addr))[1] = u.u8[1]; \ + ((uint8*)(addr))[2] = u.u8[2]; \ + ((uint8*)(addr))[3] = u.u8[3]; \ + } \ + } \ + } while (0) + +#define STORE_U16(addr, value) do { \ + union { uint16 val; uint8 u8[2]; } u; \ + u.val = (uint16)(value); \ + ((uint8*)(addr))[0] = u.u8[0]; \ + ((uint8*)(addr))[1] = u.u8[1]; \ + } while (0) + +/* For LOAD opcodes */ +static inline int64 +LOAD_I64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { int64 val; uint32 u32[2]; + uint16 u16[4]; uint8 u8[8]; } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(int64*)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32*)addr)[0]; + u.u32[1] = ((uint32*)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + u.u16[2] = ((uint16*)addr)[2]; + u.u16[3] = ((uint16*)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8*)addr)[t]; + } + return u.val; +} + +static inline float64 +LOAD_F64(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { float64 val; uint32 u32[2]; + uint16 u16[4]; uint8 u8[8]; } u; + if ((addr1 & (uintptr_t)7) == 0) + return *(float64*)addr; + + if ((addr1 & (uintptr_t)3) == 0) { + u.u32[0] = ((uint32*)addr)[0]; + u.u32[1] = ((uint32*)addr)[1]; + } + else if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + u.u16[2] = ((uint16*)addr)[2]; + u.u16[3] = ((uint16*)addr)[3]; + } + else { + int32 t; + for (t = 0; t < 8; t++) + u.u8[t] = ((uint8*)addr)[t]; + } + return u.val; +} + +static inline int32 +LOAD_I32(void *addr) +{ + uintptr_t addr1 = (uintptr_t)addr; + union { int32 val; uint16 u16[2]; uint8 u8[4]; } u; + if ((addr1 & (uintptr_t)3) == 0) + return *(int32*)addr; + + if ((addr1 & (uintptr_t)1) == 0) { + u.u16[0] = ((uint16*)addr)[0]; + u.u16[1] = ((uint16*)addr)[1]; + } + else { + u.u8[0] = ((uint8*)addr)[0]; + u.u8[1] = ((uint8*)addr)[1]; + u.u8[2] = ((uint8*)addr)[2]; + u.u8[3] = ((uint8*)addr)[3]; + } + return u.val; +} + +static inline int16 +LOAD_I16(void *addr) +{ + union { int16 val; uint8 u8[2]; } u; + u.u8[0] = ((uint8*)addr)[0]; + u.u8[1] = ((uint8*)addr)[1]; + return u.val; +} + +#define LOAD_U32(addr) ((uint32)LOAD_I32(addr)) +#define LOAD_U16(addr) ((uint16)LOAD_I16(addr)) +#define LOAD_F32(addr) ((float32)LOAD_I32(addr)) + +#endif /* WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 */ + +#define CHECK_MEMORY_OVERFLOW(bytes) do { \ + uint64 offset1 = (uint64)offset + (uint64)addr; \ + if (offset1 + bytes <= (uint64)linear_mem_size) \ + /* If offset1 is in valid range, maddr must also be in valid range,\ + no need to check it again. */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_BULK_MEMORY_OVERFLOW(start, bytes, maddr) do { \ + uint64 offset1 = (uint32)(start); \ + if (offset1 + bytes <= linear_mem_size) \ + /* App heap space is not valid space for bulk memory operation */ \ + maddr = memory->memory_data + offset1; \ + else \ + goto out_of_bounds; \ + } while (0) + +#define CHECK_ATOMIC_MEMORY_ACCESS(align) do { \ + if (((uintptr_t)maddr & (align - 1)) != 0) \ + goto unaligned_atomic; \ + } while (0) + +static inline uint32 +rotl32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n<>( (-c)&mask )); +} + +static inline uint32 +rotr32(uint32 n, uint32 c) +{ + const uint32 mask = (31); + c = c % 32; + c &= mask; + return (n>>c) | (n<<( (-c)&mask )); +} + +static inline uint64 +rotl64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n<>( (-c)&mask )); +} + +static inline uint64 +rotr64(uint64 n, uint64 c) +{ + const uint64 mask = (63); + c = c % 64; + c &= mask; + return (n>>c) | (n<<( (-c)&mask )); +} + +static inline double +wa_fmax(double a, double b) +{ + double c = fmax(a, b); + if (c==0 && a==b) + return signbit(a) ? b : a; + return c; +} + +static inline double +wa_fmin(double a, double b) +{ + double c = fmin(a, b); + if (c==0 && a==b) + return signbit(a) ? a : b; + return c; +} + +static inline uint32 +clz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 0x80000000)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +clz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 0x8000000000000000LL)) { + num++; + type <<= 1; + } + return num; +} + +static inline uint32 +ctz32(uint32 type) +{ + uint32 num = 0; + if (type == 0) + return 32; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +ctz64(uint64 type) +{ + uint32 num = 0; + if (type == 0) + return 64; + while (!(type & 1)) { + num++; + type >>= 1; + } + return num; +} + +static inline uint32 +popcount32(uint32 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static inline uint32 +popcount64(uint64 u) +{ + uint32 ret = 0; + while (u) { + u = (u & (u - 1)); + ret++; + } + return ret; +} + +static uint64 +read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign) +{ + uint64 result = 0; + uint32 shift = 0; + uint32 bcnt = 0; + uint64 byte; + + while (true) { + byte = buf[*p_offset]; + *p_offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + if ((byte & 0x80) == 0) { + break; + } + bcnt += 1; + } + if (sign && (shift < maxbits) && (byte & 0x40)) { + /* Sign extend */ + result |= - ((uint64)1 << shift); + } + return result; +} + +#define read_leb_uint32(p, p_end, res) do { \ + uint8 _val = *p; \ + if (!(_val & 0x80)) { \ + res = _val; \ + p++; \ + break; \ + } \ + uint32 _off = 0; \ + res = (uint32)read_leb(p, &_off, 32, false); \ + p += _off; \ +} while (0) + +#define read_uint32(p) (p += sizeof(uint32), *(uint32 *)(p - sizeof(uint32))) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ + uint32 param_count = cur_func->param_count; \ + read_leb_uint32(frame_ip, frame_ip_end, local_idx); \ + bh_assert(local_idx < param_count + cur_func->local_count); \ + local_offset = cur_func->local_offsets[local_idx]; \ + if (local_idx < param_count) \ + local_type = cur_func->param_types[local_idx]; \ + else \ + local_type = cur_func->local_types[local_idx - param_count]; \ + } while (0) + +#define GET_OFFSET() (frame_ip += 2, *(int16 *)(frame_ip - 2)) + +#define SET_OPERAND(type, off, value) \ + (*(type*)(frame_lp + *(int16*)(frame_ip + off))) = value + +#define GET_OPERAND(type, off) (*(type*)(frame_lp + *(int16*)(frame_ip + off))) + +#define PUSH_I32(value) do { \ + *(int32*)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define PUSH_F32(value) do { \ + *(float32*)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define PUSH_I64(value) do { \ + *(int64*)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define PUSH_F64(value) do { \ + *(float64*)(frame_lp + GET_OFFSET()) = value; \ + } while (0) + +#define POP_I32() (*(int32*)(frame_lp + GET_OFFSET())) + +#define POP_F32() (*(float32*)(frame_lp + GET_OFFSET())) + +#define POP_I64() (*(int64*)(frame_lp + GET_OFFSET())) + +#define POP_F64() (*(float64*)(frame_lp + GET_OFFSET())) + +#define SYNC_ALL_TO_FRAME() do { \ + frame->ip = frame_ip; \ + } while (0) + +#define UPDATE_ALL_FROM_FRAME() do { \ + frame_ip = frame->ip; \ + } while (0) + +#define RECOVER_CONTEXT(new_frame) do { \ + frame = (new_frame); \ + cur_func = frame->function; \ + prev_frame = frame->prev_frame; \ + frame_ip = frame->ip; \ + frame_lp = frame->lp; \ + } while (0) + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#define GET_OPCODE() opcode = *(frame_ip++); +#else +#define GET_OPCODE() (void)0 +#endif + +#define DEF_OP_EQZ(ctype, src_op_type) do { \ + SET_OPERAND(int32, 2, (GET_OPERAND(ctype, 0) == 0)); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_CMP(src_type, src_op_type, cond) do { \ + SET_OPERAND(uint32, 4, GET_OPERAND(src_type, 2) cond \ + GET_OPERAND(src_type, 0)); \ + frame_ip += 6; \ + } while (0) + +#define DEF_OP_BIT_COUNT(src_type, src_op_type, operation) do { \ + SET_OPERAND(src_type, 2, \ + (src_type)operation(GET_OPERAND(src_type, 0))); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_NUMERIC(src_type1, src_type2, src_op_type, operation) do { \ + SET_OPERAND(src_type1, 4, (GET_OPERAND(src_type1, 2) \ + operation GET_OPERAND(src_type2, 0))); \ + frame_ip += 6; \ + } while (0) + +#if defined(BUILD_TARGET_X86_32) +#define DEF_OP_REINTERPRET(src_type) do { \ + void *src = frame_lp + GET_OFFSET(); \ + void *dst = frame_lp + GET_OFFSET(); \ + bh_memcpy_s(dst, sizeof(src_type), src, sizeof(src_type)); \ + } while (0) +#else +#define DEF_OP_REINTERPRET(src_type) do { \ + SET_OPERAND(src_type, 2, GET_OPERAND(src_type, 0)); \ + frame_ip += 4; \ + } while (0) +#endif + +#if WASM_CPU_SUPPORTS_UNALIGNED_64BIT_ACCESS != 0 +#define DEF_OP_NUMERIC_64 DEF_OP_NUMERIC +#else +#define DEF_OP_NUMERIC_64(src_type1, src_type2, src_op_type, operation) do { \ + src_type1 val1; \ + src_type2 val2; \ + val1 = \ + (src_type1)GET_##src_op_type##_FROM_ADDR(frame_lp + (*(int16*)(frame_ip + 2))); \ + val2 = \ + (src_type2)GET_##src_op_type##_FROM_ADDR(frame_lp + (*(int16*)(frame_ip))); \ + val1 operation##= val2; \ + PUT_##src_op_type##_TO_ADDR(frame_lp + (*(int16*)(frame_ip + 4)), val1); \ + frame_ip += 6; \ + } while (0) +#endif + +#define DEF_OP_NUMERIC2(src_type1, src_type2, src_op_type, operation) do { \ + SET_OPERAND(src_type1, 4, (GET_OPERAND(src_type1, 2) \ + operation (GET_OPERAND(src_type2, 0) % 32))); \ + frame_ip += 6; \ + } while (0) + +#define DEF_OP_NUMERIC2_64(src_type1, src_type2, src_op_type, operation) do { \ + SET_OPERAND(src_type1, 4, (GET_OPERAND(src_type1, 2) \ + operation (GET_OPERAND(src_type2, 0) % 64))); \ + frame_ip += 6; \ + } while (0) + +#define DEF_ATOMIC_RMW_OPCODE(OP_NAME, op) \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U: \ + { \ + uint32 readv, sval; \ + \ + sval = POP_I32(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##8_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(1); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint32)(*(uint8*)maddr); \ + *(uint8*)maddr = (uint8)(readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I32_##OP_NAME##16_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(2); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint32)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(4); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = LOAD_I32(maddr); \ + STORE_U32(maddr, readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + PUSH_I32(readv); \ + break; \ + } \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U: \ + case WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U: \ + { \ + uint64 readv, sval; \ + \ + sval = (uint64)POP_I64(); \ + addr = POP_I32(); \ + \ + if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##8_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(1); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)(*(uint8*)maddr); \ + *(uint8*)maddr = (uint8)(readv op sval); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##16_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(2); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_U16(maddr); \ + STORE_U16(maddr, (uint16)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else if (opcode == WASM_OP_ATOMIC_RMW_I64_##OP_NAME##32_U) { \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(4); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_U32(maddr); \ + STORE_U32(maddr, (uint32)(readv op sval)); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + else { \ + uint64 op_result; \ + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); \ + CHECK_ATOMIC_MEMORY_ACCESS(8); \ + \ + os_mutex_lock(&memory->mem_lock); \ + readv = (uint64)LOAD_I64(maddr); \ + op_result = readv op sval; \ + STORE_I64(maddr, op_result); \ + os_mutex_unlock(&memory->mem_lock); \ + } \ + PUSH_I64(readv); \ + break; \ + } + +#define DEF_OP_MATH(src_type, src_op_type, method) do { \ + SET_OPERAND(src_type, 2, method(GET_OPERAND(src_type, 0))); \ + frame_ip += 4; \ + } while (0) + +#define TRUNC_FUNCTION(func_name, src_type, dst_type, signed_type) \ +static dst_type \ +func_name(src_type src_value, src_type src_min, src_type src_max, \ + dst_type dst_min, dst_type dst_max, bool is_sign) \ +{ \ + dst_type dst_value = 0; \ + if (!isnan(src_value)) { \ + if (src_value <= src_min) \ + dst_value = dst_min; \ + else if (src_value >= src_max) \ + dst_value = dst_max; \ + else { \ + if (is_sign) \ + dst_value = (dst_type)(signed_type)src_value; \ + else \ + dst_value = (dst_type)src_value; \ + } \ + } \ + return dst_value; \ +} + +TRUNC_FUNCTION(trunc_f32_to_i32, float32, uint32, int32) +TRUNC_FUNCTION(trunc_f32_to_i64, float32, uint64, int64) +TRUNC_FUNCTION(trunc_f64_to_i32, float64, uint32, int32) +TRUNC_FUNCTION(trunc_f64_to_i64, float64, uint64, int64) + +static bool +trunc_f32_to_int(WASMModuleInstance *module, + uint8 *frame_ip, uint32 *frame_lp, + float32 src_min, float32 src_max, + bool saturating, bool is_i32, bool is_sign) +{ + float32 src_value = GET_OPERAND(float32, 0); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return true; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return true; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f32_to_i32(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + SET_OPERAND(uint32, 2, dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f32_to_i64(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + SET_OPERAND(uint64, 2, dst_value_i64); + } + return false; +} + +static bool +trunc_f64_to_int(WASMModuleInstance *module, + uint8 *frame_ip, uint32 *frame_lp, + float64 src_min, float64 src_max, + bool saturating, bool is_i32, bool is_sign) +{ + float64 src_value = GET_OPERAND(float64, 0); + uint64 dst_value_i64; + uint32 dst_value_i32; + + if (!saturating) { + if (isnan(src_value)) { + wasm_set_exception(module, "invalid conversion to integer"); + return true; + } + else if (src_value <= src_min || src_value >= src_max) { + wasm_set_exception(module, "integer overflow"); + return true; + } + } + + if (is_i32) { + uint32 dst_min = is_sign ? INT32_MIN : 0; + uint32 dst_max = is_sign ? INT32_MAX : UINT32_MAX; + dst_value_i32 = trunc_f64_to_i32(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + SET_OPERAND(uint32, 2, dst_value_i32); + } + else { + uint64 dst_min = is_sign ? INT64_MIN : 0; + uint64 dst_max = is_sign ? INT64_MAX : UINT64_MAX; + dst_value_i64 = trunc_f64_to_i64(src_value, src_min, src_max, + dst_min, dst_max, is_sign); + SET_OPERAND(uint64, 2, dst_value_i64); + } + return false; +} + +#define DEF_OP_TRUNC_F32(min, max, is_i32, is_sign) do { \ + if (trunc_f32_to_int(module, frame_ip, frame_lp, min, max, \ + false, is_i32, is_sign)) \ + goto got_exception; \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_F64(min, max, is_i32, is_sign) do { \ + if (trunc_f64_to_int(module, frame_ip, frame_lp, min, max, \ + false, is_i32, is_sign)) \ + goto got_exception; \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F32(min, max, is_i32, is_sign) do { \ + (void)trunc_f32_to_int(module, frame_ip, frame_lp, min, max, \ + true, is_i32, is_sign); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_TRUNC_SAT_F64(min, max, is_i32, is_sign) do { \ + (void)trunc_f64_to_int(module, frame_ip, frame_lp, min, max, \ + true, is_i32, is_sign); \ + frame_ip += 4; \ + } while (0) + +#define DEF_OP_CONVERT(dst_type, dst_op_type, \ + src_type, src_op_type) do { \ + dst_type value = (dst_type)(src_type)POP_##src_op_type(); \ + PUSH_##dst_op_type(value); \ + } while (0) + +static bool +copy_stack_values(WASMModuleInstance *module, + uint32 *frame_lp, + uint32 arity, + uint32 total_cell_num, + const uint8 *cells, + const int16 *src_offsets, + const uint16 *dst_offsets) +{ + /* To avoid the overlap issue between src offsets and dst offset, + * we use 2 steps to do the copy. First step, copy the src values + * to a tmp buf. Second step, copy the values from tmp buf to dst. + */ + uint32 buf[16] = {0}, i; + uint32 *tmp_buf = buf; + uint8 cell; + int16 src, buf_index = 0; + uint16 dst; + + /* Allocate memory if the buf is not large enough */ + if (total_cell_num > sizeof(buf)/sizeof(uint32)) { + uint64 total_size = sizeof(uint32) * (uint64)total_cell_num; + if (total_size >= UINT32_MAX + || !(tmp_buf = wasm_runtime_malloc((uint32)total_size))) { + wasm_set_exception(module, "allocate memory failed"); + return false; + } + } + + /* 1) Copy values from src to tmp buf */ + for (i = 0; i < arity; i++) { + cell = cells[i]; + src = src_offsets[i]; + if (cell == 1) + tmp_buf[buf_index] = frame_lp[src]; + else + *(uint64*)(tmp_buf + buf_index) = *(uint64*)(frame_lp + src); + buf_index += cell; + } + + /* 2) Copy values from tmp buf to dest */ + buf_index = 0; + for (i = 0; i < arity; i++) { + cell = cells[i]; + dst = dst_offsets[i]; + if (cell == 1) + frame_lp[dst] = tmp_buf[buf_index]; + else + *(uint64*)(frame_lp + dst) = *(uint64*)(tmp_buf + buf_index); + buf_index += cell; + } + + if (tmp_buf != buf) { + wasm_runtime_free(tmp_buf); + } + + return true; +} + +#define RECOVER_BR_INFO() do { \ + uint32 arity; \ + /* read arity */ \ + arity = *(uint32*)frame_ip; \ + frame_ip += sizeof(arity); \ + if (arity) { \ + uint32 total_cell; \ + uint16 *dst_offsets = NULL; \ + uint8 *cells; \ + int16 *src_offsets = NULL; \ + /* read total cell num */ \ + total_cell = *(uint32*)frame_ip; \ + frame_ip += sizeof(total_cell); \ + /* cells */ \ + cells = (uint8 *)frame_ip; \ + frame_ip += arity * sizeof(uint8); \ + /* src offsets */ \ + src_offsets = (int16 *)frame_ip; \ + frame_ip += arity * sizeof(int16); \ + /* dst offsets */ \ + dst_offsets = (uint16*)frame_ip; \ + frame_ip += arity * sizeof(uint16); \ + if (arity == 1) { \ + if (cells[0] == 1) \ + frame_lp[dst_offsets[0]] = \ + frame_lp[src_offsets[0]]; \ + else if (cells[0] == 2) { \ + *(int64*)(frame_lp + dst_offsets[0]) = \ + *(int64*)(frame_lp + src_offsets[0]); \ + } \ + } \ + else { \ + if (!copy_stack_values(module, frame_lp, \ + arity, total_cell, \ + cells, src_offsets, \ + dst_offsets)) \ + goto got_exception; \ + } \ + } \ + frame_ip = *(uint8**)frame_ip; \ + } while (0) + +#define SKIP_BR_INFO() do { \ + uint32 arity; \ + /* read and skip arity */ \ + arity = *(uint32*)frame_ip; \ + frame_ip += sizeof(arity); \ + if (arity) { \ + /* skip total cell num */ \ + frame_ip += sizeof(uint32); \ + /* skip cells, src offsets and dst offsets */ \ + frame_ip += (sizeof(uint8) + sizeof(int16) + sizeof(uint16)) * arity; \ + } \ + /* skip target address */ \ + frame_ip += sizeof(uint8*); \ + } while (0) + +static inline int32 +sign_ext_8_32(int8 val) +{ + if (val & 0x80) + return (int32)val | (int32)0xffffff00; + return val; +} + +static inline int32 +sign_ext_16_32(int16 val) +{ + if (val & 0x8000) + return (int32)val | (int32)0xffff0000; + return val; +} + +static inline int64 +sign_ext_8_64(int8 val) +{ + if (val & 0x80) + return (int64)val | (int64)0xffffffffffffff00; + return val; +} + +static inline int64 +sign_ext_16_64(int16 val) +{ + if (val & 0x8000) + return (int64)val | (int64)0xffffffffffff0000; + return val; +} + +static inline int64 +sign_ext_32_64(int32 val) +{ + if (val & (int32)0x80000000) + return (int64)val | (int64)0xffffffff00000000; + return val; +} + +static inline void +word_copy(uint32 *dest, uint32 *src, unsigned num) +{ + for (; num > 0; num--) + *dest++ = *src++; +} + +static inline WASMInterpFrame* +ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame) +{ + WASMInterpFrame *frame = wasm_exec_env_alloc_wasm_frame(exec_env, size); + + if (frame) + frame->prev_frame = prev_frame; + else { + wasm_set_exception((WASMModuleInstance*)exec_env->module_inst, + "stack overflow"); + } + + return frame; +} + +static inline void +FREE_FRAME(WASMExecEnv *exec_env, WASMInterpFrame *frame) +{ + wasm_exec_env_free_wasm_frame(exec_env, frame); +} + +static void +wasm_interp_call_func_native(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + 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]; + bool ret; + + if (!(frame = ALLOC_FRAME(exec_env, + wasm_interp_interp_frame_size(local_cell_num), + prev_frame))) + return; + + frame->function = cur_func; + frame->ip = NULL; + frame->lp = frame->operand; + + wasm_exec_env_set_cur_frame(exec_env, frame); + + if (!func_import->func_ptr_linked) { + char buf[128]; + snprintf(buf, + sizeof(buf), "fail to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception((WASMModuleInstance*)module_inst, buf); + return; + } + + if (!func_import->call_conv_raw) { + ret = wasm_runtime_invoke_native(exec_env, func_import->func_ptr_linked, + func_import->func_type, func_import->signature, + func_import->attachment, + frame->lp, cur_func->param_cell_num, argv_ret); + } + else { + ret = wasm_runtime_invoke_native_raw(exec_env, func_import->func_ptr_linked, + func_import->func_type, func_import->signature, + func_import->attachment, + frame->lp, cur_func->param_cell_num, argv_ret); + } + + if (!ret) + return; + + if (cur_func->ret_cell_num == 1) { + prev_frame->lp[prev_frame->ret_offset] = argv_ret[0]; + } + else if (cur_func->ret_cell_num == 2) { + prev_frame->lp[prev_frame->ret_offset] = argv_ret[0]; + prev_frame->lp[prev_frame->ret_offset + 1] = argv_ret[1]; + } + + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame); + +static void +wasm_interp_call_func_import(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMModuleInstance *sub_module_inst = cur_func->import_module_inst; + WASMFunctionInstance *sub_func_inst = cur_func->import_func_inst; + WASMFunctionImport *func_import = cur_func->u.func_import; + uint8 *ip = prev_frame->ip; + char buf[128]; + + if (!sub_func_inst) { + snprintf(buf, sizeof(buf), + "fail to call unlinked import function (%s, %s)", + func_import->module_name, func_import->field_name); + wasm_set_exception(module_inst, buf); + return; + } + + /* set ip NULL to make call_func_bytecode return after executing + this function */ + prev_frame->ip = NULL; + + /* replace exec_env's module_inst with sub_module_inst so we can + call it */ + exec_env->module_inst = (WASMModuleInstanceCommon *)sub_module_inst; + + /* call function of sub-module*/ + wasm_interp_call_func_bytecode(sub_module_inst, exec_env, + sub_func_inst, prev_frame); + + /* restore ip and module_inst */ + prev_frame->ip = ip; + exec_env->module_inst = (WASMModuleInstanceCommon *)module_inst; + + /* transfer exception if it is thrown */ + if (wasm_get_exception(sub_module_inst)) { + bh_memcpy_s(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + sub_module_inst->cur_exception, + sizeof(sub_module_inst->cur_exception)); + } +} +#endif + +#if WASM_ENABLE_THREAD_MGR != 0 +#define CHECK_SUSPEND_FLAGS() do { \ + if (exec_env->suspend_flags.flags != 0) { \ + if (exec_env->suspend_flags.flags & 0x01) { \ + /* terminate current thread */ \ + return; \ + } \ + /* TODO: support suspend and breakpoint */ \ + } \ + } while (0) +#endif + +#if WASM_ENABLE_OPCODE_COUNTER != 0 +typedef struct OpcodeInfo { + char *name; + uint64 count; +} OpcodeInfo; + +#define HANDLE_OPCODE(op) { #op, 0 } +DEFINE_GOTO_TABLE (OpcodeInfo, opcode_table); +#undef HANDLE_OPCODE + +static void +wasm_interp_dump_op_count() +{ + uint32 i; + uint64 total_count = 0; + for (i = 0; i < WASM_OP_IMPDEP; i++) + total_count += opcode_table[i].count; + + printf("total opcode count: %ld\n", total_count); + for (i = 0; i < WASM_OP_IMPDEP; i++) + if (opcode_table[i].count > 0) + printf("\t\t%s count:\t\t%ld,\t\t%.2f%%\n", + opcode_table[i].name, opcode_table[i].count, + opcode_table[i].count * 100.0f / total_count); +} +#endif + + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + +//#define HANDLE_OP(opcode) HANDLE_##opcode:printf(#opcode"\n");h_##opcode +#if WASM_ENABLE_OPCODE_COUNTER != 0 +#define HANDLE_OP(opcode) HANDLE_##opcode:opcode_table[opcode].count++;h_##opcode +#else +#define HANDLE_OP(opcode) HANDLE_##opcode +#endif +#if WASM_ENABLE_FAST_INTERP == 0 +#define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++] +#else +#if WASM_ENABLE_ABS_LABEL_ADDR != 0 +#define FETCH_OPCODE_AND_DISPATCH() do { \ + const void *p_label_addr = *(void**)frame_ip; \ + frame_ip += sizeof(void*); \ + goto *p_label_addr; \ + } while (0) +#else +#define FETCH_OPCODE_AND_DISPATCH() do { \ + const void *p_label_addr = label_base \ + + *(int16*)frame_ip; \ + frame_ip += sizeof(int16); \ + goto *p_label_addr; \ + } while (0) +#endif +#endif +#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() + +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ + +#define HANDLE_OP(opcode) case opcode +#define HANDLE_OP_END() continue + +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +#if WASM_ENABLE_FAST_INTERP != 0 +static void **global_handle_table; +#endif + +static void +wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, + WASMFunctionInstance *cur_func, + WASMInterpFrame *prev_frame) +{ + WASMMemoryInstance *memory = module->default_memory; + uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; + uint8 *global_data = module->global_data; + uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; + WASMTableInstance *table = module->default_table; + WASMGlobalInstance *globals = module->globals, *global; + uint8 opcode_IMPDEP = WASM_OP_IMPDEP; + WASMInterpFrame *frame = NULL; + /* Points to this special opcode so as to jump to the call_method_from_entry. */ + register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */ + register uint32 *frame_lp = NULL; /* cache of frame->lp */ +#if WASM_ENABLE_ABS_LABEL_ADDR == 0 + register uint8 *label_base = &&HANDLE_WASM_OP_UNREACHABLE; /* cache of label base addr */ +#endif + uint8 *frame_ip_end; + uint32 cond, count, fidx, tidx, frame_size = 0; + uint64 all_cell_num = 0; + int16 addr1, addr2, addr_ret = 0; + int32 didx, val; + uint8 *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + uint8 opcode, local_type, *global_addr; + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + #define HANDLE_OPCODE(op) &&HANDLE_##op + DEFINE_GOTO_TABLE (const void*, handle_table); + #undef HANDLE_OPCODE +#if WASM_ENABLE_FAST_INTERP != 0 + if (exec_env == NULL) { + global_handle_table = (void **)handle_table; + return; + } +#endif +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + while (frame_ip < frame_ip_end) { + opcode = *frame_ip++; + switch (opcode) { +#else + goto *handle_table[WASM_OP_IMPDEP]; +#endif + /* control instructions */ + HANDLE_OP (WASM_OP_UNREACHABLE): + wasm_set_exception(module, "unreachable"); + goto got_exception; + + HANDLE_OP (WASM_OP_IF): + cond = (uint32)POP_I32(); + + if (cond == 0) { + if (*(uint8**)frame_ip == NULL) { + frame_ip = *(uint8**)(frame_ip + sizeof(uint8*)); + } + else { + frame_ip = *(uint8**)(frame_ip); + } + } + else { + frame_ip += sizeof(uint8*) * 2; + } + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_ELSE): + frame_ip = *(uint8**)(frame_ip); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif +recover_br_info: + RECOVER_BR_INFO(); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR_IF): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + cond = frame_lp[GET_OFFSET()]; + + if (cond) + goto recover_br_info; + else + SKIP_BR_INFO(); + + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_BR_TABLE): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + count = read_uint32(frame_ip); + didx = GET_OPERAND(uint32, 0); + frame_ip += 2; + + if (!(didx >= 0 && (uint32)didx < count)) + didx = count; + + while (didx--) + SKIP_BR_INFO(); + + goto recover_br_info; + + HANDLE_OP (WASM_OP_RETURN): + { + uint32 ret_idx; + WASMType *func_type; + uint32 off, ret_offset; + uint8 *ret_types; + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + func_type = cur_func->u.func_import->func_type; + else + func_type = cur_func->u.func->func_type; + + /* types of each return value */ + ret_types = func_type->types + func_type->param_count; + ret_offset = prev_frame->ret_offset; + + for (ret_idx = 0, off = sizeof(int16) * (func_type->result_count - 1); + ret_idx < func_type->result_count; + ret_idx++, off -= sizeof(int16)) { + if (ret_types[ret_idx] == VALUE_TYPE_I64 + || ret_types[ret_idx] == VALUE_TYPE_F64) { + *((uint64 *)(prev_frame->lp + ret_offset)) = + GET_OPERAND(uint64, off); + ret_offset += 2; + } + else { + prev_frame->lp[ret_offset] = GET_OPERAND(int32, off); + ret_offset++; + } + } + } + goto return_func; + + HANDLE_OP (WASM_OP_CALL_INDIRECT): + { + WASMType *cur_type, *cur_func_type; + WASMTableInstance *cur_table_inst; + +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + + tidx = read_uint32(frame_ip); + val = GET_OPERAND(int32, 0); + frame_ip += 2; + + if (tidx >= module->module->type_count) { + wasm_set_exception(module, "type index is overflow"); + goto got_exception; + } + cur_type = module->module->types[tidx]; + + /* careful, it might be a table in another module */ + cur_table_inst = table; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table->table_inst_linked) { + cur_table_inst = table->table_inst_linked; + } +#endif + + if (val < 0 || val >= (int32)cur_table_inst->cur_size) { + wasm_set_exception(module, "undefined element"); + goto got_exception; + } + + fidx = ((uint32*)cur_table_inst->base_addr)[val]; + if (fidx == (uint32)-1) { + wasm_set_exception(module, "uninitialized element"); + goto got_exception; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + + /* always call module own functions */ + cur_func = module->functions + fidx; + + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + cur_func_type = cur_func->u.func_import->func_type; + else + cur_func_type = cur_func->u.func->func_type; + if (!wasm_type_equal(cur_type, cur_func_type)) { + wasm_set_exception(module, "indirect call type mismatch"); + goto got_exception; + } + goto call_func_from_interp; + } + + /* parametric instructions */ + HANDLE_OP (WASM_OP_SELECT): + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { +#if defined(BUILD_TARGET_X86_32) + if (addr_ret != addr1) + bh_memcpy_s(frame_lp + addr_ret, sizeof(int32), + frame_lp + addr1, sizeof(int32)); +#else + frame_lp[addr_ret] = frame_lp[addr1]; +#endif + } + else { +#if defined(BUILD_TARGET_X86_32) + if (addr_ret != addr2) + bh_memcpy_s(frame_lp + addr_ret, sizeof(int32), + frame_lp + addr2, sizeof(int32)); +#else + frame_lp[addr_ret] = frame_lp[addr2]; +#endif + } + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SELECT_64): + { + cond = frame_lp[GET_OFFSET()]; + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + if (!cond) { +#if defined(BUILD_TARGET_X86_32) + if (addr_ret != addr1) + bh_memcpy_s(frame_lp + addr_ret, sizeof(int64), + frame_lp + addr1, sizeof(int64)); +#else + *(int64*)(frame_lp + addr_ret) = *(int64*)(frame_lp + addr1); +#endif + } + else { +#if defined(BUILD_TARGET_X86_32) + if (addr_ret != addr2) + bh_memcpy_s(frame_lp + addr_ret, sizeof(int64), + frame_lp + addr2, sizeof(int64)); +#else + *(int64*)(frame_lp + addr_ret) = *(int64*)(frame_lp + addr2); +#endif + } + HANDLE_OP_END (); + } + + /* variable instructions */ + HANDLE_OP (EXT_OP_SET_LOCAL_FAST): + HANDLE_OP (EXT_OP_TEE_LOCAL_FAST): + { + local_offset = *frame_ip++; + *(int32*)(frame_lp + local_offset) = GET_OPERAND(uint32, 0); + frame_ip += 2; + HANDLE_OP_END (); + } + + HANDLE_OP (EXT_OP_SET_LOCAL_FAST_I64): + HANDLE_OP (EXT_OP_TEE_LOCAL_FAST_I64): + { + local_offset = *frame_ip++; + PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), GET_OPERAND(uint64, 0)); + frame_ip += 2; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_GET_GLOBAL): + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = *(uint32*)global_addr; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_GET_GLOBAL_64): + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + addr_ret = GET_OFFSET(); + *(uint64 *)(frame_lp + addr_ret) = GET_I64_FROM_ADDR((uint32*)global_addr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL): + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + addr1 = GET_OFFSET(); + *(int32*)global_addr = frame_lp[addr1]; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL_AUX_STACK): + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + addr1 = GET_OFFSET(); + if (frame_lp[addr1] < exec_env->aux_stack_boundary) + goto out_of_bounds; + *(int32*)global_addr = frame_lp[addr1]; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_SET_GLOBAL_64): + { + global_idx = read_uint32(frame_ip); + bh_assert(global_idx < module->global_count); + global = globals + global_idx; +#if WASM_ENABLE_MULTI_MODULE == 0 + global_addr = global_data + global->data_offset; +#else + global_addr = global->import_global_inst + ? global->import_module_inst->global_data + + global->import_global_inst->data_offset + : global_data + global->data_offset; +#endif + addr1 = GET_OFFSET(); + PUT_I64_TO_ADDR((uint32*)global_addr, *(int64 *)(frame_lp + addr1)); + HANDLE_OP_END (); + } + + /* memory load instructions */ + HANDLE_OP (WASM_OP_I32_LOAD): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + frame_lp[addr_ret] = LOAD_I32(maddr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(8); + PUT_I64_TO_ADDR(frame_lp + addr_ret, LOAD_I64(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_LOAD8_S): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + frame_lp[addr_ret] = sign_ext_8_32(*(int8*)maddr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_LOAD8_U): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + frame_lp[addr_ret] = (uint32)(*(uint8*)maddr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_LOAD16_S): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + frame_lp[addr_ret] = sign_ext_16_32(LOAD_I16(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_LOAD16_U): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + frame_lp[addr_ret] = (uint32)(LOAD_U16(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD8_S): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + *(int64 *)(frame_lp + addr_ret) = sign_ext_8_64(*(int8*)maddr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD8_U): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(1); + *(int64 *)(frame_lp + addr_ret) = (uint64)(*(uint8*)maddr); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD16_S): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + *(int64 *)(frame_lp + addr_ret) = sign_ext_16_64(LOAD_I16(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD16_U): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(2); + *(int64 *)(frame_lp + addr_ret) = (uint64)(LOAD_U16(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD32_S): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + *(int64 *)(frame_lp + addr_ret) = sign_ext_32_64(LOAD_I32(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_LOAD32_U): + { + uint32 offset, addr; + offset = read_uint32(frame_ip); + addr = GET_OPERAND(uint32, 0); + frame_ip += 2; + addr_ret = GET_OFFSET(); + CHECK_MEMORY_OVERFLOW(4); + *(int64 *)(frame_lp + addr_ret) = (uint64)(LOAD_U32(maddr)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_STORE): + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, sval); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_STORE8): + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(1); + *(uint8*)maddr = (uint8)sval; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_STORE16): + { + uint32 offset, addr; + uint32 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint32, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE): + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(8); + STORE_I64(maddr, sval); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE8): + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(1); + *(uint8*)maddr = (uint8)sval; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE16): + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(2); + STORE_U16(maddr, (uint16)sval); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_STORE32): + { + uint32 offset, addr; + uint64 sval; + offset = read_uint32(frame_ip); + sval = GET_OPERAND(uint64, 0); + addr = GET_OPERAND(uint32, 2); + frame_ip += 4; + CHECK_MEMORY_OVERFLOW(4); + STORE_U32(maddr, (uint32)sval); + HANDLE_OP_END (); + } + + /* memory size and memory grow instructions */ + HANDLE_OP (WASM_OP_MEMORY_SIZE): + { + uint32 reserved; + addr_ret = GET_OFFSET(); + frame_lp[addr_ret] = memory->cur_page_count; + (void)reserved; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_MEMORY_GROW): + { + uint32 reserved, delta, prev_page_count = memory->cur_page_count; + + addr1 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + delta = (uint32)frame_lp[addr1]; + + if (!wasm_enlarge_memory(module, delta)) { + /* fail to memory.grow, return -1 */ + frame_lp[addr_ret] = -1; + } + else { + /* success, return previous page count */ + frame_lp[addr_ret] = prev_page_count; + /* update the memory instance ptr */ + memory = module->default_memory; + linear_mem_size = num_bytes_per_page * memory->cur_page_count; + } + + (void)reserved; + HANDLE_OP_END (); + } + + /* comparison instructions of i32 */ + HANDLE_OP (WASM_OP_I32_EQZ): + DEF_OP_EQZ(int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_EQ): + DEF_OP_CMP(uint32, I32, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_NE): + DEF_OP_CMP(uint32, I32, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LT_S): + DEF_OP_CMP(int32, I32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LT_U): + DEF_OP_CMP(uint32, I32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GT_S): + DEF_OP_CMP(int32, I32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GT_U): + DEF_OP_CMP(uint32, I32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LE_S): + DEF_OP_CMP(int32, I32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_LE_U): + DEF_OP_CMP(uint32, I32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GE_S): + DEF_OP_CMP(int32, I32, >=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_GE_U): + DEF_OP_CMP(uint32, I32, >=); + HANDLE_OP_END (); + + /* comparison instructions of i64 */ + HANDLE_OP (WASM_OP_I64_EQZ): + DEF_OP_EQZ(int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EQ): + DEF_OP_CMP(uint64, I64, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_NE): + DEF_OP_CMP(uint64, I64, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LT_S): + DEF_OP_CMP(int64, I64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LT_U): + DEF_OP_CMP(uint64, I64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GT_S): + DEF_OP_CMP(int64, I64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GT_U): + DEF_OP_CMP(uint64, I64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LE_S): + DEF_OP_CMP(int64, I64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_LE_U): + DEF_OP_CMP(uint64, I64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GE_S): + DEF_OP_CMP(int64, I64, >=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_GE_U): + DEF_OP_CMP(uint64, I64, >=); + HANDLE_OP_END (); + + /* comparison instructions of f32 */ + HANDLE_OP (WASM_OP_F32_EQ): + DEF_OP_CMP(float32, F32, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NE): + DEF_OP_CMP(float32, F32, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_LT): + DEF_OP_CMP(float32, F32, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_GT): + DEF_OP_CMP(float32, F32, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_LE): + DEF_OP_CMP(float32, F32, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_GE): + DEF_OP_CMP(float32, F32, >=); + HANDLE_OP_END (); + + /* comparison instructions of f64 */ + HANDLE_OP (WASM_OP_F64_EQ): + DEF_OP_CMP(float64, F64, ==); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NE): + DEF_OP_CMP(float64, F64, !=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_LT): + DEF_OP_CMP(float64, F64, <); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_GT): + DEF_OP_CMP(float64, F64, >); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_LE): + DEF_OP_CMP(float64, F64, <=); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_GE): + DEF_OP_CMP(float64, F64, >=); + HANDLE_OP_END (); + + /* numberic instructions of i32 */ + HANDLE_OP (WASM_OP_I32_CLZ): + DEF_OP_BIT_COUNT(uint32, I32, clz32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_CTZ): + DEF_OP_BIT_COUNT(uint32, I32, ctz32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_POPCNT): + DEF_OP_BIT_COUNT(uint32, I32, popcount32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_ADD): + DEF_OP_NUMERIC(uint32, uint32, I32, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_SUB): + DEF_OP_NUMERIC(uint32, uint32, I32, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_MUL): + DEF_OP_NUMERIC(uint32, uint32, I32, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_DIV_S): + { + int32 a, b; + + b = frame_lp[GET_OFFSET()]; + a = frame_lp[GET_OFFSET()]; + addr_ret = GET_OFFSET(); + if (a == (int32)0x80000000 && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_DIV_U): + { + uint32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = (uint32)frame_lp[addr1]; + a = (uint32)frame_lp[addr2]; + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_REM_S): + { + int32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = frame_lp[addr1]; + a = frame_lp[addr2]; + if (a == (int32)0x80000000 && b == -1) { + frame_lp[addr_ret] = 0; + HANDLE_OP_END (); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_REM_U): + { + uint32 a, b; + + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + addr_ret = GET_OFFSET(); + + b = (uint32)frame_lp[addr1]; + a = (uint32)frame_lp[addr2]; + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + frame_lp[addr_ret] = (a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_AND): + DEF_OP_NUMERIC(uint32, uint32, I32, &); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_OR): + DEF_OP_NUMERIC(uint32, uint32, I32, |); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_XOR): + DEF_OP_NUMERIC(uint32, uint32, I32, ^); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_SHL): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(uint32, uint32, I32, <<); +#else + DEF_OP_NUMERIC2(uint32, uint32, I32, <<); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_SHR_S): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(int32, uint32, I32, >>); +#else + DEF_OP_NUMERIC2(int32, uint32, I32, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_SHR_U): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC(uint32, uint32, I32, >>); +#else + DEF_OP_NUMERIC2(uint32, uint32, I32, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_ROTL): + { + uint32 a, b; + + b = (uint32)frame_lp[GET_OFFSET()]; + a = (uint32)frame_lp[GET_OFFSET()]; + frame_lp[GET_OFFSET()] = rotl32(a, b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_ROTR): + { + uint32 a, b; + + b = (uint32)frame_lp[GET_OFFSET()]; + a = (uint32)frame_lp[GET_OFFSET()]; + frame_lp[GET_OFFSET()] = rotr32(a, b); + HANDLE_OP_END (); + } + + /* numberic instructions of i64 */ + HANDLE_OP (WASM_OP_I64_CLZ): + DEF_OP_BIT_COUNT(uint64, I64, clz64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_CTZ): + DEF_OP_BIT_COUNT(uint64, I64, ctz64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_POPCNT): + DEF_OP_BIT_COUNT(uint64, I64, popcount64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_ADD): + DEF_OP_NUMERIC_64(uint64, uint64, I64, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_SUB): + DEF_OP_NUMERIC_64(uint64, uint64, I64, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_MUL): + DEF_OP_NUMERIC_64(uint64, uint64, I64, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_DIV_S): + { + int64 a, b; + + b = *(int64*)(frame_lp + GET_OFFSET()); + a = *(int64*)(frame_lp + GET_OFFSET()); + if (a == (int64)0x8000000000000000LL && b == -1) { + wasm_set_exception(module, "integer overflow"); + goto got_exception; + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + *(int64*)(frame_lp + GET_OFFSET()) = (a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_DIV_U): + { + uint64 a, b; + + b = *(uint64*)(frame_lp + GET_OFFSET()); + a = *(uint64*)(frame_lp + GET_OFFSET()); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + *(uint64*)(frame_lp + GET_OFFSET()) = (a / b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_REM_S): + { + int64 a, b; + + b = *(int64*)(frame_lp + GET_OFFSET()); + a = *(int64*)(frame_lp + GET_OFFSET()); + if (a == (int64)0x8000000000000000LL && b == -1) { + *(int64*)(frame_lp + GET_OFFSET()) = 0; + HANDLE_OP_END (); + } + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + *(int64*)(frame_lp + GET_OFFSET()) = (a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_REM_U): + { + uint64 a, b; + + b = *(uint64*)(frame_lp + GET_OFFSET()); + a = *(uint64*)(frame_lp + GET_OFFSET()); + if (b == 0) { + wasm_set_exception(module, "integer divide by zero"); + goto got_exception; + } + *(uint64*)(frame_lp + GET_OFFSET()) = (a % b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_AND): + DEF_OP_NUMERIC_64(uint64, uint64, I64, &); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_OR): + DEF_OP_NUMERIC_64(uint64, uint64, I64, |); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_XOR): + DEF_OP_NUMERIC_64(uint64, uint64, I64, ^); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_SHL): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(uint64, uint64, I64, <<); +#else + DEF_OP_NUMERIC2_64(uint64, uint64, I64, <<); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_SHR_S): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(int64, uint64, I64, >>); +#else + DEF_OP_NUMERIC2_64(int64, uint64, I64, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_SHR_U): + { +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_X86_32) + DEF_OP_NUMERIC_64(uint64, uint64, I64, >>); +#else + DEF_OP_NUMERIC2_64(uint64, uint64, I64, >>); +#endif + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_ROTL): + { + uint64 a, b; + + b = *(int64*)(frame_lp + GET_OFFSET()); + a = *(int64*)(frame_lp + GET_OFFSET()); + *(int64*)(frame_lp + GET_OFFSET()) = rotl64(a, b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I64_ROTR): + { + uint64 a, b; + + b = *(uint64*)(frame_lp + GET_OFFSET()); + a = *(uint64*)(frame_lp + GET_OFFSET()); + *(uint64*)(frame_lp + GET_OFFSET()) = rotr64(a, b); + HANDLE_OP_END (); + } + + /* numberic instructions of f32 */ + HANDLE_OP (WASM_OP_F32_ABS): + DEF_OP_MATH(float32, F32, fabs); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NEG): + { + int32 i32 = (int32)frame_lp[GET_OFFSET()]; + addr_ret = GET_OFFSET(); + int32 sign_bit = i32 & (1 << 31); + if (sign_bit) + frame_lp[addr_ret] = i32 & ~(1 << 31); + else + frame_lp[addr_ret] = (uint32)(i32 | (1 << 31)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_CEIL): + DEF_OP_MATH(float32, F32, ceil); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_FLOOR): + DEF_OP_MATH(float32, F32, floor); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_TRUNC): + DEF_OP_MATH(float32, F32, trunc); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_NEAREST): + DEF_OP_MATH(float32, F32, rint); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_SQRT): + DEF_OP_MATH(float32, F32, sqrt); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_ADD): + DEF_OP_NUMERIC(float32, float32, F32, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_SUB): + DEF_OP_NUMERIC(float32, float32, F32, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_MUL): + DEF_OP_NUMERIC(float32, float32, F32, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_DIV): + DEF_OP_NUMERIC(float32, float32, F32, /); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_MIN): + { + float32 a, b; + + b = *(float32*)(frame_lp + GET_OFFSET()); + a = *(float32*)(frame_lp + GET_OFFSET()); + + if (isnan(a)) + *(float32*)(frame_lp + GET_OFFSET()) = a; + else if (isnan(b)) + *(float32*)(frame_lp + GET_OFFSET()) = b; + else + *(float32*)(frame_lp + GET_OFFSET()) = wa_fmin(a, b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_MAX): + { + float32 a, b; + + b = *(float32*)(frame_lp + GET_OFFSET()); + a = *(float32*)(frame_lp + GET_OFFSET()); + + if (isnan(a)) + *(float32*)(frame_lp + GET_OFFSET()) = a; + else if (isnan(b)) + *(float32*)(frame_lp + GET_OFFSET()) = b; + else + *(float32*)(frame_lp + GET_OFFSET()) = wa_fmax(a, b); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F32_COPYSIGN): + { + float32 a, b; + + b = *(float32*)(frame_lp + GET_OFFSET()); + a = *(float32*)(frame_lp + GET_OFFSET()); + *(float32*)(frame_lp + GET_OFFSET()) = (signbit(b) ? -fabs(a) : fabs(a)); + HANDLE_OP_END (); + } + + /* numberic instructions of f64 */ + HANDLE_OP (WASM_OP_F64_ABS): + DEF_OP_MATH(float64, F64, fabs); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NEG): + { + int64 i64 = *(int64*)(frame_lp + GET_OFFSET()); + int64 sign_bit = i64 & (((int64)1) << 63); + if (sign_bit) + *(int64*)(frame_lp + GET_OFFSET()) = (uint64)i64 & ~(((uint64)1) << 63); + else + *(int64*)(frame_lp + GET_OFFSET()) = (uint64)i64 | (((uint64)1) << 63); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_CEIL): + DEF_OP_MATH(float64, F64, ceil); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_FLOOR): + DEF_OP_MATH(float64, F64, floor); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_TRUNC): + DEF_OP_MATH(float64, F64, trunc); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_NEAREST): + DEF_OP_MATH(float64, F64, rint); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_SQRT): + DEF_OP_MATH(float64, F64, sqrt); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_ADD): + DEF_OP_NUMERIC_64(float64, float64, F64, +); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_SUB): + DEF_OP_NUMERIC_64(float64, float64, F64, -); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_MUL): + DEF_OP_NUMERIC_64(float64, float64, F64, *); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_DIV): + DEF_OP_NUMERIC_64(float64, float64, F64, /); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_MIN): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + if (isnan(a)) + PUSH_F64(a); + else if (isnan(b)) + PUSH_F64(b); + else + PUSH_F64(wa_fmin(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_MAX): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + + if (isnan(a)) + PUSH_F64(a); + else if (isnan(b)) + PUSH_F64(b); + else + PUSH_F64(wa_fmax(a, b)); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_F64_COPYSIGN): + { + float64 a, b; + + b = POP_F64(); + a = POP_F64(); + PUSH_F64(signbit(b) ? -fabs(a) : fabs(a)); + HANDLE_OP_END (); + } + + /* conversions of i32 */ + HANDLE_OP (WASM_OP_I32_WRAP_I64): + { + int32 value = (int32)(POP_I64() & 0xFFFFFFFFLL); + PUSH_I32(value); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_TRUNC_S_F32): + /* We don't use INT32_MIN/INT32_MAX/UINT32_MIN/UINT32_MAX, + since float/double values of ieee754 cannot precisely represent + all int32/uint32/int64/uint64 values, e.g.: + UINT32_MAX is 4294967295, but (float32)4294967295 is 4294967296.0f, + but not 4294967295.0f. */ + DEF_OP_TRUNC_F32(-2147483904.0f, 2147483648.0f, true, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_U_F32): + DEF_OP_TRUNC_F32(-1.0f, 4294967296.0f, true, false); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_S_F64): + DEF_OP_TRUNC_F64(-2147483649.0, 2147483648.0, true, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_TRUNC_U_F64): + DEF_OP_TRUNC_F64(-1.0, 4294967296.0, true, false); + HANDLE_OP_END (); + + /* conversions of i64 */ + HANDLE_OP (WASM_OP_I64_EXTEND_S_I32): + DEF_OP_CONVERT(int64, I64, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND_U_I32): + DEF_OP_CONVERT(int64, I64, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_S_F32): + DEF_OP_TRUNC_F32(-9223373136366403584.0f, + 9223372036854775808.0f, false, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_U_F32): + DEF_OP_TRUNC_F32(-1.0f, 18446744073709551616.0f, + false, false); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_S_F64): + DEF_OP_TRUNC_F64(-9223372036854777856.0, + 9223372036854775808.0, false, true); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_TRUNC_U_F64): + DEF_OP_TRUNC_F64(-1.0, 18446744073709551616.0, + false, false); + HANDLE_OP_END (); + + /* conversions of f32 */ + HANDLE_OP (WASM_OP_F32_CONVERT_S_I32): + DEF_OP_CONVERT(float32, F32, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_U_I32): + DEF_OP_CONVERT(float32, F32, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_S_I64): + DEF_OP_CONVERT(float32, F32, int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_CONVERT_U_I64): + DEF_OP_CONVERT(float32, F32, uint64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F32_DEMOTE_F64): + DEF_OP_CONVERT(float32, F32, float64, F64); + HANDLE_OP_END (); + + /* conversions of f64 */ + HANDLE_OP (WASM_OP_F64_CONVERT_S_I32): + DEF_OP_CONVERT(float64, F64, int32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_U_I32): + DEF_OP_CONVERT(float64, F64, uint32, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_S_I64): + DEF_OP_CONVERT(float64, F64, int64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_CONVERT_U_I64): + DEF_OP_CONVERT(float64, F64, uint64, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_F64_PROMOTE_F32): + DEF_OP_CONVERT(float64, F64, float32, F32); + HANDLE_OP_END (); + + /* reinterpretations */ + HANDLE_OP (WASM_OP_I32_REINTERPRET_F32): + DEF_OP_REINTERPRET(float32); + HANDLE_OP_END (); + HANDLE_OP (WASM_OP_I64_REINTERPRET_F64): + DEF_OP_REINTERPRET(float64); + HANDLE_OP_END (); + HANDLE_OP (WASM_OP_F32_REINTERPRET_I32): + DEF_OP_REINTERPRET(int32); + HANDLE_OP_END (); + HANDLE_OP (WASM_OP_F64_REINTERPRET_I64): + DEF_OP_REINTERPRET(int64); + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_COPY_STACK_TOP): + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + frame_lp[addr2] = frame_lp[addr1]; + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_COPY_STACK_TOP_I64): + addr1 = GET_OFFSET(); + addr2 = GET_OFFSET(); + *(uint64*)(frame_lp + addr2) = *(uint64*)(frame_lp + addr1); + HANDLE_OP_END (); + + HANDLE_OP (EXT_OP_COPY_STACK_VALUES): + { + uint32 values_count, total_cell; + uint8 *cells; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + + /* read values_count */ + values_count = *(uint32*)frame_ip; + frame_ip += sizeof(values_count); + /* read total cell num */ + total_cell = *(uint32*)frame_ip; + frame_ip += sizeof(total_cell); + /* cells */ + cells = (uint8 *)frame_ip; + frame_ip += values_count * sizeof(uint8); + /* src offsets */ + src_offsets = (int16 *)frame_ip; + frame_ip += values_count * sizeof(int16); + /* dst offsets */ + dst_offsets = (uint16*)frame_ip; + frame_ip += values_count * sizeof(uint16); + + if (!copy_stack_values(module, frame_lp, + values_count, total_cell, + cells, src_offsets, + dst_offsets)) + goto got_exception; + + HANDLE_OP_END (); + } + HANDLE_OP (WASM_OP_SET_LOCAL): + HANDLE_OP (WASM_OP_TEE_LOCAL): + { + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + addr1 = GET_OFFSET(); + + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + *(int32*)(frame_lp + local_offset) = frame_lp[addr1]; + } + else if (local_type == VALUE_TYPE_I64 + || local_type == VALUE_TYPE_F64) { + PUT_I64_TO_ADDR((uint32*)(frame_lp + local_offset), + GET_I64_FROM_ADDR(frame_lp + addr1)); + } + else { + wasm_set_exception(module, "invalid local type"); + goto got_exception; + } + + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_I32_EXTEND8_S): + DEF_OP_CONVERT(int32, I32, int8, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I32_EXTEND16_S): + DEF_OP_CONVERT(int32, I32, int16, I32); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND8_S): + DEF_OP_CONVERT(int64, I64, int8, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND16_S): + DEF_OP_CONVERT(int64, I64, int16, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_I64_EXTEND32_S): + DEF_OP_CONVERT(int64, I64, int32, I64); + HANDLE_OP_END (); + + HANDLE_OP (WASM_OP_MISC_PREFIX): + { + GET_OPCODE(); + switch (opcode) + { + case WASM_OP_I32_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-2147483904.0f, 2147483648.0f, + true, true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 4294967296.0f, + true, false); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-2147483649.0, 2147483648.0, + true, true); + break; + case WASM_OP_I32_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 4294967296.0, + true, false); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + DEF_OP_TRUNC_SAT_F32(-9223373136366403584.0f, 9223372036854775808.0f, + false, true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F32: + DEF_OP_TRUNC_SAT_F32(-1.0f, 18446744073709551616.0f, + false, false); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + DEF_OP_TRUNC_SAT_F64(-9223372036854777856.0, 9223372036854775808.0, + false, true); + break; + case WASM_OP_I64_TRUNC_SAT_U_F64: + DEF_OP_TRUNC_SAT_F64(-1.0, 18446744073709551616.0, + false, false); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + { + uint32 addr, segment; + uint64 bytes, offset, seg_len; + uint8* data; + + segment = read_uint32(frame_ip); + + bytes = (uint64)POP_I32(); + offset = (uint64)POP_I32(); + addr = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(addr, bytes, maddr); + + seg_len = (uint64)module->module->data_segments[segment]->data_length; + data = module->module->data_segments[segment]->data; + if (offset + bytes > seg_len) + goto out_of_bounds; + + bh_memcpy_s(maddr, linear_mem_size - addr, + data + offset, bytes); + break; + } + case WASM_OP_DATA_DROP: + { + uint32 segment; + + segment = read_uint32(frame_ip); + + module->module->data_segments[segment]->data_length = 0; + + break; + } + case WASM_OP_MEMORY_COPY: + { + uint32 dst, src, len; + uint8 *mdst, *msrc; + + len = POP_I32(); + src = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(src, len, msrc); + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + /* allowing the destination and source to overlap */ + bh_memmove_s(mdst, linear_mem_size - dst, + msrc, len); + + break; + } + case WASM_OP_MEMORY_FILL: + { + uint32 dst, len; + uint8 val, *mdst; + + len = POP_I32(); + val = POP_I32(); + dst = POP_I32(); + + CHECK_BULK_MEMORY_OVERFLOW(dst, len, mdst); + + memset(mdst, val, len); + + break; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + break; + } + HANDLE_OP_END (); + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + HANDLE_OP (WASM_OP_ATOMIC_PREFIX): + { + uint32 offset, addr; + + GET_OPCODE(); + + offset = read_uint32(frame_ip); + + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + { + uint32 count, ret; + + count = POP_I32(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + ret = wasm_runtime_atomic_notify((WASMModuleInstanceCommon*)module, + maddr, count); + bh_assert((int32)ret >= 0); + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT32: + { + uint64 timeout; + uint32 expect, addr, ret; + + timeout = POP_I64(); + expect = POP_I32(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + ret = wasm_runtime_atomic_wait((WASMModuleInstanceCommon*)module, maddr, + (uint64)expect, timeout, false); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + case WASM_OP_ATOMIC_WAIT64: + { + uint64 timeout, expect; + uint32 ret; + + timeout = POP_I64(); + expect = POP_I64(); + addr = POP_I32(); + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(8); + + ret = wasm_runtime_atomic_wait((WASMModuleInstanceCommon*)module, + maddr, expect, timeout, true); + if (ret == (uint32)-1) + goto got_exception; + + PUSH_I32(ret); + break; + } + + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + { + uint32 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_LOAD8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + os_mutex_lock(&memory->mem_lock); + readv = (uint32)(*(uint8*)maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I32_LOAD16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + os_mutex_lock(&memory->mem_lock); + readv = (uint32)LOAD_U16(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I32(maddr); + os_mutex_unlock(&memory->mem_lock); + } + + PUSH_I32(readv); + break; + } + + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + { + uint64 readv; + + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_LOAD8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)(*(uint8*)maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U16(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_LOAD32_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U32(maddr); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(8); + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I64(maddr); + os_mutex_unlock(&memory->mem_lock); + } + + PUSH_I64(readv); + break; + } + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + { + uint32 sval; + + sval = (uint32)POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I32_STORE8) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + os_mutex_lock(&memory->mem_lock); + *(uint8*)maddr = (uint8)sval; + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I32_STORE16) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + os_mutex_lock(&memory->mem_lock); + STORE_U16(maddr, (uint16)sval); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + os_mutex_lock(&memory->mem_lock); + STORE_U32(maddr, sval); + os_mutex_unlock(&memory->mem_lock); + } + break; + } + + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + { + uint64 sval; + + sval = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_I64_STORE8) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + os_mutex_lock(&memory->mem_lock); + *(uint8*)maddr = (uint8)sval; + os_mutex_unlock(&memory->mem_lock); + } + else if(opcode == WASM_OP_ATOMIC_I64_STORE16) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + os_mutex_lock(&memory->mem_lock); + STORE_U16(maddr, (uint16)sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_I64_STORE32) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + os_mutex_lock(&memory->mem_lock); + STORE_U32(maddr, (uint32)sval); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(8); + os_mutex_lock(&memory->mem_lock); + STORE_I64(maddr, sval); + os_mutex_unlock(&memory->mem_lock); + } + break; + } + + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + { + uint32 readv, sval, expect; + + sval = POP_I32(); + expect = POP_I32(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + + os_mutex_lock(&memory->mem_lock); + readv = (uint32)(*(uint8*)maddr); + if (readv == expect) + *(uint8*)maddr = (uint8)(sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + + os_mutex_lock(&memory->mem_lock); + readv = (uint32)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + os_mutex_lock(&memory->mem_lock); + readv = LOAD_I32(maddr); + if (readv == expect) + STORE_U32(maddr, sval); + os_mutex_unlock(&memory->mem_lock); + } + PUSH_I32(readv); + break; + } + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + { + uint64 readv, sval, expect; + + sval = (uint64)POP_I64(); + expect = (uint64)POP_I64(); + addr = POP_I32(); + + if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 1, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(1); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)(*(uint8*)maddr); + if (readv == expect) + *(uint8*)maddr = (uint8)(sval); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 2, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(2); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U16(maddr); + if (readv == expect) + STORE_U16(maddr, (uint16)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else if (opcode == WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U) { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 4, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(4); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_U32(maddr); + if (readv == expect) + STORE_U32(maddr, (uint32)(sval)); + os_mutex_unlock(&memory->mem_lock); + } + else { + CHECK_BULK_MEMORY_OVERFLOW(addr + offset, 8, maddr); + CHECK_ATOMIC_MEMORY_ACCESS(8); + + os_mutex_lock(&memory->mem_lock); + readv = (uint64)LOAD_I64(maddr); + if (readv == expect) { + STORE_I64(maddr, sval); + } + os_mutex_unlock(&memory->mem_lock); + } + PUSH_I64(readv); + break; + } + + DEF_ATOMIC_RMW_OPCODE(ADD, +); + DEF_ATOMIC_RMW_OPCODE(SUB, -); + DEF_ATOMIC_RMW_OPCODE(AND, &); + DEF_ATOMIC_RMW_OPCODE(OR, |); + DEF_ATOMIC_RMW_OPCODE(XOR, ^); + /* xchg, ignore the read value, and store the given value: + readv * 0 + sval */ + DEF_ATOMIC_RMW_OPCODE(XCHG, *0 +); + } + + HANDLE_OP_END (); + } +#endif + + HANDLE_OP (WASM_OP_IMPDEP): + frame = prev_frame; + frame_ip = frame->ip; + goto call_func_from_entry; + + HANDLE_OP (WASM_OP_CALL): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + fidx = read_uint32(frame_ip); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->functions + fidx; + goto call_func_from_interp; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + default: + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 + HANDLE_OP (WASM_OP_UNUSED_0x06): + HANDLE_OP (WASM_OP_UNUSED_0x07): + HANDLE_OP (WASM_OP_UNUSED_0x08): + HANDLE_OP (WASM_OP_UNUSED_0x09): + HANDLE_OP (WASM_OP_UNUSED_0x0a): + HANDLE_OP (WASM_OP_UNUSED_0x12): + HANDLE_OP (WASM_OP_UNUSED_0x13): + HANDLE_OP (WASM_OP_UNUSED_0x14): + HANDLE_OP (WASM_OP_UNUSED_0x15): + HANDLE_OP (WASM_OP_UNUSED_0x16): + HANDLE_OP (WASM_OP_UNUSED_0x17): + HANDLE_OP (WASM_OP_UNUSED_0x18): + HANDLE_OP (WASM_OP_UNUSED_0x19): + HANDLE_OP (WASM_OP_UNUSED_0x1c): + HANDLE_OP (WASM_OP_UNUSED_0x1d): + HANDLE_OP (WASM_OP_UNUSED_0x1e): + HANDLE_OP (WASM_OP_UNUSED_0x1f): + /* optimized op code */ + HANDLE_OP (WASM_OP_F32_STORE): + HANDLE_OP (WASM_OP_F64_STORE): + HANDLE_OP (WASM_OP_F32_LOAD): + HANDLE_OP (WASM_OP_F64_LOAD): + HANDLE_OP (EXT_OP_GET_LOCAL_FAST): + HANDLE_OP (WASM_OP_GET_LOCAL): + HANDLE_OP (WASM_OP_F64_CONST): + HANDLE_OP (WASM_OP_I64_CONST): + HANDLE_OP (WASM_OP_F32_CONST): + HANDLE_OP (WASM_OP_I32_CONST): + HANDLE_OP (WASM_OP_DROP): + HANDLE_OP (WASM_OP_DROP_64): + HANDLE_OP (WASM_OP_BLOCK): + HANDLE_OP (WASM_OP_LOOP): + HANDLE_OP (WASM_OP_END): + HANDLE_OP (WASM_OP_NOP): + HANDLE_OP (EXT_OP_BLOCK): + HANDLE_OP (EXT_OP_LOOP): + HANDLE_OP (EXT_OP_IF): + { + wasm_set_exception(module, "unsupported opcode"); + goto got_exception; + } +#endif + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + continue; +#else + FETCH_OPCODE_AND_DISPATCH (); +#endif + + call_func_from_interp: + /* Only do the copy when it's called from interpreter. */ + { + WASMInterpFrame *outs_area = wasm_exec_env_wasm_stack_top(exec_env); + outs_area->lp = outs_area->operand + cur_func->const_cell_num; + for (int i = 0; i < cur_func->param_count; i++) { + if (cur_func->param_types[i] == VALUE_TYPE_I64 + || cur_func->param_types[i] == VALUE_TYPE_F64) { + *(int64*)(outs_area->lp) = + GET_OPERAND(int64, (2 * (cur_func->param_count - i - 1))); + outs_area->lp += 2; + } else { + *(outs_area->lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1))); + outs_area->lp ++; + } + } + frame_ip += cur_func->param_count * sizeof(int16); + if (cur_func->ret_cell_num != 0) { + /* Get the first return value's offset. Since loader emit all return + * values' offset so we must skip remain return values' offsets. + */ + WASMType *func_type; + if (cur_func->is_import_func +#if WASM_ENABLE_MULTI_MODULE != 0 + && !cur_func->import_func_inst +#endif + ) + func_type = cur_func->u.func_import->func_type; + else + func_type = cur_func->u.func->func_type; + frame->ret_offset = GET_OFFSET(); + frame_ip += 2 * (func_type->result_count - 1); + } + SYNC_ALL_TO_FRAME(); + prev_frame = frame; + } + + call_func_from_entry: + { + if (cur_func->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (cur_func->import_func_inst) { + wasm_interp_call_func_import(module, exec_env, cur_func, + prev_frame); + } + else +#endif + { + wasm_interp_call_func_native(module, exec_env, cur_func, + prev_frame); + } + + prev_frame = frame->prev_frame; + cur_func = frame->function; + UPDATE_ALL_FROM_FRAME(); + + memory = module->default_memory; + if (wasm_get_exception(module)) + goto got_exception; + } + else { + WASMFunction *cur_wasm_func = cur_func->u.func; + + all_cell_num = (uint64)cur_func->param_cell_num + + (uint64)cur_func->local_cell_num + + (uint64)cur_func->const_cell_num + + (uint64)cur_wasm_func->max_stack_cell_num; + if (all_cell_num >= UINT32_MAX) { + wasm_set_exception(module, "stack overflow"); + goto got_exception; + } + + frame_size = wasm_interp_interp_frame_size((uint32)all_cell_num); + if (!(frame = ALLOC_FRAME(exec_env, frame_size, prev_frame))) { + frame = prev_frame; + goto got_exception; + } + + /* Initialize the interpreter context. */ + frame->function = cur_func; + frame_ip = wasm_get_func_code(cur_func); + frame_ip_end = wasm_get_func_code_end(cur_func); + + frame_lp = frame->lp = frame->operand + cur_wasm_func->const_cell_num; + + /* Initialize the consts */ + bh_memcpy_s(frame->operand, all_cell_num * 4, + cur_wasm_func->consts, cur_wasm_func->const_cell_num * 4); + + /* Initialize the local varialbes */ + memset(frame_lp + cur_func->param_cell_num, 0, + (uint32)(cur_func->local_cell_num * 4)); + + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)frame); + } + HANDLE_OP_END (); + } + + return_func: + { + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)prev_frame); + + if (!prev_frame->ip) + /* Called from native. */ + return; + + RECOVER_CONTEXT(prev_frame); + HANDLE_OP_END (); + } + + (void)frame_ip_end; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + unaligned_atomic: + wasm_set_exception(module, "unaligned atomic"); + goto got_exception; +#endif + + out_of_bounds: + wasm_set_exception(module, "out of bounds memory access"); + + got_exception: + return; + +#if WASM_ENABLE_LABELS_AS_VALUES == 0 + } +#else + FETCH_OPCODE_AND_DISPATCH (); +#endif +} + +#if WASM_ENABLE_FAST_INTERP != 0 +void ** +wasm_interp_get_handle_table() +{ + WASMModuleInstance module; + memset(&module, 0, sizeof(WASMModuleInstance)); + wasm_interp_call_func_bytecode(&module, NULL, NULL, NULL); + return global_handle_table; +} +#endif + +void +wasm_interp_call_wasm(WASMModuleInstance *module_inst, + WASMExecEnv *exec_env, + WASMFunctionInstance *function, + uint32 argc, uint32 argv[]) +{ + WASMRuntimeFrame *prev_frame = wasm_exec_env_get_cur_frame(exec_env); + WASMInterpFrame *frame, *outs_area; + + /* Allocate sufficient cells for all kinds of return values. */ + unsigned all_cell_num = function->ret_cell_num > 2 ? + function->ret_cell_num : 2, i; + /* This frame won't be used by JITed code, so only allocate interp + frame here. */ + unsigned frame_size = wasm_interp_interp_frame_size(all_cell_num); + + if (argc != function->param_cell_num) { + char buf[128]; + snprintf(buf, sizeof(buf), + "invalid argument count %d, expected %d", + argc, function->param_cell_num); + wasm_set_exception(module_inst, buf); + return; + } + + if ((uint8*)&prev_frame < exec_env->native_stack_boundary) { + wasm_set_exception((WASMModuleInstance*)exec_env->module_inst, + "native stack overflow"); + return; + } + + if (!(frame = ALLOC_FRAME(exec_env, frame_size, (WASMInterpFrame*)prev_frame))) + return; + + outs_area = wasm_exec_env_wasm_stack_top(exec_env); + frame->function = NULL; + frame->ip = NULL; + /* There is no local variable. */ + frame->lp = frame->operand + 0; + frame->ret_offset = 0; + + if (argc > 0) + word_copy(outs_area->operand + function->const_cell_num, argv, argc); + + wasm_exec_env_set_cur_frame(exec_env, frame); + + if (function->is_import_func) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (function->import_module_inst) { + LOG_DEBUG("it is a function of a sub module"); + wasm_interp_call_func_import(module_inst, exec_env, + function, frame); + } + else +#endif + { + LOG_DEBUG("it is an native function"); + wasm_interp_call_func_native(module_inst, exec_env, + function, frame); + } + } + else { + wasm_interp_call_func_bytecode(module_inst, exec_env, function, frame); + } + + /* Output the return value to the caller */ + if (!wasm_get_exception(module_inst)) { + for (i = 0; i < function->ret_cell_num; i++) + argv[i] = *(frame->lp + i); + } + + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + FREE_FRAME(exec_env, frame); +#if WASM_ENABLE_OPCODE_COUNTER != 0 + wasm_interp_dump_op_count(); +#endif +} diff --git a/wamr/core/iwasm/interpreter/wasm_loader.c b/wamr/core/iwasm/interpreter/wasm_loader.c new file mode 100644 index 0000000..21e6b2f --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_loader.c @@ -0,0 +1,6870 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_loader.h" +#include "bh_common.h" +#include "bh_log.h" +#include "wasm.h" +#include "wasm_opcode.h" +#include "wasm_runtime.h" +#include "../common/wasm_native.h" + +/* Read a value of given type from the address pointed to by the given + pointer and increase the pointer to the position just after the + value being read. */ +#define TEMPLATE_READ_VALUE(Type, p) \ + (p += sizeof(Type), *(Type *)(p - sizeof(Type))) + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "WASM module load failed: %s", string); + } +} + +static void +set_error_buf_v(char *error_buf, uint32 error_buf_size, + const char *format, ...) +{ + va_list args; + char buf[128]; + + if (error_buf != NULL) { + va_start(args, format); + vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + snprintf(error_buf, error_buf_size, + "WASM module load failed: %s", buf); + } +} + +static bool +check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) +{ + if (buf + length > buf_end) { + set_error_buf(error_buf, error_buf_size, + "unexpected end of section or function"); + return false; + } + return true; +} + +static bool +check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length, + char *error_buf, uint32 error_buf_size) +{ + if (buf + length > buf_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + return true; +} + +#define CHECK_BUF(buf, buf_end, length) do { \ + if (!check_buf(buf, buf_end, length, \ + error_buf, error_buf_size)) { \ + goto fail; \ + } \ +} while (0) + +#define CHECK_BUF1(buf, buf_end, length) do { \ + if (!check_buf1(buf, buf_end, length, \ + error_buf, error_buf_size)) { \ + goto fail; \ + } \ +} while (0) + +static bool +skip_leb(const uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, + char* error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + if (bcnt + 1 > (maxbits + 6) / 7) { + set_error_buf(error_buf, error_buf_size, + "integer representation too long"); + return false; + } + + CHECK_BUF(buf, buf_end, offset + 1); + byte = buf[offset]; + offset += 1; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + *p_buf += offset; + return true; +fail: + return false; +} + +#define skip_leb_int64(p, p_end) do { \ + if (!skip_leb(&p, p_end, 64, \ + error_buf, error_buf_size)) \ + return false; \ +} while (0) + +#define skip_leb_uint32(p, p_end) do { \ + if (!skip_leb(&p, p_end, 32, \ + error_buf, error_buf_size)) \ + return false; \ +} while (0) + +#define skip_leb_int32(p, p_end) do { \ + if (!skip_leb(&p, p_end, 32, \ + error_buf, error_buf_size)) \ + return false; \ +} while (0) + +static bool +read_leb(uint8 **p_buf, const uint8 *buf_end, + uint32 maxbits, bool sign, uint64 *p_result, + char* error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + uint64 result = 0; + uint32 shift = 0; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + if (bcnt + 1 > (maxbits + 6) / 7) { + set_error_buf(error_buf, error_buf_size, + "integer representation too long"); + return false; + } + + CHECK_BUF(buf, buf_end, offset + 1); + byte = buf[offset]; + offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + if (!sign && maxbits == 32 && shift >= maxbits) { + /* The top bits set represent values > 32 bits */ + if (((uint8)byte) & 0xf0) + goto fail_integer_too_large; + } + else if (sign && maxbits == 32) { + if (shift < maxbits) { + /* Sign extend */ + result = (((int32)result) << (maxbits - shift)) + >> (maxbits - shift); + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x8; + int top_bits = ((uint8)byte) & 0xf0; + if ((sign_bit_set && top_bits != 0x70) + || (!sign_bit_set && top_bits != 0)) + goto fail_integer_too_large; + } + } + else if (sign && maxbits == 64) { + if (shift < maxbits) { + /* Sign extend */ + result = (((int64)result) << (maxbits - shift)) + >> (maxbits - shift); + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x1; + int top_bits = ((uint8)byte) & 0xfe; + + if ((sign_bit_set && top_bits != 0x7e) + || (!sign_bit_set && top_bits != 0)) + goto fail_integer_too_large; + } + } + + *p_buf += offset; + *p_result = result; + return true; + +fail_integer_too_large: + set_error_buf(error_buf, error_buf_size, "integer too large"); +fail: + return false; +} + +#define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) +#define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) +#define read_bool(p) TEMPLATE_READ_VALUE(bool, p) + +#define read_leb_int64(p, p_end, res) do { \ + uint64 res64; \ + if (!read_leb((uint8**)&p, p_end, 64, true, &res64,\ + error_buf, error_buf_size)) \ + goto fail; \ + res = (int64)res64; \ +} while (0) + +#define read_leb_uint32(p, p_end, res) do { \ + uint64 res64; \ + if (!read_leb((uint8**)&p, p_end, 32, false, &res64,\ + error_buf, error_buf_size)) \ + goto fail; \ + res = (uint32)res64; \ +} while (0) + +#define read_leb_int32(p, p_end, res) do { \ + uint64 res64; \ + if (!read_leb((uint8**)&p, p_end, 32, true, &res64,\ + error_buf, error_buf_size)) \ + goto fail; \ + res = (int32)res64; \ +} while (0) + +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static bool +check_utf8_str(const uint8* str, uint32 len) +{ + const uint8 *p = str, *p_end = str + len, *p_end1; + uint8 chr, n_bytes; + + while (p < p_end) { + chr = *p++; + if (chr >= 0x80) { + /* Calculate the byte count: the first byte must be + 110XXXXX, 1110XXXX, 11110XXX, 111110XX, or 1111110X, + the count of leading '1' denotes the total byte count */ + n_bytes = 0; + while ((chr & 0x80) != 0) { + chr = (uint8)(chr << 1); + n_bytes++; + } + + /* Check byte count */ + if (n_bytes < 2 || n_bytes > 6 + || p + n_bytes - 1 > p_end) + return false; + + /* Check the following bytes, which must be 10XXXXXX */ + p_end1 = p + n_bytes - 1; + while (p < p_end1) { + if (!(*p & 0x80) || (*p | 0x40)) + return false; + p++; + } + } + } + return true; +} + +static char* +const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, + char* error_buf, uint32 error_buf_size) +{ + StringNode *node, *node_next; + + if (!check_utf8_str(str, len)) { + set_error_buf(error_buf, error_buf_size, + "invalid UTF-8 encoding"); + return NULL; + } + + /* Search const str list */ + node = module->const_str_list; + while (node) { + node_next = node->next; + if (strlen(node->str) == len + && !memcmp(node->str, str, len)) + break; + node = node_next; + } + + if (node) { + LOG_DEBUG("reuse %s", node->str); + return node->str; + } + + if (!(node = loader_malloc(sizeof(StringNode) + len + 1, + error_buf, error_buf_size))) { + return NULL; + } + + node->str = ((char*)node) + sizeof(StringNode); + bh_memcpy_s(node->str, len + 1, str, len); + node->str[len] = '\0'; + + if (!module->const_str_list) { + /* set as head */ + module->const_str_list = node; + node->next = NULL; + } + else { + /* insert it */ + node->next = module->const_str_list; + module->const_str_list = node; + } + + return node->str; +} + +static bool +load_init_expr(const uint8 **p_buf, const uint8 *buf_end, + InitializerExpression *init_expr, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 flag, end_byte, *p_float; + uint32 i; + + CHECK_BUF(p, p_end, 1); + init_expr->init_expr_type = read_uint8(p); + flag = init_expr->init_expr_type; + + switch (flag) { + /* i32.const */ + case INIT_EXPR_TYPE_I32_CONST: + if (type != VALUE_TYPE_I32) + goto fail_type_mismatch; + read_leb_int32(p, p_end, init_expr->u.i32); + break; + /* i64.const */ + case INIT_EXPR_TYPE_I64_CONST: + if (type != VALUE_TYPE_I64) + goto fail_type_mismatch; + read_leb_int64(p, p_end, init_expr->u.i64); + break; + /* f32.const */ + case INIT_EXPR_TYPE_F32_CONST: + if (type != VALUE_TYPE_F32) + goto fail_type_mismatch; + CHECK_BUF(p, p_end, 4); + p_float = (uint8*)&init_expr->u.f32; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *p++; + break; + /* f64.const */ + case INIT_EXPR_TYPE_F64_CONST: + if (type != VALUE_TYPE_F64) + goto fail_type_mismatch; + CHECK_BUF(p, p_end, 8); + p_float = (uint8*)&init_expr->u.f64; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *p++; + break; + /* get_global */ + case INIT_EXPR_TYPE_GET_GLOBAL: + read_leb_uint32(p, p_end, init_expr->u.global_index); + break; + default: + goto fail_type_mismatch; + } + CHECK_BUF(p, p_end, 1); + end_byte = read_uint8(p); + if (end_byte != 0x0b) + goto fail_type_mismatch; + *p_buf = p; + + return true; +fail_type_mismatch: + set_error_buf(error_buf, error_buf_size, + "type mismatch or constant expression required"); +fail: + return false; +} + +static bool +load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_org; + uint32 type_count, param_count, result_count, i, j; + uint32 param_cell_num, ret_cell_num; + uint64 total_size; + uint8 flag; + WASMType *type; + + read_leb_uint32(p, p_end, type_count); + + if (type_count) { + module->type_count = type_count; + total_size = sizeof(WASMType*) * (uint64)type_count; + if (!(module->types = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < type_count; i++) { + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + if (flag != 0x60) { + set_error_buf(error_buf, error_buf_size, + "invalid type flag"); + return false; + } + + read_leb_uint32(p, p_end, param_count); + + /* Resolve param count and result count firstly */ + p_org = p; + CHECK_BUF(p, p_end, param_count); + p += param_count; + read_leb_uint32(p, p_end, result_count); + CHECK_BUF(p, p_end, result_count); + p = p_org; + + if (param_count > UINT16_MAX || result_count > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + return false; + } + + total_size = offsetof(WASMType, types) + + sizeof(uint8) * (uint64)(param_count + result_count); + if (!(type = module->types[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Resolve param types and result types */ + type->param_count = (uint16)param_count; + type->result_count = (uint16)result_count; + for (j = 0; j < param_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[j] = read_uint8(p); + } + read_leb_uint32(p, p_end, result_count); + for (j = 0; j < result_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[param_count + j] = read_uint8(p); + } + + param_cell_num = wasm_get_cell_num(type->types, param_count); + ret_cell_num = wasm_get_cell_num(type->types + param_count, + result_count); + if (param_cell_num > UINT16_MAX || ret_cell_num > UINT16_MAX) { + set_error_buf(error_buf, error_buf_size, + "param count or result count too large"); + return false; + } + type->param_cell_num = (uint16)param_cell_num; + type->ret_cell_num = (uint16)ret_cell_num; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load type section success.\n"); + return true; +fail: + return false; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +/** + * Find export item of a module with export info: + * module name, field name and export kind + */ +static WASMExport * +wasm_loader_find_export(const WASMModule *module, + const char *module_name, + const char *field_name, + uint8 export_kind, + uint32 export_index_boundary, + char *error_buf, uint32 error_buf_size) +{ + WASMExport *export; + uint32 i; + + for (i = 0, export = module->exports; i < module->export_count; + ++i, ++export) { + /** + * need to consider a scenario that different kinds of exports + * may have the same name, like + * (table (export "m1" "exported") 10 funcref) + * (memory (export "m1" "exported") 10) + **/ + if (export->kind == export_kind && !strcmp(field_name, export->name)) { + break; + } + } + + if (i == module->export_count) { + LOG_DEBUG("can not find an export %d named %s in the module %s", + export_kind, field_name, module_name); + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return NULL; + } + + if (export->index >= export_index_boundary) { + LOG_DEBUG("%s in the module %s is out of index (%d >= %d )", + field_name, module_name, + export->index, export_index_boundary); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return export; +} + +static WASMFunction * +wasm_loader_resolve_function(const char *module_name, + const char *function_name, + const WASMType *expected_function_type, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMFunction *function = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + WASMType *target_function_type = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for function", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = wasm_loader_find_export(module, module_name, function_name, + EXPORT_KIND_FUNC, + module->import_function_count + + module->function_count, + error_buf, error_buf_size); + if (!export) { + return NULL; + } + + /* run a function type check */ + if (export->index < module->import_function_count) { + target_function_type = + module->import_functions[export->index].u.function.func_type; + function = module->import_functions[export->index] + .u.function.import_func_linked; + } + else { + target_function_type = + module->functions[export->index - module->import_function_count] + ->func_type; + function = + module->functions[export->index - module->import_function_count]; + } + + if (!wasm_type_equal(expected_function_type, target_function_type)) { + LOG_DEBUG("%s.%s failed the type check", module_name, function_name); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return function; +} + +static WASMTable * +wasm_loader_resolve_table(const char *module_name, const char *table_name, + uint32 init_size, uint32 max_size, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMTable *table = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for table", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = wasm_loader_find_export(module, module_name, table_name, + EXPORT_KIND_TABLE, + module->table_count + + module->import_table_count, + error_buf, error_buf_size); + if (!export) { + return NULL; + } + + /* run a table type check */ + if (export->index < module->import_table_count) { + table = + module->import_tables[export->index].u.table.import_table_linked; + } + else { + table = &(module->tables[export->index - module->import_table_count]); + } + if (table->init_size < init_size || table->max_size > max_size) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, table_name, table->init_size, table->max_size, + init_size, max_size); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + + return table; +} + +static WASMMemory * +wasm_loader_resolve_memory(const char *module_name, const char *memory_name, + uint32 init_page_count, uint32 max_page_count, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMMemory *memory = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for memory", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = wasm_loader_find_export(module, module_name, memory_name, + EXPORT_KIND_MEMORY, + module->import_memory_count + + module->memory_count, + error_buf, error_buf_size); + if (!export) { + return NULL; + } + + + /* run a memory check */ + if (export->index < module->import_memory_count) { + memory = + module->import_memories[export->index].u.memory.import_memory_linked; + } + else { + memory = + &(module->memories[export->index - module->import_memory_count]); + } + if (memory->init_page_count < init_page_count || + memory->max_page_count > max_page_count) { + LOG_DEBUG("%s,%s failed type check(%d-%d), expected(%d-%d)", + module_name, memory_name, memory->init_page_count, + memory->max_page_count, init_page_count, max_page_count); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return memory; +} + +static WASMGlobal * +wasm_loader_resolve_global(const char *module_name, + const char *global_name, + uint8 type, bool is_mutable, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleCommon *module_reg; + WASMGlobal *global = NULL; + WASMExport *export = NULL; + WASMModule *module = NULL; + + module_reg = wasm_runtime_find_module_registered(module_name); + if (!module_reg + || module_reg->module_type != Wasm_Module_Bytecode) { + LOG_DEBUG("can not find a module named %s for global", module_name); + set_error_buf(error_buf, error_buf_size, "unknown import"); + return NULL; + } + + module = (WASMModule *)module_reg; + export = wasm_loader_find_export(module, module_name, global_name, + EXPORT_KIND_GLOBAL, + module->import_global_count + + module->global_count, + error_buf, error_buf_size); + if (!export) { + return NULL; + } + + /* run a global check */ + if (export->index < module->import_global_count) { + global = + module->import_globals[export->index].u.global.import_global_linked; + } else { + global = + &(module->globals[export->index - module->import_global_count]); + } + if (global->type != type || global->is_mutable != is_mutable) { + LOG_DEBUG("%s,%s failed type check(%d, %d), expected(%d, %d)", + module_name, global_name, global->type, global->is_mutable, + type, is_mutable); + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return NULL; + } + return global; +} +#endif /* end of WASM_ENABLE_MULTI_MODULE */ + +static bool +load_function_import(const WASMModule *parent_module, WASMModule *sub_module, + char *sub_module_name, char *function_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMFunctionImport *function, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_type_index = 0; + WASMType *declare_func_type = NULL; + WASMFunction *linked_func = NULL; + const char *linked_signature = NULL; + void *linked_attachment = NULL; + bool linked_call_conv_raw = false; + bool is_built_in_module = false; + + CHECK_BUF(p, p_end, 1); + read_leb_uint32(p, p_end, declare_type_index); + *p_buf = p; + + if (declare_type_index >= parent_module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + return false; + } + + declare_func_type = parent_module->types[declare_type_index]; + + is_built_in_module = wasm_runtime_is_built_in_module(sub_module_name); + if (is_built_in_module) { + LOG_DEBUG("%s is a function of a built-in module %s", + function_name, sub_module_name); + /* check built-in modules */ + linked_func = wasm_native_resolve_symbol(sub_module_name, + function_name, + declare_func_type, + &linked_signature, + &linked_attachment, + &linked_call_conv_raw); + } +#if WASM_ENABLE_MULTI_MODULE != 0 + else { + LOG_DEBUG("%s is a function of a sub-module %s", + function_name, sub_module_name); + linked_func = wasm_loader_resolve_function(sub_module_name, + function_name, + declare_func_type, + error_buf, + error_buf_size); + } +#endif + + if (!linked_func) { +#if WASM_ENABLE_SPEC_TEST != 0 + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; +#else +#if WASM_ENABLE_WAMR_COMPILER == 0 + LOG_WARNING("warning: fail to link import function (%s, %s)", + sub_module_name, function_name); +#endif +#endif + } + + function->module_name = sub_module_name; + function->field_name = function_name; + function->func_type = declare_func_type; + /* func_ptr_linked is for built-in functions */ + function->func_ptr_linked = is_built_in_module ? linked_func : NULL; + function->signature = linked_signature; + function->attachment = linked_attachment; + function->call_conv_raw = linked_call_conv_raw; +#if WASM_ENABLE_MULTI_MODULE != 0 + function->import_module = is_built_in_module ? NULL : sub_module; + /* can not set both func_ptr_linked and import_func_linked not NULL */ + function->import_func_linked = is_built_in_module ? NULL : linked_func; +#endif + return true; +fail: + return false; +} + +static bool +check_table_max_size(uint32 init_size, uint32 max_size, + char *error_buf, uint32 error_buf_size) +{ + if (max_size < init_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } + return true; +} + +static bool +load_table_import(WASMModule *sub_module, const char *sub_module_name, + const char *table_name, const uint8 **p_buf, + const uint8 *buf_end, WASMTableImport *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_elem_type = 0; + uint32 declare_max_size_flag = 0; + uint32 declare_init_size = 0; + uint32 declare_max_size = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMTable *linked_table = NULL; +#endif + + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + declare_elem_type = read_uint8(p); + if (TABLE_ELEM_TYPE_ANY_FUNC != declare_elem_type) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } + + read_leb_uint32(p, p_end, declare_max_size_flag); + read_leb_uint32(p, p_end, declare_init_size); + if (declare_max_size_flag & 1) { + read_leb_uint32(p, p_end, declare_max_size); + if (!check_table_max_size(table->init_size, table->max_size, + error_buf, error_buf_size)) + return false; + } else { + declare_max_size = 0x10000; + } + *p_buf = p; + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + linked_table = wasm_loader_resolve_table( + sub_module_name, table_name, + declare_init_size, declare_max_size, + error_buf, error_buf_size); + if (!linked_table) { + LOG_DEBUG("(%s, %s) is not an exported from one of modules", + table_name, sub_module_name); + return false; + } + + /** + * reset with linked table limit + */ + declare_elem_type = linked_table->elem_type; + declare_init_size = linked_table->init_size; + declare_max_size = linked_table->max_size; + declare_max_size_flag = linked_table->flags; + table->import_table_linked = linked_table; + table->import_module = sub_module; + } +#endif + + /* (table (export "table") 10 20 funcref) */ + if (!strcmp("spectest", sub_module_name)) { + uint32 spectest_table_init_size = 10; + uint32 spectest_table_max_size = 20; + + if (strcmp("table", table_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } + + if (declare_init_size > spectest_table_init_size + || declare_max_size < spectest_table_max_size) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_size = spectest_table_init_size; + declare_max_size = spectest_table_max_size; + } + + /* now we believe all declaration are ok */ + table->elem_type = declare_elem_type; + table->init_size = declare_init_size; + table->flags = declare_max_size_flag; + table->max_size = declare_max_size; + return true; +fail: + return false; +} + +unsigned +wasm_runtime_memory_pool_size(); + +static bool +check_memory_init_size(uint32 init_size, + char *error_buf, uint32 error_buf_size) +{ + if (init_size > 65536) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return false; + } + return true; +} + +static bool +check_memory_max_size(uint32 init_size, uint32 max_size, + char *error_buf, uint32 error_buf_size) +{ + if (max_size < init_size) { + set_error_buf(error_buf, error_buf_size, + "size minimum must not be greater than maximum"); + return false; + } + + if (max_size > 65536) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return false; + } + return true; +} + +static bool +load_memory_import(WASMModule *sub_module, const char *sub_module_name, + const char *memory_name, const uint8 **p_buf, + const uint8 *buf_end, WASMMemoryImport *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 pool_size = wasm_runtime_memory_pool_size(); +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count = pool_size / DEFAULT_NUM_BYTES_PER_PAGE; +#endif /* WASM_ENABLE_APP_FRAMEWORK */ + uint32 declare_max_page_count_flag = 0; + uint32 declare_init_page_count = 0; + uint32 declare_max_page_count = 0; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMMemory *linked_memory = NULL; +#endif + + read_leb_uint32(p, p_end, declare_max_page_count_flag); + read_leb_uint32(p, p_end, declare_init_page_count); + if (!check_memory_init_size(declare_init_page_count, error_buf, + error_buf_size)) { + return false; + } + + if (declare_max_page_count_flag & 1) { + read_leb_uint32(p, p_end, declare_max_page_count); + if (!check_memory_max_size(declare_init_page_count, + declare_max_page_count, error_buf, + error_buf_size)) { + return false; + } + if (declare_max_page_count > max_page_count) { + declare_max_page_count = max_page_count; + } + } + else { + /* Limit the maximum memory size to max_page_count */ + declare_max_page_count = max_page_count; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + linked_memory = wasm_loader_resolve_memory( + sub_module_name, memory_name, + declare_init_page_count, declare_max_page_count, + error_buf, error_buf_size); + if (!linked_memory) { + return false; + } + + /** + * reset with linked memory limit + */ + memory->import_module = sub_module; + memory->import_memory_linked = linked_memory; + declare_init_page_count = linked_memory->init_page_count; + declare_max_page_count = linked_memory->max_page_count; + } +#endif + + /* (memory (export "memory") 1 2) */ + if (!strcmp("spectest", sub_module_name)) { + uint32 spectest_memory_init_page = 1; + uint32 spectest_memory_max_page = 2; + + if (strcmp("memory", memory_name)) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type or unknown import"); + return false; + } + + if (declare_init_page_count > spectest_memory_init_page + || declare_max_page_count < spectest_memory_max_page) { + set_error_buf(error_buf, error_buf_size, + "incompatible import type"); + return false; + } + + declare_init_page_count = spectest_memory_init_page; + declare_max_page_count = spectest_memory_max_page; + } + + /* now we believe all declaration are ok */ + memory->flags = declare_max_page_count_flag; + memory->init_page_count = declare_init_page_count; + memory->max_page_count = declare_max_page_count; + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +fail: + return false; +} + +static bool +load_global_import(const WASMModule *parent_module, + WASMModule *sub_module, + char *sub_module_name, char *global_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMGlobalImport *global, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 declare_type = 0; + uint8 declare_mutable = 0; + bool is_mutable = false; + bool ret = false; + + CHECK_BUF(p, p_end, 2); + declare_type = read_uint8(p); + declare_mutable = read_uint8(p); + *p_buf = p; + + if (declare_mutable >= 2) { + set_error_buf(error_buf, error_buf_size, "invalid mutability"); + return false; + } + + is_mutable = declare_mutable & 1 ? true : false; + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + ret = wasm_runtime_is_built_in_module(sub_module_name); + if (ret) { + /* check built-in modules */ + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, + global_name, global); + if (ret) { + LOG_DEBUG("(%s, %s) is a global of a built-in module", + sub_module_name, global_name); + } + } +#endif /* WASM_ENABLE_LIBC_BUILTIN */ + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (!ret) { + /* check sub modules */ + WASMGlobal *linked_global = + wasm_loader_resolve_global(sub_module_name, global_name, + declare_type, declare_mutable, + error_buf, error_buf_size); + if (linked_global) { + LOG_DEBUG("(%s, %s) is a global of external module", + sub_module_name, global_name); + global->import_module = sub_module; + global->import_global_linked = linked_global; + ret = true; + } + } +#endif + + if (!ret) { +#if WASM_ENABLE_SPEC_TEST != 0 + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; +#endif + } + + global->module_name = sub_module_name; + global->field_name = global_name; + global->type = declare_type; + global->is_mutable = is_mutable; + return true; +fail: + return false; +} + +static bool +load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + table->elem_type = read_uint8(p); + if (TABLE_ELEM_TYPE_ANY_FUNC != table->elem_type) { + set_error_buf(error_buf, error_buf_size, "incompatible import type"); + return false; + } + + p_org = p; + read_leb_uint32(p, p_end, table->flags); + if (p - p_org > 1) { + set_error_buf(error_buf, error_buf_size, + "integer representation too long"); + return false; + } + if (table->flags > 1) { + set_error_buf(error_buf, error_buf_size, "integer too large"); + return false; + } + + read_leb_uint32(p, p_end, table->init_size); + if (table->flags == 0) { + table->max_size = 0x10000; + } + else if (table->flags == 1) { + read_leb_uint32(p, p_end, table->max_size); + if (!check_table_max_size(table->init_size, table->max_size, + error_buf, error_buf_size)) + return false; + } + + *p_buf = p; + return true; +fail: + return false; +} + +static bool +load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 pool_size = wasm_runtime_memory_pool_size(); +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count = pool_size / DEFAULT_NUM_BYTES_PER_PAGE; +#endif + + p_org = p; + read_leb_uint32(p, p_end, memory->flags); + if (p - p_org > 1) { + set_error_buf(error_buf, error_buf_size, + "integer representation too long"); + return false; + } +#if WASM_ENABLE_SHARED_MEMORY == 0 + if (memory->flags > 1) { + set_error_buf(error_buf, error_buf_size, "integer too large"); + return false; + } +#else + if (memory->flags > 3) { + set_error_buf(error_buf, error_buf_size, "integer too large"); + return false; + } + else if (memory->flags == 2) { + set_error_buf(error_buf, error_buf_size, "shared memory must have maximum"); + return false; + } +#endif + + read_leb_uint32(p, p_end, memory->init_page_count); + if (!check_memory_init_size(memory->init_page_count, + error_buf, error_buf_size)) + return false; + + if (memory->flags & 1) { + read_leb_uint32(p, p_end, memory->max_page_count); + if (!check_memory_max_size(memory->init_page_count, + memory->max_page_count, + error_buf, error_buf_size)) + return false; + if (memory->max_page_count > max_page_count) + memory->max_page_count = max_page_count; + } + else { + /* Limit the maximum memory size to max_page_count */ + memory->max_page_count = max_page_count; + } + + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +fail: + return false; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModule * +search_sub_module(const WASMModule *parent_module, const char *sub_module_name) +{ + WASMRegisteredModule *node = + bh_list_first_elem(parent_module->import_module_list); + while (node && strcmp(sub_module_name, node->module_name)) { + node = bh_list_elem_next(node); + } + return node ? (WASMModule*)node->module : NULL; +} + +static bool +register_sub_module(const WASMModule *parent_module, + const char *sub_module_name, WASMModule *sub_module) +{ + /* register a sub_module on its parent sub module list */ + WASMRegisteredModule *node = NULL; + bh_list_status ret; + + if (search_sub_module(parent_module, sub_module_name)) { + LOG_DEBUG("%s has been registered in its parent", sub_module_name); + return true; + } + + node = wasm_runtime_malloc(sizeof(WASMRegisteredModule)); + if (!node) { + LOG_DEBUG("malloc WASMRegisteredModule failed. SZ %d\n", + sizeof(WASMRegisteredModule)); + return false; + } + + node->module_name = sub_module_name; + node->module = (WASMModuleCommon*)sub_module; + ret = bh_list_insert(parent_module->import_module_list, node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + return true; +} + +static WASMModule * +load_depended_module(const WASMModule *parent_module, + const char *sub_module_name, char *error_buf, + uint32 error_buf_size) +{ + WASMModule *sub_module = NULL; + bool ret = false; + uint8 *buffer = NULL; + uint32 buffer_size = 0; + const module_reader reader = wasm_runtime_get_module_reader(); + const module_destroyer destroyer = wasm_runtime_get_module_destroyer(); + + /* check the registered module list of the parent */ + sub_module = search_sub_module(parent_module, sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded before", sub_module_name); + return sub_module; + } + + /* check the global registered module list */ + sub_module = + (WASMModule *)wasm_runtime_find_module_registered(sub_module_name); + if (sub_module) { + LOG_DEBUG("%s has been loaded", sub_module_name); + goto REGISTER_SUB_MODULE; + } + + LOG_VERBOSE("to load %s", sub_module_name); + + if (!reader) { + LOG_DEBUG("error: there is no sub_module reader to load %s", + sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "no sub module reader to load %s", + sub_module_name); + return NULL; + } + + /* start to maintain a loading module list */ + ret = wasm_runtime_is_loading_module(sub_module_name); + if (ret) { + LOG_DEBUG("find a circular dependency on %s", sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "found circular dependency on %s", + sub_module_name); + return NULL; + } + + ret = wasm_runtime_add_loading_module(sub_module_name, error_buf, + error_buf_size); + if (!ret) { + LOG_DEBUG("can not add %s into loading module list\n", + sub_module_name); + return NULL; + } + + ret = reader(sub_module_name, &buffer, &buffer_size); + if (!ret) { + LOG_DEBUG("read the file of %s failed", sub_module_name); + set_error_buf_v(error_buf, error_buf_size, + "failed to read module file of %s", + sub_module_name); + goto DELETE_FROM_LOADING_LIST; + } + + sub_module = + wasm_loader_load(buffer, buffer_size, error_buf, error_buf_size); + if (!sub_module) { + LOG_DEBUG("error: can not load the sub_module %s", sub_module_name); + /* + * others will be destroyed in runtime_destroy() + */ + goto DESTROY_FILE_BUFFER; + } + + wasm_runtime_delete_loading_module(sub_module_name); + + /* register on a global list */ + ret = wasm_runtime_register_module_internal(sub_module_name, + (WASMModuleCommon*)sub_module, + buffer, buffer_size, error_buf, + error_buf_size); + if (!ret) { + LOG_DEBUG("error: can not register module %s globally\n", + sub_module_name); + /* + * others will be unload in runtime_destroy() + */ + goto UNLOAD_MODULE; + } + + /* register on its parent list */ +REGISTER_SUB_MODULE: + ret = register_sub_module(parent_module, sub_module_name, sub_module); + if (!ret) { + set_error_buf_v(error_buf, error_buf_size, + "failed to register sub module %s", + sub_module_name); + /* + * since it is in the global module list, there is no need to + * unload the module. the runtime_destroy() will do it + */ + return NULL; + } + + return sub_module; + +UNLOAD_MODULE: + wasm_loader_unload(sub_module); + +DESTROY_FILE_BUFFER: + if (destroyer) { + destroyer(buffer, buffer_size); + } + else { + LOG_WARNING("need to release the reading buffer of %s manually", + sub_module_name); + } + +DELETE_FROM_LOADING_LIST: + wasm_runtime_delete_loading_module(sub_module_name); + return NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +static bool +load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_old; + uint32 import_count, name_len, type_index, i, u32, flags; + uint64 total_size; + WASMImport *import; + WASMImport *import_functions = NULL, *import_tables = NULL; + WASMImport *import_memories = NULL, *import_globals = NULL; + char *sub_module_name, *field_name; + uint8 u8, kind; + + read_leb_uint32(p, p_end, import_count); + + if (import_count) { + module->import_count = import_count; + total_size = sizeof(WASMImport) * (uint64)import_count; + if (!(module->imports = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + p_old = p; + + /* Scan firstly to get import count of each type */ + for (i = 0; i < import_count; i++) { + /* module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + /* field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + read_leb_uint32(p, p_end, type_index); + module->import_function_count++; + break; + + case IMPORT_KIND_TABLE: /* import table */ + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + u8 = read_uint8(p); + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_table_count++; + if (module->import_table_count > 1) { + set_error_buf(error_buf, error_buf_size, + "multiple tables"); + return false; + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_memory_count++; + if (module->import_memory_count > 1) { + set_error_buf(error_buf, error_buf_size, + "multiple memories"); + return false; + } + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + CHECK_BUF(p, p_end, 2); + p += 2; + module->import_global_count++; + break; + + default: + set_error_buf(error_buf, error_buf_size, + "invalid import kind"); + return false; + } + } + + if (module->import_function_count) + import_functions = module->import_functions = module->imports; + if (module->import_table_count) + import_tables = module->import_tables = + module->imports + module->import_function_count; + if (module->import_memory_count) + import_memories = module->import_memories = + module->imports + module->import_function_count + module->import_table_count; + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + module->import_table_count + + module->import_memory_count; + + p = p_old; + + /* TODO: move it out of the loop */ + /* insert "env", "wasi_unstable" and "wasi_snapshot_preview1" to const str list */ + if (!const_str_list_insert((uint8*)"env", 3, module, error_buf, error_buf_size) + || !const_str_list_insert((uint8*)"wasi_unstable", 13, module, + error_buf, error_buf_size) + || !const_str_list_insert((uint8*)"wasi_snapshot_preview1", 22, module, + error_buf, error_buf_size)) { + return false; + } + + /* Scan again to read the data */ + for (i = 0; i < import_count; i++) { + WASMModule *sub_module = NULL; + + /* load module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(sub_module_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { + return false; + } + p += name_len; + + /* load field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(field_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { + return false; + } + p += name_len; + + LOG_DEBUG("import #%d: (%s, %s)", i, sub_module_name, field_name); +#if WASM_ENABLE_MULTI_MODULE != 0 + /* assume built-in modules have been loaded */ + if (!wasm_runtime_is_built_in_module(sub_module_name)) { + LOG_DEBUG("%s is an exported field of a %s", field_name, + sub_module_name); + /* + * if it returns well, guarantee that + * the sub_module_name and its dependencies + * have been loaded well + */ + sub_module = load_depended_module(module, sub_module_name, + error_buf, error_buf_size); + if (!sub_module) { + return false; + } + } +#endif + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + bh_assert(import_functions); + import = import_functions++; + if (!load_function_import(module, sub_module, + sub_module_name, field_name, &p, + p_end, &import->u.function, + error_buf, error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_TABLE: /* import table */ + bh_assert(import_tables); + import = import_tables++; + if (!load_table_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.table, + error_buf, + error_buf_size)) { + LOG_DEBUG("can not import such a table (%s,%s)", + sub_module_name, field_name); + return false; + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + bh_assert(import_memories); + import = import_memories++; + if (!load_memory_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.memory, + error_buf, + error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + bh_assert(import_globals); + import = import_globals++; + if (!load_global_import(module, sub_module, + sub_module_name, field_name, + &p, p_end, &import->u.global, + error_buf, error_buf_size)) { + return false; + } + break; + + default: + set_error_buf(error_buf, error_buf_size, + "invalid import kind"); + return false; + } + import->kind = kind; + import->u.names.module_name = sub_module_name; + import->u.names.field_name = field_name; + (void)sub_module; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + import = module->import_functions; + for (i = 0; i < module->import_function_count; i++, import++) { + if (!strcmp(import->u.names.module_name, "wasi_unstable") + || !strcmp(import->u.names.module_name, "wasi_snapshot_preview1")) { + module->is_wasi_module = true; + break; + } + } +#endif + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load import section success.\n"); + (void)u8; + (void)u32; + (void)type_index; + return true; +fail: + return false; +} + +static bool +init_function_local_offsets(WASMFunction *func, + char *error_buf, uint32 error_buf_size) +{ + WASMType *param_type = func->func_type; + uint32 param_count = param_type->param_count; + uint8 *param_types = param_type->types; + uint32 local_count = func->local_count; + uint8 *local_types = func->local_types; + uint32 i, local_offset = 0; + uint64 total_size = sizeof(uint16) * ((uint64)param_count + local_count); + + if (!(func->local_offsets = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < param_count; i++) { + func->local_offsets[i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(param_types[i]); + } + + for (i = 0; i < local_count; i++) { + func->local_offsets[param_count + i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(local_types[i]); + } + + bh_assert(local_offset == func->param_cell_num + func->local_cell_num); + return true; +} + +static bool +load_function_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_code, const uint8 *buf_code_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_code = buf_code, *p_code_end, *p_code_save; + uint32 func_count; + uint64 total_size; + uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; + uint32 local_count, local_set_count, sub_local_count; + uint8 type; + WASMFunction *func; + + read_leb_uint32(p, p_end, func_count); + + if (buf_code) + read_leb_uint32(p_code, buf_code_end, code_count); + + if (func_count != code_count) { + set_error_buf(error_buf, error_buf_size, + "function and code section have inconsistent lengths"); + return false; + } + + if (func_count) { + module->function_count = func_count; + total_size = sizeof(WASMFunction*) * (uint64)func_count; + if (!(module->functions = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < func_count; i++) { + /* Resolve function type */ + read_leb_uint32(p, p_end, type_index); + if (type_index >= module->type_count) { + set_error_buf(error_buf, error_buf_size, "unknown type"); + return false; + } + + read_leb_uint32(p_code, buf_code_end, code_size); + if (code_size == 0 + || p_code + code_size > buf_code_end) { + set_error_buf(error_buf, error_buf_size, + "invalid function code size"); + return false; + } + + /* Resolve local set count */ + p_code_end = p_code + code_size; + local_count = 0; + read_leb_uint32(p_code, buf_code_end, local_set_count); + p_code_save = p_code; + + /* Calculate total local count */ + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + if (sub_local_count > UINT32_MAX - local_count) { + set_error_buf(error_buf, error_buf_size, + "too many locals"); + return false; + } + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + local_count += sub_local_count; + } + + /* Alloc memory, layout: function structure + local types */ + code_size = (uint32)(p_code_end - p_code); + + total_size = sizeof(WASMFunction) + (uint64)local_count; + if (!(func = module->functions[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set function type, local count, code size and code body */ + func->func_type = module->types[type_index]; + func->local_count = local_count; + if (local_count > 0) + func->local_types = (uint8*)func + sizeof(WASMFunction); + func->code_size = code_size; + /* + * we shall make a copy of code body [p_code, p_code + code_size] + * when we are worrying about inappropriate releasing behaviour. + * all code bodies are actually in a buffer which user allocates in + * his embedding environment and we don't have power on them. + * it will be like: + * code_body_cp = malloc(code_size); + * memcpy(code_body_cp, p_code, code_size); + * func->code = code_body_cp; + */ + func->code = (uint8*)p_code; + + /* Load each local type */ + p_code = p_code_save; + local_type_index = 0; + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + if (local_type_index + sub_local_count <= local_type_index + || local_type_index + sub_local_count > local_count) { + set_error_buf(error_buf, error_buf_size, + "invalid local count"); + return false; + } + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + if (type < VALUE_TYPE_F64 || type > VALUE_TYPE_I32) { + set_error_buf(error_buf, error_buf_size, + "invalid local type"); + return false; + } + for (k = 0; k < sub_local_count; k++) { + func->local_types[local_type_index++] = type; + } + } + + func->param_cell_num = func->func_type->param_cell_num; + func->ret_cell_num = func->func_type->ret_cell_num; + func->local_cell_num = + wasm_get_cell_num(func->local_types, func->local_count); + + if (!init_function_local_offsets(func, error_buf, error_buf_size)) + return false; + + p_code = p_code_end; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load function section success.\n"); + return true; +fail: + return false; +} + +static bool +load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_count, i; + uint64 total_size; + WASMTable *table; + + read_leb_uint32(p, p_end, table_count); + /* a total of one table is allowed */ + if (module->import_table_count + table_count > 1) { + set_error_buf(error_buf, error_buf_size, "multiple tables"); + return false; + } + + if (table_count) { + module->table_count = table_count; + total_size = sizeof(WASMTable) * (uint64)table_count; + if (!(module->tables = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each table */ + table = module->tables; + for (i = 0; i < table_count; i++, table++) + if (!load_table(&p, p_end, table, error_buf, error_buf_size)) + return false; + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load table section success.\n"); + return true; +fail: + return false; +} + +static bool +load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 memory_count, i; + uint64 total_size; + WASMMemory *memory; + + read_leb_uint32(p, p_end, memory_count); + /* a total of one memory is allowed */ + if (module->import_memory_count + memory_count > 1) { + set_error_buf(error_buf, error_buf_size, "multiple memories"); + return false; + } + + if (memory_count) { + module->memory_count = memory_count; + total_size = sizeof(WASMMemory) * (uint64)memory_count; + if (!(module->memories = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each memory */ + memory = module->memories; + for (i = 0; i < memory_count; i++, memory++) + if (!load_memory(&p, p_end, memory, error_buf, error_buf_size)) + return false; + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load memory section success.\n"); + return true; +fail: + return false; +} + +static bool +load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 global_count, i; + uint64 total_size; + WASMGlobal *global; + uint8 mutable; + + read_leb_uint32(p, p_end, global_count); + + if (global_count) { + module->global_count = global_count; + total_size = sizeof(WASMGlobal) * (uint64)global_count; + if (!(module->globals = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + global = module->globals; + + for(i = 0; i < global_count; i++, global++) { + CHECK_BUF(p, p_end, 2); + global->type = read_uint8(p); + mutable = read_uint8(p); + if (mutable >= 2) { + set_error_buf(error_buf, error_buf_size, "invalid mutability"); + return false; + } + global->is_mutable = mutable ? true : false; + + /* initialize expression */ + if (!load_init_expr(&p, p_end, &(global->init_expr), + global->type, error_buf, error_buf_size)) + return false; + + if (INIT_EXPR_TYPE_GET_GLOBAL == global->init_expr.init_expr_type) { + /** + * Currently, constant expressions occurring as initializers + * of globals are further constrained in that contained + * global.get instructions are + * only allowed to refer to imported globals. + */ + uint32 target_global_index = global->init_expr.u.global_index; + if (target_global_index >= module->import_global_count) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + } + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load global section success.\n"); + return true; +fail: + return false; +} + +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 export_count, i, j, index; + uint64 total_size; + uint32 str_len; + WASMExport *export; + const char *name; + + read_leb_uint32(p, p_end, export_count); + + if (export_count) { + module->export_count = export_count; + total_size = sizeof(WASMExport) * (uint64)export_count; + if (!(module->exports = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + export = module->exports; + for (i = 0; i < export_count; i++, export++) { + read_leb_uint32(p, p_end, str_len); + CHECK_BUF(p, p_end, str_len); + + for (j = 0; j < i; j++) { + name = module->exports[j].name; + if (strlen(name) == str_len + && memcmp(name, p, str_len) == 0) { + set_error_buf(error_buf, error_buf_size, + "duplicate export name"); + return false; + } + } + + if (!(export->name = const_str_list_insert(p, str_len, module, + error_buf, error_buf_size))) { + return false; + } + + p += str_len; + CHECK_BUF(p, p_end, 1); + export->kind = read_uint8(p); + read_leb_uint32(p, p_end, index); + export->index = index; + + switch(export->kind) { + /*function index*/ + case EXPORT_KIND_FUNC: + if (index >= module->function_count + + module->import_function_count) { + set_error_buf(error_buf, error_buf_size, + "unknown function"); + return false; + } + break; + /*table index*/ + case EXPORT_KIND_TABLE: + if (index >= module->table_count + + module->import_table_count) { + set_error_buf(error_buf, error_buf_size, + "unknown table"); + return false; + } + break; + /*memory index*/ + case EXPORT_KIND_MEMORY: + if (index >= module->memory_count + + module->import_memory_count) { + set_error_buf(error_buf, error_buf_size, + "unknown memory"); + return false; + } + break; + /*global index*/ + case EXPORT_KIND_GLOBAL: + if (index >= module->global_count + + module->import_global_count) { + set_error_buf(error_buf, error_buf_size, + "unknown global"); + return false; + } + break; + default: + set_error_buf(error_buf, error_buf_size, + "invalid export kind"); + return false; + } + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, + "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load export section success.\n"); + return true; +fail: + return false; +} + +static bool +load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_segment_count, i, j, table_index, function_count, function_index; + uint64 total_size; + WASMTableSeg *table_segment; + + read_leb_uint32(p, p_end, table_segment_count); + + if (table_segment_count) { + module->table_seg_count = table_segment_count; + total_size = sizeof(WASMTableSeg) * (uint64)table_segment_count; + if (!(module->table_segments = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + table_segment = module->table_segments; + for (i = 0; i < table_segment_count; i++, table_segment++) { + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + read_leb_uint32(p, p_end, table_index); + if (table_index + >= module->import_table_count + module->table_count) { + LOG_DEBUG("table#%d does not exist", table_index); + set_error_buf(error_buf, error_buf_size, "unknown table"); + return false; + } + + table_segment->table_index = table_index; + + /* initialize expression */ + if (!load_init_expr(&p, p_end, &(table_segment->base_offset), + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, function_count); + table_segment->function_count = function_count; + total_size = sizeof(uint32) * (uint64)function_count; + if (!(table_segment->func_indexes = (uint32 *) + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + for (j = 0; j < function_count; j++) { + read_leb_uint32(p, p_end, function_index); + if (function_index >= module->import_function_count + + module->function_count) { + set_error_buf(error_buf, error_buf_size, + "unknown function"); + return false; + } + table_segment->func_indexes[j] = function_index; + } + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load table segment section success.\n"); + return true; +fail: + return false; +} + +static bool +load_data_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count, i, mem_index, data_seg_len; + uint64 total_size; + WASMDataSeg *dataseg; + InitializerExpression init_expr; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive = false; + uint32 mem_flag; +#endif + + read_leb_uint32(p, p_end, data_seg_count); + +#if WASM_ENABLE_BULK_MEMORY != 0 + if ((module->data_seg_count1 != 0) + && (data_seg_count != module->data_seg_count1)) { + set_error_buf(error_buf, error_buf_size, + "data count and data section have inconsistent lengths"); + return false; + } +#endif + + if (data_seg_count) { + module->data_seg_count = data_seg_count; + total_size = sizeof(WASMDataSeg*) * (uint64)data_seg_count; + if (!(module->data_segments = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < data_seg_count; i++) { + read_leb_uint32(p, p_end, mem_index); +#if WASM_ENABLE_BULK_MEMORY != 0 + is_passive = false; + mem_flag = mem_index & 0x03; + switch (mem_flag) { + case 0x01: + is_passive = true; + break; + case 0x00: + /* no memory index, treat index as 0 */ + mem_index = 0; + goto check_mem_index; + case 0x02: + /* read following memory index */ + read_leb_uint32(p, p_end, mem_index); +check_mem_index: + if (mem_index + >= module->import_memory_count + module->memory_count) { + LOG_DEBUG("memory#%d does not exist", mem_index); + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } + break; + case 0x03: + default: + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + break; + } +#else + if (mem_index + >= module->import_memory_count + module->memory_count) { + LOG_DEBUG("memory#%d does not exist", mem_index); + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + if (!load_init_expr(&p, p_end, &init_expr, VALUE_TYPE_I32, + error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, data_seg_len); + + if (!(dataseg = module->data_segments[i] = loader_malloc + (sizeof(WASMDataSeg), error_buf, error_buf_size))) { + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + dataseg->is_passive = is_passive; + if (!is_passive) +#endif + { + bh_memcpy_s(&dataseg->base_offset, sizeof(InitializerExpression), + &init_expr, sizeof(InitializerExpression)); + + dataseg->memory_index = mem_index; + } + + dataseg->data_length = data_seg_len; + CHECK_BUF(p, p_end, data_seg_len); + dataseg->data = (uint8*)p; + p += data_seg_len; + } + } + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load data segment section success.\n"); + return true; +fail: + return false; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +load_datacount_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count1 = 0; + + read_leb_uint32(p, p_end, data_seg_count1); + module->data_seg_count1 = data_seg_count1; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load datacount section success.\n"); + return true; +fail: + return false; +} +#endif + +static bool +load_code_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_func, + const uint8 *buf_func_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_func = buf_func; + uint32 func_count = 0, code_count; + + /* code has been loaded in function section, so pass it here, just check + * whether function and code section have inconsistent lengths */ + read_leb_uint32(p, p_end, code_count); + + if (buf_func) + read_leb_uint32(p_func, buf_func_end, func_count); + + if (func_count != code_count) { + set_error_buf(error_buf, error_buf_size, + "function and code section have inconsistent lengths"); + return false; + } + + LOG_VERBOSE("Load code segment section success.\n"); + return true; +fail: + return false; +} + +static bool +load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + WASMType *type; + uint32 start_function; + + read_leb_uint32(p, p_end, start_function); + + if (start_function + >= module->function_count + module->import_function_count) { + set_error_buf(error_buf, error_buf_size, "unknown function"); + return false; + } + + if (start_function < module->import_function_count) + type = module->import_functions[start_function].u.function.func_type; + else + type = + module->functions[start_function - module->import_function_count] + ->func_type; + if (type->param_count != 0 || type->result_count != 0) { + set_error_buf(error_buf, error_buf_size, "invalid start function"); + return false; + } + + module->start_function = start_function; + + if (p != p_end) { + set_error_buf(error_buf, error_buf_size, "section size mismatch"); + return false; + } + + LOG_VERBOSE("Load start section success.\n"); + return true; +fail: + return false; +} + +static bool +load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 name_len; + + if (p >= p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + + read_leb_uint32(p, p_end, name_len); + + if (name_len == 0 + || p + name_len > p_end) { + set_error_buf(error_buf, error_buf_size, "unexpected end"); + return false; + } + + if (!check_utf8_str(p, name_len)) { + set_error_buf(error_buf, error_buf_size, + "invalid UTF-8 encoding"); + return false; + } + + LOG_VERBOSE("Load custom section success.\n"); + return true; +fail: + return false; +} + + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + BlockAddr *block_addr_cache, + char *error_buf, uint32 error_buf_size); + +#if WASM_ENABLE_FAST_INTERP != 0 +void ** +wasm_interp_get_handle_table(); + +static void **handle_table; +#endif + +static bool +load_from_sections(WASMModule *module, WASMSection *sections, + char *error_buf, uint32 error_buf_size) +{ + WASMExport *export; + WASMSection *section = sections; + const uint8 *buf, *buf_end, *buf_code = NULL, *buf_code_end = NULL, + *buf_func = NULL, *buf_func_end = NULL; + WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; + WASMGlobal *aux_stack_top_global = NULL, *global; + uint32 aux_data_end = (uint32)-1, aux_heap_base = (uint32)-1; + uint32 aux_stack_top = (uint32)-1, global_index, func_index, i; + uint32 aux_data_end_global_index = (uint32)-1; + uint32 aux_heap_base_global_index = (uint32)-1; + BlockAddr *block_addr_cache; + WASMType *func_type; + uint64 total_size; + + /* Find code and function sections if have */ + while (section) { + if (section->section_type == SECTION_TYPE_CODE) { + buf_code = section->section_body; + buf_code_end = buf_code + section->section_body_size; + } + else if (section->section_type == SECTION_TYPE_FUNC) { + buf_func = section->section_body; + buf_func_end = buf_func + section->section_body_size; + } + section = section->next; + } + + section = sections; + while (section) { + buf = section->section_body; + buf_end = buf + section->section_body_size; + LOG_DEBUG("to section %d", section->section_type); + switch (section->section_type) { + case SECTION_TYPE_USER: + /* unsupported user section, ignore it. */ + if (!load_user_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TYPE: + if (!load_type_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_IMPORT: + if (!load_import_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_FUNC: + if (!load_function_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TABLE: + if (!load_table_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_MEMORY: + if (!load_memory_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_GLOBAL: + if (!load_global_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_EXPORT: + if (!load_export_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_START: + if (!load_start_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_ELEM: + if (!load_table_segment_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_CODE: + if (!load_code_section(buf, buf_end, buf_func, buf_func_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_DATA: + if (!load_data_segment_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case SECTION_TYPE_DATACOUNT: + if (!load_datacount_section(buf, buf_end, module, + error_buf, error_buf_size)) + return false; + break; +#endif + default: + set_error_buf(error_buf, error_buf_size, + "invalid section id"); + return false; + } + + section = section->next; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + handle_table = wasm_interp_get_handle_table(); +#endif + + total_size = sizeof(BlockAddr) * (uint64)BLOCK_ADDR_CACHE_SIZE + * BLOCK_ADDR_CONFLICT_SIZE; + if (!(block_addr_cache = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + memset(block_addr_cache, 0, (uint32)total_size); + if (!wasm_loader_prepare_bytecode(module, func, block_addr_cache, + error_buf, error_buf_size)) { + wasm_runtime_free(block_addr_cache); + return false; + } + } + wasm_runtime_free(block_addr_cache); + + module->aux_data_end_global_index = (uint32)-1; + module->aux_heap_base_global_index = (uint32)-1; + module->aux_stack_top_global_index = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export++) { + if (export->kind == EXPORT_KIND_GLOBAL) { + if (!strcmp(export->name, "__heap_base")) { + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type == VALUE_TYPE_I32 + && !global->is_mutable + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST) { + aux_heap_base_global = global; + aux_heap_base = global->init_expr.u.i32; + aux_heap_base_global_index = export->index; + LOG_VERBOSE("Found aux __heap_base global, value: %d", + aux_heap_base); + } + } + else if (!strcmp(export->name, "__data_end")) { + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type == VALUE_TYPE_I32 + && !global->is_mutable + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST) { + aux_data_end_global = global; + aux_data_end = global->init_expr.u.i32; + aux_data_end_global_index = export->index; + LOG_VERBOSE("Found aux __data_end global, value: %d", + aux_data_end); + + aux_data_end = align_uint(aux_data_end, 16); + } + } + + /* For module compiled with -pthread option, the global is: + [0] stack_top <-- 0 + [1] tls_pointer + [2] tls_size + [3] data_end <-- 3 + [4] global_base + [5] heap_base <-- 5 + [6] dso_handle + + For module compiled without -pthread option: + [0] stack_top <-- 0 + [1] data_end <-- 1 + [2] global_base + [3] heap_base <-- 3 + [4] dso_handle + */ + if (aux_data_end_global && aux_heap_base_global + && aux_data_end <= aux_heap_base) { + module->aux_data_end_global_index = aux_data_end_global_index; + module->aux_data_end = aux_data_end; + module->aux_heap_base_global_index = aux_heap_base_global_index; + module->aux_heap_base = aux_heap_base; + + /* Resolve aux stack top global */ + for (global_index = 0; global_index < module->global_count; + global_index++) { + global = module->globals + global_index; + if (global->is_mutable /* heap_base and data_end is + not mutable */ + && global->type == VALUE_TYPE_I32 + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + && (uint32)global->init_expr.u.i32 <= aux_heap_base) { + aux_stack_top_global = global; + aux_stack_top = (uint32)global->init_expr.u.i32; + module->aux_stack_top_global_index = + module->import_global_count + global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = aux_stack_top > aux_data_end + ? aux_stack_top - aux_data_end + : aux_stack_top; + LOG_VERBOSE("Found aux stack top global, value: %d, " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); + break; + } + } + break; + } + } + } + + module->malloc_function = (uint32)-1; + module->free_function = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export++) { + if (export->kind == EXPORT_KIND_FUNC) { + if (!strcmp(export->name, "malloc") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == VALUE_TYPE_I32 + && func_type->types[1] == VALUE_TYPE_I32) { + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, index: %u", + export->index); + } + } + else if (!strcmp(export->name, "free") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 0 + && func_type->types[0] == VALUE_TYPE_I32) { + module->free_function = export->index; + LOG_VERBOSE("Found free function, index: %u", + export->index); + } + } + } + } + + if (!module->possible_memory_grow) { + WASMMemoryImport *memory_import; + WASMMemory *memory; + + if (aux_data_end_global + && aux_heap_base_global + && aux_stack_top_global) { + uint64 init_memory_size; + uint32 shrunk_memory_size = align_uint(aux_heap_base, 8); + + if (module->import_memory_count) { + memory_import = &module->import_memories[0].u.memory; + init_memory_size = (uint64)memory_import->num_bytes_per_page * + memory_import->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory_import->num_bytes_per_page = shrunk_memory_size; + memory_import->init_page_count = 1; + LOG_VERBOSE("Shrink import memory size to %d", + shrunk_memory_size); + } + } + if (module->memory_count) { + memory = &module->memories[0]; + init_memory_size = (uint64)memory->num_bytes_per_page * + memory->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory->num_bytes_per_page = shrunk_memory_size; + memory->init_page_count = 1; + LOG_VERBOSE("Shrink memory size to %d", shrunk_memory_size); + } + } + } + +#if WASM_ENABLE_MULTI_MODULE == 0 + if (module->import_memory_count) { + memory_import = &module->import_memories[0].u.memory; + /* Memory init page count cannot be larger than 65536, we don't + check integer overflow again. */ + memory_import->num_bytes_per_page *= memory_import->init_page_count; + memory_import->init_page_count = memory_import->max_page_count = 1; + } + if (module->memory_count) { + /* Memory init page count cannot be larger than 65536, we don't + check integer overflow again. */ + memory = &module->memories[0]; + memory->num_bytes_per_page *= memory->init_page_count; + memory->init_page_count = memory->max_page_count = 1; + } +#endif + } + + return true; +} + +#if BH_ENABLE_MEMORY_PROFILING != 0 +static void wasm_loader_free(void *ptr) +{ + wasm_runtime_free(ptr); +} +#else +#define wasm_loader_free wasm_free +#endif + +static WASMModule* +create_module(char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = loader_malloc(sizeof(WASMModule), + error_buf, error_buf_size); + + if (!module) { + return NULL; + } + + module->module_type = Wasm_Module_Bytecode; + + /* Set start_function to -1, means no start function */ + module->start_function = (uint32)-1; + +#if WASM_ENABLE_MULTI_MODULE != 0 + module->import_module_list = &module->import_module_list_head; +#endif + return module; +} + +WASMModule * +wasm_loader_load_from_sections(WASMSection *section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(error_buf, error_buf_size); + if (!module) + return NULL; + + if (!load_from_sections(module, section_list, error_buf, error_buf_size)) { + wasm_loader_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module from sections success.\n"); + return module; +} + +static void +destroy_sections(WASMSection *section_list) +{ + WASMSection *section = section_list, *next; + while (section) { + next = section->next; + wasm_runtime_free(section); + section = next; + } +} + +static uint8 section_ids[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM, +#if WASM_ENABLE_BULK_MEMORY != 0 + SECTION_TYPE_DATACOUNT, +#endif + SECTION_TYPE_CODE, + SECTION_TYPE_DATA +}; + +static uint8 +get_section_index(uint8 section_type) +{ + uint8 max_id = sizeof(section_ids) / sizeof(uint8); + + for (uint8 i = 0; i < max_id; i++) { + if (section_type == section_ids[i]) + return i; + } + + return (uint8)-1; +} + +static bool +create_sections(const uint8 *buf, uint32 size, + WASMSection **p_section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMSection *section_list_end = NULL, *section; + const uint8 *p = buf, *p_end = buf + size/*, *section_body*/; + uint8 section_type, section_index, last_section_index = (uint8)-1; + uint32 section_size; + + bh_assert(!*p_section_list); + + p += 8; + while (p < p_end) { + CHECK_BUF(p, p_end, 1); + section_type = read_uint8(p); + section_index = get_section_index(section_type); + if (section_index != (uint8)-1) { + if (section_type != SECTION_TYPE_USER) { + /* Custom sections may be inserted at any place, + while other sections must occur at most once + and in prescribed order. */ + if (last_section_index != (uint8)-1 + && (section_index <= last_section_index)) { + set_error_buf(error_buf, error_buf_size, + "junk after last section"); + return false; + } + last_section_index = section_index; + } + CHECK_BUF1(p, p_end, 1); + read_leb_uint32(p, p_end, section_size); + CHECK_BUF1(p, p_end, section_size); + + if (!(section = loader_malloc(sizeof(WASMSection), + error_buf, error_buf_size))) { + return false; + } + + section->section_type = section_type; + section->section_body = (uint8*)p; + section->section_body_size = section_size; + + if (!*p_section_list) + *p_section_list = section_list_end = section; + else { + section_list_end->next = section; + section_list_end = section; + } + + p += section_size; + } + else { + set_error_buf(error_buf, error_buf_size, + "invalid section id"); + return false; + } + } + + return true; +fail: + return false; +} + +static void +exchange32(uint8* p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +static bool +load(const uint8 *buf, uint32 size, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf_end = buf + size; + const uint8 *p = buf, *p_end = buf_end; + uint32 magic_number, version; + WASMSection *section_list = NULL; + + CHECK_BUF1(p, p_end, sizeof(uint32)); + magic_number = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8*)&magic_number); + + if (magic_number != WASM_MAGIC_NUMBER) { + set_error_buf(error_buf, error_buf_size, + "magic header not detected"); + return false; + } + + CHECK_BUF1(p, p_end, sizeof(uint32)); + version = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8*)&version); + + if (version != WASM_CURRENT_VERSION) { + set_error_buf(error_buf, error_buf_size, + "unknown binary version"); + return false; + } + + if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) + || !load_from_sections(module, section_list, error_buf, error_buf_size)) { + destroy_sections(section_list); + return false; + } + + destroy_sections(section_list); + return true; +fail: + return false; +} + +WASMModule* +wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(error_buf, error_buf_size); + if (!module) { + return NULL; + } + + if (!load(buf, size, module, error_buf, error_buf_size)) { + goto fail; + } + + LOG_VERBOSE("Load module success.\n"); + return module; + +fail: + wasm_loader_unload(module); + return NULL; +} + +void +wasm_loader_unload(WASMModule *module) +{ + uint32 i; + + if (!module) + return; + + if (module->types) { + for (i = 0; i < module->type_count; i++) { + if (module->types[i]) + wasm_runtime_free(module->types[i]); + } + wasm_runtime_free(module->types); + } + + if (module->imports) + wasm_runtime_free(module->imports); + + if (module->functions) { + for (i = 0; i < module->function_count; i++) { + if (module->functions[i]) { + if (module->functions[i]->local_offsets) + wasm_runtime_free(module->functions[i]->local_offsets); +#if WASM_ENABLE_FAST_INTERP != 0 + if (module->functions[i]->code_compiled) + wasm_runtime_free(module->functions[i]->code_compiled); + if (module->functions[i]->consts) + wasm_runtime_free(module->functions[i]->consts); +#endif + wasm_runtime_free(module->functions[i]); + } + } + wasm_runtime_free(module->functions); + } + + if (module->tables) + wasm_runtime_free(module->tables); + + if (module->memories) + wasm_runtime_free(module->memories); + + if (module->globals) + wasm_runtime_free(module->globals); + + if (module->exports) + wasm_runtime_free(module->exports); + + if (module->table_segments) { + for (i = 0; i < module->table_seg_count; i++) { + if (module->table_segments[i].func_indexes) + wasm_runtime_free(module->table_segments[i].func_indexes); + } + wasm_runtime_free(module->table_segments); + } + + if (module->data_segments) { + for (i = 0; i < module->data_seg_count; i++) { + if (module->data_segments[i]) + wasm_runtime_free(module->data_segments[i]); + } + wasm_runtime_free(module->data_segments); + } + + if (module->const_str_list) { + StringNode *node = module->const_str_list, *node_next; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just release the sub module list */ + if (module->import_module_list) { + WASMRegisteredModule *node = + bh_list_first_elem(module->import_module_list); + while (node) { + WASMRegisteredModule *next = bh_list_elem_next(node); + bh_list_remove(module->import_module_list, node); + /* + * unload(sub_module) will be trigged during runtime_destroy(). + * every module in the global module list will be unloaded one by + * one. so don't worry. + */ + wasm_runtime_free(node); + /* + * + * the module file reading buffer will be released + * in runtime_destroy() + */ + node = next; + } + } +#endif + + wasm_runtime_free(module); +} + +bool +wasm_loader_find_block_addr(BlockAddr *block_addr_cache, + const uint8 *start_addr, + const uint8 *code_end_addr, + uint8 label_type, + uint8 **p_else_addr, + uint8 **p_end_addr, + char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = start_addr, *p_end = code_end_addr; + uint8 *else_addr = NULL; + uint32 block_nested_depth = 1, count, i, j, t; + uint8 opcode, u8; + BlockAddr block_stack[16] = { 0 }, *block; + + i = ((uintptr_t)start_addr) % BLOCK_ADDR_CACHE_SIZE; + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { + if (block[j].start_addr == start_addr) { + /* Cache hit */ + *p_else_addr = block[j].else_addr; + *p_end_addr = block[j].end_addr; + return true; + } + } + + /* Cache unhit */ + block_stack[0].start_addr = start_addr; + + while (p < code_end_addr) { + opcode = *p++; + + switch (opcode) { + case WASM_OP_UNREACHABLE: + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + CHECK_BUF(p, p_end, 1); + /* block result type: 0x40/0x7F/0x7E/0x7D/0x7C */ + u8 = read_uint8(p); + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + /* block type */ + skip_leb_uint32(p, p_end); + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case WASM_OP_ELSE: + if (label_type == LABEL_TYPE_IF && block_nested_depth == 1) + else_addr = (uint8*)(p - 1); + if (block_nested_depth - 1 < sizeof(block_stack)/sizeof(BlockAddr)) + block_stack[block_nested_depth - 1].else_addr = (uint8*)(p - 1); + break; + + case WASM_OP_END: + if (block_nested_depth == 1) { + if (label_type == LABEL_TYPE_IF) + *p_else_addr = else_addr; + *p_end_addr = (uint8*)(p - 1); + + block_stack[0].end_addr = (uint8*)(p - 1); + for (t = 0; t < sizeof(block_stack)/sizeof(BlockAddr); t++) { + start_addr = block_stack[t].start_addr; + if (start_addr) { + i = ((uintptr_t)start_addr) % BLOCK_ADDR_CACHE_SIZE; + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) + if (!block[j].start_addr) + break; + + if (j == BLOCK_ADDR_CONFLICT_SIZE) { + memmove(block + 1, block, (BLOCK_ADDR_CONFLICT_SIZE - 1) * + sizeof(BlockAddr)); + j = 0; + + } + block[j].start_addr = block_stack[t].start_addr; + block[j].else_addr = block_stack[t].else_addr; + block[j].end_addr = block_stack[t].end_addr; + } + else + break; + } + return true; + } + else { + block_nested_depth--; + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = (uint8*)(p - 1); + } + break; + + case WASM_OP_BR: + case WASM_OP_BR_IF: + skip_leb_uint32(p, p_end); /* labelidx */ + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(p, p_end, count); /* lable num */ + for (i = 0; i <= count; i++) /* lableidxs */ + skip_leb_uint32(p, p_end); + break; + + case WASM_OP_RETURN: + break; + + case WASM_OP_CALL: + skip_leb_uint32(p, p_end); /* funcidx */ + break; + + case WASM_OP_CALL_INDIRECT: + skip_leb_uint32(p, p_end); /* typeidx */ + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* 0x00 */ + break; + + case WASM_OP_DROP: + case WASM_OP_SELECT: + case WASM_OP_DROP_64: + case WASM_OP_SELECT_64: + break; + + case WASM_OP_GET_LOCAL: + case WASM_OP_SET_LOCAL: + case WASM_OP_TEE_LOCAL: + case WASM_OP_GET_GLOBAL: + case WASM_OP_SET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_AUX_STACK: + skip_leb_uint32(p, p_end); /* localidx */ + break; + + case EXT_OP_GET_LOCAL_FAST: + case EXT_OP_SET_LOCAL_FAST: + case EXT_OP_TEE_LOCAL_FAST: + CHECK_BUF(p, p_end, 1); + p++; + break; + + case WASM_OP_I32_LOAD: + case WASM_OP_I64_LOAD: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_I32_STORE: + case WASM_OP_I64_STORE: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + skip_leb_uint32(p, p_end); /* align */ + skip_leb_uint32(p, p_end); /* offset */ + break; + + case WASM_OP_MEMORY_SIZE: + case WASM_OP_MEMORY_GROW: + skip_leb_uint32(p, p_end); /* 0x00 */ + break; + + case WASM_OP_I32_CONST: + skip_leb_int32(p, p_end); + break; + case WASM_OP_I64_CONST: + skip_leb_int64(p, p_end); + break; + case WASM_OP_F32_CONST: + p += sizeof(float32); + break; + case WASM_OP_F64_CONST: + p += sizeof(float64); + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + case WASM_OP_I32_WRAP_I64: + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + case WASM_OP_F32_DEMOTE_F64: + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + case WASM_OP_F64_PROMOTE_F32: + case WASM_OP_I32_REINTERPRET_F32: + case WASM_OP_I64_REINTERPRET_F64: + case WASM_OP_F32_REINTERPRET_I32: + case WASM_OP_F64_REINTERPRET_I64: + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + break; + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + + switch (opcode1) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + skip_leb_uint32(p, p_end); + /* skip memory idx */ + p++; + break; + case WASM_OP_DATA_DROP: + skip_leb_uint32(p, p_end); + break; + case WASM_OP_MEMORY_COPY: + /* skip two memory idx */ + p += 2; + break; + case WASM_OP_MEMORY_FILL: + /* skip memory idx */ + p++; + break; +#endif + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", + "unsupported opcode", 0xfc, opcode); + return false; + } + break; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + /* atomic_op (1 u8) + memarg (2 u32_leb) */ + opcode = read_uint8(p); + if (opcode != WASM_OP_ATOMIC_FENCE) { + skip_leb_uint32(p, p_end); /* align */ + skip_leb_uint32(p, p_end); /* offset */ + } + else { + /* atomic.fence doesn't have memarg */ + p++; + } + break; + } +#endif + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x", + "unsupported opcode", opcode); + return false; + } + } + + (void)u8; + return false; +fail: + return false; +} + +#define REF_I32 VALUE_TYPE_I32 +#define REF_F32 VALUE_TYPE_F32 +#define REF_I64_1 VALUE_TYPE_I64 +#define REF_I64_2 VALUE_TYPE_I64 +#define REF_F64_1 VALUE_TYPE_F64 +#define REF_F64_2 VALUE_TYPE_F64 +#define REF_ANY VALUE_TYPE_ANY + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_DEBUG_PREPROCESSOR != 0 +#define LOG_OP(...) os_printf(__VA_ARGS__) +#else +#define LOG_OP(...) (void)0 +#endif + +#define PATCH_ELSE 0 +#define PATCH_END 1 +typedef struct BranchBlockPatch { + struct BranchBlockPatch *next; + uint8 patch_type; + uint8 *code_compiled; +} BranchBlockPatch; +#endif + +typedef struct BranchBlock { + uint8 label_type; + BlockType block_type; + uint8 *start_addr; + uint8 *else_addr; + uint8 *end_addr; + uint32 stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + uint16 dynamic_offset; + uint8 *code_compiled; + BranchBlockPatch *patch_list; + /* This is used to save params frame_offset of of if block */ + int16 *param_frame_offsets; +#endif + + /* Indicate the operand stack is in polymorphic state. + * If the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed. + * If stack is in polymorphic state and stack is empty, instruction can + * pop any type of value directly without decreasing stack top pointer + * and stack cell num. */ + bool is_stack_polymorphic; +} BranchBlock; + +typedef struct WASMLoaderContext { + /* frame ref stack */ + uint8 *frame_ref; + uint8 *frame_ref_bottom; + uint8 *frame_ref_boundary; + uint32 frame_ref_size; + uint32 stack_cell_num; + uint32 max_stack_cell_num; + + /* frame csp stack */ + BranchBlock *frame_csp; + BranchBlock *frame_csp_bottom; + BranchBlock *frame_csp_boundary; + uint32 frame_csp_size; + uint32 csp_num; + uint32 max_csp_num; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* frame offset stack */ + int16 *frame_offset; + int16 *frame_offset_bottom; + int16 *frame_offset_boundary; + uint32 frame_offset_size; + int16 dynamic_offset; + int16 start_dynamic_offset; + int16 max_dynamic_offset; + + /* preserved local offset */ + int16 preserved_local_offset; + + /* const buffer */ + uint8 *const_buf; + uint16 num_const; + uint16 const_buf_size; + uint16 const_cell_num; + + /* processed code */ + uint8 *p_code_compiled; + uint8 *p_code_compiled_end; + uint32 code_compiled_size; +#endif +} WASMLoaderContext; + +typedef struct Const { + WASMValue value; + uint16 slot_index; + uint8 value_type; +} Const; + +static void* +memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, + char *error_buf, uint32 error_buf_size) +{ + uint8 *mem_new; + bh_assert(size_new > size_old); + if ((mem_new = loader_malloc + (size_new, error_buf, error_buf_size))) { + bh_memcpy_s(mem_new, size_new, mem_old, size_old); + memset(mem_new + size_old, 0, size_new - size_old); + wasm_runtime_free(mem_old); + } + return mem_new; +} + +#define MEM_REALLOC(mem, size_old, size_new) do { \ + void *mem_new = memory_realloc(mem, size_old, size_new, \ + error_buf, error_buf_size); \ + if (!mem_new) \ + goto fail; \ + mem = mem_new; \ + } while (0) + +#define CHECK_CSP_PUSH() do { \ + if (ctx->frame_csp >= ctx->frame_csp_boundary) { \ + MEM_REALLOC(ctx->frame_csp_bottom, ctx->frame_csp_size, \ + (uint32)(ctx->frame_csp_size \ + + 8 * sizeof(BranchBlock))); \ + ctx->frame_csp_size += (uint32)(8 * sizeof(BranchBlock)); \ + ctx->frame_csp_boundary = ctx->frame_csp_bottom + \ + ctx->frame_csp_size / sizeof(BranchBlock); \ + ctx->frame_csp = ctx->frame_csp_bottom + ctx->csp_num; \ + } \ + } while (0) + +#define CHECK_CSP_POP() do { \ + if (ctx->csp_num < 1) { \ + set_error_buf(error_buf, error_buf_size, \ + "type mismatch: " \ + "expect data but block stack was empty"); \ + goto fail; \ + } \ + } while (0) + +#if WASM_ENABLE_FAST_INTERP != 0 +static bool +check_offset_push(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + uint32 cell_num = (ctx->frame_offset - ctx->frame_offset_bottom); + if (ctx->frame_offset >= ctx->frame_offset_boundary) { + MEM_REALLOC(ctx->frame_offset_bottom, ctx->frame_offset_size, + ctx->frame_offset_size + 16); + ctx->frame_offset_size += 16; + ctx->frame_offset_boundary = ctx->frame_offset_bottom + + ctx->frame_offset_size / sizeof(int16); + ctx->frame_offset = ctx->frame_offset_bottom + cell_num; + } + return true; +fail: + return false; +} + +static bool +check_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->frame_offset - cells < ctx->frame_offset_bottom) + return false; + return true; +} + +static void +free_label_patch_list(BranchBlock *frame_csp) +{ + BranchBlockPatch *label_patch = frame_csp->patch_list; + BranchBlockPatch *next; + while (label_patch != NULL) { + next = label_patch->next; + wasm_runtime_free(label_patch); + label_patch = next; + } + frame_csp->patch_list = NULL; +} + +static void +free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + + for (uint32 i = 0; i < csp_num; i++) { + free_label_patch_list(tmp_csp); + tmp_csp ++; + } +} + +#endif + +static bool +check_stack_push(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + if (ctx->frame_ref >= ctx->frame_ref_boundary) { + MEM_REALLOC(ctx->frame_ref_bottom, ctx->frame_ref_size, + ctx->frame_ref_size + 16); + ctx->frame_ref_size += 16; + ctx->frame_ref_boundary = ctx->frame_ref_bottom + ctx->frame_ref_size; + ctx->frame_ref = ctx->frame_ref_bottom + ctx->stack_cell_num; + } + return true; +fail: + return false; +} + + +static bool +check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + char *type_str[] = { "f64", "f32", "i64", "i32" }; + + if (((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + && stack_cell_num < 1) + || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + && stack_cell_num < 2)) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: expect data but stack was empty"); + return false; + } + + if ((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) + || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) + || (type == VALUE_TYPE_I64 + && (*(frame_ref - 2) != REF_I64_1 + || *(frame_ref - 1) != REF_I64_2)) + || (type == VALUE_TYPE_F64 + && (*(frame_ref - 2) != REF_F64_1 + || *(frame_ref - 1) != REF_F64_2))) { + set_error_buf_v(error_buf, error_buf_size, "%s%s%s", + "type mismatch: expect ", + type_str[type - VALUE_TYPE_F64], + " but got other"); + return false; + } + + return true; +} + +static bool +check_stack_pop(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + int32 block_stack_cell_num = (int32) + (ctx->stack_cell_num - (ctx->frame_csp - 1)->stack_cell_num); + + if (block_stack_cell_num > 0 + && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + /* the stack top is a value of any type, return success */ + return true; + } + + if (!check_stack_top_values(ctx->frame_ref, block_stack_cell_num, + type, error_buf, error_buf_size)) + return false; + + return true; +} + +static void +wasm_loader_ctx_destroy(WASMLoaderContext *ctx) +{ + if (ctx) { + if (ctx->frame_ref_bottom) + wasm_runtime_free(ctx->frame_ref_bottom); + if (ctx->frame_csp_bottom) { +#if WASM_ENABLE_FAST_INTERP != 0 + free_all_label_patch_lists(ctx->frame_csp_bottom, ctx->csp_num); +#endif + wasm_runtime_free(ctx->frame_csp_bottom); + } +#if WASM_ENABLE_FAST_INTERP != 0 + if (ctx->frame_offset_bottom) + wasm_runtime_free(ctx->frame_offset_bottom); + if (ctx->const_buf) + wasm_runtime_free(ctx->const_buf); +#endif + wasm_runtime_free(ctx); + } +} + +static WASMLoaderContext* +wasm_loader_ctx_init(WASMFunction *func) +{ + WASMLoaderContext *loader_ctx = + wasm_runtime_malloc(sizeof(WASMLoaderContext)); + if (!loader_ctx) + return false; + memset(loader_ctx, 0, sizeof(WASMLoaderContext)); + + loader_ctx->frame_ref_size = 32; + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = + wasm_runtime_malloc(loader_ctx->frame_ref_size))) + goto fail; + memset(loader_ctx->frame_ref_bottom, 0, loader_ctx->frame_ref_size); + loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + + loader_ctx->frame_ref_size; + + loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = + wasm_runtime_malloc(loader_ctx->frame_csp_size))) + goto fail; + memset(loader_ctx->frame_csp_bottom, 0, loader_ctx->frame_csp_size); + loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset_size = sizeof(int16) * 32; + if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = + wasm_runtime_malloc(loader_ctx->frame_offset_size))) + goto fail; + memset(loader_ctx->frame_offset_bottom, 0, + loader_ctx->frame_offset_size); + loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; + + loader_ctx->num_const = 0; + loader_ctx->const_buf_size = sizeof(Const) * 8; + if (!(loader_ctx->const_buf = wasm_runtime_malloc(loader_ctx->const_buf_size))) + goto fail; + memset(loader_ctx->const_buf, 0, loader_ctx->const_buf_size); + + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = + loader_ctx->max_dynamic_offset = func->param_cell_num + + func->local_cell_num; +#endif + return loader_ctx; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + return NULL; +} + +static bool +wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; + + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || type == VALUE_TYPE_ANY) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; + return true; +} + +static bool +wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_pop(ctx, type, error_buf, error_buf_size)) + return false; + + ctx->frame_ref--; + ctx->stack_cell_num--; + + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || *ctx->frame_ref == VALUE_TYPE_ANY) + return true; + + ctx->frame_ref--; + ctx->stack_cell_num--; + return true; +} + +static bool +wasm_loader_push_pop_frame_ref(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + char *error_buf, uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + return true; +} + +static bool +wasm_loader_push_frame_csp(WASMLoaderContext *ctx, uint8 label_type, + BlockType block_type, uint8* start_addr, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_PUSH(); + memset(ctx->frame_csp, 0, sizeof(BranchBlock)); + ctx->frame_csp->label_type = label_type; + ctx->frame_csp->block_type = block_type; + ctx->frame_csp->start_addr = start_addr; + ctx->frame_csp->stack_cell_num = ctx->stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + ctx->frame_csp->dynamic_offset = ctx->dynamic_offset; + ctx->frame_csp->patch_list = NULL; +#endif + ctx->frame_csp++; + ctx->csp_num++; + if (ctx->csp_num > ctx->max_csp_num) + ctx->max_csp_num = ctx->csp_num; + return true; +fail: + return false; +} + +static bool +wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_POP(); +#if WASM_ENABLE_FAST_INTERP != 0 + if ((ctx->frame_csp - 1)->param_frame_offsets) + wasm_runtime_free((ctx->frame_csp - 1)->param_frame_offsets); +#endif + ctx->frame_csp--; + ctx->csp_num--; + + return true; +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_ENABLE_ABS_LABEL_ADDR != 0 + +#define emit_label(opcode) do { \ + wasm_loader_emit_ptr(loader_ctx, handle_table[opcode]); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) + +#define skip_label() do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(void *)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) + +#else + +#define emit_label(opcode) do { \ + int32 offset = (int32)(handle_table[opcode] - handle_table[0]); \ + if (!(offset >= INT16_MIN && offset < INT16_MAX)) { \ + set_error_buf(error_buf, error_buf_size, \ + "pre-compiled label offset out of range"); \ + goto fail; \ + } \ + wasm_loader_emit_int16(loader_ctx, offset); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) + +// drop local.get / const / block / loop / end +#define skip_label() do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) + +#endif /* WASM_ENABLE_ABS_LABEL_ADDR */ + +#define emit_empty_label_addr_and_frame_ip(type) do { \ + if (!add_label_patch_to_list(loader_ctx->frame_csp - 1, type, \ + loader_ctx->p_code_compiled, \ + error_buf, error_buf_size)) \ + goto fail; \ + /* label address, to be patched */ \ + wasm_loader_emit_ptr(loader_ctx, NULL); \ + } while (0) + +#define emit_br_info(frame_csp) do { \ + if (!wasm_loader_emit_br_info(loader_ctx, frame_csp, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define LAST_OP_OUTPUT_I32() (last_op >= WASM_OP_I32_EQZ \ + && last_op <= WASM_OP_I32_ROTR) \ + || (last_op == WASM_OP_I32_LOAD \ + || last_op == WASM_OP_F32_LOAD) \ + || (last_op >= WASM_OP_I32_LOAD8_S \ + && last_op <= WASM_OP_I32_LOAD16_U) \ + || (last_op >= WASM_OP_F32_ABS \ + && last_op <= WASM_OP_F32_COPYSIGN) \ + || (last_op >= WASM_OP_I32_WRAP_I64 \ + && last_op <= WASM_OP_I32_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F32_CONVERT_S_I32 \ + && last_op <= WASM_OP_F32_DEMOTE_F64) \ + || (last_op == WASM_OP_I32_REINTERPRET_F32) \ + || (last_op == WASM_OP_F32_REINTERPRET_I32) \ + || (last_op == EXT_OP_COPY_STACK_TOP) + +#define LAST_OP_OUTPUT_I64() (last_op >= WASM_OP_I64_CLZ \ + && last_op <= WASM_OP_I64_ROTR) \ + || (last_op >= WASM_OP_F64_ABS \ + && last_op <= WASM_OP_F64_COPYSIGN) \ + || (last_op == WASM_OP_I64_LOAD \ + || last_op == WASM_OP_F64_LOAD) \ + || (last_op >= WASM_OP_I64_LOAD8_S \ + && last_op <= WASM_OP_I64_LOAD32_U) \ + || (last_op >= WASM_OP_I64_EXTEND_S_I32 \ + && last_op <= WASM_OP_I64_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F64_CONVERT_S_I32 \ + && last_op <= WASM_OP_F64_PROMOTE_F32) \ + || (last_op == WASM_OP_I64_REINTERPRET_F64) \ + || (last_op == WASM_OP_F64_REINTERPRET_I64) \ + || (last_op == EXT_OP_COPY_STACK_TOP_I64) + +#define GET_CONST_OFFSET(type, val) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &val, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F32_OFFSET(type, fval) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &fval, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F64_OFFSET(type, fval) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &fval, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define emit_operand(ctx, offset) do { \ + wasm_loader_emit_int16(ctx, offset); \ + LOG_OP("%d\t", offset); \ + } while (0) + +#define emit_byte(ctx, byte) do { \ + wasm_loader_emit_uint8(ctx, byte); \ + LOG_OP("%d\t", byte); \ + } while (0) + +#define emit_uint32(ctx, value) do { \ + wasm_loader_emit_uint32(ctx, value); \ + LOG_OP("%d\t", value); \ + } while (0) + +#define emit_leb() do { \ + wasm_loader_emit_leb(loader_ctx, p_org, p); \ + } while (0) + +static bool +wasm_loader_ctx_reinit(WASMLoaderContext *ctx) +{ + if (!(ctx->p_code_compiled = wasm_runtime_malloc(ctx->code_compiled_size))) + return false; + memset(ctx->p_code_compiled, 0, ctx->code_compiled_size); + ctx->p_code_compiled_end = ctx->p_code_compiled + + ctx->code_compiled_size; + + /* clean up frame ref */ + memset(ctx->frame_ref_bottom, 0, ctx->frame_ref_size); + ctx->frame_ref = ctx->frame_ref_bottom; + ctx->stack_cell_num = 0; + + /* clean up frame csp */ + memset(ctx->frame_csp_bottom, 0, ctx->frame_csp_size); + ctx->frame_csp = ctx->frame_csp_bottom; + ctx->csp_num = 0; + ctx->max_csp_num = 0; + + /* clean up frame offset */ + memset(ctx->frame_offset_bottom, 0, ctx->frame_offset_size); + ctx->frame_offset = ctx->frame_offset_bottom; + ctx->dynamic_offset = ctx->start_dynamic_offset; + + /* init preserved local offsets */ + ctx->preserved_local_offset = ctx->max_dynamic_offset; + + /* const buf is reserved */ + return true; +} + +static void +wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) +{ + if (ctx->p_code_compiled) { + *(uint32*)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint32); + } + else + ctx->code_compiled_size += sizeof(uint32); +} + +static void +wasm_loader_emit_int16(WASMLoaderContext *ctx, int16 value) +{ + if (ctx->p_code_compiled) { + *(int16*)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(int16); + } + else + ctx->code_compiled_size += sizeof(int16); +} + +static void +wasm_loader_emit_uint8(WASMLoaderContext *ctx, uint8 value) +{ + if (ctx->p_code_compiled) { + *(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint8); + } + else + ctx->code_compiled_size += sizeof(uint8); +} + +static void +wasm_loader_emit_ptr(WASMLoaderContext *ctx, void *value) +{ + if (ctx->p_code_compiled) { + *(uint8**)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(void *); + } + else + ctx->code_compiled_size += sizeof(void *); +} + +static void +wasm_loader_emit_backspace(WASMLoaderContext *ctx, uint32 size) +{ + if (ctx->p_code_compiled) { + ctx->p_code_compiled -= size; + } + else + ctx->code_compiled_size -= size; +} + +static void +wasm_loader_emit_leb(WASMLoaderContext *ctx, uint8* start, uint8* end) +{ + if (ctx->p_code_compiled) { + bh_memcpy_s(ctx->p_code_compiled, + ctx->p_code_compiled_end - ctx->p_code_compiled, + start, end - start); + ctx->p_code_compiled += (end - start); + } + else { + ctx->code_compiled_size += (end - start); + } + +} + +static bool +preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, + uint32 local_index, uint32 local_type, bool *preserved, + char *error_buf, uint32 error_buf_size) +{ + int16 preserved_offset = (int16)local_index; + *preserved = false; + for (uint32 i = 0; i < loader_ctx->stack_cell_num; i++) { + /* move previous local into dynamic space before a set/tee_local opcode */ + if (loader_ctx->frame_offset_bottom[i] == (int16)local_index) { + if (preserved_offset == (int16)local_index) { + *preserved = true; + skip_label(); + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + preserved_offset = loader_ctx->preserved_local_offset; + /* Only increase preserve offset in the second traversal */ + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset++; + emit_label(EXT_OP_COPY_STACK_TOP); + } + else { + preserved_offset = loader_ctx->preserved_local_offset; + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset += 2; + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + emit_operand(loader_ctx, local_index); + emit_operand(loader_ctx, preserved_offset); + emit_label(opcode); + } + loader_ctx->frame_offset_bottom[i] = preserved_offset; + } + } + + return true; + +#if WASM_ENABLE_ABS_LABEL_ADDR == 0 +fail: + return false; +#endif +} + +static bool +add_label_patch_to_list(BranchBlock *frame_csp, + uint8 patch_type, uint8 *p_code_compiled, + char *error_buf, uint32 error_buf_size) +{ + BranchBlockPatch *patch = loader_malloc + (sizeof(BranchBlockPatch), error_buf, error_buf_size); + if (!patch) { + return false; + } + patch->patch_type = patch_type; + patch->code_compiled = p_code_compiled; + if (!frame_csp->patch_list) { + frame_csp->patch_list = patch; + patch->next = NULL; + } + else { + patch->next = frame_csp->patch_list; + frame_csp->patch_list = patch; + } + return true; +} + +static void +apply_label_patch(WASMLoaderContext *ctx, uint8 depth, + uint8 patch_type) +{ + BranchBlock *frame_csp = ctx->frame_csp - depth; + BranchBlockPatch *node = frame_csp->patch_list; + BranchBlockPatch *node_prev = NULL, *node_next; + + if (!ctx->p_code_compiled) + return; + + while (node) { + node_next = node->next; + if (node->patch_type == patch_type) { + *((uint8**)node->code_compiled) = ctx->p_code_compiled; + if (node_prev == NULL) { + frame_csp->patch_list = node_next; + } + else { + node_prev->next = node_next; + } + wasm_runtime_free(node); + } + else { + node_prev = node; + } + node = node_next; + } +} + +static bool +wasm_loader_emit_br_info(WASMLoaderContext *ctx, BranchBlock *frame_csp, + char *error_buf, uint32 error_buf_size) +{ + /* br info layout: + * a) arity of target block + * b) total cell num of arity values + * c) each arity value's cell num + * d) each arity value's src frame offset + * e) each arity values's dst dynamic offset + * f) branch target address + * + * Note: b-e are omitted when arity is 0 so that + * interpreter can recover the br info quickly. + */ + BlockType *block_type = &frame_csp->block_type; + uint8 *types = NULL, cell; + uint32 arity = 0; + int32 i; + int16 *frame_offset = ctx->frame_offset; + uint16 dynamic_offset; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(block_type, &types); + else + arity = block_type_get_result_types(block_type, &types); + + /* Part a */ + emit_uint32(ctx, arity); + + if (arity) { + /* Part b */ + emit_uint32(ctx, wasm_get_cell_num(types, arity)); + /* Part c */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + emit_byte(ctx, cell); + } + /* Part d */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + frame_offset -= cell; + emit_operand(ctx, *(int16*)(frame_offset)); + } + /* Part e */ + dynamic_offset = frame_csp->dynamic_offset + + wasm_get_cell_num(types, arity); + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + dynamic_offset -= cell; + emit_operand(ctx, dynamic_offset); + } + } + + /* Part f */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) { + wasm_loader_emit_ptr(ctx, frame_csp->code_compiled); + } + else { + if (!add_label_patch_to_list(frame_csp, PATCH_END, + ctx->p_code_compiled, + error_buf, error_buf_size)) + return false; + /* label address, to be patched */ + wasm_loader_emit_ptr(ctx, NULL); + } + + return true; +} + +static bool +wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (type == VALUE_TYPE_VOID) + return true; + + // only check memory overflow in first traverse + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + if (disable_emit) + *(ctx->frame_offset)++ = operand_offset; + else { + emit_operand(ctx, ctx->dynamic_offset); + *(ctx->frame_offset)++ = ctx->dynamic_offset; + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) + ctx->max_dynamic_offset = ctx->dynamic_offset; + } + + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + return true; + + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + ctx->frame_offset++; + if (!disable_emit) { + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) + ctx->max_dynamic_offset = ctx->dynamic_offset; + } + return true; +} + +/* This function should be in front of wasm_loader_pop_frame_ref + as they both use ctx->stack_cell_num, and ctx->stack_cell_num + will be modified by wasm_loader_pop_frame_ref */ +static bool +wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* if ctx->frame_csp equals ctx->frame_csp_bottom, + then current block is the function block */ + uint32 depth = ctx->frame_csp > ctx->frame_csp_bottom ? 1 : 0; + BranchBlock *cur_block = ctx->frame_csp - depth; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + /* Check the offset stack bottom to ensure the frame offset + stack will not go underflow. But we don't thrown error + and return true here, because the error msg should be + given in wasm_loader_pop_frame_ref */ + if (!check_offset_pop(ctx, 1)) + return true; + + ctx->frame_offset -= 1; + if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= 1; + } + else { + if (!check_offset_pop(ctx, 2)) + return true; + + ctx->frame_offset -= 2; + if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= 2; + } + emit_operand(ctx, *(ctx->frame_offset)); + return true; +} + +static bool +wasm_loader_push_pop_frame_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_offset(ctx, type_pop, error_buf, error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_offset(ctx, type_push, + disable_emit, operand_offset, + error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!(wasm_loader_push_frame_offset(ctx, type, disable_emit, operand_offset, + error_buf, error_buf_size))) + return false; + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) + return false; + + return true; +} + +static bool +wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* put wasm_loader_pop_frame_offset in front of wasm_loader_pop_frame_ref */ + if (!wasm_loader_pop_frame_offset(ctx, type, error_buf, error_buf_size)) + return false; + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!wasm_loader_push_pop_frame_offset(ctx, pop_cnt, type_push, type_pop, + disable_emit, operand_offset, + error_buf, error_buf_size)) + return false; + if (!wasm_loader_push_pop_frame_ref(ctx, pop_cnt, type_push, type_pop, + error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, + void *value, int16 *offset, + char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + Const *c; + for (c = (Const *)ctx->const_buf; + (uint8*)c < ctx->const_buf + ctx->num_const * sizeof(Const); c ++) { + if ((type == c->value_type) + && ((type == VALUE_TYPE_I64 && *(int64*)value == c->value.i64) + || (type == VALUE_TYPE_I32 && *(int32*)value == c->value.i32) + || (type == VALUE_TYPE_F64 + && (0 == memcmp(value, &(c->value.f64), sizeof(float64)))) + || (type == VALUE_TYPE_F32 + && (0 == memcmp(value, &(c->value.f32), sizeof(float32)))))) { + operand_offset = c->slot_index; + break; + } + if (c->value_type == VALUE_TYPE_I64 + || c->value_type == VALUE_TYPE_F64) + operand_offset += 2; + else + operand_offset += 1; + } + if ((uint8 *)c == ctx->const_buf + ctx->num_const * sizeof(Const)) { + if ((uint8 *)c == ctx->const_buf + ctx->const_buf_size) { + MEM_REALLOC(ctx->const_buf, + ctx->const_buf_size, + ctx->const_buf_size + 4 * sizeof(Const)); + ctx->const_buf_size += 4 * sizeof(Const); + c = (Const *)(ctx->const_buf + ctx->num_const * sizeof(Const)); + } + c->value_type = type; + switch (type) { + case VALUE_TYPE_F64: + bh_memcpy_s(&(c->value.f64), sizeof(WASMValue), value, sizeof(float64)); + ctx->const_cell_num += 2; + /* The const buf will be reversed, we use the second cell */ + /* of the i64/f64 const so the finnal offset is corrent */ + operand_offset ++; + break; + case VALUE_TYPE_I64: + c->value.i64 = *(int64*)value; + ctx->const_cell_num += 2; + operand_offset ++; + break; + case VALUE_TYPE_F32: + bh_memcpy_s(&(c->value.f32), sizeof(WASMValue), value, sizeof(float32)); + ctx->const_cell_num ++; + break; + case VALUE_TYPE_I32: + c->value.i32 = *(int32*)value; + ctx->const_cell_num ++; + break; + default: + break; + } + c->slot_index = operand_offset; + ctx->num_const ++; + LOG_OP("#### new const [%d]: %ld\n", + ctx->num_const, (int64)c->value.i64); + } + /* use negetive index for const */ + operand_offset = -(operand_offset + 1); + *offset = operand_offset; + return true; +fail: + return false; +} + +/* + PUSH(POP)_XXX = push(pop) frame_ref + push(pop) frame_offset + -- Mostly used for the binary / compare operation + PUSH(POP)_OFFSET_TYPE only push(pop) the frame_offset stack + -- Mostly used in block / control instructions + + The POP will always emit the offset on the top of the frame_offset stack + PUSH can be used in two ways: + 1. directly PUSH: + PUSH_XXX(); + will allocate a dynamic space and emit + 2. silent PUSH: + operand_offset = xxx; disable_emit = true; + PUSH_XXX(); + only push the frame_offset stack, no emit +*/ +#define PUSH_I32() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F32() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_I64() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F64() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I32() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F32() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I64() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F64() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_OFFSET_TYPE(type) do { \ + if (!(wasm_loader_push_frame_offset(loader_ctx, type, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_OFFSET_TYPE(type) do { \ + if (!(wasm_loader_pop_frame_offset(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref_offset(loader_ctx, 1, \ + type_push, type_pop, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref_offset(loader_ctx, 2, \ + type_push, type_pop, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#else /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_I32() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F32() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_I64() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F64() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I32() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F32() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I64() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F64() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, \ + type_push, type_pop, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 2, \ + type_push, type_pop, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) +#endif /* WASM_ENABLE_FAST_INTERP */ + +#if WASM_ENABLE_FAST_INTERP != 0 + +static bool +reserve_block_ret(WASMLoaderContext *loader_ctx, + uint8 opcode, bool disable_emit, + char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + BranchBlock *block = (opcode == WASM_OP_ELSE) ? + loader_ctx->frame_csp - 1 : loader_ctx->frame_csp; + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0, value_count = 0, total_cel_num = 0; + int32 i = 0; + int16 dynamic_offset, dynamic_offset_org, + *frame_offset = NULL, *frame_offset_org = NULL; + + return_count = block_type_get_result_types(block_type, &return_types); + + /* If there is only one return value, use EXT_OP_COPY_STACK_TOP/_I64 instead + * of EXT_OP_COPY_STACK_VALUES for interpreter performance. */ + if (return_count == 1) { + uint8 cell = wasm_value_type_cell_num(return_types[0]); + if (block->dynamic_offset != *(loader_ctx->frame_offset - cell)) { + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(cell == 1 ? EXT_OP_COPY_STACK_TOP : EXT_OP_COPY_STACK_TOP_I64); + emit_operand(loader_ctx, *(loader_ctx->frame_offset - cell)); + emit_operand(loader_ctx, block->dynamic_offset); + + if (opcode == WASM_OP_ELSE) { + *(loader_ctx->frame_offset - cell) = block->dynamic_offset; + } + else { + loader_ctx->frame_offset -= cell; + loader_ctx->dynamic_offset = block->dynamic_offset; + PUSH_OFFSET_TYPE(return_types[0]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + } + return true; + } + + /* Copy stack top values to block's results which are in dynamic space. + * The instruction format: + * Part a: values count + * Part b: all values total cell num + * Part c: each value's cell_num, src offset and dst offset + * Part d: each value's src offset and dst offset + * Part e: each value's dst offset + */ + frame_offset = frame_offset_org = loader_ctx->frame_offset; + dynamic_offset = dynamic_offset_org = + block->dynamic_offset + + wasm_get_cell_num(return_types, return_count); + + /* First traversal to get the count of values needed to be copied. */ + for (i = (int32)return_count - 1; i >= 0; i--) { + uint8 cells = wasm_value_type_cell_num(return_types[i]); + + frame_offset -= cells; + dynamic_offset -= cells; + if (dynamic_offset != *frame_offset) { + value_count++; + total_cel_num += cells; + } + } + + if (value_count) { + uint32 j = 0; + uint8 *emit_data = NULL, *cells = NULL; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + uint64 size = (uint64)value_count * (sizeof(*cells) + + sizeof(*src_offsets) + + sizeof(*dst_offsets)); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + value_count); + dst_offsets = (uint16 *)(src_offsets + value_count); + + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, value_count); + /* Part b) */ + emit_uint32(loader_ctx, total_cel_num); + + /* Second traversal to get each value's cell num, src offset and dst offset. */ + frame_offset = frame_offset_org; + dynamic_offset = dynamic_offset_org; + for (i = (int32)return_count - 1, j = 0; i >= 0; i--) { + uint8 cell = wasm_value_type_cell_num(return_types[i]); + frame_offset -= cell; + dynamic_offset -= cell; + if (dynamic_offset != *frame_offset) { + /* cell num */ + cells[j] = cell; + /* src offset */ + src_offsets[j] = *frame_offset; + /* dst offset */ + dst_offsets[j] = dynamic_offset; + j++; + } + if (opcode == WASM_OP_ELSE) { + *frame_offset = dynamic_offset; + } + else { + loader_ctx->frame_offset = frame_offset; + loader_ctx->dynamic_offset = dynamic_offset; + PUSH_OFFSET_TYPE(return_types[i]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + loader_ctx->frame_offset = frame_offset_org; + loader_ctx->dynamic_offset = dynamic_offset_org; + } + } + + bh_assert(j == value_count); + + /* Emit the cells, src_offsets and dst_offsets */ + for (j = 0; j < value_count; j++) + emit_byte(loader_ctx, cells[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, src_offsets[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, dst_offsets[j]); + + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + + wasm_runtime_free(emit_data); + } + + return true; + +fail: + return false; +} +#endif /* WASM_ENABLE_FAST_INTERP */ + +#define RESERVE_BLOCK_RET() do { \ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_TYPE(type) do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_TYPE(type) do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_CSP(label_type, block_type, _start_addr) do { \ + if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \ + _start_addr, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_CSP() do { \ + if (!wasm_loader_pop_frame_csp(loader_ctx, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ + read_leb_uint32(p, p_end, local_idx); \ + if (local_idx >= param_count + local_count) { \ + set_error_buf(error_buf, error_buf_size, \ + "unknown local"); \ + goto fail; \ + } \ + local_type = local_idx < param_count \ + ? param_types[local_idx] \ + : local_types[local_idx - param_count]; \ + local_offset = local_offsets[local_idx]; \ + } while (0) + +#define CHECK_BR(depth) do { \ + if (!wasm_loader_check_br(loader_ctx, depth, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +static bool +check_memory(WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + if (module->memory_count == 0 + && module->import_memory_count == 0) { + set_error_buf(error_buf, error_buf_size, "unknown memory"); + return false; + } + return true; +} + +#define CHECK_MEMORY() do { \ + if (!check_memory(module, error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +static bool +check_memory_access_align(uint8 opcode, uint32 align, + char *error_buf, uint32 error_buf_size) +{ + uint8 mem_access_aligns[] = { + 2, 3, 2, 3, 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, /* loads */ + 2, 3, 2, 3, 0, 1, 0, 1, 2 /* stores */ + }; + bh_assert(opcode >= WASM_OP_I32_LOAD + && opcode <= WASM_OP_I64_STORE32); + if (align > mem_access_aligns[opcode - WASM_OP_I32_LOAD]) { + set_error_buf(error_buf, error_buf_size, + "alignment must not be larger than natural"); + return false; + } + return true; +} + +#if WASM_ENABLE_SHARED_MEMORY != 0 +static bool +check_memory_align_equal(uint8 opcode, uint32 align, + char *error_buf, uint32 error_buf_size) +{ + uint8 wait_notify_aligns[] = {2, 2, 3}; + uint8 mem_access_aligns[] = { + 2, 3, 0, 1, 0, 1, 2, + }; + uint8 expect; + + bh_assert((opcode <= WASM_OP_ATOMIC_WAIT64) + || (opcode >= WASM_OP_ATOMIC_I32_LOAD + && opcode <= WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U)); + if (opcode <= WASM_OP_ATOMIC_WAIT64) { + expect = wait_notify_aligns[opcode - WASM_OP_ATOMIC_NOTIFY]; + } + else { + /* 7 opcodes in every group */ + expect = mem_access_aligns[(opcode - WASM_OP_ATOMIC_I32_LOAD) % 7]; + } + if (align != expect) { + set_error_buf(error_buf, error_buf_size, + "alignment isn't equal to natural"); + return false; + } + return true; +} +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + +static bool +is_value_type(uint8 type) +{ + return type == VALUE_TYPE_I32 || + type == VALUE_TYPE_I64 || + type == VALUE_TYPE_F32 || + type == VALUE_TYPE_F64 || + type == VALUE_TYPE_VOID; +} + +static bool +wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *target_block, *cur_block; + BlockType *target_block_type; + uint8 *types = NULL, *frame_ref; + uint32 arity = 0; + int32 i, available_stack_cell; + uint16 cell_num; + + if (loader_ctx->csp_num < depth + 1) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + return false; + } + + cur_block = loader_ctx->frame_csp - 1; + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + frame_ref = loader_ctx->frame_ref; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (target_block->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(target_block_type, &types); + else + arity = block_type_get_result_types(target_block_type, &types); + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (cur_block->is_stack_polymorphic) { + for (i = (int32)arity -1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(types[i]); +#endif + POP_TYPE(types[i]); + } + for (i = 0; i < (int32)arity; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(types[i]); +#endif + PUSH_TYPE(types[i]); + } + return true; + } + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Check stack top values match target block type */ + for (i = (int32)arity -1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + types[i], + error_buf, error_buf_size)) + return false; + cell_num = wasm_value_type_cell_num(types[i]); + frame_ref -= cell_num; + available_stack_cell -= cell_num; + } + + return true; + +fail: + return false; +} + +static BranchBlock * +check_branch_block(WASMLoaderContext *loader_ctx, + uint8 **p_buf, uint8 *buf_end, + char *error_buf, uint32 error_buf_size) +{ + uint8 *p = *p_buf, *p_end = buf_end; + BranchBlock *frame_csp_tmp; + uint32 depth; + + read_leb_uint32(p, p_end, depth); + CHECK_BR(depth); + frame_csp_tmp = loader_ctx->frame_csp - depth - 1; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(frame_csp_tmp); +#endif + + *p_buf = p; + return frame_csp_tmp; +fail: + return NULL; +} + +static bool +check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block, + char *error_buf, uint32 error_buf_size) +{ + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0; + int32 available_stack_cell, return_cell_num, i; + uint8 *frame_ref = NULL; + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num + - block->stack_cell_num); + + return_count = block_type_get_result_types(block_type, &return_types); + return_cell_num = return_count > 0 ? + wasm_get_cell_num(return_types, return_count) : 0; + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (block->is_stack_polymorphic) { + for (i = (int32)return_count -1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(return_types[i]); +#endif + POP_TYPE(return_types[i]); + } + + /* Check stack is empty */ + if (loader_ctx->stack_cell_num != block->stack_cell_num) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: stack size does not match block type"); + goto fail; + } + + for (i = 0; i < (int32)return_count; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(return_types[i]); +#endif + PUSH_TYPE(return_types[i]); + } + return true; + } + + /* Check stack cell num equals return cell num */ + if (available_stack_cell != return_cell_num) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: stack size does not match block type"); + goto fail; + } + + /* Check stack values match return types */ + frame_ref = loader_ctx->frame_ref; + for (i = (int32)return_count -1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + return_types[i], + error_buf, error_buf_size)) + return false; + frame_ref -= wasm_value_type_cell_num(return_types[i]); + available_stack_cell -= wasm_value_type_cell_num(return_types[i]); + } + + return true; + +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 +/* Copy parameters to dynamic space. + * 1) POP original parameter out; + * 2) Push and copy original values to dynamic space. + * The copy instruction format: + * Part a: param count + * Part b: all param total cell num + * Part c: each param's cell_num, src offset and dst offset + * Part d: each param's src offset + * Part e: each param's dst offset + */ +static bool +copy_params_to_dynamic_space(WASMLoaderContext *loader_ctx, bool is_if_block, + char* error_buf, uint32 error_buf_size) +{ + int16 *frame_offset = NULL; + uint8 *cells = NULL, cell; + int16 *src_offsets = NULL; + uint8 *emit_data = NULL; + uint32 i; + BranchBlock *block = loader_ctx->frame_csp - 1; + BlockType *block_type = &block->block_type; + WASMType *wasm_type = block_type->u.type; + uint32 param_count = block_type->u.type->param_count; + int16 condition_offset = 0; + bool disable_emit = false; + int16 operand_offset = 0; + + uint64 size = (uint64)param_count * (sizeof(*cells) + + sizeof(*src_offsets)); + + /* For if block, we also need copy the condition operand offset. */ + if (is_if_block) + size += sizeof(*cells) + sizeof(*src_offsets); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + param_count); + + if (is_if_block) + condition_offset = *loader_ctx->frame_offset; + + /* POP original parameter out */ + for (i = 0; i < param_count; i++) { + POP_OFFSET_TYPE(wasm_type->types[param_count - i - 1]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + frame_offset = loader_ctx->frame_offset; + + /* Get each param's cell num and src offset */ + for (i = 0; i < param_count; i++) { + cell = wasm_value_type_cell_num(wasm_type->types[i]); + cells[i] = cell; + src_offsets[i] = *frame_offset; + frame_offset += cell; + } + + /* emit copy instruction */ + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, is_if_block ? param_count + 1 : param_count); + /* Part b) */ + emit_uint32(loader_ctx, is_if_block ? + wasm_type->param_cell_num + 1 : + wasm_type->param_cell_num); + /* Part c) */ + for (i = 0; i < param_count; i++) + emit_byte(loader_ctx, cells[i]); + if (is_if_block) + emit_byte(loader_ctx, 1); + + /* Part d) */ + for (i = 0; i < param_count; i++) + emit_operand(loader_ctx, src_offsets[i]); + if (is_if_block) + emit_operand(loader_ctx, condition_offset); + + /* Part e) */ + /* Push to dynamic space. The push will emit the dst offset. */ + for (i = 0; i < param_count; i++) + PUSH_OFFSET_TYPE(wasm_type->types[i]); + if (is_if_block) + PUSH_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Free the emit data */ + wasm_runtime_free(emit_data); + + return true; + +fail: + return false; +} +#endif + +/* reset the stack to the state of before entering the last block */ +#if WASM_ENABLE_FAST_INTERP != 0 +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + loader_ctx->frame_offset = \ + loader_ctx->frame_offset_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#else +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#endif + +/* set current block's stack polymorphic state */ +#define SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(flag) do { \ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; \ + cur_block->is_stack_polymorphic = flag; \ +} while (0) + +#define BLOCK_HAS_PARAM(block_type) \ + (!block_type.is_value_type && block_type.u.type->param_count > 0) + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + BlockAddr *block_addr_cache, + char *error_buf, uint32 error_buf_size) +{ + uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; + uint32 param_count, local_count, global_count; + uint8 *param_types, *local_types, local_type, global_type; + BlockType func_type; + uint16 *local_offsets, local_offset; + uint32 count, i, local_idx, global_idx, u32, align, mem_offset; + int32 i32, i32_const = 0; + int64 i64; + uint8 opcode, u8; + bool return_value = false; + WASMLoaderContext *loader_ctx; + BranchBlock *frame_csp_tmp; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 segment_index; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *func_const_end, *func_const; + int16 operand_offset; + uint8 last_op = 0; + bool disable_emit, preserve_local = false; + float32 f32; + float64 f64; + + LOG_OP("\nProcessing func | [%d] params | [%d] locals | [%d] return\n", + func->param_cell_num, + func->local_cell_num, + func->ret_cell_num); +#endif + + global_count = module->import_global_count + module->global_count; + + param_count = func->func_type->param_count; + param_types = func->func_type->types; + + func_type.is_value_type = false; + func_type.u.type = func->func_type; + + local_count = func->local_count; + local_types = func->local_types; + local_offsets = func->local_offsets; + + if (!(loader_ctx = wasm_loader_ctx_init(func))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 +re_scan: + if (loader_ctx->code_compiled_size > 0) { + if (!wasm_loader_ctx_reinit(loader_ctx)) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail; + } + p = func->code; + func->code_compiled = loader_ctx->p_code_compiled; + } +#endif + + PUSH_CSP(LABEL_TYPE_FUNCTION, func_type, p); + + while (p < p_end) { + opcode = *p++; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + disable_emit = false; + emit_label(opcode); +#endif + + switch (opcode) { + case WASM_OP_UNREACHABLE: + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + + case WASM_OP_NOP: +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + break; + + case WASM_OP_IF: + POP_I32(); + goto handle_op_block_and_loop; + case WASM_OP_BLOCK: + case WASM_OP_LOOP: +handle_op_block_and_loop: + { + uint8 value_type; + BlockType block_type; + + value_type = read_uint8(p); + if (is_value_type(value_type)) { + /* If the first byte is one of these special values: + * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of + * the single return value. */ + block_type.is_value_type = true; + block_type.u.value_type = value_type; + } + else { + uint32 type_index; + /* Resolve the leb128 encoded type index as block type */ + p--; + read_leb_uint32(p, p_end, type_index); + if (type_index >= module->type_count) { + set_error_buf(error_buf, error_buf_size, + "unknown type"); + goto fail; + } + block_type.is_value_type = false; + block_type.u.type = module->types[type_index]; +#if WASM_ENABLE_FAST_INTERP == 0 \ + && WASM_ENABLE_WAMR_COMPILER == 0 \ + && WASM_ENABLE_JIT == 0 + /* If block use type index as block type, change the opcode + * to new extended opcode so that interpreter can resolve the + * block quickly. + */ + *(p - 2) = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK); +#endif + } + + /* Pop block parameters from stack */ + if (BLOCK_HAS_PARAM(block_type)) { + WASMType *wasm_type = block_type.u.type; + for (i = 0; i < block_type.u.type->param_count; i++) + POP_TYPE(wasm_type->types[wasm_type->param_count - i - 1]); + } + + PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), block_type, p); + + /* Pass parameters to block */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (opcode == WASM_OP_BLOCK) { + skip_label(); + } else if (opcode == WASM_OP_LOOP) { + skip_label(); + if (BLOCK_HAS_PARAM(block_type)) { + /* Make sure params are in dynamic space */ + if (!copy_params_to_dynamic_space(loader_ctx, + false, + error_buf, + error_buf_size)) + goto fail; + } + (loader_ctx->frame_csp - 1)->code_compiled = + loader_ctx->p_code_compiled; + } else if (opcode == WASM_OP_IF) { + /* If block has parameters, we should make sure they are in + * dynamic space. Otherwise, when else branch is missing, + * the later opcode may consume incorrect operand offset. + * Spec case: + * (func (export "params-id") (param i32) (result i32) + * (i32.const 1) + * (i32.const 2) + * (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + * (i32.add) + * ) + * + * So we should emit a copy instruction before the if. + * + * And we also need to save the parameter offsets and + * recover them before entering else branch. + * + */ + if (BLOCK_HAS_PARAM(block_type)) { + BranchBlock *block = loader_ctx->frame_csp - 1; + uint64 size; + + /* skip the if condition operand offset */ + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + /* skip the if label */ + skip_label(); + /* Emit a copy instruction */ + if (!copy_params_to_dynamic_space(loader_ctx, + true, + error_buf, + error_buf_size)) + goto fail; + + /* Emit the if instruction */ + emit_label(opcode); + /* Emit the new condition operand offset */ + POP_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Save top param_count values of frame_offset stack, so that + * we can recover it before executing else branch */ + size = sizeof(int16) * + (uint64)block_type.u.type->param_cell_num; + if (!(block->param_frame_offsets = + loader_malloc(size, error_buf, error_buf_size))) + goto fail; + bh_memcpy_s(block->param_frame_offsets, + size, + loader_ctx->frame_offset - size/sizeof(int16), + size); + } + + emit_empty_label_addr_and_frame_ip(PATCH_ELSE); + emit_empty_label_addr_and_frame_ip(PATCH_END); + } +#endif + break; + } + + case WASM_OP_ELSE: + { + BlockType block_type = (loader_ctx->frame_csp - 1)->block_type; + + if (loader_ctx->csp_num < 2 + || (loader_ctx->frame_csp - 1)->label_type != LABEL_TYPE_IF) { + set_error_buf(error_buf, error_buf_size, + "opcode else found without matched opcode if"); + goto fail; + } + + /* check whether if branch's stack matches its result type */ + if (!check_block_stack(loader_ctx, loader_ctx->frame_csp - 1, + error_buf, error_buf_size)) + goto fail; + + (loader_ctx->frame_csp - 1)->else_addr = p - 1; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* if the result of if branch is in local or const area, add a copy op */ + RESERVE_BLOCK_RET(); + + emit_empty_label_addr_and_frame_ip(PATCH_END); + apply_label_patch(loader_ctx, 1, PATCH_ELSE); +#endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + + /* Pass parameters to if-false branch */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Recover top param_count values of frame_offset stack */ + if (BLOCK_HAS_PARAM((block_type))) { + uint32 size; + BranchBlock *block = loader_ctx->frame_csp - 1; + size = sizeof(int16) * + block_type.u.type->param_cell_num; + bh_memcpy_s(loader_ctx->frame_offset, size, + block->param_frame_offsets, size); + loader_ctx->frame_offset += (size/sizeof(int16)); + } +#endif + + break; + } + + case WASM_OP_END: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + /* check whether block stack matches its result type */ + if (!check_block_stack(loader_ctx, cur_block, + error_buf, error_buf_size)) + goto fail; + + /* if no else branch, and return types do not match param types, fail */ + if (cur_block->label_type == LABEL_TYPE_IF + && !cur_block->else_addr) { + uint32 param_count = 0, ret_count = 0; + uint8 *param_types = NULL, *ret_types = NULL; + BlockType *block_type = &cur_block->block_type; + if (block_type->is_value_type) { + if (block_type->u.value_type != VALUE_TYPE_VOID) { + ret_count = 1; + ret_types = &block_type->u.value_type; + } + } + else { + param_count = block_type->u.type->param_count; + ret_count = block_type->u.type->result_count; + param_types = block_type->u.type->types; + ret_types = block_type->u.type->types + param_count; + } + if (param_count != ret_count + || (param_count && memcmp(param_types, ret_types, param_count))) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: else branch missing"); + goto fail; + } + } + + POP_CSP(); + +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + /* copy the result to the block return address */ + RESERVE_BLOCK_RET(); + + apply_label_patch(loader_ctx, 0, PATCH_END); + free_label_patch_list(loader_ctx->frame_csp); + if (loader_ctx->frame_csp->label_type == LABEL_TYPE_FUNCTION) { + int32 idx; + uint8 ret_type; + + emit_label(WASM_OP_RETURN); + for (idx = (int32)func->func_type->result_count - 1; + idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_OFFSET_TYPE(ret_type); + } + } +#endif + if (loader_ctx->csp_num > 0) { + loader_ctx->frame_csp->end_addr = p - 1; + } + else { + /* end of function block, function will return, + ignore the following bytecodes */ + p = p_end; + + continue; + } + + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + break; + } + + case WASM_OP_BR: + { + if (!(frame_csp_tmp = check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_BR_IF: + { + POP_I32(); + + if (!(frame_csp_tmp = check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + + break; + } + + case WASM_OP_BR_TABLE: + { + uint8 *ret_types = NULL; + uint32 ret_count = 0; + + read_leb_uint32(p, p_end, count); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, count); +#endif + POP_I32(); + + /* TODO: check the const */ + for (i = 0; i <= count; i++) { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + + if (i == 0) { + if (frame_csp_tmp->label_type != LABEL_TYPE_LOOP) + ret_count = + block_type_get_result_types(&frame_csp_tmp->block_type, + &ret_types); + } + else { + uint8 *tmp_ret_types = NULL; + uint32 tmp_ret_count = 0; + + /* Check whether all table items have the same return type */ + if (frame_csp_tmp->label_type != LABEL_TYPE_LOOP) + tmp_ret_count = + block_type_get_result_types(&frame_csp_tmp->block_type, + &tmp_ret_types); + + if (ret_count != tmp_ret_count + || (ret_count + && 0 != memcmp(ret_types, tmp_ret_types, ret_count))) { + set_error_buf(error_buf, error_buf_size, + "type mismatch: br_table targets must " + "all use same result type"); + goto fail; + } + } + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_RETURN: + { + int32 idx; + uint8 ret_type; + for (idx = (int32)func->func_type->result_count - 1; idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_TYPE(ret_type); +#if WASM_ENABLE_FAST_INTERP != 0 + // emit the offset after return opcode + POP_OFFSET_TYPE(ret_type); +#endif + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + break; + } + + case WASM_OP_CALL: + { + WASMType *func_type; + uint32 func_idx; + int32 idx; + + read_leb_uint32(p, p_end, func_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + // we need to emit func_idx before arguments + emit_uint32(loader_ctx, func_idx); +#endif + + if (func_idx >= module->import_function_count + module->function_count) { + set_error_buf(error_buf, error_buf_size, + "unknown function"); + goto fail; + } + + if (func_idx < module->import_function_count) + func_type = module->import_functions[func_idx].u.function.func_type; + else + func_type = + module->functions[func_idx - module->import_function_count]->func_type; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) { + POP_TYPE(func_type->types[idx]); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + } + } + + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Here we emit each return value's dynamic_offset. But in fact + * these offsets are continuous, so interpreter only need to get + * the first return value's offset. + */ + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); +#endif + } + + func->has_op_func_call = true; + break; + } + + case WASM_OP_CALL_INDIRECT: + { + int32 idx; + WASMType *func_type; + uint32 type_idx; + + if (module->table_count == 0 + && module->import_table_count == 0) { + set_error_buf(error_buf, error_buf_size, + "call indirect with unknown table"); + goto fail; + } + + read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + // we need to emit func_idx before arguments + emit_uint32(loader_ctx, type_idx); +#endif + + /* reserved byte 0x00 */ + if (*p++ != 0x00) { + set_error_buf(error_buf, error_buf_size, + "zero flag expected"); + goto fail; + } + + POP_I32(); + + if (type_idx >= module->type_count) { + set_error_buf(error_buf, error_buf_size, + "unknown type"); + goto fail; + } + + func_type = module->types[type_idx]; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) { + POP_TYPE(func_type->types[idx]); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + } + } + + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); +#endif + } + + func->has_op_func_call = true; + break; + } + + case WASM_OP_DROP: + case WASM_OP_DROP_64: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { + set_error_buf(error_buf, error_buf_size, + "type mismatch, opcode drop was found " + "but stack was empty"); + goto fail; + } + + if (available_stack_cell > 0) { + if (*(loader_ctx->frame_ref - 1) == REF_I32 + || *(loader_ctx->frame_ref - 1) == REF_F32) { + loader_ctx->frame_ref--; + loader_ctx->stack_cell_num--; +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset--; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset --; +#endif + } + else { + loader_ctx->frame_ref -= 2; + loader_ctx->stack_cell_num -= 2; +#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) + *(p - 1) = WASM_OP_DROP_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 2; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset -= 2; +#endif + } + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + } + break; + } + + case WASM_OP_SELECT: + case WASM_OP_SELECT_64: + { + uint8 ref_type; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell; + + POP_I32(); + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + if (available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic) { + set_error_buf(error_buf, error_buf_size, + "type mismatch, opcode select was found " + "but stack was empty"); + goto fail; + } + + if (available_stack_cell > 0) { + switch (*(loader_ctx->frame_ref - 1)) { + case REF_I32: + case REF_F32: + break; + case REF_I64_2: + case REF_F64_2: +#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) + *(p - 1) = WASM_OP_SELECT_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { +#if WASM_ENABLE_ABS_LABEL_ADDR != 0 + *(void**)(loader_ctx->p_code_compiled - 2 - sizeof(void*)) = + handle_table[WASM_OP_SELECT_64]; +#else + *((int16*)loader_ctx->p_code_compiled - 2) = (int16) + (handle_table[WASM_OP_SELECT_64] - handle_table[0]); +#endif + } +#endif + break; + } + + ref_type = *(loader_ctx->frame_ref - 1); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(VALUE_TYPE_ANY); +#endif + PUSH_TYPE(VALUE_TYPE_ANY); + } + break; + } + + case WASM_OP_GET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Get Local is optimized out */ + skip_label(); + disable_emit = true; + operand_offset = local_offset; + PUSH_OFFSET_TYPE(local_type); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_GET_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_SET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + POP_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local(loader_ctx, opcode, local_offset, + local_type, &preserve_local, + error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if ((!preserve_local) && (LAST_OP_OUTPUT_I32())) { + if (loader_ctx->p_code_compiled) + *(int16*)(loader_ctx->p_code_compiled - 2) = local_offset; + loader_ctx->frame_offset --; + loader_ctx->dynamic_offset --; + } + else if ((!preserve_local) && (LAST_OP_OUTPUT_I64())) { + if (loader_ctx->p_code_compiled) + *(int16*)(loader_ctx->p_code_compiled - 2) = local_offset; + loader_ctx->frame_offset -= 2; + loader_ctx->dynamic_offset -= 2; + } + else { + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + emit_label(EXT_OP_SET_LOCAL_FAST); + emit_byte(loader_ctx, local_offset); + } + else { + emit_label(EXT_OP_SET_LOCAL_FAST_I64); + emit_byte(loader_ctx, local_offset); + } + POP_OFFSET_TYPE(local_type); + } + } + else { /* local index larger than 255, reserve leb */ + p_org ++; + emit_leb(); + POP_OFFSET_TYPE(local_type); + } +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_SET_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_TEE_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the same + with ref stack */ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(local_type); + PUSH_OFFSET_TYPE(local_type); + } +#endif + POP_TYPE(local_type); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local(loader_ctx, opcode, local_offset, + local_type, &preserve_local, + error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + emit_label(EXT_OP_TEE_LOCAL_FAST); + emit_byte(loader_ctx, local_offset); + } + else { + emit_label(EXT_OP_TEE_LOCAL_FAST_I64); + emit_byte(loader_ctx, local_offset); + } + } + else { /* local index larger than 255, reserve leb */ + p_org ++; + emit_leb(); + } + emit_operand(loader_ctx, *(loader_ctx->frame_offset - + wasm_value_type_cell_num(local_type))); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_TEE_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_GET_GLOBAL: + { + p_org = p - 1; + read_leb_uint32(p, p_end, global_idx); + if (global_idx >= global_count) { + set_error_buf(error_buf, error_buf_size, + "unknown global"); + goto fail; + } + + global_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.type + : module->globals[global_idx - module->import_global_count] + .type; + + PUSH_TYPE(global_type); + +#if WASM_ENABLE_FAST_INTERP == 0 +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + *p_org = WASM_OP_GET_GLOBAL_64; + } +#endif +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_GET_GLOBAL_64); + } + emit_uint32(loader_ctx, global_idx); + PUSH_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; + } + + case WASM_OP_SET_GLOBAL: + { + bool is_mutable = false; + + p_org = p - 1; + read_leb_uint32(p, p_end, global_idx); + if (global_idx >= global_count) { + set_error_buf(error_buf, error_buf_size, + "unknown global"); + goto fail; + } + + is_mutable = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.is_mutable + : module->globals[global_idx - module->import_global_count] + .is_mutable; + if (!is_mutable) { + set_error_buf(error_buf, error_buf_size, + "global is immutable"); + goto fail; + } + + global_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.type + : module->globals[global_idx - module->import_global_count] + .type; + + POP_TYPE(global_type); + +#if WASM_ENABLE_FAST_INTERP == 0 +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + *p_org = WASM_OP_SET_GLOBAL_64; + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + *p_org = WASM_OP_SET_GLOBAL_AUX_STACK; + } +#endif +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_64); + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_AUX_STACK); + } + emit_uint32(loader_ctx, global_idx); + POP_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; + } + + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + { +#if WASM_ENABLE_FAST_INTERP != 0 + /* change F32/F64 into I32/I64 */ + if (opcode == WASM_OP_F32_LOAD) { + skip_label(); + emit_label(WASM_OP_I32_LOAD); + } + else if (opcode == WASM_OP_F64_LOAD) { + skip_label(); + emit_label(WASM_OP_I64_LOAD); + } + else if (opcode == WASM_OP_F32_STORE) { + skip_label(); + emit_label(WASM_OP_I32_STORE); + } + else if (opcode == WASM_OP_F64_STORE) { + skip_label(); + emit_label(WASM_OP_I64_STORE); + } +#endif + CHECK_MEMORY(); + read_leb_uint32(p, p_end, align); /* align */ + read_leb_uint32(p, p_end, mem_offset); /* offset */ + if (!check_memory_access_align(opcode, align, + error_buf, error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + switch (opcode) + { + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + case WASM_OP_F32_LOAD: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + case WASM_OP_F64_LOAD: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + POP_I32(); + POP_I32(); + break; + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + POP_I64(); + POP_I32(); + break; + case WASM_OP_F32_STORE: + POP_F32(); + POP_I32(); + break; + case WASM_OP_F64_STORE: + POP_F64(); + POP_I32(); + break; + default: + break; + } + break; + } + + case WASM_OP_MEMORY_SIZE: + CHECK_MEMORY(); + /* reserved byte 0x00 */ + if (*p++ != 0x00) { + set_error_buf(error_buf, error_buf_size, + "zero flag expected"); + goto fail; + } + PUSH_I32(); + + module->possible_memory_grow = true; + break; + + case WASM_OP_MEMORY_GROW: + CHECK_MEMORY(); + /* reserved byte 0x00 */ + if (*p++ != 0x00) { + set_error_buf(error_buf, error_buf_size, + "zero flag expected"); + goto fail; + } + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + + func->has_op_memory_grow = true; + module->possible_memory_grow = true; + break; + + case WASM_OP_I32_CONST: + read_leb_int32(p, p_end, i32_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); +#else + (void)i32_const; +#endif + PUSH_I32(); + break; + + case WASM_OP_I64_CONST: + read_leb_int64(p, p_end, i64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I64, i64); +#endif + PUSH_I64(); + break; + + case WASM_OP_F32_CONST: + p += sizeof(float32); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + bh_memcpy_s((uint8*)&f32, sizeof(float32), p_org, sizeof(float32)); + GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32); +#endif + PUSH_F32(); + break; + + case WASM_OP_F64_CONST: + p += sizeof(float64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + /* Some MCU may require 8-byte align */ + bh_memcpy_s((uint8*)&f64, sizeof(float64), p_org, sizeof(float64)); + GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64); +#endif + PUSH_F64(); + break; + + case WASM_OP_I32_EQZ: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQZ: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_WRAP_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_DEMOTE_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_PROMOTE_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_REINTERPRET_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_REINTERPRET_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_REINTERPRET_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_REINTERPRET_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, ((uint8)opcode1)); +#endif + switch (opcode1) + { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + if (module->import_memory_count == 0 && module->memory_count == 0) + goto fail_unknown_memory; + + if (*p++ != 0x00) + goto fail_zero_flag_expected; + + if (segment_index >= module->data_seg_count) { + set_error_buf(error_buf, error_buf_size, + "unknown data segment"); + goto fail; + } + + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_DATA_DROP: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + if (segment_index >= module->data_seg_count) { + set_error_buf(error_buf, error_buf_size, + "unknown data segment"); + goto fail; + } + + if (module->data_seg_count1 == 0) + goto fail_data_cnt_sec_require; + + break; + case WASM_OP_MEMORY_COPY: + /* both src and dst memory index should be 0 */ + if (*(int16*)p != 0x0000) + goto fail_zero_flag_expected; + p += 2; + + if (module->import_memory_count == 0 && module->memory_count == 0) + goto fail_unknown_memory; + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_MEMORY_FILL: + if (*p++ != 0x00) { + goto fail_zero_flag_expected; + } + if (module->import_memory_count == 0 && module->memory_count == 0) { + goto fail_unknown_memory; + } + + POP_I32(); + POP_I32(); + POP_I32(); + break; +fail_zero_flag_expected: + set_error_buf(error_buf, error_buf_size, + "zero flag expected"); + goto fail; + +fail_unknown_memory: + set_error_buf(error_buf, error_buf_size, + "unknown memory 0"); + goto fail; +fail_data_cnt_sec_require: + set_error_buf(error_buf, error_buf_size, + "data count section required"); + goto fail; + /* TODO: to support bulk table operation */ +#endif /* WASM_ENABLE_BULK_MEMORY */ + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", + "unsupported opcode", 0xfc, opcode1); + goto fail; + } + break; + } +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + opcode = read_uint8(p); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, opcode); +#endif + if (opcode != WASM_OP_ATOMIC_FENCE) { + CHECK_MEMORY(); + read_leb_uint32(p, p_end, align); /* align */ + read_leb_uint32(p, p_end, mem_offset); /* offset */ + if (!check_memory_align_equal(opcode, align, + error_buf, + error_buf_size)) { + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + } + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_WAIT32: + POP_I64(); + POP_I32(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT64: + POP_I64(); + POP_I64(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_FENCE: + /* reserved byte 0x00 */ + if (*p++ != 0x00) { + set_error_buf(error_buf, error_buf_size, + "zero flag expected"); + goto fail; + } + break; + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + POP_I32(); + POP_I32(); + break; + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + POP_I64(); + POP_I32(); + break; + case WASM_OP_ATOMIC_RMW_I32_ADD: + case WASM_OP_ATOMIC_RMW_I32_ADD8_U: + case WASM_OP_ATOMIC_RMW_I32_ADD16_U: + case WASM_OP_ATOMIC_RMW_I32_SUB: + case WASM_OP_ATOMIC_RMW_I32_SUB8_U: + case WASM_OP_ATOMIC_RMW_I32_SUB16_U: + case WASM_OP_ATOMIC_RMW_I32_AND: + case WASM_OP_ATOMIC_RMW_I32_AND8_U: + case WASM_OP_ATOMIC_RMW_I32_AND16_U: + case WASM_OP_ATOMIC_RMW_I32_OR: + case WASM_OP_ATOMIC_RMW_I32_OR8_U: + case WASM_OP_ATOMIC_RMW_I32_OR16_U: + case WASM_OP_ATOMIC_RMW_I32_XOR: + case WASM_OP_ATOMIC_RMW_I32_XOR8_U: + case WASM_OP_ATOMIC_RMW_I32_XOR16_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG: + case WASM_OP_ATOMIC_RMW_I32_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG16_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_RMW_I64_ADD: + case WASM_OP_ATOMIC_RMW_I64_ADD8_U: + case WASM_OP_ATOMIC_RMW_I64_ADD16_U: + case WASM_OP_ATOMIC_RMW_I64_ADD32_U: + case WASM_OP_ATOMIC_RMW_I64_SUB: + case WASM_OP_ATOMIC_RMW_I64_SUB8_U: + case WASM_OP_ATOMIC_RMW_I64_SUB16_U: + case WASM_OP_ATOMIC_RMW_I64_SUB32_U: + case WASM_OP_ATOMIC_RMW_I64_AND: + case WASM_OP_ATOMIC_RMW_I64_AND8_U: + case WASM_OP_ATOMIC_RMW_I64_AND16_U: + case WASM_OP_ATOMIC_RMW_I64_AND32_U: + case WASM_OP_ATOMIC_RMW_I64_OR: + case WASM_OP_ATOMIC_RMW_I64_OR8_U: + case WASM_OP_ATOMIC_RMW_I64_OR16_U: + case WASM_OP_ATOMIC_RMW_I64_OR32_U: + case WASM_OP_ATOMIC_RMW_I64_XOR: + case WASM_OP_ATOMIC_RMW_I64_XOR8_U: + case WASM_OP_ATOMIC_RMW_I64_XOR16_U: + case WASM_OP_ATOMIC_RMW_I64_XOR32_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG: + case WASM_OP_ATOMIC_RMW_I64_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG32_U: + POP_I64(); + POP_I32(); + PUSH_I64(); + break; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + POP_I32(); + POP_I32(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + POP_I64(); + POP_I64(); + POP_I32(); + PUSH_I64(); + break; + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x %02x", + "unsupported opcode", 0xfe, opcode); + goto fail; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + default: + set_error_buf_v(error_buf, error_buf_size, + "%s %02x", + "unsupported opcode", opcode); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + last_op = opcode; +#endif + } + + if (loader_ctx->csp_num > 0) { + set_error_buf(error_buf, error_buf_size, + "function body must end with END opcode"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled == NULL) + goto re_scan; + + func->const_cell_num = loader_ctx->const_cell_num; + if (!(func->consts = func_const = + loader_malloc(func->const_cell_num * 4, + error_buf, error_buf_size))) { + goto fail; + } + func_const_end = func->consts + func->const_cell_num * 4; + /* reverse the const buf */ + for (int i = loader_ctx->num_const - 1; i >= 0; i--) { + Const *c = (Const*)(loader_ctx->const_buf + i * sizeof(Const)); + if (c->value_type == VALUE_TYPE_F64 + || c->value_type == VALUE_TYPE_I64) { + bh_memcpy_s(func_const, func_const_end - func_const, + &(c->value.f64), sizeof(int64)); + func_const += sizeof(int64); + } else { + bh_memcpy_s(func_const, func_const_end - func_const, + &(c->value.f32), sizeof(int32)); + func_const += sizeof(int32); + } + } + + func->max_stack_cell_num = loader_ctx->preserved_local_offset - + loader_ctx->start_dynamic_offset + 1; +#else + func->max_stack_cell_num = loader_ctx->max_stack_cell_num; +#endif + func->max_block_num = loader_ctx->max_csp_num; + return_value = true; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + + (void)u8; + (void)u32; + (void)i32; + (void)i64; + (void)local_offset; + (void)p_org; + (void)mem_offset; + (void)align; + return return_value; +} diff --git a/wamr/core/iwasm/interpreter/wasm_loader.h b/wamr/core/iwasm/interpreter/wasm_loader.h new file mode 100644 index 0000000..c7a57e6 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_loader.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef _WASM_LOADER_H +#define _WASM_LOADER_H + +#include "wasm.h" +#include "bh_hashmap.h" +#include "../common/wasm_runtime_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Load a WASM module from a specified byte buffer. + * + * @param buf the byte buffer which contains the WASM binary data + * @param size the size of the buffer + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return return module loaded, NULL if failed + */ +WASMModule* +wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size); + +/** + * Load a WASM module from a specified WASM section list. + * + * @param section_list the section list which contains each section data + * @param error_buf output of the exception info + * @param error_buf_size the size of the exception string + * + * @return return WASM module loaded, NULL if failed + */ +WASMModule* +wasm_loader_load_from_sections(WASMSection *section_list, + char *error_buf, uint32 error_buf_size); + +/** + * Unload a WASM module. + * + * @param module the module to be unloaded + */ +void +wasm_loader_unload(WASMModule *module); + +/** + * Find address of related else opcode and end opcode of opcode block/loop/if + * according to the start address of opcode. + * + * @param module the module to find + * @param start_addr the next address of opcode block/loop/if + * @param code_end_addr the end address of function code block + * @param block_type the type of block, 0/1/2 denotes block/loop/if + * @param p_else_addr returns the else addr if found + * @param p_end_addr returns the end addr if found + * @param error_buf returns the error log for this function + * @param error_buf_size returns the error log string length + * + * @return true if success, false otherwise + */ +bool +wasm_loader_find_block_addr(BlockAddr *block_addr_cache, + const uint8 *start_addr, + const uint8 *code_end_addr, + uint8 block_type, + uint8 **p_else_addr, + uint8 **p_end_addr, + char *error_buf, + uint32 error_buf_size); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_LOADER_H */ diff --git a/wamr/core/iwasm/interpreter/wasm_mini_loader.c b/wamr/core/iwasm/interpreter/wasm_mini_loader.c new file mode 100644 index 0000000..d1541a6 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_mini_loader.c @@ -0,0 +1,5653 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_loader.h" +#include "bh_common.h" +#include "bh_log.h" +#include "wasm.h" +#include "wasm_opcode.h" +#include "wasm_runtime.h" +#include "../common/wasm_native.h" + +/* Read a value of given type from the address pointed to by the given + pointer and increase the pointer to the position just after the + value being read. */ +#define TEMPLATE_READ_VALUE(Type, p) \ + (p += sizeof(Type), *(Type *)(p - sizeof(Type))) + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, + "WASM module load failed: %s", string); +} + +#define CHECK_BUF(buf, buf_end, length) do { \ + bh_assert(buf + length <= buf_end); \ + } while (0) + +#define CHECK_BUF1(buf, buf_end, length) do { \ + bh_assert(buf + length <= buf_end); \ + } while (0) + +static void +skip_leb(const uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, + char* error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + bh_assert(bcnt + 1 <= (maxbits + 6) / 7); + CHECK_BUF(buf, buf_end, offset + 1); + byte = buf[offset]; + offset += 1; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + *p_buf += offset; +} + +#define skip_leb_int64(p, p_end) do { \ + skip_leb(&p, p_end, 64, \ + error_buf, error_buf_size); \ + } while (0) + +#define skip_leb_uint32(p, p_end) do { \ + skip_leb(&p, p_end, 32, \ + error_buf, error_buf_size); \ + } while (0) + +#define skip_leb_int32(p, p_end) do { \ + skip_leb(&p, p_end, 32, \ + error_buf, error_buf_size); \ + } while (0) + +static void +read_leb(uint8 **p_buf, const uint8 *buf_end, + uint32 maxbits, bool sign, uint64 *p_result, + char* error_buf, uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + uint64 result = 0; + uint32 shift = 0; + uint32 offset = 0, bcnt = 0; + uint64 byte; + + while (true) { + bh_assert(bcnt + 1 <= (maxbits + 6) / 7); + CHECK_BUF(buf, buf_end, offset + 1); + byte = buf[offset]; + offset += 1; + result |= ((byte & 0x7f) << shift); + shift += 7; + bcnt += 1; + if ((byte & 0x80) == 0) { + break; + } + } + + if (!sign && maxbits == 32 && shift >= maxbits) { + /* The top bits set represent values > 32 bits */ + bh_assert(!(((uint8)byte) & 0xf0)); + } + else if (sign && maxbits == 32) { + if (shift < maxbits) { + /* Sign extend */ + result = (((int32)result) << (maxbits - shift)) + >> (maxbits - shift); + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x8; + int top_bits = ((uint8)byte) & 0xf0; + bh_assert(!((sign_bit_set && top_bits != 0x70) + || (!sign_bit_set && top_bits != 0))); + (void)top_bits; + (void)sign_bit_set; + } + } + else if (sign && maxbits == 64) { + if (shift < maxbits) { + /* Sign extend */ + result = (((int64)result) << (maxbits - shift)) + >> (maxbits - shift); + } + else { + /* The top bits should be a sign-extension of the sign bit */ + bool sign_bit_set = ((uint8)byte) & 0x1; + int top_bits = ((uint8)byte) & 0xfe; + + bh_assert(!((sign_bit_set && top_bits != 0x7e) + || (!sign_bit_set && top_bits != 0))); + (void)top_bits; + (void)sign_bit_set; + } + } + + *p_buf += offset; + *p_result = result; +} + +#define read_uint8(p) TEMPLATE_READ_VALUE(uint8, p) +#define read_uint32(p) TEMPLATE_READ_VALUE(uint32, p) +#define read_bool(p) TEMPLATE_READ_VALUE(bool, p) + +#define read_leb_int64(p, p_end, res) do { \ + uint64 res64; \ + read_leb((uint8**)&p, p_end, 64, true, &res64, \ + error_buf, error_buf_size); \ + res = (int64)res64; \ +} while (0) + +#define read_leb_uint32(p, p_end, res) do { \ + uint64 res64; \ + read_leb((uint8**)&p, p_end, 32, false, &res64, \ + error_buf, error_buf_size); \ + res = (uint32)res64; \ +} while (0) + +#define read_leb_int32(p, p_end, res) do { \ + uint64 res64; \ + read_leb((uint8**)&p, p_end, 32, true, &res64, \ + error_buf, error_buf_size); \ + res = (int32)res64; \ +} while (0) + +static void * +loader_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +static char * +const_str_list_insert(const uint8 *str, uint32 len, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + StringNode *node, *node_next; + + /* Search const str list */ + node = module->const_str_list; + while (node) { + node_next = node->next; + if (strlen(node->str) == len + && !memcmp(node->str, str, len)) + break; + node = node_next; + } + + if (node) { + LOG_DEBUG("reuse %s", node->str); + return node->str; + } + + if (!(node = loader_malloc(sizeof(StringNode) + len + 1, + error_buf, error_buf_size))) { + return NULL; + } + + node->str = ((char*)node) + sizeof(StringNode); + bh_memcpy_s(node->str, len + 1, str, len); + node->str[len] = '\0'; + + if (!module->const_str_list) { + /* set as head */ + module->const_str_list = node; + node->next = NULL; + } + else { + /* insert it */ + node->next = module->const_str_list; + module->const_str_list = node; + } + + return node->str; +} + +static bool +load_init_expr(const uint8 **p_buf, const uint8 *buf_end, + InitializerExpression *init_expr, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 flag, end_byte, *p_float; + uint32 i; + + CHECK_BUF(p, p_end, 1); + init_expr->init_expr_type = read_uint8(p); + flag = init_expr->init_expr_type; + + switch (flag) { + /* i32.const */ + case INIT_EXPR_TYPE_I32_CONST: + bh_assert(type == VALUE_TYPE_I32); + read_leb_int32(p, p_end, init_expr->u.i32); + break; + /* i64.const */ + case INIT_EXPR_TYPE_I64_CONST: + bh_assert(type == VALUE_TYPE_I64); + read_leb_int64(p, p_end, init_expr->u.i64); + break; + /* f32.const */ + case INIT_EXPR_TYPE_F32_CONST: + bh_assert(type == VALUE_TYPE_F32); + CHECK_BUF(p, p_end, 4); + p_float = (uint8*)&init_expr->u.f32; + for (i = 0; i < sizeof(float32); i++) + *p_float++ = *p++; + break; + /* f64.const */ + case INIT_EXPR_TYPE_F64_CONST: + bh_assert(type == VALUE_TYPE_F64); + CHECK_BUF(p, p_end, 8); + p_float = (uint8*)&init_expr->u.f64; + for (i = 0; i < sizeof(float64); i++) + *p_float++ = *p++; + break; + /* get_global */ + case INIT_EXPR_TYPE_GET_GLOBAL: + read_leb_uint32(p, p_end, init_expr->u.global_index); + break; + default: + bh_assert(0); + break; + } + CHECK_BUF(p, p_end, 1); + end_byte = read_uint8(p); + bh_assert(end_byte == 0x0b); + *p_buf = p; + + (void)end_byte; + return true; +} + +static bool +load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_org; + uint32 type_count, param_count, result_count, i, j; + uint32 param_cell_num, ret_cell_num; + uint64 total_size; + uint8 flag; + WASMType *type; + + read_leb_uint32(p, p_end, type_count); + + if (type_count) { + module->type_count = type_count; + total_size = sizeof(WASMType*) * (uint64)type_count; + if (!(module->types = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < type_count; i++) { + CHECK_BUF(p, p_end, 1); + flag = read_uint8(p); + bh_assert(flag == 0x60); + + read_leb_uint32(p, p_end, param_count); + + /* Resolve param count and result count firstly */ + p_org = p; + CHECK_BUF(p, p_end, param_count); + p += param_count; + read_leb_uint32(p, p_end, result_count); + CHECK_BUF(p, p_end, result_count); + p = p_org; + + bh_assert(param_count <= UINT16_MAX && result_count <= UINT16_MAX); + + total_size = offsetof(WASMType, types) + + sizeof(uint8) * (uint64)(param_count + result_count); + if (!(type = module->types[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Resolve param types and result types */ + type->param_count = (uint16)param_count; + type->result_count = (uint16)result_count; + for (j = 0; j < param_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[j] = read_uint8(p); + } + read_leb_uint32(p, p_end, result_count); + for (j = 0; j < result_count; j++) { + CHECK_BUF(p, p_end, 1); + type->types[param_count + j] = read_uint8(p); + } + + param_cell_num = wasm_get_cell_num(type->types, param_count); + ret_cell_num = wasm_get_cell_num(type->types + param_count, + result_count); + bh_assert(param_cell_num <= UINT16_MAX && ret_cell_num <= UINT16_MAX); + type->param_cell_num = (uint16)param_cell_num; + type->ret_cell_num = (uint16)ret_cell_num; + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load type section success.\n"); + (void)flag; + return true; +} + +static bool +load_function_import(const WASMModule *parent_module, WASMModule *sub_module, + char *sub_module_name, char *function_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMFunctionImport *function, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_type_index = 0; + WASMType *declare_func_type = NULL; + WASMFunction *linked_func = NULL; + const char *linked_signature = NULL; + void *linked_attachment = NULL; + bool linked_call_conv_raw = false; + bool is_built_in_module = false; + + CHECK_BUF(p, p_end, 1); + read_leb_uint32(p, p_end, declare_type_index); + *p_buf = p; + + bh_assert(declare_type_index < parent_module->type_count); + + declare_func_type = parent_module->types[declare_type_index]; + + is_built_in_module = wasm_runtime_is_built_in_module(sub_module_name); + if (is_built_in_module) { + LOG_DEBUG("%s is a function of a built-in module %s", + function_name, sub_module_name); + /* check built-in modules */ + linked_func = wasm_native_resolve_symbol(sub_module_name, + function_name, + declare_func_type, + &linked_signature, + &linked_attachment, + &linked_call_conv_raw); + } + + if (!linked_func) { +#if WASM_ENABLE_SPEC_TEST != 0 + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; +#else +#if WASM_ENABLE_WAMR_COMPILER == 0 + LOG_WARNING("warning: fail to link import function (%s, %s)", + sub_module_name, function_name); +#endif +#endif + } + + function->module_name = sub_module_name; + function->field_name = function_name; + function->func_type = declare_func_type; + /* func_ptr_linked is for built-in functions */ + function->func_ptr_linked = is_built_in_module ? linked_func : NULL; + function->signature = linked_signature; + function->attachment = linked_attachment; + function->call_conv_raw = linked_call_conv_raw; + return true; +} + +static bool +load_table_import(WASMModule *sub_module, const char *sub_module_name, + const char *table_name, const uint8 **p_buf, + const uint8 *buf_end, WASMTableImport *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 declare_elem_type = 0; + uint32 declare_max_size_flag = 0; + uint32 declare_init_size = 0; + uint32 declare_max_size = 0; + + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + declare_elem_type = read_uint8(p); + bh_assert(TABLE_ELEM_TYPE_ANY_FUNC == declare_elem_type); + + read_leb_uint32(p, p_end, declare_max_size_flag); + read_leb_uint32(p, p_end, declare_init_size); + if (declare_max_size_flag & 1) { + read_leb_uint32(p, p_end, declare_max_size); + bh_assert(table->init_size <= table->max_size); + } else { + declare_max_size = 0x10000; + } + *p_buf = p; + + bh_assert(!((declare_max_size_flag & 1) + && declare_init_size > declare_max_size)); + + /* now we believe all declaration are ok */ + table->elem_type = declare_elem_type; + table->init_size = declare_init_size; + table->flags = declare_max_size_flag; + table->max_size = declare_max_size; + return true; +} + +unsigned +wasm_runtime_memory_pool_size(); + +static bool +load_memory_import(WASMModule *sub_module, const char *sub_module_name, + const char *memory_name, const uint8 **p_buf, + const uint8 *buf_end, WASMMemoryImport *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 pool_size = wasm_runtime_memory_pool_size(); +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count = pool_size / DEFAULT_NUM_BYTES_PER_PAGE; +#endif /* WASM_ENABLE_APP_FRAMEWORK */ + uint32 declare_max_page_count_flag = 0; + uint32 declare_init_page_count = 0; + uint32 declare_max_page_count = 0; + + read_leb_uint32(p, p_end, declare_max_page_count_flag); + read_leb_uint32(p, p_end, declare_init_page_count); + bh_assert(declare_init_page_count <= 65536); + + if (declare_max_page_count_flag & 1) { + read_leb_uint32(p, p_end, declare_max_page_count); + bh_assert(declare_init_page_count <= declare_max_page_count); + bh_assert(declare_max_page_count <= 65536); + if (declare_max_page_count > max_page_count) { + declare_max_page_count = max_page_count; + } + } + else { + /* Limit the maximum memory size to max_page_count */ + declare_max_page_count = max_page_count; + } + + /* now we believe all declaration are ok */ + memory->flags = declare_max_page_count_flag; + memory->init_page_count = declare_init_page_count; + memory->max_page_count = declare_max_page_count; + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +} + +static bool +load_global_import(const WASMModule *parent_module, + WASMModule *sub_module, + char *sub_module_name, char *global_name, + const uint8 **p_buf, const uint8 *buf_end, + WASMGlobalImport *global, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 declare_type = 0; + uint8 declare_mutable = 0; + bool is_mutable = false; + bool ret = false; + + CHECK_BUF(p, p_end, 2); + declare_type = read_uint8(p); + declare_mutable = read_uint8(p); + *p_buf = p; + + bh_assert(declare_mutable < 2); + + is_mutable = declare_mutable & 1 ? true : false; + +#if WASM_ENABLE_LIBC_BUILTIN != 0 + ret = wasm_runtime_is_built_in_module(sub_module_name); + if (ret) { + /* check built-in modules */ + ret = wasm_native_lookup_libc_builtin_global(sub_module_name, + global_name, global); + if (ret) { + LOG_DEBUG("(%s, %s) is a global of a built-in module", + sub_module_name, global_name); + } + } +#endif /* WASM_ENABLE_LIBC_BUILTIN */ + + if (!ret) { + set_error_buf(error_buf, error_buf_size, + "unknown import or incompatible import type"); + return false; + } + + global->module_name = sub_module_name; + global->field_name = global_name; + global->type = declare_type; + global->is_mutable = is_mutable; + (void)p_end; + return true; +} + +static bool +load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + table->elem_type = read_uint8(p); + bh_assert(TABLE_ELEM_TYPE_ANY_FUNC == table->elem_type); + + p_org = p; + read_leb_uint32(p, p_end, table->flags); + bh_assert(p - p_org <= 1); + bh_assert(table->flags <= 1); + (void)p_org; + + read_leb_uint32(p, p_end, table->init_size); + if (table->flags == 0) { + table->max_size = 0x10000; + } + else if (table->flags == 1) { + read_leb_uint32(p, p_end, table->max_size); + bh_assert(table->init_size <= table->max_size); + } + + *p_buf = p; + return true; +} + +static bool +load_memory(const uint8 **p_buf, const uint8 *buf_end, WASMMemory *memory, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end, *p_org; + uint32 pool_size = wasm_runtime_memory_pool_size(); +#if WASM_ENABLE_APP_FRAMEWORK != 0 + uint32 max_page_count = pool_size * APP_MEMORY_MAX_GLOBAL_HEAP_PERCENT + / DEFAULT_NUM_BYTES_PER_PAGE; +#else + uint32 max_page_count = pool_size / DEFAULT_NUM_BYTES_PER_PAGE; +#endif + + p_org = p; + read_leb_uint32(p, p_end, memory->flags); + bh_assert(p - p_org <= 1); + (void)p_org; +#if WASM_ENABLE_SHARED_MEMORY == 0 + bh_assert(memory->flags <= 1); +#else + bh_assert(memory->flags <= 3 && memory->flags != 2); +#endif + + read_leb_uint32(p, p_end, memory->init_page_count); + bh_assert(memory->init_page_count <= 65536); + + if (memory->flags & 1) { + read_leb_uint32(p, p_end, memory->max_page_count); + bh_assert(memory->init_page_count <= memory->max_page_count); + bh_assert(memory->max_page_count <= 65536); + if (memory->max_page_count > max_page_count) + memory->max_page_count = max_page_count; + } + else { + /* Limit the maximum memory size to max_page_count */ + memory->max_page_count = max_page_count; + } + + memory->num_bytes_per_page = DEFAULT_NUM_BYTES_PER_PAGE; + + *p_buf = p; + return true; +} + +static bool +load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end, *p_old; + uint32 import_count, name_len, type_index, i, u32, flags; + uint64 total_size; + WASMImport *import; + WASMImport *import_functions = NULL, *import_tables = NULL; + WASMImport *import_memories = NULL, *import_globals = NULL; + char *sub_module_name, *field_name; + uint8 u8, kind; + + read_leb_uint32(p, p_end, import_count); + + if (import_count) { + module->import_count = import_count; + total_size = sizeof(WASMImport) * (uint64)import_count; + if (!(module->imports = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + p_old = p; + + /* Scan firstly to get import count of each type */ + for (i = 0; i < import_count; i++) { + /* module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + /* field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + p += name_len; + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + read_leb_uint32(p, p_end, type_index); + module->import_function_count++; + break; + + case IMPORT_KIND_TABLE: /* import table */ + CHECK_BUF(p, p_end, 1); + /* 0x70 */ + u8 = read_uint8(p); + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_table_count++; + bh_assert(module->import_table_count <= 1); + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + read_leb_uint32(p, p_end, flags); + read_leb_uint32(p, p_end, u32); + if (flags & 1) + read_leb_uint32(p, p_end, u32); + module->import_memory_count++; + bh_assert(module->import_memory_count <= 1); + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + CHECK_BUF(p, p_end, 2); + p += 2; + module->import_global_count++; + break; + + default: + bh_assert(0); + break; + } + } + + if (module->import_function_count) + import_functions = module->import_functions = module->imports; + if (module->import_table_count) + import_tables = module->import_tables = + module->imports + module->import_function_count; + if (module->import_memory_count) + import_memories = module->import_memories = + module->imports + module->import_function_count + module->import_table_count; + if (module->import_global_count) + import_globals = module->import_globals = + module->imports + module->import_function_count + module->import_table_count + + module->import_memory_count; + + p = p_old; + + /* TODO: move it out of the loop */ + /* insert "env", "wasi_unstable" and "wasi_snapshot_preview1" to const str list */ + if (!const_str_list_insert((uint8*)"env", 3, module, error_buf, error_buf_size) + || !const_str_list_insert((uint8*)"wasi_unstable", 13, module, + error_buf, error_buf_size) + || !const_str_list_insert((uint8*)"wasi_snapshot_preview1", 22, module, + error_buf, error_buf_size)) { + return false; + } + + /* Scan again to read the data */ + for (i = 0; i < import_count; i++) { + WASMModule *sub_module = NULL; + + /* load module name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(sub_module_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { + return false; + } + p += name_len; + + /* load field name */ + read_leb_uint32(p, p_end, name_len); + CHECK_BUF(p, p_end, name_len); + if (!(field_name = const_str_list_insert( + p, name_len, module, error_buf, error_buf_size))) { + return false; + } + p += name_len; + + LOG_DEBUG("import #%d: (%s, %s)", i, sub_module_name, field_name); + + CHECK_BUF(p, p_end, 1); + /* 0x00/0x01/0x02/0x03 */ + kind = read_uint8(p); + switch (kind) { + case IMPORT_KIND_FUNC: /* import function */ + bh_assert(import_functions); + import = import_functions++; + if (!load_function_import(module, sub_module, + sub_module_name, field_name, &p, + p_end, &import->u.function, + error_buf, error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_TABLE: /* import table */ + bh_assert(import_tables); + import = import_tables++; + if (!load_table_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.table, + error_buf, + error_buf_size)) { + LOG_DEBUG("can not import such a table (%s,%s)", + sub_module_name, field_name); + return false; + } + break; + + case IMPORT_KIND_MEMORY: /* import memory */ + bh_assert(import_memories); + import = import_memories++; + if (!load_memory_import(sub_module, + sub_module_name, + field_name, + &p, + p_end, + &import->u.memory, + error_buf, + error_buf_size)) { + return false; + } + break; + + case IMPORT_KIND_GLOBAL: /* import global */ + bh_assert(import_globals); + import = import_globals++; + if (!load_global_import(module, sub_module, + sub_module_name, field_name, + &p, p_end, &import->u.global, + error_buf, error_buf_size)) { + return false; + } + break; + + default: + bh_assert(0); + import = NULL; + break; + } + import->kind = kind; + import->u.names.module_name = sub_module_name; + import->u.names.field_name = field_name; + (void)sub_module; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + import = module->import_functions; + for (i = 0; i < module->import_function_count; i++, import++) { + if (!strcmp(import->u.names.module_name, "wasi_unstable") + || !strcmp(import->u.names.module_name, "wasi_snapshot_preview1")) { + module->is_wasi_module = true; + break; + } + } +#endif + } + + bh_assert(p == p_end); + + LOG_VERBOSE("Load import section success.\n"); + (void)u8; + (void)u32; + (void)type_index; + return true; +} + +static bool +init_function_local_offsets(WASMFunction *func, + char *error_buf, uint32 error_buf_size) +{ + WASMType *param_type = func->func_type; + uint32 param_count = param_type->param_count; + uint8 *param_types = param_type->types; + uint32 local_count = func->local_count; + uint8 *local_types = func->local_types; + uint32 i, local_offset = 0; + uint64 total_size = sizeof(uint16) * ((uint64)param_count + local_count); + + if (!(func->local_offsets = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < param_count; i++) { + func->local_offsets[i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(param_types[i]); + } + + for (i = 0; i < local_count; i++) { + func->local_offsets[param_count + i] = (uint16)local_offset; + local_offset += wasm_value_type_cell_num(local_types[i]); + } + + bh_assert(local_offset == func->param_cell_num + func->local_cell_num); + return true; +} + +static bool +load_function_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_code, const uint8 *buf_code_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_code = buf_code, *p_code_end, *p_code_save; + uint32 func_count; + uint64 total_size; + uint32 code_count = 0, code_size, type_index, i, j, k, local_type_index; + uint32 local_count, local_set_count, sub_local_count; + uint8 type; + WASMFunction *func; + + read_leb_uint32(p, p_end, func_count); + + if (buf_code) + read_leb_uint32(p_code, buf_code_end, code_count); + + bh_assert(func_count == code_count); + + if (func_count) { + module->function_count = func_count; + total_size = sizeof(WASMFunction*) * (uint64)func_count; + if (!(module->functions = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < func_count; i++) { + /* Resolve function type */ + read_leb_uint32(p, p_end, type_index); + bh_assert(type_index < module->type_count); + + read_leb_uint32(p_code, buf_code_end, code_size); + bh_assert(code_size > 0 + && p_code + code_size <= buf_code_end); + + /* Resolve local set count */ + p_code_end = p_code + code_size; + local_count = 0; + read_leb_uint32(p_code, buf_code_end, local_set_count); + p_code_save = p_code; + + /* Calculate total local count */ + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + bh_assert(sub_local_count <= UINT32_MAX - local_count); + + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + local_count += sub_local_count; + } + + /* Alloc memory, layout: function structure + local types */ + code_size = (uint32)(p_code_end - p_code); + + total_size = sizeof(WASMFunction) + (uint64)local_count; + if (!(func = module->functions[i] = + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + /* Set function type, local count, code size and code body */ + func->func_type = module->types[type_index]; + func->local_count = local_count; + if (local_count > 0) + func->local_types = (uint8*)func + sizeof(WASMFunction); + func->code_size = code_size; + /* + * we shall make a copy of code body [p_code, p_code + code_size] + * when we are worrying about inappropriate releasing behaviour. + * all code bodies are actually in a buffer which user allocates in + * his embedding environment and we don't have power on them. + * it will be like: + * code_body_cp = malloc(code_size); + * memcpy(code_body_cp, p_code, code_size); + * func->code = code_body_cp; + */ + func->code = (uint8*)p_code; + + /* Load each local type */ + p_code = p_code_save; + local_type_index = 0; + for (j = 0; j < local_set_count; j++) { + read_leb_uint32(p_code, buf_code_end, sub_local_count); + bh_assert(!(local_type_index + sub_local_count <= local_type_index + || local_type_index + sub_local_count > local_count)); + + CHECK_BUF(p_code, buf_code_end, 1); + /* 0x7F/0x7E/0x7D/0x7C */ + type = read_uint8(p_code); + bh_assert(type >= VALUE_TYPE_F64 && type <= VALUE_TYPE_I32); + for (k = 0; k < sub_local_count; k++) { + func->local_types[local_type_index++] = type; + } + } + + func->param_cell_num = func->func_type->param_cell_num; + func->ret_cell_num = func->func_type->ret_cell_num; + func->local_cell_num = + wasm_get_cell_num(func->local_types, func->local_count); + + if (!init_function_local_offsets(func, error_buf, error_buf_size)) + return false; + + p_code = p_code_end; + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load function section success.\n"); + (void)code_count; + return true; +} + +static bool +load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_count, i; + uint64 total_size; + WASMTable *table; + + read_leb_uint32(p, p_end, table_count); + bh_assert(module->import_table_count + table_count <= 1); + + if (table_count) { + module->table_count = table_count; + total_size = sizeof(WASMTable) * (uint64)table_count; + if (!(module->tables = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each table */ + table = module->tables; + for (i = 0; i < table_count; i++, table++) + if (!load_table(&p, p_end, table, error_buf, error_buf_size)) + return false; + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load table section success.\n"); + return true; +} + +static bool +load_memory_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 memory_count, i; + uint64 total_size; + WASMMemory *memory; + + read_leb_uint32(p, p_end, memory_count); + bh_assert(module->import_memory_count + memory_count <= 1); + + if (memory_count) { + module->memory_count = memory_count; + total_size = sizeof(WASMMemory) * (uint64)memory_count; + if (!(module->memories = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + /* load each memory */ + memory = module->memories; + for (i = 0; i < memory_count; i++, memory++) + if (!load_memory(&p, p_end, memory, error_buf, error_buf_size)) + return false; + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load memory section success.\n"); + return true; +} + +static bool +load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 global_count, i; + uint64 total_size; + WASMGlobal *global; + uint8 mutable; + + read_leb_uint32(p, p_end, global_count); + + if (global_count) { + module->global_count = global_count; + total_size = sizeof(WASMGlobal) * (uint64)global_count; + if (!(module->globals = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + global = module->globals; + + for(i = 0; i < global_count; i++, global++) { + CHECK_BUF(p, p_end, 2); + global->type = read_uint8(p); + mutable = read_uint8(p); + bh_assert(mutable < 2); + global->is_mutable = mutable ? true : false; + + /* initialize expression */ + if (!load_init_expr(&p, p_end, &(global->init_expr), + global->type, error_buf, error_buf_size)) + return false; + + if (INIT_EXPR_TYPE_GET_GLOBAL == global->init_expr.init_expr_type) { + /** + * Currently, constant expressions occurring as initializers + * of globals are further constrained in that contained + * global.get instructions are + * only allowed to refer to imported globals. + */ + uint32 target_global_index = global->init_expr.u.global_index; + bh_assert(target_global_index < module->import_global_count); + (void)target_global_index; + } + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load global section success.\n"); + return true; +} + +static bool +load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 export_count, i, j, index; + uint64 total_size; + uint32 str_len; + WASMExport *export; + const char *name; + + read_leb_uint32(p, p_end, export_count); + + if (export_count) { + module->export_count = export_count; + total_size = sizeof(WASMExport) * (uint64)export_count; + if (!(module->exports = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + export = module->exports; + for (i = 0; i < export_count; i++, export++) { + read_leb_uint32(p, p_end, str_len); + CHECK_BUF(p, p_end, str_len); + + for (j = 0; j < i; j++) { + name = module->exports[j].name; + bh_assert(!(strlen(name) == str_len + && memcmp(name, p, str_len) == 0)); + } + + if (!(export->name = const_str_list_insert(p, str_len, module, + error_buf, error_buf_size))) { + return false; + } + + p += str_len; + CHECK_BUF(p, p_end, 1); + export->kind = read_uint8(p); + read_leb_uint32(p, p_end, index); + export->index = index; + + switch(export->kind) { + /*function index*/ + case EXPORT_KIND_FUNC: + bh_assert(index < module->function_count + + module->import_function_count); + break; + /*table index*/ + case EXPORT_KIND_TABLE: + bh_assert(index < module->table_count + + module->import_table_count); + break; + /*memory index*/ + case EXPORT_KIND_MEMORY: + bh_assert(index < module->memory_count + + module->import_memory_count); + break; + /*global index*/ + case EXPORT_KIND_GLOBAL: + bh_assert(index < module->global_count + + module->import_global_count); + break; + default: + bh_assert(0); + break; + } + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load export section success.\n"); + (void)name; + return true; +} + +static bool +load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 table_segment_count, i, j, table_index, function_count, function_index; + uint64 total_size; + WASMTableSeg *table_segment; + + read_leb_uint32(p, p_end, table_segment_count); + + if (table_segment_count) { + module->table_seg_count = table_segment_count; + total_size = sizeof(WASMTableSeg) * (uint64)table_segment_count; + if (!(module->table_segments = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + table_segment = module->table_segments; + for (i = 0; i < table_segment_count; i++, table_segment++) { + bh_assert(p < p_end); + read_leb_uint32(p, p_end, table_index); + bh_assert(table_index < module->import_table_count + + module->table_count); + + table_segment->table_index = table_index; + + /* initialize expression */ + if (!load_init_expr(&p, p_end, &(table_segment->base_offset), + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, function_count); + table_segment->function_count = function_count; + total_size = sizeof(uint32) * (uint64)function_count; + if (!(table_segment->func_indexes = (uint32 *) + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (j = 0; j < function_count; j++) { + read_leb_uint32(p, p_end, function_index); + bh_assert(function_index < module->import_function_count + + module->function_count); + table_segment->func_indexes[j] = function_index; + } + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load table segment section success.\n"); + return true; +} + +static bool +load_data_segment_section(const uint8 *buf, const uint8 *buf_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count, i, mem_index, data_seg_len; + uint64 total_size; + WASMDataSeg *dataseg; + InitializerExpression init_expr; +#if WASM_ENABLE_BULK_MEMORY != 0 + bool is_passive = false; + uint32 mem_flag; +#endif + + read_leb_uint32(p, p_end, data_seg_count); + +#if WASM_ENABLE_BULK_MEMORY != 0 + bh_assert(module->data_seg_count1 == 0 + || data_seg_count == module->data_seg_count1); +#endif + + if (data_seg_count) { + module->data_seg_count = data_seg_count; + total_size = sizeof(WASMDataSeg*) * (uint64)data_seg_count; + if (!(module->data_segments = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < data_seg_count; i++) { + read_leb_uint32(p, p_end, mem_index); +#if WASM_ENABLE_BULK_MEMORY != 0 + is_passive = false; + mem_flag = mem_index & 0x03; + switch (mem_flag) { + case 0x01: + is_passive = true; + break; + case 0x00: + /* no memory index, treat index as 0 */ + mem_index = 0; + goto check_mem_index; + case 0x02: + /* read following memory index */ + read_leb_uint32(p, p_end, mem_index); +check_mem_index: + bh_assert(mem_index < module->import_memory_count + + module->memory_count); + break; + case 0x03: + default: + bh_assert(0); + break; + } +#else + bh_assert(mem_index < module->import_memory_count + + module->memory_count); +#endif /* WASM_ENABLE_BULK_MEMORY */ + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (!is_passive) +#endif + if (!load_init_expr(&p, p_end, &init_expr, VALUE_TYPE_I32, + error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, data_seg_len); + + if (!(dataseg = module->data_segments[i] = loader_malloc + (sizeof(WASMDataSeg), error_buf, error_buf_size))) { + return false; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 + dataseg->is_passive = is_passive; + if (!is_passive) +#endif + { + bh_memcpy_s(&dataseg->base_offset, sizeof(InitializerExpression), + &init_expr, sizeof(InitializerExpression)); + + dataseg->memory_index = mem_index; + } + + dataseg->data_length = data_seg_len; + CHECK_BUF(p, p_end, data_seg_len); + dataseg->data = (uint8*)p; + p += data_seg_len; + } + } + + bh_assert(p == p_end); + LOG_VERBOSE("Load data segment section success.\n"); + return true; +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +load_datacount_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 data_seg_count1 = 0; + + read_leb_uint32(p, p_end, data_seg_count1); + module->data_seg_count1 = data_seg_count1; + + bh_assert(p == p_end); + LOG_VERBOSE("Load datacount section success.\n"); + return true; +} +#endif + +static bool +load_code_section(const uint8 *buf, const uint8 *buf_end, + const uint8 *buf_func, + const uint8 *buf_func_end, + WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + const uint8 *p_func = buf_func; + uint32 func_count = 0, code_count; + + /* code has been loaded in function section, so pass it here, just check + * whether function and code section have inconsistent lengths */ + read_leb_uint32(p, p_end, code_count); + + if (buf_func) + read_leb_uint32(p_func, buf_func_end, func_count); + + bh_assert(func_count == code_count); + LOG_VERBOSE("Load code segment section success.\n"); + (void)code_count; + (void)func_count; + return true; +} + +static bool +load_start_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + WASMType *type; + uint32 start_function; + + read_leb_uint32(p, p_end, start_function); + + bh_assert(start_function < module->function_count + + module->import_function_count); + + if (start_function < module->import_function_count) + type = module->import_functions[start_function].u.function.func_type; + else + type = module->functions[start_function - module->import_function_count] + ->func_type; + + bh_assert(type->param_count == 0 && type->result_count == 0); + + module->start_function = start_function; + + bh_assert(p == p_end); + LOG_VERBOSE("Load start section success.\n"); + (void)type; + return true; +} + +static bool +load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = buf, *p_end = buf_end; + uint32 name_len; + + bh_assert(p < p_end); + + read_leb_uint32(p, p_end, name_len); + + bh_assert(name_len > 0 + && p + name_len <= p_end); + LOG_VERBOSE("Load custom section success.\n"); + (void)name_len; + return true; +} + + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + BlockAddr *block_addr_cache, + char *error_buf, uint32 error_buf_size); + +#if WASM_ENABLE_FAST_INTERP != 0 +void ** +wasm_interp_get_handle_table(); + +static void **handle_table; +#endif + +static bool +load_from_sections(WASMModule *module, WASMSection *sections, + char *error_buf, uint32 error_buf_size) +{ + WASMExport *export; + WASMSection *section = sections; + const uint8 *buf, *buf_end, *buf_code = NULL, *buf_code_end = NULL, + *buf_func = NULL, *buf_func_end = NULL; + WASMGlobal *aux_data_end_global = NULL, *aux_heap_base_global = NULL; + WASMGlobal *aux_stack_top_global = NULL, *global; + uint32 aux_data_end = (uint32)-1, aux_heap_base = (uint32)-1; + uint32 aux_stack_top = (uint32)-1, global_index, func_index, i; + uint32 aux_data_end_global_index = (uint32)-1; + uint32 aux_heap_base_global_index = (uint32)-1; + BlockAddr *block_addr_cache; + WASMType *func_type; + uint64 total_size; + + /* Find code and function sections if have */ + while (section) { + if (section->section_type == SECTION_TYPE_CODE) { + buf_code = section->section_body; + buf_code_end = buf_code + section->section_body_size; + } + else if (section->section_type == SECTION_TYPE_FUNC) { + buf_func = section->section_body; + buf_func_end = buf_func + section->section_body_size; + } + section = section->next; + } + + section = sections; + while (section) { + buf = section->section_body; + buf_end = buf + section->section_body_size; + LOG_DEBUG("to section %d", section->section_type); + switch (section->section_type) { + case SECTION_TYPE_USER: + /* unsupported user section, ignore it. */ + if (!load_user_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TYPE: + if (!load_type_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_IMPORT: + if (!load_import_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_FUNC: + if (!load_function_section(buf, buf_end, buf_code, buf_code_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_TABLE: + if (!load_table_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_MEMORY: + if (!load_memory_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_GLOBAL: + if (!load_global_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_EXPORT: + if (!load_export_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_START: + if (!load_start_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_ELEM: + if (!load_table_segment_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_CODE: + if (!load_code_section(buf, buf_end, buf_func, buf_func_end, + module, error_buf, error_buf_size)) + return false; + break; + case SECTION_TYPE_DATA: + if (!load_data_segment_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case SECTION_TYPE_DATACOUNT: + if (!load_datacount_section(buf, buf_end, module, error_buf, error_buf_size)) + return false; + break; +#endif + default: + set_error_buf(error_buf, error_buf_size, + "invalid section id"); + return false; + } + + section = section->next; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + handle_table = wasm_interp_get_handle_table(); +#endif + + total_size = sizeof(BlockAddr) * (uint64)BLOCK_ADDR_CACHE_SIZE * BLOCK_ADDR_CONFLICT_SIZE; + if (!(block_addr_cache = loader_malloc + (total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < module->function_count; i++) { + WASMFunction *func = module->functions[i]; + memset(block_addr_cache, 0, (uint32)total_size); + if (!wasm_loader_prepare_bytecode(module, func, block_addr_cache, + error_buf, error_buf_size)) { + wasm_runtime_free(block_addr_cache); + return false; + } + } + wasm_runtime_free(block_addr_cache); + + module->aux_data_end_global_index = (uint32)-1; + module->aux_heap_base_global_index = (uint32)-1; + module->aux_stack_top_global_index = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export++) { + if (export->kind == EXPORT_KIND_GLOBAL) { + if (!strcmp(export->name, "__heap_base")) { + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type == VALUE_TYPE_I32 + && !global->is_mutable + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST) { + aux_heap_base_global = global; + aux_heap_base = global->init_expr.u.i32; + aux_heap_base_global_index = export->index; + LOG_VERBOSE("Found aux __heap_base global, value: %d", + aux_heap_base); + } + } + else if (!strcmp(export->name, "__data_end")) { + global_index = export->index - module->import_global_count; + global = module->globals + global_index; + if (global->type == VALUE_TYPE_I32 + && !global->is_mutable + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST) { + aux_data_end_global = global; + aux_data_end = global->init_expr.u.i32; + aux_data_end_global_index = export->index; + LOG_VERBOSE("Found aux __data_end global, value: %d", + aux_data_end); + + aux_data_end = align_uint(aux_data_end, 16); + } + } + + /* For module compiled with -pthread option, the global is: + [0] stack_top <-- 0 + [1] tls_pointer + [2] tls_size + [3] data_end <-- 3 + [4] global_base + [5] heap_base <-- 5 + [6] dso_handle + + For module compiled without -pthread option: + [0] stack_top <-- 0 + [1] data_end <-- 1 + [2] global_base + [3] heap_base <-- 3 + [4] dso_handle + */ + if (aux_data_end_global && aux_heap_base_global + && aux_data_end <= aux_heap_base) { + module->aux_data_end_global_index = aux_data_end_global_index; + module->aux_data_end = aux_data_end; + module->aux_heap_base_global_index = aux_heap_base_global_index; + module->aux_heap_base = aux_heap_base; + + /* Resolve aux stack top global */ + for (global_index = 0; global_index < module->global_count; + global_index++) { + global = module->globals + global_index; + if (global->is_mutable /* heap_base and data_end is + not mutable */ + && global->type == VALUE_TYPE_I32 + && global->init_expr.init_expr_type == + INIT_EXPR_TYPE_I32_CONST + && (uint32)global->init_expr.u.i32 <= aux_heap_base) { + aux_stack_top_global = global; + aux_stack_top = (uint32)global->init_expr.u.i32; + module->aux_stack_top_global_index = + module->import_global_count + global_index; + module->aux_stack_bottom = aux_stack_top; + module->aux_stack_size = aux_stack_top > aux_data_end + ? aux_stack_top - aux_data_end + : aux_stack_top; + LOG_VERBOSE("Found aux stack top global, value: %d, " + "global index: %d, stack size: %d", + aux_stack_top, global_index, + module->aux_stack_size); + break; + } + } + break; + } + } + } + + module->malloc_function = (uint32)-1; + module->free_function = (uint32)-1; + + /* Resolve auxiliary data/stack/heap info and reset memory info */ + export = module->exports; + for (i = 0; i < module->export_count; i++, export++) { + if (export->kind == EXPORT_KIND_FUNC) { + if (!strcmp(export->name, "malloc") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 1 + && func_type->types[0] == VALUE_TYPE_I32 + && func_type->types[1] == VALUE_TYPE_I32) { + module->malloc_function = export->index; + LOG_VERBOSE("Found malloc function, index: %u", + export->index); + } + } + else if (!strcmp(export->name, "free") + && export->index >= module->import_function_count) { + func_index = export->index - module->import_function_count; + func_type = module->functions[func_index]->func_type; + if (func_type->param_count == 1 + && func_type->result_count == 0 + && func_type->types[0] == VALUE_TYPE_I32) { + module->free_function = export->index; + LOG_VERBOSE("Found free function, index: %u", + export->index); + } + } + } + } + + if (!module->possible_memory_grow) { + WASMMemoryImport *memory_import; + WASMMemory *memory; + + if (aux_data_end_global + && aux_heap_base_global + && aux_stack_top_global) { + uint64 init_memory_size; + uint32 shrunk_memory_size = align_uint(aux_heap_base, 8); + + if (module->import_memory_count) { + memory_import = &module->import_memories[0].u.memory; + init_memory_size = (uint64)memory_import->num_bytes_per_page * + memory_import->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory_import->num_bytes_per_page = shrunk_memory_size; + memory_import->init_page_count = 1; + LOG_VERBOSE("Shrink import memory size to %d", + shrunk_memory_size); + } + } + if (module->memory_count) { + memory = &module->memories[0]; + init_memory_size = (uint64)memory->num_bytes_per_page * + memory->init_page_count; + if (shrunk_memory_size <= init_memory_size) { + /* Reset memory info to decrease memory usage */ + memory->num_bytes_per_page = shrunk_memory_size; + memory->init_page_count = 1; + LOG_VERBOSE("Shrink memory size to %d", shrunk_memory_size); + } + } + } + +#if WASM_ENABLE_MULTI_MODULE == 0 + if (module->import_memory_count) { + memory_import = &module->import_memories[0].u.memory; + /* Memory init page count cannot be larger than 65536, we don't + check integer overflow again. */ + memory_import->num_bytes_per_page *= memory_import->init_page_count; + memory_import->init_page_count = memory_import->max_page_count = 1; + } + if (module->memory_count) { + /* Memory init page count cannot be larger than 65536, we don't + check integer overflow again. */ + memory = &module->memories[0]; + memory->num_bytes_per_page *= memory->init_page_count; + memory->init_page_count = memory->max_page_count = 1; + } +#endif + } + + return true; +} + +#if BH_ENABLE_MEMORY_PROFILING != 0 +static void wasm_loader_free(void *ptr) +{ + wasm_runtime_free(ptr); +} +#else +#define wasm_loader_free wasm_free +#endif + +static WASMModule* +create_module(char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = loader_malloc(sizeof(WASMModule), + error_buf, error_buf_size); + + if (!module) { + return NULL; + } + + module->module_type = Wasm_Module_Bytecode; + + /* Set start_function to -1, means no start function */ + module->start_function = (uint32)-1; + +#if WASM_ENABLE_MULTI_MODULE != 0 + module->import_module_list = &module->import_module_list_head; +#endif + return module; +} + +WASMModule * +wasm_loader_load_from_sections(WASMSection *section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(error_buf, error_buf_size); + if (!module) + return NULL; + + if (!load_from_sections(module, section_list, error_buf, error_buf_size)) { + wasm_loader_unload(module); + return NULL; + } + + LOG_VERBOSE("Load module from sections success.\n"); + return module; +} + +static void +destroy_sections(WASMSection *section_list) +{ + WASMSection *section = section_list, *next; + while (section) { + next = section->next; + wasm_runtime_free(section); + section = next; + } +} + +static uint8 section_ids[] = { + SECTION_TYPE_USER, + SECTION_TYPE_TYPE, + SECTION_TYPE_IMPORT, + SECTION_TYPE_FUNC, + SECTION_TYPE_TABLE, + SECTION_TYPE_MEMORY, + SECTION_TYPE_GLOBAL, + SECTION_TYPE_EXPORT, + SECTION_TYPE_START, + SECTION_TYPE_ELEM, +#if WASM_ENABLE_BULK_MEMORY != 0 + SECTION_TYPE_DATACOUNT, +#endif + SECTION_TYPE_CODE, + SECTION_TYPE_DATA +}; + +static uint8 +get_section_index(uint8 section_type) +{ + uint8 max_id = sizeof(section_ids) / sizeof(uint8); + + for (uint8 i = 0; i < max_id; i++) { + if (section_type == section_ids[i]) + return i; + } + + return (uint8)-1; +} + +static bool +create_sections(const uint8 *buf, uint32 size, + WASMSection **p_section_list, + char *error_buf, uint32 error_buf_size) +{ + WASMSection *section_list_end = NULL, *section; + const uint8 *p = buf, *p_end = buf + size/*, *section_body*/; + uint8 section_type, section_index, last_section_index = (uint8)-1; + uint32 section_size; + + bh_assert(!*p_section_list); + + p += 8; + while (p < p_end) { + CHECK_BUF(p, p_end, 1); + section_type = read_uint8(p); + section_index = get_section_index(section_type); + if (section_index != (uint8)-1) { + if (section_type != SECTION_TYPE_USER) { + /* Custom sections may be inserted at any place, + while other sections must occur at most once + and in prescribed order. */ + bh_assert(last_section_index == (uint8)-1 + || last_section_index < section_index); + last_section_index = section_index; + } + CHECK_BUF1(p, p_end, 1); + read_leb_uint32(p, p_end, section_size); + CHECK_BUF1(p, p_end, section_size); + + if (!(section = loader_malloc(sizeof(WASMSection), + error_buf, error_buf_size))) { + return false; + } + + section->section_type = section_type; + section->section_body = (uint8*)p; + section->section_body_size = section_size; + + if (!*p_section_list) + *p_section_list = section_list_end = section; + else { + section_list_end->next = section; + section_list_end = section; + } + + p += section_size; + } + else { + bh_assert(0); + } + } + + (void)last_section_index; + return true; +} + +static void +exchange32(uint8* p_data) +{ + uint8 value = *p_data; + *p_data = *(p_data + 3); + *(p_data + 3) = value; + + value = *(p_data + 1); + *(p_data + 1) = *(p_data + 2); + *(p_data + 2) = value; +} + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +static bool +load(const uint8 *buf, uint32 size, WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *buf_end = buf + size; + const uint8 *p = buf, *p_end = buf_end; + uint32 magic_number, version; + WASMSection *section_list = NULL; + + CHECK_BUF1(p, p_end, sizeof(uint32)); + magic_number = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8*)&magic_number); + + bh_assert(magic_number == WASM_MAGIC_NUMBER); + + CHECK_BUF1(p, p_end, sizeof(uint32)); + version = read_uint32(p); + if (!is_little_endian()) + exchange32((uint8*)&version); + + if (version != WASM_CURRENT_VERSION) { + set_error_buf(error_buf, error_buf_size, "unknown binary version"); + return false; + } + + if (!create_sections(buf, size, §ion_list, error_buf, error_buf_size) + || !load_from_sections(module, section_list, error_buf, error_buf_size)) { + destroy_sections(section_list); + return false; + } + + destroy_sections(section_list); + (void)p_end; + return true; +} + +WASMModule* +wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = create_module(error_buf, error_buf_size); + if (!module) { + return NULL; + } + + if (!load(buf, size, module, error_buf, error_buf_size)) { + goto fail; + } + + LOG_VERBOSE("Load module success.\n"); + return module; + +fail: + wasm_loader_unload(module); + return NULL; +} + +void +wasm_loader_unload(WASMModule *module) +{ + uint32 i; + + if (!module) + return; + + if (module->types) { + for (i = 0; i < module->type_count; i++) { + if (module->types[i]) + wasm_runtime_free(module->types[i]); + } + wasm_runtime_free(module->types); + } + + if (module->imports) + wasm_runtime_free(module->imports); + + if (module->functions) { + for (i = 0; i < module->function_count; i++) { + if (module->functions[i]) { + if (module->functions[i]->local_offsets) + wasm_runtime_free(module->functions[i]->local_offsets); +#if WASM_ENABLE_FAST_INTERP != 0 + if (module->functions[i]->code_compiled) + wasm_runtime_free(module->functions[i]->code_compiled); + if (module->functions[i]->consts) + wasm_runtime_free(module->functions[i]->consts); +#endif + wasm_runtime_free(module->functions[i]); + } + } + wasm_runtime_free(module->functions); + } + + if (module->tables) + wasm_runtime_free(module->tables); + + if (module->memories) + wasm_runtime_free(module->memories); + + if (module->globals) + wasm_runtime_free(module->globals); + + if (module->exports) + wasm_runtime_free(module->exports); + + if (module->table_segments) { + for (i = 0; i < module->table_seg_count; i++) { + if (module->table_segments[i].func_indexes) + wasm_runtime_free(module->table_segments[i].func_indexes); + } + wasm_runtime_free(module->table_segments); + } + + if (module->data_segments) { + for (i = 0; i < module->data_seg_count; i++) { + if (module->data_segments[i]) + wasm_runtime_free(module->data_segments[i]); + } + wasm_runtime_free(module->data_segments); + } + + if (module->const_str_list) { + StringNode *node = module->const_str_list, *node_next; + while (node) { + node_next = node->next; + wasm_runtime_free(node); + node = node_next; + } + } + + wasm_runtime_free(module); +} + +bool +wasm_loader_find_block_addr(BlockAddr *block_addr_cache, + const uint8 *start_addr, + const uint8 *code_end_addr, + uint8 label_type, + uint8 **p_else_addr, + uint8 **p_end_addr, + char *error_buf, + uint32 error_buf_size) +{ + const uint8 *p = start_addr, *p_end = code_end_addr; + uint8 *else_addr = NULL; + uint32 block_nested_depth = 1, count, i, j, t; + uint8 opcode, u8; + BlockAddr block_stack[16] = { 0 }, *block; + + i = ((uintptr_t)start_addr) % BLOCK_ADDR_CACHE_SIZE; + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) { + if (block[j].start_addr == start_addr) { + /* Cache hit */ + *p_else_addr = block[j].else_addr; + *p_end_addr = block[j].end_addr; + return true; + } + } + + /* Cache unhit */ + block_stack[0].start_addr = start_addr; + + while (p < code_end_addr) { + opcode = *p++; + + switch (opcode) { + case WASM_OP_UNREACHABLE: + case WASM_OP_NOP: + break; + + case WASM_OP_BLOCK: + case WASM_OP_LOOP: + case WASM_OP_IF: + CHECK_BUF(p, p_end, 1); + /* block result type: 0x40/0x7F/0x7E/0x7D/0x7C */ + u8 = read_uint8(p); + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case EXT_OP_BLOCK: + case EXT_OP_LOOP: + case EXT_OP_IF: + /* block type */ + skip_leb_uint32(p, p_end); + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) { + block_stack[block_nested_depth].start_addr = p; + block_stack[block_nested_depth].else_addr = NULL; + } + block_nested_depth++; + break; + + case WASM_OP_ELSE: + if (label_type == LABEL_TYPE_IF && block_nested_depth == 1) + else_addr = (uint8*)(p - 1); + if (block_nested_depth - 1 < sizeof(block_stack)/sizeof(BlockAddr)) + block_stack[block_nested_depth - 1].else_addr = (uint8*)(p - 1); + break; + + case WASM_OP_END: + if (block_nested_depth == 1) { + if (label_type == LABEL_TYPE_IF) + *p_else_addr = else_addr; + *p_end_addr = (uint8*)(p - 1); + + block_stack[0].end_addr = (uint8*)(p - 1); + for (t = 0; t < sizeof(block_stack)/sizeof(BlockAddr); t++) { + start_addr = block_stack[t].start_addr; + if (start_addr) { + i = ((uintptr_t)start_addr) % BLOCK_ADDR_CACHE_SIZE; + block = block_addr_cache + BLOCK_ADDR_CONFLICT_SIZE * i; + for (j = 0; j < BLOCK_ADDR_CONFLICT_SIZE; j++) + if (!block[j].start_addr) + break; + + if (j == BLOCK_ADDR_CONFLICT_SIZE) { + memmove(block + 1, block, (BLOCK_ADDR_CONFLICT_SIZE - 1) * + sizeof(BlockAddr)); + j = 0; + + } + block[j].start_addr = block_stack[t].start_addr; + block[j].else_addr = block_stack[t].else_addr; + block[j].end_addr = block_stack[t].end_addr; + } + else + break; + } + return true; + } + else { + block_nested_depth--; + if (block_nested_depth < sizeof(block_stack)/sizeof(BlockAddr)) + block_stack[block_nested_depth].end_addr = (uint8*)(p - 1); + } + break; + + case WASM_OP_BR: + case WASM_OP_BR_IF: + skip_leb_uint32(p, p_end); /* labelidx */ + break; + + case WASM_OP_BR_TABLE: + read_leb_uint32(p, p_end, count); /* lable num */ + for (i = 0; i <= count; i++) /* lableidxs */ + skip_leb_uint32(p, p_end); + break; + + case WASM_OP_RETURN: + break; + + case WASM_OP_CALL: + skip_leb_uint32(p, p_end); /* funcidx */ + break; + + case WASM_OP_CALL_INDIRECT: + skip_leb_uint32(p, p_end); /* typeidx */ + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* 0x00 */ + break; + + case WASM_OP_DROP: + case WASM_OP_SELECT: + case WASM_OP_DROP_64: + case WASM_OP_SELECT_64: + break; + + case WASM_OP_GET_LOCAL: + case WASM_OP_SET_LOCAL: + case WASM_OP_TEE_LOCAL: + case WASM_OP_GET_GLOBAL: + case WASM_OP_SET_GLOBAL: + case WASM_OP_GET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_64: + case WASM_OP_SET_GLOBAL_AUX_STACK: + skip_leb_uint32(p, p_end); /* localidx */ + break; + + case EXT_OP_GET_LOCAL_FAST: + case EXT_OP_SET_LOCAL_FAST: + case EXT_OP_TEE_LOCAL_FAST: + CHECK_BUF(p, p_end, 1); + p++; + break; + + case WASM_OP_I32_LOAD: + case WASM_OP_I64_LOAD: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_I32_STORE: + case WASM_OP_I64_STORE: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + skip_leb_uint32(p, p_end); /* align */ + skip_leb_uint32(p, p_end); /* offset */ + break; + + case WASM_OP_MEMORY_SIZE: + case WASM_OP_MEMORY_GROW: + skip_leb_uint32(p, p_end); /* 0x00 */ + break; + + case WASM_OP_I32_CONST: + skip_leb_int32(p, p_end); + break; + case WASM_OP_I64_CONST: + skip_leb_int64(p, p_end); + break; + case WASM_OP_F32_CONST: + p += sizeof(float32); + break; + case WASM_OP_F64_CONST: + p += sizeof(float64); + break; + + case WASM_OP_I32_EQZ: + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + case WASM_OP_I64_EQZ: + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + case WASM_OP_I32_WRAP_I64: + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + case WASM_OP_F32_DEMOTE_F64: + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + case WASM_OP_F64_PROMOTE_F32: + case WASM_OP_I32_REINTERPRET_F32: + case WASM_OP_I64_REINTERPRET_F64: + case WASM_OP_F32_REINTERPRET_I32: + case WASM_OP_F64_REINTERPRET_I64: + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + break; + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); + + switch (opcode1) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + skip_leb_uint32(p, p_end); + /* skip memory idx */ + p++; + break; + case WASM_OP_DATA_DROP: + skip_leb_uint32(p, p_end); + break; + case WASM_OP_MEMORY_COPY: + /* skip two memory idx */ + p += 2; + break; + case WASM_OP_MEMORY_FILL: + /* skip memory idx */ + p++; + break; +#endif + default: + bh_assert(0); + break; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + /* atomic_op (1 u8) + memarg (2 u32_leb) */ + opcode = read_uint8(p); + if (opcode != WASM_OP_ATOMIC_FENCE) { + skip_leb_uint32(p, p_end); /* align */ + skip_leb_uint32(p, p_end); /* offset */ + } + else { + /* atomic.fence doesn't have memarg */ + p++; + } + break; + } +#endif + + default: + bh_assert(0); + break; + } + } + + (void)u8; + return false; +} + +#define REF_I32 VALUE_TYPE_I32 +#define REF_F32 VALUE_TYPE_F32 +#define REF_I64_1 VALUE_TYPE_I64 +#define REF_I64_2 VALUE_TYPE_I64 +#define REF_F64_1 VALUE_TYPE_F64 +#define REF_F64_2 VALUE_TYPE_F64 +#define REF_ANY VALUE_TYPE_ANY + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_DEBUG_PREPROCESSOR != 0 +#define LOG_OP(...) os_printf(__VA_ARGS__) +#else +#define LOG_OP(...) (void)0 +#endif + +#define PATCH_ELSE 0 +#define PATCH_END 1 +typedef struct BranchBlockPatch { + struct BranchBlockPatch *next; + uint8 patch_type; + uint8 *code_compiled; +} BranchBlockPatch; +#endif + +typedef struct BranchBlock { + uint8 label_type; + BlockType block_type; + uint8 *start_addr; + uint8 *else_addr; + uint8 *end_addr; + uint32 stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + uint16 dynamic_offset; + uint8 *code_compiled; + BranchBlockPatch *patch_list; + /* This is used to save params frame_offset of of if block */ + int16 *param_frame_offsets; +#endif + + /* Indicate the operand stack is in polymorphic state. + * If the opcode is one of unreachable/br/br_table/return, stack is marked + * to polymorphic state until the block's 'end' opcode is processed. + * If stack is in polymorphic state and stack is empty, instruction can + * pop any type of value directly without decreasing stack top pointer + * and stack cell num. */ + bool is_stack_polymorphic; +} BranchBlock; + +typedef struct WASMLoaderContext { + /* frame ref stack */ + uint8 *frame_ref; + uint8 *frame_ref_bottom; + uint8 *frame_ref_boundary; + uint32 frame_ref_size; + uint32 stack_cell_num; + uint32 max_stack_cell_num; + + /* frame csp stack */ + BranchBlock *frame_csp; + BranchBlock *frame_csp_bottom; + BranchBlock *frame_csp_boundary; + uint32 frame_csp_size; + uint32 csp_num; + uint32 max_csp_num; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* frame offset stack */ + int16 *frame_offset; + int16 *frame_offset_bottom; + int16 *frame_offset_boundary; + uint32 frame_offset_size; + int16 dynamic_offset; + int16 start_dynamic_offset; + int16 max_dynamic_offset; + + /* preserved local offset */ + int16 preserved_local_offset; + + /* const buffer */ + uint8 *const_buf; + uint16 num_const; + uint16 const_buf_size; + uint16 const_cell_num; + + /* processed code */ + uint8 *p_code_compiled; + uint8 *p_code_compiled_end; + uint32 code_compiled_size; +#endif +} WASMLoaderContext; + +typedef struct Const { + WASMValue value; + uint16 slot_index; + uint8 value_type; +} Const; + +static void* +memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, + char *error_buf, uint32 error_buf_size) +{ + uint8 *mem_new; + bh_assert(size_new > size_old); + if ((mem_new = loader_malloc + (size_new, error_buf, error_buf_size))) { + bh_memcpy_s(mem_new, size_new, mem_old, size_old); + memset(mem_new + size_old, 0, size_new - size_old); + wasm_runtime_free(mem_old); + } + return mem_new; +} + +#define MEM_REALLOC(mem, size_old, size_new) do { \ + void *mem_new = memory_realloc(mem, size_old, size_new, \ + error_buf, error_buf_size); \ + if (!mem_new) \ + goto fail; \ + mem = mem_new; \ + } while (0) + +#define CHECK_CSP_PUSH() do { \ + if (ctx->frame_csp >= ctx->frame_csp_boundary) { \ + MEM_REALLOC(ctx->frame_csp_bottom, ctx->frame_csp_size, \ + (uint32)(ctx->frame_csp_size \ + + 8 * sizeof(BranchBlock))); \ + ctx->frame_csp_size += (uint32)(8 * sizeof(BranchBlock)); \ + ctx->frame_csp_boundary = ctx->frame_csp_bottom + \ + ctx->frame_csp_size / sizeof(BranchBlock); \ + ctx->frame_csp = ctx->frame_csp_bottom + ctx->csp_num; \ + } \ + } while (0) + +#define CHECK_CSP_POP() do { \ + bh_assert(ctx->csp_num >= 1); \ + } while (0) + +#if WASM_ENABLE_FAST_INTERP != 0 +static bool +check_offset_push(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + uint32 cell_num = (ctx->frame_offset - ctx->frame_offset_bottom); + if (ctx->frame_offset >= ctx->frame_offset_boundary) { + MEM_REALLOC(ctx->frame_offset_bottom, ctx->frame_offset_size, + ctx->frame_offset_size + 16); + ctx->frame_offset_size += 16; + ctx->frame_offset_boundary = ctx->frame_offset_bottom + + ctx->frame_offset_size / sizeof(int16); + ctx->frame_offset = ctx->frame_offset_bottom + cell_num; + } + return true; +fail: + return false; +} + +static bool +check_offset_pop(WASMLoaderContext *ctx, uint32 cells) +{ + if (ctx->frame_offset - cells < ctx->frame_offset_bottom) + return false; + return true; +} + +static void +free_label_patch_list(BranchBlock *frame_csp) +{ + BranchBlockPatch *label_patch = frame_csp->patch_list; + BranchBlockPatch *next; + while (label_patch != NULL) { + next = label_patch->next; + wasm_runtime_free(label_patch); + label_patch = next; + } + frame_csp->patch_list = NULL; +} + +static void +free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num) +{ + BranchBlock *tmp_csp = frame_csp; + + for (uint32 i = 0; i < csp_num; i++) { + free_label_patch_list(tmp_csp); + tmp_csp ++; + } +} + +#endif + +static bool +check_stack_push(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + if (ctx->frame_ref >= ctx->frame_ref_boundary) { + MEM_REALLOC(ctx->frame_ref_bottom, ctx->frame_ref_size, + ctx->frame_ref_size + 16); + ctx->frame_ref_size += 16; + ctx->frame_ref_boundary = ctx->frame_ref_bottom + ctx->frame_ref_size; + ctx->frame_ref = ctx->frame_ref_bottom + ctx->stack_cell_num; + } + return true; +fail: + return false; +} + + +static bool +check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + bh_assert(!(((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + && stack_cell_num < 1) + || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + && stack_cell_num < 2))); + + bh_assert(!((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) + || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) + || (type == VALUE_TYPE_I64 + && (*(frame_ref - 2) != REF_I64_1 + || *(frame_ref - 1) != REF_I64_2)) + || (type == VALUE_TYPE_F64 + && (*(frame_ref - 2) != REF_F64_1 + || *(frame_ref - 1) != REF_F64_2)))); + return true; +} + +static bool +check_stack_pop(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + int32 block_stack_cell_num = (int32) + (ctx->stack_cell_num - (ctx->frame_csp - 1)->stack_cell_num); + + if (block_stack_cell_num > 0 + && *(ctx->frame_ref - 1) == VALUE_TYPE_ANY) { + /* the stack top is a value of any type, return success */ + return true; + } + + if (!check_stack_top_values(ctx->frame_ref, block_stack_cell_num, + type, error_buf, error_buf_size)) + return false; + + return true; +} + +static void +wasm_loader_ctx_destroy(WASMLoaderContext *ctx) +{ + if (ctx) { + if (ctx->frame_ref_bottom) + wasm_runtime_free(ctx->frame_ref_bottom); + if (ctx->frame_csp_bottom) { +#if WASM_ENABLE_FAST_INTERP != 0 + free_all_label_patch_lists(ctx->frame_csp_bottom, ctx->csp_num); +#endif + wasm_runtime_free(ctx->frame_csp_bottom); + } +#if WASM_ENABLE_FAST_INTERP != 0 + if (ctx->frame_offset_bottom) + wasm_runtime_free(ctx->frame_offset_bottom); + if (ctx->const_buf) + wasm_runtime_free(ctx->const_buf); +#endif + wasm_runtime_free(ctx); + } +} + +static WASMLoaderContext* +wasm_loader_ctx_init(WASMFunction *func) +{ + WASMLoaderContext *loader_ctx = + wasm_runtime_malloc(sizeof(WASMLoaderContext)); + if (!loader_ctx) + return false; + memset(loader_ctx, 0, sizeof(WASMLoaderContext)); + + loader_ctx->frame_ref_size = 32; + if (!(loader_ctx->frame_ref_bottom = loader_ctx->frame_ref = + wasm_runtime_malloc(loader_ctx->frame_ref_size))) + goto fail; + memset(loader_ctx->frame_ref_bottom, 0, loader_ctx->frame_ref_size); + loader_ctx->frame_ref_boundary = loader_ctx->frame_ref_bottom + + loader_ctx->frame_ref_size; + + loader_ctx->frame_csp_size = sizeof(BranchBlock) * 8; + if (!(loader_ctx->frame_csp_bottom = loader_ctx->frame_csp = + wasm_runtime_malloc(loader_ctx->frame_csp_size))) + goto fail; + memset(loader_ctx->frame_csp_bottom, 0, loader_ctx->frame_csp_size); + loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8; + +#if WASM_ENABLE_FAST_INTERP != 0 + loader_ctx->frame_offset_size = sizeof(int16) * 32; + if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset = + wasm_runtime_malloc(loader_ctx->frame_offset_size))) + goto fail; + memset(loader_ctx->frame_offset_bottom, 0, + loader_ctx->frame_offset_size); + loader_ctx->frame_offset_boundary = loader_ctx->frame_offset_bottom + 32; + + loader_ctx->num_const = 0; + loader_ctx->const_buf_size = sizeof(Const) * 8; + if (!(loader_ctx->const_buf = wasm_runtime_malloc(loader_ctx->const_buf_size))) + goto fail; + memset(loader_ctx->const_buf, 0, loader_ctx->const_buf_size); + + loader_ctx->start_dynamic_offset = loader_ctx->dynamic_offset = + loader_ctx->max_dynamic_offset = func->param_cell_num + + func->local_cell_num; +#endif + return loader_ctx; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + return NULL; +} + +static bool +wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; + + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || type == VALUE_TYPE_ANY) + return true; + + if (!check_stack_push(ctx, error_buf, error_buf_size)) + return false; + *ctx->frame_ref++ = type; + ctx->stack_cell_num++; + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; + return true; +} + +static bool +wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *cur_block = ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (!check_stack_pop(ctx, type, error_buf, error_buf_size)) + return false; + + ctx->frame_ref--; + ctx->stack_cell_num--; + + if (type == VALUE_TYPE_I32 + || type == VALUE_TYPE_F32 + || *ctx->frame_ref == VALUE_TYPE_ANY) + return true; + + ctx->frame_ref--; + ctx->stack_cell_num--; + return true; +} + +static bool +wasm_loader_push_pop_frame_ref(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + char *error_buf, uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_ref(ctx, type_pop, error_buf, error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_ref(ctx, type_push, error_buf, error_buf_size)) + return false; + return true; +} + +static bool +wasm_loader_push_frame_csp(WASMLoaderContext *ctx, uint8 label_type, + BlockType block_type, uint8* start_addr, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_PUSH(); + memset(ctx->frame_csp, 0, sizeof(BranchBlock)); + ctx->frame_csp->label_type = label_type; + ctx->frame_csp->block_type = block_type; + ctx->frame_csp->start_addr = start_addr; + ctx->frame_csp->stack_cell_num = ctx->stack_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + ctx->frame_csp->dynamic_offset = ctx->dynamic_offset; + ctx->frame_csp->patch_list = NULL; +#endif + ctx->frame_csp++; + ctx->csp_num++; + if (ctx->csp_num > ctx->max_csp_num) + ctx->max_csp_num = ctx->csp_num; + return true; +fail: + return false; +} + +static bool +wasm_loader_pop_frame_csp(WASMLoaderContext *ctx, + char *error_buf, uint32 error_buf_size) +{ + CHECK_CSP_POP(); +#if WASM_ENABLE_FAST_INTERP != 0 + if ((ctx->frame_csp - 1)->param_frame_offsets) + wasm_runtime_free((ctx->frame_csp - 1)->param_frame_offsets); +#endif + ctx->frame_csp--; + ctx->csp_num--; + return true; +} + +#if WASM_ENABLE_FAST_INTERP != 0 + +#if WASM_ENABLE_ABS_LABEL_ADDR != 0 + +#define emit_label(opcode) do { \ + wasm_loader_emit_ptr(loader_ctx, handle_table[opcode]); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) + +#define skip_label() do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(void *)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) + +#else + +#define emit_label(opcode) do { \ + int32 offset = (int32)(handle_table[opcode] - handle_table[0]); \ + bh_assert(offset >= INT16_MIN && offset < INT16_MAX); \ + wasm_loader_emit_int16(loader_ctx, offset); \ + LOG_OP("\nemit_op [%02x]\t", opcode); \ + } while (0) + +// drop local.get / const / block / loop / end +#define skip_label() do { \ + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); \ + LOG_OP("\ndelete last op\n"); \ + } while (0) + +#endif /* WASM_ENABLE_ABS_LABEL_ADDR */ + +#define emit_empty_label_addr_and_frame_ip(type) do { \ + if (!add_label_patch_to_list(loader_ctx->frame_csp - 1, type, \ + loader_ctx->p_code_compiled, \ + error_buf, error_buf_size)) \ + goto fail; \ + /* label address, to be patched */ \ + wasm_loader_emit_ptr(loader_ctx, NULL); \ + } while (0) + +#define emit_br_info(frame_csp) do { \ + if (!wasm_loader_emit_br_info(loader_ctx, frame_csp, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define LAST_OP_OUTPUT_I32() (last_op >= WASM_OP_I32_EQZ \ + && last_op <= WASM_OP_I32_ROTR) \ + || (last_op == WASM_OP_I32_LOAD \ + || last_op == WASM_OP_F32_LOAD) \ + || (last_op >= WASM_OP_I32_LOAD8_S \ + && last_op <= WASM_OP_I32_LOAD16_U) \ + || (last_op >= WASM_OP_F32_ABS \ + && last_op <= WASM_OP_F32_COPYSIGN) \ + || (last_op >= WASM_OP_I32_WRAP_I64 \ + && last_op <= WASM_OP_I32_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F32_CONVERT_S_I32 \ + && last_op <= WASM_OP_F32_DEMOTE_F64) \ + || (last_op == WASM_OP_I32_REINTERPRET_F32) \ + || (last_op == WASM_OP_F32_REINTERPRET_I32) \ + || (last_op == EXT_OP_COPY_STACK_TOP) + +#define LAST_OP_OUTPUT_I64() (last_op >= WASM_OP_I64_CLZ \ + && last_op <= WASM_OP_I64_ROTR) \ + || (last_op >= WASM_OP_F64_ABS \ + && last_op <= WASM_OP_F64_COPYSIGN) \ + || (last_op == WASM_OP_I64_LOAD \ + || last_op == WASM_OP_F64_LOAD) \ + || (last_op >= WASM_OP_I64_LOAD8_S \ + && last_op <= WASM_OP_I64_LOAD32_U) \ + || (last_op >= WASM_OP_I64_EXTEND_S_I32 \ + && last_op <= WASM_OP_I64_TRUNC_U_F64) \ + || (last_op >= WASM_OP_F64_CONVERT_S_I32 \ + && last_op <= WASM_OP_F64_PROMOTE_F32) \ + || (last_op == WASM_OP_I64_REINTERPRET_F64) \ + || (last_op == WASM_OP_F64_REINTERPRET_I64) \ + || (last_op == EXT_OP_COPY_STACK_TOP_I64) + +#define GET_CONST_OFFSET(type, val) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &val, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F32_OFFSET(type, fval) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &fval, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define GET_CONST_F64_OFFSET(type, fval) do { \ + if (!(wasm_loader_get_const_offset(loader_ctx, type, \ + &fval, &operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define emit_operand(ctx, offset) do { \ + wasm_loader_emit_int16(ctx, offset); \ + LOG_OP("%d\t", offset); \ + } while (0) + +#define emit_byte(ctx, byte) do { \ + wasm_loader_emit_uint8(ctx, byte); \ + LOG_OP("%d\t", byte); \ + } while (0) + +#define emit_uint32(ctx, value) do { \ + wasm_loader_emit_uint32(ctx, value); \ + LOG_OP("%d\t", value); \ + } while (0) + +#define emit_leb() do { \ + wasm_loader_emit_leb(loader_ctx, p_org, p); \ + } while (0) + +static bool +wasm_loader_ctx_reinit(WASMLoaderContext *ctx) +{ + if (!(ctx->p_code_compiled = wasm_runtime_malloc(ctx->code_compiled_size))) + return false; + memset(ctx->p_code_compiled, 0, ctx->code_compiled_size); + ctx->p_code_compiled_end = ctx->p_code_compiled + + ctx->code_compiled_size; + + /* clean up frame ref */ + memset(ctx->frame_ref_bottom, 0, ctx->frame_ref_size); + ctx->frame_ref = ctx->frame_ref_bottom; + ctx->stack_cell_num = 0; + + /* clean up frame csp */ + memset(ctx->frame_csp_bottom, 0, ctx->frame_csp_size); + ctx->frame_csp = ctx->frame_csp_bottom; + ctx->csp_num = 0; + ctx->max_csp_num = 0; + + /* clean up frame offset */ + memset(ctx->frame_offset_bottom, 0, ctx->frame_offset_size); + ctx->frame_offset = ctx->frame_offset_bottom; + ctx->dynamic_offset = ctx->start_dynamic_offset; + + /* init preserved local offsets */ + ctx->preserved_local_offset = ctx->max_dynamic_offset; + + /* const buf is reserved */ + return true; +} + +static void +wasm_loader_emit_uint32(WASMLoaderContext *ctx, uint32 value) +{ + if (ctx->p_code_compiled) { + *(uint32*)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint32); + } + else + ctx->code_compiled_size += sizeof(uint32); +} + +static void +wasm_loader_emit_int16(WASMLoaderContext *ctx, int16 value) +{ + if (ctx->p_code_compiled) { + *(int16*)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(int16); + } + else + ctx->code_compiled_size += sizeof(int16); +} + +static void +wasm_loader_emit_uint8(WASMLoaderContext *ctx, uint8 value) +{ + if (ctx->p_code_compiled) { + *(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(uint8); + } + else + ctx->code_compiled_size += sizeof(uint8); +} + +static void +wasm_loader_emit_ptr(WASMLoaderContext *ctx, void *value) +{ + if (ctx->p_code_compiled) { + *(uint8**)(ctx->p_code_compiled) = value; + ctx->p_code_compiled += sizeof(void *); + } + else + ctx->code_compiled_size += sizeof(void *); +} + +static void +wasm_loader_emit_backspace(WASMLoaderContext *ctx, uint32 size) +{ + if (ctx->p_code_compiled) { + ctx->p_code_compiled -= size; + } + else + ctx->code_compiled_size -= size; +} + +static void +wasm_loader_emit_leb(WASMLoaderContext *ctx, uint8* start, uint8* end) +{ + if (ctx->p_code_compiled) { + bh_memcpy_s(ctx->p_code_compiled, + ctx->p_code_compiled_end - ctx->p_code_compiled, + start, end - start); + ctx->p_code_compiled += (end - start); + } + else { + ctx->code_compiled_size += (end - start); + } + +} + +static bool +preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, + uint32 local_index, uint32 local_type, bool *preserved, + char *error_buf, uint32 error_buf_size) +{ + int16 preserved_offset = (int16)local_index; + *preserved = false; + for (uint32 i = 0; i < loader_ctx->stack_cell_num; i++) { + /* move previous local into dynamic space before a set/tee_local opcode */ + if (loader_ctx->frame_offset_bottom[i] == (int16)local_index) { + if (preserved_offset == (int16)local_index) { + *preserved = true; + skip_label(); + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + preserved_offset = loader_ctx->preserved_local_offset; + /* Only increase preserve offset in the second traversal */ + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset++; + emit_label(EXT_OP_COPY_STACK_TOP); + } + else { + preserved_offset = loader_ctx->preserved_local_offset; + if (loader_ctx->p_code_compiled) + loader_ctx->preserved_local_offset += 2; + emit_label(EXT_OP_COPY_STACK_TOP_I64); + } + emit_operand(loader_ctx, local_index); + emit_operand(loader_ctx, preserved_offset); + emit_label(opcode); + } + loader_ctx->frame_offset_bottom[i] = preserved_offset; + } + } + + return true; + +#if WASM_ENABLE_ABS_LABEL_ADDR == 0 +fail: + return false; +#endif +} + +static bool +add_label_patch_to_list(BranchBlock *frame_csp, + uint8 patch_type, uint8 *p_code_compiled, + char *error_buf, uint32 error_buf_size) +{ + BranchBlockPatch *patch = loader_malloc + (sizeof(BranchBlockPatch), error_buf, error_buf_size); + if (!patch) { + return false; + } + patch->patch_type = patch_type; + patch->code_compiled = p_code_compiled; + if (!frame_csp->patch_list) { + frame_csp->patch_list = patch; + patch->next = NULL; + } + else { + patch->next = frame_csp->patch_list; + frame_csp->patch_list = patch; + } + return true; +} + +static void +apply_label_patch(WASMLoaderContext *ctx, uint8 depth, + uint8 patch_type) +{ + BranchBlock *frame_csp = ctx->frame_csp - depth; + BranchBlockPatch *node = frame_csp->patch_list; + BranchBlockPatch *node_prev = NULL, *node_next; + + if (!ctx->p_code_compiled) + return; + + while (node) { + node_next = node->next; + if (node->patch_type == patch_type) { + *((uint8**)node->code_compiled) = ctx->p_code_compiled; + if (node_prev == NULL) { + frame_csp->patch_list = node_next; + } + else { + node_prev->next = node_next; + } + wasm_runtime_free(node); + } + else { + node_prev = node; + } + node = node_next; + } +} + +static bool +wasm_loader_emit_br_info(WASMLoaderContext *ctx, BranchBlock *frame_csp, + char *error_buf, uint32 error_buf_size) +{ + /* br info layout: + * a) arity of target block + * b) total cell num of arity values + * c) each arity value's cell num + * d) each arity value's src frame offset + * e) each arity values's dst dynamic offset + * f) branch target address + * + * Note: b-e are omitted when arity is 0 so that + * interpreter can recover the br info quickly. + */ + BlockType *block_type = &frame_csp->block_type; + uint8 *types = NULL, cell; + uint32 arity = 0; + int32 i; + int16 *frame_offset = ctx->frame_offset; + uint16 dynamic_offset; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(block_type, &types); + else + arity = block_type_get_result_types(block_type, &types); + + /* Part a */ + emit_uint32(ctx, arity); + + if (arity) { + /* Part b */ + emit_uint32(ctx, wasm_get_cell_num(types, arity)); + + /* Part c */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + emit_byte(ctx, cell); + } + /* Part d */ + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + frame_offset -= cell; + emit_operand(ctx, *(int16*)(frame_offset)); + } + /* Part e */ + dynamic_offset = frame_csp->dynamic_offset + + wasm_get_cell_num(types, arity); + for (i = (int32)arity - 1; i >= 0; i--) { + cell = wasm_value_type_cell_num(types[i]); + dynamic_offset -= cell; + emit_operand(ctx, dynamic_offset); + } + } + + /* Part f */ + if (frame_csp->label_type == LABEL_TYPE_LOOP) { + wasm_loader_emit_ptr(ctx, frame_csp->code_compiled); + } + else { + if (!add_label_patch_to_list(frame_csp, PATCH_END, + ctx->p_code_compiled, + error_buf, error_buf_size)) + return false; + /* label address, to be patched */ + wasm_loader_emit_ptr(ctx, NULL); + } + + return true; +} + +static bool +wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (type == VALUE_TYPE_VOID) + return true; + + // only check memory overflow in first traverse + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + if (disable_emit) + *(ctx->frame_offset)++ = operand_offset; + else { + emit_operand(ctx, ctx->dynamic_offset); + *(ctx->frame_offset)++ = ctx->dynamic_offset; + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) + ctx->max_dynamic_offset = ctx->dynamic_offset; + } + + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + return true; + + if (ctx->p_code_compiled == NULL) { + if (!check_offset_push(ctx, error_buf, error_buf_size)) + return false; + } + + ctx->frame_offset++; + if (!disable_emit) { + ctx->dynamic_offset++; + if (ctx->dynamic_offset > ctx->max_dynamic_offset) + ctx->max_dynamic_offset = ctx->dynamic_offset; + } + return true; +} + +/* This function should be in front of wasm_loader_pop_frame_ref + as they both use ctx->stack_cell_num, and ctx->stack_cell_num + will be modified by wasm_loader_pop_frame_ref */ +static bool +wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* if ctx->frame_csp equals ctx->frame_csp_bottom, + then current block is the function block */ + uint32 depth = ctx->frame_csp > ctx->frame_csp_bottom ? 1 : 0; + BranchBlock *cur_block = ctx->frame_csp - depth; + int32 available_stack_cell = (int32) + (ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Directly return success if current block is in stack + * polymorphic state while stack is empty. */ + if (available_stack_cell <= 0 && cur_block->is_stack_polymorphic) + return true; + + if (type == VALUE_TYPE_VOID) + return true; + + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + /* Check the offset stack bottom to ensure the frame offset + stack will not go underflow. But we don't thrown error + and return true here, because the error msg should be + given in wasm_loader_pop_frame_ref */ + if (!check_offset_pop(ctx, 1)) + return true; + + ctx->frame_offset -= 1; + if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= 1; + } + else { + if (!check_offset_pop(ctx, 2)) + return true; + + ctx->frame_offset -= 2; + if ((*(ctx->frame_offset) > ctx->start_dynamic_offset) + && (*(ctx->frame_offset) < ctx->max_dynamic_offset)) + ctx->dynamic_offset -= 2; + } + emit_operand(ctx, *(ctx->frame_offset)); + return true; +} + +static bool +wasm_loader_push_pop_frame_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + for (int i = 0; i < pop_cnt; i++) { + if (!wasm_loader_pop_frame_offset(ctx, type_pop, error_buf, error_buf_size)) + return false; + } + if (!wasm_loader_push_frame_offset(ctx, type_push, + disable_emit, operand_offset, + error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!(wasm_loader_push_frame_offset(ctx, type, disable_emit, operand_offset, + error_buf, error_buf_size))) + return false; + if (!(wasm_loader_push_frame_ref(ctx, type, error_buf, error_buf_size))) + return false; + + return true; +} + +static bool +wasm_loader_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 type, + char *error_buf, uint32 error_buf_size) +{ + /* put wasm_loader_pop_frame_offset in front of wasm_loader_pop_frame_ref */ + if (!wasm_loader_pop_frame_offset(ctx, type, error_buf, error_buf_size)) + return false; + if (!wasm_loader_pop_frame_ref(ctx, type, error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_push_pop_frame_ref_offset(WASMLoaderContext *ctx, uint8 pop_cnt, + uint8 type_push, uint8 type_pop, + bool disable_emit, int16 operand_offset, + char *error_buf, uint32 error_buf_size) +{ + if (!wasm_loader_push_pop_frame_offset(ctx, pop_cnt, type_push, type_pop, + disable_emit, operand_offset, + error_buf, error_buf_size)) + return false; + if (!wasm_loader_push_pop_frame_ref(ctx, pop_cnt, type_push, type_pop, + error_buf, error_buf_size)) + return false; + + return true; +} + +static bool +wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, + void *value, int16 *offset, + char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + Const *c; + for (c = (Const *)ctx->const_buf; + (uint8*)c < ctx->const_buf + ctx->num_const * sizeof(Const); c ++) { + if ((type == c->value_type) + && ((type == VALUE_TYPE_I64 && *(int64*)value == c->value.i64) + || (type == VALUE_TYPE_I32 && *(int32*)value == c->value.i32) + || (type == VALUE_TYPE_F64 + && (0 == memcmp(value, &(c->value.f64), sizeof(float64)))) + || (type == VALUE_TYPE_F32 + && (0 == memcmp(value, &(c->value.f32), sizeof(float32)))))) { + operand_offset = c->slot_index; + break; + } + if (c->value_type == VALUE_TYPE_I64 + || c->value_type == VALUE_TYPE_F64) + operand_offset += 2; + else + operand_offset += 1; + } + if ((uint8 *)c == ctx->const_buf + ctx->num_const * sizeof(Const)) { + if ((uint8 *)c == ctx->const_buf + ctx->const_buf_size) { + MEM_REALLOC(ctx->const_buf, + ctx->const_buf_size, + ctx->const_buf_size + 4 * sizeof(Const)); + ctx->const_buf_size += 4 * sizeof(Const); + c = (Const *)(ctx->const_buf + ctx->num_const * sizeof(Const)); + } + c->value_type = type; + switch (type) { + case VALUE_TYPE_F64: + bh_memcpy_s(&(c->value.f64), sizeof(WASMValue), value, sizeof(float64)); + ctx->const_cell_num += 2; + /* The const buf will be reversed, we use the second cell */ + /* of the i64/f64 const so the finnal offset is corrent */ + operand_offset ++; + break; + case VALUE_TYPE_I64: + c->value.i64 = *(int64*)value; + ctx->const_cell_num += 2; + operand_offset ++; + break; + case VALUE_TYPE_F32: + bh_memcpy_s(&(c->value.f32), sizeof(WASMValue), value, sizeof(float32)); + ctx->const_cell_num ++; + break; + case VALUE_TYPE_I32: + c->value.i32 = *(int32*)value; + ctx->const_cell_num ++; + break; + default: + break; + } + c->slot_index = operand_offset; + ctx->num_const ++; + LOG_OP("#### new const [%d]: %ld\n", + ctx->num_const, (int64)c->value.i64); + } + /* use negetive index for const */ + operand_offset = -(operand_offset + 1); + *offset = operand_offset; + return true; +fail: + return false; +} + +/* + PUSH(POP)_XXX = push(pop) frame_ref + push(pop) frame_offset + -- Mostly used for the binary / compare operation + PUSH(POP)_OFFSET_TYPE only push(pop) the frame_offset stack + -- Mostly used in block / control instructions + + The POP will always emit the offset on the top of the frame_offset stack + PUSH can be used in two ways: + 1. directly PUSH: + PUSH_XXX(); + will allocate a dynamic space and emit + 2. silent PUSH: + operand_offset = xxx; disable_emit = true; + PUSH_XXX(); + only push the frame_offset stack, no emit +*/ +#define PUSH_I32() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F32() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_I64() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_F64() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I32() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F32() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_I64() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_F64() do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_OFFSET_TYPE(type) do { \ + if (!(wasm_loader_push_frame_offset(loader_ctx, type, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_OFFSET_TYPE(type) do { \ + if (!(wasm_loader_pop_frame_offset(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref_offset(loader_ctx, 1, \ + type_push, type_pop, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref_offset(loader_ctx, 2, \ + type_push, type_pop, \ + disable_emit, operand_offset, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#else /* WASM_ENABLE_FAST_INTERP */ + +#define PUSH_I32() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F32() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_I64() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_F64() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I32() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F32() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F32, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_I64() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_F64() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F64, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, \ + type_push, type_pop, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +/* type of POPs should be the same */ +#define POP2_AND_PUSH(type_pop, type_push) do { \ + if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 2, \ + type_push, type_pop, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) +#endif /* WASM_ENABLE_FAST_INTERP */ + +#if WASM_ENABLE_FAST_INTERP != 0 + +static bool +reserve_block_ret(WASMLoaderContext *loader_ctx, + uint8 opcode, bool disable_emit, + char *error_buf, uint32 error_buf_size) +{ + int16 operand_offset = 0; + BranchBlock *block = (opcode == WASM_OP_ELSE) ? + loader_ctx->frame_csp - 1 : loader_ctx->frame_csp; + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0, value_count = 0, total_cel_num = 0; + int32 i = 0; + int16 dynamic_offset, dynamic_offset_org, + *frame_offset = NULL, *frame_offset_org = NULL; + + return_count = block_type_get_result_types(block_type, &return_types); + + /* If there is only one return value, use EXT_OP_COPY_STACK_TOP/_I64 instead + * of EXT_OP_COPY_STACK_VALUES for interpreter performance. */ + if (return_count == 1) { + uint8 cell = wasm_value_type_cell_num(return_types[0]); + if (block->dynamic_offset != *(loader_ctx->frame_offset - cell)) { + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(cell == 1 ? EXT_OP_COPY_STACK_TOP : EXT_OP_COPY_STACK_TOP_I64); + emit_operand(loader_ctx, *(loader_ctx->frame_offset - cell)); + emit_operand(loader_ctx, block->dynamic_offset); + + if (opcode == WASM_OP_ELSE) { + *(loader_ctx->frame_offset - cell) = block->dynamic_offset; + } + else { + loader_ctx->frame_offset -= cell; + loader_ctx->dynamic_offset = block->dynamic_offset; + PUSH_OFFSET_TYPE(return_types[0]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + } + return true; + } + + /* Copy stack top values to block's results which are in dynamic space. + * The instruction format: + * Part a: values count + * Part b: all values total cell num + * Part c: each value's cell_num, src offset and dst offset + * Part d: each value's src offset and dst offset + * Part e: each value's dst offset + */ + frame_offset = frame_offset_org = loader_ctx->frame_offset; + dynamic_offset = dynamic_offset_org = + block->dynamic_offset + + wasm_get_cell_num(return_types, return_count); + + /* First traversal to get the count of values needed to be copied. */ + for (i = (int32)return_count - 1; i >= 0; i--) { + uint8 cells = wasm_value_type_cell_num(return_types[i]); + + frame_offset -= cells; + dynamic_offset -= cells; + if (dynamic_offset != *frame_offset) { + value_count++; + total_cel_num += cells; + } + } + + if (value_count) { + uint32 j = 0; + uint8 *emit_data = NULL, *cells = NULL; + int16 *src_offsets = NULL; + uint16 *dst_offsets = NULL; + uint64 size = (uint64)value_count * (sizeof(*cells) + + sizeof(*src_offsets) + + sizeof(*dst_offsets)); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + value_count); + dst_offsets = (uint16 *)(src_offsets + value_count); + + /* insert op_copy before else opcode */ + if (opcode == WASM_OP_ELSE) + skip_label(); + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, value_count); + /* Part b) */ + emit_uint32(loader_ctx, total_cel_num); + + /* Second traversal to get each value's cell num, src offset and dst offset. */ + frame_offset = frame_offset_org; + dynamic_offset = dynamic_offset_org; + for (i = (int32)return_count - 1, j = 0; i >= 0; i--) { + uint8 cell = wasm_value_type_cell_num(return_types[i]); + frame_offset -= cell; + dynamic_offset -= cell; + if (dynamic_offset != *frame_offset) { + /* cell num */ + cells[j] = cell; + /* src offset */ + src_offsets[j] = *frame_offset; + /* dst offset */ + dst_offsets[j] = dynamic_offset; + j++; + } + if (opcode == WASM_OP_ELSE) { + *frame_offset = dynamic_offset; + } + else { + loader_ctx->frame_offset = frame_offset; + loader_ctx->dynamic_offset = dynamic_offset; + PUSH_OFFSET_TYPE(return_types[i]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + loader_ctx->frame_offset = frame_offset_org; + loader_ctx->dynamic_offset = dynamic_offset_org; + } + } + + bh_assert(j == value_count); + + /* Emit the cells, src_offsets and dst_offsets */ + for (j = 0; j < value_count; j++) + emit_byte(loader_ctx, cells[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, src_offsets[j]); + for (j = 0; j < value_count; j++) + emit_operand(loader_ctx, dst_offsets[j]); + + if (opcode == WASM_OP_ELSE) + emit_label(opcode); + + wasm_runtime_free(emit_data); + } + + return true; + +fail: + return false; +} + +#endif /* WASM_ENABLE_FAST_INTERP */ + +#define RESERVE_BLOCK_RET() do { \ + if (!reserve_block_ret(loader_ctx, opcode, disable_emit, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define PUSH_TYPE(type) do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define POP_TYPE(type) do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, type, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + +#define PUSH_CSP(label_type, block_type, _start_addr) do { \ + if (!wasm_loader_push_frame_csp(loader_ctx, label_type, block_type, \ + _start_addr, error_buf, \ + error_buf_size)) \ + goto fail; \ + } while (0) + +#define POP_CSP() do { \ + if (!wasm_loader_pop_frame_csp(loader_ctx, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define GET_LOCAL_INDEX_TYPE_AND_OFFSET() do { \ + read_leb_uint32(p, p_end, local_idx); \ + bh_assert(local_idx < param_count + local_count);\ + local_type = local_idx < param_count \ + ? param_types[local_idx] \ + : local_types[local_idx - param_count]; \ + local_offset = local_offsets[local_idx]; \ + } while (0) + +#define CHECK_BR(depth) do { \ + if (!wasm_loader_check_br(loader_ctx, depth, \ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + +#define CHECK_MEMORY() do { \ + bh_assert(module->import_memory_count \ + + module->memory_count > 0); \ + } while (0) + + +static bool +is_value_type(uint8 type) +{ + return type == VALUE_TYPE_I32 || + type == VALUE_TYPE_I64 || + type == VALUE_TYPE_F32 || + type == VALUE_TYPE_F64 || + type == VALUE_TYPE_VOID; +} + +static bool +wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, + char *error_buf, uint32 error_buf_size) +{ + BranchBlock *target_block, *cur_block; + BlockType *target_block_type; + uint8 *types = NULL, *frame_ref; + uint32 arity = 0; + int32 i, available_stack_cell; + uint16 cell_num; + + if (loader_ctx->csp_num < depth + 1) { + set_error_buf(error_buf, error_buf_size, + "unknown label, " + "unexpected end of section or function"); + return false; + } + + cur_block = loader_ctx->frame_csp - 1; + target_block = loader_ctx->frame_csp - (depth + 1); + target_block_type = &target_block->block_type; + frame_ref = loader_ctx->frame_ref; + + /* Note: loop's arity is different from if and block. loop's arity is + * its parameter count while if and block arity is result count. + */ + if (target_block->label_type == LABEL_TYPE_LOOP) + arity = block_type_get_param_types(target_block_type, &types); + else + arity = block_type_get_result_types(target_block_type, &types); + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (cur_block->is_stack_polymorphic) { + for (i = (int32)arity -1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(types[i]); +#endif + POP_TYPE(types[i]); + } + for (i = 0; i < (int32)arity; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(types[i]); +#endif + PUSH_TYPE(types[i]); + } + return true; + } + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + /* Check stack top values match target block type */ + for (i = (int32)arity -1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + types[i], + error_buf, error_buf_size)) + return false; + cell_num = wasm_value_type_cell_num(types[i]); + frame_ref -= cell_num; + available_stack_cell -= cell_num; + } + + return true; + +fail: + return false; +} + +static BranchBlock * +check_branch_block(WASMLoaderContext *loader_ctx, + uint8 **p_buf, uint8 *buf_end, + char *error_buf, uint32 error_buf_size) +{ + uint8 *p = *p_buf, *p_end = buf_end; + BranchBlock *frame_csp_tmp; + uint32 depth; + + read_leb_uint32(p, p_end, depth); + CHECK_BR(depth); + frame_csp_tmp = loader_ctx->frame_csp - depth - 1; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_br_info(frame_csp_tmp); +#endif + + *p_buf = p; + return frame_csp_tmp; +fail: + return NULL; +} + +static bool +check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block, + char *error_buf, uint32 error_buf_size) +{ + BlockType *block_type = &block->block_type; + uint8 *return_types = NULL; + uint32 return_count = 0; + int32 available_stack_cell, return_cell_num, i; + uint8 *frame_ref = NULL; + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num + - block->stack_cell_num); + + return_count = block_type_get_result_types(block_type, &return_types); + return_cell_num = return_count > 0 ? + wasm_get_cell_num(return_types, return_count) : 0; + + /* If the stack is in polymorphic state, just clear the stack + * and then re-push the values to make the stack top values + * match block type. */ + if (block->is_stack_polymorphic) { + for (i = (int32)return_count -1; i >= 0; i--) { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(return_types[i]); +#endif + POP_TYPE(return_types[i]); + } + + /* Check stack is empty */ + bh_assert(loader_ctx->stack_cell_num == block->stack_cell_num); + + for (i = 0; i < (int32)return_count; i++) { +#if WASM_ENABLE_FAST_INTERP != 0 + bool disable_emit = true; + int16 operand_offset = 0; + PUSH_OFFSET_TYPE(return_types[i]); +#endif + PUSH_TYPE(return_types[i]); + } + return true; + } + + /* Check stack cell num equals return cell num */ + bh_assert(available_stack_cell == return_cell_num); + + /* Check stack values match return types */ + frame_ref = loader_ctx->frame_ref; + for (i = (int32)return_count -1; i >= 0; i--) { + if (!check_stack_top_values(frame_ref, available_stack_cell, + return_types[i], + error_buf, error_buf_size)) + return false; + frame_ref -= wasm_value_type_cell_num(return_types[i]); + available_stack_cell -= wasm_value_type_cell_num(return_types[i]); + } + + (void)return_cell_num; + return true; + +fail: + return false; +} + +#if WASM_ENABLE_FAST_INTERP != 0 +/* Copy parameters to dynamic space. + * 1) POP original parameter out; + * 2) Push and copy original values to dynamic space. + * The copy instruction format: + * Part a: param count + * Part b: all param total cell num + * Part c: each param's cell_num, src offset and dst offset + * Part d: each param's src offset + * Part e: each param's dst offset + */ +static bool +copy_params_to_dynamic_space(WASMLoaderContext *loader_ctx, bool is_if_block, + char* error_buf, uint32 error_buf_size) +{ + int16 *frame_offset = NULL; + uint8 *cells = NULL, cell; + int16 *src_offsets = NULL; + uint8 *emit_data = NULL; + uint32 i; + BranchBlock *block = loader_ctx->frame_csp - 1; + BlockType *block_type = &block->block_type; + WASMType *wasm_type = block_type->u.type; + uint32 param_count = block_type->u.type->param_count; + int16 condition_offset = 0; + bool disable_emit = false; + int16 operand_offset = 0; + + uint64 size = (uint64)param_count * (sizeof(*cells) + + sizeof(*src_offsets)); + + /* For if block, we also need copy the condition operand offset. */ + if (is_if_block) + size += sizeof(*cells) + sizeof(*src_offsets); + + /* Allocate memory for the emit data */ + if (!(emit_data = loader_malloc(size, error_buf, error_buf_size))) + return false; + + cells = emit_data; + src_offsets = (int16 *)(cells + param_count); + + if (is_if_block) + condition_offset = *loader_ctx->frame_offset; + + /* POP original parameter out */ + for (i = 0; i < param_count; i++) { + POP_OFFSET_TYPE(wasm_type->types[param_count - i - 1]); + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + } + frame_offset = loader_ctx->frame_offset; + + /* Get each param's cell num and src offset */ + for (i = 0; i < param_count; i++) { + cell = wasm_value_type_cell_num(wasm_type->types[i]); + cells[i] = cell; + src_offsets[i] = *frame_offset; + frame_offset += cell; + } + + /* emit copy instruction */ + emit_label(EXT_OP_COPY_STACK_VALUES); + /* Part a) */ + emit_uint32(loader_ctx, is_if_block ? param_count + 1 : param_count); + /* Part b) */ + emit_uint32(loader_ctx, is_if_block ? + wasm_type->param_cell_num + 1 : + wasm_type->param_cell_num); + /* Part c) */ + for (i = 0; i < param_count; i++) + emit_byte(loader_ctx, cells[i]); + if (is_if_block) + emit_byte(loader_ctx, 1); + + /* Part d) */ + for (i = 0; i < param_count; i++) + emit_operand(loader_ctx, src_offsets[i]); + if (is_if_block) + emit_operand(loader_ctx, condition_offset); + + /* Part e) */ + /* Push to dynamic space. The push will emit the dst offset. */ + for (i = 0; i < param_count; i++) + PUSH_OFFSET_TYPE(wasm_type->types[i]); + if (is_if_block) + PUSH_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Free the emit data */ + wasm_runtime_free(emit_data); + + return true; + +fail: + return false; +} +#endif + +/* reset the stack to the state of before entering the last block */ +#if WASM_ENABLE_FAST_INTERP != 0 +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ + loader_ctx->frame_offset = \ + loader_ctx->frame_offset_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#else +#define RESET_STACK() do { \ + loader_ctx->stack_cell_num = \ + (loader_ctx->frame_csp - 1)->stack_cell_num; \ + loader_ctx->frame_ref = \ + loader_ctx->frame_ref_bottom + loader_ctx->stack_cell_num; \ +} while (0) +#endif + +/* set current block's stack polymorphic state */ +#define SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(flag) do { \ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; \ + cur_block->is_stack_polymorphic = flag; \ +} while (0) + +#define BLOCK_HAS_PARAM(block_type) \ + (!block_type.is_value_type && block_type.u.type->param_count > 0) + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, + BlockAddr *block_addr_cache, + char *error_buf, uint32 error_buf_size) +{ + uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; + uint32 param_count, local_count, global_count; + uint8 *param_types, *local_types, local_type, global_type; + BlockType func_type; + uint16 *local_offsets, local_offset; + uint32 count, i, local_idx, global_idx, u32, align, mem_offset; + int32 i32, i32_const = 0; + int64 i64; + uint8 opcode, u8; + bool return_value = false; + WASMLoaderContext *loader_ctx; + BranchBlock *frame_csp_tmp; +#if WASM_ENABLE_BULK_MEMORY != 0 + uint32 segment_index; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + uint8 *func_const_end, *func_const; + int16 operand_offset; + uint8 last_op = 0; + bool disable_emit, preserve_local = false; + float32 f32; + float64 f64; + + LOG_OP("\nProcessing func | [%d] params | [%d] locals | [%d] return\n", + func->param_cell_num, + func->local_cell_num, + func->ret_cell_num); +#endif + + global_count = module->import_global_count + module->global_count; + + param_count = func->func_type->param_count; + param_types = func->func_type->types; + + func_type.is_value_type = false; + func_type.u.type = func->func_type; + + local_count = func->local_count; + local_types = func->local_types; + local_offsets = func->local_offsets; + + if (!(loader_ctx = wasm_loader_ctx_init(func))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 +re_scan: + if (loader_ctx->code_compiled_size > 0) { + if (!wasm_loader_ctx_reinit(loader_ctx)) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail; + } + p = func->code; + func->code_compiled = loader_ctx->p_code_compiled; + } +#endif + + PUSH_CSP(LABEL_TYPE_FUNCTION, func_type, p); + + while (p < p_end) { + opcode = *p++; +#if WASM_ENABLE_FAST_INTERP != 0 + p_org = p; + disable_emit = false; + emit_label(opcode); +#endif + + switch (opcode) { + case WASM_OP_UNREACHABLE: + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + + case WASM_OP_NOP: +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + break; + + case WASM_OP_IF: + POP_I32(); + goto handle_op_block_and_loop; + case WASM_OP_BLOCK: + case WASM_OP_LOOP: +handle_op_block_and_loop: + { + uint8 value_type; + BlockType block_type; + + value_type = read_uint8(p); + if (is_value_type(value_type)) { + /* If the first byte is one of these special values: + * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of + * the single return value. */ + block_type.is_value_type = true; + block_type.u.value_type = value_type; + } + else { + uint32 type_index; + /* Resolve the leb128 encoded type index as block type */ + p--; + read_leb_uint32(p, p_end, type_index); + bh_assert(type_index < module->type_count); + block_type.is_value_type = false; + block_type.u.type = module->types[type_index]; +#if WASM_ENABLE_FAST_INTERP == 0 \ + && WASM_ENABLE_WAMR_COMPILER == 0 \ + && WASM_ENABLE_JIT == 0 + /* If block use type index as block type, change the opcode + * to new extended opcode so that interpreter can resolve the + * block quickly. + */ + *(p - 2) = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK); +#endif + } + + /* Pop block parameters from stack */ + if (BLOCK_HAS_PARAM(block_type)) { + WASMType *wasm_type = block_type.u.type; + for (i = 0; i < block_type.u.type->param_count; i++) + POP_TYPE(wasm_type->types[wasm_type->param_count - i - 1]); + } + + PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK), block_type, p); + + /* Pass parameters to block */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (opcode == WASM_OP_BLOCK) { + skip_label(); + } else if (opcode == WASM_OP_LOOP) { + skip_label(); + if (BLOCK_HAS_PARAM(block_type)) { + /* Make sure params are in dynamic space */ + if (!copy_params_to_dynamic_space(loader_ctx, + false, + error_buf, + error_buf_size)) + goto fail; + } + (loader_ctx->frame_csp - 1)->code_compiled = + loader_ctx->p_code_compiled; + } else if (opcode == WASM_OP_IF) { + /* If block has parameters, we should make sure they are in + * dynamic space. Otherwise, when else branch is missing, + * the later opcode may consume incorrect operand offset. + * Spec case: + * (func (export "params-id") (param i32) (result i32) + * (i32.const 1) + * (i32.const 2) + * (if (param i32 i32) (result i32 i32) (local.get 0) (then)) + * (i32.add) + * ) + * + * So we should emit a copy instruction before the if. + * + * And we also need to save the parameter offsets and + * recover them before entering else branch. + * + */ + if (BLOCK_HAS_PARAM(block_type)) { + BranchBlock *block = loader_ctx->frame_csp - 1; + uint64 size; + + /* skip the if condition operand offset */ + wasm_loader_emit_backspace(loader_ctx, sizeof(int16)); + /* skip the if label */ + skip_label(); + /* Emit a copy instruction */ + if (!copy_params_to_dynamic_space(loader_ctx, + true, + error_buf, + error_buf_size)) + goto fail; + + /* Emit the if instruction */ + emit_label(opcode); + /* Emit the new condition operand offset */ + POP_OFFSET_TYPE(VALUE_TYPE_I32); + + /* Save top param_count values of frame_offset stack, so that + * we can recover it before executing else branch */ + size = sizeof(int16) * + (uint64)block_type.u.type->param_cell_num; + if (!(block->param_frame_offsets = + loader_malloc(size, error_buf, error_buf_size))) + goto fail; + bh_memcpy_s(block->param_frame_offsets, + size, + loader_ctx->frame_offset - size/sizeof(int16), + size); + } + + emit_empty_label_addr_and_frame_ip(PATCH_ELSE); + emit_empty_label_addr_and_frame_ip(PATCH_END); + } +#endif + break; + } + + case WASM_OP_ELSE: + { + BlockType block_type = (loader_ctx->frame_csp - 1)->block_type; + bh_assert(loader_ctx->csp_num >= 2 + && (loader_ctx->frame_csp - 1)->label_type + == LABEL_TYPE_IF); + + /* check whether if branch's stack matches its result type */ + if (!check_block_stack(loader_ctx, loader_ctx->frame_csp - 1, + error_buf, error_buf_size)) + goto fail; + + (loader_ctx->frame_csp - 1)->else_addr = p - 1; + +#if WASM_ENABLE_FAST_INTERP != 0 + /* if the result of if branch is in local or const area, add a copy op */ + RESERVE_BLOCK_RET(); + + emit_empty_label_addr_and_frame_ip(PATCH_END); + apply_label_patch(loader_ctx, 1, PATCH_ELSE); +#endif + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + + /* Pass parameters to if-false branch */ + if (BLOCK_HAS_PARAM(block_type)) { + for (i = 0; i < block_type.u.type->param_count; i++) + PUSH_TYPE(block_type.u.type->types[i]); + } + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Recover top param_count values of frame_offset stack */ + if (BLOCK_HAS_PARAM((block_type))) { + uint32 size; + BranchBlock *block = loader_ctx->frame_csp - 1; + size = sizeof(int16) * + block_type.u.type->param_cell_num; + bh_memcpy_s(loader_ctx->frame_offset, size, + block->param_frame_offsets, size); + loader_ctx->frame_offset += (size/sizeof(int16)); + } +#endif + + break; + } + + case WASM_OP_END: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + + /* check whether block stack matches its result type */ + if (!check_block_stack(loader_ctx, cur_block, + error_buf, error_buf_size)) + goto fail; + + /* if no else branch, and return types do not match param types, fail */ + if (cur_block->label_type == LABEL_TYPE_IF + && !cur_block->else_addr) { + uint32 param_count = 0, ret_count = 0; + uint8 *param_types = NULL, *ret_types = NULL; + BlockType *block_type = &cur_block->block_type; + if (block_type->is_value_type) { + if (block_type->u.value_type != VALUE_TYPE_VOID) { + ret_count = 1; + ret_types = &block_type->u.value_type; + } + } + else { + param_count = block_type->u.type->param_count; + ret_count = block_type->u.type->result_count; + param_types = block_type->u.type->types; + ret_types = block_type->u.type->types + param_count; + } + bh_assert(param_count == ret_count + && (!param_count + || !memcmp(param_types, ret_types, param_count))); + (void)ret_types; + (void)ret_count; + (void)param_types; + } + + POP_CSP(); + +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + /* copy the result to the block return address */ + RESERVE_BLOCK_RET(); + + apply_label_patch(loader_ctx, 0, PATCH_END); + free_label_patch_list(loader_ctx->frame_csp); + if (loader_ctx->frame_csp->label_type == LABEL_TYPE_FUNCTION) { + int32 idx; + uint8 ret_type; + + emit_label(WASM_OP_RETURN); + for (idx = (int32)func->func_type->result_count - 1; + idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_OFFSET_TYPE(ret_type); + } + } +#endif + if (loader_ctx->csp_num > 0) { + loader_ctx->frame_csp->end_addr = p - 1; + } + else { + /* end of function block, function will return, + ignore the following bytecodes */ + p = p_end; + + continue; + } + + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(false); + break; + } + + case WASM_OP_BR: + { + if (!(frame_csp_tmp = check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + break; + } + + case WASM_OP_BR_IF: + { + POP_I32(); + + if (!(frame_csp_tmp = check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + + break; + } + + case WASM_OP_BR_TABLE: + { + uint8 *ret_types = NULL; + uint32 ret_count = 0; + + read_leb_uint32(p, p_end, count); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, count); +#endif + POP_I32(); + + /* TODO: check the const */ + for (i = 0; i <= count; i++) { + if (!(frame_csp_tmp = + check_branch_block(loader_ctx, &p, p_end, + error_buf, error_buf_size))) + goto fail; + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + (void)ret_count; + (void)ret_types; + break; + } + + case WASM_OP_RETURN: + { + int32 idx; + uint8 ret_type; + for (idx = (int32)func->func_type->result_count - 1; idx >= 0; idx--) { + ret_type = *(func->func_type->types + + func->func_type->param_count + idx); + POP_TYPE(ret_type); +#if WASM_ENABLE_FAST_INTERP != 0 + // emit the offset after return opcode + POP_OFFSET_TYPE(ret_type); +#endif + } + + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + + break; + } + + case WASM_OP_CALL: + { + WASMType *func_type; + uint32 func_idx; + int32 idx; + + read_leb_uint32(p, p_end, func_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + // we need to emit func_idx before arguments + emit_uint32(loader_ctx, func_idx); +#endif + + bh_assert(func_idx < module->import_function_count + + module->function_count); + + if (func_idx < module->import_function_count) + func_type = module->import_functions[func_idx].u.function.func_type; + else + func_type = + module->functions[func_idx - module->import_function_count]->func_type; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) { + POP_TYPE(func_type->types[idx]); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + } + } + + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + /* Here we emit each return value's dynamic_offset. But in fact + * these offsets are continuous, so interpreter only need to get + * the first return value's offset. + */ + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); +#endif + } + + func->has_op_func_call = true; + break; + } + + case WASM_OP_CALL_INDIRECT: + { + int32 idx; + WASMType *func_type; + uint32 type_idx; + + bh_assert(module->import_table_count + + module->table_count > 0); + + read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_FAST_INTERP != 0 + // we need to emit func_idx before arguments + emit_uint32(loader_ctx, type_idx); +#endif + + /* reserved byte 0x00 */ + bh_assert(*p == 0x00); + p++; + + POP_I32(); + + bh_assert(type_idx < module->type_count); + + func_type = module->types[type_idx]; + + if (func_type->param_count > 0) { + for (idx = (int32)(func_type->param_count - 1); idx >= 0; idx--) { + POP_TYPE(func_type->types[idx]); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(func_type->types[idx]); +#endif + } + } + + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); +#endif + } + + func->has_op_func_call = true; + break; + } + + case WASM_OP_DROP: + case WASM_OP_DROP_64: + { + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + bh_assert(!(available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic)); + + if (available_stack_cell > 0) { + if (*(loader_ctx->frame_ref - 1) == REF_I32 + || *(loader_ctx->frame_ref - 1) == REF_F32) { + loader_ctx->frame_ref--; + loader_ctx->stack_cell_num--; +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset--; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset --; +#endif + } + else { + loader_ctx->frame_ref -= 2; + loader_ctx->stack_cell_num -= 2; +#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) + *(p - 1) = WASM_OP_DROP_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + loader_ctx->frame_offset -= 2; + if (*(loader_ctx->frame_offset) > + loader_ctx->start_dynamic_offset) + loader_ctx->dynamic_offset -= 2; +#endif + } + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); +#endif + } + break; + } + + case WASM_OP_SELECT: + case WASM_OP_SELECT_64: + { + uint8 ref_type; + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + int32 available_stack_cell; + + POP_I32(); + + available_stack_cell = (int32) + (loader_ctx->stack_cell_num - cur_block->stack_cell_num); + + bh_assert(!(available_stack_cell <= 0 + && !cur_block->is_stack_polymorphic)); + + if (available_stack_cell > 0) { + switch (*(loader_ctx->frame_ref - 1)) { + case REF_I32: + case REF_F32: + break; + case REF_I64_2: + case REF_F64_2: +#if (WASM_ENABLE_FAST_INTERP == 0) || (WASM_ENABLE_JIT != 0) + *(p - 1) = WASM_OP_SELECT_64; +#endif +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { +#if WASM_ENABLE_ABS_LABEL_ADDR != 0 + *(void**)(loader_ctx->p_code_compiled - 2 - sizeof(void*)) = + handle_table[WASM_OP_SELECT_64]; +#else + *((int16*)loader_ctx->p_code_compiled - 2) = (int16) + (handle_table[WASM_OP_SELECT_64] - handle_table[0]); +#endif + } +#endif + break; + } + + ref_type = *(loader_ctx->frame_ref - 1); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); +#endif + POP_TYPE(ref_type); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(VALUE_TYPE_ANY); +#endif + PUSH_TYPE(VALUE_TYPE_ANY); + } + break; + } + + case WASM_OP_GET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + /* Get Local is optimized out */ + skip_label(); + disable_emit = true; + operand_offset = local_offset; + PUSH_OFFSET_TYPE(local_type); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_GET_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_SET_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); + POP_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local(loader_ctx, opcode, local_offset, + local_type, &preserve_local, + error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if ((!preserve_local) && (LAST_OP_OUTPUT_I32())) { + if (loader_ctx->p_code_compiled) + *(int16*)(loader_ctx->p_code_compiled - 2) = local_offset; + loader_ctx->frame_offset --; + loader_ctx->dynamic_offset --; + } + else if ((!preserve_local) && (LAST_OP_OUTPUT_I64())) { + if (loader_ctx->p_code_compiled) + *(int16*)(loader_ctx->p_code_compiled - 2) = local_offset; + loader_ctx->frame_offset -= 2; + loader_ctx->dynamic_offset -= 2; + } + else { + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + emit_label(EXT_OP_SET_LOCAL_FAST); + emit_byte(loader_ctx, local_offset); + } + else { + emit_label(EXT_OP_SET_LOCAL_FAST_I64); + emit_byte(loader_ctx, local_offset); + } + POP_OFFSET_TYPE(local_type); + } + } + else { /* local index larger than 255, reserve leb */ + p_org ++; + emit_leb(); + POP_OFFSET_TYPE(local_type); + } +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_SET_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_TEE_LOCAL: + { + p_org = p - 1; + GET_LOCAL_INDEX_TYPE_AND_OFFSET(); +#if WASM_ENABLE_FAST_INTERP != 0 + /* If the stack is in polymorphic state, do fake pop and push on + offset stack to keep the depth of offset stack to be the same + with ref stack */ + BranchBlock *cur_block = loader_ctx->frame_csp - 1; + if (cur_block->is_stack_polymorphic) { + POP_OFFSET_TYPE(local_type); + PUSH_OFFSET_TYPE(local_type); + } +#endif + POP_TYPE(local_type); + PUSH_TYPE(local_type); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!(preserve_referenced_local(loader_ctx, opcode, local_offset, + local_type, &preserve_local, + error_buf, error_buf_size))) + goto fail; + + if (local_offset < 256) { + skip_label(); + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) { + emit_label(EXT_OP_TEE_LOCAL_FAST); + emit_byte(loader_ctx, local_offset); + } + else { + emit_label(EXT_OP_TEE_LOCAL_FAST_I64); + emit_byte(loader_ctx, local_offset); + } + } + else { /* local index larger than 255, reserve leb */ + p_org ++; + emit_leb(); + } + emit_operand(loader_ctx, *(loader_ctx->frame_offset - + wasm_value_type_cell_num(local_type))); +#else +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (local_offset < 0x80) { + *p_org++ = EXT_OP_TEE_LOCAL_FAST; + if (local_type == VALUE_TYPE_I32 + || local_type == VALUE_TYPE_F32) + *p_org++ = (uint8)local_offset; + else + *p_org++ = (uint8)(local_offset | 0x80); + while (p_org < p) + *p_org++ = WASM_OP_NOP; + } +#endif +#endif + break; + } + + case WASM_OP_GET_GLOBAL: + { + p_org = p - 1; + read_leb_uint32(p, p_end, global_idx); + bh_assert(global_idx < global_count); + + global_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.type + : module->globals[global_idx - module->import_global_count] + .type; + + PUSH_TYPE(global_type); + +#if WASM_ENABLE_FAST_INTERP == 0 +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + *p_org = WASM_OP_GET_GLOBAL_64; + } +#endif +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_GET_GLOBAL_64); + } + emit_uint32(loader_ctx, global_idx); + PUSH_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + break; + } + + case WASM_OP_SET_GLOBAL: + { + bool is_mutable = false; + + p_org = p - 1; + read_leb_uint32(p, p_end, global_idx); + bh_assert(global_idx < global_count); + + is_mutable = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.is_mutable + : module->globals[global_idx - module->import_global_count] + .is_mutable; + bh_assert(is_mutable); + + global_type = + global_idx < module->import_global_count + ? module->import_globals[global_idx].u.global.type + : module->globals[global_idx - module->import_global_count] + .type; + + POP_TYPE(global_type); + +#if WASM_ENABLE_FAST_INTERP == 0 +#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + *p_org = WASM_OP_SET_GLOBAL_64; + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + *p_org = WASM_OP_SET_GLOBAL_AUX_STACK; + } +#endif +#else /* else of WASM_ENABLE_FAST_INTERP */ + if (global_type == VALUE_TYPE_I64 + || global_type == VALUE_TYPE_F64) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_64); + } + else if (module->aux_stack_size > 0 + && global_idx == module->aux_stack_top_global_index) { + skip_label(); + emit_label(WASM_OP_SET_GLOBAL_AUX_STACK); + } + emit_uint32(loader_ctx, global_idx); + POP_OFFSET_TYPE(global_type); +#endif /* end of WASM_ENABLE_FAST_INTERP */ + + (void)is_mutable; + break; + } + + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + case WASM_OP_F32_LOAD: + case WASM_OP_F64_LOAD: + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + case WASM_OP_F32_STORE: + case WASM_OP_F64_STORE: + { +#if WASM_ENABLE_FAST_INTERP != 0 + /* change F32/F64 into I32/I64 */ + if (opcode == WASM_OP_F32_LOAD) { + skip_label(); + emit_label(WASM_OP_I32_LOAD); + } + else if (opcode == WASM_OP_F64_LOAD) { + skip_label(); + emit_label(WASM_OP_I64_LOAD); + } + else if (opcode == WASM_OP_F32_STORE) { + skip_label(); + emit_label(WASM_OP_I32_STORE); + } + else if (opcode == WASM_OP_F64_STORE) { + skip_label(); + emit_label(WASM_OP_I64_STORE); + } +#endif + CHECK_MEMORY(); + read_leb_uint32(p, p_end, align); /* align */ + read_leb_uint32(p, p_end, mem_offset); /* offset */ +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + switch (opcode) + { + /* load */ + case WASM_OP_I32_LOAD: + case WASM_OP_I32_LOAD8_S: + case WASM_OP_I32_LOAD8_U: + case WASM_OP_I32_LOAD16_S: + case WASM_OP_I32_LOAD16_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_I64_LOAD: + case WASM_OP_I64_LOAD8_S: + case WASM_OP_I64_LOAD8_U: + case WASM_OP_I64_LOAD16_S: + case WASM_OP_I64_LOAD16_U: + case WASM_OP_I64_LOAD32_S: + case WASM_OP_I64_LOAD32_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + case WASM_OP_F32_LOAD: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + case WASM_OP_F64_LOAD: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + /* store */ + case WASM_OP_I32_STORE: + case WASM_OP_I32_STORE8: + case WASM_OP_I32_STORE16: + POP_I32(); + POP_I32(); + break; + case WASM_OP_I64_STORE: + case WASM_OP_I64_STORE8: + case WASM_OP_I64_STORE16: + case WASM_OP_I64_STORE32: + POP_I64(); + POP_I32(); + break; + case WASM_OP_F32_STORE: + POP_F32(); + POP_I32(); + break; + case WASM_OP_F64_STORE: + POP_F64(); + POP_I32(); + break; + default: + break; + } + break; + } + + case WASM_OP_MEMORY_SIZE: + CHECK_MEMORY(); + /* reserved byte 0x00 */ + bh_assert(*p == 0x00); + p++; + PUSH_I32(); + + module->possible_memory_grow = true; + break; + + case WASM_OP_MEMORY_GROW: + CHECK_MEMORY(); + /* reserved byte 0x00 */ + bh_assert(*p == 0x00); + p++; + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + + func->has_op_memory_grow = true; + module->possible_memory_grow = true; + break; + + case WASM_OP_I32_CONST: + read_leb_int32(p, p_end, i32_const); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I32, i32_const); +#else + (void)i32_const; +#endif + PUSH_I32(); + break; + + case WASM_OP_I64_CONST: + read_leb_int64(p, p_end, i64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + GET_CONST_OFFSET(VALUE_TYPE_I64, i64); +#endif + PUSH_I64(); + break; + + case WASM_OP_F32_CONST: + p += sizeof(float32); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + bh_memcpy_s((uint8*)&f32, sizeof(float32), p_org, sizeof(float32)); + GET_CONST_F32_OFFSET(VALUE_TYPE_F32, f32); +#endif + PUSH_F32(); + break; + + case WASM_OP_F64_CONST: + p += sizeof(float64); +#if WASM_ENABLE_FAST_INTERP != 0 + skip_label(); + disable_emit = true; + /* Some MCU may require 8-byte align */ + bh_memcpy_s((uint8*)&f64, sizeof(float64), p_org, sizeof(float64)); + GET_CONST_F64_OFFSET(VALUE_TYPE_F64, f64); +#endif + PUSH_F64(); + break; + + case WASM_OP_I32_EQZ: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_EQ: + case WASM_OP_I32_NE: + case WASM_OP_I32_LT_S: + case WASM_OP_I32_LT_U: + case WASM_OP_I32_GT_S: + case WASM_OP_I32_GT_U: + case WASM_OP_I32_LE_S: + case WASM_OP_I32_LE_U: + case WASM_OP_I32_GE_S: + case WASM_OP_I32_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQZ: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EQ: + case WASM_OP_I64_NE: + case WASM_OP_I64_LT_S: + case WASM_OP_I64_LT_U: + case WASM_OP_I64_GT_S: + case WASM_OP_I64_GT_U: + case WASM_OP_I64_LE_S: + case WASM_OP_I64_LE_U: + case WASM_OP_I64_GE_S: + case WASM_OP_I64_GE_U: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_F32_EQ: + case WASM_OP_F32_NE: + case WASM_OP_F32_LT: + case WASM_OP_F32_GT: + case WASM_OP_F32_LE: + case WASM_OP_F32_GE: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_F64_EQ: + case WASM_OP_F64_NE: + case WASM_OP_F64_LT: + case WASM_OP_F64_GT: + case WASM_OP_F64_LE: + case WASM_OP_F64_GE: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_CLZ: + case WASM_OP_I32_CTZ: + case WASM_OP_I32_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_ADD: + case WASM_OP_I32_SUB: + case WASM_OP_I32_MUL: + case WASM_OP_I32_DIV_S: + case WASM_OP_I32_DIV_U: + case WASM_OP_I32_REM_S: + case WASM_OP_I32_REM_U: + case WASM_OP_I32_AND: + case WASM_OP_I32_OR: + case WASM_OP_I32_XOR: + case WASM_OP_I32_SHL: + case WASM_OP_I32_SHR_S: + case WASM_OP_I32_SHR_U: + case WASM_OP_I32_ROTL: + case WASM_OP_I32_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_CLZ: + case WASM_OP_I64_CTZ: + case WASM_OP_I64_POPCNT: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_ADD: + case WASM_OP_I64_SUB: + case WASM_OP_I64_MUL: + case WASM_OP_I64_DIV_S: + case WASM_OP_I64_DIV_U: + case WASM_OP_I64_REM_S: + case WASM_OP_I64_REM_U: + case WASM_OP_I64_AND: + case WASM_OP_I64_OR: + case WASM_OP_I64_XOR: + case WASM_OP_I64_SHL: + case WASM_OP_I64_SHR_S: + case WASM_OP_I64_SHR_U: + case WASM_OP_I64_ROTL: + case WASM_OP_I64_ROTR: + POP2_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_ABS: + case WASM_OP_F32_NEG: + case WASM_OP_F32_CEIL: + case WASM_OP_F32_FLOOR: + case WASM_OP_F32_TRUNC: + case WASM_OP_F32_NEAREST: + case WASM_OP_F32_SQRT: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_ADD: + case WASM_OP_F32_SUB: + case WASM_OP_F32_MUL: + case WASM_OP_F32_DIV: + case WASM_OP_F32_MIN: + case WASM_OP_F32_MAX: + case WASM_OP_F32_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_ABS: + case WASM_OP_F64_NEG: + case WASM_OP_F64_CEIL: + case WASM_OP_F64_FLOOR: + case WASM_OP_F64_TRUNC: + case WASM_OP_F64_NEAREST: + case WASM_OP_F64_SQRT: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_ADD: + case WASM_OP_F64_SUB: + case WASM_OP_F64_MUL: + case WASM_OP_F64_DIV: + case WASM_OP_F64_MIN: + case WASM_OP_F64_MAX: + case WASM_OP_F64_COPYSIGN: + POP2_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_WRAP_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F32: + case WASM_OP_I32_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I32_TRUNC_S_F64: + case WASM_OP_I32_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND_S_I32: + case WASM_OP_I64_EXTEND_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F32: + case WASM_OP_I64_TRUNC_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + + case WASM_OP_I64_TRUNC_S_F64: + case WASM_OP_I64_TRUNC_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_CONVERT_S_I32: + case WASM_OP_F32_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_CONVERT_S_I64: + case WASM_OP_F32_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F32); + break; + + case WASM_OP_F32_DEMOTE_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_CONVERT_S_I32: + case WASM_OP_F64_CONVERT_U_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_CONVERT_S_I64: + case WASM_OP_F64_CONVERT_U_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_F64_PROMOTE_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_REINTERPRET_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_REINTERPRET_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; + + case WASM_OP_F32_REINTERPRET_I32: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_F32); + break; + + case WASM_OP_F64_REINTERPRET_I64: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_F64); + break; + + case WASM_OP_I32_EXTEND8_S: + case WASM_OP_I32_EXTEND16_S: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + + case WASM_OP_I64_EXTEND8_S: + case WASM_OP_I64_EXTEND16_S: + case WASM_OP_I64_EXTEND32_S: + POP_AND_PUSH(VALUE_TYPE_I64, VALUE_TYPE_I64); + break; + + case WASM_OP_MISC_PREFIX: + { + uint32 opcode1; + + read_leb_uint32(p, p_end, opcode1); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, ((uint8)opcode1)); +#endif + switch (opcode1) { + case WASM_OP_I32_TRUNC_SAT_S_F32: + case WASM_OP_I32_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I32); + break; + case WASM_OP_I32_TRUNC_SAT_S_F64: + case WASM_OP_I32_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I32); + break; + case WASM_OP_I64_TRUNC_SAT_S_F32: + case WASM_OP_I64_TRUNC_SAT_U_F32: + POP_AND_PUSH(VALUE_TYPE_F32, VALUE_TYPE_I64); + break; + case WASM_OP_I64_TRUNC_SAT_S_F64: + case WASM_OP_I64_TRUNC_SAT_U_F64: + POP_AND_PUSH(VALUE_TYPE_F64, VALUE_TYPE_I64); + break; +#if WASM_ENABLE_BULK_MEMORY != 0 + case WASM_OP_MEMORY_INIT: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + bh_assert(module->import_memory_count + + module->memory_count > 0); + + bh_assert(*p == 0x00); + p++; + + bh_assert(segment_index < module->data_seg_count); + bh_assert(module->data_seg_count1 > 0); + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_DATA_DROP: + read_leb_uint32(p, p_end, segment_index); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, segment_index); +#endif + bh_assert(segment_index < module->data_seg_count); + bh_assert(module->data_seg_count1 > 0); + break; + case WASM_OP_MEMORY_COPY: + /* both src and dst memory index should be 0 */ + bh_assert(*(int16*)p != 0x0000); + p += 2; + + bh_assert(module->import_memory_count + + module->memory_count > 0); + + POP_I32(); + POP_I32(); + POP_I32(); + break; + case WASM_OP_MEMORY_FILL: + bh_assert(*p == 0); + p++; + + bh_assert(module->import_memory_count + + module->memory_count > 0); + + POP_I32(); + POP_I32(); + POP_I32(); + break; + /* TODO: to support bulk table operation */ +#endif /* WASM_ENABLE_BULK_MEMORY */ + default: + bh_assert(0); + break; + } + break; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + case WASM_OP_ATOMIC_PREFIX: + { + opcode = read_uint8(p); +#if WASM_ENABLE_FAST_INTERP != 0 + emit_byte(loader_ctx, opcode); +#endif + if (opcode != WASM_OP_ATOMIC_FENCE) { + CHECK_MEMORY(); + read_leb_uint32(p, p_end, align); /* align */ + read_leb_uint32(p, p_end, mem_offset); /* offset */ +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, mem_offset); +#endif + } + switch (opcode) { + case WASM_OP_ATOMIC_NOTIFY: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_WAIT32: + POP_I64(); + POP_I32(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_WAIT64: + POP_I64(); + POP_I64(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_FENCE: + /* reserved byte 0x00 */ + bh_assert(*p == 0x00); + p++; + break; + case WASM_OP_ATOMIC_I32_LOAD: + case WASM_OP_ATOMIC_I32_LOAD8_U: + case WASM_OP_ATOMIC_I32_LOAD16_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_I32_STORE: + case WASM_OP_ATOMIC_I32_STORE8: + case WASM_OP_ATOMIC_I32_STORE16: + POP_I32(); + POP_I32(); + break; + case WASM_OP_ATOMIC_I64_LOAD: + case WASM_OP_ATOMIC_I64_LOAD8_U: + case WASM_OP_ATOMIC_I64_LOAD16_U: + case WASM_OP_ATOMIC_I64_LOAD32_U: + POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I64); + break; + case WASM_OP_ATOMIC_I64_STORE: + case WASM_OP_ATOMIC_I64_STORE8: + case WASM_OP_ATOMIC_I64_STORE16: + case WASM_OP_ATOMIC_I64_STORE32: + POP_I64(); + POP_I32(); + break; + case WASM_OP_ATOMIC_RMW_I32_ADD: + case WASM_OP_ATOMIC_RMW_I32_ADD8_U: + case WASM_OP_ATOMIC_RMW_I32_ADD16_U: + case WASM_OP_ATOMIC_RMW_I32_SUB: + case WASM_OP_ATOMIC_RMW_I32_SUB8_U: + case WASM_OP_ATOMIC_RMW_I32_SUB16_U: + case WASM_OP_ATOMIC_RMW_I32_AND: + case WASM_OP_ATOMIC_RMW_I32_AND8_U: + case WASM_OP_ATOMIC_RMW_I32_AND16_U: + case WASM_OP_ATOMIC_RMW_I32_OR: + case WASM_OP_ATOMIC_RMW_I32_OR8_U: + case WASM_OP_ATOMIC_RMW_I32_OR16_U: + case WASM_OP_ATOMIC_RMW_I32_XOR: + case WASM_OP_ATOMIC_RMW_I32_XOR8_U: + case WASM_OP_ATOMIC_RMW_I32_XOR16_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG: + case WASM_OP_ATOMIC_RMW_I32_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_XCHG16_U: + POP2_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); + break; + case WASM_OP_ATOMIC_RMW_I64_ADD: + case WASM_OP_ATOMIC_RMW_I64_ADD8_U: + case WASM_OP_ATOMIC_RMW_I64_ADD16_U: + case WASM_OP_ATOMIC_RMW_I64_ADD32_U: + case WASM_OP_ATOMIC_RMW_I64_SUB: + case WASM_OP_ATOMIC_RMW_I64_SUB8_U: + case WASM_OP_ATOMIC_RMW_I64_SUB16_U: + case WASM_OP_ATOMIC_RMW_I64_SUB32_U: + case WASM_OP_ATOMIC_RMW_I64_AND: + case WASM_OP_ATOMIC_RMW_I64_AND8_U: + case WASM_OP_ATOMIC_RMW_I64_AND16_U: + case WASM_OP_ATOMIC_RMW_I64_AND32_U: + case WASM_OP_ATOMIC_RMW_I64_OR: + case WASM_OP_ATOMIC_RMW_I64_OR8_U: + case WASM_OP_ATOMIC_RMW_I64_OR16_U: + case WASM_OP_ATOMIC_RMW_I64_OR32_U: + case WASM_OP_ATOMIC_RMW_I64_XOR: + case WASM_OP_ATOMIC_RMW_I64_XOR8_U: + case WASM_OP_ATOMIC_RMW_I64_XOR16_U: + case WASM_OP_ATOMIC_RMW_I64_XOR32_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG: + case WASM_OP_ATOMIC_RMW_I64_XCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_XCHG32_U: + POP_I64(); + POP_I32(); + PUSH_I64(); + break; + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U: + POP_I32(); + POP_I32(); + POP_I32(); + PUSH_I32(); + break; + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U: + case WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U: + POP_I64(); + POP_I64(); + POP_I32(); + PUSH_I64(); + break; + default: + bh_assert(0); + break; + } + break; + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + + default: + bh_assert(0); + break; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + last_op = opcode; +#endif + } + + if (loader_ctx->csp_num > 0) { + set_error_buf(error_buf, error_buf_size, + "function body must end with END opcode"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled == NULL) + goto re_scan; + + func->const_cell_num = loader_ctx->const_cell_num; + if (!(func->consts = func_const = + loader_malloc(func->const_cell_num * 4, + error_buf, error_buf_size))) { + goto fail; + } + func_const_end = func->consts + func->const_cell_num * 4; + /* reverse the const buf */ + for (int i = loader_ctx->num_const - 1; i >= 0; i--) { + Const *c = (Const*)(loader_ctx->const_buf + i * sizeof(Const)); + if (c->value_type == VALUE_TYPE_F64 + || c->value_type == VALUE_TYPE_I64) { + bh_memcpy_s(func_const, func_const_end - func_const, + &(c->value.f64), sizeof(int64)); + func_const += sizeof(int64); + } else { + bh_memcpy_s(func_const, func_const_end - func_const, + &(c->value.f32), sizeof(int32)); + func_const += sizeof(int32); + } + } + + func->max_stack_cell_num = loader_ctx->preserved_local_offset - + loader_ctx->start_dynamic_offset + 1; +#else + func->max_stack_cell_num = loader_ctx->max_stack_cell_num; +#endif + func->max_block_num = loader_ctx->max_csp_num; + return_value = true; + +fail: + wasm_loader_ctx_destroy(loader_ctx); + + (void)u8; + (void)u32; + (void)i32; + (void)i64; + (void)global_count; + (void)local_count; + (void)local_offset; + (void)p_org; + (void)mem_offset; + (void)align; + return return_value; +} diff --git a/wamr/core/iwasm/interpreter/wasm_opcode.h b/wamr/core/iwasm/interpreter/wasm_opcode.h new file mode 100644 index 0000000..f546088 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_opcode.h @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_OPCODE_H +#define _WASM_OPCODE_H + +#include "wasm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum WASMOpcode { + /* control instructions */ + WASM_OP_UNREACHABLE = 0x00, /* unreachable */ + WASM_OP_NOP = 0x01, /* nop */ + WASM_OP_BLOCK = 0x02, /* block */ + WASM_OP_LOOP = 0x03, /* loop */ + WASM_OP_IF = 0x04, /* if */ + WASM_OP_ELSE = 0x05, /* else */ + + WASM_OP_UNUSED_0x06 = 0x06, + WASM_OP_UNUSED_0x07 = 0x07, + WASM_OP_UNUSED_0x08 = 0x08, + WASM_OP_UNUSED_0x09 = 0x09, + WASM_OP_UNUSED_0x0a = 0x0a, + + WASM_OP_END = 0x0b, /* end */ + WASM_OP_BR = 0x0c, /* br */ + WASM_OP_BR_IF = 0x0d, /* br if */ + WASM_OP_BR_TABLE = 0x0e, /* br table */ + WASM_OP_RETURN = 0x0f, /* return */ + WASM_OP_CALL = 0x10, /* call */ + WASM_OP_CALL_INDIRECT = 0x11, /* call_indirect */ + + WASM_OP_UNUSED_0x12 = 0x12, + WASM_OP_UNUSED_0x13 = 0x13, + WASM_OP_UNUSED_0x14 = 0x14, + WASM_OP_UNUSED_0x15 = 0x15, + WASM_OP_UNUSED_0x16 = 0x16, + WASM_OP_UNUSED_0x17 = 0x17, + WASM_OP_UNUSED_0x18 = 0x18, + WASM_OP_UNUSED_0x19 = 0x19, + + /* parametric instructions */ + WASM_OP_DROP = 0x1a, /* drop */ + WASM_OP_SELECT = 0x1b, /* select */ + + WASM_OP_UNUSED_0x1c = 0x1c, + WASM_OP_UNUSED_0x1d = 0x1d, + WASM_OP_UNUSED_0x1e = 0x1e, + WASM_OP_UNUSED_0x1f = 0x1f, + + /* variable instructions */ + WASM_OP_GET_LOCAL = 0x20, /* get_local */ + WASM_OP_SET_LOCAL = 0x21, /* set_local */ + WASM_OP_TEE_LOCAL = 0x22, /* tee_local */ + WASM_OP_GET_GLOBAL = 0x23, /* get_global */ + WASM_OP_SET_GLOBAL = 0x24, /* set_global */ + + WASM_OP_GET_GLOBAL_64 = 0x25, + WASM_OP_SET_GLOBAL_64 = 0x26, + WASM_OP_SET_GLOBAL_AUX_STACK = 0x27, + + /* memory instructions */ + WASM_OP_I32_LOAD = 0x28, /* i32.load */ + WASM_OP_I64_LOAD = 0x29, /* i64.load */ + WASM_OP_F32_LOAD = 0x2a, /* f32.load */ + WASM_OP_F64_LOAD = 0x2b, /* f64.load */ + WASM_OP_I32_LOAD8_S = 0x2c, /* i32.load8_s */ + WASM_OP_I32_LOAD8_U = 0x2d, /* i32.load8_u */ + WASM_OP_I32_LOAD16_S = 0x2e, /* i32.load16_s */ + WASM_OP_I32_LOAD16_U = 0x2f, /* i32.load16_u */ + WASM_OP_I64_LOAD8_S = 0x30, /* i64.load8_s */ + WASM_OP_I64_LOAD8_U = 0x31, /* i64.load8_u */ + WASM_OP_I64_LOAD16_S = 0x32, /* i64.load16_s */ + WASM_OP_I64_LOAD16_U = 0x33, /* i64.load16_u */ + WASM_OP_I64_LOAD32_S = 0x34, /* i32.load32_s */ + WASM_OP_I64_LOAD32_U = 0x35, /* i32.load32_u */ + WASM_OP_I32_STORE = 0x36, /* i32.store */ + WASM_OP_I64_STORE = 0x37, /* i64.store */ + WASM_OP_F32_STORE = 0x38, /* f32.store */ + WASM_OP_F64_STORE = 0x39, /* f64.store */ + WASM_OP_I32_STORE8 = 0x3a, /* i32.store8 */ + WASM_OP_I32_STORE16 = 0x3b, /* i32.store16 */ + WASM_OP_I64_STORE8 = 0x3c, /* i64.store8 */ + WASM_OP_I64_STORE16 = 0x3d, /* i64.sotre16 */ + WASM_OP_I64_STORE32 = 0x3e, /* i64.store32 */ + WASM_OP_MEMORY_SIZE = 0x3f, /* memory.size */ + WASM_OP_MEMORY_GROW = 0x40, /* memory.grow */ + + /* constant instructions */ + WASM_OP_I32_CONST = 0x41, /* i32.const */ + WASM_OP_I64_CONST = 0x42, /* i64.const */ + WASM_OP_F32_CONST = 0x43, /* f32.const */ + WASM_OP_F64_CONST = 0x44, /* f64.const */ + + /* comparison instructions */ + WASM_OP_I32_EQZ = 0x45, /* i32.eqz */ + WASM_OP_I32_EQ = 0x46, /* i32.eq */ + WASM_OP_I32_NE = 0x47, /* i32.ne */ + WASM_OP_I32_LT_S = 0x48, /* i32.lt_s */ + WASM_OP_I32_LT_U = 0x49, /* i32.lt_u */ + WASM_OP_I32_GT_S = 0x4a, /* i32.gt_s */ + WASM_OP_I32_GT_U = 0x4b, /* i32.gt_u */ + WASM_OP_I32_LE_S = 0x4c, /* i32.le_s */ + WASM_OP_I32_LE_U = 0x4d, /* i32.le_u */ + WASM_OP_I32_GE_S = 0x4e, /* i32.ge_s */ + WASM_OP_I32_GE_U = 0x4f, /* i32.ge_u */ + + WASM_OP_I64_EQZ = 0x50, /* i64.eqz */ + WASM_OP_I64_EQ = 0x51, /* i64.eq */ + WASM_OP_I64_NE = 0x52, /* i64.ne */ + WASM_OP_I64_LT_S = 0x53, /* i64.lt_s */ + WASM_OP_I64_LT_U = 0x54, /* i64.lt_u */ + WASM_OP_I64_GT_S = 0x55, /* i64.gt_s */ + WASM_OP_I64_GT_U = 0x56, /* i64.gt_u */ + WASM_OP_I64_LE_S = 0x57, /* i64.le_s */ + WASM_OP_I64_LE_U = 0x58, /* i64.le_u */ + WASM_OP_I64_GE_S = 0x59, /* i64.ge_s */ + WASM_OP_I64_GE_U = 0x5a, /* i64.ge_u */ + + WASM_OP_F32_EQ = 0x5b, /* f32.eq */ + WASM_OP_F32_NE = 0x5c, /* f32.ne */ + WASM_OP_F32_LT = 0x5d, /* f32.lt */ + WASM_OP_F32_GT = 0x5e, /* f32.gt */ + WASM_OP_F32_LE = 0x5f, /* f32.le */ + WASM_OP_F32_GE = 0x60, /* f32.ge */ + + WASM_OP_F64_EQ = 0x61, /* f64.eq */ + WASM_OP_F64_NE = 0x62, /* f64.ne */ + WASM_OP_F64_LT = 0x63, /* f64.lt */ + WASM_OP_F64_GT = 0x64, /* f64.gt */ + WASM_OP_F64_LE = 0x65, /* f64.le */ + WASM_OP_F64_GE = 0x66, /* f64.ge */ + + /* numeric operators */ + WASM_OP_I32_CLZ = 0x67, /* i32.clz */ + WASM_OP_I32_CTZ = 0x68, /* i32.ctz */ + WASM_OP_I32_POPCNT = 0x69, /* i32.popcnt */ + WASM_OP_I32_ADD = 0x6a, /* i32.add */ + WASM_OP_I32_SUB = 0x6b, /* i32.sub */ + WASM_OP_I32_MUL = 0x6c, /* i32.mul */ + WASM_OP_I32_DIV_S = 0x6d, /* i32.div_s */ + WASM_OP_I32_DIV_U = 0x6e, /* i32.div_u */ + WASM_OP_I32_REM_S = 0x6f, /* i32.rem_s */ + WASM_OP_I32_REM_U = 0x70, /* i32.rem_u */ + WASM_OP_I32_AND = 0x71, /* i32.and */ + WASM_OP_I32_OR = 0x72, /* i32.or */ + WASM_OP_I32_XOR = 0x73, /* i32.xor */ + WASM_OP_I32_SHL = 0x74, /* i32.shl */ + WASM_OP_I32_SHR_S = 0x75, /* i32.shr_s */ + WASM_OP_I32_SHR_U = 0x76, /* i32.shr_u */ + WASM_OP_I32_ROTL = 0x77, /* i32.rotl */ + WASM_OP_I32_ROTR = 0x78, /* i32.rotr */ + + WASM_OP_I64_CLZ = 0x79, /* i64.clz */ + WASM_OP_I64_CTZ = 0x7a, /* i64.ctz */ + WASM_OP_I64_POPCNT = 0x7b, /* i64.popcnt */ + WASM_OP_I64_ADD = 0x7c, /* i64.add */ + WASM_OP_I64_SUB = 0x7d, /* i64.sub */ + WASM_OP_I64_MUL = 0x7e, /* i64.mul */ + WASM_OP_I64_DIV_S = 0x7f, /* i64.div_s */ + WASM_OP_I64_DIV_U = 0x80, /* i64.div_u */ + WASM_OP_I64_REM_S = 0x81, /* i64.rem_s */ + WASM_OP_I64_REM_U = 0x82, /* i64.rem_u */ + WASM_OP_I64_AND = 0x83, /* i64.and */ + WASM_OP_I64_OR = 0x84, /* i64.or */ + WASM_OP_I64_XOR = 0x85, /* i64.xor */ + WASM_OP_I64_SHL = 0x86, /* i64.shl */ + WASM_OP_I64_SHR_S = 0x87, /* i64.shr_s */ + WASM_OP_I64_SHR_U = 0x88, /* i64.shr_u */ + WASM_OP_I64_ROTL = 0x89, /* i64.rotl */ + WASM_OP_I64_ROTR = 0x8a, /* i64.rotr */ + + WASM_OP_F32_ABS = 0x8b, /* f32.abs */ + WASM_OP_F32_NEG = 0x8c, /* f32.neg */ + WASM_OP_F32_CEIL = 0x8d, /* f32.ceil */ + WASM_OP_F32_FLOOR = 0x8e, /* f32.floor */ + WASM_OP_F32_TRUNC = 0x8f, /* f32.trunc */ + WASM_OP_F32_NEAREST = 0x90, /* f32.nearest */ + WASM_OP_F32_SQRT = 0x91, /* f32.sqrt */ + WASM_OP_F32_ADD = 0x92, /* f32.add */ + WASM_OP_F32_SUB = 0x93, /* f32.sub */ + WASM_OP_F32_MUL = 0x94, /* f32.mul */ + WASM_OP_F32_DIV = 0x95, /* f32.div */ + WASM_OP_F32_MIN = 0x96, /* f32.min */ + WASM_OP_F32_MAX = 0x97, /* f32.max */ + WASM_OP_F32_COPYSIGN = 0x98, /* f32.copysign */ + + WASM_OP_F64_ABS = 0x99, /* f64.abs */ + WASM_OP_F64_NEG = 0x9a, /* f64.neg */ + WASM_OP_F64_CEIL = 0x9b, /* f64.ceil */ + WASM_OP_F64_FLOOR = 0x9c, /* f64.floor */ + WASM_OP_F64_TRUNC = 0x9d, /* f64.trunc */ + WASM_OP_F64_NEAREST = 0x9e, /* f64.nearest */ + WASM_OP_F64_SQRT = 0x9f, /* f64.sqrt */ + WASM_OP_F64_ADD = 0xa0, /* f64.add */ + WASM_OP_F64_SUB = 0xa1, /* f64.sub */ + WASM_OP_F64_MUL = 0xa2, /* f64.mul */ + WASM_OP_F64_DIV = 0xa3, /* f64.div */ + WASM_OP_F64_MIN = 0xa4, /* f64.min */ + WASM_OP_F64_MAX = 0xa5, /* f64.max */ + WASM_OP_F64_COPYSIGN = 0xa6, /* f64.copysign */ + + /* conversions */ + WASM_OP_I32_WRAP_I64 = 0xa7, /* i32.wrap/i64 */ + WASM_OP_I32_TRUNC_S_F32 = 0xa8, /* i32.trunc_s/f32 */ + WASM_OP_I32_TRUNC_U_F32 = 0xa9, /* i32.trunc_u/f32 */ + WASM_OP_I32_TRUNC_S_F64 = 0xaa, /* i32.trunc_s/f64 */ + WASM_OP_I32_TRUNC_U_F64 = 0xab, /* i32.trunc_u/f64 */ + + WASM_OP_I64_EXTEND_S_I32 = 0xac, /* i64.extend_s/i32 */ + WASM_OP_I64_EXTEND_U_I32 = 0xad, /* i64.extend_u/i32 */ + WASM_OP_I64_TRUNC_S_F32 = 0xae, /* i64.trunc_s/f32 */ + WASM_OP_I64_TRUNC_U_F32 = 0xaf, /* i64.trunc_u/f32 */ + WASM_OP_I64_TRUNC_S_F64 = 0xb0, /* i64.trunc_s/f64 */ + WASM_OP_I64_TRUNC_U_F64 = 0xb1, /* i64.trunc_u/f64 */ + + WASM_OP_F32_CONVERT_S_I32 = 0xb2, /* f32.convert_s/i32 */ + WASM_OP_F32_CONVERT_U_I32 = 0xb3, /* f32.convert_u/i32 */ + WASM_OP_F32_CONVERT_S_I64 = 0xb4, /* f32.convert_s/i64 */ + WASM_OP_F32_CONVERT_U_I64 = 0xb5, /* f32.convert_u/i64 */ + WASM_OP_F32_DEMOTE_F64 = 0xb6, /* f32.demote/f64 */ + + WASM_OP_F64_CONVERT_S_I32 = 0xb7, /* f64.convert_s/i32 */ + WASM_OP_F64_CONVERT_U_I32 = 0xb8, /* f64.convert_u/i32 */ + WASM_OP_F64_CONVERT_S_I64 = 0xb9, /* f64.convert_s/i64 */ + WASM_OP_F64_CONVERT_U_I64 = 0xba, /* f64.convert_u/i64 */ + WASM_OP_F64_PROMOTE_F32 = 0xbb, /* f64.promote/f32 */ + + /* reinterpretations */ + WASM_OP_I32_REINTERPRET_F32 = 0xbc, /* i32.reinterpret/f32 */ + WASM_OP_I64_REINTERPRET_F64 = 0xbd, /* i64.reinterpret/f64 */ + WASM_OP_F32_REINTERPRET_I32 = 0xbe, /* f32.reinterpret/i32 */ + WASM_OP_F64_REINTERPRET_I64 = 0xbf, /* f64.reinterpret/i64 */ + + WASM_OP_I32_EXTEND8_S = 0xc0, /* i32.extend8_s */ + WASM_OP_I32_EXTEND16_S = 0xc1, /* i32.extend16_s */ + WASM_OP_I64_EXTEND8_S = 0xc2, /* i64.extend8_s */ + WASM_OP_I64_EXTEND16_S = 0xc3, /* i64.extend16_s */ + WASM_OP_I64_EXTEND32_S = 0xc4, /* i64.extend32_s */ + + /* drop/select specified types*/ + WASM_OP_DROP_64 = 0xc5, + WASM_OP_SELECT_64 = 0xc6, + + /* extend op code */ + EXT_OP_GET_LOCAL_FAST = 0xc7, + EXT_OP_SET_LOCAL_FAST_I64 = 0xc8, + EXT_OP_SET_LOCAL_FAST = 0xc9, + EXT_OP_TEE_LOCAL_FAST = 0xca, + EXT_OP_TEE_LOCAL_FAST_I64 = 0xcb, + EXT_OP_COPY_STACK_TOP = 0xcc, + EXT_OP_COPY_STACK_TOP_I64 = 0xcd, + EXT_OP_COPY_STACK_VALUES = 0xce, + EXT_OP_BLOCK = 0xcf, /* block with blocktype */ + EXT_OP_LOOP = 0xd0, /* loop with blocktype */ + EXT_OP_IF = 0xd1, /* if with blocktype */ + WASM_OP_IMPDEP = 0xd2, + + /* Post-MVP extend op prefix */ + WASM_OP_MISC_PREFIX = 0xfc, + WASM_OP_ATOMIC_PREFIX = 0xfe, +} WASMOpcode; + +typedef enum WASMMiscEXTOpcode { + WASM_OP_I32_TRUNC_SAT_S_F32 = 0x00, + WASM_OP_I32_TRUNC_SAT_U_F32 = 0x01, + WASM_OP_I32_TRUNC_SAT_S_F64 = 0x02, + WASM_OP_I32_TRUNC_SAT_U_F64 = 0x03, + WASM_OP_I64_TRUNC_SAT_S_F32 = 0x04, + WASM_OP_I64_TRUNC_SAT_U_F32 = 0x05, + WASM_OP_I64_TRUNC_SAT_S_F64 = 0x06, + WASM_OP_I64_TRUNC_SAT_U_F64 = 0x07, +#if WASM_ENABLE_BULK_MEMORY != 0 + WASM_OP_MEMORY_INIT = 0x08, + WASM_OP_DATA_DROP = 0x09, + WASM_OP_MEMORY_COPY = 0x0a, + WASM_OP_MEMORY_FILL = 0x0b, + WASM_OP_TABLE_INIT = 0x0c, + WASM_OP_ELEM_DROP = 0x0d, + WASM_OP_TABLE_COPY = 0x0e +#endif +} WASMMiscEXTOpcode; + +typedef enum WASMAtomicEXTOpcode { + /* atomic wait and notify */ + WASM_OP_ATOMIC_NOTIFY = 0x00, + WASM_OP_ATOMIC_WAIT32 = 0x01, + WASM_OP_ATOMIC_WAIT64 = 0x02, + WASM_OP_ATOMIC_FENCE = 0x03, + /* atomic load and store */ + WASM_OP_ATOMIC_I32_LOAD = 0x10, + WASM_OP_ATOMIC_I64_LOAD = 0x11, + WASM_OP_ATOMIC_I32_LOAD8_U = 0x12, + WASM_OP_ATOMIC_I32_LOAD16_U = 0x13, + WASM_OP_ATOMIC_I64_LOAD8_U = 0x14, + WASM_OP_ATOMIC_I64_LOAD16_U = 0x15, + WASM_OP_ATOMIC_I64_LOAD32_U = 0x16, + WASM_OP_ATOMIC_I32_STORE = 0x17, + WASM_OP_ATOMIC_I64_STORE = 0x18, + WASM_OP_ATOMIC_I32_STORE8 = 0x19, + WASM_OP_ATOMIC_I32_STORE16 = 0x1a, + WASM_OP_ATOMIC_I64_STORE8 = 0x1b, + WASM_OP_ATOMIC_I64_STORE16 = 0x1c, + WASM_OP_ATOMIC_I64_STORE32 = 0x1d, + /* atomic add */ + WASM_OP_ATOMIC_RMW_I32_ADD = 0x1e, + WASM_OP_ATOMIC_RMW_I64_ADD = 0x1f, + WASM_OP_ATOMIC_RMW_I32_ADD8_U = 0x20, + WASM_OP_ATOMIC_RMW_I32_ADD16_U = 0x21, + WASM_OP_ATOMIC_RMW_I64_ADD8_U = 0x22, + WASM_OP_ATOMIC_RMW_I64_ADD16_U = 0x23, + WASM_OP_ATOMIC_RMW_I64_ADD32_U = 0x24, + /* atomic sub */ + WASM_OP_ATOMIC_RMW_I32_SUB = 0x25, + WASM_OP_ATOMIC_RMW_I64_SUB = 0x26, + WASM_OP_ATOMIC_RMW_I32_SUB8_U = 0x27, + WASM_OP_ATOMIC_RMW_I32_SUB16_U = 0x28, + WASM_OP_ATOMIC_RMW_I64_SUB8_U = 0x29, + WASM_OP_ATOMIC_RMW_I64_SUB16_U = 0x2a, + WASM_OP_ATOMIC_RMW_I64_SUB32_U = 0x2b, + /* atomic and */ + WASM_OP_ATOMIC_RMW_I32_AND = 0x2c, + WASM_OP_ATOMIC_RMW_I64_AND = 0x2d, + WASM_OP_ATOMIC_RMW_I32_AND8_U = 0x2e, + WASM_OP_ATOMIC_RMW_I32_AND16_U = 0x2f, + WASM_OP_ATOMIC_RMW_I64_AND8_U = 0x30, + WASM_OP_ATOMIC_RMW_I64_AND16_U = 0x31, + WASM_OP_ATOMIC_RMW_I64_AND32_U = 0x32, + /* atomic or */ + WASM_OP_ATOMIC_RMW_I32_OR = 0x33, + WASM_OP_ATOMIC_RMW_I64_OR = 0x34, + WASM_OP_ATOMIC_RMW_I32_OR8_U = 0x35, + WASM_OP_ATOMIC_RMW_I32_OR16_U = 0x36, + WASM_OP_ATOMIC_RMW_I64_OR8_U = 0x37, + WASM_OP_ATOMIC_RMW_I64_OR16_U = 0x38, + WASM_OP_ATOMIC_RMW_I64_OR32_U = 0x39, + /* atomic xor */ + WASM_OP_ATOMIC_RMW_I32_XOR = 0x3a, + WASM_OP_ATOMIC_RMW_I64_XOR = 0x3b, + WASM_OP_ATOMIC_RMW_I32_XOR8_U = 0x3c, + WASM_OP_ATOMIC_RMW_I32_XOR16_U = 0x3d, + WASM_OP_ATOMIC_RMW_I64_XOR8_U = 0x3e, + WASM_OP_ATOMIC_RMW_I64_XOR16_U = 0x3f, + WASM_OP_ATOMIC_RMW_I64_XOR32_U = 0x40, + /* atomic xchg */ + WASM_OP_ATOMIC_RMW_I32_XCHG = 0x41, + WASM_OP_ATOMIC_RMW_I64_XCHG = 0x42, + WASM_OP_ATOMIC_RMW_I32_XCHG8_U = 0x43, + WASM_OP_ATOMIC_RMW_I32_XCHG16_U = 0x44, + WASM_OP_ATOMIC_RMW_I64_XCHG8_U = 0x45, + WASM_OP_ATOMIC_RMW_I64_XCHG16_U = 0x46, + WASM_OP_ATOMIC_RMW_I64_XCHG32_U = 0x47, + /* atomic cmpxchg */ + WASM_OP_ATOMIC_RMW_I32_CMPXCHG = 0x48, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG = 0x49, + WASM_OP_ATOMIC_RMW_I32_CMPXCHG8_U = 0x4a, + WASM_OP_ATOMIC_RMW_I32_CMPXCHG16_U = 0x4b, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG8_U = 0x4c, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG16_U = 0x4d, + WASM_OP_ATOMIC_RMW_I64_CMPXCHG32_U = 0x4e, +} WASMAtomicEXTOpcode; + +#ifdef __cplusplus +} +#endif + +/* Opcode prefix controlled by features */ +#if WASM_ENABLE_SHARED_MEMORY != 0 +#define DEF_ATOMIC_PREFIX_HANDLE(_name) \ + _name[WASM_OP_ATOMIC_PREFIX] = \ + HANDLE_OPCODE (WASM_OP_ATOMIC_PREFIX); /* 0xfe */ +#else +#define DEF_ATOMIC_PREFIX_HANDLE(_name) +#endif + +/* + * Macro used to generate computed goto tables for the C interpreter. + */ +#define WASM_INSTRUCTION_NUM 256 + +#define DEFINE_GOTO_TABLE(type, _name) \ +static type _name[WASM_INSTRUCTION_NUM] = { \ + HANDLE_OPCODE (WASM_OP_UNREACHABLE), /* 0x00 */ \ + HANDLE_OPCODE (WASM_OP_NOP), /* 0x01 */ \ + HANDLE_OPCODE (WASM_OP_BLOCK), /* 0x02 */ \ + HANDLE_OPCODE (WASM_OP_LOOP), /* 0x03 */ \ + HANDLE_OPCODE (WASM_OP_IF), /* 0x04 */ \ + HANDLE_OPCODE (WASM_OP_ELSE), /* 0x05 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x06), /* 0x06 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x07), /* 0x07 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x08), /* 0x08 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x09), /* 0x09 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x0a), /* 0x0a */ \ + HANDLE_OPCODE (WASM_OP_END), /* 0x0b */ \ + HANDLE_OPCODE (WASM_OP_BR), /* 0x0c */ \ + HANDLE_OPCODE (WASM_OP_BR_IF), /* 0x0d */ \ + HANDLE_OPCODE (WASM_OP_BR_TABLE), /* 0x0e */ \ + HANDLE_OPCODE (WASM_OP_RETURN), /* 0x0f */ \ + HANDLE_OPCODE (WASM_OP_CALL), /* 0x10 */ \ + HANDLE_OPCODE (WASM_OP_CALL_INDIRECT), /* 0x11 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x12), /* 0x12 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x13), /* 0x13 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x14), /* 0x14 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x15), /* 0x15 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x16), /* 0x16 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x17), /* 0x17 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x18), /* 0x18 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x19), /* 0x19 */ \ + HANDLE_OPCODE (WASM_OP_DROP), /* 0x1a */ \ + HANDLE_OPCODE (WASM_OP_SELECT), /* 0x1b */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x1c), /* 0x1c */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x1d), /* 0x1d */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x1e), /* 0x1e */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x1f), /* 0x1f */ \ + HANDLE_OPCODE (WASM_OP_GET_LOCAL), /* 0x20 */ \ + HANDLE_OPCODE (WASM_OP_SET_LOCAL), /* 0x21 */ \ + HANDLE_OPCODE (WASM_OP_TEE_LOCAL), /* 0x22 */ \ + HANDLE_OPCODE (WASM_OP_GET_GLOBAL), /* 0x23 */ \ + HANDLE_OPCODE (WASM_OP_SET_GLOBAL), /* 0x24 */ \ + HANDLE_OPCODE (WASM_OP_GET_GLOBAL_64), /* 0x25 */ \ + HANDLE_OPCODE (WASM_OP_SET_GLOBAL_64), /* 0x26 */ \ + HANDLE_OPCODE (WASM_OP_SET_GLOBAL_AUX_STACK), /* 0x27 */ \ + HANDLE_OPCODE (WASM_OP_I32_LOAD), /* 0x28 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD), /* 0x29 */ \ + HANDLE_OPCODE (WASM_OP_F32_LOAD), /* 0x2a */ \ + HANDLE_OPCODE (WASM_OP_F64_LOAD), /* 0x2b */ \ + HANDLE_OPCODE (WASM_OP_I32_LOAD8_S), /* 0x2c */ \ + HANDLE_OPCODE (WASM_OP_I32_LOAD8_U), /* 0x2d */ \ + HANDLE_OPCODE (WASM_OP_I32_LOAD16_S), /* 0x2e */ \ + HANDLE_OPCODE (WASM_OP_I32_LOAD16_U), /* 0x2f */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD8_S), /* 0x30 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD8_U), /* 0x31 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD16_S), /* 0x32 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD16_U), /* 0x33 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD32_S), /* 0x34 */ \ + HANDLE_OPCODE (WASM_OP_I64_LOAD32_U), /* 0x35 */ \ + HANDLE_OPCODE (WASM_OP_I32_STORE), /* 0x36 */ \ + HANDLE_OPCODE (WASM_OP_I64_STORE), /* 0x37 */ \ + HANDLE_OPCODE (WASM_OP_F32_STORE), /* 0x38 */ \ + HANDLE_OPCODE (WASM_OP_F64_STORE), /* 0x39 */ \ + HANDLE_OPCODE (WASM_OP_I32_STORE8), /* 0x3a */ \ + HANDLE_OPCODE (WASM_OP_I32_STORE16), /* 0x3b */ \ + HANDLE_OPCODE (WASM_OP_I64_STORE8), /* 0x3c */ \ + HANDLE_OPCODE (WASM_OP_I64_STORE16), /* 0x3d */ \ + HANDLE_OPCODE (WASM_OP_I64_STORE32), /* 0x3e */ \ + HANDLE_OPCODE (WASM_OP_MEMORY_SIZE), /* 0x3f */ \ + HANDLE_OPCODE (WASM_OP_MEMORY_GROW), /* 0x40 */ \ + HANDLE_OPCODE (WASM_OP_I32_CONST), /* 0x41 */ \ + HANDLE_OPCODE (WASM_OP_I64_CONST), /* 0x42 */ \ + HANDLE_OPCODE (WASM_OP_F32_CONST), /* 0x43 */ \ + HANDLE_OPCODE (WASM_OP_F64_CONST), /* 0x44 */ \ + HANDLE_OPCODE (WASM_OP_I32_EQZ), /* 0x45 */ \ + HANDLE_OPCODE (WASM_OP_I32_EQ), /* 0x46 */ \ + HANDLE_OPCODE (WASM_OP_I32_NE), /* 0x47 */ \ + HANDLE_OPCODE (WASM_OP_I32_LT_S), /* 0x48 */ \ + HANDLE_OPCODE (WASM_OP_I32_LT_U), /* 0x49 */ \ + HANDLE_OPCODE (WASM_OP_I32_GT_S), /* 0x4a */ \ + HANDLE_OPCODE (WASM_OP_I32_GT_U), /* 0x4b */ \ + HANDLE_OPCODE (WASM_OP_I32_LE_S), /* 0x4c */ \ + HANDLE_OPCODE (WASM_OP_I32_LE_U), /* 0x4d */ \ + HANDLE_OPCODE (WASM_OP_I32_GE_S), /* 0x4e */ \ + HANDLE_OPCODE (WASM_OP_I32_GE_U), /* 0x4f */ \ + HANDLE_OPCODE (WASM_OP_I64_EQZ), /* 0x50 */ \ + HANDLE_OPCODE (WASM_OP_I64_EQ), /* 0x51 */ \ + HANDLE_OPCODE (WASM_OP_I64_NE), /* 0x52 */ \ + HANDLE_OPCODE (WASM_OP_I64_LT_S), /* 0x53 */ \ + HANDLE_OPCODE (WASM_OP_I64_LT_U), /* 0x54 */ \ + HANDLE_OPCODE (WASM_OP_I64_GT_S), /* 0x55 */ \ + HANDLE_OPCODE (WASM_OP_I64_GT_U), /* 0x56 */ \ + HANDLE_OPCODE (WASM_OP_I64_LE_S), /* 0x57 */ \ + HANDLE_OPCODE (WASM_OP_I64_LE_U), /* 0x58 */ \ + HANDLE_OPCODE (WASM_OP_I64_GE_S), /* 0x59 */ \ + HANDLE_OPCODE (WASM_OP_I64_GE_U), /* 0x5a */ \ + HANDLE_OPCODE (WASM_OP_F32_EQ), /* 0x5b */ \ + HANDLE_OPCODE (WASM_OP_F32_NE), /* 0x5c */ \ + HANDLE_OPCODE (WASM_OP_F32_LT), /* 0x5d */ \ + HANDLE_OPCODE (WASM_OP_F32_GT), /* 0x5e */ \ + HANDLE_OPCODE (WASM_OP_F32_LE), /* 0x5f */ \ + HANDLE_OPCODE (WASM_OP_F32_GE), /* 0x60 */ \ + HANDLE_OPCODE (WASM_OP_F64_EQ), /* 0x61 */ \ + HANDLE_OPCODE (WASM_OP_F64_NE), /* 0x62 */ \ + HANDLE_OPCODE (WASM_OP_F64_LT), /* 0x63 */ \ + HANDLE_OPCODE (WASM_OP_F64_GT), /* 0x64 */ \ + HANDLE_OPCODE (WASM_OP_F64_LE), /* 0x65 */ \ + HANDLE_OPCODE (WASM_OP_F64_GE), /* 0x66 */ \ + HANDLE_OPCODE (WASM_OP_I32_CLZ), /* 0x67 */ \ + HANDLE_OPCODE (WASM_OP_I32_CTZ), /* 0x68 */ \ + HANDLE_OPCODE (WASM_OP_I32_POPCNT), /* 0x69 */ \ + HANDLE_OPCODE (WASM_OP_I32_ADD), /* 0x6a */ \ + HANDLE_OPCODE (WASM_OP_I32_SUB), /* 0x6b */ \ + HANDLE_OPCODE (WASM_OP_I32_MUL), /* 0x6c */ \ + HANDLE_OPCODE (WASM_OP_I32_DIV_S), /* 0x6d */ \ + HANDLE_OPCODE (WASM_OP_I32_DIV_U), /* 0x6e */ \ + HANDLE_OPCODE (WASM_OP_I32_REM_S), /* 0x6f */ \ + HANDLE_OPCODE (WASM_OP_I32_REM_U), /* 0x70 */ \ + HANDLE_OPCODE (WASM_OP_I32_AND), /* 0x71 */ \ + HANDLE_OPCODE (WASM_OP_I32_OR), /* 0x72 */ \ + HANDLE_OPCODE (WASM_OP_I32_XOR), /* 0x73 */ \ + HANDLE_OPCODE (WASM_OP_I32_SHL), /* 0x74 */ \ + HANDLE_OPCODE (WASM_OP_I32_SHR_S), /* 0x75 */ \ + HANDLE_OPCODE (WASM_OP_I32_SHR_U), /* 0x76 */ \ + HANDLE_OPCODE (WASM_OP_I32_ROTL), /* 0x77 */ \ + HANDLE_OPCODE (WASM_OP_I32_ROTR), /* 0x78 */ \ + HANDLE_OPCODE (WASM_OP_I64_CLZ), /* 0x79 */ \ + HANDLE_OPCODE (WASM_OP_I64_CTZ), /* 0x7a */ \ + HANDLE_OPCODE (WASM_OP_I64_POPCNT), /* 0x7b */ \ + HANDLE_OPCODE (WASM_OP_I64_ADD), /* 0x7c */ \ + HANDLE_OPCODE (WASM_OP_I64_SUB), /* 0x7d */ \ + HANDLE_OPCODE (WASM_OP_I64_MUL), /* 0x7e */ \ + HANDLE_OPCODE (WASM_OP_I64_DIV_S), /* 0x7f */ \ + HANDLE_OPCODE (WASM_OP_I64_DIV_U), /* 0x80 */ \ + HANDLE_OPCODE (WASM_OP_I64_REM_S), /* 0x81 */ \ + HANDLE_OPCODE (WASM_OP_I64_REM_U), /* 0x82 */ \ + HANDLE_OPCODE (WASM_OP_I64_AND), /* 0x83 */ \ + HANDLE_OPCODE (WASM_OP_I64_OR), /* 0x84 */ \ + HANDLE_OPCODE (WASM_OP_I64_XOR), /* 0x85 */ \ + HANDLE_OPCODE (WASM_OP_I64_SHL), /* 0x86 */ \ + HANDLE_OPCODE (WASM_OP_I64_SHR_S), /* 0x87 */ \ + HANDLE_OPCODE (WASM_OP_I64_SHR_U), /* 0x88 */ \ + HANDLE_OPCODE (WASM_OP_I64_ROTL), /* 0x89 */ \ + HANDLE_OPCODE (WASM_OP_I64_ROTR), /* 0x8a */ \ + HANDLE_OPCODE (WASM_OP_F32_ABS), /* 0x8b */ \ + HANDLE_OPCODE (WASM_OP_F32_NEG), /* 0x8c */ \ + HANDLE_OPCODE (WASM_OP_F32_CEIL), /* 0x8d */ \ + HANDLE_OPCODE (WASM_OP_F32_FLOOR), /* 0x8e */ \ + HANDLE_OPCODE (WASM_OP_F32_TRUNC), /* 0x8f */ \ + HANDLE_OPCODE (WASM_OP_F32_NEAREST), /* 0x90 */ \ + HANDLE_OPCODE (WASM_OP_F32_SQRT), /* 0x91 */ \ + HANDLE_OPCODE (WASM_OP_F32_ADD), /* 0x92 */ \ + HANDLE_OPCODE (WASM_OP_F32_SUB), /* 0x93 */ \ + HANDLE_OPCODE (WASM_OP_F32_MUL), /* 0x94 */ \ + HANDLE_OPCODE (WASM_OP_F32_DIV), /* 0x95 */ \ + HANDLE_OPCODE (WASM_OP_F32_MIN), /* 0x96 */ \ + HANDLE_OPCODE (WASM_OP_F32_MAX), /* 0x97 */ \ + HANDLE_OPCODE (WASM_OP_F32_COPYSIGN), /* 0x98 */ \ + HANDLE_OPCODE (WASM_OP_F64_ABS), /* 0x99 */ \ + HANDLE_OPCODE (WASM_OP_F64_NEG), /* 0x9a */ \ + HANDLE_OPCODE (WASM_OP_F64_CEIL), /* 0x9b */ \ + HANDLE_OPCODE (WASM_OP_F64_FLOOR), /* 0x9c */ \ + HANDLE_OPCODE (WASM_OP_F64_TRUNC), /* 0x9d */ \ + HANDLE_OPCODE (WASM_OP_F64_NEAREST), /* 0x9e */ \ + HANDLE_OPCODE (WASM_OP_F64_SQRT), /* 0x9f */ \ + HANDLE_OPCODE (WASM_OP_F64_ADD), /* 0xa0 */ \ + HANDLE_OPCODE (WASM_OP_F64_SUB), /* 0xa1 */ \ + HANDLE_OPCODE (WASM_OP_F64_MUL), /* 0xa2 */ \ + HANDLE_OPCODE (WASM_OP_F64_DIV), /* 0xa3 */ \ + HANDLE_OPCODE (WASM_OP_F64_MIN), /* 0xa4 */ \ + HANDLE_OPCODE (WASM_OP_F64_MAX), /* 0xa5 */ \ + HANDLE_OPCODE (WASM_OP_F64_COPYSIGN), /* 0xa6 */ \ + HANDLE_OPCODE (WASM_OP_I32_WRAP_I64), /* 0xa7 */ \ + HANDLE_OPCODE (WASM_OP_I32_TRUNC_S_F32), /* 0xa8 */ \ + HANDLE_OPCODE (WASM_OP_I32_TRUNC_U_F32), /* 0xa9 */ \ + HANDLE_OPCODE (WASM_OP_I32_TRUNC_S_F64), /* 0xaa */ \ + HANDLE_OPCODE (WASM_OP_I32_TRUNC_U_F64), /* 0xab */ \ + HANDLE_OPCODE (WASM_OP_I64_EXTEND_S_I32), /* 0xac */ \ + HANDLE_OPCODE (WASM_OP_I64_EXTEND_U_I32), /* 0xad */ \ + HANDLE_OPCODE (WASM_OP_I64_TRUNC_S_F32), /* 0xae */ \ + HANDLE_OPCODE (WASM_OP_I64_TRUNC_U_F32), /* 0xaf */ \ + HANDLE_OPCODE (WASM_OP_I64_TRUNC_S_F64), /* 0xb0 */ \ + HANDLE_OPCODE (WASM_OP_I64_TRUNC_U_F64), /* 0xb1 */ \ + HANDLE_OPCODE (WASM_OP_F32_CONVERT_S_I32), /* 0xb2 */ \ + HANDLE_OPCODE (WASM_OP_F32_CONVERT_U_I32), /* 0xb3 */ \ + HANDLE_OPCODE (WASM_OP_F32_CONVERT_S_I64), /* 0xb4 */ \ + HANDLE_OPCODE (WASM_OP_F32_CONVERT_U_I64), /* 0xb5 */ \ + HANDLE_OPCODE (WASM_OP_F32_DEMOTE_F64), /* 0xb6 */ \ + HANDLE_OPCODE (WASM_OP_F64_CONVERT_S_I32), /* 0xb7 */ \ + HANDLE_OPCODE (WASM_OP_F64_CONVERT_U_I32), /* 0xb8 */ \ + HANDLE_OPCODE (WASM_OP_F64_CONVERT_S_I64), /* 0xb9 */ \ + HANDLE_OPCODE (WASM_OP_F64_CONVERT_U_I64), /* 0xba */ \ + HANDLE_OPCODE (WASM_OP_F64_PROMOTE_F32), /* 0xbb */ \ + HANDLE_OPCODE (WASM_OP_I32_REINTERPRET_F32), /* 0xbc */ \ + HANDLE_OPCODE (WASM_OP_I64_REINTERPRET_F64), /* 0xbd */ \ + HANDLE_OPCODE (WASM_OP_F32_REINTERPRET_I32), /* 0xbe */ \ + HANDLE_OPCODE (WASM_OP_F64_REINTERPRET_I64), /* 0xbf */ \ + HANDLE_OPCODE (WASM_OP_I32_EXTEND8_S), /* 0xc0 */ \ + HANDLE_OPCODE (WASM_OP_I32_EXTEND16_S), /* 0xc1 */ \ + HANDLE_OPCODE (WASM_OP_I64_EXTEND8_S), /* 0xc2 */ \ + HANDLE_OPCODE (WASM_OP_I64_EXTEND16_S), /* 0xc3 */ \ + HANDLE_OPCODE (WASM_OP_I64_EXTEND32_S), /* 0xc4 */ \ + HANDLE_OPCODE (WASM_OP_DROP_64), /* 0xc5 */ \ + HANDLE_OPCODE (WASM_OP_SELECT_64), /* 0xc6 */ \ + HANDLE_OPCODE (EXT_OP_GET_LOCAL_FAST), /* 0xc7 */ \ + HANDLE_OPCODE (EXT_OP_SET_LOCAL_FAST_I64), /* 0xc8 */ \ + HANDLE_OPCODE (EXT_OP_SET_LOCAL_FAST), /* 0xc9 */ \ + HANDLE_OPCODE (EXT_OP_TEE_LOCAL_FAST), /* 0xca */ \ + HANDLE_OPCODE (EXT_OP_TEE_LOCAL_FAST_I64), /* 0xcb */ \ + HANDLE_OPCODE (EXT_OP_COPY_STACK_TOP), /* 0xcc */ \ + HANDLE_OPCODE (EXT_OP_COPY_STACK_TOP_I64), /* 0xcd */ \ + HANDLE_OPCODE (EXT_OP_COPY_STACK_VALUES), /* 0xce */ \ + HANDLE_OPCODE (EXT_OP_BLOCK), /* 0xcf */ \ + HANDLE_OPCODE (EXT_OP_LOOP), /* 0xd0 */ \ + HANDLE_OPCODE (EXT_OP_IF), /* 0xd1 */ \ + HANDLE_OPCODE (WASM_OP_IMPDEP), /* 0xd2 */ \ +}; \ +do { \ + _name[WASM_OP_MISC_PREFIX] = \ + HANDLE_OPCODE (WASM_OP_MISC_PREFIX); /* 0xfc */ \ + DEF_ATOMIC_PREFIX_HANDLE(_name) \ +} while (0) +#endif /* end of _WASM_OPCODE_H */ diff --git a/wamr/core/iwasm/interpreter/wasm_runtime.c b/wamr/core/iwasm/interpreter/wasm_runtime.c new file mode 100644 index 0000000..f139ae3 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_runtime.c @@ -0,0 +1,1930 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_runtime.h" +#include "wasm_loader.h" +#include "wasm_interp.h" +#include "bh_common.h" +#include "bh_log.h" +#include "mem_alloc.h" +#include "../common/wasm_runtime_common.h" +#if WASM_ENABLE_SHARED_MEMORY != 0 +#include "../common/wasm_shared_memory.h" +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) { + snprintf(error_buf, error_buf_size, + "WASM module instantiate failed: %s", string); + } +} + +WASMModule* +wasm_load(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size) +{ + return wasm_loader_load(buf, size, error_buf, error_buf_size); +} + +WASMModule* +wasm_load_from_sections(WASMSection *section_list, + char *error_buf, uint32_t error_buf_size) +{ + return wasm_loader_load_from_sections(section_list, + error_buf, error_buf_size); +} + +void +wasm_unload(WASMModule *module) +{ + wasm_loader_unload(module); +} + +static void * +runtime_malloc(uint64 size, char *error_buf, uint32 error_buf_size) +{ + void *mem; + + if (size >= UINT32_MAX + || !(mem = wasm_runtime_malloc((uint32)size))) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + return NULL; + } + + memset(mem, 0, (uint32)size); + return mem; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static WASMModuleInstance * +get_sub_module_inst(const WASMModuleInstance *parent_module_inst, + const WASMModule *sub_module) +{ + bh_list *sub_module_inst_list = parent_module_inst->sub_module_inst_list; + WASMSubModInstNode *node = bh_list_first_elem(sub_module_inst_list); + + while (node && sub_module != node->module_inst->module) { + node = bh_list_elem_next(node); + } + return node ? node->module_inst : NULL; +} +#endif + +/** + * Destroy memory instances. + */ +static void +memories_deinstantiate(WASMModuleInstance *module_inst, + WASMMemoryInstance **memories, + uint32 count) +{ + uint32 i; + if (memories) { + for (i = 0; i < count; i++) + if (memories[i]) { +#if WASM_ENABLE_MULTI_MODULE != 0 + if (memories[i]->owner != module_inst) + continue; +#endif +#if WASM_ENABLE_SHARED_MEMORY != 0 + os_mutex_destroy(&memories[0]->mem_lock); + if (memories[i]->is_shared) { + int32 ref_count = + shared_memory_dec_reference( + (WASMModuleCommon *)module_inst->module); + bh_assert(ref_count >= 0); + + /* if the reference count is not zero, + don't free the memory */ + if (ref_count > 0) + continue; + } +#endif + if (memories[i]->heap_handle) { + mem_allocator_destroy(memories[i]->heap_handle); + memories[i]->heap_handle = NULL; + } + wasm_runtime_free(memories[i]); + } + wasm_runtime_free(memories); + } + (void)module_inst; +} + +static WASMMemoryInstance* +memory_instantiate(WASMModuleInstance *module_inst, + uint32 num_bytes_per_page, + uint32 init_page_count, uint32 max_page_count, + uint32 heap_size, uint32 flags, + char *error_buf, uint32 error_buf_size) +{ + WASMModule *module = module_inst->module; + WASMMemoryInstance *memory; + uint64 total_size, memory_data_size; + uint32 heap_offset = num_bytes_per_page * init_page_count; + uint32 inc_page_count, aux_heap_base, global_idx; + uint32 bytes_of_last_page, bytes_to_page_end; + uint8 *global_addr; + +#if WASM_ENABLE_SHARED_MEMORY != 0 + bool is_shared_memory = flags & 0x02 ? true : false; + + /* shared memory */ + if (is_shared_memory) { + WASMSharedMemNode *node = + wasm_module_get_shared_memory( + (WASMModuleCommon *)module_inst->module); + /* If the memory of this module has been instantiated, + return the memory instance directly */ + if (node) { + uint32 ref_count; + ref_count = shared_memory_inc_reference( + (WASMModuleCommon *)module_inst->module); + bh_assert(ref_count > 0); + memory = (WASMMemoryInstance *)shared_memory_get_memory_inst(node); + bh_assert(memory); + + (void)ref_count; + return memory; + } + } +#endif /* end of WASM_ENABLE_SHARED_MEMORY */ + + if (heap_size > 0 + && module_inst->module->malloc_function != (uint32)-1 + && module_inst->module->free_function != (uint32)-1) { + /* Disable app heap, use malloc/free function exported + by wasm app to allocate/free memory instead */ + heap_size = 0; + } + + if (init_page_count == max_page_count && init_page_count == 1) { + /* If only one page and at most one page, we just append + the app heap to the end of linear memory, enlarge the + num_bytes_per_page, and don't change the page count*/ + heap_offset = num_bytes_per_page; + num_bytes_per_page += heap_size; + if (num_bytes_per_page < heap_size) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return NULL; + } + } + else if (heap_size > 0) { + if (module->aux_heap_base_global_index != (uint32)-1 + && module->aux_heap_base < num_bytes_per_page + * init_page_count) { + /* Insert app heap before __heap_base */ + aux_heap_base = module->aux_heap_base; + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + inc_page_count = (heap_size - bytes_to_page_end + + num_bytes_per_page - 1) / num_bytes_per_page; + heap_offset = aux_heap_base; + aux_heap_base += heap_size; + + bytes_of_last_page = aux_heap_base % num_bytes_per_page; + if (bytes_of_last_page == 0) + bytes_of_last_page = num_bytes_per_page; + bytes_to_page_end = num_bytes_per_page - bytes_of_last_page; + if (bytes_to_page_end < 1 * BH_KB) { + aux_heap_base += 1 * BH_KB; + inc_page_count++; + } + + /* Adjust __heap_base global value */ + global_idx = module->aux_heap_base_global_index; + global_addr = module_inst->global_data + + module_inst->globals[global_idx].data_offset; + *(uint32 *)global_addr = aux_heap_base; + LOG_VERBOSE("Reset __heap_base global to %u", aux_heap_base); + } + else { + /* Insert app heap before new page */ + inc_page_count = (heap_size + num_bytes_per_page - 1) + / num_bytes_per_page; + heap_offset = num_bytes_per_page * init_page_count; + heap_size = num_bytes_per_page * inc_page_count; + if (heap_size > 0) + heap_size -= 1 * BH_KB; + } + init_page_count += inc_page_count; + max_page_count += inc_page_count; + if (init_page_count > 65536) { + set_error_buf(error_buf, error_buf_size, + "memory size must be at most 65536 pages (4GiB)"); + return NULL; + } + if (max_page_count > 65536) + max_page_count = 65536; + } + + LOG_VERBOSE("Memory instantiate:"); + LOG_VERBOSE(" page bytes: %u, init pages: %u, max pages: %u", + num_bytes_per_page, init_page_count, max_page_count); + LOG_VERBOSE(" heap offset: %u, heap size: %d\n", heap_offset, heap_size); + + memory_data_size = (uint64)num_bytes_per_page * init_page_count; +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (is_shared_memory) { + /* Allocate max page for shared memory */ + memory_data_size = (uint64)num_bytes_per_page * max_page_count; + } +#endif + + total_size = offsetof(WASMMemoryInstance, memory_data) + + memory_data_size; + + /* Allocate memory space, addr data and global data */ + if (!(memory = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + memory->module_type = Wasm_Module_Bytecode; + memory->num_bytes_per_page = num_bytes_per_page; + memory->cur_page_count = init_page_count; + memory->max_page_count = max_page_count; + + memory->heap_data = memory->memory_data + heap_offset; + memory->heap_data_end = memory->heap_data + heap_size; + memory->memory_data_end = memory->memory_data + (uint32)memory_data_size; + + bh_assert(memory->memory_data_end - (uint8*)memory == (uint32)total_size); + + /* Initialize heap */ + if (heap_size > 0 + && !(memory->heap_handle = + mem_allocator_create(memory->heap_data, heap_size))) { + set_error_buf(error_buf, error_buf_size, "init app heap failed"); + goto fail1; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (0 != os_mutex_init(&memory->mem_lock)) { + set_error_buf(error_buf, error_buf_size, "init mutex failed"); + goto fail2; + } + if (is_shared_memory) { + memory->is_shared = true; + if (!shared_memory_set_memory_inst( + (WASMModuleCommon *)module_inst->module, + (WASMMemoryInstanceCommon *)memory)) { + set_error_buf(error_buf, error_buf_size, + "allocate memory failed"); + goto fail3; + } + } +#endif + return memory; +#if WASM_ENABLE_SHARED_MEMORY != 0 +fail3: + os_mutex_destroy(&memory->mem_lock); +fail2: + if (heap_size > 0) + mem_allocator_destroy(memory->heap_handle); +#endif +fail1: + wasm_runtime_free(memory); + return NULL; +} + +/** + * Instantiate memories in a module. + */ +static WASMMemoryInstance ** +memories_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 heap_size, char *error_buf, uint32 error_buf_size) +{ + WASMImport *import; + uint32 mem_index = 0, i, memory_count = + module->import_memory_count + module->memory_count; + uint64 total_size; + WASMMemoryInstance **memories, *memory; + + total_size = sizeof(WASMMemoryInstance*) * (uint64)memory_count; + + if (!(memories = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + /* instantiate memories from import section */ + import = module->import_memories; + for (i = 0; i < module->import_memory_count; i++, import++) { + uint32 num_bytes_per_page = import->u.memory.num_bytes_per_page; + uint32 init_page_count = import->u.memory.init_page_count; + uint32 max_page_count = import->u.memory.max_page_count; + uint32 flags = import->u.memory.flags; + uint32 actual_heap_size = heap_size; + +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMMemoryInstance *memory_inst_linked = NULL; + if (import->u.memory.import_module != NULL) { + LOG_DEBUG("(%s, %s) is a memory of a sub-module", + import->u.memory.module_name, + import->u.memory.field_name); + + // TODO: how about native memory ? + WASMModuleInstance *module_inst_linked = + get_sub_module_inst( + module_inst, + import->u.memory.import_module); + bh_assert(module_inst_linked); + + memory_inst_linked = + wasm_lookup_memory(module_inst_linked, + import->u.memory.field_name); + bh_assert(memory_inst_linked); + + memories[mem_index++] = memory_inst_linked; + memory = memory_inst_linked; + } + else +#endif + { + if (!(memory = memories[mem_index++] = memory_instantiate( + module_inst, num_bytes_per_page, init_page_count, + max_page_count, actual_heap_size, flags, + error_buf, error_buf_size))) { + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } + } + } + + /* instantiate memories from memory section */ + for (i = 0; i < module->memory_count; i++) { + if (!(memory = memories[mem_index++] = + memory_instantiate(module_inst, + module->memories[i].num_bytes_per_page, + module->memories[i].init_page_count, + module->memories[i].max_page_count, + heap_size, module->memories[i].flags, + error_buf, error_buf_size))) { + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } +#if WASM_ENABLE_MULTI_MODULE != 0 + memory->owner = module_inst; +#endif + } + + if (mem_index == 0) { + /** + * no import memory and define memory, but still need heap + * for wasm code + */ + if (!(memory = memories[mem_index++] = + memory_instantiate(module_inst, 0, 0, 0, heap_size, 0, + error_buf, error_buf_size))) { + memories_deinstantiate(module_inst, memories, memory_count); + return NULL; + } + } + + bh_assert(mem_index == memory_count); + (void)module_inst; + return memories; +} + +/** + * Destroy table instances. + */ +static void +tables_deinstantiate(WASMTableInstance **tables, uint32 count) +{ + uint32 i; + if (tables) { + for (i = 0; i < count; i++) + if (tables[i]) + wasm_runtime_free(tables[i]); + wasm_runtime_free(tables); + } +} + +/** + * Instantiate tables in a module. + */ +static WASMTableInstance ** +tables_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + char *error_buf, uint32 error_buf_size) +{ + WASMImport *import; + uint32 table_index = 0, i, table_count = + module->import_table_count + module->table_count; + uint64 total_size = sizeof(WASMTableInstance*) * (uint64)table_count; + WASMTableInstance **tables, *table; + + if (!(tables = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + /* instantiate tables from import section */ + import = module->import_tables; + for (i = 0; i < module->import_table_count; i++, import++) { +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMTableInstance *table_inst_linked = NULL; + WASMModuleInstance *module_inst_linked = NULL; + if (import->u.table.import_module) { + LOG_DEBUG("(%s, %s) is a table of a sub-module", + import->u.table.module_name, + import->u.memory.field_name); + + module_inst_linked = + get_sub_module_inst(module_inst, import->u.table.import_module); + bh_assert(module_inst_linked); + + table_inst_linked = wasm_lookup_table(module_inst_linked, + import->u.table.field_name); + bh_assert(table_inst_linked); + + total_size = offsetof(WASMTableInstance, base_addr); + } + else +#endif + { + /* it is a built-in table */ + total_size = offsetof(WASMTableInstance, base_addr) + + sizeof(uint32) * (uint64)import->u.table.init_size; + } + + if (!(table = tables[table_index++] = runtime_malloc + (total_size, error_buf, error_buf_size))) { + tables_deinstantiate(tables, table_count); + return NULL; + } + + /* Set all elements to -1 to mark them as uninitialized elements */ + memset(table, -1, (uint32)total_size); +#if WASM_ENABLE_MULTI_MODULE != 0 + table->table_inst_linked = table_inst_linked; + if (table_inst_linked != NULL) { + table->elem_type = table_inst_linked->elem_type; + table->cur_size = table_inst_linked->cur_size; + table->max_size = table_inst_linked->max_size; + } + else +#endif + { + table->elem_type = import->u.table.elem_type; + table->cur_size = import->u.table.init_size; + table->max_size = import->u.table.max_size; + } + } + + /* instantiate tables from table section */ + for (i = 0; i < module->table_count; i++) { + total_size = offsetof(WASMTableInstance, base_addr) + + sizeof(uint32) * (uint64)module->tables[i].init_size; + if (!(table = tables[table_index++] = runtime_malloc + (total_size, error_buf, error_buf_size))) { + tables_deinstantiate(tables, table_count); + return NULL; + } + + /* Set all elements to -1 to mark them as uninitialized elements */ + memset(table, -1, (uint32)total_size); + table->elem_type = module->tables[i].elem_type; + table->cur_size = module->tables[i].init_size; + table->max_size = module->tables[i].max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + table->table_inst_linked = NULL; +#endif + } + + bh_assert(table_index == table_count); + (void)module_inst; + return tables; +} + +/** + * Destroy function instances. + */ +static void +functions_deinstantiate(WASMFunctionInstance *functions, uint32 count) +{ + if (functions) { + wasm_runtime_free(functions); + } +} + +/** + * Instantiate functions in a module. + */ +static WASMFunctionInstance * +functions_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + char *error_buf, uint32 error_buf_size) +{ + WASMImport *import; + uint32 i, function_count = + module->import_function_count + module->function_count; + uint64 total_size = sizeof(WASMFunctionInstance) * (uint64)function_count; + WASMFunctionInstance *functions, *function; + + if (!(functions = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + /* instantiate functions from import section */ + function = functions; + import = module->import_functions; + for (i = 0; i < module->import_function_count; i++, import++) { + function->is_import_func = true; + +#if WASM_ENABLE_MULTI_MODULE != 0 + if (import->u.function.import_module) { + LOG_DEBUG("(%s, %s) is a function of a sub-module", + import->u.function.module_name, + import->u.function.field_name); + + function->import_module_inst = + get_sub_module_inst(module_inst, + import->u.function.import_module); + bh_assert(function->import_module_inst); + + WASMFunction *function_linked = + import->u.function.import_func_linked; + + function->u.func = function_linked; + function->import_func_inst = + wasm_lookup_function(function->import_module_inst, + import->u.function.field_name, + NULL); + bh_assert(function->import_func_inst); + + function->param_cell_num = function->u.func->param_cell_num; + function->ret_cell_num = function->u.func->ret_cell_num; + function->local_cell_num = function->u.func->local_cell_num; + function->param_count = + (uint16)function->u.func->func_type->param_count; + function->local_count = (uint16)function->u.func->local_count; + function->param_types = function->u.func->func_type->types; + function->local_types = function->u.func->local_types; + function->local_offsets = function->u.func->local_offsets; +#if WASM_ENABLE_FAST_INTERP != 0 + function->const_cell_num = function->u.func->const_cell_num; +#endif + } + else +#endif /* WASM_ENABLE_MULTI_MODULE */ + { + LOG_DEBUG("(%s, %s) is a function of native", + import->u.function.module_name, + import->u.function.field_name); + function->u.func_import = &import->u.function; + function->param_cell_num = + import->u.function.func_type->param_cell_num; + function->ret_cell_num = + import->u.function.func_type->ret_cell_num; + function->param_count = + (uint16)function->u.func_import->func_type->param_count; + function->param_types = function->u.func_import->func_type->types; + function->local_cell_num = 0; + function->local_count = 0; + function->local_types = NULL; + } + + function++; + } + + /* instantiate functions from function section */ + for (i = 0; i < module->function_count; i++) { + function->is_import_func = false; + function->u.func = module->functions[i]; + + function->param_cell_num = function->u.func->param_cell_num; + function->ret_cell_num = function->u.func->ret_cell_num; + function->local_cell_num = function->u.func->local_cell_num; + + function->param_count = (uint16)function->u.func->func_type->param_count; + function->local_count = (uint16)function->u.func->local_count; + function->param_types = function->u.func->func_type->types; + function->local_types = function->u.func->local_types; + + function->local_offsets = function->u.func->local_offsets; + +#if WASM_ENABLE_FAST_INTERP != 0 + function->const_cell_num = function->u.func->const_cell_num; +#endif + + function++; + } + + bh_assert((uint32)(function - functions) == function_count); + (void)module_inst; + return functions; +} + +/** + * Destroy global instances. + */ +static void +globals_deinstantiate(WASMGlobalInstance *globals) +{ + if (globals) + wasm_runtime_free(globals); +} + +/** + * init_expr->u ==> init_val + */ +static bool +parse_init_expr(const InitializerExpression *init_expr, + const WASMGlobalInstance *global_inst_array, + uint32 boundary, WASMValue *init_val) +{ + if (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + uint32 target_global_index = init_expr->u.global_index; + /** + * a global gets the init value of another global + */ + if (target_global_index >= boundary) { + LOG_DEBUG("unknown target global, %d", target_global_index); + return false; + } + + /** + * it will work if using WASMGlobalImport and WASMGlobal in + * WASMModule, but will have to face complicated cases + * + * but we still have no sure the target global has been + * initialized before + */ + WASMValue target_value = + global_inst_array[target_global_index].initial_value; + bh_memcpy_s(init_val, sizeof(WASMValue), &target_value, + sizeof(target_value)); + } + else { + bh_memcpy_s(init_val, sizeof(WASMValue), &init_expr->u, + sizeof(init_expr->u)); + } + return true; +} + +/** + * Instantiate globals in a module. + */ +static WASMGlobalInstance * +globals_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 *p_global_data_size, char *error_buf, + uint32 error_buf_size) +{ + WASMImport *import; + uint32 global_data_offset = 0; + uint32 i, global_count = + module->import_global_count + module->global_count; + uint64 total_size = sizeof(WASMGlobalInstance) * (uint64)global_count; + WASMGlobalInstance *globals, *global; + + if (!(globals = runtime_malloc(total_size, + error_buf, error_buf_size))) { + return NULL; + } + + /* instantiate globals from import section */ + global = globals; + import = module->import_globals; + for (i = 0; i < module->import_global_count; i++, import++) { + WASMGlobalImport *global_import = &import->u.global; + global->type = global_import->type; + global->is_mutable = global_import->is_mutable; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (global_import->import_module) { + WASMModuleInstance *sub_module_inst = get_sub_module_inst( + module_inst, global_import->import_module); + bh_assert(sub_module_inst); + + WASMGlobalInstance *global_inst_linked = + wasm_lookup_global(sub_module_inst, global_import->field_name); + bh_assert(global_inst_linked); + + global->import_global_inst = global_inst_linked; + global->import_module_inst = sub_module_inst; + + /** + * although, actually don't need initial_value for an imported + * global, we keep it here like a place holder because of + * global-data and + * (global $g2 i32 (global.get $g1)) + */ + WASMGlobal *linked_global = global_import->import_global_linked; + InitializerExpression *linked_init_expr = + &(linked_global->init_expr); + + bool ret = parse_init_expr( + linked_init_expr, + sub_module_inst->globals, + sub_module_inst->global_count, &(global->initial_value)); + if (!ret) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return NULL; + } + } + else +#endif + { + /* native globals share their initial_values in one module */ + global->initial_value = global_import->global_data_linked; + } + global->data_offset = global_data_offset; + global_data_offset += wasm_value_type_size(global->type); + + global++; + } + + /* instantiate globals from global section */ + for (i = 0; i < module->global_count; i++) { + bool ret = false; + uint32 global_count = + module->import_global_count + module->global_count; + InitializerExpression *init_expr = &(module->globals[i].init_expr); + + global->type = module->globals[i].type; + global->is_mutable = module->globals[i].is_mutable; + global->data_offset = global_data_offset; + + global_data_offset += wasm_value_type_size(global->type); + + /** + * first init, it might happen that the target global instance + * has not been initialize yet + */ + if (init_expr->init_expr_type != INIT_EXPR_TYPE_GET_GLOBAL) { + ret = + parse_init_expr(init_expr, globals, global_count, + &(global->initial_value)); + if (!ret) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return NULL; + } + } + global++; + } + + bh_assert((uint32)(global - globals) == global_count); + *p_global_data_size = global_data_offset; + (void)module_inst; + return globals; +} + +static bool +globals_instantiate_fix(WASMGlobalInstance *globals, + const WASMModule *module, + char *error_buf, uint32 error_buf_size) +{ + WASMGlobalInstance *global = globals; + uint32 i; + uint32 global_count = module->import_global_count + module->global_count; + + /** + * second init, only target global instances from global + * (ignore import_global) + * to fix skipped init_value in the previous round + * hope two rounds are enough but how about a chain ? + */ + for (i = 0; i < module->global_count; i++) { + bool ret = false; + InitializerExpression *init_expr = &module->globals[i].init_expr; + + if (init_expr->init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + ret = parse_init_expr(init_expr, globals, global_count, + &global->initial_value); + if (!ret) { + set_error_buf(error_buf, error_buf_size, "unknown global"); + return false; + } + } + + global++; + } + return true; +} + +/** + * Return export function count in module export section. + */ +static uint32 +get_export_count(const WASMModule *module, uint8 kind) +{ + WASMExport *export = module->exports; + uint32 count = 0, i; + + for (i = 0; i < module->export_count; i++, export++) + if (export->kind == kind) + count++; + + return count; +} + +/** + * Destroy export function instances. + */ +static void +export_functions_deinstantiate(WASMExportFuncInstance *functions) +{ + if (functions) + wasm_runtime_free(functions); +} + +/** + * Instantiate export functions in a module. + */ +static WASMExportFuncInstance* +export_functions_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_func_count, + char *error_buf, uint32 error_buf_size) +{ + WASMExportFuncInstance *export_funcs, *export_func; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = sizeof(WASMExportFuncInstance) * (uint64)export_func_count; + + if (!(export_func = export_funcs = runtime_malloc + (total_size, error_buf, error_buf_size))) { + return NULL; + } + + for (i = 0; i < module->export_count; i++, export++) + if (export->kind == EXPORT_KIND_FUNC) { + export_func->name = export->name; + export_func->function = &module_inst->functions[export->index]; + export_func++; + } + + bh_assert((uint32)(export_func - export_funcs) == export_func_count); + return export_funcs; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static void +export_globals_deinstantiate(WASMExportGlobInstance *globals) +{ + if (globals) + wasm_runtime_free(globals); +} + +static WASMExportGlobInstance * +export_globals_instantiate(const WASMModule *module, + WASMModuleInstance *module_inst, + uint32 export_glob_count, char *error_buf, + uint32 error_buf_size) +{ + WASMExportGlobInstance *export_globals, *export_global; + WASMExport *export = module->exports; + uint32 i; + uint64 total_size = sizeof(WASMExportGlobInstance) * (uint64)export_glob_count; + + if (!(export_global = export_globals = runtime_malloc + (total_size, error_buf, error_buf_size))) { + return NULL; + } + + for (i = 0; i < module->export_count; i++, export++) + if (export->kind == EXPORT_KIND_GLOBAL) { + export_global->name = export->name; + export_global->global = &module_inst->globals[export->index]; + export_global++; + } + + bh_assert((uint32)(export_global - export_globals) == export_glob_count); + return export_globals; +} +#endif + +static bool +execute_post_inst_function(WASMModuleInstance *module_inst) +{ + WASMFunctionInstance *post_inst_func = NULL; + WASMType *post_inst_func_type; + uint32 i; + + for (i = 0; i < module_inst->export_func_count; i++) + if (!strcmp(module_inst->export_functions[i].name, "__post_instantiate")) { + post_inst_func = module_inst->export_functions[i].function; + break; + } + + if (!post_inst_func) + /* Not found */ + return true; + + post_inst_func_type = post_inst_func->u.func->func_type; + if (post_inst_func_type->param_count != 0 + || post_inst_func_type->result_count != 0) + /* Not a valid function type, ignore it */ + return true; + + return wasm_create_exec_env_and_call_function(module_inst, post_inst_func, + 0, NULL); +} + +#if WASM_ENABLE_BULK_MEMORY != 0 +static bool +execute_memory_init_function(WASMModuleInstance *module_inst) +{ + WASMFunctionInstance *memory_init_func = NULL; + WASMType *memory_init_func_type; + uint32 i; + + for (i = 0; i < module_inst->export_func_count; i++) + if (!strcmp(module_inst->export_functions[i].name, "__wasm_call_ctors")) { + memory_init_func = module_inst->export_functions[i].function; + break; + } + + if (!memory_init_func) + /* Not found */ + return true; + + memory_init_func_type = memory_init_func->u.func->func_type; + if (memory_init_func_type->param_count != 0 + || memory_init_func_type->result_count != 0) + /* Not a valid function type, ignore it */ + return true; + + return wasm_create_exec_env_and_call_function(module_inst, + memory_init_func, + 0, NULL); +} +#endif + +static bool +execute_start_function(WASMModuleInstance *module_inst) +{ + WASMFunctionInstance *func = module_inst->start_function; + + if (!func) + return true; + + bh_assert(!func->is_import_func && func->param_cell_num == 0 + && func->ret_cell_num == 0); + + return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL); +} + +static bool +execute_malloc_function(WASMModuleInstance *module_inst, + WASMFunctionInstance *malloc_func, + uint32 size, uint32 *p_result) +{ + uint32 argv[2]; + bool ret; + + argv[0] = size; + ret = wasm_create_exec_env_and_call_function + (module_inst, malloc_func, 1, argv); + if (ret) + *p_result = argv[0]; + return ret; +} + +static bool +execute_free_function(WASMModuleInstance *module_inst, + WASMFunctionInstance *free_func, + uint32 offset) +{ + uint32 argv[2]; + + argv[0] = offset; + return wasm_create_exec_env_and_call_function + (module_inst, free_func, 1, argv); +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +static bool +sub_module_instantiate(WASMModule *module, WASMModuleInstance *module_inst, + uint32 stack_size, uint32 heap_size, char *error_buf, + uint32 error_buf_size) +{ + bh_list *sub_module_inst_list = module_inst->sub_module_inst_list; + WASMRegisteredModule *sub_module_list_node = + bh_list_first_elem(module->import_module_list); + + while (sub_module_list_node) { + WASMSubModInstNode *sub_module_inst_list_node; + WASMModule *sub_module = (WASMModule*)sub_module_list_node->module; + WASMModuleInstance *sub_module_inst = + wasm_instantiate(sub_module, false, stack_size, heap_size, + error_buf, error_buf_size); + if (!sub_module_inst) { + LOG_DEBUG("instantiate %s failed", + sub_module_list_node->module_name); + return false; + } + + sub_module_inst_list_node = runtime_malloc + (sizeof(WASMSubModInstNode), error_buf, error_buf_size); + if (!sub_module_inst_list_node) { + LOG_DEBUG("Malloc WASMSubModInstNode failed, SZ:%d", + sizeof(WASMSubModInstNode)); + wasm_deinstantiate(sub_module_inst, false); + return false; + } + + sub_module_inst_list_node->module_inst = sub_module_inst; + sub_module_inst_list_node->module_name = + sub_module_list_node->module_name; + bh_list_status ret = + bh_list_insert(sub_module_inst_list, sub_module_inst_list_node); + bh_assert(BH_LIST_SUCCESS == ret); + (void)ret; + + sub_module_list_node = bh_list_elem_next(sub_module_list_node); + } + + return true; +} + +static void +sub_module_deinstantiate(WASMModuleInstance *module_inst) +{ + bh_list *list = module_inst->sub_module_inst_list; + WASMSubModInstNode *node = bh_list_first_elem(list); + while (node) { + WASMSubModInstNode *next_node = bh_list_elem_next(node); + bh_list_remove(list, node); + wasm_deinstantiate(node->module_inst, false); + node = next_node; + } +} +#endif + +/** + * Instantiate module + */ +WASMModuleInstance* +wasm_instantiate(WASMModule *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size) +{ + WASMModuleInstance *module_inst; + WASMGlobalInstance *globals = NULL, *global; + uint32 global_count, global_data_size = 0, i; + uint32 base_offset, length; + uint8 *global_data, *global_data_end; +#if WASM_ENABLE_MULTI_MODULE != 0 + bool ret = false; +#endif + + if (!module) + return NULL; + + /* Check heap size */ + heap_size = align_uint(heap_size, 8); + if (heap_size > APP_HEAP_SIZE_MAX) + heap_size = APP_HEAP_SIZE_MAX; + + /* Allocate the memory */ + if (!(module_inst = runtime_malloc(sizeof(WASMModuleInstance), + error_buf, error_buf_size))) { + return NULL; + } + + module_inst->module = module; + +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->sub_module_inst_list = + &module_inst->sub_module_inst_list_head; + ret = sub_module_instantiate(module, module_inst, stack_size, heap_size, + error_buf, error_buf_size); + if (!ret) { + LOG_DEBUG("build a sub module list failed"); + wasm_deinstantiate(module_inst, false); + return NULL; + } +#endif + + /* Instantiate global firstly to get the mutable data size */ + global_count = module->import_global_count + module->global_count; + if (global_count && !(globals = globals_instantiate( + module, + module_inst, + &global_data_size, error_buf, error_buf_size))) { + wasm_deinstantiate(module_inst, false); + return NULL; + } + module_inst->global_count = global_count; + module_inst->globals = globals; + + module_inst->memory_count = + module->import_memory_count + module->memory_count; + module_inst->table_count = + module->import_table_count + module->table_count; + module_inst->function_count = + module->import_function_count + module->function_count; + + /* export */ + module_inst->export_func_count = get_export_count(module, EXPORT_KIND_FUNC); +#if WASM_ENABLE_MULTI_MODULE != 0 + module_inst->export_tab_count = get_export_count(module, EXPORT_KIND_TABLE); + module_inst->export_mem_count = get_export_count(module, EXPORT_KIND_MEMORY); + module_inst->export_glob_count = get_export_count(module, EXPORT_KIND_GLOBAL); +#endif + + if (global_count > 0) { + if (!(module_inst->global_data = runtime_malloc + (global_data_size, error_buf, error_buf_size))) { + wasm_deinstantiate(module_inst, false); + return NULL; + } + } + + /* Instantiate memories/tables/functions */ + if ((module_inst->memory_count > 0 + && !(module_inst->memories = + memories_instantiate(module, + module_inst, + heap_size, error_buf, error_buf_size))) + || (module_inst->table_count > 0 + && !(module_inst->tables = + tables_instantiate(module, + module_inst, + error_buf, error_buf_size))) + || (module_inst->function_count > 0 + && !(module_inst->functions = + functions_instantiate(module, + module_inst, + error_buf, error_buf_size))) + || (module_inst->export_func_count > 0 + && !(module_inst->export_functions = export_functions_instantiate( + module, module_inst, module_inst->export_func_count, + error_buf, error_buf_size))) +#if WASM_ENABLE_MULTI_MODULE != 0 + || (module_inst->export_glob_count > 0 + && !(module_inst->export_globals = export_globals_instantiate( + module, module_inst, module_inst->export_glob_count, + error_buf, error_buf_size))) +#endif + ) { + wasm_deinstantiate(module_inst, false); + return NULL; + } + + if (global_count > 0) { + /** + * since there might be some globals are not instantiate the first + * instantiate round + */ + if (!globals_instantiate_fix(globals, module, + error_buf, error_buf_size)) { + wasm_deinstantiate(module_inst, false); + return NULL; + } + + /* Initialize the global data */ + global_data = module_inst->global_data; + global_data_end = global_data + global_data_size; + global = globals; + for (i = 0; i < global_count; i++, global++) { + switch (global->type) { + case VALUE_TYPE_I32: + case VALUE_TYPE_F32: + *(int32*)global_data = global->initial_value.i32; + global_data += sizeof(int32); + break; + case VALUE_TYPE_I64: + case VALUE_TYPE_F64: + bh_memcpy_s(global_data, (uint32)(global_data_end - global_data), + &global->initial_value.i64, sizeof(int64)); + global_data += sizeof(int64); + break; + default: + bh_assert(0); + } + } + bh_assert(global_data == global_data_end); + } + + /* Initialize the memory data with data segment section */ + module_inst->default_memory = + module_inst->memory_count ? module_inst->memories[0] : NULL; + + for (i = 0; i < module->data_seg_count; i++) { + WASMMemoryInstance *memory = NULL; + uint8 *memory_data = NULL; + uint32 memory_size = 0; + WASMDataSeg *data_seg = module->data_segments[i]; + +#if WASM_ENABLE_BULK_MEMORY != 0 + if (data_seg->is_passive) + continue; +#endif + + /* has check it in loader */ + memory = module_inst->memories[data_seg->memory_index]; + bh_assert(memory); + + memory_data = memory->memory_data; + bh_assert(memory_data); + + memory_size = memory->num_bytes_per_page * memory->cur_page_count; + + bh_assert(data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + || data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL); + + if (data_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL) { + bh_assert(data_seg->base_offset.u.global_index < global_count + && globals[data_seg->base_offset.u.global_index].type + == VALUE_TYPE_I32); + data_seg->base_offset.u.i32 = + globals[data_seg->base_offset.u.global_index] + .initial_value.i32; + } + + /* check offset since length might negative */ + base_offset = (uint32)data_seg->base_offset.u.i32; + if (base_offset > memory_size) { + LOG_DEBUG("base_offset(%d) > memory_size(%d)", base_offset, + memory_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + wasm_deinstantiate(module_inst, false); + return NULL; + } + + /* check offset + length(could be zero) */ + length = data_seg->data_length; + if (base_offset + length > memory_size) { + LOG_DEBUG("base_offset(%d) + length(%d) > memory_size(%d)", + base_offset, length, memory_size); + set_error_buf(error_buf, error_buf_size, + "data segment does not fit"); + wasm_deinstantiate(module_inst, false); + return NULL; + } + + bh_memcpy_s(memory_data + base_offset, memory_size - base_offset, + data_seg->data, length); + } + + /* Initialize the table data with table segment section */ + module_inst->default_table = + module_inst->table_count ? module_inst->tables[0] : NULL; + for (i = 0; i < module->table_seg_count; i++) { + WASMTableSeg *table_seg = module->table_segments + i; + /* has check it in loader */ + WASMTableInstance *table = module_inst->tables[table_seg->table_index]; + bh_assert(table); + + uint32 *table_data = (uint32 *)table->base_addr; +#if WASM_ENABLE_MULTI_MODULE != 0 + table_data = table->table_inst_linked + ? (uint32 *)table->table_inst_linked->base_addr + : table_data; +#endif + bh_assert(table_data); + + /* init vec(funcidx) */ + bh_assert(table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_I32_CONST + || table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL); + + if (table_seg->base_offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL) { + bh_assert(table_seg->base_offset.u.global_index < global_count + && globals[table_seg->base_offset.u.global_index].type + == VALUE_TYPE_I32); + table_seg->base_offset.u.i32 = + globals[table_seg->base_offset.u.global_index].initial_value.i32; + } + + /* check offset since length might negative */ + if ((uint32)table_seg->base_offset.u.i32 > table->cur_size) { + LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", + table_seg->base_offset.u.i32, table->cur_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + wasm_deinstantiate(module_inst, false); + return NULL; + } + + /* check offset + length(could be zero) */ + length = table_seg->function_count; + if ((uint32)table_seg->base_offset.u.i32 + length > table->cur_size) { + LOG_DEBUG("base_offset(%d) + length(%d)> table->cur_size(%d)", + table_seg->base_offset.u.i32, length, table->cur_size); + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + wasm_deinstantiate(module_inst, false); + return NULL; + } + + /** + * Check function index in the current module inst for now. + * will check the linked table inst owner in future. + * so loader check is enough + */ + bh_memcpy_s( + table_data + table_seg->base_offset.u.i32, + (uint32)((table->cur_size - (uint32)table_seg->base_offset.u.i32) + * sizeof(uint32)), + table_seg->func_indexes, (uint32)(length * sizeof(uint32))); + } + + /* module instance type */ + module_inst->module_type = Wasm_Module_Bytecode; + + /* Initialize the thread related data */ + if (stack_size == 0) + stack_size = DEFAULT_WASM_STACK_SIZE; +#if WASM_ENABLE_SPEC_TEST != 0 + if (stack_size < 48 *1024) + stack_size = 48 * 1024; +#endif + module_inst->default_wasm_stack_size = stack_size; + + if (module->malloc_function != (uint32)-1) { + module_inst->malloc_function = + &module_inst->functions[module->malloc_function]; + } + + if (module->free_function != (uint32)-1) { + module_inst->free_function = + &module_inst->functions[module->free_function]; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + /* The sub-instance will get the wasi_ctx from main-instance */ + if (!is_sub_inst) { + if (heap_size > 0 + && !wasm_runtime_init_wasi((WASMModuleInstanceCommon*)module_inst, + module->wasi_args.dir_list, + module->wasi_args.dir_count, + module->wasi_args.map_dir_list, + module->wasi_args.map_dir_count, + module->wasi_args.env, + module->wasi_args.env_count, + module->wasi_args.argv, + module->wasi_args.argc, + error_buf, error_buf_size)) { + wasm_deinstantiate(module_inst, false); + return NULL; + } + } +#endif + + if (module->start_function != (uint32)-1) { + /* TODO: fix start function can be import function issue */ + if (module->start_function >= module->import_function_count) + module_inst->start_function = + &module_inst->functions[module->start_function]; + } + + /* Execute __post_instantiate function */ + if (!execute_post_inst_function(module_inst) + || !execute_start_function(module_inst)) { + set_error_buf(error_buf, error_buf_size, + module_inst->cur_exception); + wasm_deinstantiate(module_inst, false); + return NULL; + } + +#if WASM_ENABLE_BULK_MEMORY != 0 +#if WASM_ENABLE_LIBC_WASI != 0 + if (!module->is_wasi_module) { +#endif + /* Only execute the memory init function for main instance because + the data segments will be dropped once initialized. + */ + if (!is_sub_inst) { + if (!execute_memory_init_function(module_inst)) { + set_error_buf(error_buf, error_buf_size, + module_inst->cur_exception); + wasm_deinstantiate(module_inst, false); + return NULL; + } + } +#if WASM_ENABLE_LIBC_WASI != 0 + } +#endif +#endif + + (void)global_data_end; + return module_inst; +} + +void +wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) +{ + if (!module_inst) + return; + +#if WASM_ENABLE_MULTI_MODULE != 0 + sub_module_deinstantiate(module_inst); +#endif + +#if WASM_ENABLE_LIBC_WASI != 0 + /* Destroy wasi resource before freeing app heap, since some fields of + wasi contex are allocated from app heap, and if app heap is freed, + these fields will be set to NULL, we cannot free their internal data + which may allocated from global heap. */ + /* Only destroy wasi ctx in the main module instance */ + if (!is_sub_inst) + wasm_runtime_destroy_wasi((WASMModuleInstanceCommon*)module_inst); +#endif + + if (module_inst->memory_count > 0) + memories_deinstantiate( + module_inst, + module_inst->memories, module_inst->memory_count); + + tables_deinstantiate(module_inst->tables, module_inst->table_count); + functions_deinstantiate(module_inst->functions, module_inst->function_count); + globals_deinstantiate(module_inst->globals); + export_functions_deinstantiate(module_inst->export_functions); +#if WASM_ENABLE_MULTI_MODULE != 0 + export_globals_deinstantiate(module_inst->export_globals); +#endif + + if (module_inst->global_data) + wasm_runtime_free(module_inst->global_data); + + wasm_runtime_free(module_inst); +} + +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)) + return module_inst->export_functions[i].function; + (void)signature; + return NULL; +} + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name) +{ + uint32 i; + for (i = 0; i < module_inst->export_glob_count; i++) + if (!strcmp(module_inst->export_globals[i].name, name)) + return module_inst->export_globals[i].global; + return NULL; +} + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name) +{ + /** + * using a strong assumption that one module instance only has + * one memory instance + */ + (void)module_inst->export_memories; + return module_inst->memories[0]; +} + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name) +{ + /** + * using a strong assumption that one module instance only has + * one table instance + */ + (void)module_inst->export_tables; + return module_inst->tables[0]; +} +#endif + +bool +wasm_call_function(WASMExecEnv *exec_env, + WASMFunctionInstance *function, + unsigned argc, uint32 argv[]) +{ + WASMModuleInstance *module_inst = (WASMModuleInstance*)exec_env->module_inst; + wasm_interp_call_wasm(module_inst, exec_env, function, argc, argv); + return !wasm_get_exception(module_inst) ? true : false; +} + +bool +wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, + WASMFunctionInstance *func, + unsigned argc, uint32 argv[]) +{ + WASMExecEnv *exec_env; + bool ret; + + if (!(exec_env = wasm_exec_env_create( + (WASMModuleInstanceCommon*)module_inst, + module_inst->default_wasm_stack_size))) { + wasm_set_exception(module_inst, "allocate memory failed"); + return false; + } + + /* set thread handle and stack boundary */ + wasm_exec_env_set_thread_info(exec_env); + + ret = wasm_call_function(exec_env, func, argc, argv); + wasm_exec_env_destroy(exec_env); + return ret; +} + +void +wasm_set_exception(WASMModuleInstance *module_inst, + const char *exception) +{ + if (exception) + snprintf(module_inst->cur_exception, + sizeof(module_inst->cur_exception), + "Exception: %s", exception); + else + module_inst->cur_exception[0] = '\0'; +} + +const char* +wasm_get_exception(WASMModuleInstance *module_inst) +{ + if (module_inst->cur_exception[0] == '\0') + return NULL; + else + return module_inst->cur_exception; +} + +uint32 +wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size, + void **p_native_addr) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint8 *addr = NULL; + uint32 offset = 0; + + if (memory->heap_handle) { + addr = mem_allocator_malloc(memory->heap_handle, size); + } + else if (module_inst->malloc_function + && module_inst->free_function) { + if (!execute_malloc_function(module_inst, + module_inst->malloc_function, + size, &offset)) { + return 0; + } + /* If we use app's malloc function, + the default memory may be changed while memory growing */ + memory = module_inst->default_memory; + addr = offset ? memory->memory_data + offset : NULL; + } + + if (!addr) { + wasm_set_exception(module_inst, "out of memory"); + return 0; + } + if (p_native_addr) + *p_native_addr = addr; + + return (uint32)(addr - memory->memory_data); +} + +void +wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr) +{ + if (ptr) { + WASMMemoryInstance *memory = module_inst->default_memory; + uint8 *addr = memory->memory_data + ptr; + + if (memory->heap_handle + && memory->heap_data <= addr + && addr < memory->heap_data_end) { + mem_allocator_free(memory->heap_handle, addr); + } + else if (module_inst->malloc_function + && module_inst->free_function + && memory->memory_data <= addr + && addr < memory->memory_data_end) { + execute_free_function(module_inst, + module_inst->free_function, + ptr); + } + } +} + +uint32 +wasm_module_dup_data(WASMModuleInstance *module_inst, + const char *src, uint32 size) +{ + char *buffer; + uint32 buffer_offset = wasm_module_malloc(module_inst, size, + (void**)&buffer); + if (buffer_offset != 0) { + buffer = wasm_addr_app_to_native(module_inst, buffer_offset); + bh_memcpy_s(buffer, size, src, size); + } + return buffer_offset; +} + +bool +wasm_validate_app_addr(WASMModuleInstance *module_inst, + uint32 app_offset, uint32 size) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint32 memory_data_size = + memory->num_bytes_per_page * memory->cur_page_count; + + /* integer overflow check */ + if (app_offset + size < app_offset) { + goto fail; + } + + if (app_offset + size <= memory_data_size) { + return true; + } +fail: + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +bool +wasm_validate_native_addr(WASMModuleInstance *module_inst, + void *native_ptr, uint32 size) +{ + uint8 *addr = (uint8 *)native_ptr; + WASMMemoryInstance *memory = module_inst->default_memory; + + /* integer overflow check */ + if (addr + size < addr) { + goto fail; + } + + if (memory->memory_data <= addr + && addr + size <= memory->memory_data_end) { + return true; + } +fail: + wasm_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +void * +wasm_addr_app_to_native(WASMModuleInstance *module_inst, + uint32 app_offset) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint8 *addr = memory->memory_data + app_offset; + + if (memory->memory_data <= addr + && addr < memory->memory_data_end) + return addr; + return NULL; +} + +uint32 +wasm_addr_native_to_app(WASMModuleInstance *module_inst, + void *native_ptr) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint8 *addr = (uint8 *)native_ptr; + + if (memory->memory_data <= addr + && addr < memory->memory_data_end) + return (uint32)(addr - memory->memory_data); + return 0; +} + +bool +wasm_get_app_addr_range(WASMModuleInstance *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint32 memory_data_size = + memory->num_bytes_per_page * memory->cur_page_count; + + if (app_offset < memory_data_size) { + if (p_app_start_offset) + *p_app_start_offset = 0; + if (p_app_end_offset) + *p_app_end_offset = memory_data_size; + return true; + } + return false; +} + +bool +wasm_get_native_addr_range(WASMModuleInstance *module_inst, + uint8 *native_ptr, + uint8 **p_native_start_addr, + uint8 **p_native_end_addr) +{ + WASMMemoryInstance *memory = module_inst->default_memory; + uint8 *addr = (uint8 *)native_ptr; + + if (memory->memory_data <= addr + && addr < memory->memory_data_end) { + if (p_native_start_addr) + *p_native_start_addr = memory->memory_data; + if (p_native_end_addr) + *p_native_end_addr = memory->memory_data_end; + return true; + } + return false; +} + +bool +wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) +{ + WASMMemoryInstance *memory = module->default_memory, *new_memory; + uint32 heap_size = memory->heap_data_end - memory->heap_data; + uint32 total_size_old = memory->memory_data_end - (uint8 *)memory; + uint32 total_page_count = inc_page_count + memory->cur_page_count; + uint64 total_size = offsetof(WASMMemoryInstance, memory_data) + + memory->num_bytes_per_page * (uint64)total_page_count; + void *heap_handle_old = memory->heap_handle; + uint8 *heap_data_old = memory->heap_data; + + if (inc_page_count <= 0) + /* No need to enlarge memory */ + return true; + + if (total_page_count < memory->cur_page_count /* integer overflow */ + || total_page_count > memory->max_page_count) { + return false; + } + + if (total_size >= UINT32_MAX) { + return false; + } + +#if WASM_ENABLE_SHARED_MEMORY != 0 + if (memory->is_shared) { + /* For shared memory, we have reserved the maximum spaces during + instantiate, only change the cur_page_count here */ + memory->cur_page_count = total_page_count; + return true; + } +#endif + + if (heap_size > 0) { + /* Destroy heap's lock firstly, if its memory is re-allocated, + we cannot access its lock again. */ + mem_allocator_destroy_lock(memory->heap_handle); + } + if (!(new_memory = wasm_runtime_realloc(memory, (uint32)total_size))) { + if (!(new_memory = wasm_runtime_malloc((uint32)total_size))) { + if (heap_size > 0) { + /* Restore heap's lock if memory re-alloc failed */ + mem_allocator_reinit_lock(memory->heap_handle); + } + return false; + } + bh_memcpy_s((uint8 *)new_memory, (uint32)total_size, + (uint8 *)memory, total_size_old); + wasm_runtime_free(memory); + } + + memset((uint8 *)new_memory + total_size_old, + 0, (uint32)total_size - total_size_old); + + if (heap_size > 0) { + new_memory->heap_handle = (uint8 *)heap_handle_old + + ((uint8 *)new_memory - (uint8 *)memory); + if (mem_allocator_migrate(new_memory->heap_handle, + heap_handle_old) != 0) { + return false; + } + } + + new_memory->cur_page_count = total_page_count; + new_memory->heap_data = heap_data_old + + ((uint8 *)new_memory - (uint8 *)memory); + new_memory->heap_data_end = new_memory->heap_data + heap_size; + new_memory->memory_data_end = new_memory->memory_data + + new_memory->num_bytes_per_page + * total_page_count; + + module->memories[0] = module->default_memory = new_memory; + return true; +} + +bool +wasm_call_indirect(WASMExecEnv *exec_env, + uint32_t element_indices, + uint32_t argc, uint32_t argv[]) +{ + WASMModuleInstance *module_inst = NULL; + WASMTableInstance *table_inst = NULL; + uint32_t function_indices = 0; + WASMFunctionInstance *function_inst = NULL; + + module_inst = + (WASMModuleInstance*)exec_env->module_inst; + bh_assert(module_inst); + + table_inst = module_inst->default_table; + if (!table_inst) { + wasm_set_exception(module_inst, "unknown table"); + goto got_exception; + } + + if (element_indices >= table_inst->cur_size) { + wasm_set_exception(module_inst, "undefined element"); + goto got_exception; + } + + /** + * please be aware that table_inst->base_addr may point + * to another module's table + **/ + function_indices = ((uint32_t*)table_inst->base_addr)[element_indices]; + if (function_indices == 0xFFFFFFFF) { + wasm_set_exception(module_inst, "uninitialized element"); + goto got_exception; + } + + /** + * we insist to call functions owned by the module itself + **/ + if (function_indices >= module_inst->function_count) { + wasm_set_exception(module_inst, "unknown function"); + goto got_exception; + } + + function_inst = module_inst->functions + function_indices; + + wasm_interp_call_wasm(module_inst, exec_env, function_inst, argc, argv); + + return !wasm_get_exception(module_inst) ? true : false; + +got_exception: + return false; +} + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance*)exec_env->module_inst; + uint32 stack_top_idx = module_inst->module->aux_stack_top_global_index; + uint32 data_end = module_inst->module->aux_data_end; + uint32 stack_bottom = module_inst->module->aux_stack_bottom; + bool is_stack_before_data = + stack_bottom < data_end ? true : false; + + /* Check the aux stack space, currently we don't allocate space in heap */ + if ((is_stack_before_data && (size > start_offset)) + || ((!is_stack_before_data) && (start_offset - data_end < size))) + return false; + + if (stack_top_idx != (uint32)-1) { + /* The aux stack top is a wasm global, + set the initial value for the global */ + uint8 *global_addr = + module_inst->global_data + + module_inst->globals[stack_top_idx].data_offset; + *(int32*)global_addr = start_offset; + /* The aux stack boundary is a constant value, + set the value to exec_env */ + exec_env->aux_stack_boundary = start_offset - size; + return true; + } + + return false; +} + +bool +wasm_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size) +{ + WASMModuleInstance *module_inst = + (WASMModuleInstance*)exec_env->module_inst; + + /* The aux stack information is resolved in loader + and store in module */ + uint32 stack_bottom = + module_inst->module->aux_stack_bottom; + uint32 total_aux_stack_size = + module_inst->module->aux_stack_size; + + if (stack_bottom != 0 && total_aux_stack_size != 0) { + if (start_offset) + *start_offset = stack_bottom; + if (size) + *size = total_aux_stack_size; + return true; + } + return false; +} +#endif diff --git a/wamr/core/iwasm/interpreter/wasm_runtime.h b/wamr/core/iwasm/interpreter/wasm_runtime.h new file mode 100644 index 0000000..1682c39 --- /dev/null +++ b/wamr/core/iwasm/interpreter/wasm_runtime.h @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_RUNTIME_H +#define _WASM_RUNTIME_H + +#include "wasm.h" +#include "bh_hashmap.h" +#include "../common/wasm_runtime_common.h" +#include "../common/wasm_exec_env.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WASMModuleInstance WASMModuleInstance; +typedef struct WASMFunctionInstance WASMFunctionInstance; +typedef struct WASMMemoryInstance WASMMemoryInstance; +typedef struct WASMTableInstance WASMTableInstance; +typedef struct WASMGlobalInstance WASMGlobalInstance; + +typedef struct WASMMemoryInstance { + /* Module type */ + uint32 module_type; + /* Shared memory flag */ + bool is_shared; + /* Number bytes per page */ + uint32 num_bytes_per_page; + /* Current page count */ + uint32 cur_page_count; + /* Maximum page count */ + uint32 max_page_count; + + /* Heap data base address */ + uint8 *heap_data; + /* Heap data end address */ + uint8 *heap_data_end; + /* The heap created */ + void *heap_handle; + +#if WASM_ENABLE_MULTI_MODULE != 0 + /* to indicate which module instance create it */ + WASMModuleInstance *owner; +#endif + +#if WASM_ENABLE_SHARED_MEMORY != 0 + /* mutex lock for the memory, used in atomic operation */ + korp_mutex mem_lock; +#endif + + /* Memory data end address */ + uint8 *memory_data_end; + + /* Memory data begin address, the layout is: memory data + heap data + Note: when memory is re-allocated, the heap data and memory data + must be copied to new memory also. */ + uint8 memory_data[1]; +} WASMMemoryInstance; + +typedef struct WASMTableInstance { + /* The element type, TABLE_ELEM_TYPE_ANY_FUNC currently */ + uint8 elem_type; + /* Current size */ + uint32 cur_size; + /* Maximum size */ + uint32 max_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just for import, keep the reference here */ + WASMTableInstance *table_inst_linked; +#endif + /* Base address */ + uint8 base_addr[1]; +} WASMTableInstance; + +typedef struct WASMGlobalInstance { + /* value type, VALUE_TYPE_I32/I64/F32/F64 */ + uint8 type; + /* mutable or constant */ + bool is_mutable; + /* data offset to base_addr of WASMMemoryInstance */ + uint32 data_offset; + /* initial value */ + WASMValue initial_value; +#if WASM_ENABLE_MULTI_MODULE != 0 + /* just for import, keep the reference here */ + WASMModuleInstance *import_module_inst; + WASMGlobalInstance *import_global_inst; +#endif +} WASMGlobalInstance; + +typedef struct WASMFunctionInstance { + /* whether it is import function or WASM function */ + bool is_import_func; + /* parameter count */ + uint16 param_count; + /* local variable count, 0 for import function */ + uint16 local_count; + /* cell num of parameters */ + uint16 param_cell_num; + /* cell num of return type */ + uint16 ret_cell_num; + /* cell num of local variables, 0 for import function */ + uint16 local_cell_num; +#if WASM_ENABLE_FAST_INTERP != 0 + /* cell num of consts */ + uint16 const_cell_num; +#endif + uint16 *local_offsets; + /* parameter types */ + uint8 *param_types; + /* local types, NULL for import function */ + uint8 *local_types; + union { + WASMFunctionImport *func_import; + WASMFunction *func; + } u; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMModuleInstance *import_module_inst; + WASMFunctionInstance *import_func_inst; +#endif +} WASMFunctionInstance; + +typedef struct WASMExportFuncInstance { + char *name; + WASMFunctionInstance *function; +} WASMExportFuncInstance; + +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMExportGlobInstance { + char *name; + WASMGlobalInstance *global; +} WASMExportGlobInstance; + +typedef struct WASMExportTabInstance { + char *name; + WASMTableInstance *table; +} WASMExportTabInstance; + +typedef struct WASMExportMemInstance { + char *name; + WASMMemoryInstance *memory; +} WASMExportMemInstance; +#endif + +typedef struct WASMModuleInstance { + /* Module instance type, for module instance loaded from + WASM bytecode binary, this field is Wasm_Module_Bytecode; + for module instance loaded from AOT file, this field is + Wasm_Module_AoT, and this structure should be treated as + AOTModuleInstance structure. */ + uint32 module_type; + + uint32 memory_count; + uint32 table_count; + uint32 global_count; + uint32 function_count; + + uint32 export_func_count; +#if WASM_ENABLE_MULTI_MODULE != 0 + uint32 export_glob_count; + uint32 export_mem_count; + uint32 export_tab_count; +#endif + + WASMMemoryInstance **memories; + WASMTableInstance **tables; + WASMGlobalInstance *globals; + WASMFunctionInstance *functions; + + WASMExportFuncInstance *export_functions; +#if WASM_ENABLE_MULTI_MODULE != 0 + WASMExportGlobInstance *export_globals; + WASMExportMemInstance *export_memories; + WASMExportTabInstance *export_tables; +#endif + + WASMMemoryInstance *default_memory; + WASMTableInstance *default_table; + /* Global data of global instances */ + uint8 *global_data; + + WASMFunctionInstance *start_function; + WASMFunctionInstance *malloc_function; + WASMFunctionInstance *free_function; + + WASMModule *module; + +#if WASM_ENABLE_LIBC_WASI != 0 + WASIContext *wasi_ctx; +#endif + + uint32 temp_ret; + uint32 llvm_stack; + + /* Default WASM stack size of threads of this Module instance. */ + uint32 default_wasm_stack_size; + + /* The exception buffer of wasm interpreter for current thread. */ + char cur_exception[128]; + + /* The custom data that can be set/get by + * wasm_set_custom_data/wasm_get_custom_data */ + void *custom_data; + + /* Main exec env */ + WASMExecEnv *main_exec_env; + +#if WASM_ENABLE_MULTI_MODULE != 0 + // TODO: mutex ? mutli-threads ? + bh_list sub_module_inst_list_head; + bh_list *sub_module_inst_list; +#endif +} WASMModuleInstance; + +struct WASMInterpFrame; +typedef struct WASMInterpFrame WASMRuntimeFrame; + +#if WASM_ENABLE_MULTI_MODULE != 0 +typedef struct WASMSubModInstNode { + bh_list_link l; + /* point to a string pool */ + const char *module_name; + WASMModuleInstance *module_inst; +} WASMSubModInstNode; +#endif + +/** + * Return the code block of a function. + * + * @param func the WASM function instance + * + * @return the code block of the function + */ +static inline uint8* +wasm_get_func_code(WASMFunctionInstance *func) +{ +#if WASM_ENABLE_FAST_INTERP == 0 + return func->is_import_func ? NULL : func->u.func->code; +#else + return func->is_import_func ? NULL : func->u.func->code_compiled; +#endif +} + +/** + * Return the code block end of a function. + * + * @param func the WASM function instance + * + * @return the code block end of the function + */ +static inline uint8* +wasm_get_func_code_end(WASMFunctionInstance *func) +{ +#if WASM_ENABLE_FAST_INTERP == 0 + return func->is_import_func + ? NULL : func->u.func->code + func->u.func->code_size; +#else + return func->is_import_func + ? NULL + : func->u.func->code_compiled + func->u.func->code_compiled_size; +#endif +} + +WASMModule * +wasm_load(const uint8 *buf, uint32 size, + char *error_buf, uint32 error_buf_size); + +WASMModule * +wasm_load_from_sections(WASMSection *section_list, + char *error_buf, uint32_t error_buf_size); + +void +wasm_unload(WASMModule *module); + +WASMModuleInstance * +wasm_instantiate(WASMModule *module, bool is_sub_inst, + uint32 stack_size, uint32 heap_size, + char *error_buf, uint32 error_buf_size); + +void +wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst); + +WASMFunctionInstance * +wasm_lookup_function(const WASMModuleInstance *module_inst, + const char *name, const char *signature); + +#if WASM_ENABLE_MULTI_MODULE != 0 +WASMGlobalInstance * +wasm_lookup_global(const WASMModuleInstance *module_inst, const char *name); + +WASMMemoryInstance * +wasm_lookup_memory(const WASMModuleInstance *module_inst, const char *name); + +WASMTableInstance * +wasm_lookup_table(const WASMModuleInstance *module_inst, const char *name); +#endif + +bool +wasm_call_function(WASMExecEnv *exec_env, + WASMFunctionInstance *function, + unsigned argc, uint32 argv[]); + +bool +wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, + WASMFunctionInstance *function, + unsigned argc, uint32 argv[]); + +void +wasm_set_exception(WASMModuleInstance *module, const char *exception); + +const char* +wasm_get_exception(WASMModuleInstance *module); + +uint32 +wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size, + void **p_native_addr); + +void +wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr); + +uint32 +wasm_module_dup_data(WASMModuleInstance *module_inst, + const char *src, uint32 size); + +bool +wasm_validate_app_addr(WASMModuleInstance *module_inst, + uint32 app_offset, uint32 size); + +bool +wasm_validate_app_str_addr(WASMModuleInstance *module_inst, + uint32 app_offset); + +bool +wasm_validate_native_addr(WASMModuleInstance *module_inst, + void *native_ptr, uint32 size); + +void * +wasm_addr_app_to_native(WASMModuleInstance *module_inst, + uint32 app_offset); + +uint32 +wasm_addr_native_to_app(WASMModuleInstance *module_inst, + void *native_ptr); + +bool +wasm_get_app_addr_range(WASMModuleInstance *module_inst, + uint32 app_offset, + uint32 *p_app_start_offset, + uint32 *p_app_end_offset); + +bool +wasm_get_native_addr_range(WASMModuleInstance *module_inst, + uint8_t *native_ptr, + uint8_t **p_native_start_addr, + uint8_t **p_native_end_addr); + +bool +wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count); + +bool +wasm_call_indirect(WASMExecEnv *exec_env, + uint32_t element_indices, + uint32_t argc, uint32_t argv[]); + +#if WASM_ENABLE_THREAD_MGR != 0 +bool +wasm_set_aux_stack(WASMExecEnv *exec_env, + uint32 start_offset, uint32 size); + +bool +wasm_get_aux_stack(WASMExecEnv *exec_env, + uint32 *start_offset, uint32 *size); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* end of _WASM_RUNTIME_H */ + diff --git a/wamr/core/iwasm/libraries/lib-pthread/lib_pthread.cmake b/wamr/core/iwasm/libraries/lib-pthread/lib_pthread.cmake new file mode 100644 index 0000000..7611278 --- /dev/null +++ b/wamr/core/iwasm/libraries/lib-pthread/lib_pthread.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_PTHREAD_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIB_PTHREAD=1) + +include_directories(${LIB_PTHREAD_DIR}) + +file (GLOB source_all ${LIB_PTHREAD_DIR}/*.c) + +set (LIB_PTHREAD_SOURCE ${source_all}) + diff --git a/wamr/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c b/wamr/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c new file mode 100644 index 0000000..c13e8c5 --- /dev/null +++ b/wamr/core/iwasm/libraries/lib-pthread/lib_pthread_wrapper.c @@ -0,0 +1,1020 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" +#include "../common/wasm_runtime_common.h" +#include "thread_manager.h" + +#define WAMR_PTHREAD_KEYS_MAX 32 + +#define get_module(exec_env) \ + wasm_exec_env_get_module(exec_env) + +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define get_thread_arg(exec_env) \ + wasm_exec_env_get_thread_arg(exec_env) + +#define get_wasi_ctx(module_inst) \ + wasm_runtime_get_wasi_ctx(module_inst) + +#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) + +extern bool +wasm_runtime_call_indirect(wasm_exec_env_t exec_env, + uint32 element_indices, + uint32 argc, uint32 argv[]); + +enum { + T_THREAD, + T_MUTEX, + T_COND, +}; + +enum thread_status_t { + THREAD_INIT, + THREAD_RUNNING, + THREAD_CANCELLED, + THREAD_EXIT, +}; + +enum mutex_status_t { + MUTEX_CREATED, + MUTEX_DESTROYED, +}; + +enum cond_status_t { + COND_CREATED, + COND_DESTROYED, +}; + +typedef struct ThreadKeyValueNode { + bh_list_link l; + wasm_exec_env_t exec_env; + int32 thread_key_values[WAMR_PTHREAD_KEYS_MAX]; +} ThreadKeyValueNode; + +typedef struct KeyData { + int32 destructor_func; + bool is_created; +} KeyData; + +typedef struct ClusterInfoNode { + bh_list_link l; + WASMCluster *cluster; + HashMap *thread_info_map; + /* Key data list */ + KeyData key_data_list[WAMR_PTHREAD_KEYS_MAX]; + korp_mutex key_data_list_lock; + /* Every node contains the key value list for a thread */ + bh_list thread_list_head; + bh_list *thread_list; +} ClusterInfoNode; + +typedef struct ThreadInfoNode { + wasm_exec_env_t parent_exec_env; + wasm_exec_env_t exec_env; + /* the id returned to app */ + uint32 handle; + /* type can be [THREAD | MUTEX | CONDITION] */ + uint32 type; + /* Thread status, this variable should be volatile + as its value may be changed in different threads */ + volatile uint32 status; + union { + korp_tid thread; + korp_mutex *mutex; + korp_cond *cond; + } u; +} ThreadInfoNode; + +typedef struct { + ThreadInfoNode *info_node; + /* table elem index of the app's entry function */ + uint32 elem_index; + /* arg of the app's entry function */ + void *arg; + wasm_module_inst_t module_inst; +} ThreadRoutineArgs; + +static bh_list cluster_info_list; +static korp_mutex pthread_global_lock; +static uint32 handle_id = 1; + +static void +lib_pthread_destroy_callback(WASMCluster *cluster); + +static uint32 +thread_handle_hash(void *handle) +{ + return (uint32)(uintptr_t)handle; +} + +static bool +thread_handle_equal(void *h1, void *h2) +{ + return (uint32)(uintptr_t)h1 == (uint32)(uintptr_t)h2 ? true : false; +} + +static void +thread_info_destroy(void *node) +{ + ThreadInfoNode *info_node = (ThreadInfoNode *)node; + ThreadRoutineArgs *args; + + pthread_mutex_lock(&pthread_global_lock); + if (info_node->type == T_THREAD) { + args = get_thread_arg(info_node->exec_env); + if (args) { + wasm_runtime_free(args); + } + } + else if (info_node->type == T_MUTEX) { + if (info_node->status != MUTEX_DESTROYED) + os_mutex_destroy(info_node->u.mutex); + wasm_runtime_free(info_node->u.mutex); + } + else if (info_node->type == T_COND) { + if (info_node->status != COND_DESTROYED) + os_cond_destroy(info_node->u.cond); + wasm_runtime_free(info_node->u.cond); + } + wasm_runtime_free(info_node); + pthread_mutex_unlock(&pthread_global_lock); +} + +bool +lib_pthread_init() +{ + if (0 != os_mutex_init(&pthread_global_lock)) + return false; + bh_list_init(&cluster_info_list); + if (!wasm_cluster_register_destroy_callback( + lib_pthread_destroy_callback)) { + os_mutex_destroy(&pthread_global_lock); + return false; + } + return true; +} + +void +lib_pthread_destroy() +{ + os_mutex_destroy(&pthread_global_lock); +} + +static ClusterInfoNode* +get_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node; + + os_mutex_lock(&pthread_global_lock); + node = bh_list_first_elem(&cluster_info_list); + + while (node) { + if (cluster == node->cluster) { + os_mutex_unlock(&pthread_global_lock); + return node; + } + node = bh_list_elem_next(node); + } + os_mutex_unlock(&pthread_global_lock); + + return NULL; +} + +static KeyData* +key_data_list_lookup(wasm_exec_env_t exec_env, int32 key) +{ + ClusterInfoNode *node; + WASMCluster *cluster = + wasm_exec_env_get_cluster(exec_env); + + if ((node = get_cluster_info(cluster))) { + return (key >= 0 && key < WAMR_PTHREAD_KEYS_MAX + && node->key_data_list[key].is_created) + ? &(node->key_data_list[key]) : NULL; + } + + return NULL; +} + +/* Lookup the thread key value node for a thread, + create a new one if failed + This design will reduce the memory usage. If the thread doesn't use + the local storage, it will not occupy memory space +*/ +static int32* +key_value_list_lookup_or_create(wasm_exec_env_t exec_env, + ClusterInfoNode *info, int32 key) +{ + KeyData *key_node; + ThreadKeyValueNode *data; + + /* Check if the key is valid */ + key_node = key_data_list_lookup(exec_env, key); + if (!key_node) { + return NULL; + } + + /* Find key values node */ + data = bh_list_first_elem(info->thread_list); + while (data) { + if (data->exec_env == exec_env) + return data->thread_key_values; + data = bh_list_elem_next(data); + } + + /* If not found, create a new node for this thread */ + if (!(data = wasm_runtime_malloc(sizeof(ThreadKeyValueNode)))) + return NULL; + memset(data, 0, sizeof(ThreadKeyValueNode)); + data->exec_env = exec_env; + + if (bh_list_insert(info->thread_list, data) != 0) { + wasm_runtime_free(data); + return NULL; + } + + return data->thread_key_values; +} + +static void +call_key_destructor(wasm_exec_env_t exec_env) +{ + int32 i; + uint32 destructor_index; + KeyData *key_node; + ThreadKeyValueNode *value_node; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + value_node = bh_list_first_elem(info->thread_list); + while (value_node) { + if (value_node->exec_env == exec_env) + break; + value_node = bh_list_elem_next(value_node); + } + + /* This thread hasn't created key value node */ + if (!value_node) + return; + + /* Destroy key values */ + for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) { + if (value_node->thread_key_values[i] != 0) { + int32 value = value_node->thread_key_values[i]; + os_mutex_lock(&info->key_data_list_lock); + + if ((key_node = key_data_list_lookup(exec_env, i))) + destructor_index = key_node->destructor_func; + else + destructor_index = 0; + os_mutex_unlock(&info->key_data_list_lock); + + /* reset key value */ + value_node->thread_key_values[i] = 0; + + /* Call the destructor func provided by app */ + if (destructor_index) { + uint32 argv[1]; + + argv[0] = value; + wasm_runtime_call_indirect(exec_env, + destructor_index, + 1, argv); + } + } + } + + bh_list_remove(info->thread_list, value_node); + wasm_runtime_free(value_node); +} + +static void +destroy_thread_key_value_list(bh_list *list) +{ + ThreadKeyValueNode *node, *next; + + /* There should be only one node for main thread */ + bh_assert(list->len <= 1); + + if (list->len) { + node = bh_list_first_elem(list); + while (node) { + next = bh_list_elem_next(node); + call_key_destructor(node->exec_env); + node = next; + } + } +} + +static ClusterInfoNode* +create_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node; + bh_list_status ret; + + if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) { + return NULL; + } + memset(node, 0, sizeof(WASMCluster)); + + node->thread_list = &node->thread_list_head; + ret = bh_list_init(node->thread_list); + bh_assert(ret == BH_LIST_SUCCESS); + + if (os_mutex_init(&node->key_data_list_lock) != 0) { + wasm_runtime_free(node); + return NULL; + } + + node->cluster = cluster; + if (!(node->thread_info_map = + bh_hash_map_create(32, true, + (HashFunc)thread_handle_hash, + (KeyEqualFunc)thread_handle_equal, + NULL, + thread_info_destroy))) { + os_mutex_destroy(&node->key_data_list_lock); + wasm_runtime_free(node); + return NULL; + } + os_mutex_lock(&pthread_global_lock); + ret = bh_list_insert(&cluster_info_list, node); + bh_assert(ret == BH_LIST_SUCCESS); + os_mutex_unlock(&pthread_global_lock); + + (void)ret; + return node; +} + +static bool +destroy_cluster_info(WASMCluster *cluster) +{ + ClusterInfoNode *node = get_cluster_info(cluster); + if (node) { + bh_hash_map_destroy(node->thread_info_map); + destroy_thread_key_value_list(node->thread_list); + os_mutex_destroy(&node->key_data_list_lock); + + /* Remove from the cluster info list */ + os_mutex_lock(&pthread_global_lock); + bh_list_remove(&cluster_info_list, node); + wasm_runtime_free(node); + os_mutex_unlock(&pthread_global_lock); + return true; + } + return false; +} + +static void +lib_pthread_destroy_callback(WASMCluster *cluster) +{ + destroy_cluster_info(cluster); +} + +static void +delete_thread_info_node(ThreadInfoNode *thread_info) +{ + ClusterInfoNode *node; + bool ret; + WASMCluster *cluster = + wasm_exec_env_get_cluster(thread_info->exec_env); + + if ((node = get_cluster_info(cluster))) { + ret = bh_hash_map_remove(node->thread_info_map, + (void *)(uintptr_t)thread_info->handle, + NULL, NULL); + (void)ret; + } + + thread_info_destroy(thread_info); +} + +static bool +append_thread_info_node(ThreadInfoNode *thread_info) +{ + ClusterInfoNode *node; + WASMCluster *cluster = + wasm_exec_env_get_cluster(thread_info->exec_env); + + if (!(node = get_cluster_info(cluster))) { + if (!(node = create_cluster_info(cluster))) { + return false; + } + } + + if (!bh_hash_map_insert(node->thread_info_map, + (void *)(uintptr_t)thread_info->handle, + thread_info)) { + return false; + } + + return true; +} + +static ThreadInfoNode* +get_thread_info(wasm_exec_env_t exec_env, uint32 handle) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + return bh_hash_map_find(info->thread_info_map, (void *)(uintptr_t)handle); +} + +static uint32 +allocate_handle() +{ + uint32 id; + os_mutex_lock(&pthread_global_lock); + id = handle_id++; + os_mutex_unlock(&pthread_global_lock); + return id; +} + +static void* +pthread_start_routine(void *arg) +{ + wasm_exec_env_t exec_env = (wasm_exec_env_t)arg; + wasm_exec_env_t parent_exec_env; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + ThreadRoutineArgs *routine_args = exec_env->thread_arg; + ThreadInfoNode *info_node = routine_args->info_node; + uint32 argv[1]; + + parent_exec_env = info_node->parent_exec_env; + os_mutex_lock(&parent_exec_env->wait_lock); + info_node->exec_env = exec_env; + info_node->u.thread = exec_env->handle; + if (!append_thread_info_node(info_node)) { + wasm_runtime_deinstantiate_internal(module_inst, true); + delete_thread_info_node(info_node); + os_cond_signal(&parent_exec_env->wait_cond); + os_mutex_unlock(&parent_exec_env->wait_lock); + return NULL; + } + + info_node->status = THREAD_RUNNING; + os_cond_signal(&parent_exec_env->wait_cond); + os_mutex_unlock(&parent_exec_env->wait_lock); + + if (!validate_native_addr(routine_args->arg, sizeof(uint32))) { + /* If there are exceptions, copy the exception to + all other instance in this cluster */ + wasm_cluster_spread_exception(exec_env); + wasm_runtime_deinstantiate_internal(module_inst, true); + delete_thread_info_node(info_node); + return NULL; + } + + wasm_exec_env_set_thread_info(exec_env); + argv[0] = addr_native_to_app(routine_args->arg); + + if(!wasm_runtime_call_indirect(exec_env, + routine_args->elem_index, + 1, argv)) { + if (wasm_runtime_get_exception(module_inst)) + wasm_cluster_spread_exception(exec_env); + } + + /* destroy pthread key values */ + call_key_destructor(exec_env); + + /* routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + + info_node->status = THREAD_EXIT; + + delete_thread_info_node(info_node); + + return (void *)(uintptr_t)argv[0]; +} + +static int +pthread_create_wrapper(wasm_exec_env_t exec_env, + uint32 *thread, /* thread_handle */ + const void *attr, /* not supported */ + uint32 elem_index, /* entry function */ + void *arg) /* arguments buffer */ +{ + wasm_module_t module = get_module(exec_env); + wasm_module_inst_t new_module_inst = NULL; + ThreadInfoNode *info_node = NULL; + ThreadRoutineArgs *routine_args = NULL; + uint32 thread_handle; + int32 ret = -1; +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_module_inst_t module_inst = get_module_inst(exec_env); + WASIContext *wasi_ctx = get_wasi_ctx(module_inst); +#endif + + if (!(new_module_inst = + wasm_runtime_instantiate_internal(module, true, 8192, 0, + NULL, 0))) + return -1; + +#if WASM_ENABLE_LIBC_WASI != 0 + if (wasi_ctx) + wasm_runtime_set_wasi_ctx(new_module_inst, wasi_ctx); +#endif + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + thread_handle = allocate_handle(); + info_node->parent_exec_env = exec_env; + info_node->handle = thread_handle; + info_node->type = T_THREAD; + info_node->status = THREAD_INIT; + + if (!(routine_args = wasm_runtime_malloc(sizeof(ThreadRoutineArgs)))) + goto fail; + + routine_args->arg = arg; + routine_args->elem_index = elem_index; + routine_args->info_node = info_node; + routine_args->module_inst = new_module_inst; + + os_mutex_lock(&exec_env->wait_lock); + ret = wasm_cluster_create_thread(exec_env, new_module_inst, + pthread_start_routine, + (void *)routine_args); + if (ret != 0) { + os_mutex_unlock(&exec_env->wait_lock); + goto fail; + } + + /* Wait for the thread routine to assign the exec_env to + thread_info_node, otherwise the exec_env in the thread + info node may be NULL in the next pthread API call */ + os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock); + os_mutex_unlock(&exec_env->wait_lock); + + if (thread) + *thread = thread_handle; + + return 0; + +fail: + if (new_module_inst) + wasm_runtime_deinstantiate_internal(new_module_inst, true); + if (info_node) + wasm_runtime_free(info_node); + if (routine_args) + wasm_runtime_free(routine_args); + return ret; +} + +static int32 +pthread_join_wrapper(wasm_exec_env_t exec_env, uint32 thread, + int32 retval_offset) /* void **retval */ +{ + uint32 *ret; + int32 join_ret; + void **retval; + ThreadInfoNode *node; + wasm_module_inst_t module_inst; + wasm_exec_env_t target_exec_env; + + node = get_thread_info(exec_env, thread); + if (!node) { + /* The thread has exited, return 0 to app */ + return 0; + } + + target_exec_env = node->exec_env; + bh_assert(target_exec_env != NULL); + module_inst = get_module_inst(target_exec_env); + + /* validate addr before join thread, otherwise + the module_inst may be freed */ + if (!validate_app_addr(retval_offset, sizeof(uint32))) { + /* Join failed, but we don't want to terminate all threads, + do not spread exception here */ + wasm_runtime_set_exception(module_inst, NULL); + return -1; + } + retval = (void **)addr_app_to_native(retval_offset); + + join_ret = wasm_cluster_join_thread(target_exec_env, (void **)&ret); + + if (retval_offset != 0) + *retval = (void*)ret; + + return join_ret; +} + +static int32 +pthread_detach_wrapper(wasm_exec_env_t exec_env, uint32 thread) +{ + ThreadInfoNode *node; + wasm_exec_env_t target_exec_env; + + node = get_thread_info(exec_env, thread); + if (!node) + return 0; + + target_exec_env = node->exec_env; + bh_assert(target_exec_env != NULL); + + return wasm_cluster_detach_thread(target_exec_env); +} + +static int32 +pthread_cancel_wrapper(wasm_exec_env_t exec_env, uint32 thread) +{ + ThreadInfoNode *node; + wasm_exec_env_t target_exec_env; + + node = get_thread_info(exec_env, thread); + if (!node) + return 0; + + target_exec_env = node->exec_env; + bh_assert(target_exec_env != NULL); + + return wasm_cluster_cancel_thread(target_exec_env); +} + +static int32 +pthread_self_wrapper(wasm_exec_env_t exec_env) +{ + ThreadRoutineArgs *args = get_thread_arg(exec_env); + /* If thread_arg is NULL, it's the exec_env of the main thread, + return id 0 to app */ + if (!args) + return 0; + + return args->info_node->handle; +} + +static void +pthread_exit_wrapper(wasm_exec_env_t exec_env, int32 retval_offset) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + ThreadRoutineArgs *args = get_thread_arg(exec_env); + /* Currently exit main thread is not allowed */ + if (!args) + return; + +#ifdef OS_ENABLE_HW_BOUND_CHECK + /* If hardware bound check enabled, don't deinstantiate module inst + and thread info node here for AoT module, as they will be freed + in pthread_start_routine */ + if (exec_env->jmpbuf_stack_top) { + wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset); + } +#endif + + /* destroy pthread key values */ + call_key_destructor(exec_env); + + /* routine exit, destroy instance */ + wasm_runtime_deinstantiate_internal(module_inst, true); + + delete_thread_info_node(args->info_node); + + wasm_cluster_exit_thread(exec_env, (void *)(uintptr_t)retval_offset); +} + +static int32 +pthread_mutex_init_wrapper(wasm_exec_env_t exec_env, uint32 *mutex, void *attr) +{ + korp_mutex *pmutex; + ThreadInfoNode *info_node; + + if (!(pmutex = wasm_runtime_malloc(sizeof(korp_mutex)))) { + return -1; + } + + if (os_mutex_init(pmutex) != 0) { + goto fail1; + } + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail2; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + info_node->exec_env = exec_env; + info_node->handle = allocate_handle(); + info_node->type = T_MUTEX; + info_node->u.mutex = pmutex; + info_node->status = MUTEX_CREATED; + + if (!append_thread_info_node(info_node)) + goto fail3; + + /* Return the mutex handle to app */ + if (mutex) + *(uint32*)mutex = info_node->handle; + + return 0; + +fail3: + delete_thread_info_node(info_node); +fail2: + os_mutex_destroy(pmutex); +fail1: + wasm_runtime_free(pmutex); + + return -1; +} + +static int32 +pthread_mutex_lock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + return os_mutex_lock(info_node->u.mutex); +} + +static int32 +pthread_mutex_unlock_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + return os_mutex_unlock(info_node->u.mutex); +} + +static int32 +pthread_mutex_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *mutex) +{ + int32 ret_val; + ThreadInfoNode* info_node = get_thread_info(exec_env, *mutex); + if (!info_node || info_node->type != T_MUTEX) + return -1; + + ret_val = os_mutex_destroy(info_node->u.mutex); + + info_node->status = MUTEX_DESTROYED; + delete_thread_info_node(info_node); + + return ret_val; +} + +static int32 +pthread_cond_init_wrapper(wasm_exec_env_t exec_env, uint32 *cond, void *attr) +{ + korp_cond *pcond; + ThreadInfoNode *info_node; + + if (!(pcond = wasm_runtime_malloc(sizeof(korp_cond)))) { + return -1; + } + + if (os_cond_init(pcond) != 0) { + goto fail1; + } + + if (!(info_node = wasm_runtime_malloc(sizeof(ThreadInfoNode)))) + goto fail2; + + memset(info_node, 0, sizeof(ThreadInfoNode)); + info_node->exec_env = exec_env; + info_node->handle = allocate_handle(); + info_node->type = T_COND; + info_node->u.cond = pcond; + info_node->status = COND_CREATED; + + if (!append_thread_info_node(info_node)) + goto fail3; + + /* Return the cond handle to app */ + if (cond) + *(uint32*)cond = info_node->handle; + + return 0; + +fail3: + delete_thread_info_node(info_node); +fail2: + os_cond_destroy(pcond); +fail1: + wasm_runtime_free(pcond); + + return -1; +} + +static int32 +pthread_cond_wait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, uint32 *mutex) +{ + ThreadInfoNode *cond_info_node, *mutex_info_node; + + cond_info_node = get_thread_info(exec_env, *cond); + if (!cond_info_node || cond_info_node->type != T_COND) + return -1; + + mutex_info_node = get_thread_info(exec_env, *mutex); + if (!mutex_info_node || mutex_info_node->type != T_MUTEX) + return -1; + + return os_cond_wait(cond_info_node->u.cond, mutex_info_node->u.mutex); +} + +/* Currently we don't support struct timespec in built-in libc, + so the pthread_cond_timedwait use useconds instead +*/ +static int32 +pthread_cond_timedwait_wrapper(wasm_exec_env_t exec_env, uint32 *cond, + uint32 *mutex, uint32 useconds) +{ + ThreadInfoNode *cond_info_node, *mutex_info_node; + + cond_info_node = get_thread_info(exec_env, *cond); + if (!cond_info_node || cond_info_node->type != T_COND) + return -1; + + mutex_info_node = get_thread_info(exec_env, *mutex); + if (!mutex_info_node || mutex_info_node->type != T_MUTEX) + return -1; + + return os_cond_reltimedwait(cond_info_node->u.cond, + mutex_info_node->u.mutex, useconds); +} + +static int32 +pthread_cond_signal_wrapper(wasm_exec_env_t exec_env, uint32 *cond) +{ + ThreadInfoNode* info_node = get_thread_info(exec_env, *cond); + if (!info_node || info_node->type != T_COND) + return -1; + + return os_cond_signal(info_node->u.cond); +} + +static int32 +pthread_cond_destroy_wrapper(wasm_exec_env_t exec_env, uint32 *cond) +{ + int32 ret_val; + ThreadInfoNode* info_node = get_thread_info(exec_env, *cond); + if (!info_node || info_node->type != T_COND) + return -1; + + ret_val = os_cond_destroy(info_node->u.cond); + + info_node->status = COND_DESTROYED; + delete_thread_info_node(info_node); + + return ret_val; +} + +static int32 +pthread_key_create_wrapper(wasm_exec_env_t exec_env, int32 *key, + int32 destructor_elem_index) +{ + uint32 i; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info) { + /* The user may call pthread_key_create in main thread, + in this case the cluster info hasn't been created */ + if (!(info = create_cluster_info(cluster))) { + return -1; + } + } + + os_mutex_lock(&info->key_data_list_lock); + for (i = 0; i < WAMR_PTHREAD_KEYS_MAX; i++) { + if (!info->key_data_list[i].is_created) { + break; + } + } + + if (i == WAMR_PTHREAD_KEYS_MAX) { + os_mutex_unlock(&info->key_data_list_lock); + return -1; + } + + info->key_data_list[i].destructor_func = destructor_elem_index; + info->key_data_list[i].is_created = true; + *key = i; + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +static int32 +pthread_setspecific_wrapper(wasm_exec_env_t exec_env, int32 key, + int32 value_offset) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + int32 *key_values; + + if (!info) + return -1; + + os_mutex_lock(&info->key_data_list_lock); + + key_values = key_value_list_lookup_or_create(exec_env, info, key); + if (!key_values) { + os_mutex_unlock(&info->key_data_list_lock); + return 0; + } + + key_values[key] = value_offset; + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +static int32 +pthread_getspecific_wrapper(wasm_exec_env_t exec_env, int32 key) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + int32 ret, *key_values; + + if (!info) + return -1; + + os_mutex_lock(&info->key_data_list_lock); + + key_values = key_value_list_lookup_or_create(exec_env, info, key); + if (!key_values) { + os_mutex_unlock(&info->key_data_list_lock); + return 0; + } + + ret = key_values[key]; + os_mutex_unlock(&info->key_data_list_lock); + + return ret; +} + +static int32 +pthread_key_delete_wrapper(wasm_exec_env_t exec_env, int32 key) +{ + KeyData *data; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + ClusterInfoNode *info = get_cluster_info(cluster); + + if (!info) + return -1; + + os_mutex_lock(&info->key_data_list_lock); + data = key_data_list_lookup(exec_env, key); + if (!data) { + os_mutex_unlock(&info->key_data_list_lock); + return -1; + } + + memset(data, 0, sizeof(KeyData)); + os_mutex_unlock(&info->key_data_list_lock); + + return 0; +} + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +static NativeSymbol native_symbols_lib_pthread[] = { + REG_NATIVE_FUNC(pthread_create, "(**i*)i"), + REG_NATIVE_FUNC(pthread_join, "(ii)i"), + REG_NATIVE_FUNC(pthread_detach, "(i)i"), + REG_NATIVE_FUNC(pthread_cancel, "(i)i"), + REG_NATIVE_FUNC(pthread_self, "()i"), + REG_NATIVE_FUNC(pthread_exit, "(i)"), + REG_NATIVE_FUNC(pthread_mutex_init, "(**)i"), + REG_NATIVE_FUNC(pthread_mutex_lock, "(*)i"), + REG_NATIVE_FUNC(pthread_mutex_unlock, "(*)i"), + REG_NATIVE_FUNC(pthread_mutex_destroy, "(*)i"), + REG_NATIVE_FUNC(pthread_cond_init, "(**)i"), + REG_NATIVE_FUNC(pthread_cond_wait, "(**)i"), + REG_NATIVE_FUNC(pthread_cond_timedwait, "(**i)i"), + REG_NATIVE_FUNC(pthread_cond_signal, "(*)i"), + REG_NATIVE_FUNC(pthread_cond_destroy, "(*)i"), + REG_NATIVE_FUNC(pthread_key_create, "(*i)i"), + REG_NATIVE_FUNC(pthread_setspecific, "(ii)i"), + REG_NATIVE_FUNC(pthread_getspecific, "(i)i"), + REG_NATIVE_FUNC(pthread_key_delete, "(i)i"), +}; + +uint32 +get_lib_pthread_export_apis(NativeSymbol **p_lib_pthread_apis) +{ + *p_lib_pthread_apis = native_symbols_lib_pthread; + return sizeof(native_symbols_lib_pthread) / sizeof(NativeSymbol); +} \ No newline at end of file diff --git a/wamr/core/iwasm/libraries/libc-builtin/libc_builtin.cmake b/wamr/core/iwasm/libraries/libc-builtin/libc_builtin.cmake new file mode 100644 index 0000000..0838712 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-builtin/libc_builtin.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIBC_BUILTIN_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIBC_BUILTIN=1) + +include_directories(${LIBC_BUILTIN_DIR}) + +file (GLOB source_all ${LIBC_BUILTIN_DIR}/*.c) + +set (LIBC_BUILTIN_SOURCE ${source_all}) + diff --git a/wamr/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c b/wamr/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c new file mode 100644 index 0000000..c00fd02 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-builtin/libc_builtin_wrapper.c @@ -0,0 +1,1190 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" + +#if defined(_WIN32) || defined(_WIN32_) +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +void +wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); + +uint32 +wasm_runtime_get_temp_ret(wasm_module_inst_t module); + +void +wasm_runtime_set_temp_ret(wasm_module_inst_t module, uint32 temp_ret); + +uint32 +wasm_runtime_get_llvm_stack(wasm_module_inst_t module); + +void +wasm_runtime_set_llvm_stack(wasm_module_inst_t module, uint32 llvm_stack); + +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define validate_app_addr(offset, size) \ + wasm_runtime_validate_app_addr(module_inst, offset, size) + +#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, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) + +#define module_free(offset) \ + wasm_runtime_module_free(module_inst, offset) + +typedef int (*out_func_t)(int c, void *ctx); + +enum pad_type { + PAD_NONE, + PAD_ZERO_BEFORE, + PAD_SPACE_BEFORE, + PAD_SPACE_AFTER, +}; + +typedef char *_va_list; +#define _INTSIZEOF(n) \ + (((uint32)sizeof(n) + 3) & (uint32)~3) +#define _va_arg(ap, t) \ + (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) + +#define CHECK_VA_ARG(ap, t) do { \ + if ((uint8*)ap + _INTSIZEOF(t) > native_end_addr) \ + goto fail; \ +} while (0) + +/** + * @brief Output an unsigned int in hex format + * + * Output an unsigned int on output installed by platform at init time. Should + * be able to handle an unsigned int of any size, 32 or 64 bit. + * @param num Number to output + * + * @return N/A + */ +static void +_printf_hex_uint(out_func_t out, void *ctx, + const uint64 num, bool is_u64, + enum pad_type padding, + int min_width) +{ + int shift = sizeof(num) * 8; + int found_largest_digit = 0; + int remaining = 16; /* 16 digits max */ + int digits = 0; + char nibble; + + while (shift >= 4) { + shift -= 4; + nibble = (num >> shift) & 0xf; + + if (nibble || found_largest_digit || shift == 0) { + found_largest_digit = 1; + nibble = (char)(nibble + (nibble > 9 ? 87 : 48)); + out((int) nibble, ctx); + digits++; + continue; + } + + if (remaining-- <= min_width) { + if (padding == PAD_ZERO_BEFORE) { + out('0', ctx); + } else if (padding == PAD_SPACE_BEFORE) { + out(' ', ctx); + } + } + } + + if (padding == PAD_SPACE_AFTER) { + remaining = min_width * 2 - digits; + while (remaining-- > 0) { + out(' ', ctx); + } + } +} + +/** + * @brief Output an unsigned int in decimal format + * + * Output an unsigned int on output installed by platform at init time. Only + * works with 32-bit values. + * @param num Number to output + * + * @return N/A + */ +static void +_printf_dec_uint(out_func_t out, void *ctx, + const uint32 num, + enum pad_type padding, + int min_width) +{ + uint32 pos = 999999999; + uint32 remainder = num; + int found_largest_digit = 0; + int remaining = 10; /* 10 digits max */ + int digits = 1; + + /* make sure we don't skip if value is zero */ + if (min_width <= 0) { + min_width = 1; + } + + while (pos >= 9) { + if (found_largest_digit || remainder > pos) { + found_largest_digit = 1; + out((int) ((remainder / (pos + 1)) + 48), ctx); + digits++; + } else if (remaining <= min_width && padding < PAD_SPACE_AFTER) { + out((int) (padding == PAD_ZERO_BEFORE ? '0' : ' '), ctx); + digits++; + } + remaining--; + remainder %= (pos + 1); + pos /= 10; + } + out((int) (remainder + 48), ctx); + + if (padding == PAD_SPACE_AFTER) { + remaining = min_width - digits; + while (remaining-- > 0) { + out(' ', ctx); + } + } +} + +static void +print_err(out_func_t out, void *ctx) +{ + out('E', ctx); + out('R', ctx); + out('R', ctx); +} + +static bool +_vprintf_wa(out_func_t out, void *ctx, const char *fmt, _va_list ap, + wasm_module_inst_t module_inst) +{ + int might_format = 0; /* 1 if encountered a '%' */ + enum pad_type padding = PAD_NONE; + int min_width = -1; + int long_ctr = 0; + uint8 *native_end_addr; + + if (!wasm_runtime_get_native_addr_range(module_inst, (uint8*)ap, + NULL, &native_end_addr)) + goto fail; + + /* fmt has already been adjusted if needed */ + + while (*fmt) { + if (!might_format) { + if (*fmt != '%') { + out((int) *fmt, ctx); + } + else { + might_format = 1; + min_width = -1; + padding = PAD_NONE; + long_ctr = 0; + } + } + else { + switch (*fmt) { + case '-': + padding = PAD_SPACE_AFTER; + goto still_might_format; + + case '0': + if (min_width < 0 && padding == PAD_NONE) { + padding = PAD_ZERO_BEFORE; + goto still_might_format; + } + goto handle_1_to_9; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': +handle_1_to_9: + if (min_width < 0) { + min_width = *fmt - '0'; + } else { + min_width = 10 * min_width + *fmt - '0'; + } + + if (padding == PAD_NONE) { + padding = PAD_SPACE_BEFORE; + } + goto still_might_format; + + case 'l': + long_ctr++; + /* Fall through */ + case 'z': + case 'h': + /* FIXME: do nothing for these modifiers */ + goto still_might_format; + + case 'd': + case 'i': { + int32 d; + + if (long_ctr < 2) { + CHECK_VA_ARG(ap, int32); + d = _va_arg(ap, int32); + } + else { + int64 lld; + CHECK_VA_ARG(ap, int64); + lld = _va_arg(ap, int64); + if (lld > INT32_MAX || lld < INT32_MIN) { + print_err(out, ctx); + break; + } + d = (int32)lld; + } + + if (d < 0) { + out((int)'-', ctx); + d = -d; + min_width--; + } + _printf_dec_uint(out, ctx, (uint32)d, padding, min_width); + break; + } + case 'u': { + uint32 u; + + if (long_ctr < 2) { + CHECK_VA_ARG(ap, uint32); + u = _va_arg(ap, uint32); + } + else { + uint64 llu; + CHECK_VA_ARG(ap, uint64); + llu = _va_arg(ap, uint64); + if (llu > INT32_MAX) { + print_err(out, ctx); + break; + } + u = (uint32)llu; + } + _printf_dec_uint(out, ctx, u, padding, min_width); + break; + } + case 'p': + out('0', ctx); + out('x', ctx); + /* left-pad pointers with zeros */ + padding = PAD_ZERO_BEFORE; + min_width = 8; + /* Fall through */ + case 'x': + case 'X': { + uint64 x; + bool is_ptr = (*fmt == 'p') ? true : false; + + if (long_ctr < 2) { + CHECK_VA_ARG(ap, uint32); + x = _va_arg(ap, uint32); + } else { + CHECK_VA_ARG(ap, uint64); + x = _va_arg(ap, uint64); + } + _printf_hex_uint(out, ctx, x, !is_ptr, padding, min_width); + break; + } + + case 's': { + char *s; + char *start; + uint32 s_offset; + + CHECK_VA_ARG(ap, int32); + s_offset = _va_arg(ap, uint32); + + if (!validate_app_str_addr(s_offset)) { + return false; + } + + s = start = addr_app_to_native(s_offset); + + while (*s) + out((int) (*s++), ctx); + + if (padding == PAD_SPACE_AFTER) { + int remaining = min_width - (int32)(s - start); + while (remaining-- > 0) { + out(' ', ctx); + } + } + break; + } + + case 'c': { + int c; + CHECK_VA_ARG(ap, int); + c = _va_arg(ap, int); + out(c, ctx); + break; + } + + case '%': { + out((int) '%', ctx); + break; + } + + default: + out((int) '%', ctx); + out((int) *fmt, ctx); + break; + } + + might_format = 0; + } + +still_might_format: + ++fmt; + } + return true; + +fail: + wasm_runtime_set_exception(module_inst, "out of bounds memory access"); + return false; +} + +struct str_context { + char *str; + uint32 max; + uint32 count; +}; + +static int +sprintf_out(int c, struct str_context *ctx) +{ + if (!ctx->str || ctx->count >= ctx->max) { + ctx->count++; + return c; + } + + if (ctx->count == ctx->max - 1) { + ctx->str[ctx->count++] = '\0'; + } else { + ctx->str[ctx->count++] = (char)c; + } + + return c; +} + +static int +printf_out(int c, struct str_context *ctx) +{ + os_printf("%c", c); + ctx->count++; + return c; +} + +static int +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 }; + + /* 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, format, va_args, module_inst)) + return 0; + + return (int)ctx.count; +} + +static int +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); + uint8 *native_end_offset; + struct str_context ctx; + + /* 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; + } + + ctx.str = str; + ctx.max = (uint32)(native_end_offset - (uint8*)str); + ctx.count = 0; + + if (!_vprintf_wa((out_func_t)sprintf_out, &ctx, format, va_args, module_inst)) + return 0; + + if (ctx.count < ctx.max) { + str[ctx.count] = '\0'; + } + + return (int)ctx.count; +} + +static int +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; + + /* 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, format, va_args, module_inst)) + return 0; + + if (ctx.count < ctx.max) { + str[ctx.count] = '\0'; + } + + return (int)ctx.count; +} + +static int +puts_wrapper(wasm_exec_env_t exec_env, const char *str) +{ + return os_printf("%s\n", str); +} + +static int +putchar_wrapper(wasm_exec_env_t exec_env, int c) +{ + os_printf("%c", c); + return 1; +} + +static uint32 +strdup_wrapper(wasm_exec_env_t exec_env, const char *str) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char *str_ret; + uint32 len; + uint32 str_ret_offset = 0; + + /* str has been checked by runtime */ + if (str) { + len = (uint32)strlen(str) + 1; + + str_ret_offset = module_malloc(len, (void**)&str_ret); + if (str_ret_offset) { + bh_memcpy_s(str_ret, len, str, len); + } + } + + return str_ret_offset; +} + +static uint32 +_strdup_wrapper(wasm_exec_env_t exec_env, const char *str) +{ + return strdup_wrapper(exec_env, str); +} + +static int32 +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); + + /* s2 has been checked by runtime */ + if (!validate_native_addr((void*)s1, size)) + return 0; + + return memcmp(s1, s2, size); +} + +static uint32 +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); + uint32 dst_offset = addr_native_to_app(dst); + + if (size == 0) + return dst_offset; + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) + return dst_offset; + + bh_memcpy_s(dst, size, src, size); + return dst_offset; +} + +static uint32 +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); + uint32 dst_offset = addr_native_to_app(dst); + + if (size == 0) + return dst_offset; + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) + return dst_offset; + + memmove(dst, src, size); + return dst_offset; +} + +static uint32 +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); + uint32 s_offset = addr_native_to_app(s); + + if (!validate_native_addr(s, size)) + return s_offset; + + memset(s, c, size); + return s_offset; +} + +static uint32 +strchr_wrapper(wasm_exec_env_t exec_env, + const char *s, int32 c) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char *ret; + + /* 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, + const char *s1, const char *s2) +{ + /* s1 and s2 have been checked by runtime */ + return strcmp(s1, s2); +} + +static int32 +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); + + /* s2 has been checked by runtime */ + if (!validate_native_addr((void*)s1, size)) + return 0; + + return strncmp(s1, s2, size); +} + +static uint32 +strcpy_wrapper(wasm_exec_env_t exec_env, char *dst, const char *src) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uint32 len = strlen(src) + 1; + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, len)) + return 0; + + strncpy(dst, src, len); + return addr_native_to_app(dst); +} + +static uint32 +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); + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) + return 0; + + strncpy(dst, src, size); + return addr_native_to_app(dst); +} + +static uint32 +strlen_wrapper(wasm_exec_env_t exec_env, const char *s) +{ + /* s has been checked by runtime */ + return (uint32)strlen(s); +} + +static uint32 +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, NULL); +} + +static uint32 +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; + uint32 ret_offset = 0; + uint8 *ret_ptr; + + if (total_size >= UINT32_MAX) + return 0; + + ret_offset = module_malloc((uint32)total_size, (void**)&ret_ptr); + if (ret_offset) { + memset(ret_ptr, 0, (uint32)total_size); + } + + return ret_offset; +} + +static void +free_wrapper(wasm_exec_env_t exec_env, void *ptr) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_native_addr(ptr, sizeof(uint32))) + return; + + return module_free(addr_native_to_app(ptr)); +} + +static int32 +atoi_wrapper(wasm_exec_env_t exec_env, const char *s) +{ + /* s has been checked by runtime */ + return atoi(s); +} + +static void +exit_wrapper(wasm_exec_env_t exec_env, int32 status) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "env.exit(%i)", status); + wasm_runtime_set_exception(module_inst, buf); +} + +static int32 +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); + int32 num = 0; + + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, sizeof(uint32))) + return 0; + + num = (int32)strtol(nptr, endptr, base); + *(uint32*)endptr = addr_native_to_app(*endptr); + + return num; +} + +static uint32 +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); + uint32 num = 0; + + /* nptr has been checked by runtime */ + if (!validate_native_addr(endptr, sizeof(uint32))) + return 0; + + num = (uint32)strtoul(nptr, endptr, base); + *(uint32 *)endptr = addr_native_to_app(*endptr); + + return num; +} + +static uint32 +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 *res; + + if (!validate_native_addr((void*)s, n)) + return 0; + + res = memchr(s, c, n); + return addr_native_to_app(res); +} + +static int32 +strncasecmp_wrapper(wasm_exec_env_t exec_env, + const char *s1, const char *s2, uint32 n) +{ + /* s1 and s2 have been checked by runtime */ + return strncasecmp(s1, s2, n); +} + +static uint32 +strspn_wrapper(wasm_exec_env_t exec_env, + const char *s, const char *accept) +{ + /* s and accept have been checked by runtime */ + return (uint32)strspn(s, accept); +} + +static uint32 +strcspn_wrapper(wasm_exec_env_t exec_env, + const char *s, const char *reject) +{ + /* s and reject have been checked by runtime */ + return (uint32)strcspn(s, reject); +} + +static uint32 +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); + /* 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) +{ + return isupper(c); +} + +static int32 +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) +{ + return isspace(c); +} + +static int32 +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) +{ + return isprint(c); +} + +static int32 +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) +{ + return isxdigit(c); +} + +static int32 +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) +{ + return toupper(c); +} + +static int32 +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) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_runtime_set_temp_ret(module_inst, temp_ret); +} + +static uint32 +getTempRet0_wrapper(wasm_exec_env_t exec_env) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + return wasm_runtime_get_temp_ret(module_inst); +} + +static uint32 +llvm_bswap_i16_wrapper(wasm_exec_env_t exec_env, uint32 data) +{ + return (data & 0xFFFF0000) + | ((data & 0xFF) << 8) + | ((data & 0xFF00) >> 8); +} + +static uint32 +llvm_bswap_i32_wrapper(wasm_exec_env_t exec_env, uint32 data) +{ + return ((data & 0xFF) << 24) + | ((data & 0xFF00) << 8) + | ((data & 0xFF0000) >> 8) + | ((data & 0xFF000000) >> 24); +} + +static uint32 +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 { + uint64 value; + uint32 parts[2]; + } u; + + u.parts[0] = uint64_part0; + u.parts[1] = uint64_part1; + + u.value >>= bits; + /* return low 32bit and save high 32bit to temp ret */ + wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32)); + return (uint32) u.value; +} + +static uint32 +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 { + int64 value; + uint32 parts[2]; + } u; + + u.parts[0] = int64_part0; + u.parts[1] = int64_part1; + + u.value <<= bits; + /* return low 32bit and save high 32bit to temp ret */ + wasm_runtime_set_temp_ret(module_inst, (uint32) (u.value >> 32)); + return (uint32) u.value; +} + +static void +llvm_stackrestore_wrapper(wasm_exec_env_t exec_env, uint32 llvm_stack) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + os_printf("_llvm_stackrestore called!\n"); + wasm_runtime_set_llvm_stack(module_inst, llvm_stack); +} + +static uint32 +llvm_stacksave_wrapper(wasm_exec_env_t exec_env) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + os_printf("_llvm_stacksave called!\n"); + return wasm_runtime_get_llvm_stack(module_inst); +} + +static uint32 +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); + uint32 dst_offset = addr_native_to_app(dst); + + /* src has been checked by runtime */ + if (!validate_native_addr(dst, size)) + return dst_offset; + + bh_memcpy_s(dst, size, src, size); + return dst_offset; +} + +static void +abort_wrapper(wasm_exec_env_t exec_env, int32 code) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "env.abort(%i)", code); + wasm_runtime_set_exception(module_inst, buf); +} + +static void +abortStackOverflow_wrapper(wasm_exec_env_t exec_env, int32 code) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "env.abortStackOverflow(%i)", code); + wasm_runtime_set_exception(module_inst, buf); +} + +static void +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]; + snprintf(buf, sizeof(buf), "env.nullFunc_X(%i)", code); + wasm_runtime_set_exception(module_inst, buf); +} + +static uint32 +__cxa_allocate_exception_wrapper(wasm_exec_env_t exec_env, + uint32 thrown_size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + uint32 exception = module_malloc(thrown_size, NULL); + if (!exception) + return 0; + + return exception; +} + +static void +__cxa_begin_catch_wrapper(wasm_exec_env_t exec_env, + void *exception_object) +{ +} + +static void +__cxa_throw_wrapper(wasm_exec_env_t exec_env, + void *thrown_exception, + void *tinfo, + uint32 table_elem_idx) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + + snprintf(buf, sizeof(buf), "%s", "exception thrown by stdc++"); + wasm_runtime_set_exception(module_inst, buf); +} + +#if WASM_ENABLE_SPEC_TEST != 0 +static void +print_wrapper(wasm_exec_env_t exec_env) +{ + os_printf("in specttest.print()\n"); + +} + +static void +print_i32_wrapper(wasm_exec_env_t exec_env, int32 i32) +{ + os_printf("in specttest.print_i32(%d)\n", i32); +} + +static void +print_i32_f32_wrapper(wasm_exec_env_t exec_env, int32 i32, float f32) +{ + os_printf("in specttest.print_i32_f32(%d, %f)\n", i32, f32); +} + +static void +print_f64_f64_wrapper(wasm_exec_env_t exec_env, double f64_1, double f64_2) +{ + os_printf("in specttest.print_f64_f64(%f, %f)\n", f64_1, f64_2); +} + +static void +print_f32_wrapper(wasm_exec_env_t exec_env, float f32) +{ + os_printf("in specttest.print_f32(%f)\n", f32); +} + +static void +print_f64_wrapper(wasm_exec_env_t exec_env, double f64) +{ + os_printf("in specttest.print_f64(%f)\n", f64); +} +#endif /* WASM_ENABLE_SPEC_TEST */ + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, func_name##_wrapper, signature, NULL } + +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(_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)i"), + 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)") +}; + +#if WASM_ENABLE_SPEC_TEST != 0 +static NativeSymbol native_symbols_spectest[] = { + REG_NATIVE_FUNC(print, "()"), + REG_NATIVE_FUNC(print_i32, "(i)"), + REG_NATIVE_FUNC(print_i32_f32, "(if)"), + REG_NATIVE_FUNC(print_f64_f64, "(FF)"), + REG_NATIVE_FUNC(print_f32, "(f)"), + REG_NATIVE_FUNC(print_f64, "(F)") +}; +#endif + +uint32 +get_libc_builtin_export_apis(NativeSymbol **p_libc_builtin_apis) +{ + *p_libc_builtin_apis = native_symbols_libc_builtin; + return sizeof(native_symbols_libc_builtin) / sizeof(NativeSymbol); +} + +#if WASM_ENABLE_SPEC_TEST != 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); +} +#endif + +/************************************* + * Global Variables * + *************************************/ + +typedef struct WASMNativeGlobalDef { + const char *module_name; + const char *global_name; + WASMValue global_data; +} WASMNativeGlobalDef; + +static WASMNativeGlobalDef native_global_defs[] = { + { "spectest", "global_i32", .global_data.i32 = 666 }, + { "spectest", "global_f32", .global_data.f32 = 666.6 }, + { "spectest", "global_f64", .global_data.f64 = 666.6 }, + { "test", "global-i32", .global_data.i32 = 0 }, + { "test", "global-f32", .global_data.f32 = 0 }, + { "env", "STACKTOP", .global_data.u32 = 0 }, + { "env", "STACK_MAX", .global_data.u32 = 0 }, + { "env", "ABORT", .global_data.u32 = 0 }, + { "env", "memoryBase", .global_data.u32 = 0 }, + { "env", "__memory_base", .global_data.u32 = 0 }, + { "env", "tableBase", .global_data.u32 = 0 }, + { "env", "__table_base", .global_data.u32 = 0 }, + { "env", "DYNAMICTOP_PTR", .global_data.addr = 0 }, + { "env", "tempDoublePtr", .global_data.addr = 0 }, + { "global", "NaN", .global_data.u64 = 0x7FF8000000000000LL }, + { "global", "Infinity", .global_data.u64 = 0x7FF0000000000000LL } +}; + +bool +wasm_native_lookup_libc_builtin_global(const char *module_name, + const char *global_name, + WASMGlobalImport *global) +{ + uint32 size = sizeof(native_global_defs) / sizeof(WASMNativeGlobalDef); + WASMNativeGlobalDef *global_def = native_global_defs; + WASMNativeGlobalDef *global_def_end = global_def + size; + + if (!module_name || !global_name || !global) + return false; + + /* Lookup constant globals which can be defined by table */ + while (global_def < global_def_end) { + if (!strcmp(global_def->module_name, module_name) + && !strcmp(global_def->global_name, global_name)) { + global->global_data_linked = global_def->global_data; + return true; + } + global_def++; + } + + return false; +} + diff --git a/wamr/core/iwasm/libraries/libc-wasi/libc_wasi.cmake b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi.cmake new file mode 100644 index 0000000..d72c42a --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIBC_WASI_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_LIBC_WASI=1) + +include_directories(${LIBC_WASI_DIR}/sandboxed-system-primitives/include + ${LIBC_WASI_DIR}/sandboxed-system-primitives/src) + +file (GLOB_RECURSE source_all ${LIBC_WASI_DIR}/*.c ) + +set (LIBC_WASI_SOURCE ${source_all}) diff --git a/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c new file mode 100644 index 0000000..6a968c4 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.c @@ -0,0 +1,1241 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "libc_wasi_wrapper.h" +#include "bh_platform.h" +#include "wasm_export.h" + +void +wasm_runtime_set_exception(wasm_module_inst_t module, const char *exception); + +#define get_module_inst(exec_env) \ + wasm_runtime_get_module_inst(exec_env) + +#define get_wasi_ctx(module_inst) \ + wasm_runtime_get_wasi_ctx(module_inst) + +#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, p_native_addr) \ + wasm_runtime_module_malloc(module_inst, size, p_native_addr) + +#define module_free(offset) \ + wasm_runtime_module_free(module_inst, offset) + +typedef struct wasi_prestat_app { + wasi_preopentype_t pr_type; + uint32 pr_name_len; +} wasi_prestat_app_t; + +typedef struct iovec_app { + uint32 buf_offset; + uint32 buf_len; +} iovec_app_t; + +typedef struct WASIContext { + uint32 curfds_offset; + uint32 prestats_offset; + uint32 argv_environ_offset; + uint32 argv_buf_offset; + uint32 argv_offsets_offset; + uint32 env_buf_offset; + uint32 env_offsets_offset; +} *wasi_ctx_t; + +wasi_ctx_t +wasm_runtime_get_wasi_ctx(wasm_module_inst_t module_inst); + +static struct fd_table * +wasi_ctx_get_curfds(wasm_module_inst_t module_inst, + wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return (struct fd_table *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->curfds_offset); +} + +static struct argv_environ_values * +wasi_ctx_get_argv_environ(wasm_module_inst_t module_inst, + wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return (struct argv_environ_values *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->argv_environ_offset); +} + +static struct fd_prestats * +wasi_ctx_get_prestats(wasm_module_inst_t module_inst, + wasi_ctx_t wasi_ctx) +{ + if (!wasi_ctx) + return NULL; + return (struct fd_prestats *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->prestats_offset); +} + +static wasi_errno_t +wasi_args_get(wasm_exec_env_t exec_env, uint32 *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); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); + size_t argc, argv_buf_size, i; + char **argv; + uint64 total_size; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_args_sizes_get(argv_environ, &argc, &argv_buf_size); + if (err) + 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 = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_args_get(argv_environ, argv, argv_buf); + if (err) { + wasm_runtime_free(argv); + return err; + } + + for (i = 0; i < argc; i++) + argv_offsets[i] = addr_native_to_app(argv[i]); + argv_offsets[argc] = 0; + + wasm_runtime_free(argv); + return 0; +} + +static wasi_errno_t +wasi_args_sizes_get(wasm_exec_env_t exec_env, + 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); + struct argv_environ_values *argv_environ; + size_t argc, argv_buf_size; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(argc_app, sizeof(uint32)) + || !validate_native_addr(argv_buf_size_app, sizeof(uint32))) + return (wasi_errno_t)-1; + + argv_environ = (struct argv_environ_values *) + wasm_runtime_addr_app_to_native(module_inst, + wasi_ctx->argv_environ_offset); + + err = wasmtime_ssp_args_sizes_get(argv_environ, + &argc, &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, /* uint32 clock_id */ + wasi_timestamp_t *resolution /* uint64 *resolution */) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + if (!validate_native_addr(resolution, sizeof(wasi_timestamp_t))) + return (wasi_errno_t)-1; + + 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, /* 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); + + if (!validate_native_addr(time, sizeof(wasi_timestamp_t))) + return (wasi_errno_t)-1; + + return wasmtime_ssp_clock_time_get(clock_id, precision, time); +} + +static wasi_errno_t +wasi_environ_get(wasm_exec_env_t exec_env, + uint32 *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); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); + size_t environ_count, environ_buf_size, i; + uint64 total_size; + char **environs; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_environ_sizes_get(argv_environ, + &environ_count, &environ_buf_size); + if (err) + 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 = wasm_runtime_malloc((uint32)total_size))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_environ_get(argv_environ, environs, environ_buf); + if (err) { + wasm_runtime_free(environs); + return err; + } + + for (i = 0; i < environ_count; i++) + environ_offsets[i] = addr_native_to_app(environs[i]); + environ_offsets[environ_count] = 0; + + wasm_runtime_free(environs); + return 0; +} + +static wasi_errno_t +wasi_environ_sizes_get(wasm_exec_env_t exec_env, + 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); + struct argv_environ_values *argv_environ = + wasi_ctx_get_argv_environ(module_inst, wasi_ctx); + size_t environ_count, environ_buf_size; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + 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(argv_environ, + &environ_count, &environ_buf_size); + if (err) + return err; + + *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, 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); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + wasi_prestat_t prestat; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(prestat_app, sizeof(wasi_prestat_app_t))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_fd_prestat_get(prestats, fd, &prestat); + if (err) + return err; + + 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, 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); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_prestat_dir_name(prestats, + fd, path, path_len); +} + +static wasi_errno_t +wasi_fd_close(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_close(curfds, prestats, fd); +} + +static wasi_errno_t +wasi_fd_datasync(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_datasync(curfds, fd); +} + +static wasi_errno_t +wasi_fd_pread(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + size_t nread; + uint32 mem; + uint32 i; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nread_app, (uint32)sizeof(uint32)) + || total_size >= UINT32_MAX + || !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, (void**)&iovec_begin))) + return (wasi_errno_t)-1; + + 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)) { + err = (wasi_errno_t)-1; + goto fail; + } + iovec->buf = (void*)addr_app_to_native(iovec_app->buf_offset); + iovec->buf_len = iovec_app->buf_len; + } + + err = wasmtime_ssp_fd_pread(curfds, fd, iovec_begin, + iovs_len, offset, &nread); + if (err) + goto fail; + + *nread_app = (uint32)nread; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +wasi_fd_pwrite(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + size_t nwritten; + uint32 mem; + uint32 i; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void*)iovec_app, (uint32)total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) + return (wasi_errno_t)-1; + + 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(iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; + } + + err = wasmtime_ssp_fd_pwrite(curfds, fd, ciovec_begin, + iovs_len, offset, &nwritten); + if (err) + goto fail; + + *nwritten_app = (uint32)nwritten; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +wasi_fd_read(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + size_t nread; + uint32 i; + uint32 mem; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nread_app, (uint32)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void*)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, (void**)&iovec_begin))) + return (wasi_errno_t)-1; + + 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)) { + err = (wasi_errno_t)-1; + goto fail; + } + iovec->buf = (void*)addr_app_to_native(iovec_app->buf_offset); + iovec->buf_len = iovec_app->buf_len; + } + + err = wasmtime_ssp_fd_read(curfds, fd, + iovec_begin, iovs_len, &nread); + if (err) + goto fail; + + *nread_app = (uint32)nread; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_renumber(curfds, prestats, from, to); +} + +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, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_seek(curfds, fd, offset, whence, newoffset); +} + +static wasi_errno_t +wasi_fd_tell(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(newoffset, sizeof(wasi_filesize_t))) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_tell(curfds, fd, newoffset); +} + +static wasi_errno_t +wasi_fd_fdstat_get(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_fdstat_t fdstat; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(fdstat_app, sizeof(wasi_fdstat_t))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_fd_fdstat_get(curfds, fd, &fdstat); + if (err) + return err; + + 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) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_fdstat_set_flags(curfds, fd, flags); +} + +static wasi_errno_t +wasi_fd_fdstat_set_rights(wasm_exec_env_t exec_env, + wasi_fd_t fd, + wasi_rights_t fs_rights_base, + wasi_rights_t fs_rights_inheriting) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_fdstat_set_rights(curfds, fd, + fs_rights_base, fs_rights_inheriting); +} + +static wasi_errno_t +wasi_fd_sync(wasm_exec_env_t exec_env, wasi_fd_t fd) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_sync(curfds, fd); +} + +static wasi_errno_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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + size_t nwritten; + uint32 mem; + uint32 i; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)iovs_len; + if (!validate_native_addr(nwritten_app, (uint32)sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void*)iovec_app, (uint32)total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)iovs_len; + if (total_size >= UINT32_MAX + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) + return (wasi_errno_t)-1; + + 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(iovec_app->buf_offset); + ciovec->buf_len = iovec_app->buf_len; + } + + err = wasmtime_ssp_fd_write(curfds, fd, + ciovec_begin, iovs_len, &nwritten); + if (err) + goto fail; + + *nwritten_app = (uint32)nwritten; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +wasi_fd_advise(wasm_exec_env_t exec_env, + wasi_fd_t fd, + wasi_filesize_t offset, + wasi_filesize_t len, + wasi_advice_t advice) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_advise(curfds, fd, offset, len, advice); +} + +static wasi_errno_t +wasi_fd_allocate(wasm_exec_env_t exec_env, + wasi_fd_t fd, + wasi_filesize_t offset, + wasi_filesize_t len) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_allocate(curfds, fd, offset, len); +} + +static wasi_errno_t +wasi_path_create_directory(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_create_directory(curfds, fd, + path, path_len); +} + +static wasi_errno_t +wasi_path_link(wasm_exec_env_t exec_env, + wasi_fd_t old_fd, + wasi_lookupflags_t old_flags, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_link(curfds, prestats, + old_fd, old_flags, old_path, old_path_len, + new_fd, new_path, new_path_len); +} + +static wasi_errno_t +wasi_path_open(wasm_exec_env_t exec_env, + wasi_fd_t dirfd, + wasi_lookupflags_t dirflags, + 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, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_fd_t fd = (wasi_fd_t)-1; /* set fd_app -1 if path open failed */ + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(fd_app, sizeof(wasi_fd_t))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_path_open(curfds, + dirfd, dirflags, + path, path_len, + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + &fd); + + *fd_app = fd; + return err; +} + +static wasi_errno_t +wasi_fd_readdir(wasm_exec_env_t exec_env, + wasi_fd_t fd, + void *buf, uint32 buf_len, + wasi_dircookie_t cookie, + uint32 *bufused_app) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + size_t bufused; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(bufused_app, sizeof(uint32))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_fd_readdir(curfds, fd, + buf, buf_len, cookie, &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, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + size_t bufused; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(bufused_app, sizeof(uint32))) + return (wasi_errno_t)-1; + + err = wasmtime_ssp_path_readlink(curfds, fd, + 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, 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_rename(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, 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(filestat, sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_filestat_get(curfds, fd, filestat); +} + +static wasi_errno_t +wasi_fd_filestat_set_times(wasm_exec_env_t exec_env, + wasi_fd_t fd, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_filestat_set_times(curfds, fd, + st_atim, st_mtim, fstflags); +} + +static wasi_errno_t +wasi_fd_filestat_set_size(wasm_exec_env_t exec_env, + wasi_fd_t fd, + wasi_filesize_t st_size) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_fd_filestat_set_size(curfds, fd, st_size); +} + +static wasi_errno_t +wasi_path_filestat_get(wasm_exec_env_t exec_env, + wasi_fd_t fd, + wasi_lookupflags_t flags, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + if (!validate_native_addr(filestat, sizeof(wasi_filestat_t))) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_filestat_get(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, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_filestat_set_times(curfds, fd, + flags, path, path_len, + st_atim, st_mtim, fstflags); +} + +static wasi_errno_t +wasi_path_symlink(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + struct fd_prestats *prestats = wasi_ctx_get_prestats(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_symlink(curfds, prestats, + 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, 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_unlink_file(curfds, fd, path, path_len); +} + +static wasi_errno_t +wasi_path_remove_directory(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_path_remove_directory(curfds, fd, path, path_len); +} + +static wasi_errno_t +wasi_poll_oneoff(wasm_exec_env_t exec_env, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + size_t nevents; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + 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; + + err = wasmtime_ssp_poll_oneoff(curfds, in, out, + nsubscriptions, &nevents); + if (err) + return err; + + *nevents_app = (uint32)nevents; + return 0; +} + +void wasi_proc_exit(wasm_exec_env_t exec_env, wasi_exitcode_t rval) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_runtime_set_exception(module_inst, "wasi proc exit"); +} + +static wasi_errno_t +wasi_proc_raise(wasm_exec_env_t exec_env, wasi_signal_t sig) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + char buf[32]; + snprintf(buf, sizeof(buf), "%s%d", "wasi proc raise ", sig); + wasm_runtime_set_exception(module_inst, buf); + + return 0; +} + +static wasi_errno_t +wasi_random_get(wasm_exec_env_t exec_env, void *buf, uint32 buf_len) +{ + 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, + iovec_app_t *ri_data, uint32 ri_data_len, + wasi_riflags_t ri_flags, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_iovec_t *iovec, *iovec_begin; + uint64 total_size; + size_t ro_datalen; + uint32 mem; + uint32 i; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)ri_data_len; + 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_native_addr(ri_data, (uint32)total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_iovec_t) * (uint64)ri_data_len; + if (total_size >= UINT32_MAX + || !(mem = module_malloc((uint32)total_size, (void**)&iovec_begin))) + return (wasi_errno_t)-1; + + iovec = iovec_begin; + + 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->buf_offset); + iovec->buf_len = ri_data->buf_len; + } + + err = wasmtime_ssp_sock_recv(curfds, sock, + iovec_begin, ri_data_len, + ri_flags, &ro_datalen, + ro_flags); + if (err) + goto fail; + + *(uint32*)ro_datalen_app = (uint32)ro_datalen; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +wasi_sock_send(wasm_exec_env_t exec_env, + wasi_fd_t sock, + const iovec_app_t *si_data, uint32 si_data_len, + wasi_siflags_t si_flags, + 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); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + wasi_ciovec_t *ciovec, *ciovec_begin; + uint64 total_size; + size_t so_datalen; + uint32 mem; + uint32 i; + wasi_errno_t err; + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + total_size = sizeof(iovec_app_t) * (uint64)si_data_len; + if (!validate_native_addr(so_datalen_app, sizeof(uint32)) + || total_size >= UINT32_MAX + || !validate_native_addr((void*)si_data, (uint32)total_size)) + return (wasi_errno_t)-1; + + total_size = sizeof(wasi_ciovec_t) * (uint64)si_data_len; + if (total_size >= UINT32_MAX + || !(mem = module_malloc((uint32)total_size, (void**)&ciovec_begin))) + return (wasi_errno_t)-1; + + ciovec = ciovec_begin; + + 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->buf_offset); + ciovec->buf_len = si_data->buf_len; + } + + err = wasmtime_ssp_sock_send(curfds, sock, + ciovec_begin, si_data_len, + si_flags, &so_datalen); + if (err) + goto fail; + + *so_datalen_app = (uint32)so_datalen; + + /* success */ + err = 0; + +fail: + module_free(mem); + return err; +} + +static wasi_errno_t +wasi_sock_shutdown(wasm_exec_env_t exec_env, + wasi_fd_t sock, wasi_sdflags_t how) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasi_ctx_t wasi_ctx = get_wasi_ctx(module_inst); + struct fd_table *curfds = wasi_ctx_get_curfds(module_inst, wasi_ctx); + + if (!wasi_ctx) + return (wasi_errno_t)-1; + + return wasmtime_ssp_sock_shutdown(curfds, sock, how); +} + +static wasi_errno_t +wasi_sched_yield(wasm_exec_env_t exec_env) +{ + return wasmtime_ssp_sched_yield(); +} + +#define REG_NATIVE_FUNC(func_name, signature) \ + { #func_name, wasi_##func_name, signature, NULL } + +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"), +}; + +uint32 +get_libc_wasi_export_apis(NativeSymbol **p_libc_wasi_apis) +{ + *p_libc_wasi_apis = native_symbols_libc_wasi; + return sizeof(native_symbols_libc_wasi) / sizeof(NativeSymbol); +} + diff --git a/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h new file mode 100644 index 0000000..53219f4 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/libc_wasi_wrapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _LIBC_WASI_WRAPPER_H +#define _LIBC_WASI_WRAPPER_H + +#include "wasmtime_ssp.h" +#include "posix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __wasi_errno_t wasi_errno_t; +typedef __wasi_fd_t wasi_fd_t; +typedef __wasi_clockid_t wasi_clockid_t; +typedef __wasi_timestamp_t wasi_timestamp_t; +typedef __wasi_prestat_t wasi_prestat_t; +typedef __wasi_iovec_t wasi_iovec_t; +typedef __wasi_ciovec_t wasi_ciovec_t; +typedef __wasi_filetype_t wasi_filetype_t; +typedef __wasi_filesize_t wasi_filesize_t; +typedef __wasi_filedelta_t wasi_filedelta_t; +typedef __wasi_whence_t wasi_whence_t; +typedef __wasi_fdstat_t wasi_fdstat_t; +typedef __wasi_fdflags_t wasi_fdflags_t; +typedef __wasi_rights_t wasi_rights_t; +typedef __wasi_advice_t wasi_advice_t; +typedef __wasi_lookupflags_t wasi_lookupflags_t; +typedef __wasi_oflags_t wasi_oflags_t; +typedef __wasi_dircookie_t wasi_dircookie_t; +typedef __wasi_filestat_t wasi_filestat_t; +typedef __wasi_fstflags_t wasi_fstflags_t; +typedef __wasi_subscription_t wasi_subscription_t; +typedef __wasi_event_t wasi_event_t; +typedef __wasi_exitcode_t wasi_exitcode_t; +typedef __wasi_signal_t wasi_signal_t; +typedef __wasi_riflags_t wasi_riflags_t; +typedef __wasi_roflags_t wasi_roflags_t; +typedef __wasi_siflags_t wasi_siflags_t; +typedef __wasi_sdflags_t wasi_sdflags_t; +typedef __wasi_dircookie_t wasi_dircookie_t; +typedef __wasi_preopentype_t wasi_preopentype_t; + +#ifdef __cplusplus +} +#endif + +#endif /* end of _LIBC_WASI_WRAPPER_H */ diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/LICENSE b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/LICENSE new file mode 100644 index 0000000..83386f8 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/LICENSE @@ -0,0 +1,7 @@ +Please see the LICENSE file in each top-level directory for the terms applicable to that directory and its relative sub-directories. + +The relevant directories and licenses are: + +src/ - BSD-2-Clause; see src/LICENSE for details +include/ - CC0 1.0 Universal (CC0 1.0) Public Domain Dedication +polyfill/clang/ - MIT; see the header of polyfill/clang/stdatomic.h for details diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/LICENSE b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h new file mode 100644 index 0000000..c8473bd --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include/wasmtime_ssp.h @@ -0,0 +1,895 @@ +/* + * Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. + * + * This file declares an interface similar to WASI, but augmented to expose + * some implementation details such as the curfds arguments that we pass + * around to avoid storing them in TLS. + */ + +#ifndef WASMTIME_SSP_H +#define WASMTIME_SSP_H + +#include +#include + +#ifdef __cplusplus +#ifndef _Static_assert +#define _Static_assert static_assert +#endif /* _Static_assert */ + +#ifndef _Alignof +#define _Alignof alignof +#endif /* _Alignof */ + +#ifndef _Noreturn +#define _Noreturn [[ noreturn ]] +#endif /* _Noreturn */ +extern "C" { +#endif + + +_Static_assert(_Alignof(int8_t) == 1, "non-wasi data layout"); +_Static_assert(_Alignof(uint8_t) == 1, "non-wasi data layout"); +_Static_assert(_Alignof(int16_t) == 2, "non-wasi data layout"); +_Static_assert(_Alignof(uint16_t) == 2, "non-wasi data layout"); +_Static_assert(_Alignof(int32_t) == 4, "non-wasi data layout"); +_Static_assert(_Alignof(uint32_t) == 4, "non-wasi data layout"); +#if 0 +_Static_assert(_Alignof(int64_t) == 8, "non-wasi data layout"); +_Static_assert(_Alignof(uint64_t) == 8, "non-wasi data layout"); +#endif + +typedef uint8_t __wasi_advice_t; +#define __WASI_ADVICE_NORMAL (0) +#define __WASI_ADVICE_SEQUENTIAL (1) +#define __WASI_ADVICE_RANDOM (2) +#define __WASI_ADVICE_WILLNEED (3) +#define __WASI_ADVICE_DONTNEED (4) +#define __WASI_ADVICE_NOREUSE (5) + +typedef uint32_t __wasi_clockid_t; +#define __WASI_CLOCK_REALTIME (0) +#define __WASI_CLOCK_MONOTONIC (1) +#define __WASI_CLOCK_PROCESS_CPUTIME_ID (2) +#define __WASI_CLOCK_THREAD_CPUTIME_ID (3) + +typedef uint64_t __wasi_device_t; + +typedef uint64_t __wasi_dircookie_t; +#define __WASI_DIRCOOKIE_START (0) + +typedef uint16_t __wasi_errno_t; +#define __WASI_ESUCCESS (0) +#define __WASI_E2BIG (1) +#define __WASI_EACCES (2) +#define __WASI_EADDRINUSE (3) +#define __WASI_EADDRNOTAVAIL (4) +#define __WASI_EAFNOSUPPORT (5) +#define __WASI_EAGAIN (6) +#define __WASI_EALREADY (7) +#define __WASI_EBADF (8) +#define __WASI_EBADMSG (9) +#define __WASI_EBUSY (10) +#define __WASI_ECANCELED (11) +#define __WASI_ECHILD (12) +#define __WASI_ECONNABORTED (13) +#define __WASI_ECONNREFUSED (14) +#define __WASI_ECONNRESET (15) +#define __WASI_EDEADLK (16) +#define __WASI_EDESTADDRREQ (17) +#define __WASI_EDOM (18) +#define __WASI_EDQUOT (19) +#define __WASI_EEXIST (20) +#define __WASI_EFAULT (21) +#define __WASI_EFBIG (22) +#define __WASI_EHOSTUNREACH (23) +#define __WASI_EIDRM (24) +#define __WASI_EILSEQ (25) +#define __WASI_EINPROGRESS (26) +#define __WASI_EINTR (27) +#define __WASI_EINVAL (28) +#define __WASI_EIO (29) +#define __WASI_EISCONN (30) +#define __WASI_EISDIR (31) +#define __WASI_ELOOP (32) +#define __WASI_EMFILE (33) +#define __WASI_EMLINK (34) +#define __WASI_EMSGSIZE (35) +#define __WASI_EMULTIHOP (36) +#define __WASI_ENAMETOOLONG (37) +#define __WASI_ENETDOWN (38) +#define __WASI_ENETRESET (39) +#define __WASI_ENETUNREACH (40) +#define __WASI_ENFILE (41) +#define __WASI_ENOBUFS (42) +#define __WASI_ENODEV (43) +#define __WASI_ENOENT (44) +#define __WASI_ENOEXEC (45) +#define __WASI_ENOLCK (46) +#define __WASI_ENOLINK (47) +#define __WASI_ENOMEM (48) +#define __WASI_ENOMSG (49) +#define __WASI_ENOPROTOOPT (50) +#define __WASI_ENOSPC (51) +#define __WASI_ENOSYS (52) +#define __WASI_ENOTCONN (53) +#define __WASI_ENOTDIR (54) +#define __WASI_ENOTEMPTY (55) +#define __WASI_ENOTRECOVERABLE (56) +#define __WASI_ENOTSOCK (57) +#define __WASI_ENOTSUP (58) +#define __WASI_ENOTTY (59) +#define __WASI_ENXIO (60) +#define __WASI_EOVERFLOW (61) +#define __WASI_EOWNERDEAD (62) +#define __WASI_EPERM (63) +#define __WASI_EPIPE (64) +#define __WASI_EPROTO (65) +#define __WASI_EPROTONOSUPPORT (66) +#define __WASI_EPROTOTYPE (67) +#define __WASI_ERANGE (68) +#define __WASI_EROFS (69) +#define __WASI_ESPIPE (70) +#define __WASI_ESRCH (71) +#define __WASI_ESTALE (72) +#define __WASI_ETIMEDOUT (73) +#define __WASI_ETXTBSY (74) +#define __WASI_EXDEV (75) +#define __WASI_ENOTCAPABLE (76) + +typedef uint16_t __wasi_eventrwflags_t; +#define __WASI_EVENT_FD_READWRITE_HANGUP (0x0001) + +typedef uint8_t __wasi_eventtype_t; +#define __WASI_EVENTTYPE_CLOCK (0) +#define __WASI_EVENTTYPE_FD_READ (1) +#define __WASI_EVENTTYPE_FD_WRITE (2) + +typedef uint32_t __wasi_exitcode_t; + +typedef uint32_t __wasi_fd_t; + +typedef uint16_t __wasi_fdflags_t; +#define __WASI_FDFLAG_APPEND (0x0001) +#define __WASI_FDFLAG_DSYNC (0x0002) +#define __WASI_FDFLAG_NONBLOCK (0x0004) +#define __WASI_FDFLAG_RSYNC (0x0008) +#define __WASI_FDFLAG_SYNC (0x0010) + +typedef int64_t __wasi_filedelta_t; + +typedef uint64_t __wasi_filesize_t; + +typedef uint8_t __wasi_filetype_t; +#define __WASI_FILETYPE_UNKNOWN (0) +#define __WASI_FILETYPE_BLOCK_DEVICE (1) +#define __WASI_FILETYPE_CHARACTER_DEVICE (2) +#define __WASI_FILETYPE_DIRECTORY (3) +#define __WASI_FILETYPE_REGULAR_FILE (4) +#define __WASI_FILETYPE_SOCKET_DGRAM (5) +#define __WASI_FILETYPE_SOCKET_STREAM (6) +#define __WASI_FILETYPE_SYMBOLIC_LINK (7) + +typedef uint16_t __wasi_fstflags_t; +#define __WASI_FILESTAT_SET_ATIM (0x0001) +#define __WASI_FILESTAT_SET_ATIM_NOW (0x0002) +#define __WASI_FILESTAT_SET_MTIM (0x0004) +#define __WASI_FILESTAT_SET_MTIM_NOW (0x0008) + +typedef uint64_t __wasi_inode_t; + +typedef uint32_t __wasi_linkcount_t; + +typedef uint32_t __wasi_lookupflags_t; +#define __WASI_LOOKUP_SYMLINK_FOLLOW (0x00000001) + +typedef uint16_t __wasi_oflags_t; +#define __WASI_O_CREAT (0x0001) +#define __WASI_O_DIRECTORY (0x0002) +#define __WASI_O_EXCL (0x0004) +#define __WASI_O_TRUNC (0x0008) + +typedef uint16_t __wasi_riflags_t; +#define __WASI_SOCK_RECV_PEEK (0x0001) +#define __WASI_SOCK_RECV_WAITALL (0x0002) + +typedef uint64_t __wasi_rights_t; +#define __WASI_RIGHT_FD_DATASYNC (0x0000000000000001) +#define __WASI_RIGHT_FD_READ (0x0000000000000002) +#define __WASI_RIGHT_FD_SEEK (0x0000000000000004) +#define __WASI_RIGHT_FD_FDSTAT_SET_FLAGS (0x0000000000000008) +#define __WASI_RIGHT_FD_SYNC (0x0000000000000010) +#define __WASI_RIGHT_FD_TELL (0x0000000000000020) +#define __WASI_RIGHT_FD_WRITE (0x0000000000000040) +#define __WASI_RIGHT_FD_ADVISE (0x0000000000000080) +#define __WASI_RIGHT_FD_ALLOCATE (0x0000000000000100) +#define __WASI_RIGHT_PATH_CREATE_DIRECTORY (0x0000000000000200) +#define __WASI_RIGHT_PATH_CREATE_FILE (0x0000000000000400) +#define __WASI_RIGHT_PATH_LINK_SOURCE (0x0000000000000800) +#define __WASI_RIGHT_PATH_LINK_TARGET (0x0000000000001000) +#define __WASI_RIGHT_PATH_OPEN (0x0000000000002000) +#define __WASI_RIGHT_FD_READDIR (0x0000000000004000) +#define __WASI_RIGHT_PATH_READLINK (0x0000000000008000) +#define __WASI_RIGHT_PATH_RENAME_SOURCE (0x0000000000010000) +#define __WASI_RIGHT_PATH_RENAME_TARGET (0x0000000000020000) +#define __WASI_RIGHT_PATH_FILESTAT_GET (0x0000000000040000) +#define __WASI_RIGHT_PATH_FILESTAT_SET_SIZE (0x0000000000080000) +#define __WASI_RIGHT_PATH_FILESTAT_SET_TIMES (0x0000000000100000) +#define __WASI_RIGHT_FD_FILESTAT_GET (0x0000000000200000) +#define __WASI_RIGHT_FD_FILESTAT_SET_SIZE (0x0000000000400000) +#define __WASI_RIGHT_FD_FILESTAT_SET_TIMES (0x0000000000800000) +#define __WASI_RIGHT_PATH_SYMLINK (0x0000000001000000) +#define __WASI_RIGHT_PATH_REMOVE_DIRECTORY (0x0000000002000000) +#define __WASI_RIGHT_PATH_UNLINK_FILE (0x0000000004000000) +#define __WASI_RIGHT_POLL_FD_READWRITE (0x0000000008000000) +#define __WASI_RIGHT_SOCK_SHUTDOWN (0x0000000010000000) + +typedef uint16_t __wasi_roflags_t; +#define __WASI_SOCK_RECV_DATA_TRUNCATED (0x0001) + +typedef uint8_t __wasi_sdflags_t; +#define __WASI_SHUT_RD (0x01) +#define __WASI_SHUT_WR (0x02) + +typedef uint16_t __wasi_siflags_t; + +typedef uint8_t __wasi_signal_t; +// 0 is reserved; POSIX has special semantics for kill(pid, 0). +#define __WASI_SIGHUP (1) +#define __WASI_SIGINT (2) +#define __WASI_SIGQUIT (3) +#define __WASI_SIGILL (4) +#define __WASI_SIGTRAP (5) +#define __WASI_SIGABRT (6) +#define __WASI_SIGBUS (7) +#define __WASI_SIGFPE (8) +#define __WASI_SIGKILL (9) +#define __WASI_SIGUSR1 (10) +#define __WASI_SIGSEGV (11) +#define __WASI_SIGUSR2 (12) +#define __WASI_SIGPIPE (13) +#define __WASI_SIGALRM (14) +#define __WASI_SIGTERM (15) +#define __WASI_SIGCHLD (16) +#define __WASI_SIGCONT (17) +#define __WASI_SIGSTOP (18) +#define __WASI_SIGTSTP (19) +#define __WASI_SIGTTIN (20) +#define __WASI_SIGTTOU (21) +#define __WASI_SIGURG (22) +#define __WASI_SIGXCPU (23) +#define __WASI_SIGXFSZ (24) +#define __WASI_SIGVTALRM (25) +#define __WASI_SIGPROF (26) +#define __WASI_SIGWINCH (27) +#define __WASI_SIGPOLL (28) +#define __WASI_SIGPWR (29) +#define __WASI_SIGSYS (30) + +typedef uint16_t __wasi_subclockflags_t; +#define __WASI_SUBSCRIPTION_CLOCK_ABSTIME (0x0001) + +typedef uint64_t __wasi_timestamp_t; + +typedef uint64_t __wasi_userdata_t; + +typedef uint8_t __wasi_whence_t; +#define __WASI_WHENCE_SET (0) +#define __WASI_WHENCE_CUR (1) +#define __WASI_WHENCE_END (2) + +typedef uint8_t __wasi_preopentype_t; +#define __WASI_PREOPENTYPE_DIR (0) + +struct fd_table; +struct fd_prestats; +struct argv_environ_values; + +typedef struct __wasi_dirent_t { + __wasi_dircookie_t d_next; + __wasi_inode_t d_ino; + uint32_t d_namlen; + __wasi_filetype_t d_type; +} __wasi_dirent_t __attribute__((aligned(8))); +_Static_assert(offsetof(__wasi_dirent_t, d_next) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_ino) == 8, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_namlen) == 16, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_dirent_t, d_type) == 20, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_dirent_t) == 24, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_dirent_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_event_t { + __wasi_userdata_t userdata; + __wasi_errno_t error; + __wasi_eventtype_t type; + uint8_t __paddings[5]; + union __wasi_event_u { + struct __wasi_event_u_fd_readwrite_t { + __wasi_filesize_t nbytes; + __wasi_eventrwflags_t flags; + uint8_t __paddings[6]; + } fd_readwrite; + } u; +} __wasi_event_t __attribute__((aligned(8))); +_Static_assert(offsetof(__wasi_event_t, userdata) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_event_t, error) == 8, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_event_t, type) == 10, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_event_t, u.fd_readwrite.nbytes) == 16, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_event_t, u.fd_readwrite.flags) == 24, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_event_t) == 32, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_event_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_prestat_t { + __wasi_preopentype_t pr_type; + union __wasi_prestat_u { + struct __wasi_prestat_u_dir_t { + size_t pr_name_len; + } dir; + } u; +} __wasi_prestat_t; +_Static_assert(offsetof(__wasi_prestat_t, pr_type) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_prestat_t, u.dir.pr_name_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_prestat_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_prestat_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_prestat_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_prestat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_fdstat_t { + __wasi_filetype_t fs_filetype; + __wasi_fdflags_t fs_flags; + uint8_t __paddings[4]; + __wasi_rights_t fs_rights_base; + __wasi_rights_t fs_rights_inheriting; +} __wasi_fdstat_t __attribute__((aligned(8))); +_Static_assert( + offsetof(__wasi_fdstat_t, fs_filetype) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_fdstat_t, fs_flags) == 2, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_fdstat_t, fs_rights_base) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_fdstat_t, fs_rights_inheriting) == 16, + "non-wasi data layout"); +_Static_assert(sizeof(__wasi_fdstat_t) == 24, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_fdstat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_filestat_t { + __wasi_device_t st_dev; + __wasi_inode_t st_ino; + __wasi_filetype_t st_filetype; + __wasi_linkcount_t st_nlink; + __wasi_filesize_t st_size; + __wasi_timestamp_t st_atim; + __wasi_timestamp_t st_mtim; + __wasi_timestamp_t st_ctim; +} __wasi_filestat_t __attribute__((aligned(8))); +_Static_assert(offsetof(__wasi_filestat_t, st_dev) == 0, "non-wasi data layout"); +_Static_assert(offsetof(__wasi_filestat_t, st_ino) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_filetype) == 16, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_nlink) == 20, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_size) == 24, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_atim) == 32, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_mtim) == 40, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_filestat_t, st_ctim) == 48, "non-wasi data layout"); +_Static_assert(sizeof(__wasi_filestat_t) == 56, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_filestat_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_ciovec_t { + const void *buf; + size_t buf_len; +} __wasi_ciovec_t; +_Static_assert(offsetof(__wasi_ciovec_t, buf) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_ciovec_t, buf_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_ciovec_t, buf_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_ciovec_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_ciovec_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_ciovec_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_ciovec_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_iovec_t { + void *buf; + size_t buf_len; +} __wasi_iovec_t; +_Static_assert(offsetof(__wasi_iovec_t, buf) == 0, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + offsetof(__wasi_iovec_t, buf_len) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + offsetof(__wasi_iovec_t, buf_len) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + sizeof(__wasi_iovec_t) == 8, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + sizeof(__wasi_iovec_t) == 16, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 4 || + _Alignof(__wasi_iovec_t) == 4, "non-wasi data layout"); +_Static_assert(sizeof(void *) != 8 || + _Alignof(__wasi_iovec_t) == 8, "non-wasi data layout"); + +typedef struct __wasi_subscription_t { + __wasi_userdata_t userdata; + __wasi_eventtype_t type; + uint8_t __paddings[7]; + union __wasi_subscription_u { + struct __wasi_subscription_u_clock_t { + __wasi_userdata_t identifier; + __wasi_clockid_t clock_id; + uint8_t __paddings1[4]; + __wasi_timestamp_t timeout; + __wasi_timestamp_t precision; + __wasi_subclockflags_t flags; + uint8_t __paddings2[6]; + } clock; + struct __wasi_subscription_u_fd_readwrite_t { + __wasi_fd_t fd; + } fd_readwrite; + } u; +} __wasi_subscription_t __attribute__((aligned(8))); +_Static_assert( + offsetof(__wasi_subscription_t, userdata) == 0, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, type) == 8, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.identifier) == 16, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.clock_id) == 24, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.timeout) == 32, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.precision) == 40, + "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.clock.flags) == 48, "non-wasi data layout"); +_Static_assert( + offsetof(__wasi_subscription_t, u.fd_readwrite.fd) == 16, + "non-wasi data layout"); +_Static_assert(sizeof(__wasi_subscription_t) == 56, "non-wasi data layout"); +_Static_assert(_Alignof(__wasi_subscription_t) == 8, "non-wasi data layout"); + +#if defined(WASMTIME_SSP_WASI_API) +#define WASMTIME_SSP_SYSCALL_NAME(name) \ + asm("__wasi_" #name) +#else +#define WASMTIME_SSP_SYSCALL_NAME(name) +#endif + +__wasi_errno_t wasmtime_ssp_args_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + char **argv, + char *argv_buf +) WASMTIME_SSP_SYSCALL_NAME(args_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_args_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + size_t *argc, + size_t *argv_buf_size +) WASMTIME_SSP_SYSCALL_NAME(args_sizes_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_clock_res_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t *resolution +) WASMTIME_SSP_SYSCALL_NAME(clock_res_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_clock_time_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t precision, + __wasi_timestamp_t *time +) WASMTIME_SSP_SYSCALL_NAME(clock_time_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_environ_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + char **environ, + char *environ_buf +) WASMTIME_SSP_SYSCALL_NAME(environ_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_environ_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *arg_environ, +#endif + size_t *environ_count, + size_t *environ_buf_size +) WASMTIME_SSP_SYSCALL_NAME(environ_sizes_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_prestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + __wasi_prestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(fd_prestat_dir_name) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_close( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_close) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_datasync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_datasync) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_pread( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iovs, + size_t iovs_len, + __wasi_filesize_t offset, + size_t *nread +) WASMTIME_SSP_SYSCALL_NAME(fd_pread) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_pwrite( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iovs, + size_t iovs_len, + __wasi_filesize_t offset, + size_t *nwritten +) WASMTIME_SSP_SYSCALL_NAME(fd_pwrite) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_read( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iovs, + size_t iovs_len, + size_t *nread +) WASMTIME_SSP_SYSCALL_NAME(fd_read) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_renumber( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t from, + __wasi_fd_t to +) WASMTIME_SSP_SYSCALL_NAME(fd_renumber) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_seek( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filedelta_t offset, + __wasi_whence_t whence, + __wasi_filesize_t *newoffset +) WASMTIME_SSP_SYSCALL_NAME(fd_seek) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_tell( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t *newoffset +) WASMTIME_SSP_SYSCALL_NAME(fd_tell) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdstat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdflags_t flags +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_flags) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting +) WASMTIME_SSP_SYSCALL_NAME(fd_fdstat_set_rights) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_sync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) WASMTIME_SSP_SYSCALL_NAME(fd_sync) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_write( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iovs, + size_t iovs_len, + size_t *nwritten +) WASMTIME_SSP_SYSCALL_NAME(fd_write) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_advise( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len, + __wasi_advice_t advice +) WASMTIME_SSP_SYSCALL_NAME(fd_advise) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_allocate( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len +) WASMTIME_SSP_SYSCALL_NAME(fd_allocate) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_create_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_create_directory) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_link( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_link) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t dirfd, + __wasi_lookupflags_t dirflags, + const char *path, + size_t path_len, + __wasi_oflags_t oflags, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, + __wasi_fd_t *fd +) WASMTIME_SSP_SYSCALL_NAME(path_open) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_readdir( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + void *buf, + size_t buf_len, + __wasi_dircookie_t cookie, + size_t *bufused +) WASMTIME_SSP_SYSCALL_NAME(fd_readdir) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_readlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len, + char *buf, + size_t buf_len, + size_t *bufused +) WASMTIME_SSP_SYSCALL_NAME(path_readlink) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_rename( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_rename) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_times) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t st_size +) WASMTIME_SSP_SYSCALL_NAME(fd_filestat_set_size) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t path_len, + __wasi_filestat_t *buf +) WASMTIME_SSP_SYSCALL_NAME(path_filestat_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t path_len, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) WASMTIME_SSP_SYSCALL_NAME(path_filestat_set_times) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_symlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + const char *old_path, + size_t old_path_len, + __wasi_fd_t fd, + const char *new_path, + size_t new_path_len +) WASMTIME_SSP_SYSCALL_NAME(path_symlink) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_unlink_file( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_unlink_file) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_path_remove_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t path_len +) WASMTIME_SSP_SYSCALL_NAME(path_remove_directory) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_poll_oneoff( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const __wasi_subscription_t *in, + __wasi_event_t *out, + size_t nsubscriptions, + size_t *nevents +) WASMTIME_SSP_SYSCALL_NAME(poll_oneoff) __attribute__((__warn_unused_result__)); + +#if 0 +/** + * We throw exception in libc-wasi wrapper function wasi_proc_exit() + * but not call this function. + */ +_Noreturn void wasmtime_ssp_proc_exit( + __wasi_exitcode_t rval +) WASMTIME_SSP_SYSCALL_NAME(proc_exit); +#endif + +__wasi_errno_t wasmtime_ssp_proc_raise( + __wasi_signal_t sig +) WASMTIME_SSP_SYSCALL_NAME(proc_raise) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_random_get( + void *buf, + size_t buf_len +) WASMTIME_SSP_SYSCALL_NAME(random_get) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_recv( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_iovec_t *ri_data, + size_t ri_data_len, + __wasi_riflags_t ri_flags, + size_t *ro_datalen, + __wasi_roflags_t *ro_flags +) WASMTIME_SSP_SYSCALL_NAME(sock_recv) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_send( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_ciovec_t *si_data, + size_t si_data_len, + __wasi_siflags_t si_flags, + size_t *so_datalen +) WASMTIME_SSP_SYSCALL_NAME(sock_send) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sock_shutdown( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + __wasi_sdflags_t how +) WASMTIME_SSP_SYSCALL_NAME(sock_shutdown) __attribute__((__warn_unused_result__)); + +__wasi_errno_t wasmtime_ssp_sched_yield(void) + WASMTIME_SSP_SYSCALL_NAME(sched_yield) __attribute__((__warn_unused_result__)); + +#ifdef __cplusplus +} +#endif + +#undef WASMTIME_SSP_SYSCALL_NAME + +#endif + diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/LICENSE b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/LICENSE new file mode 100644 index 0000000..04c6f48 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/LICENSE @@ -0,0 +1,24 @@ +All code is distributed under the following license: + + Copyright (c) 2015 Nuxi, https://nuxi.nl/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md new file mode 100644 index 0000000..b4d55d8 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/README.md @@ -0,0 +1,14 @@ +This directory consists of selected files copied from the [src/libemulator] +directory in the [cloudabi-utils] repository, with minor modifications, +along with the accompanying LICENSE file from that repository. + +The modifications are marked with `WASMTIME_*` preprocessor macros. + +The files were copied at git revision +223dadc53248552db43e012c67ed08cf416a2b12 +which is dated +Tue Jun 25 17:22:07 2019 -0700 +. + +[libemulator]: https://github.com/NuxiNL/cloudabi-utils/tree/223dadc53248552db43e012c67ed08cf416a2b12/src/libemulator +[cloudabi-utils]: https://github.com/NuxiNL/cloudabi-utils diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h new file mode 100644 index 0000000..490f9a1 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/locking.h @@ -0,0 +1,259 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef LOCKING_H +#define LOCKING_H + +#include "ssp_config.h" + +#ifndef __has_extension +#define __has_extension(x) 0 +#endif + +#if __has_extension(c_thread_safety_attributes) +#define LOCK_ANNOTATE(x) __attribute__((x)) +#else +#define LOCK_ANNOTATE(x) +#endif + +/* Lock annotation macros. */ + +#define LOCKABLE LOCK_ANNOTATE(lockable) + +#define LOCKS_EXCLUSIVE(...) LOCK_ANNOTATE(exclusive_lock_function(__VA_ARGS__)) +#define LOCKS_SHARED(...) LOCK_ANNOTATE(shared_lock_function(__VA_ARGS__)) + +#define TRYLOCKS_EXCLUSIVE(...) \ + LOCK_ANNOTATE(exclusive_trylock_function(__VA_ARGS__)) +#define TRYLOCKS_SHARED(...) LOCK_ANNOTATE(shared_trylock_function(__VA_ARGS__)) + +#define UNLOCKS(...) LOCK_ANNOTATE(unlock_function(__VA_ARGS__)) + +#define REQUIRES_EXCLUSIVE(...) \ + LOCK_ANNOTATE(exclusive_locks_required(__VA_ARGS__)) +#define REQUIRES_SHARED(...) LOCK_ANNOTATE(shared_locks_required(__VA_ARGS__)) +#define REQUIRES_UNLOCKED(...) LOCK_ANNOTATE(locks_excluded(__VA_ARGS__)) + +#define NO_LOCK_ANALYSIS LOCK_ANNOTATE(no_thread_safety_analysis) + +/* Mutex that uses the lock annotations. */ + +struct LOCKABLE mutex { + pthread_mutex_t object; +}; + +#define MUTEX_INITIALIZER \ + { PTHREAD_MUTEX_INITIALIZER } + +static inline bool +mutex_init(struct mutex *lock) REQUIRES_UNLOCKED(*lock) +{ + return pthread_mutex_init(&lock->object, NULL) == 0 ? true : false; +} + +static inline void +mutex_destroy(struct mutex *lock) REQUIRES_UNLOCKED(*lock) +{ + pthread_mutex_destroy(&lock->object); +} + +static inline void +mutex_lock(struct mutex *lock) LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + pthread_mutex_lock(&lock->object); +} + +static inline void +mutex_unlock(struct mutex *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + pthread_mutex_unlock(&lock->object); +} + +/* Read-write lock that uses the lock annotations. */ + +struct LOCKABLE rwlock { + pthread_rwlock_t object; +}; + +static inline bool +rwlock_init(struct rwlock *lock) REQUIRES_UNLOCKED(*lock) +{ + return pthread_rwlock_init(&lock->object, NULL) == 0 ? true : false; +} + +static inline void +rwlock_rdlock(struct rwlock *lock) LOCKS_SHARED(*lock) NO_LOCK_ANALYSIS +{ + pthread_rwlock_rdlock(&lock->object); +} + +static inline void +rwlock_wrlock(struct rwlock *lock) LOCKS_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + pthread_rwlock_wrlock(&lock->object); +} + +static inline void +rwlock_unlock(struct rwlock *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + pthread_rwlock_unlock(&lock->object); +} + +static inline void +rwlock_destroy(struct rwlock *lock) UNLOCKS(*lock) NO_LOCK_ANALYSIS +{ + pthread_rwlock_destroy(&lock->object); +} + +/* Condition variable that uses the lock annotations. */ + +struct LOCKABLE cond { + pthread_cond_t object; +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + clockid_t clock; +#endif +}; + +static inline bool +cond_init_monotonic(struct cond *cond) { + bool ret = false; +#if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr) != 0) + return false; + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) != 0) + goto fail; + + if (pthread_cond_init(&cond->object, &attr) != 0) + goto fail; + + ret = true; +fail: + pthread_condattr_destroy(&attr); +#else + if (pthread_cond_init(&cond->object, NULL) != 0) + return false; + ret = true; +#endif + +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + cond->clock = CLOCK_MONOTONIC; +#endif + return ret; +} + +static inline bool +cond_init_realtime(struct cond *cond) +{ + if (pthread_cond_init(&cond->object, NULL) != 0) + return false; +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK || \ + !CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + cond->clock = CLOCK_REALTIME; +#endif + return true; +} + +static inline void +cond_destroy(struct cond *cond) { + pthread_cond_destroy(&cond->object); +} + +static inline void +cond_signal(struct cond *cond) { + pthread_cond_signal(&cond->object); +} + +#if !CONFIG_HAS_CLOCK_NANOSLEEP +static inline bool +cond_timedwait(struct cond *cond, struct mutex *lock, + uint64_t timeout, bool abstime) + REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + int ret; + struct timespec ts = { + .tv_sec = (time_t)(timeout / 1000000000), + .tv_nsec = (long)(timeout % 1000000000), + }; + + if (abstime) { +#if !CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + /** + * No native support for sleeping on monotonic clocks. Convert the + * timeout to a relative value and then to an absolute value for the + * realtime clock. + */ + if (cond->clock != CLOCK_REALTIME) { + struct timespec ts_monotonic; + struct timespec ts_realtime; + + clock_gettime(cond->clock, &ts_monotonic); + ts.tv_sec -= ts_monotonic.tv_sec; + ts.tv_nsec -= ts_monotonic.tv_nsec; + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1000000000; + --ts.tv_sec; + } + + clock_gettime(CLOCK_REALTIME, &ts_realtime); + ts.tv_sec += ts_realtime.tv_sec; + ts.tv_nsec += ts_realtime.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } + } +#endif + } + else { +#if CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP + /* Implementation supports relative timeouts. */ + ret = pthread_cond_timedwait_relative_np(&cond->object, + &lock->object, &ts); + bh_assert((ret == 0 || ret == ETIMEDOUT) + && "pthread_cond_timedwait_relative_np() failed"); + return ret == ETIMEDOUT; +#else + /* Convert to absolute timeout. */ + struct timespec ts_now; +#if CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK + clock_gettime(cond->clock, &ts_now); +#else + clock_gettime(CLOCK_REALTIME, &ts_now); +#endif + ts.tv_sec += ts_now.tv_sec; + ts.tv_nsec += ts_now.tv_nsec; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ++ts.tv_sec; + } +#endif + } + + ret = pthread_cond_timedwait(&cond->object, &lock->object, &ts); + bh_assert((ret == 0 || ret == ETIMEDOUT) + && "pthread_cond_timedwait() failed"); + return ret == ETIMEDOUT; +} +#endif + +static inline void +cond_wait(struct cond *cond, struct mutex *lock) + REQUIRES_EXCLUSIVE(*lock) NO_LOCK_ANALYSIS +{ + pthread_cond_wait(&cond->object, &lock->object); +} + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h new file mode 100644 index 0000000..2b81527 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/numeric_limits.h @@ -0,0 +1,40 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2015 Nuxi, https://nuxi.nl/ + +#ifndef COMMON_LIMITS_H +#define COMMON_LIMITS_H + +#define NUMERIC_MIN(t) \ + _Generic((t)0, char \ + : CHAR_MIN, signed char \ + : SCHAR_MIN, unsigned char : 0, short \ + : SHRT_MIN, unsigned short : 0, int \ + : INT_MIN, unsigned int : 0, long \ + : LONG_MIN, unsigned long : 0, long long \ + : LLONG_MIN, unsigned long long : 0, default \ + : (void)0) + +#define NUMERIC_MAX(t) \ + _Generic((t)0, char \ + : CHAR_MAX, signed char \ + : SCHAR_MAX, unsigned char \ + : UCHAR_MAX, short \ + : SHRT_MAX, unsigned short \ + : USHRT_MAX, int \ + : INT_MAX, unsigned int \ + : UINT_MAX, long \ + : LONG_MAX, unsigned long \ + : ULONG_MAX, long long \ + : LLONG_MAX, unsigned long long \ + : ULLONG_MAX, default \ + : (void)0) + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c new file mode 100644 index 0000000..90978a1 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.c @@ -0,0 +1,2970 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016-2018 Nuxi, https://nuxi.nl/ + +#include "ssp_config.h" +#include "bh_platform.h" +#include "wasmtime_ssp.h" +#include "locking.h" +#include "numeric_limits.h" +#include "posix.h" +#include "random.h" +#include "refcount.h" +#include "rights.h" +#include "str.h" + +#if 0 /* TODO: -std=gnu99 causes compile error, comment them first */ +// struct iovec must have the same layout as __wasi_iovec_t. +static_assert(offsetof(struct iovec, iov_base) == + offsetof(__wasi_iovec_t, buf), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_base) == + sizeof(((__wasi_iovec_t *)0)->buf), + "Size mismatch"); +static_assert(offsetof(struct iovec, iov_len) == + offsetof(__wasi_iovec_t, buf_len), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_len) == + sizeof(((__wasi_iovec_t *)0)->buf_len), + "Size mismatch"); +static_assert(sizeof(struct iovec) == sizeof(__wasi_iovec_t), + "Size mismatch"); + +// struct iovec must have the same layout as __wasi_ciovec_t. +static_assert(offsetof(struct iovec, iov_base) == + offsetof(__wasi_ciovec_t, buf), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_base) == + sizeof(((__wasi_ciovec_t *)0)->buf), + "Size mismatch"); +static_assert(offsetof(struct iovec, iov_len) == + offsetof(__wasi_ciovec_t, buf_len), + "Offset mismatch"); +static_assert(sizeof(((struct iovec *)0)->iov_len) == + sizeof(((__wasi_ciovec_t *)0)->buf_len), + "Size mismatch"); +static_assert(sizeof(struct iovec) == sizeof(__wasi_ciovec_t), + "Size mismatch"); +#endif + +#if defined(WASMTIME_SSP_STATIC_CURFDS) +static __thread struct fd_table *curfds; +static __thread struct fd_prestats *prestats; +static __thread struct argv_environ_values *argv_environ; +#endif + +// Converts a POSIX error code to a CloudABI error code. +static __wasi_errno_t convert_errno(int error) { + static const __wasi_errno_t errors[] = { +#define X(v) [v] = __WASI_##v + X(E2BIG), + X(EACCES), + X(EADDRINUSE), + X(EADDRNOTAVAIL), + X(EAFNOSUPPORT), + X(EAGAIN), + X(EALREADY), + X(EBADF), + X(EBADMSG), + X(EBUSY), + X(ECANCELED), + X(ECHILD), + X(ECONNABORTED), + X(ECONNREFUSED), + X(ECONNRESET), + X(EDEADLK), + X(EDESTADDRREQ), + X(EDOM), + X(EDQUOT), + X(EEXIST), + X(EFAULT), + X(EFBIG), + X(EHOSTUNREACH), + X(EIDRM), + X(EILSEQ), + X(EINPROGRESS), + X(EINTR), + X(EINVAL), + X(EIO), + X(EISCONN), + X(EISDIR), + X(ELOOP), + X(EMFILE), + X(EMLINK), + X(EMSGSIZE), + X(EMULTIHOP), + X(ENAMETOOLONG), + X(ENETDOWN), + X(ENETRESET), + X(ENETUNREACH), + X(ENFILE), + X(ENOBUFS), + X(ENODEV), + X(ENOENT), + X(ENOEXEC), + X(ENOLCK), + X(ENOLINK), + X(ENOMEM), + X(ENOMSG), + X(ENOPROTOOPT), + X(ENOSPC), + X(ENOSYS), +#ifdef ENOTCAPABLE + X(ENOTCAPABLE), +#endif + X(ENOTCONN), + X(ENOTDIR), + X(ENOTEMPTY), + X(ENOTRECOVERABLE), + X(ENOTSOCK), + X(ENOTSUP), + X(ENOTTY), + X(ENXIO), + X(EOVERFLOW), + X(EOWNERDEAD), + X(EPERM), + X(EPIPE), + X(EPROTO), + X(EPROTONOSUPPORT), + X(EPROTOTYPE), + X(ERANGE), + X(EROFS), + X(ESPIPE), + X(ESRCH), + X(ESTALE), + X(ETIMEDOUT), + X(ETXTBSY), + X(EXDEV), +#undef X +#if EOPNOTSUPP != ENOTSUP + [EOPNOTSUPP] = __WASI_ENOTSUP, +#endif +#if EWOULDBLOCK != EAGAIN + [EWOULDBLOCK] = __WASI_EAGAIN, +#endif + }; + if (error < 0 || (size_t)error >= sizeof(errors) / sizeof(errors[0]) || + errors[error] == 0) + return __WASI_ENOSYS; + return errors[error]; +} + +// Converts a POSIX timespec to a CloudABI timestamp. +static __wasi_timestamp_t convert_timespec( + const struct timespec *ts +) { + if (ts->tv_sec < 0) + return 0; + if ((__wasi_timestamp_t)ts->tv_sec >= UINT64_MAX / 1000000000) + return UINT64_MAX; + return (__wasi_timestamp_t)ts->tv_sec * 1000000000 + (__wasi_timestamp_t)ts->tv_nsec; +} + +// Converts a CloudABI clock identifier to a POSIX clock identifier. +static bool convert_clockid( + __wasi_clockid_t in, + clockid_t *out +) { + switch (in) { + case __WASI_CLOCK_MONOTONIC: + *out = CLOCK_MONOTONIC; + return true; + case __WASI_CLOCK_PROCESS_CPUTIME_ID: + *out = CLOCK_PROCESS_CPUTIME_ID; + return true; + case __WASI_CLOCK_REALTIME: + *out = CLOCK_REALTIME; + return true; + case __WASI_CLOCK_THREAD_CPUTIME_ID: + *out = CLOCK_THREAD_CPUTIME_ID; + return true; + default: + return false; + } +} + +__wasi_errno_t wasmtime_ssp_clock_res_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t *resolution +) { + clockid_t nclock_id; + if (!convert_clockid(clock_id, &nclock_id)) + return __WASI_EINVAL; + struct timespec ts; + if (clock_getres(nclock_id, &ts) < 0) + return convert_errno(errno); + *resolution = convert_timespec(&ts); + return 0; +} + +__wasi_errno_t wasmtime_ssp_clock_time_get( + __wasi_clockid_t clock_id, + __wasi_timestamp_t precision, + __wasi_timestamp_t *time +) { + clockid_t nclock_id; + if (!convert_clockid(clock_id, &nclock_id)) + return __WASI_EINVAL; + struct timespec ts; + if (clock_gettime(nclock_id, &ts) < 0) + return convert_errno(errno); + *time = convert_timespec(&ts); + return 0; +} + +struct fd_prestat { + const char *dir; +}; + +bool fd_prestats_init( + struct fd_prestats *pt +) { + if (!rwlock_init(&pt->lock)) + return false; + pt->prestats = NULL; + pt->size = 0; + pt->used = 0; +#if defined(WASMTIME_SSP_STATIC_CURFDS) + prestats = pt; +#endif + return true; +} + +// Grows the preopened resource table to a required lower bound and a +// minimum number of free preopened resource table entries. +static bool fd_prestats_grow( + struct fd_prestats *pt, + size_t min, + size_t incr +) REQUIRES_EXCLUSIVE(pt->lock) { + if (pt->size <= min || pt->size < (pt->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = pt->size == 0 ? 1 : pt->size; + while (size <= min || size < (pt->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_prestat *prestats = wasm_runtime_malloc((uint32)(sizeof(*prestats) * size)); + if (prestats == NULL) + return false; + + if (pt->prestats && pt->size > 0) { + bh_memcpy_s(prestats, (uint32)(sizeof(*prestats) * size), + pt->prestats, (uint32)(sizeof(*prestats) * pt->size)); + } + + if (pt->prestats) + wasm_runtime_free(pt->prestats); + + // Mark all new file descriptors as unused. + for (size_t i = pt->size; i < size; ++i) + prestats[i].dir = NULL; + pt->prestats = prestats; + pt->size = size; + } + return true; +} + +// Inserts a preopened resource record into the preopened resource table. +bool fd_prestats_insert( + struct fd_prestats *pt, + const char *dir, + __wasi_fd_t fd +) { + // Grow the preopened resource table if needed. + rwlock_wrlock(&pt->lock); + if (!fd_prestats_grow(pt, fd, 1)) { + rwlock_unlock(&pt->lock); + return false; + } + + pt->prestats[fd].dir = bh_strdup(dir); + rwlock_unlock(&pt->lock); + + if (pt->prestats[fd].dir == NULL) + return false; + + return true; +} + +// Looks up a preopened resource table entry by number. +static __wasi_errno_t fd_prestats_get_entry( + struct fd_prestats *pt, + __wasi_fd_t fd, + struct fd_prestat **ret +) REQUIRES_SHARED(pt->lock) { + // Test for file descriptor existence. + if (fd >= pt->size) + return __WASI_EBADF; + struct fd_prestat *prestat = &pt->prestats[fd]; + if (prestat->dir == NULL) + return __WASI_EBADF; + + *ret = prestat; + return 0; +} + +struct fd_object { + struct refcount refcount; + __wasi_filetype_t type; + int number; + + union { + // Data associated with directory file descriptors. + struct { + struct mutex lock; // Lock to protect members below. + DIR *handle; // Directory handle. + __wasi_dircookie_t offset; // Offset of the directory. + } directory; + }; +}; + +struct fd_entry { + struct fd_object *object; + __wasi_rights_t rights_base; + __wasi_rights_t rights_inheriting; +}; + +bool fd_table_init( + struct fd_table *ft +) { + if (!rwlock_init(&ft->lock)) + return false; + ft->entries = NULL; + ft->size = 0; + ft->used = 0; +#if defined(WASMTIME_SSP_STATIC_CURFDS) + curfds = ft; +#endif + return true; +} + +// Looks up a file descriptor table entry by number and required rights. +static __wasi_errno_t fd_table_get_entry( + struct fd_table *ft, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + struct fd_entry **ret +) REQUIRES_SHARED(ft->lock) { + // Test for file descriptor existence. + if (fd >= ft->size) + return __WASI_EBADF; + struct fd_entry *fe = &ft->entries[fd]; + if (fe->object == NULL) + return __WASI_EBADF; + + // Validate rights. + if ((~fe->rights_base & rights_base) != 0 || + (~fe->rights_inheriting & rights_inheriting) != 0) + return __WASI_ENOTCAPABLE; + *ret = fe; + return 0; +} + +// Grows the file descriptor table to a required lower bound and a +// minimum number of free file descriptor table entries. +static bool fd_table_grow( + struct fd_table *ft, + size_t min, + size_t incr +) REQUIRES_EXCLUSIVE(ft->lock) { + if (ft->size <= min || ft->size < (ft->used + incr) * 2) { + // Keep on doubling the table size until we've met our constraints. + size_t size = ft->size == 0 ? 1 : ft->size; + while (size <= min || size < (ft->used + incr) * 2) + size *= 2; + + // Grow the file descriptor table's allocation. + struct fd_entry *entries = wasm_runtime_malloc((uint32)(sizeof(*entries) * size)); + if (entries == NULL) + return false; + + if (ft->entries && ft->size > 0) { + bh_memcpy_s(entries, (uint32)(sizeof(*entries) * size), + ft->entries, (uint32)(sizeof(*entries) * ft->size)); + } + + if (ft->entries) + wasm_runtime_free(ft->entries); + + // Mark all new file descriptors as unused. + for (size_t i = ft->size; i < size; ++i) + entries[i].object = NULL; + ft->entries = entries; + ft->size = size; + } + return true; +} + +// Allocates a new file descriptor object. +static __wasi_errno_t fd_object_new( + __wasi_filetype_t type, + struct fd_object **fo +) TRYLOCKS_SHARED(0, (*fo)->refcount) { + *fo = wasm_runtime_malloc(sizeof(**fo)); + if (*fo == NULL) + return __WASI_ENOMEM; + refcount_init(&(*fo)->refcount, 1); + (*fo)->type = type; + (*fo)->number = -1; + return 0; +} + +// Attaches a file descriptor to the file descriptor table. +static void fd_table_attach( + struct fd_table *ft, + __wasi_fd_t fd, + struct fd_object *fo, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) REQUIRES_EXCLUSIVE(ft->lock) CONSUMES(fo->refcount) { + assert(ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + assert(fe->object == NULL && "Attempted to overwrite an existing descriptor"); + fe->object = fo; + fe->rights_base = rights_base; + fe->rights_inheriting = rights_inheriting; + ++ft->used; + assert(ft->size >= ft->used * 2 && "File descriptor too full"); +} + +// Detaches a file descriptor from the file descriptor table. +static void fd_table_detach( + struct fd_table *ft, + __wasi_fd_t fd, + struct fd_object **fo +) REQUIRES_EXCLUSIVE(ft->lock) PRODUCES((*fo)->refcount) { + assert(ft->size > fd && "File descriptor table too small"); + struct fd_entry *fe = &ft->entries[fd]; + *fo = fe->object; + assert(*fo != NULL && "Attempted to detach nonexistent descriptor"); + fe->object = NULL; + assert(ft->used > 0 && "Reference count mismatch"); + --ft->used; +} + +// Determines the type of a file descriptor and its maximum set of +// rights that should be attached to it. +static __wasi_errno_t fd_determine_type_rights( + int fd, + __wasi_filetype_t *type, + __wasi_rights_t *rights_base, + __wasi_rights_t *rights_inheriting +) { + struct stat sb; + if (fstat(fd, &sb) < 0) + return convert_errno(errno); + if (S_ISBLK(sb.st_mode)) { + *type = __WASI_FILETYPE_BLOCK_DEVICE; + *rights_base = RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = RIGHTS_BLOCK_DEVICE_INHERITING; + } else if (S_ISCHR(sb.st_mode)) { + *type = __WASI_FILETYPE_CHARACTER_DEVICE; +#if CONFIG_HAS_ISATTY + if (isatty(fd)) { + *rights_base = RIGHTS_TTY_BASE; + *rights_inheriting = RIGHTS_TTY_INHERITING; + } else +#endif + { + *rights_base = RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = RIGHTS_CHARACTER_DEVICE_INHERITING; + } + } else if (S_ISDIR(sb.st_mode)) { + *type = __WASI_FILETYPE_DIRECTORY; + *rights_base = RIGHTS_DIRECTORY_BASE; + *rights_inheriting = RIGHTS_DIRECTORY_INHERITING; + } else if (S_ISREG(sb.st_mode)) { + *type = __WASI_FILETYPE_REGULAR_FILE; + *rights_base = RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = RIGHTS_REGULAR_FILE_INHERITING; + } else if (S_ISSOCK(sb.st_mode)) { + int socktype; + socklen_t socktypelen = sizeof(socktype); + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &socktype, &socktypelen) < 0) + return convert_errno(errno); + switch (socktype) { + case SOCK_DGRAM: + *type = __WASI_FILETYPE_SOCKET_DGRAM; + break; + case SOCK_STREAM: + *type = __WASI_FILETYPE_SOCKET_STREAM; + break; + default: + return __WASI_EINVAL; + } + *rights_base = RIGHTS_SOCKET_BASE; + *rights_inheriting = RIGHTS_SOCKET_INHERITING; + } else if (S_ISFIFO(sb.st_mode)) { + *type = __WASI_FILETYPE_SOCKET_STREAM; + *rights_base = RIGHTS_SOCKET_BASE; + *rights_inheriting = RIGHTS_SOCKET_INHERITING; + } else { + return __WASI_EINVAL; + } + + // Strip off read/write bits based on the access mode. + switch (fcntl(fd, F_GETFL) & O_ACCMODE) { + case O_RDONLY: + *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_WRITE; + break; + case O_WRONLY: + *rights_base &= ~(__wasi_rights_t)__WASI_RIGHT_FD_READ; + break; + } + return 0; +} + +// Returns the underlying file descriptor number of a file descriptor +// object. This function can only be applied to objects that have an +// underlying file descriptor number. +static int fd_number( + const struct fd_object *fo +) { + int number = fo->number; + assert(number >= 0 && "fd_number() called on virtual file descriptor"); + return number; +} + +#define CLOSE_NON_STD_FD(fd) do { \ + if (fd > 2) \ + close(fd); \ + } while (0) + +// Lowers the reference count on a file descriptor object. When the +// reference count reaches zero, its resources are cleaned up. +static void fd_object_release( + struct fd_object *fo +) UNLOCKS(fo->refcount) { + if (refcount_release(&fo->refcount)) { + switch (fo->type) { + case __WASI_FILETYPE_DIRECTORY: + // For directories we may keep track of a DIR object. Calling + // closedir() on it also closes the underlying file descriptor. + mutex_destroy(&fo->directory.lock); + if (fo->directory.handle == NULL) { + CLOSE_NON_STD_FD(fd_number(fo)); + } else { + closedir(fo->directory.handle); + } + break; + default: + CLOSE_NON_STD_FD(fd_number(fo)); + break; + } + wasm_runtime_free(fo); + } +} + +// Inserts an already existing file descriptor into the file descriptor +// table. +bool fd_table_insert_existing( + struct fd_table *ft, + __wasi_fd_t in, + int out +) { + __wasi_filetype_t type; + __wasi_rights_t rights_base, rights_inheriting; + struct fd_object *fo; + __wasi_errno_t error; + + if (fd_determine_type_rights(out, &type, &rights_base, + &rights_inheriting) != 0) + return false; + + error = fd_object_new(type, &fo); + if (error != 0) + return false; + fo->number = out; + if (type == __WASI_FILETYPE_DIRECTORY) { + if (!mutex_init(&fo->directory.lock)) { + fd_object_release(fo); + return false; + } + fo->directory.handle = NULL; + } + + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, in, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return false; + } + + fd_table_attach(ft, in, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return true; +} + +// Picks an unused slot from the file descriptor table. +static __wasi_fd_t fd_table_unused( + struct fd_table *ft +) REQUIRES_SHARED(ft->lock) { + assert(ft->size > ft->used && "File descriptor table has no free slots"); + for (;;) { + __wasi_fd_t fd = (__wasi_fd_t)random_uniform(ft->size); + if (ft->entries[fd].object == NULL) + return fd; + } +} + +// Inserts a file descriptor object into an unused slot of the file +// descriptor table. +static __wasi_errno_t fd_table_insert( + struct fd_table *ft, + struct fd_object *fo, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + __wasi_fd_t *out +) REQUIRES_UNLOCKED(ft->lock) UNLOCKS(fo->refcount) { + // Grow the file descriptor table if needed. + rwlock_wrlock(&ft->lock); + if (!fd_table_grow(ft, 0, 1)) { + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return convert_errno(errno); + } + + *out = fd_table_unused(ft); + fd_table_attach(ft, *out, fo, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return 0; +} + +// Inserts a numerical file descriptor into the file descriptor table. +static __wasi_errno_t fd_table_insert_fd( + struct fd_table *ft, + int in, + __wasi_filetype_t type, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + __wasi_fd_t *out +) REQUIRES_UNLOCKED(ft->lock) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_new(type, &fo); + + if (error != 0) { + close(in); + return error; + } + fo->number = in; + if (type == __WASI_FILETYPE_DIRECTORY) { + if (!mutex_init(&fo->directory.lock)) { + fd_object_release(fo); + return (__wasi_errno_t)-1; + } + fo->directory.handle = NULL; + } + return fd_table_insert(ft, fo, rights_base, rights_inheriting, out); +} + +__wasi_errno_t wasmtime_ssp_fd_prestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + __wasi_prestat_t *buf +) { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + + *buf = (__wasi_prestat_t) { + .pr_type = __WASI_PREOPENTYPE_DIR, + }; + + buf->u.dir.pr_name_len = strlen(prestat->dir); + + rwlock_unlock(&prestats->lock); + + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_prestat_dir_name( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd, + char *path, + size_t path_len +) { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + if (error != 0) { + rwlock_unlock(&prestats->lock); + return error; + } + if (path_len != strlen(prestat->dir)) { + rwlock_unlock(&prestats->lock); + return EINVAL; + } + + bh_memcpy_s(path, (uint32)path_len, prestat->dir, (uint32)path_len); + + rwlock_unlock(&prestats->lock); + + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_close( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t fd +) { + // Don't allow closing a pre-opened resource. + // TODO: Eventually, we do want to permit this, once libpreopen in + // userspace is capable of removing entries from its tables as well. + { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, fd, &prestat); + rwlock_unlock(&prestats->lock); + if (error == 0) { + return __WASI_ENOTSUP; + } + } + + // Validate the file descriptor. + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Remove it from the file descriptor table. + struct fd_object *fo; + fd_table_detach(ft, fd, &fo); + rwlock_unlock(&ft->lock); + fd_object_release(fo); + return 0; +} + +// Look up a file descriptor object in a locked file descriptor table +// and increases its reference count. +static __wasi_errno_t fd_object_get_locked( + struct fd_object **fo, + struct fd_table *ft, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) REQUIRES_EXCLUSIVE(ft->lock) { + // Test whether the file descriptor number is valid. + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, rights_base, rights_inheriting, &fe); + if (error != 0) + return error; + + // Increase the reference count on the file descriptor object. A copy + // of the rights are also stored, so callers can still access those if + // needed. + *fo = fe->object; + refcount_acquire(&(*fo)->refcount); + return 0; +} + +// Temporarily locks the file descriptor table to look up a file +// descriptor object, increases its reference count and drops the lock. +static __wasi_errno_t fd_object_get( + struct fd_table *curfds, + struct fd_object **fo, + __wasi_fd_t fd, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting +) TRYLOCKS_EXCLUSIVE(0, (*fo)->refcount) { + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + __wasi_errno_t error = + fd_object_get_locked(fo, ft, fd, rights_base, rights_inheriting); + rwlock_unlock(&ft->lock); + return error; +} + +__wasi_errno_t wasmtime_ssp_fd_datasync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_DATASYNC, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_FDATASYNC + int ret = fdatasync(fd_number(fo)); +#else + int ret = fsync(fd_number(fo)); +#endif + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_pread( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iov, + size_t iovcnt, + __wasi_filesize_t offset, + size_t *nread +) { + if (iovcnt == 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, + &fo, fd, __WASI_RIGHT_FD_READ, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_PREADV + ssize_t len = + preadv(fd_number(fo), (const struct iovec *)iov, (int)iovcnt, (off_t)offset); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = (size_t)len; + return 0; +#else + if (iovcnt == 1) { + ssize_t len = pread(fd_number(fo), iov->buf, iov->buf_len, offset); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = len; + return 0; + } else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (size_t i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = wasm_runtime_malloc(totalsize); + if (buf == NULL) { + fd_object_release(fo); + return __WASI_ENOMEM; + } + + // Perform a single read operation. + ssize_t len = pread(fd_number(fo), buf, totalsize, offset); + fd_object_release(fo); + if (len < 0) { + wasm_runtime_free(buf); + return convert_errno(errno); + } + + // Copy data back to vectors. + size_t bufoff = 0; + for (size_t i = 0; i < iovcnt; ++i) { + if (bufoff + iov[i].buf_len < len) { + bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, iov[i].buf_len); + bufoff += iov[i].buf_len; + } else { + bh_memcpy_s(iov[i].buf, iov[i].buf_len, buf + bufoff, len - bufoff); + break; + } + } + wasm_runtime_free(buf); + *nread = len; + return 0; + } +#endif +} + +__wasi_errno_t wasmtime_ssp_fd_pwrite( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iov, + size_t iovcnt, + __wasi_filesize_t offset, + size_t *nwritten +) { + if (iovcnt == 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, + &fo, fd, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + return error; + + ssize_t len; +#if CONFIG_HAS_PWRITEV + len = pwritev(fd_number(fo), (const struct iovec *)iov, (int)iovcnt, (off_t)offset); +#else + if (iovcnt == 1) { + len = pwrite(fd_number(fo), iov->buf, iov->buf_len, offset); + } else { + // Allocate a single buffer to fit all data. + size_t totalsize = 0; + for (size_t i = 0; i < iovcnt; ++i) + totalsize += iov[i].buf_len; + char *buf = wasm_runtime_malloc(totalsize); + if (buf == NULL) { + fd_object_release(fo); + return __WASI_ENOMEM; + } + size_t bufoff = 0; + for (size_t i = 0; i < iovcnt; ++i) { + bh_memcpy_s(buf + bufoff, totalsize - bufoff, + iov[i].buf, iov[i].buf_len); + bufoff += iov[i].buf_len; + } + + // Perform a single write operation. + len = pwrite(fd_number(fo), buf, totalsize, offset); + wasm_runtime_free(buf); + } +#endif + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nwritten = (size_t)len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_read( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_iovec_t *iov, + size_t iovcnt, + size_t *nread +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READ, 0); + if (error != 0) + return error; + + ssize_t len = readv(fd_number(fo), (const struct iovec *)iov, (int)iovcnt); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nread = (size_t)len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_renumber( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t from, + __wasi_fd_t to +) { + // Don't allow renumbering over a pre-opened resource. + // TODO: Eventually, we do want to permit this, once libpreopen in + // userspace is capable of removing entries from its tables as well. + { + rwlock_rdlock(&prestats->lock); + struct fd_prestat *prestat; + __wasi_errno_t error = fd_prestats_get_entry(prestats, to, &prestat); + if (error != 0) { + error = fd_prestats_get_entry(prestats, from, &prestat); + } + rwlock_unlock(&prestats->lock); + if (error == 0) { + return __WASI_ENOTSUP; + } + } + + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe_from; + __wasi_errno_t error = fd_table_get_entry(ft, from, 0, 0, &fe_from); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + struct fd_entry *fe_to; + error = fd_table_get_entry(ft, to, 0, 0, &fe_to); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + struct fd_object *fo; + fd_table_detach(ft, to, &fo); + refcount_acquire(&fe_from->object->refcount); + fd_table_attach(ft, to, fe_from->object, fe_from->rights_base, + fe_from->rights_inheriting); + fd_object_release(fo); + + // Remove the old fd from the file descriptor table. + fd_table_detach(ft, from, &fo); + fd_object_release(fo); + --ft->used; + + rwlock_unlock(&ft->lock); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_seek( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filedelta_t offset, + __wasi_whence_t whence, + __wasi_filesize_t *newoffset +) { + int nwhence; + switch (whence) { + case __WASI_WHENCE_CUR: + nwhence = SEEK_CUR; + break; + case __WASI_WHENCE_END: + nwhence = SEEK_END; + break; + case __WASI_WHENCE_SET: + nwhence = SEEK_SET; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, + offset == 0 && whence == __WASI_WHENCE_CUR + ? __WASI_RIGHT_FD_TELL + : __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_TELL, + 0); + if (error != 0) + return error; + + off_t ret = lseek(fd_number(fo), offset, nwhence); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + *newoffset = (__wasi_filesize_t)ret; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_tell( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t *newoffset +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_TELL, 0); + if (error != 0) + return error; + + off_t ret = lseek(fd_number(fo), 0, SEEK_CUR); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + *newoffset = (__wasi_filesize_t)ret; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdstat_t *buf +) { + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = fd_table_get_entry(ft, fd, 0, 0, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Extract file descriptor type and rights. + struct fd_object *fo = fe->object; + *buf = (__wasi_fdstat_t){ + .fs_filetype = fo->type, + .fs_rights_base = fe->rights_base, + .fs_rights_inheriting = fe->rights_inheriting, + }; + + // Fetch file descriptor flags. + int ret; + switch (fo->type) { + default: + ret = fcntl(fd_number(fo), F_GETFL); + break; + } + rwlock_unlock(&ft->lock); + if (ret < 0) + return convert_errno(errno); + + if ((ret & O_APPEND) != 0) + buf->fs_flags |= __WASI_FDFLAG_APPEND; +#ifdef O_DSYNC + if ((ret & O_DSYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_DSYNC; +#endif + if ((ret & O_NONBLOCK) != 0) + buf->fs_flags |= __WASI_FDFLAG_NONBLOCK; +#ifdef O_RSYNC + if ((ret & O_RSYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_RSYNC; +#endif + if ((ret & O_SYNC) != 0) + buf->fs_flags |= __WASI_FDFLAG_SYNC; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_flags( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_fdflags_t fs_flags +) { + int noflags = 0; + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + noflags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) +#ifdef O_DSYNC + noflags |= O_DSYNC; +#else + noflags |= O_SYNC; +#endif + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + noflags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) +#ifdef O_RSYNC + noflags |= O_RSYNC; +#else + noflags |= O_SYNC; +#endif + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) + noflags |= O_SYNC; + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FDSTAT_SET_FLAGS, 0); + if (error != 0) + return error; + + int ret = fcntl(fd_number(fo), F_SETFL, noflags); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_fdstat_set_rights( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting +) { + struct fd_table *ft = curfds; + rwlock_wrlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, fs_rights_base, fs_rights_inheriting, &fe); + if (error != 0) { + rwlock_unlock(&ft->lock); + return error; + } + + // Restrict the rights on the file descriptor. + fe->rights_base = fs_rights_base; + fe->rights_inheriting = fs_rights_inheriting; + rwlock_unlock(&ft->lock); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_sync( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_SYNC, 0); + if (error != 0) + return error; + + int ret = fsync(fd_number(fo)); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_write( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const __wasi_ciovec_t *iov, + size_t iovcnt, + size_t *nwritten +) { + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + return error; + + ssize_t len = writev(fd_number(fo), (const struct iovec *)iov, (int)iovcnt); + fd_object_release(fo); + if (len < 0) + return convert_errno(errno); + *nwritten = (size_t)len; + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_advise( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len, + __wasi_advice_t advice +) { +#ifdef POSIX_FADV_NORMAL + int nadvice; + switch (advice) { + case __WASI_ADVICE_DONTNEED: + nadvice = POSIX_FADV_DONTNEED; + break; + case __WASI_ADVICE_NOREUSE: + nadvice = POSIX_FADV_NOREUSE; + break; + case __WASI_ADVICE_NORMAL: + nadvice = POSIX_FADV_NORMAL; + break; + case __WASI_ADVICE_RANDOM: + nadvice = POSIX_FADV_RANDOM; + break; + case __WASI_ADVICE_SEQUENTIAL: + nadvice = POSIX_FADV_SEQUENTIAL; + break; + case __WASI_ADVICE_WILLNEED: + nadvice = POSIX_FADV_WILLNEED; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ADVISE, 0); + if (error != 0) + return error; + + int ret = posix_fadvise(fd_number(fo), (off_t)offset, (off_t)len, nadvice); + fd_object_release(fo); + if (ret != 0) + return convert_errno(ret); + return 0; +#else + // Advisory information can safely be ignored if unsupported. + switch (advice) { + case __WASI_ADVICE_DONTNEED: + case __WASI_ADVICE_NOREUSE: + case __WASI_ADVICE_NORMAL: + case __WASI_ADVICE_RANDOM: + case __WASI_ADVICE_SEQUENTIAL: + case __WASI_ADVICE_WILLNEED: + break; + default: + return __WASI_EINVAL; + } + + // At least check for file descriptor existence. + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + struct fd_entry *fe; + __wasi_errno_t error = + fd_table_get_entry(ft, fd, __WASI_RIGHT_FD_ADVISE, 0, &fe); + rwlock_unlock(&ft->lock); + return error; +#endif +} + +__wasi_errno_t wasmtime_ssp_fd_allocate( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t offset, + __wasi_filesize_t len +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_ALLOCATE, 0); + if (error != 0) + return error; + +#if CONFIG_HAS_POSIX_FALLOCATE + int ret = posix_fallocate(fd_number(fo), (off_t)offset, (off_t)len); +#else + // At least ensure that the file is grown to the right size. + // TODO(ed): See if this can somehow be implemented without any race + // conditions. We may end up shrinking the file right now. + struct stat sb; + int ret = fstat(fd_number(fo), &sb); + if (ret == 0 && sb.st_size < offset + len) + ret = ftruncate(fd_number(fo), offset + len); +#endif + + fd_object_release(fo); + if (ret != 0) + return convert_errno(ret); + return 0; +} + +// Reads the entire contents of a symbolic link, returning the contents +// in an allocated buffer. The allocated buffer is large enough to fit +// at least one extra byte, so the caller may append a trailing slash to +// it. This is needed by path_get(). +static char *readlinkat_dup( + int fd, + const char *path, + size_t *p_len +) { + char *buf = NULL; + size_t len = 32; + size_t len_org = len; + + for (;;) { + char *newbuf = wasm_runtime_malloc((uint32)len); + + if (newbuf == NULL) { + if (buf) + wasm_runtime_free(buf); + return NULL; + } + + if (buf != NULL) { + bh_memcpy_s(newbuf, (uint32)len, buf, (uint32)len_org); + wasm_runtime_free(buf); + } + + buf = newbuf; + ssize_t ret = readlinkat(fd, path, buf, len); + if (ret < 0) { + wasm_runtime_free(buf); + return NULL; + } + if ((size_t)ret + 1 < len) { + buf[ret] = '\0'; + *p_len = len; + return buf; + } + len_org = len; + len *= 2; + } +} + +// Lease to a directory, so a path underneath it can be accessed. +// +// This structure is used by system calls that operate on pathnames. In +// this environment, pathnames always consist of a pair of a file +// descriptor representing the directory where the lookup needs to start +// and the actual pathname string. +struct path_access { + int fd; // Directory file descriptor. + const char *path; // Pathname. + bool follow; // Whether symbolic links should be followed. + char *path_start; // Internal: pathname to free. + struct fd_object *fd_object; // Internal: directory file descriptor object. +}; + +// Creates a lease to a file descriptor and pathname pair. If the +// operating system does not implement Capsicum, it also normalizes the +// pathname to ensure the target path is placed underneath the +// directory. +static __wasi_errno_t path_get( + struct fd_table *curfds, + struct path_access *pa, + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *upath, + size_t upathlen, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + bool needs_final_component +) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { + char *path = str_nullterminate(upath, upathlen); + if (path == NULL) + return convert_errno(errno); + + // Fetch the directory file descriptor. + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, rights_base, rights_inheriting); + if (error != 0) { + wasm_runtime_free(path); + return error; + } + +#if CONFIG_HAS_CAP_ENTER + // Rely on the kernel to constrain access to automatically constrain + // access to files stored underneath this directory. + pa->fd = fd_number(fo); + pa->path = pa->path_start = path; + pa->follow = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0; + pa->fd_object = fo; + return 0; +#else + // The implementation provides no mechanism to constrain lookups to a + // directory automatically. Emulate this logic by resolving the + // pathname manually. + + // Stack of directory file descriptors. Index 0 always corresponds + // with the directory provided to this function. Entering a directory + // causes a file descriptor to be pushed, while handling ".." entries + // causes an entry to be popped. Index 0 cannot be popped, as this + // would imply escaping the base directory. + int fds[128]; + fds[0] = fd_number(fo); + size_t curfd = 0; + + // Stack of pathname strings used for symlink expansion. By using a + // stack, there is no need to concatenate any pathname strings while + // expanding symlinks. + char *paths[32]; + char *paths_start[32]; + paths[0] = paths_start[0] = path; + size_t curpath = 0; + size_t expansions = 0; + char *symlink; + size_t symlink_len; + + for (;;) { + // Extract the next pathname component from 'paths[curpath]', null + // terminate it and store it in 'file'. 'ends_with_slashes' stores + // whether the pathname component is followed by one or more + // trailing slashes, as this requires it to be a directory. + char *file = paths[curpath]; + char *file_end = file + strcspn(file, "/"); + paths[curpath] = file_end + strspn(file_end, "/"); + bool ends_with_slashes = *file_end == '/'; + *file_end = '\0'; + + // Test for empty pathname strings and absolute paths. + if (file == file_end) { + error = ends_with_slashes ? __WASI_ENOTCAPABLE : __WASI_ENOENT; + goto fail; + } + + if (strcmp(file, ".") == 0) { + // Skip component. + } else if (strcmp(file, "..") == 0) { + // Pop a directory off the stack. + if (curfd == 0) { + // Attempted to go to parent directory of the directory file + // descriptor. + error = __WASI_ENOTCAPABLE; + goto fail; + } + close(fds[curfd--]); + } else if (curpath > 0 || *paths[curpath] != '\0' || + (ends_with_slashes && !needs_final_component)) { + // A pathname component whose name we're not interested in that is + // followed by a slash or is followed by other pathname + // components. In other words, a pathname component that must be a + // directory. First attempt to obtain a directory file descriptor + // for it. + int newdir = +#ifdef O_SEARCH + openat(fds[curfd], file, O_SEARCH | O_DIRECTORY | O_NOFOLLOW); +#else + openat(fds[curfd], file, O_RDONLY | O_DIRECTORY | O_NOFOLLOW); +#endif + if (newdir != -1) { + // Success. Push it onto the directory stack. + if (curfd + 1 == sizeof(fds) / sizeof(fds[0])) { + close(newdir); + error = __WASI_ENAMETOOLONG; + goto fail; + } + fds[++curfd] = newdir; + } else { + // Failed to open it. Attempt symlink expansion. + if (errno != ELOOP && errno != EMLINK && errno != ENOTDIR) { + error = convert_errno(errno); + goto fail; + } + symlink = readlinkat_dup(fds[curfd], file, &symlink_len); + if (symlink != NULL) + goto push_symlink; + + // readlink returns EINVAL if the path isn't a symlink. In that case, + // it's more informative to return ENOTDIR. + if (errno == EINVAL) + errno = ENOTDIR; + + error = convert_errno(errno); + goto fail; + } + } else { + // The final pathname component. Depending on whether it ends with + // a slash or the symlink-follow flag is set, perform symlink + // expansion. + if (ends_with_slashes || + (flags & __WASI_LOOKUP_SYMLINK_FOLLOW) != 0) { + symlink = readlinkat_dup(fds[curfd], file, &symlink_len); + if (symlink != NULL) + goto push_symlink; + if (errno != EINVAL && errno != ENOENT) { + error = convert_errno(errno); + goto fail; + } + } + + // Not a symlink, meaning we're done. Return the filename, + // together with the directory containing this file. + // + // If the file was followed by a trailing slash, we must retain + // it, to ensure system calls properly return ENOTDIR. + // Unfortunately, this opens up a race condition, because this + // means that users of path_get() will perform symlink expansion a + // second time. There is nothing we can do to mitigate this, as + // far as I know. + if (ends_with_slashes) + *file_end = '/'; + pa->path = file; + pa->path_start = paths_start[0]; + goto success; + } + + if (*paths[curpath] == '\0') { + if (curpath == 0) { + // No further pathname components to process. We may end up here + // when called on paths like ".", "a/..", but also if the path + // had trailing slashes and the caller is not interested in the + // name of the pathname component. + wasm_runtime_free(paths_start[0]); + pa->path = "."; + pa->path_start = NULL; + goto success; + } + + // Finished expanding symlink. Continue processing along the + // original path. + wasm_runtime_free(paths_start[curpath--]); + } + continue; + + push_symlink: + // Prevent infinite loops by placing an upper limit on the number of + // symlink expansions. + if (++expansions == 128) { + wasm_runtime_free(symlink); + error = __WASI_ELOOP; + goto fail; + } + + if (*paths[curpath] == '\0') { + // The original path already finished processing. Replace it by + // this symlink entirely. + wasm_runtime_free(paths_start[curpath]); + } else if (curpath + 1 == sizeof(paths) / sizeof(paths[0])) { + // Too many nested symlinks. Stop processing. + wasm_runtime_free(symlink); + error = __WASI_ELOOP; + goto fail; + } else { + // The original path still has components left. Retain the + // components that remain, so we can process them afterwards. + ++curpath; + } + + // Append a trailing slash to the symlink if the path leading up to + // it also contained one. Otherwise we would not throw ENOTDIR if + // the target is not a directory. + if (ends_with_slashes) + bh_strcat_s(symlink, (uint32)symlink_len, "/"); + paths[curpath] = paths_start[curpath] = symlink; + } + +success: + // Return the lease. Close all directories, except the one the caller + // needs to use. + for (size_t i = 1; i < curfd; ++i) + close(fds[i]); + pa->fd = fds[curfd]; + pa->follow = false; + pa->fd_object = fo; + return 0; + +fail: + // Failure. Free all resources. + for (size_t i = 1; i <= curfd; ++i) + close(fds[i]); + for (size_t i = 0; i <= curpath; ++i) + wasm_runtime_free(paths_start[i]); + fd_object_release(fo); + return error; +#endif +} + +static __wasi_errno_t path_get_nofollow( + struct fd_table *curfds, + struct path_access *pa, + __wasi_fd_t fd, + const char *path, + size_t pathlen, + __wasi_rights_t rights_base, + __wasi_rights_t rights_inheriting, + bool needs_final_component +) TRYLOCKS_EXCLUSIVE(0, pa->fd_object->refcount) { + __wasi_lookupflags_t flags = 0; + return path_get(curfds, pa, fd, flags, path, pathlen, rights_base, rights_inheriting, + needs_final_component); +} + +static void path_put( + struct path_access *pa +) UNLOCKS(pa->fd_object->refcount) { + wasm_runtime_free(pa->path_start); + if (fd_number(pa->fd_object) != pa->fd) + close(pa->fd); + fd_object_release(pa->fd_object); +} + +__wasi_errno_t wasmtime_ssp_path_create_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = + path_get_nofollow(curfds, &pa, fd, path, pathlen, + __WASI_RIGHT_PATH_CREATE_DIRECTORY, 0, true); + if (error != 0) + return error; + + int ret = mkdirat(pa.fd, pa.path, 0777); + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +static bool +validate_path(const char *path, struct fd_prestats *pt) +{ + size_t i; + char path_resolved[PATH_MAX], prestat_dir_resolved[PATH_MAX]; + char *path_real, *prestat_dir_real; + + if (!(path_real = realpath(path, path_resolved))) + /* path doesn't exist, creating a link to this file + is allowed: if this file is to be created in + the future, WASI will strictly check whether it + can be created or not. */ + return true; + + for (i = 0; i < pt->size; i++) { + if (pt->prestats[i].dir) { + if (!(prestat_dir_real = realpath(pt->prestats[i].dir, + prestat_dir_resolved))) + return false; + if (!strncmp(path_real, prestat_dir_real, strlen(prestat_dir_real))) + return true; + } + } + + return false; +} + +__wasi_errno_t wasmtime_ssp_path_link( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + __wasi_fd_t old_fd, + __wasi_lookupflags_t old_flags, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) { + struct path_access old_pa; + __wasi_errno_t error = path_get(curfds, &old_pa, old_fd, old_flags, old_path, old_path_len, + __WASI_RIGHT_PATH_LINK_SOURCE, 0, false); + if (error != 0) + return error; + + struct path_access new_pa; + error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, + __WASI_RIGHT_PATH_LINK_TARGET, 0, true); + if (error != 0) { + path_put(&old_pa); + return error; + } + + rwlock_rdlock(&prestats->lock); + if (!validate_path(old_pa.path, prestats) + || !validate_path(new_pa.path, prestats)) { + rwlock_unlock(&prestats->lock); + return __WASI_EBADF; + } + rwlock_unlock(&prestats->lock); + + int ret = linkat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path, + old_pa.follow ? AT_SYMLINK_FOLLOW : 0); + if (ret < 0 && errno == ENOTSUP && !old_pa.follow) { + // OS X doesn't allow creating hardlinks to symbolic links. + // Duplicate the symbolic link instead. + size_t target_len; + char *target = readlinkat_dup(old_pa.fd, old_pa.path, &target_len); + if (target != NULL) { + bh_assert(target[target_len] == '\0'); + rwlock_rdlock(&prestats->lock); + if (!validate_path(target, prestats)) { + rwlock_unlock(&prestats->lock); + wasm_runtime_free(target); + return __WASI_EBADF; + } + rwlock_unlock(&prestats->lock); + ret = symlinkat(target, new_pa.fd, new_pa.path); + wasm_runtime_free(target); + } + } + path_put(&old_pa); + path_put(&new_pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_open( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t dirfd, + __wasi_lookupflags_t dirflags, + const char *path, + size_t pathlen, + __wasi_oflags_t oflags, + __wasi_rights_t fs_rights_base, + __wasi_rights_t fs_rights_inheriting, + __wasi_fdflags_t fs_flags, + __wasi_fd_t *fd +) { + // Rights that should be installed on the new file descriptor. + __wasi_rights_t rights_base = fs_rights_base; + __wasi_rights_t rights_inheriting = fs_rights_inheriting; + + // Which open() mode should be used to satisfy the needed rights. + bool read = + (rights_base & (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_READDIR)) != 0; + bool write = + (rights_base & (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_WRITE | + __WASI_RIGHT_FD_ALLOCATE | + __WASI_RIGHT_FD_FILESTAT_SET_SIZE)) != 0; + int noflags = write ? read ? O_RDWR : O_WRONLY : O_RDONLY; + + // Which rights are needed on the directory file descriptor. + __wasi_rights_t needed_base = __WASI_RIGHT_PATH_OPEN; + __wasi_rights_t needed_inheriting = rights_base | rights_inheriting; + + // Convert open flags. + if ((oflags & __WASI_O_CREAT) != 0) { + noflags |= O_CREAT; + needed_base |= __WASI_RIGHT_PATH_CREATE_FILE; + } + if ((oflags & __WASI_O_DIRECTORY) != 0) + noflags |= O_DIRECTORY; + if ((oflags & __WASI_O_EXCL) != 0) + noflags |= O_EXCL; + if ((oflags & __WASI_O_TRUNC) != 0) { + noflags |= O_TRUNC; + needed_base |= __WASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + // Convert file descriptor flags. + if ((fs_flags & __WASI_FDFLAG_APPEND) != 0) + noflags |= O_APPEND; + if ((fs_flags & __WASI_FDFLAG_DSYNC) != 0) { +#ifdef O_DSYNC + noflags |= O_DSYNC; +#else + noflags |= O_SYNC; +#endif + needed_inheriting |= __WASI_RIGHT_FD_DATASYNC; + } + if ((fs_flags & __WASI_FDFLAG_NONBLOCK) != 0) + noflags |= O_NONBLOCK; + if ((fs_flags & __WASI_FDFLAG_RSYNC) != 0) { +#ifdef O_RSYNC + noflags |= O_RSYNC; +#else + noflags |= O_SYNC; +#endif + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if ((fs_flags & __WASI_FDFLAG_SYNC) != 0) { + noflags |= O_SYNC; + needed_inheriting |= __WASI_RIGHT_FD_SYNC; + } + if (write && (noflags & (O_APPEND | O_TRUNC)) == 0) + needed_inheriting |= __WASI_RIGHT_FD_SEEK; + + struct path_access pa; + __wasi_errno_t error = + path_get(curfds, &pa, dirfd, dirflags, path, pathlen, needed_base, needed_inheriting, + (oflags & __WASI_O_CREAT) != 0); + if (error != 0) + return error; + if (!pa.follow) + noflags |= O_NOFOLLOW; + + int nfd = openat(pa.fd, pa.path, noflags, 0666); + if (nfd < 0) { + int openat_errno = errno; + // Linux returns ENXIO instead of EOPNOTSUPP when opening a socket. + if (openat_errno == ENXIO) { + struct stat sb; + int ret = + fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + path_put(&pa); + return ret == 0 && S_ISSOCK(sb.st_mode) ? __WASI_ENOTSUP + : __WASI_ENXIO; + } + // Linux returns ENOTDIR instead of ELOOP when using O_NOFOLLOW|O_DIRECTORY + // on a symlink. + if (openat_errno == ENOTDIR && (noflags & (O_NOFOLLOW | O_DIRECTORY)) != 0) { + struct stat sb; + int ret = fstatat(pa.fd, pa.path, &sb, AT_SYMLINK_NOFOLLOW); + if (S_ISLNK(sb.st_mode)) { + path_put(&pa); + return __WASI_ELOOP; + } + (void)ret; + } + path_put(&pa); + // FreeBSD returns EMLINK instead of ELOOP when using O_NOFOLLOW on + // a symlink. + if (!pa.follow && openat_errno == EMLINK) + return __WASI_ELOOP; + return convert_errno(openat_errno); + } + path_put(&pa); + + // Determine the type of the new file descriptor and which rights + // contradict with this type. + __wasi_filetype_t type; + __wasi_rights_t max_base, max_inheriting; + error = fd_determine_type_rights(nfd, &type, &max_base, &max_inheriting); + if (error != 0) { + close(nfd); + return error; + } + + { + struct stat sb; + + if (fstat(nfd, &sb) < 0) { + close(nfd); + return convert_errno(errno); + } + + if (S_ISDIR(sb.st_mode)) + rights_base |= RIGHTS_DIRECTORY_BASE; + else if (S_ISREG(sb.st_mode)) + rights_base |= RIGHTS_REGULAR_FILE_BASE; + } + + return fd_table_insert_fd(curfds, nfd, type, rights_base & max_base, + rights_inheriting & max_inheriting, fd); +} + +// Copies out directory entry metadata or filename, potentially +// truncating it in the process. +static void fd_readdir_put( + void *buf, + size_t bufsize, + size_t *bufused, + const void *elem, + size_t elemsize +) { + size_t bufavail = bufsize - *bufused; + if (elemsize > bufavail) + elemsize = bufavail; + bh_memcpy_s((char *)buf + *bufused, (uint32)bufavail, elem, (uint32)elemsize); + *bufused += elemsize; +} + +__wasi_errno_t wasmtime_ssp_fd_readdir( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + void *buf, + size_t nbyte, + __wasi_dircookie_t cookie, + size_t *bufused +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_READDIR, 0); + if (error != 0) { + return error; + } + + // Create a directory handle if none has been opened yet. + mutex_lock(&fo->directory.lock); + DIR *dp = fo->directory.handle; + if (dp == NULL) { + dp = fdopendir(fd_number(fo)); + if (dp == NULL) { + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return convert_errno(errno); + } + fo->directory.handle = dp; + fo->directory.offset = __WASI_DIRCOOKIE_START; + } + + // Seek to the right position if the requested offset does not match + // the current offset. + if (fo->directory.offset != cookie) { + if (cookie == __WASI_DIRCOOKIE_START) + rewinddir(dp); + else + seekdir(dp, (long)cookie); + fo->directory.offset = cookie; + } + + *bufused = 0; + while (*bufused < nbyte) { + // Read the next directory entry. + errno = 0; + struct dirent *de = readdir(dp); + if (de == NULL) { + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return errno == 0 || *bufused > 0 ? 0 : convert_errno(errno); + } + fo->directory.offset = (__wasi_dircookie_t)telldir(dp); + + // Craft a directory entry and copy that back. + size_t namlen = strlen(de->d_name); + __wasi_dirent_t cde = { + .d_next = fo->directory.offset, + .d_ino = de->d_ino, + .d_namlen = (uint32)namlen, + }; + switch (de->d_type) { + case DT_BLK: + cde.d_type = __WASI_FILETYPE_BLOCK_DEVICE; + break; + case DT_CHR: + cde.d_type = __WASI_FILETYPE_CHARACTER_DEVICE; + break; + case DT_DIR: + cde.d_type = __WASI_FILETYPE_DIRECTORY; + break; + case DT_FIFO: + cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; + case DT_LNK: + cde.d_type = __WASI_FILETYPE_SYMBOLIC_LINK; + break; + case DT_REG: + cde.d_type = __WASI_FILETYPE_REGULAR_FILE; + break; +#ifdef DT_SOCK + case DT_SOCK: + // Technically not correct, but good enough. + cde.d_type = __WASI_FILETYPE_SOCKET_STREAM; + break; +#endif + default: + cde.d_type = __WASI_FILETYPE_UNKNOWN; + break; + } + fd_readdir_put(buf, nbyte, bufused, &cde, sizeof(cde)); + fd_readdir_put(buf, nbyte, bufused, de->d_name, namlen); + } + mutex_unlock(&fo->directory.lock); + fd_object_release(fo); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_readlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen, + char *buf, + size_t bufsize, + size_t *bufused +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_READLINK, 0, false); + if (error != 0) + return error; + + // Linux requires that the buffer size is positive. whereas POSIX does + // not. Use a fake buffer to store the results if the size is zero. + char fakebuf[1]; + ssize_t len = readlinkat(pa.fd, pa.path, bufsize == 0 ? fakebuf : buf, + bufsize == 0 ? sizeof(fakebuf) : bufsize); + path_put(&pa); + if (len < 0) + return convert_errno(errno); + *bufused = (size_t)len < bufsize ? (size_t)len : bufsize; + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_rename( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t old_fd, + const char *old_path, + size_t old_path_len, + __wasi_fd_t new_fd, + const char *new_path, + size_t new_path_len +) { + struct path_access old_pa; + __wasi_errno_t error = path_get_nofollow(curfds, &old_pa, old_fd, old_path, old_path_len, + __WASI_RIGHT_PATH_RENAME_SOURCE, 0, true); + if (error != 0) + return error; + + struct path_access new_pa; + error = path_get_nofollow(curfds, &new_pa, new_fd, new_path, new_path_len, + __WASI_RIGHT_PATH_RENAME_TARGET, 0, true); + if (error != 0) { + path_put(&old_pa); + return error; + } + + int ret = renameat(old_pa.fd, old_pa.path, new_pa.fd, new_pa.path); + path_put(&old_pa); + path_put(&new_pa); + if (ret < 0) { + return convert_errno(errno); + } + return 0; +} + +// Converts a POSIX stat structure to a CloudABI filestat structure. +static void convert_stat( + const struct stat *in, + __wasi_filestat_t *out +) { + *out = (__wasi_filestat_t){ + .st_dev = in->st_dev, + .st_ino = in->st_ino, + .st_nlink = (__wasi_linkcount_t)in->st_nlink, + .st_size = (__wasi_filesize_t)in->st_size, + .st_atim = convert_timespec(&in->st_atim), + .st_mtim = convert_timespec(&in->st_mtim), + .st_ctim = convert_timespec(&in->st_ctim), + }; +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filestat_t *buf +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_GET, 0); + if (error != 0) + return error; + + int ret; + switch (fo->type) { + default: { + struct stat sb; + ret = fstat(fd_number(fo), &sb); + convert_stat(&sb, buf); + break; + } + } + buf->st_filetype = fo->type; + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +static void convert_timestamp( + __wasi_timestamp_t in, + struct timespec *out +) { + // Store sub-second remainder. +#ifndef __APPLE__ + out->tv_nsec = (__syscall_slong_t)(in % 1000000000); +#else + out->tv_nsec = (long)(in % 1000000000); +#endif + in /= 1000000000; + + // Clamp to the maximum in case it would overflow our system's time_t. + out->tv_sec = (time_t)in < NUMERIC_MAX(time_t) ? (time_t)in : NUMERIC_MAX(time_t); +} + +// Converts the provided timestamps and flags to a set of arguments for +// futimens() and utimensat(). +static void convert_utimens_arguments( + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags, + struct timespec *ts +) { + if ((fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) { + ts[0].tv_nsec = UTIME_NOW; + } else if ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0) { + convert_timestamp(st_atim, &ts[0]); + } else { + ts[0].tv_nsec = UTIME_OMIT; + } + + if ((fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0) { + ts[1].tv_nsec = UTIME_NOW; + } else if ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0) { + convert_timestamp(st_mtim, &ts[1]); + } else { + ts[1].tv_nsec = UTIME_OMIT; + } +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_size( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_filesize_t st_size +) { + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_SIZE, 0); + if (error != 0) + return error; + + int ret = ftruncate(fd_number(fo), (off_t)st_size); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_fd_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) { + if ((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | + __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) + return __WASI_EINVAL; + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, fd, __WASI_RIGHT_FD_FILESTAT_SET_TIMES, 0); + if (error != 0) + return error; + + struct timespec ts[2]; + convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); + int ret = futimens(fd_number(fo), ts); + + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_filestat_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t pathlen, + __wasi_filestat_t *buf +) { + struct path_access pa; + __wasi_errno_t error = + path_get(curfds, &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_GET, 0, false); + if (error != 0) + return error; + + struct stat sb; + int ret = fstatat(pa.fd, pa.path, &sb, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + convert_stat(&sb, buf); + + // Convert the file type. In the case of sockets there is no way we + // can easily determine the exact socket type. + if (S_ISBLK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_BLOCK_DEVICE; + else if (S_ISCHR(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_CHARACTER_DEVICE; + else if (S_ISDIR(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_DIRECTORY; + else if (S_ISFIFO(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + else if (S_ISLNK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SYMBOLIC_LINK; + else if (S_ISREG(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_REGULAR_FILE; + else if (S_ISSOCK(sb.st_mode)) + buf->st_filetype = __WASI_FILETYPE_SOCKET_STREAM; + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_filestat_set_times( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + __wasi_lookupflags_t flags, + const char *path, + size_t pathlen, + __wasi_timestamp_t st_atim, + __wasi_timestamp_t st_mtim, + __wasi_fstflags_t fstflags +) { + if (((fstflags & ~(__WASI_FILESTAT_SET_ATIM | __WASI_FILESTAT_SET_ATIM_NOW | + __WASI_FILESTAT_SET_MTIM | __WASI_FILESTAT_SET_MTIM_NOW)) != 0) + /* ATIM & ATIM_NOW can't be set at the same time */ + || ((fstflags & __WASI_FILESTAT_SET_ATIM) != 0 + && (fstflags & __WASI_FILESTAT_SET_ATIM_NOW) != 0) + /* MTIM & MTIM_NOW can't be set at the same time */ + || ((fstflags & __WASI_FILESTAT_SET_MTIM) != 0 + && (fstflags & __WASI_FILESTAT_SET_MTIM_NOW) != 0)) + return __WASI_EINVAL; + + struct path_access pa; + __wasi_errno_t error = path_get(curfds, + &pa, fd, flags, path, pathlen, __WASI_RIGHT_PATH_FILESTAT_SET_TIMES, 0, false); + if (error != 0) + return error; + + struct timespec ts[2]; + convert_utimens_arguments(st_atim, st_mtim, fstflags, ts); + int ret = utimensat(pa.fd, pa.path, ts, pa.follow ? 0 : AT_SYMLINK_NOFOLLOW); + + path_put(&pa); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_symlink( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, + struct fd_prestats *prestats, +#endif + const char *old_path, + size_t old_path_len, + __wasi_fd_t fd, + const char *new_path, + size_t new_path_len +) { + char *target = str_nullterminate(old_path, old_path_len); + if (target == NULL) + return convert_errno(errno); + + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, new_path, new_path_len, __WASI_RIGHT_PATH_SYMLINK, 0, true); + if (error != 0) { + wasm_runtime_free(target); + return error; + } + + rwlock_rdlock(&prestats->lock); + if (!validate_path(target, prestats)) { + rwlock_unlock(&prestats->lock); + wasm_runtime_free(target); + return __WASI_EBADF; + } + rwlock_unlock(&prestats->lock); + + int ret = symlinkat(target, pa.fd, pa.path); + path_put(&pa); + wasm_runtime_free(target); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_unlink_file( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_UNLINK_FILE, 0, true); + if (error != 0) + return error; + + int ret = unlinkat(pa.fd, pa.path, 0); +#ifndef __linux__ + // Non-Linux implementations may return EPERM when attempting to remove a + // directory without REMOVEDIR. While that's what POSIX specifies, it's + // less useful. Adjust this to EISDIR. It doesn't matter that this is not + // atomic with the unlinkat, because if the file is removed and a directory + // is created before fstatat sees it, we're racing with that change anyway + // and unlinkat could have legitimately seen the directory if the race had + // turned out differently. + if (ret < 0 && errno == EPERM) { + struct stat statbuf; + if (fstatat(pa.fd, pa.path, &statbuf, AT_SYMLINK_NOFOLLOW) == 0 && + S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; + } + } +#endif + path_put(&pa); + if (ret < 0) { + return convert_errno(errno); + } + return 0; +} + +__wasi_errno_t wasmtime_ssp_path_remove_directory( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t fd, + const char *path, + size_t pathlen +) { + struct path_access pa; + __wasi_errno_t error = path_get_nofollow(curfds, + &pa, fd, path, pathlen, __WASI_RIGHT_PATH_REMOVE_DIRECTORY, 0, true); + if (error != 0) + return error; + + int ret = unlinkat(pa.fd, pa.path, AT_REMOVEDIR); +#ifndef __linux__ + // POSIX permits either EEXIST or ENOTEMPTY when the directory is not empty. + // Map it to ENOTEMPTY. + if (ret < 0 && errno == EEXIST) { + errno = ENOTEMPTY; + } +#endif + path_put(&pa); + if (ret < 0) { + return convert_errno(errno); + } + return 0; +} + +__wasi_errno_t wasmtime_ssp_poll_oneoff( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + const __wasi_subscription_t *in, + __wasi_event_t *out, + size_t nsubscriptions, + size_t *nevents +) NO_LOCK_ANALYSIS { + // Sleeping. + if (nsubscriptions == 1 && in[0].type == __WASI_EVENTTYPE_CLOCK) { + out[0] = (__wasi_event_t){ + .userdata = in[0].userdata, + .type = in[0].type, + }; +#if CONFIG_HAS_CLOCK_NANOSLEEP + clockid_t clock_id; + if (convert_clockid(in[0].u.clock.clock_id, &clock_id)) { + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + int ret = clock_nanosleep( + clock_id, + (in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0 + ? TIMER_ABSTIME + : 0, + &ts, NULL); + if (ret != 0) + out[0].error = convert_errno(ret); + } else { + out[0].error = __WASI_ENOTSUP; + } +#else + switch (in[0].u.clock.clock_id) { + case __WASI_CLOCK_MONOTONIC: + if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { + // TODO(ed): Implement. + fputs("Unimplemented absolute sleep on monotonic clock\n", stderr); + out[0].error = __WASI_ENOSYS; + } else { + // Perform relative sleeps on the monotonic clock also using + // nanosleep(). This is incorrect, but good enough for now. + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + case __WASI_CLOCK_REALTIME: + if ((in[0].u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) != 0) { + // Sleeping to an absolute point in time can only be done + // by waiting on a condition variable. + struct mutex mutex; + struct cond cond; + + if (!mutex_init(&mutex)) + return -1; + if (!cond_init_realtime(&cond)) { + mutex_destroy(&mutex); + return -1; + } + mutex_lock(&mutex); + cond_timedwait(&cond, &mutex, in[0].u.clock.timeout, true); + mutex_unlock(&mutex); + mutex_destroy(&mutex); + cond_destroy(&cond); + } else { + // Relative sleeps can be done using nanosleep(). + struct timespec ts; + convert_timestamp(in[0].u.clock.timeout, &ts); + nanosleep(&ts, NULL); + } + break; + default: + out[0].error = __WASI_ENOTSUP; + break; + } +#endif + *nevents = 1; + return 0; + } + + // Last option: call into poll(). This can only be done in case all + // subscriptions consist of __WASI_EVENTTYPE_FD_READ and + // __WASI_EVENTTYPE_FD_WRITE entries. There may be up to one + // __WASI_EVENTTYPE_CLOCK entry to act as a timeout. These are also + // the subscriptions generate by cloudlibc's poll() and select(). + struct fd_object **fos = wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*fos))); + if (fos == NULL) + return __WASI_ENOMEM; + struct pollfd *pfds = wasm_runtime_malloc((uint32)(nsubscriptions * sizeof(*pfds))); + if (pfds == NULL) { + wasm_runtime_free(fos); + return __WASI_ENOMEM; + } + + // Convert subscriptions to pollfd entries. Increase the reference + // count on the file descriptors to ensure they remain valid across + // the call to poll(). + struct fd_table *ft = curfds; + rwlock_rdlock(&ft->lock); + *nevents = 0; + const __wasi_subscription_t *clock_subscription = NULL; + for (size_t i = 0; i < nsubscriptions; ++i) { + const __wasi_subscription_t *s = &in[i]; + switch (s->type) { + case __WASI_EVENTTYPE_FD_READ: + case __WASI_EVENTTYPE_FD_WRITE: { + __wasi_errno_t error = + fd_object_get_locked(&fos[i], ft, s->u.fd_readwrite.fd, + __WASI_RIGHT_POLL_FD_READWRITE, 0); + if (error == 0) { + // Proper file descriptor on which we can poll(). + pfds[i] = (struct pollfd){ + .fd = fd_number(fos[i]), + .events = s->type == __WASI_EVENTTYPE_FD_READ ? POLLRDNORM + : POLLWRNORM, + }; + } else { + // Invalid file descriptor or rights missing. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = error, + .type = s->type, + }; + } + break; + } + case __WASI_EVENTTYPE_CLOCK: + if (clock_subscription == NULL && + (s->u.clock.flags & __WASI_SUBSCRIPTION_CLOCK_ABSTIME) == 0) { + // Relative timeout. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + clock_subscription = s; + break; + } + // Fallthrough. + default: + // Unsupported event. + fos[i] = NULL; + pfds[i] = (struct pollfd){.fd = -1}; + out[(*nevents)++] = (__wasi_event_t){ + .userdata = s->userdata, + .error = __WASI_ENOSYS, + .type = s->type, + }; + break; + } + } + rwlock_unlock(&ft->lock); + + // Use a zero-second timeout in case we've already generated events in + // the loop above. + int timeout; + if (*nevents != 0) { + timeout = 0; + } else if (clock_subscription != NULL) { + __wasi_timestamp_t ts = clock_subscription->u.clock.timeout / 1000000; + timeout = ts > INT_MAX ? -1 : (int)ts; + } else { + timeout = -1; + } + int ret = poll(pfds, nsubscriptions, timeout); + + __wasi_errno_t error = 0; + if (ret == -1) { + error = convert_errno(errno); + } else if (ret == 0 && *nevents == 0 && clock_subscription != NULL) { + // No events triggered. Trigger the clock event. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = clock_subscription->userdata, + .type = __WASI_EVENTTYPE_CLOCK, + }; + } else { + // Events got triggered. Don't trigger the clock event. + for (size_t i = 0; i < nsubscriptions; ++i) { + if (pfds[i].fd >= 0) { + __wasi_filesize_t nbytes = 0; + if (in[i].type == __WASI_EVENTTYPE_FD_READ) { + int l; + if (ioctl(fd_number(fos[i]), FIONREAD, &l) == 0) + nbytes = (__wasi_filesize_t)l; + } + if ((pfds[i].revents & POLLNVAL) != 0) { + // Bad file descriptor. This normally cannot occur, as + // referencing the file descriptor object will always ensure + // the descriptor is valid. Still, macOS may sometimes return + // this on FIFOs when reaching end-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, +#ifdef __APPLE__ + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, +#else + .error = __WASI_EBADF, +#endif + .type = in[i].type, + }; + } else if ((pfds[i].revents & POLLERR) != 0) { + // File descriptor is in an error state. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .error = __WASI_EIO, + .type = in[i].type, + }; + } else if ((pfds[i].revents & POLLHUP) != 0) { + // End-of-file. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].type, + .u.fd_readwrite.nbytes = nbytes, + .u.fd_readwrite.flags = __WASI_EVENT_FD_READWRITE_HANGUP, + }; + } else if ((pfds[i].revents & (POLLRDNORM | POLLWRNORM)) != 0) { + // Read or write possible. + out[(*nevents)++] = (__wasi_event_t){ + .userdata = in[i].userdata, + .type = in[i].type, + .u.fd_readwrite.nbytes = nbytes, + }; + } + } + } + } + + for (size_t i = 0; i < nsubscriptions; ++i) + if (fos[i] != NULL) + fd_object_release(fos[i]); + wasm_runtime_free(fos); + wasm_runtime_free(pfds); + return error; +} + +#if 0 +/** + * We throw exception in libc-wasi wrapper function wasi_proc_exit() + * but not call this function. + */ +void wasmtime_ssp_proc_exit( + __wasi_exitcode_t rval +) { + _Exit((int32)rval); +} +#endif + +__wasi_errno_t wasmtime_ssp_proc_raise( + __wasi_signal_t sig +) { + static const int signals[] = { +#define X(v) [__WASI_##v] = v + X(SIGABRT), X(SIGALRM), X(SIGBUS), X(SIGCHLD), X(SIGCONT), X(SIGFPE), + X(SIGHUP), X(SIGILL), X(SIGINT), X(SIGKILL), X(SIGPIPE), X(SIGQUIT), + X(SIGSEGV), X(SIGSTOP), X(SIGSYS), X(SIGTERM), X(SIGTRAP), X(SIGTSTP), + X(SIGTTIN), X(SIGTTOU), X(SIGURG), X(SIGUSR1), X(SIGUSR2), X(SIGVTALRM), + X(SIGXCPU), X(SIGXFSZ), +#undef X + }; + if (sig >= sizeof(signals) / sizeof(signals[0]) || signals[sig] == 0) + return __WASI_EINVAL; + +#if CONFIG_TLS_USE_GSBASE + // TLS on OS X depends on installing a SIGSEGV handler. Reset SIGSEGV + // to the default action before raising. + if (sig == __WASI_SIGSEGV) { + struct sigaction sa = { + .sa_handler = SIG_DFL, + }; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); + } +#endif + + if (raise(signals[sig]) < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_random_get( + void *buf, + size_t nbyte +) { + random_buf(buf, nbyte); + return 0; +} + +__wasi_errno_t wasmtime_ssp_sock_recv( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_iovec_t *ri_data, + size_t ri_data_len, + __wasi_riflags_t ri_flags, + size_t *ro_datalen, + __wasi_roflags_t *ro_flags +) { + // Convert input to msghdr. + struct msghdr hdr = { + .msg_iov = (struct iovec *)ri_data, + .msg_iovlen = ri_data_len, + }; + int nflags = 0; + if ((ri_flags & __WASI_SOCK_RECV_PEEK) != 0) + nflags |= MSG_PEEK; + if ((ri_flags & __WASI_SOCK_RECV_WAITALL) != 0) + nflags |= MSG_WAITALL; + + struct fd_object *fo; + __wasi_errno_t error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_READ, 0); + if (error != 0) { + return error; + } + + ssize_t datalen = recvmsg(fd_number(fo), &hdr, nflags); + fd_object_release(fo); + if (datalen < 0) { + return convert_errno(errno); + } + + + // Convert msghdr to output. + *ro_datalen = (size_t)datalen; + *ro_flags = 0; + if ((hdr.msg_flags & MSG_TRUNC) != 0) + *ro_flags |= __WASI_SOCK_RECV_DATA_TRUNCATED; + return 0; +} + +__wasi_errno_t wasmtime_ssp_sock_send( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + const __wasi_ciovec_t *si_data, + size_t si_data_len, + __wasi_siflags_t si_flags, + size_t *so_datalen +) NO_LOCK_ANALYSIS { + // Convert input to msghdr. + struct msghdr hdr = { + .msg_iov = (struct iovec *)si_data, + .msg_iovlen = si_data_len, + }; + + // Attach file descriptors if present. + __wasi_errno_t error; + + // Send message. + struct fd_object *fo; + error = fd_object_get(curfds, &fo, sock, __WASI_RIGHT_FD_WRITE, 0); + if (error != 0) + goto out; + ssize_t len = sendmsg(fd_number(fo), &hdr, 0); + fd_object_release(fo); + if (len < 0) { + error = convert_errno(errno); + } else { + *so_datalen = (size_t)len; + } + +out: + return error; +} + +__wasi_errno_t wasmtime_ssp_sock_shutdown( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct fd_table *curfds, +#endif + __wasi_fd_t sock, + __wasi_sdflags_t how +) { + int nhow; + switch (how) { + case __WASI_SHUT_RD: + nhow = SHUT_RD; + break; + case __WASI_SHUT_WR: + nhow = SHUT_WR; + break; + case __WASI_SHUT_RD | __WASI_SHUT_WR: + nhow = SHUT_RDWR; + break; + default: + return __WASI_EINVAL; + } + + struct fd_object *fo; + __wasi_errno_t error = + fd_object_get(curfds, &fo, sock, __WASI_RIGHT_SOCK_SHUTDOWN, 0); + if (error != 0) + return error; + + int ret = shutdown(fd_number(fo), nhow); + fd_object_release(fo); + if (ret < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_sched_yield(void) { + if (sched_yield() < 0) + return convert_errno(errno); + return 0; +} + +__wasi_errno_t wasmtime_ssp_args_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + char **argv, + char *argv_buf +) { + for (size_t i = 0; i < argv_environ->argc; ++i) { + argv[i] = argv_buf + (argv_environ->argv[i] - argv_environ->argv_buf); + } + argv[argv_environ->argc] = NULL; + bh_memcpy_s(argv_buf, (uint32)argv_environ->argv_buf_size, + argv_environ->argv_buf, (uint32)argv_environ->argv_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_args_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + size_t *argc, + size_t *argv_buf_size +) { + *argc = argv_environ->argc; + *argv_buf_size = argv_environ->argv_buf_size; + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_environ_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + char **environ, + char *environ_buf +) { + for (size_t i = 0; i < argv_environ->environ_count; ++i) { + environ[i] = environ_buf + (argv_environ->environ[i] - argv_environ->environ_buf); + } + environ[argv_environ->environ_count] = NULL; + bh_memcpy_s(environ_buf, (uint32)argv_environ->environ_buf_size, + argv_environ->environ_buf, (uint32)argv_environ->environ_buf_size); + return __WASI_ESUCCESS; +} + +__wasi_errno_t wasmtime_ssp_environ_sizes_get( +#if !defined(WASMTIME_SSP_STATIC_CURFDS) + struct argv_environ_values *argv_environ, +#endif + size_t *environ_count, + size_t *environ_buf_size +) { + *environ_count = argv_environ->environ_count; + *environ_buf_size = argv_environ->environ_buf_size; + return __WASI_ESUCCESS; +} + +bool argv_environ_init(struct argv_environ_values *argv_environ, + const size_t *argv_offsets, size_t argv_offsets_len, + const char *argv_buf, size_t argv_buf_len, + const size_t *environ_offsets, size_t environ_offsets_len, + const char *environ_buf, size_t environ_buf_len) +{ + uint64 total_size; + size_t i; + + memset(argv_environ, 0, sizeof(struct argv_environ_values)); + + argv_environ->argc = argv_offsets_len; + argv_environ->argv_buf_size = argv_buf_len; + + total_size = sizeof(char *) * (uint64)argv_offsets_len; + if (total_size >= UINT32_MAX + || !(argv_environ->argv = wasm_runtime_malloc((uint32)total_size))) + return false; + + + if (argv_buf_len >= UINT32_MAX + || !(argv_environ->argv_buf = wasm_runtime_malloc((uint32)argv_buf_len))) + goto fail1; + + for (i = 0; i < argv_offsets_len; ++i) { + argv_environ->argv[i] = argv_environ->argv_buf + argv_offsets[i]; + } + bh_memcpy_s(argv_environ->argv_buf, (uint32)argv_buf_len, + argv_buf, (uint32)argv_buf_len); + + argv_environ->environ_count = environ_offsets_len; + argv_environ->environ_buf_size = environ_buf_len; + + total_size = sizeof(char *) * (uint64)environ_offsets_len; + if (total_size >= UINT32_MAX + || !(argv_environ->environ = wasm_runtime_malloc((uint32)total_size))) + goto fail2; + + if (environ_buf_len >= UINT32_MAX + || !(argv_environ->environ_buf = wasm_runtime_malloc((uint32)environ_buf_len))) + goto fail3; + + for (i = 0; i < environ_offsets_len; ++i) { + argv_environ->environ[i] = argv_environ->environ_buf + environ_offsets[i]; + } + bh_memcpy_s(argv_environ->environ_buf, (uint32)environ_buf_len, + environ_buf, (uint32)environ_buf_len); + + return true; + +fail3: + wasm_runtime_free(argv_environ->environ); +fail2: + wasm_runtime_free(argv_environ->argv_buf); +fail1: + wasm_runtime_free(argv_environ->argv); + + memset(argv_environ, 0, sizeof(struct argv_environ_values)); + return false; +} + +void argv_environ_destroy(struct argv_environ_values *argv_environ) +{ + if (argv_environ->argv_buf) + wasm_runtime_free(argv_environ->argv_buf); + if (argv_environ->argv) + wasm_runtime_free(argv_environ->argv); + if (argv_environ->environ_buf) + wasm_runtime_free(argv_environ->environ_buf); + if (argv_environ->environ) + wasm_runtime_free(argv_environ->environ); +} + +void fd_table_destroy(struct fd_table *ft) +{ + if (ft->entries) { + for (uint32 i = 0; i < ft->size; i++) { + if (ft->entries[i].object != NULL) { + fd_object_release(ft->entries[i].object); + } + } + rwlock_destroy(&ft->lock); + wasm_runtime_free(ft->entries); + } +} + +void fd_prestats_destroy(struct fd_prestats *pt) +{ + if (pt->prestats) { + for (uint32 i = 0; i < pt->size; i++) { + if (pt->prestats[i].dir != NULL) { + wasm_runtime_free((void*)pt->prestats[i].dir); + } + } + rwlock_destroy(&pt->lock); + wasm_runtime_free(pt->prestats); + } +} diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h new file mode 100644 index 0000000..24e4d49 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/posix.h @@ -0,0 +1,60 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016-2018 Nuxi, https://nuxi.nl/ + +#ifndef POSIX_H +#define POSIX_H + +#include "bh_platform.h" +#include "locking.h" + +struct fd_entry; +struct fd_prestat; +struct syscalls; + +struct fd_table { + struct rwlock lock; + struct fd_entry *entries; + size_t size; + size_t used; +}; + +struct fd_prestats { + struct rwlock lock; + struct fd_prestat *prestats; + size_t size; + size_t used; +}; + +struct argv_environ_values { + size_t argc; + size_t argv_buf_size; + char **argv; + char *argv_buf; + size_t environ_count; + size_t environ_buf_size; + char **environ; + char *environ_buf; +}; + +bool fd_table_init(struct fd_table *); +bool fd_table_insert_existing(struct fd_table *, __wasi_fd_t, int); +bool fd_prestats_init(struct fd_prestats *); +bool fd_prestats_insert(struct fd_prestats *, const char *, __wasi_fd_t); +bool argv_environ_init(struct argv_environ_values *, + const size_t *argv_offsets, size_t argv_offsets_len, + const char *argv_buf, size_t argv_buf_len, + const size_t *environ_offsets, size_t environ_offsets_len, + const char *environ_buf, size_t environ_buf_len); +void argv_environ_destroy(struct argv_environ_values *argv_environ); +void fd_table_destroy(struct fd_table *ft); +void fd_prestats_destroy(struct fd_prestats *pt); + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h new file mode 100644 index 0000000..b88f80d --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/queue.h @@ -0,0 +1,90 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef QUEUE_H +#define QUEUE_H + +// LIST: Double-linked list. + +#define LIST_HEAD(name, type) \ + struct name { \ + struct type *l_first; \ + } +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ + struct { \ + struct type *l_next; \ + struct type **l_prev; \ + } + +#define LIST_FOREACH(var, head, field) \ + for ((var) = (head)->l_first; (var) != NULL; (var) = (var)->field.l_next) +#define LIST_INIT(head) \ + do { \ + (head)->l_first = NULL; \ + } while (0) +#define LIST_INSERT_HEAD(head, element, field) \ + do { \ + (element)->field.l_next = (head)->l_first; \ + if ((head)->l_first != NULL) \ + (head)->l_first->field.l_prev = &(element)->field.l_next; \ + (head)->l_first = (element); \ + (element)->field.l_prev = &(head)->l_first; \ + } while (0) +#define LIST_REMOVE(element, field) \ + do { \ + if ((element)->field.l_next != NULL) \ + (element)->field.l_next->field.l_prev = (element)->field.l_prev; \ + *(element)->field.l_prev = (element)->field.l_next; \ + } while (0) + +// TAILQ: Double-linked list with tail pointer. + +#define TAILQ_HEAD(name, type) \ + struct name { \ + struct type *t_first; \ + struct type **t_last; \ + } + +#define TAILQ_ENTRY(type) \ + struct { \ + struct type *t_next; \ + struct type **t_prev; \ + } + +#define TAILQ_EMPTY(head) ((head)->t_first == NULL) +#define TAILQ_FIRST(head) ((head)->t_first) +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = (head)->t_first; (var) != NULL; (var) = (var)->field.t_next) +#define TAILQ_INIT(head) \ + do { \ + (head)->t_first = NULL; \ + (head)->t_last = &(head)->t_first; \ + } while (0) +#define TAILQ_INSERT_TAIL(head, elm, field) \ + do { \ + (elm)->field.t_next = NULL; \ + (elm)->field.t_prev = (head)->t_last; \ + *(head)->t_last = (elm); \ + (head)->t_last = &(elm)->field.t_next; \ + } while (0) +#define TAILQ_REMOVE(head, element, field) \ + do { \ + if ((element)->field.t_next != NULL) \ + (element)->field.t_next->field.t_prev = (element)->field.t_prev; \ + else \ + (head)->t_last = (element)->field.t_prev; \ + *(element)->field.t_prev = (element)->field.t_next; \ + } while (0) + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c new file mode 100644 index 0000000..0428828 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.c @@ -0,0 +1,86 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#include "ssp_config.h" +#include "bh_platform.h" +#include "random.h" + +#if CONFIG_HAS_ARC4RANDOM_BUF + +void random_buf(void *buf, size_t len) { + arc4random_buf(buf, len); +} + +#elif CONFIG_HAS_GETRANDOM + +#ifndef BH_PLATFORM_LINUX_SGX +#include +#endif + +void random_buf(void *buf, size_t len) { + for (;;) { + ssize_t x = getrandom(buf, len, 0); + if (x < 0) { + if (errno == EINTR) + continue; + os_printf("getrandom failed: %s", strerror(errno)); + abort(); + } + if ((size_t)x == len) + return; + buf = (void *)((unsigned char *)buf + x); + len -= (size_t)x; + } +} + +#else + +static int urandom; + +static void open_urandom(void) { + urandom = open("/dev/urandom", O_RDONLY); + if (urandom < 0) { + os_printf("Failed to open /dev/urandom\n"); + abort(); + } +} + +void random_buf(void *buf, size_t len) { + static pthread_once_t open_once = PTHREAD_ONCE_INIT; + pthread_once(&open_once, open_urandom); + + if ((size_t)read(urandom, buf, len) != len) { + os_printf("Short read on /dev/urandom\n"); + abort(); + } +} + +#endif + +// Calculates a random number within the range [0, upper - 1] without +// any modulo bias. +// +// The function below repeatedly obtains a random number from +// arc4random() until it lies within the range [2^k % upper, 2^k). As +// this range has length k * upper, we can safely obtain a number +// without any modulo bias. +uintmax_t random_uniform(uintmax_t upper) { + // Compute 2^k % upper + // == (2^k - upper) % upper + // == -upper % upper. + uintmax_t lower = -upper % upper; + for (;;) { + uintmax_t value; + random_buf(&value, sizeof(value)); + if (value >= lower) + return value % upper; + } +} diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h new file mode 100644 index 0000000..8f9ecaf --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/random.h @@ -0,0 +1,18 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef RANDOM_H +#define RANDOM_H + +void random_buf(void *, size_t); +uintmax_t random_uniform(uintmax_t); + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h new file mode 100644 index 0000000..a1c9541 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/refcount.h @@ -0,0 +1,100 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#include "bh_platform.h" +#include "locking.h" + +#define PRODUCES(...) LOCKS_SHARED(__VA_ARGS__) NO_LOCK_ANALYSIS +#define CONSUMES(...) UNLOCKS(__VA_ARGS__) NO_LOCK_ANALYSIS + +#if CONFIG_HAS_STD_ATOMIC != 0 + +#include + +/* Simple reference counter. */ +struct LOCKABLE refcount { + atomic_uint count; +}; + +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) PRODUCES(*r) +{ + atomic_init(&r->count, count); +} + +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) PRODUCES(*r) +{ + atomic_fetch_add_explicit(&r->count, 1, memory_order_acquire); +} + +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) CONSUMES(*r) +{ + int old = (int)atomic_fetch_sub_explicit(&r->count, 1, + memory_order_release); + bh_assert(old != 0 && "Reference count becoming negative"); + return old == 1; +} + +#elif defined(BH_PLATFORM_LINUX_SGX) + +#include + +/* Simple reference counter. */ +struct refcount { + sgx_spinlock_t lock; + unsigned int count; +}; + +/* Initialize the reference counter. */ +static inline void +refcount_init(struct refcount *r, unsigned int count) +{ + r->lock = SGX_SPINLOCK_INITIALIZER; + r->count = count; +} + +/* Increment the reference counter. */ +static inline void +refcount_acquire(struct refcount *r) +{ + sgx_spin_lock(&r->lock); + r->count++; + sgx_spin_unlock(&r->lock); +} + +/* Decrement the reference counter, returning whether the reference + dropped to zero. */ +static inline bool +refcount_release(struct refcount *r) +{ + int old; + sgx_spin_lock(&r->lock); + old = (int)r->count; + r->count--; + sgx_spin_unlock(&r->lock); + bh_assert(old != 0 && "Reference count becoming negative"); + return old == 1; +} + +#else /* else of CONFIG_HAS_STD_ATOMIC */ +#error "Reference counter isn't implemented" +#endif /* end of CONFIG_HAS_STD_ATOMIC */ + +#endif /* end of REFCOUNT_H */ diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h new file mode 100644 index 0000000..565635e --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/rights.h @@ -0,0 +1,83 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef RIGHTS_H +#define RIGHTS_H + +#define RIGHTS_ALL \ + (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_READ | \ + __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_SYNC | __WASI_RIGHT_FD_TELL | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_ALLOCATE | \ + __WASI_RIGHT_PATH_CREATE_DIRECTORY | __WASI_RIGHT_PATH_CREATE_FILE | \ + __WASI_RIGHT_PATH_LINK_SOURCE | __WASI_RIGHT_PATH_LINK_TARGET | \ + __WASI_RIGHT_PATH_OPEN | __WASI_RIGHT_FD_READDIR | \ + __WASI_RIGHT_PATH_READLINK | __WASI_RIGHT_PATH_RENAME_SOURCE | \ + __WASI_RIGHT_PATH_RENAME_TARGET | __WASI_RIGHT_PATH_FILESTAT_GET | \ + __WASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ + __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) + +// Block and character device interaction is outside the scope of +// CloudABI. Simply allow everything. +#define RIGHTS_BLOCK_DEVICE_BASE RIGHTS_ALL +#define RIGHTS_BLOCK_DEVICE_INHERITING RIGHTS_ALL +#define RIGHTS_CHARACTER_DEVICE_BASE RIGHTS_ALL +#define RIGHTS_CHARACTER_DEVICE_INHERITING RIGHTS_ALL + +// Only allow directory operations on directories. Directories can only +// yield file descriptors to other directories and files. +#define RIGHTS_DIRECTORY_BASE \ + (__WASI_RIGHT_FD_FDSTAT_SET_FLAGS | __WASI_RIGHT_FD_SYNC | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_PATH_CREATE_DIRECTORY | \ + __WASI_RIGHT_PATH_CREATE_FILE | __WASI_RIGHT_PATH_LINK_SOURCE | \ + __WASI_RIGHT_PATH_LINK_TARGET | __WASI_RIGHT_PATH_OPEN | \ + __WASI_RIGHT_FD_READDIR | __WASI_RIGHT_PATH_READLINK | \ + __WASI_RIGHT_PATH_RENAME_SOURCE | __WASI_RIGHT_PATH_RENAME_TARGET | \ + __WASI_RIGHT_PATH_FILESTAT_GET | \ + __WASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + __WASI_RIGHT_PATH_SYMLINK | __WASI_RIGHT_PATH_UNLINK_FILE | \ + __WASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_DIRECTORY_INHERITING \ + (RIGHTS_DIRECTORY_BASE | RIGHTS_REGULAR_FILE_BASE) + +// Operations that apply to regular files. +#define RIGHTS_REGULAR_FILE_BASE \ + (__WASI_RIGHT_FD_DATASYNC | __WASI_RIGHT_FD_READ | \ + __WASI_RIGHT_FD_SEEK | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_SYNC | __WASI_RIGHT_FD_TELL | __WASI_RIGHT_FD_WRITE | \ + __WASI_RIGHT_FD_ADVISE | __WASI_RIGHT_FD_ALLOCATE | \ + __WASI_RIGHT_FD_FILESTAT_GET | __WASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + __WASI_RIGHT_FD_FILESTAT_SET_TIMES | __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_REGULAR_FILE_INHERITING 0 + +// Operations that apply to sockets and socket pairs. +#define RIGHTS_SOCKET_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE | __WASI_RIGHT_SOCK_SHUTDOWN) +#define RIGHTS_SOCKET_INHERITING RIGHTS_ALL + +// Operations that apply to TTYs. +#define RIGHTS_TTY_BASE \ + (__WASI_RIGHT_FD_READ | __WASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + __WASI_RIGHT_FD_WRITE | __WASI_RIGHT_FD_FILESTAT_GET | \ + __WASI_RIGHT_POLL_FD_READWRITE) +#define RIGHTS_TTY_INHERITING 0 + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h new file mode 100644 index 0000000..ead95e7 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/signals.h @@ -0,0 +1,17 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef SIGNALS_H +#define SIGNALS_H + +void signals_init(void); + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h new file mode 100644 index 0000000..5114366 --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/ssp_config.h @@ -0,0 +1,106 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef SSP_CONFIG_H +#define SSP_CONFIG_H + +#include + +#if defined(__FreeBSD__) || defined(__APPLE__) +#define CONFIG_HAS_ARC4RANDOM_BUF 1 +#else +#define CONFIG_HAS_ARC4RANDOM_BUF 0 +#endif + +// On Linux, prefer to use getrandom, though it isn't available in +// GLIBC before 2.25. +#if defined(__linux__) && \ + (!defined(__GLIBC__) || \ + __GLIBC__ > 2 || \ + (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) +#define CONFIG_HAS_GETRANDOM 1 +#else +#define CONFIG_HAS_GETRANDOM 0 +#endif + +#if defined(__CloudABI__) +#define CONFIG_HAS_CAP_ENTER 1 +#else +#define CONFIG_HAS_CAP_ENTER 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__EMSCRIPTEN__) +#define CONFIG_HAS_CLOCK_NANOSLEEP 1 +#else +#define CONFIG_HAS_CLOCK_NANOSLEEP 0 +#endif + +#if !defined(__APPLE__) && !defined(__FreeBSD__) +#define CONFIG_HAS_FDATASYNC 1 +#else +#define CONFIG_HAS_FDATASYNC 0 +#endif + +#ifndef __CloudABI__ +#define CONFIG_HAS_ISATTY 1 +#else +#define CONFIG_HAS_ISATTY 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_POSIX_FALLOCATE 1 +#else +#define CONFIG_HAS_POSIX_FALLOCATE 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_PREADV 1 +#else +#define CONFIG_HAS_PREADV 0 +#endif + +#if defined(__APPLE__) || defined(__CloudABI__) +#define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 1 +#else +#define CONFIG_HAS_PTHREAD_COND_TIMEDWAIT_RELATIVE_NP 0 +#endif + +#if !defined(__APPLE__) && !defined(BH_PLATFORM_LINUX_SGX) +#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 1 +#else +#define CONFIG_HAS_PTHREAD_CONDATTR_SETCLOCK 0 +#endif + +#ifndef __APPLE__ +#define CONFIG_HAS_PWRITEV 1 +#else +#define CONFIG_HAS_PWRITEV 0 +#endif + +#ifdef __APPLE__ +#define st_atimespec st_atim +#define st_mtimespec st_mtim +#define st_ctimespec st_ctim +#endif + +#ifdef __APPLE__ +#define CONFIG_TLS_USE_GSBASE 1 +#else +#define CONFIG_TLS_USE_GSBASE 0 +#endif + +#if !defined(BH_PLATFORM_LINUX_SGX) +#define CONFIG_HAS_STD_ATOMIC 1 +#else +#define CONFIG_HAS_STD_ATOMIC 0 +#endif + +#endif diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c new file mode 100644 index 0000000..442112b --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.c @@ -0,0 +1,44 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#include "ssp_config.h" +#include "bh_platform.h" +#include "str.h" + +static char * +bh_strndup(const char *s, size_t n) +{ + size_t l = strnlen(s, n); + char *s1 = wasm_runtime_malloc((uint32)(l + 1)); + + if (!s1) + return NULL; + bh_memcpy_s(s1, (uint32)(l + 1), s, (uint32)l); + s1[l] = 0; + return s1; +} + +char * +str_nullterminate(const char *s, size_t len) { + /* Copy string */ + char *ret = bh_strndup(s, len); + + if (ret == NULL) + return NULL; + + /* Ensure that it contains no null bytes within */ + if (strlen(ret) != len) { + wasm_runtime_free(ret); + errno = EILSEQ; + return NULL; + } + return ret; +} diff --git a/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h new file mode 100644 index 0000000..bf46dda --- /dev/null +++ b/wamr/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src/str.h @@ -0,0 +1,19 @@ +// Part of the Wasmtime Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://github.com/bytecodealliance/wasmtime/blob/main/LICENSE for license information. +// +// Significant parts of this file are derived from cloudabi-utils. See +// https://github.com/bytecodealliance/wasmtime/blob/main/lib/wasi/sandboxed-system-primitives/src/LICENSE +// for license information. +// +// The upstream file contains the following copyright notice: +// +// Copyright (c) 2016 Nuxi, https://nuxi.nl/ + +#ifndef STR_H +#define STR_H + +#include "ssp_config.h" + +char *str_nullterminate(const char *, size_t); + +#endif diff --git a/wamr/core/iwasm/libraries/thread-mgr/thread_manager.c b/wamr/core/iwasm/libraries/thread-mgr/thread_manager.c new file mode 100644 index 0000000..dfcd865 --- /dev/null +++ b/wamr/core/iwasm/libraries/thread-mgr/thread_manager.c @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "thread_manager.h" + +typedef struct { + bh_list_link l; + + void (*destroy_cb)(WASMCluster *); +} DestroyCallBackNode; + +static bh_list destroy_callback_list_head; +static bh_list *const destroy_callback_list = &destroy_callback_list_head; + +static bh_list cluster_list_head; +static bh_list *const cluster_list = &cluster_list_head; +static korp_mutex cluster_list_lock; + +typedef void (*list_visitor)(void *, void *); + +static uint32 cluster_max_thread_num = CLUSTER_MAX_THREAD_NUM; + +/* Set the maximum thread number, if this function is not called, + the max thread num is defined by CLUSTER_MAX_THREAD_NUM */ +void +wasm_cluster_set_max_thread_num(uint32 num) +{ + if (num > 0) + cluster_max_thread_num = num; +} + +bool +thread_manager_init() +{ + if (bh_list_init(cluster_list) != 0) + return false; + if (os_mutex_init(&cluster_list_lock) != 0) + return false; + return true; +} + +void +thread_manager_destroy() +{ + WASMCluster *cluster = bh_list_first_elem(cluster_list); + WASMCluster *next; + while (cluster) { + next = bh_list_elem_next(cluster); + wasm_cluster_destroy(cluster); + cluster = next; + } + wasm_cluster_cancel_all_callbacks(); + os_mutex_destroy(&cluster_list_lock); +} + +static void +traverse_list(bh_list *l, list_visitor visitor, void *user_data) +{ + void *next, *node = bh_list_first_elem(l); + while (node) { + next = bh_list_elem_next(node); + visitor(node, user_data); + node = next; + } +} + +static bool +allocate_aux_stack(WASMCluster *cluster, uint32 *start, uint32 *size) +{ + uint32 i; + + /* If the module doesn't have aux stack info, + it can't create any threads */ + if (!cluster->stack_segment_occupied) + return false; + + os_mutex_lock(&cluster->lock); + for (i = 0; i < cluster_max_thread_num; i++) { + if (!cluster->stack_segment_occupied[i]) { + if (start) + *start = cluster->stack_tops[i]; + if (size) + *size = cluster->stack_size; + cluster->stack_segment_occupied[i] = true; + os_mutex_unlock(&cluster->lock); + return true; + } + } + os_mutex_unlock(&cluster->lock); + return false; +} + +static bool +free_aux_stack(WASMCluster *cluster, uint32 start) +{ + uint32 i; + + for (i = 0; i < cluster_max_thread_num; i++) { + if (start == cluster->stack_tops[i]) { + os_mutex_lock(&cluster->lock); + cluster->stack_segment_occupied[i] = false; + os_mutex_unlock(&cluster->lock); + return true; + } + } + return false; +} + +WASMCluster * +wasm_cluster_create(WASMExecEnv *exec_env) +{ + WASMCluster *cluster; + uint64 total_size; + uint32 aux_stack_start, aux_stack_size, i; + + bh_assert(exec_env->cluster == NULL); + if (!(cluster = wasm_runtime_malloc(sizeof(WASMCluster)))) { + LOG_ERROR("thread manager error: failed to allocate memory"); + return NULL; + } + memset(cluster, 0, sizeof(WASMCluster)); + + exec_env->cluster = cluster; + + bh_list_init(&cluster->exec_env_list); + bh_list_insert(&cluster->exec_env_list, exec_env); + if (os_mutex_init(&cluster->lock) != 0) { + wasm_runtime_free(cluster); + LOG_ERROR("thread manager error: failed to init mutex"); + return NULL; + } + + /* Prepare the aux stack top and size for every thread */ + if (!wasm_exec_env_get_aux_stack(exec_env, + &aux_stack_start, + &aux_stack_size)) { + LOG_VERBOSE("No aux stack info for this module, can't create thread"); + + /* If the module don't have aux stack info, don't throw error here, + but remain stack_tops and stack_segment_occupied as NULL */ + os_mutex_lock(&cluster_list_lock); + if (bh_list_insert(cluster_list, cluster) != 0) { + os_mutex_unlock(&cluster_list_lock); + goto fail; + } + os_mutex_unlock(&cluster_list_lock); + + return cluster; + } + + cluster->stack_size = aux_stack_size / (cluster_max_thread_num + 1); + if (cluster->stack_size == 0) { + goto fail; + } + + /* Set initial aux stack top to the instance and + aux stack boundary to the main exec_env */ + if (!wasm_exec_env_set_aux_stack(exec_env, aux_stack_start, + cluster->stack_size)) + goto fail; + + if (cluster_max_thread_num != 0) { + total_size = cluster_max_thread_num * sizeof(uint32); + if (total_size >= UINT32_MAX + || !(cluster->stack_tops = + wasm_runtime_malloc((uint32)total_size))) { + goto fail; + } + memset(cluster->stack_tops, 0, (uint32)total_size); + + if (!(cluster->stack_segment_occupied = + wasm_runtime_malloc(cluster_max_thread_num * sizeof(bool)))) { + goto fail; + } + memset(cluster->stack_segment_occupied, 0, + cluster_max_thread_num * sizeof(bool)); + + /* Reserve space for main instance */ + aux_stack_start -= cluster->stack_size; + + for (i = 0; i < cluster_max_thread_num; i++) { + cluster->stack_tops[i] = aux_stack_start - cluster->stack_size * i; + } + } + + os_mutex_lock(&cluster_list_lock); + if (bh_list_insert(cluster_list, cluster) != 0) { + os_mutex_unlock(&cluster_list_lock); + goto fail; + } + os_mutex_unlock(&cluster_list_lock); + + return cluster; + +fail: + if (cluster) + wasm_cluster_destroy(cluster); + + return NULL; +} + +static void +destroy_cluster_visitor(void *node, void *user_data) +{ + DestroyCallBackNode *destroy_node = (DestroyCallBackNode *)node; + WASMCluster *cluster = (WASMCluster *)user_data; + + destroy_node->destroy_cb(cluster); +} + +void +wasm_cluster_destroy(WASMCluster *cluster) +{ + traverse_list(destroy_callback_list, + destroy_cluster_visitor, (void *)cluster); + + /* Remove the cluster from the cluster list */ + os_mutex_lock(&cluster_list_lock); + bh_list_remove(cluster_list, cluster); + os_mutex_unlock(&cluster_list_lock); + + os_mutex_destroy(&cluster->lock); + + if (cluster->stack_tops) + wasm_runtime_free(cluster->stack_tops); + if (cluster->stack_segment_occupied) + wasm_runtime_free(cluster->stack_segment_occupied); + wasm_runtime_free(cluster); +} + +static void +free_node_visitor(void *node, void *user_data) +{ + wasm_runtime_free(node); +} + +void +wasm_cluster_cancel_all_callbacks() +{ + traverse_list(destroy_callback_list, free_node_visitor, NULL); +} + +WASMCluster * +wasm_exec_env_get_cluster(WASMExecEnv *exec_env) +{ + return exec_env->cluster; +} + +bool +wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) +{ + bool ret = true; + + exec_env->cluster = cluster; + + os_mutex_lock(&cluster->lock); + if (bh_list_insert(&cluster->exec_env_list, exec_env) != 0) + ret = false; + os_mutex_unlock(&cluster->lock); + return ret; +} + +bool +wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env) +{ + bool ret = true; + bh_assert(exec_env->cluster == cluster); + os_mutex_lock(&cluster->lock); + if (bh_list_remove(&cluster->exec_env_list, exec_env) != 0) + ret = false; + os_mutex_unlock(&cluster->lock); + + if (cluster->exec_env_list.len == 0) { + /* exec_env_list empty, destroy the cluster */ + wasm_cluster_destroy(cluster); + } + return ret; +} + +WASMExecEnv * +wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + wasm_module_t module = wasm_exec_env_get_module(exec_env); + wasm_module_inst_t new_module_inst; + WASMExecEnv *new_exec_env; + uint32 aux_stack_start, aux_stack_size; + + if (!(new_module_inst = + wasm_runtime_instantiate_internal(module, true, 8192, + 0, NULL, 0))) { + return NULL; + } + + new_exec_env = wasm_exec_env_create_internal( + new_module_inst, exec_env->wasm_stack_size); + if (!new_exec_env) + goto fail1; + + if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { + LOG_ERROR("thread manager error: " + "failed to allocate aux stack space for new thread"); + goto fail2; + } + + /* Set aux stack for current thread */ + if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, + aux_stack_size)) { + goto fail3; + } + + if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) + goto fail3; + + return new_exec_env; + +fail3: + /* free the allocated aux stack space */ + free_aux_stack(cluster, aux_stack_start); +fail2: + wasm_exec_env_destroy(new_exec_env); +fail1: + wasm_runtime_deinstantiate_internal(new_module_inst, true); + + return NULL; +} + +void +wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + wasm_module_inst_t module_inst = wasm_runtime_get_module_inst(exec_env); + bh_assert(cluster != NULL); + + /* Free aux stack space */ + free_aux_stack(cluster, + exec_env->aux_stack_boundary + cluster->stack_size); + wasm_cluster_del_exec_env(cluster, exec_env); + wasm_exec_env_destroy_internal(exec_env); + + wasm_runtime_deinstantiate_internal(module_inst, true); +} + +/* start routine of thread manager */ +static void* +thread_manager_start_routine(void *arg) +{ + void *ret; + WASMExecEnv *exec_env = (WASMExecEnv *)arg; + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster != NULL); + + exec_env->handle = os_self_thread(); + ret = exec_env->thread_start_routine(exec_env); + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env->suspend_flags.flags & 0x08) + ret = exec_env->thread_ret_value; +#endif + + /* Routine exit */ + /* Free aux stack space */ + free_aux_stack(cluster, + exec_env->aux_stack_boundary + cluster->stack_size); + /* Detach the native thread here to ensure the resources are freed */ + wasm_cluster_detach_thread(exec_env); + /* Remove and destroy exec_env */ + wasm_cluster_del_exec_env(cluster, exec_env); + wasm_exec_env_destroy_internal(exec_env); + + os_thread_exit(ret); + return ret; +} + +int32 +wasm_cluster_create_thread(WASMExecEnv *exec_env, + wasm_module_inst_t module_inst, + void* (*thread_routine)(void *), + void *arg) +{ + WASMCluster *cluster; + WASMExecEnv *new_exec_env; + uint32 aux_stack_start, aux_stack_size; + korp_tid tid; + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + new_exec_env = wasm_exec_env_create_internal( + module_inst, exec_env->wasm_stack_size); + if (!new_exec_env) + return -1; + + if (!allocate_aux_stack(cluster, &aux_stack_start, &aux_stack_size)) { + LOG_ERROR("thread manager error: " + "failed to allocate aux stack space for new thread"); + goto fail1; + } + + /* Set aux stack for current thread */ + if (!wasm_exec_env_set_aux_stack(new_exec_env, aux_stack_start, + aux_stack_size)) { + goto fail2; + } + + if (!wasm_cluster_add_exec_env(cluster, new_exec_env)) + goto fail2; + + new_exec_env->thread_start_routine = thread_routine; + new_exec_env->thread_arg = arg; + + if (0 != os_thread_create(&tid, thread_manager_start_routine, + (void *)new_exec_env, + APP_THREAD_STACK_SIZE_DEFAULT)) { + goto fail3; + } + + return 0; + +fail3: + wasm_cluster_del_exec_env(cluster, new_exec_env); +fail2: + /* free the allocated aux stack space */ + free_aux_stack(cluster, aux_stack_start); +fail1: + wasm_exec_env_destroy(new_exec_env); + return -1; +} + +int32 +wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val) +{ + return os_thread_join(exec_env->handle, ret_val); +} + +int32 +wasm_cluster_detach_thread(WASMExecEnv *exec_env) +{ + return os_thread_detach(exec_env->handle); +} + +void +wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval) +{ + WASMCluster *cluster; + +#ifdef OS_ENABLE_HW_BOUND_CHECK + if (exec_env->jmpbuf_stack_top) { + WASMJmpBuf *jmpbuf_node; + + /* Store the return value in exec_env */ + exec_env->thread_ret_value = retval; + exec_env->suspend_flags.flags |= 0x08; + + /* Free all jmpbuf_node except the last one */ + while (exec_env->jmpbuf_stack_top->prev) { + jmpbuf_node = wasm_exec_env_pop_jmpbuf(exec_env); + wasm_runtime_free(jmpbuf_node); + } + jmpbuf_node = exec_env->jmpbuf_stack_top; + os_longjmp(jmpbuf_node->jmpbuf, 1); + return; + } +#endif + + cluster = wasm_exec_env_get_cluster(exec_env); + bh_assert(cluster); + + /* App exit the thread, free the resources before exit native thread */ + /* Free aux stack space */ + free_aux_stack(cluster, + exec_env->aux_stack_boundary + cluster->stack_size); + /* Detach the native thread here to ensure the resources are freed */ + wasm_cluster_detach_thread(exec_env); + /* Remove and destroy exec_env */ + wasm_cluster_del_exec_env(cluster, exec_env); + wasm_exec_env_destroy_internal(exec_env); + + os_thread_exit(retval); +} + +int32 +wasm_cluster_cancel_thread(WASMExecEnv *exec_env) +{ + /* Set the termination flag */ + exec_env->suspend_flags.flags |= 0x01; + return 0; +} + +static void +terminate_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + + if (curr_exec_env == exec_env) + return; + + wasm_cluster_cancel_thread(curr_exec_env); + wasm_cluster_join_thread(curr_exec_env, NULL); +} + +void +wasm_cluster_terminate_all(WASMCluster *cluster) +{ + traverse_list(&cluster->exec_env_list, + terminate_thread_visitor, NULL); +} + +void +wasm_cluster_terminate_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env) +{ + traverse_list(&cluster->exec_env_list, + terminate_thread_visitor, (void *)exec_env); +} + +bool +wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *)) +{ + DestroyCallBackNode *node; + + if (!(node = wasm_runtime_malloc(sizeof(DestroyCallBackNode)))) { + LOG_ERROR("thread manager error: failed to allocate memory"); + return false; + } + node->destroy_cb = callback; + bh_list_insert(destroy_callback_list, node); + return true; +} + +void +wasm_cluster_suspend_thread(WASMExecEnv *exec_env) +{ + /* Set the suspend flag */ + exec_env->suspend_flags.flags |= 0x02; +} + +static void +suspend_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + + if (curr_exec_env == exec_env) + return; + + wasm_cluster_suspend_thread(curr_exec_env); +} + +void +wasm_cluster_suspend_all(WASMCluster *cluster) +{ + traverse_list(&cluster->exec_env_list, + suspend_thread_visitor, NULL); +} + +void +wasm_cluster_suspend_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env) +{ + traverse_list(&cluster->exec_env_list, + suspend_thread_visitor, (void *)exec_env); +} + +void +wasm_cluster_resume_thread(WASMExecEnv *exec_env) +{ + exec_env->suspend_flags.flags &= ~0x02; +} + +static void +resume_thread_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + + wasm_cluster_resume_thread(curr_exec_env); +} + +void +wasm_cluster_resume_all(WASMCluster *cluster) +{ + traverse_list(&cluster->exec_env_list, resume_thread_visitor, NULL); +} + +static void +set_exception_visitor(void *node, void *user_data) +{ + WASMExecEnv *curr_exec_env = (WASMExecEnv *)node; + WASMExecEnv *exec_env = (WASMExecEnv *)user_data; + WASMModuleInstanceCommon *module_inst = get_module_inst(exec_env); + WASMModuleInstanceCommon *curr_module_inst = + get_module_inst(curr_exec_env); + const char *exception = wasm_runtime_get_exception(module_inst); + /* skip "Exception: " */ + exception += 11; + + if (curr_exec_env != exec_env) { + curr_module_inst = get_module_inst(curr_exec_env); + wasm_runtime_set_exception(curr_module_inst, exception); + } +} + +void +wasm_cluster_spread_exception(WASMExecEnv *exec_env) +{ + WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env); + + traverse_list(&cluster->exec_env_list, set_exception_visitor, exec_env); +} diff --git a/wamr/core/iwasm/libraries/thread-mgr/thread_manager.h b/wamr/core/iwasm/libraries/thread-mgr/thread_manager.h new file mode 100644 index 0000000..eb5550a --- /dev/null +++ b/wamr/core/iwasm/libraries/thread-mgr/thread_manager.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _THREAD_MANAGER_H +#define _THREAD_MANAGER_H + +#include "bh_common.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "../interpreter/wasm.h" +#include "../common/wasm_runtime_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct WASMCluster +{ + struct WASMCluster *next; + + korp_mutex lock; + bh_list exec_env_list; + + /* The aux stack of a module with shared memory will be + divided into several segments. This array store the + stack top of different segments */ + uint32 *stack_tops; + /* Size of every stack segment */ + uint32 stack_size; + /* Record which segments are occupied */ + bool *stack_segment_occupied; +} WASMCluster; + +void wasm_cluster_set_max_thread_num(uint32 num); + +bool +thread_manager_init(); + +void +thread_manager_destroy(); + +/* Create cluster */ +WASMCluster * +wasm_cluster_create(WASMExecEnv *exec_env); + +/* Destroy cluster */ +void +wasm_cluster_destroy(WASMCluster *cluster); + +/* Get the cluster of the current exec_env */ +WASMCluster* +wasm_exec_env_get_cluster(WASMExecEnv *exec_env); + +int32 +wasm_cluster_create_thread(WASMExecEnv *exec_env, + wasm_module_inst_t module_inst, + void* (*thread_routine)(void *), + void *arg); + +int32 +wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val); + +int32 +wasm_cluster_detach_thread(WASMExecEnv *exec_env); + +int32 +wasm_cluster_cancel_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval); + +bool +wasm_cluster_register_destroy_callback(void (*callback)(WASMCluster *)); + +void +wasm_cluster_cancel_all_callbacks(); + +void +wasm_cluster_suspend_all(WASMCluster *cluster); + +void +wasm_cluster_suspend_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env); + +void +wasm_cluster_suspend_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_resume_thread(WASMExecEnv *exec_env); + +void +wasm_cluster_resume_all(WASMCluster *cluster); + +void +wasm_cluster_terminate_all(WASMCluster *cluster); + +void +wasm_cluster_terminate_all_except_self(WASMCluster *cluster, + WASMExecEnv *exec_env); + +bool +wasm_cluster_add_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); + +bool +wasm_cluster_del_exec_env(WASMCluster *cluster, WASMExecEnv *exec_env); + +void +wasm_cluster_spread_exception(WASMExecEnv *exec_env); + +WASMExecEnv * +wasm_cluster_spawn_exec_env(WASMExecEnv *exec_env); + +void +wasm_cluster_destroy_spawned_exec_env(WASMExecEnv *exec_env); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _THREAD_MANAGER_H */ diff --git a/wamr/core/iwasm/libraries/thread-mgr/thread_mgr.cmake b/wamr/core/iwasm/libraries/thread-mgr/thread_mgr.cmake new file mode 100644 index 0000000..c37a336 --- /dev/null +++ b/wamr/core/iwasm/libraries/thread-mgr/thread_mgr.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (THREAD_MGR_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions (-DWASM_ENABLE_THREAD_MGR=1) + +include_directories(${THREAD_MGR_DIR}) + +file (GLOB source_all ${THREAD_MGR_DIR}/*.c) + +set (THREAD_MGR_SOURCE ${source_all}) + diff --git a/wamr/core/shared/coap/er-coap/LICENSE.md b/wamr/core/shared/coap/er-coap/LICENSE.md new file mode 100644 index 0000000..f4b1a05 --- /dev/null +++ b/wamr/core/shared/coap/er-coap/LICENSE.md @@ -0,0 +1,30 @@ +Copyright (c) (Year), (Name of copyright holder) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/wamr/core/shared/coap/er-coap/coap-constants.h b/wamr/core/shared/coap/er-coap/coap-constants.h new file mode 100644 index 0000000..f73a5b9 --- /dev/null +++ b/wamr/core/shared/coap/er-coap/coap-constants.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Collection of constants specified in the CoAP standard. + * \author + * Matthias Kovatsch + */ + +/** + * \addtogroup coap + * @{ + */ + +#ifndef COAP_CONSTANTS_H_ +#define COAP_CONSTANTS_H_ + +#define COAP_DEFAULT_PORT 5683 +#define COAP_DEFAULT_SECURE_PORT 5684 + +#define COAP_DEFAULT_MAX_AGE 60 +#define COAP_RESPONSE_TIMEOUT 3 +#define COAP_RESPONSE_RANDOM_FACTOR 1.5 +#define COAP_MAX_RETRANSMIT 4 + +#define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */ +#define COAP_TOKEN_LEN 8 /* The maximum number of bytes for the Token */ +#define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ + +#define COAP_HEADER_VERSION_MASK 0xC0 +#define COAP_HEADER_VERSION_POSITION 6 +#define COAP_HEADER_TYPE_MASK 0x30 +#define COAP_HEADER_TYPE_POSITION 4 +#define COAP_HEADER_TOKEN_LEN_MASK 0x0F +#define COAP_HEADER_TOKEN_LEN_POSITION 0 + +#define COAP_HEADER_OPTION_DELTA_MASK 0xF0 +#define COAP_HEADER_OPTION_SHORT_LENGTH_MASK 0x0F + +/* CoAP message types */ +typedef enum { + COAP_TYPE_CON, /* confirmables */ + COAP_TYPE_NON, /* non-confirmables */ + COAP_TYPE_ACK, /* acknowledgements */ + COAP_TYPE_RST /* reset */ +} coap_message_type_t; + +/* CoAP request method codes */ +typedef enum { + COAP_GET = 1, + COAP_POST, + COAP_PUT, + COAP_DELETE +} coap_method_t; + +/* CoAP response codes */ +typedef enum { + NO_ERROR = 0, + + CREATED_2_01 = 65, /* CREATED */ + DELETED_2_02 = 66, /* DELETED */ + VALID_2_03 = 67, /* NOT_MODIFIED */ + CHANGED_2_04 = 68, /* CHANGED */ + CONTENT_2_05 = 69, /* OK */ + CONTINUE_2_31 = 95, /* CONTINUE */ + + BAD_REQUEST_4_00 = 128, /* BAD_REQUEST */ + UNAUTHORIZED_4_01 = 129, /* UNAUTHORIZED */ + BAD_OPTION_4_02 = 130, /* BAD_OPTION */ + FORBIDDEN_4_03 = 131, /* FORBIDDEN */ + NOT_FOUND_4_04 = 132, /* NOT_FOUND */ + METHOD_NOT_ALLOWED_4_05 = 133, /* METHOD_NOT_ALLOWED */ + NOT_ACCEPTABLE_4_06 = 134, /* NOT_ACCEPTABLE */ + PRECONDITION_FAILED_4_12 = 140, /* BAD_REQUEST */ + REQUEST_ENTITY_TOO_LARGE_4_13 = 141, /* REQUEST_ENTITY_TOO_LARGE */ + UNSUPPORTED_MEDIA_TYPE_4_15 = 143, /* UNSUPPORTED_MEDIA_TYPE */ + + INTERNAL_SERVER_ERROR_5_00 = 160, /* INTERNAL_SERVER_ERROR */ + NOT_IMPLEMENTED_5_01 = 161, /* NOT_IMPLEMENTED */ + BAD_GATEWAY_5_02 = 162, /* BAD_GATEWAY */ + SERVICE_UNAVAILABLE_5_03 = 163, /* SERVICE_UNAVAILABLE */ + GATEWAY_TIMEOUT_5_04 = 164, /* GATEWAY_TIMEOUT */ + PROXYING_NOT_SUPPORTED_5_05 = 165, /* PROXYING_NOT_SUPPORTED */ + + /* Erbium errors */ + MEMORY_ALLOCATION_ERROR = 192, + PACKET_SERIALIZATION_ERROR, + + /* Erbium hooks */ + MANUAL_RESPONSE, + PING_RESPONSE +} coap_status_t; + +/* CoAP header option numbers */ +typedef enum { + COAP_OPTION_IF_MATCH = 1, /* 0-8 B */ + COAP_OPTION_URI_HOST = 3, /* 1-255 B */ + COAP_OPTION_ETAG = 4, /* 1-8 B */ + COAP_OPTION_IF_NONE_MATCH = 5, /* 0 B */ + COAP_OPTION_OBSERVE = 6, /* 0-3 B */ + COAP_OPTION_URI_PORT = 7, /* 0-2 B */ + COAP_OPTION_LOCATION_PATH = 8, /* 0-255 B */ + COAP_OPTION_URI_PATH = 11, /* 0-255 B */ + COAP_OPTION_CONTENT_FORMAT = 12, /* 0-2 B */ + COAP_OPTION_MAX_AGE = 14, /* 0-4 B */ + COAP_OPTION_URI_QUERY = 15, /* 0-255 B */ + COAP_OPTION_ACCEPT = 17, /* 0-2 B */ + COAP_OPTION_LOCATION_QUERY = 20, /* 0-255 B */ + COAP_OPTION_BLOCK2 = 23, /* 1-3 B */ + COAP_OPTION_BLOCK1 = 27, /* 1-3 B */ + COAP_OPTION_SIZE2 = 28, /* 0-4 B */ + COAP_OPTION_PROXY_URI = 35, /* 1-1034 B */ + COAP_OPTION_PROXY_SCHEME = 39, /* 1-255 B */ + COAP_OPTION_SIZE1 = 60, /* 0-4 B */ +} coap_option_t; + +/* CoAP Content-Formats */ +typedef enum { + TEXT_PLAIN = 0, + TEXT_XML = 1, + TEXT_CSV = 2, + TEXT_HTML = 3, + IMAGE_GIF = 21, + IMAGE_JPEG = 22, + IMAGE_PNG = 23, + IMAGE_TIFF = 24, + AUDIO_RAW = 25, + VIDEO_RAW = 26, + APPLICATION_LINK_FORMAT = 40, + APPLICATION_XML = 41, + APPLICATION_OCTET_STREAM = 42, + APPLICATION_RDF_XML = 43, + APPLICATION_SOAP_XML = 44, + APPLICATION_ATOM_XML = 45, + APPLICATION_XMPP_XML = 46, + APPLICATION_EXI = 47, + APPLICATION_FASTINFOSET = 48, + APPLICATION_SOAP_FASTINFOSET = 49, + APPLICATION_JSON = 50, + APPLICATION_X_OBIX_BINARY = 51 +} coap_content_format_t; + +/** + * Resource flags for allowed methods and special functionalities. + */ +typedef enum { + NO_FLAGS = 0, + + /* methods to handle */ + METHOD_GET = (1 << 0), + METHOD_POST = (1 << 1), + METHOD_PUT = (1 << 2), + METHOD_DELETE = (1 << 3), + + /* special flags */ + HAS_SUB_RESOURCES = (1 << 4), + IS_SEPARATE = (1 << 5), + IS_OBSERVABLE = (1 << 6), + IS_PERIODIC = (1 << 7) +} coap_resource_flags_t; + +#endif /* COAP_CONSTANTS_H_ */ +/** @} */ diff --git a/wamr/core/shared/coap/extension/coap_ext.h b/wamr/core/shared/coap/extension/coap_ext.h new file mode 100644 index 0000000..f61deac --- /dev/null +++ b/wamr/core/shared/coap/extension/coap_ext.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef COAP_EXTENSION_COAP_EXT_H_ +#define COAP_EXTENSION_COAP_EXT_H_ + +#include "coap-constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define COAP_EVENT (COAP_DELETE + 2) + +#ifdef __cplusplus +} +#endif +#endif /* COAP_EXTENSION_COAP_EXT_H_ */ diff --git a/wamr/core/shared/coap/lib_coap.cmake b/wamr/core/shared/coap/lib_coap.cmake new file mode 100644 index 0000000..8970e5d --- /dev/null +++ b/wamr/core/shared/coap/lib_coap.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (LIB_COAP_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${LIB_COAP_DIR}/er-coap) +include_directories(${LIB_COAP_DIR}/extension) + +file (GLOB_RECURSE source_all ${LIB_COAP_DIR}/*.c) + +set (LIB_COAP_SOURCE ${source_all}) + diff --git a/wamr/core/shared/mem-alloc/ems/ems_alloc.c b/wamr/core/shared/mem-alloc/ems/ems_alloc.c new file mode 100644 index 0000000..d677756 --- /dev/null +++ b/wamr/core/shared/mem-alloc/ems/ems_alloc.c @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "ems_gc_internal.h" + + +static int +hmu_is_in_heap(gc_heap_t* heap, hmu_t* hmu) +{ + return heap && hmu + && (gc_uint8*) hmu >= heap->base_addr + && (gc_uint8*) hmu < heap->base_addr + heap->current_size; +} + +/** + * Remove a node from the tree it belongs to + * + * @param p the node to remove, can not be NULL, can not be the ROOT node + * the node will be removed from the tree, and the left, right and + * parent pointers of the node @p will be set to be NULL. Other fields + * won't be touched. The tree will be re-organized so that the order + * conditions are still satisified. + */ +static void +remove_tree_node(hmu_tree_node_t *p) +{ + hmu_tree_node_t *q = NULL, **slot = NULL; + + bh_assert(p); + bh_assert(p->parent); /* @p can not be the ROOT node*/ + + /* get the slot which holds pointer to node p*/ + if (p == p->parent->right) { + slot = &p->parent->right; + } else { + bh_assert(p == p->parent->left); /* @p should be a child of its parent*/ + slot = &p->parent->left; + } + + /** + * algorithms used to remove node p + * case 1: if p has no left child, replace p with its right child + * case 2: if p has no right child, replace p with its left child + * case 3: otherwise, find p's predecessor, remove it from the tree + * and replace p with it. + * use predecessor can keep the left <= root < right condition. + */ + + if (!p->left) { + /* move right child up*/ + *slot = p->right; + if (p->right) + p->right->parent = p->parent; + + p->left = p->right = p->parent = NULL; + return; + } + + if (!p->right) { + /* move left child up*/ + *slot = p->left; + p->left->parent = p->parent; /* p->left can never be NULL.*/ + + p->left = p->right = p->parent = NULL; + return; + } + + /* both left & right exist, find p's predecessor at first*/ + q = p->left; + while (q->right) + q = q->right; + /* remove from the tree*/ + remove_tree_node(q); + + *slot = q; + q->parent = p->parent; + q->left = p->left; + q->right = p->right; + if (q->left) + q->left->parent = q; + if (q->right) + q->right->parent = q; + + p->left = p->right = p->parent = NULL; +} + +static void +unlink_hmu(gc_heap_t *heap, hmu_t *hmu) +{ + gc_size_t size; + + bh_assert(gci_is_heap_valid(heap)); + bh_assert(hmu && (gc_uint8*) hmu >= heap->base_addr + && (gc_uint8*) hmu < heap->base_addr + heap->current_size); + bh_assert(hmu_get_ut(hmu) == HMU_FC); + + size = hmu_get_size(hmu); + + if (HMU_IS_FC_NORMAL(size)) { + uint32 node_idx = size >> 3; + hmu_normal_node_t *node_prev = &heap->kfc_normal_list[node_idx]; + hmu_normal_node_t *node = + get_hmu_normal_node_next(&heap->kfc_normal_list[node_idx]); + while (node) { + if ((hmu_t*) node == hmu) { + set_hmu_normal_node_next(node_prev, get_hmu_normal_node_next(node)); + break; + } + node_prev = node; + node = get_hmu_normal_node_next(node); + } + + if (!node) { + os_printf("[GC_ERROR]couldn't find the node in the normal list\n"); + } + } + else { + remove_tree_node((hmu_tree_node_t *) hmu); + } +} + +static void +hmu_set_free_size(hmu_t *hmu) +{ + gc_size_t size; + bh_assert(hmu && hmu_get_ut(hmu) == HMU_FC); + + size = hmu_get_size(hmu); + *((uint32*)((char*) hmu + size) - 1) = size; +} + +/** + * Add free chunk back to KFC + * + * @param heap should not be NULL and it should be a valid heap + * @param hmu should not be NULL and it should be a HMU of length @size inside @heap + * hmu should be 8-bytes aligned + * @param size should be positive and multiple of 8 + * hmu with size @size will be added into KFC as a new FC. + */ +void +gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size) +{ + hmu_normal_node_t *np = NULL; + hmu_tree_node_t *root = NULL, *tp = NULL, *node = NULL; + uint32 node_idx; + + bh_assert(gci_is_heap_valid(heap)); + bh_assert(hmu && (gc_uint8*) hmu >= heap->base_addr + && (gc_uint8*) hmu < heap->base_addr + heap->current_size); + bh_assert(((gc_uint32)(uintptr_t)hmu_to_obj(hmu) & 7) == 0); + bh_assert(size > 0 + && ((gc_uint8*) hmu) + size <= heap->base_addr + heap->current_size); + bh_assert(!(size & 7)); + + hmu_set_ut(hmu, HMU_FC); + hmu_set_size(hmu, size); + hmu_set_free_size(hmu); + + if (HMU_IS_FC_NORMAL(size)) { + np = (hmu_normal_node_t*) hmu; + + node_idx = size >> 3; + set_hmu_normal_node_next(np, get_hmu_normal_node_next + (&heap->kfc_normal_list[node_idx])); + set_hmu_normal_node_next(&heap->kfc_normal_list[node_idx], np); + return; + } + + /* big block*/ + node = (hmu_tree_node_t*) hmu; + node->size = size; + node->left = node->right = node->parent = NULL; + + /* find proper node to link this new node to*/ + root = &heap->kfc_tree_root; + tp = root; + bh_assert(tp->size < size); + while (1) { + if (tp->size < size) { + if (!tp->right) { + tp->right = node; + node->parent = tp; + break; + } + tp = tp->right; + } + else { /* tp->size >= size*/ + if (!tp->left) { + tp->left = node; + node->parent = tp; + break; + } + tp = tp->left; + } + } +} + +/** + * Find a proper hmu for required memory size + * + * @param heap should not be NULL and should be a valid heap + * @param size should cover the header and should be 8 bytes aligned + * GC will not be performed here. + * Heap extension will not be performed here. + * + * @return hmu allocated if success, which will be aligned to 8 bytes, + * NULL otherwise + */ +static hmu_t * +alloc_hmu(gc_heap_t *heap, gc_size_t size) +{ + hmu_normal_node_t *node = NULL, *p = NULL; + uint32 node_idx = 0, init_node_idx = 0; + hmu_tree_node_t *root = NULL, *tp = NULL, *last_tp = NULL; + hmu_t *next, *rest; + + bh_assert(gci_is_heap_valid(heap)); + bh_assert(size > 0 && !(size & 7)); + + if (size < GC_SMALLEST_SIZE) + size = GC_SMALLEST_SIZE; + + /* check normal list at first*/ + if (HMU_IS_FC_NORMAL(size)) { + /* find a non-empty slot in normal_node_list with good size*/ + init_node_idx = (size >> 3); + for (node_idx = init_node_idx; node_idx < HMU_NORMAL_NODE_CNT; + node_idx++) { + node = heap->kfc_normal_list + node_idx; + if (get_hmu_normal_node_next(node)) + break; + node = NULL; + } + + /* not found in normal list*/ + if (node) { + bh_assert(node_idx >= init_node_idx); + + p = get_hmu_normal_node_next(node); + set_hmu_normal_node_next(node, get_hmu_normal_node_next(p)); + bh_assert(((gc_int32)(uintptr_t)hmu_to_obj(p) & 7) == 0); + + if ((gc_size_t)node_idx != (uint32)init_node_idx + /* with bigger size*/ + && ((gc_size_t)node_idx << 3) >= size + GC_SMALLEST_SIZE) { + rest = (hmu_t*) (((char *) p) + size); + gci_add_fc(heap, rest, (node_idx << 3) - size); + hmu_mark_pinuse(rest); + } + else { + size = node_idx << 3; + next = (hmu_t*) ((char*) p + size); + if (hmu_is_in_heap(heap, next)) + hmu_mark_pinuse(next); + } + + heap->total_free_size -= size; + if ((heap->current_size - heap->total_free_size) + > heap->highmark_size) + heap->highmark_size = heap->current_size + - heap->total_free_size; + + hmu_set_size((hmu_t* )p, size); + return (hmu_t*)p; + } + } + + /* need to find a node in tree*/ + root = &heap->kfc_tree_root; + + /* find the best node*/ + bh_assert(root); + tp = root->right; + while (tp) { + if (tp->size < size) { + tp = tp->right; + continue; + } + + /* record the last node with size equal to or bigger than given size*/ + last_tp = tp; + tp = tp->left; + } + + if (last_tp) { + bh_assert(last_tp->size >= size); + + /* alloc in last_p*/ + + /* remove node last_p from tree*/ + remove_tree_node(last_tp); + + if (last_tp->size >= size + GC_SMALLEST_SIZE) { + rest = (hmu_t*) ((char*) last_tp + size); + gci_add_fc(heap, rest, last_tp->size - size); + hmu_mark_pinuse(rest); + } + else { + size = last_tp->size; + next = (hmu_t*) ((char*) last_tp + size); + if (hmu_is_in_heap(heap, next)) + hmu_mark_pinuse(next); + } + + heap->total_free_size -= size; + if ((heap->current_size - heap->total_free_size) > heap->highmark_size) + heap->highmark_size = heap->current_size - heap->total_free_size; + + hmu_set_size((hmu_t* ) last_tp, size); + return (hmu_t*) last_tp; + } + + return NULL; +} + +/** + * Find a proper HMU with given size + * + * @param heap should not be NULL and should be a valid heap + * @param size should cover the header and should be 8 bytes aligned + * + * Note: This function will try several ways to satisfy the allocation request: + * 1. Find a proper on available HMUs. + * 2. GC will be triggered if 1 failed. + * 3. Find a proper on available HMUS. + * 4. Return NULL if 3 failed + * + * @return hmu allocated if success, which will be aligned to 8 bytes, + * NULL otherwise + */ +static hmu_t * +alloc_hmu_ex(gc_heap_t *heap, gc_size_t size) +{ + bh_assert(gci_is_heap_valid(heap)); + bh_assert(size > 0 && !(size & 7)); + + return alloc_hmu(heap, size); +} + +static unsigned long g_total_malloc = 0; +static unsigned long g_total_free = 0; + +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_alloc_vo(void *vheap, gc_size_t size) +#else +gc_object_t +gc_alloc_vo_internal(void *vheap, gc_size_t size, + const char *file, int line) +#endif +{ + gc_heap_t* heap = (gc_heap_t*) vheap; + hmu_t *hmu = NULL; + gc_object_t ret = (gc_object_t) NULL; + gc_size_t tot_size = 0, tot_size_unaligned; + + /* hmu header + prefix + obj + suffix */ + tot_size_unaligned = HMU_SIZE + OBJ_PREFIX_SIZE + size + OBJ_SUFFIX_SIZE; + /* aligned size*/ + tot_size = GC_ALIGN_8(tot_size_unaligned); + if (tot_size < size) + /* integer overflow */ + return NULL; + + os_mutex_lock(&heap->lock); + + hmu = alloc_hmu_ex(heap, tot_size); + if (!hmu) + goto finish; + + g_total_malloc += tot_size; + + hmu_set_ut(hmu, HMU_VO); + hmu_unfree_vo(hmu); + +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); +#endif + + ret = hmu_to_obj(hmu); + if (tot_size > tot_size_unaligned) + /* clear buffer appended by GC_ALIGN_8() */ + memset((uint8*)ret + size, 0, tot_size - tot_size_unaligned); + +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_printf("HEAP.ALLOC: heap: %p, size: %u\n", heap, size); +#endif + +finish: + os_mutex_unlock(&heap->lock); + return ret; +} + +#if BH_ENABLE_GC_VERIFY == 0 +gc_object_t +gc_realloc_vo(void *vheap, void *ptr, gc_size_t size) +#else +gc_object_t +gc_realloc_vo_internal(void *vheap, void *ptr, gc_size_t size, + const char *file, int line) +#endif +{ + gc_heap_t* heap = (gc_heap_t*) vheap; + hmu_t *hmu = NULL, *hmu_old = NULL, *hmu_next; + gc_object_t ret = (gc_object_t) NULL, obj_old = (gc_object_t)ptr; + gc_size_t tot_size, tot_size_unaligned, tot_size_old = 0, tot_size_next; + gc_size_t obj_size, obj_size_old; + hmu_type_t ut; + + /* hmu header + prefix + obj + suffix */ + tot_size_unaligned = HMU_SIZE + OBJ_PREFIX_SIZE + size + OBJ_SUFFIX_SIZE; + /* aligned size*/ + tot_size = GC_ALIGN_8(tot_size_unaligned); + if (tot_size < size) + /* integer overflow */ + return NULL; + + if (obj_old) { + hmu_old = obj_to_hmu(obj_old); + tot_size_old = hmu_get_size(hmu_old); + if (tot_size <= tot_size_old) + /* current node alreay meets requirement */ + return obj_old; + } + + os_mutex_lock(&heap->lock); + + if (hmu_old) { + hmu_next = (hmu_t*)((char *)hmu_old + tot_size_old); + if (hmu_is_in_heap(heap, hmu_next)) { + ut = hmu_get_ut(hmu_next); + tot_size_next = hmu_get_size(hmu_next); + if (ut == HMU_FC + && tot_size <= tot_size_old + tot_size_next) { + /* current node and next node meets requirement */ + unlink_hmu(heap, hmu_next); + hmu_set_size(hmu_old, tot_size); + memset((char*)hmu_old + tot_size_old, 0, tot_size - tot_size_old); +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu_old, tot_size, file, line); +#endif + if (tot_size < tot_size_old + tot_size_next) { + hmu_next = (hmu_t*)((char*)hmu_old + tot_size); + tot_size_next = tot_size_old + tot_size_next - tot_size; + gci_add_fc(heap, hmu_next, tot_size_next); + } + os_mutex_unlock(&heap->lock); + return obj_old; + } + } + } + + + hmu = alloc_hmu_ex(heap, tot_size); + if (!hmu) + goto finish; + + g_total_malloc += tot_size; + + hmu_set_ut(hmu, HMU_VO); + hmu_unfree_vo(hmu); + +#if BH_ENABLE_GC_VERIFY != 0 + hmu_init_prefix_and_suffix(hmu, tot_size, file, line); +#endif + + ret = hmu_to_obj(hmu); + +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_printf("HEAP.ALLOC: heap: %p, size: %u\n", heap, size); +#endif + +finish: + os_mutex_unlock(&heap->lock); + + if (ret) { + obj_size = tot_size - HMU_SIZE - OBJ_PREFIX_SIZE - OBJ_SUFFIX_SIZE; + memset(ret, 0, obj_size); + if (obj_old) { + obj_size_old = tot_size_old - HMU_SIZE + - OBJ_PREFIX_SIZE - OBJ_SUFFIX_SIZE; + bh_memcpy_s(ret, obj_size, obj_old, obj_size_old); + gc_free_vo(vheap, obj_old); + } + } + + return ret; +} + +/** + * Do some checking to see if given pointer is a possible valid heap + * @return GC_TRUE if all checking passed, GC_FALSE otherwise + */ +int +gci_is_heap_valid(gc_heap_t *heap) +{ + if (!heap) + return GC_FALSE; + if (heap->heap_id != (gc_handle_t) heap) + return GC_FALSE; + + return GC_TRUE; +} + +#if BH_ENABLE_GC_VERIFY == 0 +int +gc_free_vo(void *vheap, gc_object_t obj) +#else +int +gc_free_vo_internal(void *vheap, gc_object_t obj, + const char *file, int line) +#endif +{ + gc_heap_t* heap = (gc_heap_t*) vheap; + hmu_t *hmu = NULL; + hmu_t *prev = NULL; + hmu_t *next = NULL; + gc_size_t size = 0; + hmu_type_t ut; + int ret = GC_SUCCESS; + + if (!obj) { + return GC_SUCCESS; + } + + hmu = obj_to_hmu(obj); + + os_mutex_lock(&heap->lock); + + if ((gc_uint8 *)hmu >= heap->base_addr + && (gc_uint8 *)hmu < heap->base_addr + heap->current_size) { +#if BH_ENABLE_GC_VERIFY != 0 + hmu_verify(hmu); +#endif + ut = hmu_get_ut(hmu); + if (ut == HMU_VO) { + if (hmu_is_vo_freed(hmu)) { + bh_assert(0); + ret = GC_ERROR; + goto out; + } + + size = hmu_get_size(hmu); + + g_total_free += size; + + heap->total_free_size += size; +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_printf("HEAP.FREE, heap: %p, size: %u\n", heap, size); +#endif + + if (!hmu_get_pinuse(hmu)) { + prev = (hmu_t*) ((char*) hmu - *((int*) hmu - 1)); + + if (hmu_is_in_heap(heap, prev) && hmu_get_ut(prev) == HMU_FC) { + size += hmu_get_size(prev); + hmu = prev; + unlink_hmu(heap, prev); + } + } + + next = (hmu_t*) ((char*) hmu + size); + if (hmu_is_in_heap(heap, next)) { + if (hmu_get_ut(next) == HMU_FC) { + size += hmu_get_size(next); + unlink_hmu(heap, next); + next = (hmu_t*) ((char*) hmu + size); + } + } + + gci_add_fc(heap, hmu, size); + + if (hmu_is_in_heap(heap, next)) { + hmu_unmark_pinuse(next); + } + + } else { + ret = GC_ERROR; + goto out; + } + ret = GC_SUCCESS; + goto out; + } + +out: + os_mutex_unlock(&heap->lock); + return ret; +} + +void +gc_dump_heap_stats(gc_heap_t *heap) +{ + os_printf("heap: %p, heap start: %p\n", heap, heap->base_addr); + os_printf("total free: %u, current: %u, highmark: %u\n", + heap->total_free_size, heap->current_size, heap->highmark_size); + os_printf("g_total_malloc=%lu, g_total_free=%lu, occupied=%lu\n", + g_total_malloc, g_total_free, g_total_malloc - g_total_free); +} + +void +gci_dump(gc_heap_t *heap) +{ + hmu_t *cur = NULL, *end = NULL; + hmu_type_t ut; + gc_size_t size; + int i = 0, p, mark; + char inuse = 'U'; + + cur = (hmu_t*)heap->base_addr; + end = (hmu_t*)((char*)heap->base_addr + heap->current_size); + + while(cur < end) { + ut = hmu_get_ut(cur); + size = hmu_get_size(cur); + p = hmu_get_pinuse(cur); + mark = hmu_is_jo_marked (cur); + + if (ut == HMU_VO) + inuse = 'V'; + else if (ut == HMU_JO) + inuse = hmu_is_jo_marked(cur) ? 'J' : 'j'; + else if (ut == HMU_FC) + inuse = 'F'; + + bh_assert(size > 0); + + os_printf("#%d %08x %x %x %d %c %d\n", + i, (int32)((char*) cur - (char*) heap->base_addr), + ut, p, mark, inuse, (int32)hmu_obj_size(size)); +#if BH_ENABLE_GC_VERIFY != 0 + if (inuse == 'V') { + gc_object_prefix_t *prefix = (gc_object_prefix_t *)(cur + 1); + os_printf("#%s:%d\n", prefix->file_name, prefix->line_no); + } +#endif + + cur = (hmu_t*)((char *)cur + size); + i++; + } + + bh_assert(cur == end); +} + diff --git a/wamr/core/shared/mem-alloc/ems/ems_gc.h b/wamr/core/shared/mem-alloc/ems/ems_gc.h new file mode 100644 index 0000000..206c8bd --- /dev/null +++ b/wamr/core/shared/mem-alloc/ems/ems_gc.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * @file ems_gc.h + * @date Wed Aug 3 10:46:38 2011 + * + * @brief This file defines GC modules types and interfaces. + * + * + */ + +#ifndef _EMS_GC_H +#define _EMS_GC_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define GC_HEAD_PADDING 4 + +#define NULL_REF ((gc_object_t)NULL) + +#define GC_SUCCESS (0) +#define GC_ERROR (-1) + +#define GC_TRUE (1) +#define GC_FALSE (0) + +#define GC_MAX_HEAP_SIZE (256 * BH_KB) + +typedef void * gc_handle_t; +typedef void * gc_object_t; +typedef int64 gc_int64; +typedef uint32 gc_uint32; +typedef int32 gc_int32; +typedef uint16 gc_uint16; +typedef int16 gc_int16; +typedef uint8 gc_uint8; +typedef int8 gc_int8; +typedef uint32 gc_size_t; + +typedef enum { + GC_STAT_TOTAL = 0, + GC_STAT_FREE, + GC_STAT_HIGHMARK, +} GC_STAT_INDEX; + +/** + * GC initialization from a buffer + * + * @param buf the buffer to be initialized to a heap + * @param buf_size the size of buffer + * + * @return gc handle if success, NULL otherwise + */ +gc_handle_t +gc_init_with_pool(char *buf, gc_size_t buf_size); + +/** + * Destroy heap which is initilized from a buffer + * + * @param handle handle to heap needed destroy + * + * @return GC_SUCCESS if success + * GC_ERROR for bad parameters or failed system resource freeing. + */ +int +gc_destroy_with_pool(gc_handle_t handle); + +/** + * Migrate heap from one place to another place + * + * @param handle handle of the new heap + * @param handle_old handle of the old heap + * + * @return GC_SUCCESS if success, GC_ERROR otherwise + */ +int +gc_migrate(gc_handle_t handle, gc_handle_t handle_old); + +/** + * Re-initialize lock of heap + * + * @param handle the heap handle + * + * @return GC_SUCCESS if success, GC_ERROR otherwise + */ +int +gc_reinit_lock(gc_handle_t handle); + +/** + * Destroy lock of heap + * + * @param handle the heap handle + */ +void +gc_destroy_lock(gc_handle_t handle); + +/** + * Get Heap Stats + * + * @param stats [out] integer array to save heap stats + * @param size [in] the size of stats + * @param mmt [in] type of heap, MMT_SHARED or MMT_INSTANCE + */ +void * +gc_heap_stats(void *heap, uint32* stats, int size); + +#if BH_ENABLE_GC_VERIFY == 0 + +gc_object_t +gc_alloc_vo(void *heap, gc_size_t size); + +gc_object_t +gc_realloc_vo(void *heap, void *ptr, gc_size_t size); + +int +gc_free_vo(void *heap, gc_object_t obj); + +#else /* else of BH_ENABLE_GC_VERIFY */ + +gc_object_t +gc_alloc_vo_internal(void *heap, gc_size_t size, + const char *file, int line); + +gc_object_t +gc_realloc_vo_internal(void *heap, void *ptr, gc_size_t size, + const char *file, int line); + +int +gc_free_vo_internal(void *heap, gc_object_t obj, + const char *file, int line); + +#define gc_alloc_vo(heap, size) \ + gc_alloc_vo_internal(heap, size, __FILE__, __LINE__) + +#define gc_realloc_vo(heap, ptr, size) \ + gc_realloc_vo_internal(heap, ptr, size, __FILE__, __LINE__) + +#define gc_free_vo(heap, obj) \ + gc_free_vo_internal(heap, obj, __FILE__, __LINE__) + +#endif /* end of BH_ENABLE_GC_VERIFY */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/wamr/core/shared/mem-alloc/ems/ems_gc_internal.h b/wamr/core/shared/mem-alloc/ems/ems_gc_internal.h new file mode 100644 index 0000000..976a32a --- /dev/null +++ b/wamr/core/shared/mem-alloc/ems/ems_gc_internal.h @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _EMS_GC_INTERNAL_H +#define _EMS_GC_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" +#include "ems_gc.h" + +/* HMU (heap memory unit) basic block type */ +typedef enum hmu_type_enum { + HMU_TYPE_MIN = 0, + HMU_TYPE_MAX = 3, + HMU_JO = 3, + HMU_VO = 2, + HMU_FC = 1, + HMU_FM = 0 +} hmu_type_t; + +typedef struct hmu_struct { + gc_uint32 header; +} hmu_t; + +#if BH_ENABLE_GC_VERIFY != 0 + +#define GC_OBJECT_PREFIX_PADDING_CNT 3 +#define GC_OBJECT_SUFFIX_PADDING_CNT 4 +#define GC_OBJECT_PADDING_VALUE (0x12345678) + +typedef struct gc_object_prefix { + const char *file_name; + gc_int32 line_no; + gc_int32 size; + gc_uint32 padding[GC_OBJECT_PREFIX_PADDING_CNT]; +} gc_object_prefix_t; + +typedef struct gc_object_suffix { + gc_uint32 padding[GC_OBJECT_SUFFIX_PADDING_CNT]; +} gc_object_suffix_t; + +#define OBJ_PREFIX_SIZE (sizeof(gc_object_prefix_t)) +#define OBJ_SUFFIX_SIZE (sizeof(gc_object_suffix_t)) + +void +hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, + const char *file_name, int line_no); + +void +hmu_verify(hmu_t *hmu); + +#define SKIP_OBJ_PREFIX(p) ((void*)((gc_uint8*)(p) + OBJ_PREFIX_SIZE)) +#define SKIP_OBJ_SUFFIX(p) ((void*)((gc_uint8*)(p) + OBJ_SUFFIX_SIZE)) + +#define OBJ_EXTRA_SIZE (HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE) + +#else /* else of BH_ENABLE_GC_VERIFY */ + +#define OBJ_PREFIX_SIZE 0 +#define OBJ_SUFFIX_SIZE 0 + +#define SKIP_OBJ_PREFIX(p) ((void*)((gc_uint8*)(p) + OBJ_PREFIX_SIZE)) +#define SKIP_OBJ_SUFFIX(p) ((void*)((gc_uint8*)(p) + OBJ_SUFFIX_SIZE)) + +#define OBJ_EXTRA_SIZE (HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE) + +#endif /* end of BH_ENABLE_GC_VERIFY */ + +#define hmu_obj_size(s) ((s)-OBJ_EXTRA_SIZE) + +#define GC_ALIGN_8(s) (((uint32)(s) + 7) & (uint32)~7) + +#define GC_SMALLEST_SIZE GC_ALIGN_8(HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE + 8) +#define GC_GET_REAL_SIZE(x) GC_ALIGN_8(HMU_SIZE + OBJ_PREFIX_SIZE + OBJ_SUFFIX_SIZE + (((x) > 8) ? (x): 8)) + +/** + * hmu bit operation + */ + +#define SETBIT(v, offset) (v) |= (1 << (offset)) +#define GETBIT(v, offset) ((v) & (1 << (offset)) ? 1 : 0) +#define CLRBIT(v, offset) (v) &= (uint32)(~(1 << (offset))) + +#define SETBITS(v, offset, size, value) do { \ + (v) &= (uint32)(~(((1 << size) - 1) << offset));\ + (v) |= (uint32)(value << offset); \ + } while(0) +#define CLRBITS(v, offset, size) (v) &= ~(((1 << size) - 1) << offset) +#define GETBITS(v, offset, size) (((v) & ((uint32)(((1 << size) - 1) << offset))) >> offset) + +/** + * gc object layout definition + */ + +#define HMU_SIZE (sizeof(hmu_t)) + +#define hmu_to_obj(hmu) (gc_object_t)(SKIP_OBJ_PREFIX((hmu_t*) (hmu) + 1)) +#define obj_to_hmu(obj) ((hmu_t *)((gc_uint8*)(obj) - OBJ_PREFIX_SIZE) - 1) + +#define HMU_UT_SIZE 2 +#define HMU_UT_OFFSET 30 + +#define hmu_get_ut(hmu) GETBITS ((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE) +#define hmu_set_ut(hmu, type) SETBITS ((hmu)->header, HMU_UT_OFFSET, HMU_UT_SIZE, type) +#define hmu_is_ut_valid(tp) (tp >= HMU_TYPE_MIN && tp <= HMU_TYPE_MAX) + +/* P in use bit means the previous chunk is in use */ +#define HMU_P_OFFSET 29 + +#define hmu_mark_pinuse(hmu) SETBIT ((hmu)->header, HMU_P_OFFSET) +#define hmu_unmark_pinuse(hmu) CLRBIT ((hmu)->header, HMU_P_OFFSET) +#define hmu_get_pinuse(hmu) GETBIT ((hmu)->header, HMU_P_OFFSET) + +#define HMU_JO_VT_SIZE 27 +#define HMU_JO_VT_OFFSET 0 +#define HMU_JO_MB_OFFSET 28 + +#define hmu_mark_jo(hmu) SETBIT ((hmu)->header, HMU_JO_MB_OFFSET) +#define hmu_unmark_jo(hmu) CLRBIT ((hmu)->header, HMU_JO_MB_OFFSET) +#define hmu_is_jo_marked(hmu) GETBIT ((hmu)->header, HMU_JO_MB_OFFSET) + +/** + * The hmu size is divisible by 8, its lowest 3 bits are 0, so we only + * store its higher bits of bit [29..3], and bit [2..0] are not stored. + * After that, the maximal heap size can be enlarged from (1<<27) = 128MB + * to (1<<27) * 8 = 1GB. + */ +#define HMU_SIZE_SIZE 27 +#define HMU_SIZE_OFFSET 0 + +#define HMU_VO_FB_OFFSET 28 + +#define hmu_is_vo_freed(hmu) GETBIT ((hmu)->header, HMU_VO_FB_OFFSET) +#define hmu_unfree_vo(hmu) CLRBIT ((hmu)->header, HMU_VO_FB_OFFSET) + +#define hmu_get_size(hmu) (GETBITS ((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE) << 3) +#define hmu_set_size(hmu, size) SETBITS ((hmu)->header, HMU_SIZE_OFFSET, HMU_SIZE_SIZE, ((size) >> 3)) + +/** + * HMU free chunk management + */ + +#define HMU_NORMAL_NODE_CNT 32 +#define HMU_FC_NORMAL_MAX_SIZE ((HMU_NORMAL_NODE_CNT - 1) << 3) +#define HMU_IS_FC_NORMAL(size) ((size) < HMU_FC_NORMAL_MAX_SIZE) +#if HMU_FC_NORMAL_MAX_SIZE >= GC_MAX_HEAP_SIZE +#error "Too small GC_MAX_HEAP_SIZE" +#endif + +typedef struct hmu_normal_node { + hmu_t hmu_header; + gc_int32 next_offset; +} hmu_normal_node_t; + +static inline hmu_normal_node_t * +get_hmu_normal_node_next(hmu_normal_node_t *node) +{ + return node->next_offset + ? (hmu_normal_node_t *)((uint8*)node + node->next_offset) + : NULL; +} + +static inline void +set_hmu_normal_node_next(hmu_normal_node_t *node, hmu_normal_node_t *next) +{ + if (next) { + bh_assert((uint8*)next - (uint8*)node < INT32_MAX); + node->next_offset = (gc_int32)(intptr_t) + ((uint8*)next - (uint8*)node); + } + else { + node->next_offset = 0; + } +} + +typedef struct hmu_tree_node { + hmu_t hmu_header; + gc_size_t size; + struct hmu_tree_node *left; + struct hmu_tree_node *right; + struct hmu_tree_node *parent; +} hmu_tree_node_t; + +typedef struct gc_heap_struct { + /* for double checking*/ + gc_handle_t heap_id; + + gc_uint8 *base_addr; + gc_size_t current_size; + + korp_mutex lock; + + hmu_normal_node_t kfc_normal_list[HMU_NORMAL_NODE_CNT]; + + /* order in kfc_tree is: size[left] <= size[cur] < size[right]*/ + hmu_tree_node_t kfc_tree_root; + + gc_size_t init_size; + gc_size_t highmark_size; + gc_size_t total_free_size; +} gc_heap_t; + +/** + * MISC internal used APIs + */ + +void +gci_add_fc(gc_heap_t *heap, hmu_t *hmu, gc_size_t size); + +int +gci_is_heap_valid(gc_heap_t *heap); + +/** + * Verify heap integrity + */ +void +gci_verify_heap(gc_heap_t *heap); + +/** + * Dump heap nodes + */ +void +gci_dump(gc_heap_t *heap); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/shared/mem-alloc/ems/ems_hmu.c b/wamr/core/shared/mem-alloc/ems/ems_hmu.c new file mode 100644 index 0000000..cd55dc1 --- /dev/null +++ b/wamr/core/shared/mem-alloc/ems/ems_hmu.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "ems_gc_internal.h" + +#if BH_ENABLE_GC_VERIFY != 0 + +/** + * Set default value to prefix and suffix + * @param hmu should not be NULL and should have been correctly initilized + * (except prefix and suffix part) + * @param tot_size is offered here because hmu_get_size can not be used till now. + * tot_size should not be smaller than OBJ_EXTRA_SIZE. + * For VO, tot_size should be equal to object total size. + */ +void +hmu_init_prefix_and_suffix(hmu_t *hmu, gc_size_t tot_size, + const char *file_name, int line_no) +{ + gc_object_prefix_t *prefix = NULL; + gc_object_suffix_t *suffix = NULL; + gc_uint32 i = 0; + + bh_assert(hmu); + bh_assert(hmu_get_ut(hmu) == HMU_JO || hmu_get_ut(hmu) == HMU_VO); + bh_assert(tot_size >= OBJ_EXTRA_SIZE); + bh_assert(!(tot_size & 7)); + bh_assert(hmu_get_ut(hmu) != HMU_VO || hmu_get_size(hmu) >= tot_size); + + prefix = (gc_object_prefix_t *)(hmu + 1); + suffix = (gc_object_suffix_t *)((gc_uint8*)hmu + tot_size - OBJ_SUFFIX_SIZE); + prefix->file_name = file_name; + prefix->line_no = line_no; + prefix->size = tot_size; + + for(i = 0;i < GC_OBJECT_PREFIX_PADDING_CNT;i++) { + prefix->padding[i] = GC_OBJECT_PADDING_VALUE; + } + + for(i = 0;i < GC_OBJECT_SUFFIX_PADDING_CNT;i++) { + suffix->padding[i] = GC_OBJECT_PADDING_VALUE; + } +} + +void +hmu_verify(hmu_t *hmu) +{ + gc_object_prefix_t *prefix = NULL; + gc_object_suffix_t *suffix = NULL; + gc_uint32 i = 0; + hmu_type_t ut; + gc_size_t size = 0; + int is_padding_ok = 1; + + bh_assert(hmu); + ut = hmu_get_ut(hmu); + bh_assert(hmu_is_ut_valid(ut)); + + prefix = (gc_object_prefix_t *)(hmu + 1); + size = prefix->size; + suffix = (gc_object_suffix_t *)((gc_uint8*)hmu + size - OBJ_SUFFIX_SIZE); + + if(ut == HMU_VO || ut == HMU_JO) + { + /* check padding*/ + for(i = 0;i < GC_OBJECT_PREFIX_PADDING_CNT;i++) + { + if(prefix->padding[i] != GC_OBJECT_PADDING_VALUE) + { + is_padding_ok = 0; + break; + } + } + for(i = 0;i < GC_OBJECT_SUFFIX_PADDING_CNT;i++) + { + if(suffix->padding[i] != GC_OBJECT_PADDING_VALUE) + { + is_padding_ok = 0; + break; + } + } + + if(!is_padding_ok) + { + os_printf("Invalid padding for object created at %s:%d", + (prefix->file_name ? prefix->file_name : ""), prefix->line_no); + } + bh_assert(is_padding_ok); + } +} + +#endif /* end of BH_ENABLE_GC_VERIFY */ + diff --git a/wamr/core/shared/mem-alloc/ems/ems_kfc.c b/wamr/core/shared/mem-alloc/ems/ems_kfc.c new file mode 100644 index 0000000..8e389ca --- /dev/null +++ b/wamr/core/shared/mem-alloc/ems/ems_kfc.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "ems_gc_internal.h" + +gc_handle_t +gc_init_with_pool(char *buf, gc_size_t buf_size) +{ + char *buf_end = buf + buf_size; + char *buf_aligned = (char*) (((uintptr_t) buf + 7) & (uintptr_t)~7); + char *base_addr = buf_aligned + sizeof(gc_heap_t); + gc_heap_t *heap = (gc_heap_t*) buf_aligned; + gc_size_t heap_max_size; + hmu_normal_node_t *p = NULL; + hmu_tree_node_t *root = NULL, *q = NULL; + int i = 0, ret; + + if (buf_size < 1024) { + os_printf("[GC_ERROR]heap_init_size(%d) < 1024\n", buf_size); + return NULL; + } + + base_addr = (char*) (((uintptr_t) base_addr + 7) & (uintptr_t)~7) + GC_HEAD_PADDING; + heap_max_size = (uint32)(buf_end - base_addr) & (uint32)~7; + + memset(heap, 0, sizeof *heap); + memset(base_addr, 0, heap_max_size); + + ret = os_mutex_init(&heap->lock); + if (ret != BHT_OK) { + os_printf("[GC_ERROR]failed to init lock\n"); + return NULL; + } + + /* init all data structures*/ + heap->current_size = heap_max_size; + heap->base_addr = (gc_uint8*)base_addr; + heap->heap_id = (gc_handle_t)heap; + + heap->total_free_size = heap->current_size; + heap->highmark_size = 0; + + for (i = 0; i < HMU_NORMAL_NODE_CNT; i++) { + /* make normal node look like a FC*/ + p = &heap->kfc_normal_list[i]; + memset(p, 0, sizeof *p); + hmu_set_ut(&p->hmu_header, HMU_FC); + hmu_set_size(&p->hmu_header, sizeof *p); + } + + root = &heap->kfc_tree_root; + memset(root, 0, sizeof *root); + root->size = sizeof *root; + hmu_set_ut(&root->hmu_header, HMU_FC); + hmu_set_size(&root->hmu_header, sizeof *root); + + q = (hmu_tree_node_t *) heap->base_addr; + memset(q, 0, sizeof *q); + hmu_set_ut(&q->hmu_header, HMU_FC); + hmu_set_size(&q->hmu_header, heap->current_size); + + hmu_mark_pinuse(&q->hmu_header); + root->right = q; + q->parent = root; + q->size = heap->current_size; + + bh_assert(root->size <= HMU_FC_NORMAL_MAX_SIZE + && HMU_FC_NORMAL_MAX_SIZE < q->size); + +#if BH_ENABLE_MEMORY_PROFILING != 0 + os_printf("heap is successfully initialized with max_size=%u.\n", + heap_max_size); +#endif + return heap; +} + +int +gc_destroy_with_pool(gc_handle_t handle) +{ + gc_heap_t *heap = (gc_heap_t *) handle; +#if BH_ENABLE_GC_VERIFY != 0 + hmu_t *cur = (hmu_t*)heap->base_addr; + hmu_t *end = (hmu_t*)((char*)heap->base_addr + heap->current_size); + if ((hmu_t*)((char *)cur + hmu_get_size(cur)) != end) { + os_printf("Memory leak detected:\n"); + gci_dump(heap); +#if WASM_ENABLE_SPEC_TEST != 0 + while (1); +#endif + } +#endif + os_mutex_destroy(&heap->lock); + memset(heap->base_addr, 0, heap->current_size); + memset(heap, 0, sizeof(gc_heap_t)); + return GC_SUCCESS; +} + +static void +adjust_ptr(uint8 **p_ptr, intptr_t offset) +{ + if (*p_ptr) + *p_ptr += offset; +} + +int +gc_migrate(gc_handle_t handle, gc_handle_t handle_old) +{ + gc_heap_t *heap = (gc_heap_t *) handle; + intptr_t offset = (uint8*)handle - (uint8*)handle_old; + hmu_t *cur = NULL, *end = NULL; + hmu_tree_node_t *tree_node; + gc_size_t size; + + os_mutex_init(&heap->lock); + + if (offset == 0) + return 0; + + heap->heap_id = (gc_handle_t)heap; + heap->base_addr += offset; + adjust_ptr((uint8**)&heap->kfc_tree_root.left, offset); + adjust_ptr((uint8**)&heap->kfc_tree_root.right, offset); + adjust_ptr((uint8**)&heap->kfc_tree_root.parent, offset); + + cur = (hmu_t*)heap->base_addr; + end = (hmu_t*)((char*)heap->base_addr + heap->current_size); + + while (cur < end) { + size = hmu_get_size(cur); + bh_assert(size > 0); + + if (hmu_get_ut(cur) == HMU_FC && !HMU_IS_FC_NORMAL(size)) { + tree_node = (hmu_tree_node_t *)cur; + adjust_ptr((uint8**)&tree_node->left, offset); + adjust_ptr((uint8**)&tree_node->right, offset); + adjust_ptr((uint8**)&tree_node->parent, offset); + } + cur = (hmu_t*)((char *)cur + size); + } + + bh_assert(cur == end); + return 0; +} + +int +gc_reinit_lock(gc_handle_t handle) +{ + gc_heap_t *heap = (gc_heap_t *) handle; + return os_mutex_init(&heap->lock); +} + +void +gc_destroy_lock(gc_handle_t handle) +{ + gc_heap_t *heap = (gc_heap_t *) handle; + os_mutex_destroy(&heap->lock); +} + +#if BH_ENABLE_GC_VERIFY != 0 +void +gci_verify_heap(gc_heap_t *heap) +{ + hmu_t *cur = NULL, *end = NULL; + + bh_assert(heap && gci_is_heap_valid(heap)); + cur = (hmu_t *)heap->base_addr; + end = (hmu_t *)(heap->base_addr + heap->current_size); + while(cur < end) + { + hmu_verify(cur); + cur = (hmu_t *)((gc_uint8*)cur + hmu_get_size(cur)); + } + bh_assert(cur == end); +} +#endif + +void * +gc_heap_stats(void *heap_arg, uint32* stats, int size) +{ + int i; + gc_heap_t *heap = (gc_heap_t *) heap_arg; + + for (i = 0; i < size; i++) { + switch (i) { + case GC_STAT_TOTAL: + stats[i] = heap->current_size; + break; + case GC_STAT_FREE: + stats[i] = heap->total_free_size; + break; + case GC_STAT_HIGHMARK: + stats[i] = heap->highmark_size; + break; + default: + break; + } + } + return heap; +} + diff --git a/wamr/core/shared/mem-alloc/mem_alloc.c b/wamr/core/shared/mem-alloc/mem_alloc.c new file mode 100644 index 0000000..32d0751 --- /dev/null +++ b/wamr/core/shared/mem-alloc/mem_alloc.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mem_alloc.h" + +#if DEFAULT_MEM_ALLOCATOR == MEM_ALLOCATOR_EMS + +#include "ems/ems_gc.h" + +mem_allocator_t mem_allocator_create(void *mem, uint32_t size) +{ + return gc_init_with_pool((char *) mem, size); +} + +void mem_allocator_destroy(mem_allocator_t allocator) +{ + gc_destroy_with_pool((gc_handle_t) allocator); +} + +void * +mem_allocator_malloc(mem_allocator_t allocator, uint32_t size) +{ + return gc_alloc_vo((gc_handle_t) allocator, size); +} + +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size) +{ + return gc_realloc_vo((gc_handle_t) allocator, ptr, size); +} + +void mem_allocator_free(mem_allocator_t allocator, void *ptr) +{ + if (ptr) + gc_free_vo((gc_handle_t) allocator, ptr); +} + +int +mem_allocator_migrate(mem_allocator_t allocator, + mem_allocator_t allocator_old) +{ + return gc_migrate((gc_handle_t) allocator, + (gc_handle_t) allocator_old); +} + +int +mem_allocator_reinit_lock(mem_allocator_t allocator) +{ + return gc_reinit_lock((gc_handle_t) allocator); +} + +void +mem_allocator_destroy_lock(mem_allocator_t allocator) +{ + gc_destroy_lock((gc_handle_t) allocator); +} + +#else /* else of DEFAULT_MEM_ALLOCATOR */ + +#include "tlsf/tlsf.h" + +typedef struct mem_allocator_tlsf { + tlsf_t tlsf; + korp_mutex lock; +}mem_allocator_tlsf; + +mem_allocator_t +mem_allocator_create(void *mem, uint32_t size) +{ + mem_allocator_tlsf *allocator_tlsf; + tlsf_t tlsf; + char *mem_aligned = (char*)(((uintptr_t)mem + 3) & ~3); + + if (size < 1024) { + printf("Create mem allocator failed: pool size must be " + "at least 1024 bytes.\n"); + return NULL; + } + + size -= mem_aligned - (char*)mem; + mem = (void*)mem_aligned; + + tlsf = tlsf_create_with_pool(mem, size); + if (!tlsf) { + printf("Create mem allocator failed: tlsf_create_with_pool failed.\n"); + return NULL; + } + + allocator_tlsf = tlsf_malloc(tlsf, sizeof(mem_allocator_tlsf)); + if (!allocator_tlsf) { + printf("Create mem allocator failed: tlsf_malloc failed.\n"); + tlsf_destroy(tlsf); + return NULL; + } + + allocator_tlsf->tlsf = tlsf; + + if (os_mutex_init(&allocator_tlsf->lock)) { + printf("Create mem allocator failed: tlsf_malloc failed.\n"); + tlsf_free(tlsf, allocator_tlsf); + tlsf_destroy(tlsf); + return NULL; + } + + return allocator_tlsf; +} + +void +mem_allocator_destroy(mem_allocator_t allocator) +{ + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + tlsf_t tlsf = allocator_tlsf->tlsf; + + os_mutex_destroy(&allocator_tlsf->lock); + tlsf_free(tlsf, allocator_tlsf); + tlsf_destroy(tlsf); +} + +void * +mem_allocator_malloc(mem_allocator_t allocator, uint32_t size) +{ + void *ret; + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + + if (size == 0) + /* tlsf doesn't allow to allocate 0 byte */ + size = 1; + + os_mutex_lock(&allocator_tlsf->lock); + ret = tlsf_malloc(allocator_tlsf->tlsf, size); + os_mutex_unlock(&allocator_tlsf->lock); + return ret; +} + +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size) +{ + void *ret; + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + + if (size == 0) + /* tlsf doesn't allow to allocate 0 byte */ + size = 1; + + os_mutex_lock(&allocator_tlsf->lock); + ret = tlsf_realloc(allocator_tlsf->tlsf, ptr, size); + os_mutex_unlock(&allocator_tlsf->lock); + return ret; +} + +void +mem_allocator_free(mem_allocator_t allocator, void *ptr) +{ + if (ptr) { + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + os_mutex_lock(&allocator_tlsf->lock); + tlsf_free(allocator_tlsf->tlsf, ptr); + os_mutex_unlock(&allocator_tlsf->lock); + } +} + +int +mem_allocator_migrate(mem_allocator_t allocator, + mem_allocator_t allocator_old) +{ + return tlsf_migrate((mem_allocator_tlsf *) allocator, + (mem_allocator_tlsf *) allocator_old); +} + +int +mem_allocator_init_lock(mem_allocator_t allocator) +{ + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + return os_mutex_init(&allocator_tlsf->lock); +} + +void +mem_allocator_destroy_lock(mem_allocator_t allocator) +{ + mem_allocator_tlsf *allocator_tlsf = (mem_allocator_tlsf *)allocator; + os_mutex_destroy(&allocator_tlsf->lock); +} + +#endif /* end of DEFAULT_MEM_ALLOCATOR */ + diff --git a/wamr/core/shared/mem-alloc/mem_alloc.cmake b/wamr/core/shared/mem-alloc/mem_alloc.cmake new file mode 100644 index 0000000..be57f37 --- /dev/null +++ b/wamr/core/shared/mem-alloc/mem_alloc.cmake @@ -0,0 +1,15 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + +set (MEM_ALLOC_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${MEM_ALLOC_DIR}) + +file (GLOB_RECURSE source_all + ${MEM_ALLOC_DIR}/ems/*.c + ${MEM_ALLOC_DIR}/tlsf/*.c + ${MEM_ALLOC_DIR}/mem_alloc.c) + +set (MEM_ALLOC_SHARED_SOURCE ${source_all}) + diff --git a/wamr/core/shared/mem-alloc/mem_alloc.h b/wamr/core/shared/mem-alloc/mem_alloc.h new file mode 100644 index 0000000..eb1032e --- /dev/null +++ b/wamr/core/shared/mem-alloc/mem_alloc.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef __MEM_ALLOC_H +#define __MEM_ALLOC_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *mem_allocator_t; + +mem_allocator_t +mem_allocator_create(void *mem, uint32_t size); + +void +mem_allocator_destroy(mem_allocator_t allocator); + +void * +mem_allocator_malloc(mem_allocator_t allocator, uint32_t size); + +void * +mem_allocator_realloc(mem_allocator_t allocator, void *ptr, uint32_t size); + +void +mem_allocator_free(mem_allocator_t allocator, void *ptr); + +int +mem_allocator_migrate(mem_allocator_t allocator, + mem_allocator_t allocator_old); + +int +mem_allocator_reinit_lock(mem_allocator_t allocator); + +void +mem_allocator_destroy_lock(mem_allocator_t allocator); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef __MEM_ALLOC_H */ + diff --git a/wamr/core/shared/platform/README.md b/wamr/core/shared/platform/README.md new file mode 100644 index 0000000..de6f1cc --- /dev/null +++ b/wamr/core/shared/platform/README.md @@ -0,0 +1,10 @@ +This folder contains the platform abstract layer for multiple platforms. To support a new platform, you can simply create a new folder here and implement all the APIs defined in [`include`](./include) folder. + + + +Refer to [port_wamr.md](../../../doc/port_wamr.md) for how to port WAMR to a target platform. + + + + + diff --git a/wamr/core/shared/platform/alios/alios_platform.c b/wamr/core/shared/platform/alios/alios_platform.c new file mode 100644 index 0000000..8150548 --- /dev/null +++ b/wamr/core/shared/platform/alios/alios_platform.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +int +os_thread_sys_init(); + +void +os_thread_sys_destroy(); + +int +bh_platform_init() +{ + return os_thread_sys_init(); +} + +void +bh_platform_destroy() +{ + os_thread_sys_destroy(); +} + +void * +os_malloc(unsigned size) +{ + return NULL; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return NULL; +} + +void +os_free(void *ptr) +{ +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags) +{ + if ((uint64)size >= UINT32_MAX) + return NULL; + return BH_MALLOC((uint32)size); +} + +void +os_munmap(void *addr, size_t size) +{ + return BH_FREE(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush() +{ +} diff --git a/wamr/core/shared/platform/alios/alios_thread.c b/wamr/core/shared/platform/alios/alios_thread.c new file mode 100644 index 0000000..f4028fb --- /dev/null +++ b/wamr/core/shared/platform/alios/alios_thread.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#define bh_assert(v) do { \ + if (!(v)) { \ + printf("\nASSERTION FAILED: %s, at %s, line %d\n", \ + #v, __FILE__, __LINE__); \ + aos_reboot(); \ + while (1); \ + } \ + } while (0) + +struct os_thread_data; +typedef struct os_thread_wait_node { + aos_sem_t sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Thread body */ + aos_task_t thread; + /* Thread start routine */ + thread_start_routine_t start_routine; + /* Thread start routine argument */ + void *arg; + /* Thread local root */ + void *tlr; + /* Wait node of current thread */ + os_thread_wait_node wait_node; + /* Lock for waiting list */ + aos_mutex_t wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; +} os_thread_data; + +static bool is_thread_sys_inited = false; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Thread data key */ +static aos_task_key_t thread_data_key; + +/* Thread name index */ +static int thread_name_index; + +int +os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + if (aos_task_key_create(&thread_data_key) != 0) + return BHT_ERROR; + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + + if (aos_sem_new(&supervisor_thread_data.wait_node.sem, 1) != 0) { + aos_task_key_delete(thread_data_key); + return BHT_ERROR; + } + + if (aos_task_setspecific(thread_data_key, &supervisor_thread_data)) { + aos_sem_free(&supervisor_thread_data.wait_node.sem); + aos_task_key_delete(thread_data_key); + return BHT_ERROR; + } + + is_thread_sys_inited = true; + return BHT_OK; +} + +void +os_thread_sys_destroy() +{ + if (is_thread_sys_inited) { + aos_task_key_delete(thread_data_key); + aos_sem_free(&supervisor_thread_data.wait_node.sem); + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + return aos_task_getspecific(thread_data_key); +} + +static void +os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + os_thread_wait_list thread_wait_list; + aos_mutex_t *wait_list_lock; + aos_sem_t *wait_node_sem; + + bh_assert(thread_data != NULL); + wait_list_lock = &thread_data->wait_list_lock; + thread_wait_list = thread_data->thread_wait_list; + wait_node_sem = &thread_data->wait_node.sem; + + /* Free thread data firstly */ + BH_FREE(thread_data); + + aos_mutex_lock(wait_list_lock, AOS_WAIT_FOREVER); + if (thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + aos_sem_signal(&head->sem); + head = next; + } + } + aos_mutex_unlock(wait_list_lock); + + /* Free sem and lock */ + aos_sem_free(wait_node_sem); + aos_mutex_free(wait_list_lock); +} + +static void +os_thread_wrapper(void *arg) +{ + os_thread_data *thread_data = arg; + + /* Set thread custom data */ + if (!aos_task_setspecific(thread_data_key, thread_data)) + thread_data->start_routine(thread_data->arg); + + os_thread_cleanup(); +} + +int +os_thread_create(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int +os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + os_thread_data *thread_data; + char thread_name[32]; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Create and initialize thread data */ + if (!(thread_data = BH_MALLOC(sizeof(os_thread_data)))) + return BHT_ERROR; + + memset(thread_data, 0, sizeof(os_thread_data)); + + thread_data->start_routine = start; + thread_data->arg = arg; + + if (aos_sem_new(&thread_data->wait_node.sem, 1) != 0) + goto fail1; + + if (aos_mutex_new(&thread_data->wait_list_lock)) + goto fail2; + + snprintf(thread_name, sizeof(thread_name), "%s%d", + "wasm-thread-", ++thread_name_index); + + /* Create the thread */ + if (aos_task_new_ext((aos_task_t*)thread_data, thread_name, + os_thread_wrapper, thread_data, + stack_size, prio)) + goto fail3; + + aos_msleep(10); + *p_tid = (korp_tid)thread_data; + return BHT_OK; + +fail3: + aos_mutex_free(&thread_data->wait_list_lock); +fail2: + aos_sem_free(&thread_data->wait_node.sem); +fail1: + BH_FREE(thread_data); + return BHT_ERROR; +} + +korp_tid +os_self_thread() +{ + return (korp_tid)aos_task_getspecific(thread_data_key); +} + +int +os_thread_join (korp_tid thread, void **value_ptr) +{ + (void)value_ptr; + os_thread_data *thread_data, *curr_thread_data; + + /* Get thread data of current thread */ + curr_thread_data = thread_data_current(); + curr_thread_data->wait_node.next = NULL; + + /* Get thread data */ + thread_data = (os_thread_data*)thread; + + aos_mutex_lock(&thread_data->wait_list_lock, AOS_WAIT_FOREVER); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = &curr_thread_data->wait_node; + else { + /* Add to end of waiting list */ + os_thread_wait_node *p = thread_data->thread_wait_list; + while (p->next) + p = p->next; + p->next = &curr_thread_data->wait_node; + } + aos_mutex_unlock(&thread_data->wait_list_lock); + + /* Wait the sem */ + aos_sem_wait(&curr_thread_data->wait_node.sem, AOS_WAIT_FOREVER); + + return BHT_OK; +} + +int +os_mutex_init(korp_mutex *mutex) +{ + return aos_mutex_new(mutex) == 0 ? BHT_OK : BHT_ERROR; +} + +int +os_mutex_destroy(korp_mutex *mutex) +{ + aos_mutex_free(mutex); + return BHT_OK; +} + +int +os_mutex_lock(korp_mutex *mutex) +{ + return aos_mutex_lock(mutex, AOS_WAIT_FOREVER); +} + +int +os_mutex_unlock(korp_mutex *mutex) +{ + return aos_mutex_unlock(mutex); +} + +int +os_cond_init(korp_cond *cond) +{ + if (aos_mutex_new(&cond->wait_list_lock) != 0) + return BHT_ERROR; + + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int +os_cond_destroy(korp_cond *cond) +{ + aos_mutex_free(&cond->wait_list_lock); + return BHT_OK; +} + +static int +os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, + bool timed, int mills) +{ + os_thread_wait_node *node = &thread_data_current()->wait_node; + + node->next = NULL; + + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + aos_mutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + aos_mutex_unlock(mutex); + aos_sem_wait(&node->sem, timed ? mills : AOS_WAIT_FOREVER); + aos_mutex_lock(mutex, AOS_WAIT_FOREVER); + + /* Remove wait node from wait list */ + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + else { + /* Remove from the wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next != node) + p = p->next; + p->next = node->next; + } + aos_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int +os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int +os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds) +{ + if (useconds == BHT_WAIT_FOREVER) + return os_cond_wait_internal(cond, mutex, false, 0); + else + return os_cond_wait_internal(cond, mutex, true, useconds / 1000); +} + +int +os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + aos_mutex_lock(&cond->wait_list_lock, AOS_WAIT_FOREVER); + if (cond->thread_wait_list) + aos_sem_signal(&cond->thread_wait_list->sem); + aos_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +uint8 *os_thread_get_stack_boundary() +{ + /* TODO: get alios stack boundary */ + return NULL; +} + diff --git a/wamr/core/shared/platform/alios/alios_time.c b/wamr/core/shared/platform/alios/alios_time.c new file mode 100644 index 0000000..f793049 --- /dev/null +++ b/wamr/core/shared/platform/alios/alios_time.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_microsecond() +{ + return (uint64)aos_now_ms() * 1000; +} + diff --git a/wamr/core/shared/platform/alios/platform_internal.h b/wamr/core/shared/platform/alios/platform_internal.h new file mode 100644 index 0000000..7201fb0 --- /dev/null +++ b/wamr/core/shared/platform/alios/platform_internal.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BH_PLATFORM_ALIOS_THINGS +#define BH_PLATFORM_ALIOS_THINGS +#endif + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 30 + +typedef aos_task_t korp_thread; +typedef korp_thread *korp_tid; +typedef aos_task_t *aos_tid_t; +typedef aos_mutex_t korp_mutex; + +struct os_thread_wait_node; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct korp_cond { + aos_mutex_t wait_list_lock; + os_thread_wait_list thread_wait_list; +} korp_cond; + +#define os_printf printf +#define os_vprintf vprintf + +/* math functions which are not provided by os*/ +double sqrt(double x); +double floor(double x); +double ceil(double x); +double fmin(double x, double y); +double fmax(double x, double y); +double rint(double x); +double fabs(double x); +double trunc(double x); +float floorf(float x); +float ceilf(float x); +float fminf(float x, float y); +float fmaxf(float x, float y); +float rintf(float x); +float truncf(float x); +int signbit(double x); +int isnan(double x); + +#endif /* end of _BH_PLATFORM_H */ + diff --git a/wamr/core/shared/platform/alios/shared_platform.cmake b/wamr/core/shared/platform/alios/shared_platform.cmake new file mode 100644 index 0000000..a3aaddd --- /dev/null +++ b/wamr/core/shared/platform/alios/shared_platform.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_ALIOS_THINGS) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + diff --git a/wamr/core/shared/platform/android/platform_init.c b/wamr/core/shared/platform/android/platform_init.c new file mode 100644 index 0000000..d250d43 --- /dev/null +++ b/wamr/core/shared/platform/android/platform_init.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + +int os_printf(const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = __android_log_vprint(ANDROID_LOG_INFO, "wasm_runtime::", fmt, ap); + va_end(ap); + + return ret; +} + +int os_vprintf(const char *fmt, va_list ap) +{ + return __android_log_vprint(ANDROID_LOG_INFO, "wasm_runtime::", fmt, ap); +} + diff --git a/wamr/core/shared/platform/android/platform_internal.h b/wamr/core/shared/platform/android/platform_internal.h new file mode 100644 index 0000000..2e3505f --- /dev/null +++ b/wamr/core/shared/platform/android/platform_internal.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_ANDROID +#define BH_PLATFORM_ANDROID +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +#define os_thread_local_attribute __thread + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +#define os_getpagesize getpagesize + +typedef void (*os_signal_handler)(void *sig_addr); + +int os_signal_init(os_signal_handler handler); + +void os_signal_destroy(); + +void os_signal_unmask(); + +void os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/android/shared_platform.cmake b/wamr/core/shared/platform/android/shared_platform.cmake new file mode 100644 index 0000000..13beb8e --- /dev/null +++ b/wamr/core/shared/platform/android/shared_platform.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_ANDROID) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/shared/platform/common/math/COPYRIGHT b/wamr/core/shared/platform/common/math/COPYRIGHT new file mode 100644 index 0000000..a0e1c83 --- /dev/null +++ b/wamr/core/shared/platform/common/math/COPYRIGHT @@ -0,0 +1,126 @@ +# $FreeBSD$ +# @(#)COPYRIGHT 8.2 (Berkeley) 3/21/94 + +The compilation of software known as FreeBSD is distributed under the +following terms: + +Copyright (c) 1992-2019 The FreeBSD Project. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The 4.4BSD and 4.4BSD-Lite software is distributed under the following +terms: + +All of the documentation and software included in the 4.4BSD and 4.4BSD-Lite +Releases is copyrighted by The Regents of the University of California. + +Copyright 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: +This product includes software developed by the University of +California, Berkeley and its contributors. +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The Institute of Electrical and Electronics Engineers and the American +National Standards Committee X3, on Information Processing Systems have +given us permission to reprint portions of their documentation. + +In the following statement, the phrase ``this text'' refers to portions +of the system documentation. + +Portions of this text are reprinted and reproduced in electronic form in +the second BSD Networking Software Release, from IEEE Std 1003.1-1988, IEEE +Standard Portable Operating System Interface for Computer Environments +(POSIX), copyright C 1988 by the Institute of Electrical and Electronics +Engineers, Inc. In the event of any discrepancy between these versions +and the original IEEE Standard, the original IEEE Standard is the referee +document. + +In the following statement, the phrase ``This material'' refers to portions +of the system documentation. + +This material is reproduced with permission from American National +Standards Committee X3, on Information Processing Systems. Computer and +Business Equipment Manufacturers Association (CBEMA), 311 First St., NW, +Suite 500, Washington, DC 20001-2178. The developmental work of +Programming Language C was completed by the X3J11 Technical Committee. + +The views and conclusions contained in the software and documentation are +those of the authors and should not be interpreted as representing official +policies, either expressed or implied, of the Regents of the University +of California. + + +NOTE: The copyright of UC Berkeley's Berkeley Software Distribution ("BSD") +source has been updated. The copyright addendum may be found at +ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change and is +included below. + +July 22, 1999 + +To All Licensees, Distributors of Any Version of BSD: + +As you know, certain of the Berkeley Software Distribution ("BSD") source +code files require that further distributions of products containing all or +portions of the software, acknowledge within their advertising materials +that such products contain software developed by UC Berkeley and its +contributors. + +Specifically, the provision reads: + +" * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors." + +Effective immediately, licensees and distributors are no longer required to +include the acknowledgement within advertising materials. Accordingly, the +foregoing paragraph of those BSD Unix files containing it is hereby deleted +in its entirety. + +William Hoskins +Director, Office of Technology Licensing +University of California, Berkeley diff --git a/wamr/core/shared/platform/common/math/math.c b/wamr/core/shared/platform/common/math/math.c new file mode 100644 index 0000000..1c1137b --- /dev/null +++ b/wamr/core/shared/platform/common/math/math.c @@ -0,0 +1,861 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2004 David Schultz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include "platform_common.h" + +#define __FDLIBM_STDC__ + +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; + +typedef union u32double_tag { + int *pint; + double *pdouble; +} U32DOUBLE; + +static inline int * +pdouble2pint(double *pdouble) +{ + U32DOUBLE u; + u.pdouble = pdouble; + return u.pint; +} + +typedef union +{ + double value; + struct + { + u_int32_t lsw; + u_int32_t msw; + } parts; + struct + { + u_int64_t w; + } xparts; +} ieee_double_shape_type_little; + +typedef union +{ + double value; + struct + { + u_int32_t msw; + u_int32_t lsw; + } parts; + struct + { + u_int64_t w; + } xparts; +} ieee_double_shape_type_big; + +typedef union { + double d; + struct { + unsigned int manl :32; + unsigned int manh :20; + unsigned int exp :11; + unsigned int sign :1; + } bits; +} IEEEd2bits_L; + +typedef union { + double d; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int manh :20; + unsigned int manl :32; + } bits; +} IEEEd2bits_B; + +typedef union { + float f; + struct { + unsigned int man :23; + unsigned int exp :8; + unsigned int sign :1; + } bits; +} IEEEf2bits_L; + +typedef union { + float f; + struct { + unsigned int sign :1; + unsigned int exp :8; + unsigned int man :23; + } bits; +} IEEEf2bits_B; + +static union { + int a; + char b; +} __ue = { .a = 1 }; + +#define is_little_endian() (__ue.b == 1) + +#define __HIL(x) *(1+pdouble2pint(&x)) +#define __LOL(x) *(pdouble2pint(&x)) +#define __HIB(x) *(pdouble2pint(&x)) +#define __LOB(x) *(1+pdouble2pint(&x)) + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS_L(ix0,ix1,d) \ + do { \ + ieee_double_shape_type_little ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS_L(d,ix0,ix1) \ + do { \ + ieee_double_shape_type_little iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS_B(ix0,ix1,d) \ + do { \ + ieee_double_shape_type_big ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.parts.msw; \ + (ix1) = ew_u.parts.lsw; \ + } while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS_B(d,ix0,ix1) \ + do { \ + ieee_double_shape_type_big iw_u; \ + iw_u.parts.msw = (ix0); \ + iw_u.parts.lsw = (ix1); \ + (d) = iw_u.value; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ +#define GET_HIGH_WORD_L(i,d) \ + do { \ + ieee_double_shape_type_little gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Get the more significant 32 bit int from a double. */ +#define GET_HIGH_WORD_B(i,d) \ + do { \ + ieee_double_shape_type_big gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.parts.msw; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ +#define SET_HIGH_WORD_L(d,v) \ + do { \ + ieee_double_shape_type_little sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* Set the more significant 32 bits of a double from an int. */ +#define SET_HIGH_WORD_B(d,v) \ + do { \ + ieee_double_shape_type_big sh_u; \ + sh_u.value = (d); \ + sh_u.parts.msw = (v); \ + (d) = sh_u.value; \ + } while (0) + +/* + * A union which permits us to convert between a float and a 32 bit + * int. + */ +typedef union +{ + float value; + /* FIXME: Assumes 32 bit int. */ + unsigned int word; +} ieee_float_shape_type; + +/* Get a 32 bit int from a float. */ +#define GET_FLOAT_WORD(i,d) \ + do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = gf_u.word; \ + } while (0) + +/* Set a float from a 32 bit int. */ +#define SET_FLOAT_WORD(d,i) \ + do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = (i); \ + (d) = sf_u.value; \ + } while (0) + +/* Macro wrappers. */ +#define EXTRACT_WORDS(ix0,ix1,d) do { \ + if (is_little_endian()) \ + EXTRACT_WORDS_L(ix0,ix1,d); \ + else \ + EXTRACT_WORDS_B(ix0,ix1,d); \ +} while (0) + +#define INSERT_WORDS(d,ix0,ix1) do { \ + if (is_little_endian()) \ + INSERT_WORDS_L(d,ix0,ix1); \ + else \ + INSERT_WORDS_B(d,ix0,ix1); \ +} while (0) + +#define GET_HIGH_WORD(i,d) \ + do { \ + if (is_little_endian()) \ + GET_HIGH_WORD_L(i,d); \ + else \ + GET_HIGH_WORD_B(i,d); \ + } while (0) + +#define SET_HIGH_WORD(d,v) \ + do { \ + if (is_little_endian()) \ + SET_HIGH_WORD_L(d,v); \ + else \ + SET_HIGH_WORD_B(d,v); \ + } while (0) + +#define __HI(x) (is_little_endian() ? __HIL(x) : __HIB(x)) + +#define __LO(x) (is_little_endian() ? __LOL(x) : __LOB(x)) + +/* + * Attempt to get strict C99 semantics for assignment with non-C99 compilers. + */ +#if FLT_EVAL_METHOD == 0 || __GNUC__ == 0 +#define STRICT_ASSIGN(type, lval, rval) ((lval) = (rval)) +#else +#define STRICT_ASSIGN(type, lval, rval) do { \ + volatile type __lval; \ + \ + if (sizeof(type) >= sizeof(long double)) \ + (lval) = (rval); \ + else { \ + __lval = (rval); \ + (lval) = __lval; \ + } \ +} while (0) +#endif + +#ifdef __FDLIBM_STDC__ +static const double huge = 1.0e300; +#else +static double huge = 1.0e300; +#endif + +#ifdef __STDC__ +static const double +#else +static double +#endif +tiny = 1.0e-300; + +#ifdef __STDC__ +static const double +#else +static double +#endif +one= 1.00000000000000000000e+00; /* 0x3FF00000, 0x00000000 */ + +#ifdef __STDC__ +static const double +#else +static double +#endif +TWO52[2]={ + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ +}; + +static double freebsd_sqrt(double x); +static double freebsd_floor(double x); +static double freebsd_ceil(double x); +static double freebsd_fabs(double x); +static double freebsd_rint(double x); +static int freebsd_isnan(double x); + +static double freebsd_sqrt(double x) /* wrapper sqrt */ +{ + double z; + int32_t sign = (int)0x80000000; + int32_t ix0,s0,q,m,t,i; + u_int32_t r,t1,s1,ix1,q1; + + EXTRACT_WORDS(ix0,ix1,x); + + /* take care of Inf and NaN */ + if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix0<=0) { + if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix0<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0>>20); + if(m==0) { /* subnormal x */ + while(ix0==0) { + m -= 21; + ix0 |= (ix1>>11); ix1 <<= 21; + } + for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; + m -= i-1; + ix0 |= (ix1>>(32-i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0&0x000fffff)|0x00100000; + if(m&1){ /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + r = sign; + while(r!=0) { + t1 = s1+r; + t = s0; + if((t>31); + ix1 += ix1; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if((ix0|ix1)!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (q1==(u_int32_t)0xffffffff) { q1=0; q += 1;} + else if (z>one) { + if (q1==(u_int32_t)0xfffffffe) q+=1; + q1+=2; + } else + q1 += (q1&1); + } + } + ix0 = (q>>1)+0x3fe00000; + ix1 = q1>>1; + if ((q&1)==1) ix1 |= sign; + ix0 += (m <<20); + + INSERT_WORDS(z,ix0,ix1); + + return z; +} + +static double freebsd_floor(double x) +{ + int32_t i0,i1,j0; + u_int32_t i,j; + + EXTRACT_WORDS(i0,i1,x); + + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=i1=0;} + else if(((i0&0x7fffffff)|i1)!=0) + { i0=0xbff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) { + if(j0==20) i0+=1; + else { + j = i1+(1<<(52-j0)); + if(j>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;i1=0;} + else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(j0==20) i0+=1; + else { + j = i1 + (1<<(52-j0)); + if(j>31)&1; + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { + if(((i0&0x7fffffff)|i1)==0) return x; + i1 |= (i0&0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1|-i1)>>12)&0x80000; + SET_HIGH_WORD(x,i0); + STRICT_ASSIGN(double,w,TWO52[sx]+x); + t = w-TWO52[sx]; + GET_HIGH_WORD(i0,t); + SET_HIGH_WORD(t,(i0&0x7fffffff)|(sx<<31)); + return t; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + i>>=1; + if(((i0&i)|i1)!=0) { + /* + * Some bit is set after the 0.5 bit. To avoid the + * possibility of errors from double rounding in + * w = TWO52[sx]+x, adjust the 0.25 bit to a lower + * guard bit. We do this for all j0<=51. The + * adjustment is trickiest for j0==18 and j0==19 + * since then it spans the word boundary. + */ + if(j0==19) i1 = 0x40000000; else + if(j0==18) i1 = 0x80000000; else + i0 = (i0&(~i))|((0x20000)>>j0); + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((u_int32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + i>>=1; + if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); + } + INSERT_WORDS(x,i0,i1); + STRICT_ASSIGN(double,w,TWO52[sx]+x); + return w-TWO52[sx]; +} + +static int freebsd_isnan(double d) +{ + if (is_little_endian()) { + IEEEd2bits_L u; + u.d = d; + return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); + } + else { + IEEEd2bits_B u; + u.d = d; + return (u.bits.exp == 2047 && (u.bits.manl != 0 || u.bits.manh != 0)); + } +} + +static double freebsd_fabs(double x) +{ + u_int32_t high; + GET_HIGH_WORD(high,x); + SET_HIGH_WORD(x,high&0x7fffffff); + return x; +} + +static const float huge_f = 1.0e30F; + +static const float +TWO23[2]={ + 8.3886080000e+06, /* 0x4b000000 */ + -8.3886080000e+06, /* 0xcb000000 */ +}; + +static float +freebsd_truncf(float x) +{ + int32_t i0,j0; + u_int32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge_f+x>0.0F) /* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000; + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge_f+x>0.0F) /* raise inexact flag */ + i0 &= (~i); + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} + +static float +freebsd_rintf(float x) +{ + int32_t i0,j0,sx; + float w,t; + GET_FLOAT_WORD(i0,x); + sx = (i0>>31)&1; + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { + if((i0&0x7fffffff)==0) return x; + STRICT_ASSIGN(float,w,TWO23[sx]+x); + t = w-TWO23[sx]; + GET_FLOAT_WORD(i0,t); + SET_FLOAT_WORD(t,(i0&0x7fffffff)|(sx<<31)); + return t; + } + STRICT_ASSIGN(float,w,TWO23[sx]+x); + return w-TWO23[sx]; + } + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ +} + +static float +freebsd_ceilf(float x) +{ + int32_t i0,j0; + u_int32_t i; + + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;} + else if(i0!=0) { i0=0x3f800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge_f+x>(float)0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} + +static float +freebsd_floorf(float x) +{ + int32_t i0,j0; + u_int32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge_f+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=0;} + else if((i0&0x7fffffff)!=0) + { i0=0xbf800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge_f+x>(float)0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} + +static float +freebsd_fminf(float x, float y) +{ + if (is_little_endian()) { + IEEEf2bits_L u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].f); + } + else { + IEEEf2bits_B u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[1].bits.sign].f); + } + + return (x < y ? x : y); +} + +static float +freebsd_fmaxf(float x, float y) +{ + if (is_little_endian()) { + IEEEf2bits_L u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].f); + } + else { + IEEEf2bits_B u[2]; + + u[0].f = x; + u[1].f = y; + + /* Check for NaNs to avoid raising spurious exceptions. */ + if (u[0].bits.exp == 255 && u[0].bits.man != 0) + return (y); + if (u[1].bits.exp == 255 && u[1].bits.man != 0) + return (x); + + /* Handle comparisons of signed zeroes. */ + if (u[0].bits.sign != u[1].bits.sign) + return (u[u[0].bits.sign].f); + } + + return (x > y ? x : y); +} + +double sqrt(double x) +{ + return freebsd_sqrt(x); +} + +double floor(double x) +{ + return freebsd_floor(x); +} + +double ceil(double x) +{ + return freebsd_ceil(x); +} + +double fmin(double x, double y) +{ + return x < y ? x : y; +} + +double fmax(double x, double y) +{ + return x > y ? x : y; +} + +double rint(double x) +{ + return freebsd_rint(x); +} + +double fabs(double x) +{ + return freebsd_fabs(x); +} + +int isnan(double x) +{ + return freebsd_isnan(x); +} + +double trunc(double x) +{ + return (x > 0) ? freebsd_floor(x) : freebsd_ceil(x); +} + +int signbit(double x) +{ + return ((__HI(x) & 0x80000000) >> 31); +} + +float +truncf(float x) +{ + return freebsd_truncf(x); +} + +float +rintf(float x) +{ + return freebsd_rintf(x); +} + +float +ceilf(float x) +{ + return freebsd_ceilf(x); +} + +float +floorf(float x) +{ + return freebsd_floorf(x); +} + +float +fminf(float x, float y) +{ + return freebsd_fminf(x, y); +} + +float +fmaxf(float x, float y) +{ + return freebsd_fmaxf(x, y); +} + diff --git a/wamr/core/shared/platform/common/math/platform_api_math.cmake b/wamr/core/shared/platform/common/math/platform_api_math.cmake new file mode 100644 index 0000000..09c74bf --- /dev/null +++ b/wamr/core/shared/platform/common/math/platform_api_math.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_MATH_DIR ${CMAKE_CURRENT_LIST_DIR}) + +file (GLOB_RECURSE source_all ${PLATFORM_COMMON_MATH_DIR}/*.c) + +set (PLATFORM_COMMON_MATH_SOURCE ${source_all} ) diff --git a/wamr/core/shared/platform/common/posix/platform_api_posix.cmake b/wamr/core/shared/platform/common/posix/platform_api_posix.cmake new file mode 100644 index 0000000..4abefff --- /dev/null +++ b/wamr/core/shared/platform/common/posix/platform_api_posix.cmake @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_COMMON_POSIX_DIR ${CMAKE_CURRENT_LIST_DIR}) + +file (GLOB_RECURSE source_all ${PLATFORM_COMMON_POSIX_DIR}/*.c) + +set (PLATFORM_COMMON_POSIX_SOURCE ${source_all} ) diff --git a/wamr/core/shared/platform/common/posix/posix_malloc.c b/wamr/core/shared/platform/common/posix/posix_malloc.c new file mode 100644 index 0000000..e83fc7d --- /dev/null +++ b/wamr/core/shared/platform/common/posix/posix_malloc.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + + + diff --git a/wamr/core/shared/platform/common/posix/posix_memmap.c b/wamr/core/shared/platform/common/posix/posix_memmap.c new file mode 100644 index 0000000..825afef --- /dev/null +++ b/wamr/core/shared/platform/common/posix/posix_memmap.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_mmap(void *hint, size_t size, int prot, int flags) +{ + int map_prot = PROT_NONE; + int map_flags = MAP_ANONYMOUS | MAP_PRIVATE; + uint64 request_size, page_size; + uint8 *addr; + uint32 i; + + page_size = (uint64)getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + + if ((size_t)request_size < size) + /* integer overflow */ + return NULL; + + if (request_size > 16 * (uint64)UINT32_MAX) + /* At most 16 G is allowed */ + return NULL; + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + +#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) +#ifndef __APPLE__ + if (flags & MMAP_MAP_32BIT) + map_flags |= MAP_32BIT; +#endif +#endif + + if (flags & MMAP_MAP_FIXED) + map_flags |= MAP_FIXED; + + /* try 5 times */ + for (i = 0; i < 5; i ++) { + addr = mmap(hint, request_size, map_prot, map_flags, -1, 0); + if (addr != MAP_FAILED) + break; + } + + if (addr == MAP_FAILED) + return NULL; + + return addr; +} + +void +os_munmap(void *addr, size_t size) +{ + uint64 page_size = (uint64)getpagesize(); + uint64 request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (munmap(addr, request_size)) { + os_printf("os_munmap error addr:%p, size:0x%"PRIx64", errno:%d\n", + addr, request_size, errno); + } + } +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + int map_prot = PROT_NONE; + uint64 page_size = (uint64)getpagesize(); + uint64 request_size = (size + page_size - 1) & ~(page_size - 1); + + if (!addr) + return 0; + + if (prot & MMAP_PROT_READ) + map_prot |= PROT_READ; + + if (prot & MMAP_PROT_WRITE) + map_prot |= PROT_WRITE; + + if (prot & MMAP_PROT_EXEC) + map_prot |= PROT_EXEC; + + return mprotect(addr, request_size, map_prot); +} + +void +os_dcache_flush(void) +{ +} diff --git a/wamr/core/shared/platform/common/posix/posix_thread.c b/wamr/core/shared/platform/common/posix/posix_thread.c new file mode 100644 index 0000000..5f1bcf1 --- /dev/null +++ b/wamr/core/shared/platform/common/posix/posix_thread.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +typedef struct { + thread_start_routine_t start; + void* stack; + uint32 stack_size; + void* arg; +} thread_wrapper_arg; + +static void *os_thread_wrapper(void *arg) +{ + thread_wrapper_arg * targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; + os_printf("THREAD CREATED %p\n", &targ); + targ->stack = (void *)((uintptr_t)(&arg) & (uintptr_t)~0xfff); + BH_FREE(targ); + start_func(thread_arg); + return NULL; +} + +int os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + pthread_attr_t tattr; + thread_wrapper_arg *targ; + + assert(stack_size > 0); + assert(tid); + assert(start); + + pthread_attr_init(&tattr); + pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE); + if (pthread_attr_setstacksize(&tattr, stack_size) != 0) { + os_printf("Invalid thread stack size %u. Min stack size on Linux = %u", + stack_size, PTHREAD_STACK_MIN); + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ = (thread_wrapper_arg*) BH_MALLOC(sizeof(*targ)); + if (!targ) { + pthread_attr_destroy(&tattr); + return BHT_ERROR; + } + + targ->start = start; + targ->arg = arg; + targ->stack_size = stack_size; + + if (pthread_create(tid, &tattr, os_thread_wrapper, targ) != 0) { + pthread_attr_destroy(&tattr); + BH_FREE(targ); + return BHT_ERROR; + } + + pthread_attr_destroy(&tattr); + return BHT_OK; +} + +int os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +korp_tid os_self_thread() +{ + return (korp_tid) pthread_self(); +} + +int os_mutex_init(korp_mutex *mutex) +{ + return pthread_mutex_init(mutex, NULL) == 0 ? BHT_OK : BHT_ERROR; +} + +int os_recursive_mutex_init(korp_mutex *mutex) +{ + int ret; + + pthread_mutexattr_t mattr; + + assert(mutex); + ret = pthread_mutexattr_init(&mattr); + if (ret) + return BHT_ERROR; + + pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); + ret = pthread_mutex_init(mutex, &mattr); + pthread_mutexattr_destroy(&mattr); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +int os_mutex_destroy(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_destroy(mutex); + + return ret == 0 ? BHT_OK : BHT_ERROR; +} + +/* Returned error (EINVAL, EAGAIN and EDEADLK) from + locking the mutex indicates some logic error present in + the program somewhere. + Don't try to recover error for an existing unknown error.*/ +int os_mutex_lock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_lock(mutex); + if (0 != ret) { + os_printf("vm mutex lock failed (ret=%d)!\n", ret); + exit(-1); + } + return ret; +} + +/* Returned error (EINVAL, EAGAIN and EPERM) from + unlocking the mutex indicates some logic error present + in the program somewhere. + Don't try to recover error for an existing unknown error.*/ +int os_mutex_unlock(korp_mutex *mutex) +{ + int ret; + + assert(mutex); + ret = pthread_mutex_unlock(mutex); + if (0 != ret) { + os_printf("vm mutex unlock failed (ret=%d)!\n", ret); + exit(-1); + } + return ret; +} + +int os_cond_init(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_init(cond, NULL) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int os_cond_destroy(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_destroy(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + assert(cond); + assert(mutex); + + if (pthread_cond_wait(cond, mutex) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +static void msec_nsec_to_abstime(struct timespec *ts, int usec) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + ts->tv_sec = (long int)(tv.tv_sec + usec / 1000000); + ts->tv_nsec = (long int)(tv.tv_usec * 1000 + (usec % 1000000) * 1000); + + if (ts->tv_nsec >= 1000000000L) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} + +int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds) +{ + int ret; + struct timespec abstime; + + if (useconds == (int)BHT_WAIT_FOREVER) + ret = pthread_cond_wait(cond, mutex); + else { + msec_nsec_to_abstime(&abstime, useconds); + ret = pthread_cond_timedwait(cond, mutex, &abstime); + } + + if (ret != BHT_OK && ret != ETIMEDOUT) + return BHT_ERROR; + + return BHT_OK; +} + +int os_cond_signal(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_signal(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int os_thread_join(korp_tid thread, void **value_ptr) +{ + return pthread_join(thread, value_ptr); +} + +int os_thread_detach(korp_tid thread) +{ + return pthread_detach(thread); +} + +void os_thread_exit(void *retval) +{ + return pthread_exit(retval); +} + +uint8 *os_thread_get_stack_boundary() +{ + pthread_t self = pthread_self(); + pthread_attr_t attr; + uint8 *addr = NULL; + size_t stack_size, guard_size; + int page_size = getpagesize(); + size_t max_stack_size = (size_t) + (APP_THREAD_STACK_SIZE_MAX + page_size - 1) + & ~(page_size - 1); + + if (max_stack_size < APP_THREAD_STACK_SIZE_DEFAULT) + max_stack_size = APP_THREAD_STACK_SIZE_DEFAULT; + +#ifdef __linux__ + if (pthread_getattr_np(self, &attr) == 0) { + pthread_attr_getstack(&attr, (void**)&addr, &stack_size); + pthread_attr_getguardsize(&attr, &guard_size); + pthread_attr_destroy(&attr); + if (stack_size > max_stack_size) + addr = addr + stack_size - max_stack_size; + if (guard_size < (size_t)page_size) + /* Reserved 1 guard page at least for safety */ + guard_size = (size_t)page_size; + addr += guard_size; + } + (void)stack_size; +#elif defined(__APPLE__) + if ((addr = (uint8*)pthread_get_stackaddr_np(self))) { + stack_size = pthread_get_stacksize_np(self); + if (stack_size > max_stack_size) + addr -= max_stack_size; + else + addr -= stack_size; + /* Reserved 1 guard page at least for safety */ + addr += page_size; + } +#endif + + return addr; +} + +#ifdef OS_ENABLE_HW_BOUND_CHECK + +#define SIG_ALT_STACK_SIZE (32 * 1024) + +/* The signal alternate stack base addr */ +static uint8 *sigalt_stack_base_addr; + +/* The signal handler passed to os_signal_init() */ +static os_signal_handler signal_handler; + +static void +mask_signals(int how) +{ + sigset_t set; + + sigemptyset(&set); + sigaddset(&set, SIGSEGV); + sigaddset(&set, SIGBUS); + pthread_sigmask(how, &set, NULL); +} + +__attribute__((noreturn)) static void +signal_callback(int sig_num, siginfo_t *sig_info, void *sig_ucontext) +{ + int i; + void *sig_addr = sig_info->si_addr; + + mask_signals(SIG_BLOCK); + + if (signal_handler + && (sig_num == SIGSEGV || sig_num == SIGBUS)) { + signal_handler(sig_addr); + } + + /* signal unhandled */ + switch (sig_num) { + case SIGSEGV: + os_printf("unhandled SIGSEGV, si_addr: %p\n", sig_addr); + break; + case SIGBUS: + os_printf("unhandled SIGBUS, si_addr: %p\n", sig_addr); + break; + default: + os_printf("unhandle signal %d, si_addr: %p\n", + sig_num, sig_addr); + break; + } + + /* divived by 0 to make it abort */ + i = os_printf(" "); + os_printf("%d\n", i / (i - 1)); + /* access NULL ptr to make it abort */ + os_printf("%d\n", *(uint32*)(uintptr_t)(i - 1)); + exit(1); +} + +int +os_signal_init(os_signal_handler handler) +{ + int ret = -1; + struct sigaction sig_act; + stack_t sigalt_stack_info; + uint32 map_size = SIG_ALT_STACK_SIZE; + uint8 *map_addr; + + /* Initialize memory for signal alternate stack */ + if (!(map_addr = os_mmap(NULL, map_size, + MMAP_PROT_READ | MMAP_PROT_WRITE, + MMAP_MAP_NONE))) { + os_printf("Failed to mmap memory for alternate stack\n"); + return -1; + } + + /* Initialize signal alternate stack */ + memset(map_addr, 0, map_size); + sigalt_stack_info.ss_sp = map_addr; + sigalt_stack_info.ss_size = map_size; + sigalt_stack_info.ss_flags = 0; + if ((ret = sigaltstack(&sigalt_stack_info, NULL)) != 0) { + goto fail1; + } + + /* Install signal hanlder */ + sig_act.sa_sigaction = signal_callback; + sig_act.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; + sigemptyset(&sig_act.sa_mask); + if ((ret = sigaction(SIGSEGV, &sig_act, NULL)) != 0 + || (ret = sigaction(SIGBUS, &sig_act, NULL)) != 0) { + goto fail2; + } + + sigalt_stack_base_addr = map_addr; + signal_handler = handler; + return 0; + +fail2: + memset(&sigalt_stack_info, 0, sizeof(stack_t)); + sigalt_stack_info.ss_flags = SS_DISABLE; + sigalt_stack_info.ss_size = map_size; + sigaltstack(&sigalt_stack_info, NULL); +fail1: + os_munmap(map_addr, map_size); + return ret; +} + +void +os_signal_destroy() +{ + stack_t sigalt_stack_info; + + /* Disable signal alternate stack */ + memset(&sigalt_stack_info, 0, sizeof(stack_t)); + sigalt_stack_info.ss_flags = SS_DISABLE; + sigalt_stack_info.ss_size = SIG_ALT_STACK_SIZE; + sigaltstack(&sigalt_stack_info, NULL); + + os_munmap(sigalt_stack_base_addr, SIG_ALT_STACK_SIZE); +} + +void +os_signal_unmask() +{ + mask_signals(SIG_UNBLOCK); +} + +void +os_sigreturn() +{ +#if defined(__APPLE__) + #define UC_RESET_ALT_STACK 0x80000000 + extern int __sigreturn(void *, int); + + /* It's necessary to call __sigreturn to restore the sigaltstack state + after exiting the signal handler. */ + __sigreturn(NULL, UC_RESET_ALT_STACK); +#endif +} +#endif /* end of OS_ENABLE_HW_BOUND_CHECK */ + diff --git a/wamr/core/shared/platform/common/posix/posix_time.c b/wamr/core/shared/platform/common/posix/posix_time.c new file mode 100644 index 0000000..744dd4c --- /dev/null +++ b/wamr/core/shared/platform/common/posix/posix_time.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_microsecond() +{ + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + + return ((uint64) ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +} + diff --git a/wamr/core/shared/platform/darwin/platform_init.c b/wamr/core/shared/platform/darwin/platform_init.c new file mode 100644 index 0000000..4f80608 --- /dev/null +++ b/wamr/core/shared/platform/darwin/platform_init.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); + ret += vprintf(format, ap); + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ + return vprintf(format, ap); +} + diff --git a/wamr/core/shared/platform/darwin/platform_internal.h b/wamr/core/shared/platform/darwin/platform_internal.h new file mode 100644 index 0000000..75348df --- /dev/null +++ b/wamr/core/shared/platform/darwin/platform_internal.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_DARWIN +#define BH_PLATFORM_DARWIN +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +#define os_thread_local_attribute __thread + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +#define os_getpagesize getpagesize + +typedef void (*os_signal_handler)(void *sig_addr); + +int os_signal_init(os_signal_handler handler); + +void os_signal_destroy(); + +void os_signal_unmask(); + +void os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/darwin/shared_platform.cmake b/wamr/core/shared/platform/darwin/shared_platform.cmake new file mode 100644 index 0000000..5eecd65 --- /dev/null +++ b/wamr/core/shared/platform/darwin/shared_platform.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_DARWIN) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/shared/platform/include/platform_api_extension.h b/wamr/core/shared/platform/include/platform_api_extension.h new file mode 100644 index 0000000..55cd9c9 --- /dev/null +++ b/wamr/core/shared/platform/include/platform_api_extension.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef PLATFORM_API_EXTENSION_H +#define PLATFORM_API_EXTENSION_H + +#include "platform_common.h" +/** + * The related data structures should be defined + * in platform_internal.h + **/ +#include "platform_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************** + * * + * Extension interface * + * * + ***************************************************/ + +/** + * NOTES: + * 1. If you are building VM core only, it must be implemented to + * enable multi-thread support, otherwise no need to implement it + * 2. To build the app-mgr and app-framework, you must implement it + */ + + +/** + * Ceates a thread + * + * @param p_tid [OUTPUT] the pointer of tid + * @param start main routine of the thread + * @param arg argument passed to main routine + * @param stack_size bytes of stack size + * + * @return 0 if success. + */ +int os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size); + +/** + * Creates a thread with priority + * + * @param p_tid [OUTPUT] the pointer of tid + * @param start main routine of the thread + * @param arg argument passed to main routine + * @param stack_size bytes of stack size + * @param prio the priority + * + * @return 0 if success. + */ +int os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio); + +/** + * Waits for the thread specified by thread to terminate + * + * @param thread the thread to wait + * @param retval if not NULL, output the exit status of the terminated thread + * + * @return return 0 if success + */ +int os_thread_join(korp_tid thread, void **retval); + +/** + * Detach the thread specified by thread + * + * @param thread the thread to detach + * + * @return return 0 if success + */ +int os_thread_detach(korp_tid); + +/** + * Exit current thread + * + * @param retval the return value of the current thread + */ +void os_thread_exit(void *retval); + +/** + * Suspend execution of the calling thread for (at least) + * usec microseconds + * + * @param return 0 if success, -1 otherwise + */ +int os_usleep(uint32 usec); + +/** + * Creates a recursive mutex + * + * @param mutex [OUTPUT] pointer to mutex initialized. + * + * @return 0 if success + */ +int os_recursive_mutex_init(korp_mutex *mutex); + +/** + * This function creates a condition variable + * + * @param cond [OUTPUT] pointer to condition variable + * + * @return 0 if success + */ +int os_cond_init(korp_cond *cond); + +/** + * This function destroys condition variable + * + * @param cond pointer to condition variable + * + * @return 0 if success + */ +int os_cond_destroy(korp_cond *cond); + +/** + * Wait a condition variable. + * + * @param cond pointer to condition variable + * @param mutex pointer to mutex to protect the condition variable + * + * @return 0 if success + */ +int os_cond_wait(korp_cond *cond, korp_mutex *mutex); + +/** + * Wait a condition varible or return if time specified passes. + * + * @param cond pointer to condition variable + * @param mutex pointer to mutex to protect the condition variable + * @param useconds microseconds to wait + * + * @return 0 if success + */ +int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds); + +/** + * Signals the condition variable + * + * @param cond condition variable + * + * @return 0 if success + */ +int os_cond_signal(korp_cond *cond); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef PLATFORM_API_EXTENSION_H */ diff --git a/wamr/core/shared/platform/include/platform_api_vmcore.h b/wamr/core/shared/platform/include/platform_api_vmcore.h new file mode 100644 index 0000000..0f162fc --- /dev/null +++ b/wamr/core/shared/platform/include/platform_api_vmcore.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_API_VMCORE_H +#define _PLATFORM_API_VMCORE_H + +#include "platform_common.h" +#include "platform_internal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************** + * Section 1 * + * Interfaces required by the runtime * + ****************************************************/ + +/** + * Initialize the platform internal resources if needed, + * this function is called by wasm_runtime_init() and + * wasm_runtime_full_init() + * + * @return 0 if success + */ +int bh_platform_init(void); + +/** + * Destroy the platform internal resources if needed, + * this function is called by wasm_runtime_destroy() + */ +void bh_platform_destroy(void); + +/** + ******** memory allocator APIs ********** + */ + +void *os_malloc(unsigned size); + +void *os_realloc(void *ptr, unsigned size); + +void os_free(void *ptr); + +/** + * Note: the above APIs can simply return NULL if wasm runtime + * isn't initialized with Alloc_With_System_Allocator. + * Refer to wasm_runtime_full_init(). + */ + + +int os_printf(const char *format, ...); + +int os_vprintf(const char *format, va_list ap); + +/** + * Get microseconds after boot. + */ +uint64 os_time_get_boot_microsecond(void); + +/** + * Get current thread id. + * Implementation optional: Used by runtime for logging only. + */ +korp_tid os_self_thread(void); + +/** + * Get current thread's stack boundary address, used for runtime + * to check the native stack overflow. Return NULL if it is not + * easy to implement, but may have potential issue. + */ +uint8 *os_thread_get_stack_boundary(void); + +/** + ************** mutext APIs *********** + * vmcore: Not required until pthread is supported by runtime + * app-mgr: Must be implemented + */ + +int os_mutex_init(korp_mutex *mutex); + +int os_mutex_destroy(korp_mutex *mutex); + +int os_mutex_lock(korp_mutex *mutex); + +int os_mutex_unlock(korp_mutex *mutex); + + +/************************************************** + * Section 2 * + * APIs required by WAMR AOT * + **************************************************/ + +/* Memory map modes */ +enum { + MMAP_PROT_NONE = 0, + MMAP_PROT_READ = 1, + MMAP_PROT_WRITE = 2, + MMAP_PROT_EXEC = 4 +}; + +/* Memory map flags */ +enum { + MMAP_MAP_NONE = 0, + /* Put the mapping into 0 to 2 G, supported only on x86_64 */ + MMAP_MAP_32BIT = 1, + /* Don't interpret addr as a hint: place the mapping at exactly + that address. */ + MMAP_MAP_FIXED = 2 +}; + +void *os_mmap(void *hint, size_t size, int prot, int flags); +void os_munmap(void *addr, size_t size); +int os_mprotect(void *addr, size_t size, int prot); + +/** + * Flush cpu data cache, in some CPUs, after applying relocation to the + * AOT code, the code may haven't been written back to the cpu data cache, + * which may cause unexpected behaviour when executing the AOT code. + * Implement this function if required, or just leave it empty. + */ +void os_dcache_flush(void); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _PLATFORM_API_VMCORE_H */ diff --git a/wamr/core/shared/platform/include/platform_common.h b/wamr/core/shared/platform/include/platform_common.h new file mode 100644 index 0000000..60570d7 --- /dev/null +++ b/wamr/core/shared/platform/include/platform_common.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_COMMON_H +#define _PLATFORM_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../../config.h" +#include "platform_internal.h" + +#define BH_MAX_THREAD 32 + +#define BHT_ERROR (-1) +#define BHT_TIMED_OUT (1) +#define BHT_OK (0) + +#define BHT_NO_WAIT 0x00000000 +#define BHT_WAIT_FOREVER 0xFFFFFFFF + +#define BH_KB (1024) +#define BH_MB ((BH_KB)*1024) +#define BH_GB ((BH_MB)*1024) + +#ifndef BH_MALLOC +#define BH_MALLOC os_malloc +#endif + +#ifndef BH_FREE +#define BH_FREE os_free +#endif + +void *BH_MALLOC(unsigned int size); +void BH_FREE(void *ptr); + +#ifndef NULL +#define NULL (void*)0 +#endif + +#ifndef __cplusplus +#define true 1 +#define false 0 +#define inline __inline +#endif + +/* Return the offset of the given field in the given type */ +#ifndef offsetof +#define offsetof(Type, field) ((size_t)(&((Type *)0)->field)) +#endif + +typedef uint8_t uint8; +typedef int8_t int8; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint32_t uint32; +typedef int32_t int32; +typedef float float32; +typedef double float64; +typedef uint64_t uint64; +typedef int64_t int64; + +typedef void* (*thread_start_routine_t)(void*); + + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _PLATFORM_COMMON_H */ diff --git a/wamr/core/shared/platform/linux-sgx/platform_internal.h b/wamr/core/shared/platform/linux-sgx/platform_internal.h new file mode 100644 index 0000000..65b4087 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/platform_internal.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgx_error.h" +#include "sgx_file.h" +#include "sgx_pthread.h" +#include "sgx_time.h" +#include "sgx_socket.h" +#include "sgx_signal.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_LINUX_SGX +#define BH_PLATFORM_LINUX_SGX +#endif + +#define _STACK_SIZE_ADJUSTMENT (32 * 1024) + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (8 * 1024 + _STACK_SIZE_ADJUSTMENT) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_thread; +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; + +typedef void (*os_print_function_t)(const char* message); +void os_set_print_function(os_print_function_t pf); + +char *strcpy(char *dest, const char *src); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_file.c b/wamr/core/shared/platform/linux-sgx/sgx_file.c new file mode 100644 index 0000000..3a0fba3 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_file.c @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "sgx_error.h" +#include "sgx_file.h" + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +/** fd **/ +int ocall_open(int *p_fd, const char *pathname, int flags, + bool has_mode, unsigned mode); + +int ocall_openat(int *p_fd, int dirfd, const char *pathname, int flags, + bool has_mode, unsigned mode); + +int ocall_read(ssize_t *p_ret, int fd, void *buf, size_t read_size); + +int ocall_close(int *p_ret, int fd); + +int ocall_lseek(off_t *p_ret, int fd, off_t offset, int whence); + +int ocall_ftruncate(int *p_ret, int fd, off_t length); + +int ocall_fsync(int *p_ret, int fd); + +int ocall_fdatasync(int *p_ret, int fd); + +int ocall_isatty(int *p_ret, int fd); +/** fd end **/ + +/** DIR **/ +int ocall_fdopendir(int fd, void **p_dirp); + +int ocall_readdir(void **p_dirent, void *dirp); + +int ocall_rewinddir(void *dirp); + +int ocall_seekdir(void *dirp, long loc); + +int ocall_telldir(long *p_dir, void *dirp); + +int ocall_closedir(int *p_ret, void *dirp); +/** DIR end **/ + +/** stat **/ +int ocall_fstat(int *p_ret, int fd, void *buf, unsigned int buf_len); +int ocall_fstatat(int *p_ret, int dirfd, const char *pathname, void *buf, + unsigned int buf_len, int flags); +/** stat end **/ + +/** link **/ +int ocall_mkdirat(int *p_ret, int dirfd, const char * pathname, + unsigned mode); +int ocall_link(int *p_ret, const char *oldpath, const char *newpath); +int ocall_linkat(int *p_ret, int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, int flags); +int ocall_unlinkat(int *p_ret, int dirfd, const char *pathname, + int flags); +int ocall_readlinkat(ssize_t *p_ret, int dirfd, const char *pathname, + char *buf, size_t bufsiz); +int ocall_renameat(int *p_ret, int olddirfd,const char *oldpath, + int newdirfd,const char *newpath); +int ocall_symlinkat(int *p_ret, const char *target, int newdirfd, + const char *linkpath); +/** link end **/ + +/** control **/ +int ocall_ioctl(int *p_ret, int fd, unsigned long request, void *arg, + unsigned int arg_len); +int ocall_fcntl(int *p_ret, int fd, int cmd); +int ocall_fcntl_long(int *p_ret, int fd, int cmd, long arg); +/** control end **/ + +/** **/ +int ocall_realpath(int *p_ret, const char *path, char *buf, + unsigned int buf_len); +int ocall_posix_fallocate(int *p_ret, int fd, off_t offset, off_t len); +int ocall_poll(int *p_ret, void *fds, unsigned nfds, int timeout, + unsigned int fds_len); +int ocall_getopt(int *p_ret, int argc, char *argv_buf, + unsigned int argv_buf_len, const char *optstring); +int ocall_getrandom(ssize_t *p_ret, void *buf, size_t buflen, + unsigned int flags); +int ocall_sched_yield(int *p_ret); + +/** struct iovec **/ +ssize_t ocall_readv(ssize_t *p_ret, int fd, char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); +ssize_t ocall_writev(ssize_t *p_ret, int fd, char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); +/** iovec end **/ + +int ocall_get_errno(int *p_ret); + +int open(const char *pathname, int flags, ...) +{ + int fd; + bool has_mode = false; + mode_t mode = 0; + + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + has_mode = true; + } + + if (SGX_SUCCESS != ocall_open(&fd, pathname, flags, has_mode, mode)) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (fd >= 0 && (flags & O_CLOEXEC)) + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (fd == -1) + errno = get_errno(); + return fd; +} + +int openat(int dirfd, const char *pathname, int flags, ...) +{ + int fd; + bool has_mode = false; + mode_t mode = 0; + + if ((flags & O_CREAT) || (flags & O_TMPFILE) == O_TMPFILE) { + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + has_mode = true; + } + + if (SGX_SUCCESS != ocall_openat(&fd, dirfd, pathname, flags, + has_mode, mode)) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (fd >= 0 && (flags & O_CLOEXEC)) + fcntl(fd, F_SETFD, FD_CLOEXEC); + + if (fd == -1) + errno = get_errno(); + return fd; +} + +int close(int fd) +{ + int ret; + + if (ocall_close(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t read(int fd, void *buf, size_t size) +{ + ssize_t ret; + int size_read_max = 2048, size_read, total_size_read = 0, count, i; + char *p = buf; + + if (buf == NULL) { + TRACE_FUNC(); + return -1; + } + + count = (size + size_read_max - 1) / size_read_max; + for (i = 0; i < count; i++) { + size_read = (i < count - 1) + ? size_read_max + : size - size_read_max * i; + + if (ocall_read(&ret, fd, p, size_read) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) { + /* read failed */ + errno = get_errno(); + return -1; + } + + p += ret; + total_size_read += ret; + + if (ret < size_read) + /* end of file */ + break; + } + return total_size_read; +} + +DIR *fdopendir(int fd) +{ + DIR *result = NULL; + + result = (DIR *)BH_MALLOC(sizeof(DIR)); + if (!result) + return NULL; + + if (ocall_fdopendir(fd, (void **)result) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(result); + return NULL; + } + + if ((void *)*result == NULL) { /* opendir failed */ + TRACE_FUNC(); + BH_FREE(result); + errno = get_errno(); + return NULL; + } + + return result; +} + +struct dirent *readdir(DIR *dirp) +{ + struct dirent *result; + + if (dirp == NULL) + return NULL; + + if (ocall_readdir((void **)&result, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return NULL; + } + + if (!result) + errno = get_errno(); + return result; +} + +void rewinddir(DIR *dirp) +{ + if (ocall_rewinddir((void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } +} + +void seekdir(DIR *dirp, long loc) +{ + if (ocall_seekdir((void *)*dirp, loc) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } +} + +long telldir(DIR *dirp) +{ + long ret; + + if (ocall_telldir(&ret, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int closedir(DIR *dirp) +{ + int ret; + + if (ocall_closedir(&ret, (void *)*dirp) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + BH_FREE(dirp); + if (ret == -1) + errno = get_errno(); + return ret; +} + +static ssize_t +readv_internal(int fd, const struct iovec *iov, int iovcnt, + bool has_offset, off_t offset) +{ + ssize_t ret, size_left; + struct iovec *iov1; + int i; + char *p; + uint64 total_size = sizeof(struct iovec) * (uint64)iovcnt; + + if (iov == NULL || iovcnt < 1) + return -1; + + for (i = 0; i < iovcnt; i++) { + total_size += iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + iov1 = BH_MALLOC((uint32)total_size); + + if (iov1 == NULL) + return -1; + + memset(iov1, 0, (uint32)total_size); + + p = (char*)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + for (i = 0; i < iovcnt; i++) { + iov1[i].iov_len = iov[i].iov_len; + iov1[i].iov_base = p; + p += iov[i].iov_len; + } + + if (ocall_readv(&ret, fd, (char *)iov1, (uint32)total_size, + iovcnt, has_offset, offset) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(iov1); + return -1; + } + + p = (char *)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + size_left = ret; + for (i = 0; i < iovcnt; i++) { + if (size_left > iov[i].iov_len) { + memcpy(iov[i].iov_base, (uintptr_t)p + (char *)iov1, + iov[i].iov_len); + p += iov[i].iov_len; + size_left -= iov[i].iov_len; + } + else { + memcpy(iov[i].iov_base, (uintptr_t)p + (char *)iov1, + size_left); + break; + } + } + + BH_FREE(iov1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +static ssize_t +writev_internal(int fd, const struct iovec *iov, int iovcnt, + bool has_offset, off_t offset) +{ + ssize_t ret; + struct iovec *iov1; + int i; + char *p; + uint64 total_size = sizeof(struct iovec) * (uint64)iovcnt; + + if (iov == NULL || iovcnt < 1) + return -1; + + for (i = 0; i < iovcnt; i++) { + total_size += iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + iov1 = BH_MALLOC((uint32)total_size); + + if (iov1 == NULL) + return -1; + + memset(iov1, 0, (uint32)total_size); + + p = (char *)(uintptr_t)(sizeof(struct iovec) * iovcnt); + + for (i = 0; i < iovcnt; i++) { + iov1[i].iov_len = iov[i].iov_len; + iov1[i].iov_base = p; + memcpy((uintptr_t)p + (char *)iov1, iov[i].iov_base, + iov[i].iov_len); + p += iov[i].iov_len; + } + + if (ocall_writev(&ret, fd, (char *)iov1, (uint32)total_size, + iovcnt, has_offset, offset) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(iov1); + return -1; + } + + BH_FREE(iov1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t readv(int fd, const struct iovec *iov, int iovcnt) +{ + return readv_internal(fd, iov, iovcnt, false, 0); +} + +ssize_t writev(int fd, const struct iovec *iov, int iovcnt) +{ + return writev_internal(fd, iov, iovcnt, false, 0); +} + +ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, + off_t offset) +{ + return readv_internal(fd, iov, iovcnt, true, offset); +} + +ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, + off_t offset) +{ + return writev_internal(fd, iov, iovcnt, true, offset); +} + +off_t lseek(int fd, off_t offset, int whence) +{ + off_t ret; + if (ocall_lseek(&ret, fd, (long)offset, whence) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int ftruncate(int fd, off_t length) +{ + int ret; + + if (ocall_ftruncate(&ret, fd, length) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int fstat(int fd, struct stat *statbuf) +{ + int ret; + + if (statbuf == NULL) + return -1; + + if (ocall_fstat(&ret, fd, (void *)statbuf, + sizeof(struct stat)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int fstatat(int dirfd, const char *pathname, struct stat *statbuf, + int flags) +{ + int ret; + + if (statbuf == NULL) + return -1; + + if (ocall_fstatat(&ret, dirfd, pathname, (void *)statbuf, + sizeof(struct stat), flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int fsync(int fd) +{ + int ret; + + if (ocall_fsync(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int fdatasync(int fd) +{ + int ret; + + if (ocall_fdatasync(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int mkdirat(int dirfd, const char *pathname, mode_t mode) +{ + int ret; + + if (ocall_mkdirat(&ret, dirfd, pathname, mode) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int link(const char *oldpath, const char *newpath) +{ + int ret; + + if (ocall_link(&ret, oldpath, newpath) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int linkat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, int flags) +{ + int ret; + + if (ocall_linkat(&ret, olddirfd, oldpath, newdirfd, newpath, + flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int unlinkat(int dirfd, const char *pathname, int flags) +{ + int ret; + + if (ocall_unlinkat(&ret, dirfd, pathname, flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t readlinkat(int dirfd, const char *pathname, + char *buf, size_t bufsiz) +{ + ssize_t ret; + + if (buf == NULL) + return -1; + + if (ocall_readlinkat(&ret, dirfd, pathname, buf, + bufsiz) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int symlinkat(const char *target, int newdirfd, const char *linkpath) +{ + int ret; + + if (ocall_symlinkat(&ret, target, + newdirfd, linkpath) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int renameat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) +{ + int ret; + + if (ocall_renameat(&ret, olddirfd, oldpath, + newdirfd, newpath) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int ioctl(int fd, unsigned long request, ...) +{ + int ret; + va_list args; + + switch (request) { + case FIONREAD: + va_start(args, request); + int *arg = (int *)va_arg(args, int *); + if (ocall_ioctl(&ret, fd, request, arg, + sizeof(*arg)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + va_end(args); + return -1; + } + va_end(args); + break; + + default: + os_printf("ioctl failed: unknown request", request); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int fcntl(int fd, int cmd, ... /* arg */ ) +{ + int ret; + va_list args; + + switch (cmd) { + case F_GETFD: + case F_GETFL: + if (ocall_fcntl(&ret, fd, cmd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + break; + + case F_DUPFD: + case F_SETFD: + case F_SETFL: + va_start(args, cmd); + long arg_1 = (long)va_arg(args, long); + if (ocall_fcntl_long(&ret, fd, cmd, arg_1) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + va_end(args); + return -1; + } + va_end(args); + break; + + default: + os_printf("fcntl failed: unknown cmd %d.\n", cmd); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int isatty(int fd) +{ + int ret; + + if (ocall_isatty(&ret, fd) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == 0) + errno = get_errno(); + return ret; +} + +char *realpath(const char *path, char *resolved_path) +{ + int ret; + char buf[PATH_MAX] = { 0 }; + + if (ocall_realpath(&ret, path, buf, PATH_MAX) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return (char *)NULL; + } + + if (ret != 0) + return (char *)NULL; + + if (resolved_path) { + strcpy(resolved_path, buf); + } + else { + resolved_path = BH_MALLOC(strlen(buf) + 1); + if (resolved_path == NULL) + return NULL; + strcpy(resolved_path, buf); + } + + return resolved_path; +} + +int posix_fallocate(int fd, off_t offset, off_t len) +{ + int ret; + + if (ocall_posix_fallocate(&ret, fd, offset, len) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + return ret; +} + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret; + + if (fds == NULL) + return -1; + + if (ocall_poll(&ret, fds, nfds, timeout, + sizeof(*fds) * nfds) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + return ret; +} + +int getopt(int argc, char * const argv[], + const char *optstring) +{ + int ret; + char **argv1; + char *p; + int i; + uint64 total_size = sizeof(char *) * (uint64)argc; + + for (i = 0; i < argc; i++) { + total_size += strlen(argv[i]) + 1; + } + + if (total_size >= UINT32_MAX) + return -1; + + argv1 = BH_MALLOC((uint32)total_size); + + if (argv1 == NULL) + return -1; + + p = (char *)(uintptr_t)(sizeof(char *) * argc); + + for (i = 0; i < argc; i++) { + argv1[i] = p; + strcpy((char *)argv1 + (uintptr_t)p, argv[i]); + p += ((uintptr_t)strlen(argv[i]) + 1); + } + + if (ocall_getopt(&ret, argc, (char *)argv1, total_size, + optstring) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + BH_FREE(argv1); + return -1; + } + + BH_FREE(argv1); + if (ret == -1) + errno = get_errno(); + return ret; +} + +int sched_yield(void) +{ + int ret; + + if (ocall_sched_yield(&ret) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) +{ + ssize_t ret; + + if (ocall_getrandom(&ret, buf, buflen, flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + if (ret == -1) + errno = get_errno(); + return ret; +} + +int get_errno(void) +{ + int ret; + + if (ocall_get_errno(&ret) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + return ret; +} diff --git a/wamr/core/shared/platform/linux-sgx/sgx_file.h b/wamr/core/shared/platform/linux-sgx/sgx_file.h new file mode 100644 index 0000000..7d04676 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_file.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_FILE_H +#define _SGX_FILE_H + +#include "sgx_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define F_DUPFD 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#define F_SETFL 4 + +#define FD_CLOEXEC 1 + +#define O_PATH 010000000 +#define O_SEARCH O_PATH +#define O_EXEC O_PATH + +#define O_ACCMODE (03|O_SEARCH) +#define O_RDONLY 00 +#define O_WRONLY 01 +#define O_RDWR 02 + +#define O_CREAT 0100 +#define O_EXCL 0200 +#define O_NOCTTY 0400 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_NONBLOCK 04000 +#define O_DSYNC 010000 +#define O_SYNC 04010000 +#define O_RSYNC 04010000 +#define O_DIRECTORY 0200000 +#define O_NOFOLLOW 0400000 +#define O_CLOEXEC 02000000 + +#define O_ASYNC 020000 +#define O_DIRECT 040000 +#define O_LARGEFILE 0 +#define O_NOATIME 01000000 +#define O_PATH 010000000 +#define O_TMPFILE 020200000 +#define O_NDELAY O_NONBLOCK + +#define S_IFMT 0170000 +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFBLK 0060000 +#define S_IFREG 0100000 +#define S_IFIFO 0010000 +#define S_IFLNK 0120000 +#define S_IFSOCK 0140000 + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) + +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 +#define DT_WHT 14 + +#define AT_SYMLINK_NOFOLLOW 0x100 +#define AT_REMOVEDIR 0x200 +#define AT_SYMLINK_FOLLOW 0x400 + +#define POLLIN 0x001 +#define POLLPRI 0x002 +#define POLLOUT 0x004 +#define POLLERR 0x008 +#define POLLHUP 0x010 +#define POLLNVAL 0x020 +#define POLLRDNORM 0x040 +#define POLLRDBAND 0x080 +#define POLLWRNORM 0x100 +#define POLLWRBAND 0x200 + +#define FIONREAD 0x541B + +#define PATH_MAX 4096 + +/* Special value used to indicate openat should use the current + working directory. */ +#define AT_FDCWD -100 + +typedef long __syscall_slong_t; + +typedef unsigned long dev_t; +typedef unsigned long ino_t; +typedef unsigned mode_t; +typedef unsigned long nlink_t; +typedef unsigned socklen_t; +typedef long blksize_t; +typedef long blkcnt_t; + +typedef int pid_t; +typedef unsigned gid_t; +typedef unsigned uid_t; + +typedef unsigned long nfds_t; + +typedef uintptr_t DIR; + +struct dirent { + ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +struct stat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + long __unused[3]; +}; + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +struct pollfd { + int fd; + short events; + short revents; +}; + +int open(const char *pathname, int flags, ...); +int openat(int dirfd, const char *pathname, int flags, ...); +int close(int fd); + +DIR *fdopendir(int fd); +int closedir(DIR *dirp); +void rewinddir(DIR *dirp); +void seekdir(DIR *dirp, long loc); +struct dirent *readdir(DIR *dirp); +long telldir(DIR *dirp); + +ssize_t read(int fd, void *buf, size_t count); +ssize_t readv(int fd, const struct iovec *iov, int iovcnt); +ssize_t writev(int fd, const struct iovec *iov, int iovcnt); +ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, + off_t offset); +ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, + off_t offset); + +off_t lseek(int fd, off_t offset, int whence); +int ftruncate(int fd, off_t length); + +int fstat(int fd, struct stat *statbuf); +int fstatat(int dirfd, const char *pathname, struct stat *statbuf, + int flags); + +int fsync(int fd); +int fdatasync(int fd); + +int mkdirat(int dirfd, const char *pathname, mode_t mode); +int link(const char *oldpath, const char *newpath); +int linkat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, int flags); +int unlinkat(int dirfd, const char *pathname, int flags); +ssize_t readlinkat(int dirfd, const char *pathname, + char *buf, size_t bufsiz); +int symlinkat(const char *target, int newdirfd, const char *linkpath); +int renameat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath); + +int ioctl(int fd, unsigned long request, ...); +int fcntl(int fd, int cmd, ... /* arg */ ); + +int isatty(int fd); + +char *realpath(const char *path, char *resolved_path); + +int posix_fallocate(int fd, off_t offset, off_t len); + +int poll(struct pollfd *fds, nfds_t nfds, int timeout); + +int getopt(int argc, char * const argv[], + const char *optstring); + +int sched_yield(void); + +ssize_t getrandom(void *buf, size_t buflen, unsigned int flags); + +int get_errno(void); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_FILE_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_platform.c b/wamr/core/shared/platform/linux-sgx/sgx_platform.c new file mode 100644 index 0000000..abfb991 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_platform.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" +#include "sgx_rsrv_mem_mngr.h" + +#define FIXED_BUFFER_SIZE (1<<9) + +static os_print_function_t print_function = NULL; + +int bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + +int putchar(int c) +{ + return 0; +} + +int puts(const char *s) +{ + return 0; +} + +void os_set_print_function(os_print_function_t pf) +{ + print_function = pf; +} + +int os_printf(const char *message, ...) +{ + if (print_function != NULL) { + char msg[FIXED_BUFFER_SIZE] = { '\0' }; + va_list ap; + va_start(ap, message); + vsnprintf(msg, FIXED_BUFFER_SIZE, message, ap); + va_end(ap); + print_function(msg); + } + + return 0; +} + +int os_vprintf(const char * format, va_list arg) +{ + if (print_function != NULL) { + char msg[FIXED_BUFFER_SIZE] = { '\0' }; + vsnprintf(msg, FIXED_BUFFER_SIZE, format, arg); + print_function(msg); + } + + return 0; +} + +char *strcpy(char *dest, const char *src) +{ + const unsigned char *s = src; + unsigned char *d = dest; + + while ((*d++ = *s++)); + return dest; +} + +void* os_mmap(void *hint, size_t size, int prot, int flags) +{ + int mprot = 0; + uint64 aligned_size, page_size; + void* ret = NULL; + sgx_status_t st = 0; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + + if (aligned_size >= UINT32_MAX) + return NULL; + + ret = sgx_alloc_rsrv_mem(aligned_size); + if (ret == NULL) { + os_printf("os_mmap(size=%u, aligned size=%lu, prot=0x%x) failed.", + size, aligned_size, prot); + return NULL; + } + + if (prot & MMAP_PROT_READ) + mprot |= SGX_PROT_READ; + if (prot & MMAP_PROT_WRITE) + mprot |= SGX_PROT_WRITE; + if (prot & MMAP_PROT_EXEC) + mprot |= SGX_PROT_EXEC; + + st = sgx_tprotect_rsrv_mem(ret, aligned_size, mprot); + if (st != SGX_SUCCESS) { + os_printf("os_mmap(size=%u, prot=0x%x) failed to set protect.", + size, prot); + sgx_free_rsrv_mem(ret, aligned_size); + return NULL; + } + + return ret; +} + +void os_munmap(void *addr, size_t size) +{ + uint64 aligned_size, page_size; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + sgx_free_rsrv_mem(addr, aligned_size); +} + +int os_mprotect(void *addr, size_t size, int prot) +{ + int mprot = 0; + sgx_status_t st = 0; + uint64 aligned_size, page_size; + + page_size = getpagesize(); + aligned_size = (size + page_size - 1) & ~(page_size - 1); + + if (prot & MMAP_PROT_READ) + mprot |= SGX_PROT_READ; + if (prot & MMAP_PROT_WRITE) + mprot |= SGX_PROT_WRITE; + if (prot & MMAP_PROT_EXEC) + mprot |= SGX_PROT_EXEC; + st = sgx_tprotect_rsrv_mem(addr, aligned_size, mprot); + if (st != SGX_SUCCESS) + os_printf("os_mprotect(addr=0x%"PRIx64", size=%u, prot=0x%x) failed.", + (uintptr_t)addr, size, prot); + + return (st == SGX_SUCCESS? 0:-1); +} + +void +os_dcache_flush(void) +{ +} + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_pthread.c b/wamr/core/shared/platform/linux-sgx/sgx_pthread.c new file mode 100644 index 0000000..b25368a --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_pthread.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "sgx_pthread.h" +#include "sgx_error.h" + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +int ocall_pthread_rwlock_init(int *p_ret, void **rwlock, void *attr); + +int ocall_pthread_rwlock_destroy(int *p_ret, void **rwlock); + +int ocall_pthread_rwlock_rdlock(int *p_ret, void **rwlock); + +int ocall_pthread_rwlock_wrlock(int *p_ret, void **rwlock); + +int ocall_pthread_rwlock_unlock(int *p_ret, void **rwlock); + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, void *attr) +{ + int ret = -1; + + if (ocall_pthread_rwlock_init(&ret, (void **)rwlock, NULL) + != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + (void)attr; + return ret; +} + +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_destroy(&ret, (void *)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_rdlock(&ret, (void*)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_wrlock(&ret, (void*)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + int ret = -1; + + if (ocall_pthread_rwlock_unlock(&ret, (void*)*rwlock) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + } + return ret; +} + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_pthread.h b/wamr/core/shared/platform/linux-sgx/sgx_pthread.h new file mode 100644 index 0000000..550cbf8 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_pthread.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_PTHREAD_H +#define _SGX_PTHREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uintptr_t pthread_rwlock_t; + +int pthread_rwlock_init(pthread_rwlock_t *rwlock, void *attr); +int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); + +int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); +int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); +int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_PTHREAD_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h b/wamr/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h new file mode 100644 index 0000000..b32a68b --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_rsrv_mem_mngr.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * This file is copied from https://github.com/intel/linux-sgx/blob/4589daddd58bec7367a6a9de3fe301e6de17671a/common/inc/internal/sgx_rsrv_mem_mngr.h + * The reason we copied here is that the official SGX SDK release has + * not included this header file yet. + */ + +#pragma once + +#ifndef _SGX_RSRV_MEM_MNGR_H_ +#define _SGX_RSRV_MEM_MNGR_H_ + +#include "stdint.h" +#include "sgx_error.h" + +#define SGX_PROT_READ 0x1 /* page can be read */ +#define SGX_PROT_WRITE 0x2 /* page can be written */ +#define SGX_PROT_EXEC 0x4 /* page can be executed */ +#define SGX_PROT_NONE 0x0 /* page can not be accessed */ + +#ifdef __cplusplus +extern "C" { +#endif + + /* Allocate a range of EPC memory from the reserved memory area with RW permission + * + * Parameters: + * Inputs: length [in]: Size of region to be allocated in bytes. Page aligned + * Return: Starting address of the new allocated memory area on success; otherwise NULL + */ + void * sgx_alloc_rsrv_mem(size_t length); + + + /* Free a range of EPC memory from the reserved memory area + * + * Parameters: + * Inputs: addr[in]: Starting address of region to be freed. Page aligned. + * length[in]: The length of the memory to be freed in bytes. Page aligned + * Return: 0 on success; otherwise -1 + */ + int sgx_free_rsrv_mem(void * addr, size_t length); + + + /* Modify the access permissions of the pages in the reserved memory area. + * + * Parameters: + * Inputs: addr[in]: Starting address of region which needs to change access permission. Page aligned. + * length[in]: The length of the memory to be manipulated in bytes. Page aligned. + * prot[in]: The target memory protection. + * Return: sgx_status_t - SGX_SUCCESS or failure as defined in sgx_error.h + */ + sgx_status_t sgx_tprotect_rsrv_mem(void *addr, size_t len, int prot); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_signal.c b/wamr/core/shared/platform/linux-sgx/sgx_signal.c new file mode 100644 index 0000000..e2487de --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_signal.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +int ocall_raise(int *p_ret, int sig); + +int raise(int sig) +{ + int ret; + + if (ocall_raise(&ret, sig) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_signal.h b/wamr/core/shared/platform/linux-sgx/sgx_signal.h new file mode 100644 index 0000000..b83a923 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_signal.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_SIGNAL_H +#define _SGX_SIGNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Signals. */ +#define SIGHUP 1 /* Hangup (POSIX). */ +#define SIGINT 2 /* Interrupt (ANSI). */ +#define SIGQUIT 3 /* Quit (POSIX). */ +#define SIGILL 4 /* Illegal instruction (ANSI). */ +#define SIGTRAP 5 /* Trace trap (POSIX). */ +#define SIGABRT 6 /* Abort (ANSI). */ +#define SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define SIGBUS 7 /* BUS error (4.2 BSD). */ +#define SIGFPE 8 /* Floating-point exception (ANSI). */ +#define SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define SIGPIPE 13 /* Broken pipe (POSIX). */ +#define SIGALRM 14 /* Alarm clock (POSIX). */ +#define SIGTERM 15 /* Termination (ANSI). */ +#define SIGSTKFLT 16 /* Stack fault. */ +#define SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define SIGCHLD 17 /* Child status has changed (POSIX). */ +#define SIGCONT 18 /* Continue (POSIX). */ +#define SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define SIGTTIN 21 /* Background read from tty (POSIX). */ +#define SIGTTOU 22 /* Background write to tty (POSIX). */ +#define SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define SIGPWR 30 /* Power failure restart (System V). */ +#define SIGSYS 31 /* Bad system call. */ +#define SIGUNUSED 31 + +int raise(int sig); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_SIGNAL_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_socket.c b/wamr/core/shared/platform/linux-sgx/sgx_socket.c new file mode 100644 index 0000000..8eb545d --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_socket.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + + +int ocall_socket(int *p_ret, int domain, int type, int protocol); +int ocall_getsockopt(int *p_ret, int sockfd, int level, int optname, + void *val_buf, unsigned int val_buf_size, + void *len_buf); + +int ocall_sendmsg(ssize_t *p_ret, int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags); +int ocall_recvmsg(ssize_t *p_ret, int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags); +int ocall_shutdown(int *p_ret, int sockfd, int how); + +int socket(int domain, int type, int protocol) +{ + int ret; + + if (ocall_socket(&ret, domain, type, protocol) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int getsockopt(int sockfd, int level, int optname, + void *optval, socklen_t *optlen) +{ + int ret; + unsigned int val_buf_size = *optlen; + + if (ocall_getsockopt(&ret, sockfd, level, optname, optval, + val_buf_size, (void *)optlen) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) +{ + ssize_t ret; + int i; + char *p; + struct msghdr *msg1; + + uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + + (uint64)msg->msg_controllen; + + total_size += sizeof(struct iovec) * (msg->msg_iovlen); + + for (i = 0; i < msg->msg_iovlen; i++) { + total_size += msg->msg_iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + msg1 = BH_MALLOC((uint32)total_size); + + if (msg1 == NULL) + return -1; + + p = (char*)(uintptr_t)sizeof(struct msghdr); + + if (msg->msg_name != NULL) { + msg1->msg_name = p; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_name, + (size_t)msg->msg_namelen); + p += msg->msg_namelen; + } + + if (msg->msg_control != NULL) { + msg1->msg_control = p; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_control, + (size_t)msg->msg_control); + p += msg->msg_controllen; + } + + if (msg->msg_iov != NULL) { + msg1->msg_iov = (struct iovec *)p; + p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); + + for (i = 0; i < msg->msg_iovlen; i++) { + msg1->msg_iov[i].iov_base = p; + msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; + memcpy((uintptr_t)p + (char *)msg1, msg->msg_iov[i].iov_base, + (size_t)(msg->msg_iov[i].iov_len)); + p += msg->msg_iov[i].iov_len; + } + } + + if (ocall_sendmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, + flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) +{ + ssize_t ret; + int i; + char *p; + struct msghdr *msg1; + + uint64 total_size = sizeof(struct msghdr) + (uint64)msg->msg_namelen + + (uint64)msg->msg_controllen; + + total_size += sizeof(struct iovec) * (msg->msg_iovlen); + + for (i = 0; i < msg->msg_iovlen; i++) { + total_size += msg->msg_iov[i].iov_len; + } + + if (total_size >= UINT32_MAX) + return -1; + + msg1 = BH_MALLOC((uint32)total_size); + + if (msg1 == NULL) + return -1; + + memset(msg1, 0, total_size); + + p = (char*)(uintptr_t)sizeof(struct msghdr); + + if (msg->msg_name != NULL) { + msg1->msg_name = p; + p += msg->msg_namelen; + } + + if (msg->msg_control != NULL) { + msg1->msg_control = p; + p += msg->msg_controllen; + } + + if (msg->msg_iov != NULL) { + msg1->msg_iov = (struct iovec *)p; + p += (uintptr_t)(sizeof(struct iovec) * (msg->msg_iovlen)); + + for (i = 0; i < msg->msg_iovlen; i++) { + msg1->msg_iov[i].iov_base = p; + msg1->msg_iov[i].iov_len = msg->msg_iov[i].iov_len; + p += msg->msg_iov[i].iov_len; + } + } + + if (ocall_recvmsg(&ret, sockfd, (void *)msg1, (uint32)total_size, + flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + p = (char *)(uintptr_t)(sizeof(struct msghdr)); + + if (msg1->msg_name != NULL) { + memcpy(msg->msg_name, (uintptr_t)p + (char *)msg1, + (size_t)msg1->msg_namelen); + p += msg1->msg_namelen; + } + + if (msg1->msg_control != NULL) { + memcpy(msg->msg_control, (uintptr_t)p + (char *)msg1, + (size_t)msg1->msg_control); + p += msg->msg_controllen; + } + + if (msg1->msg_iov != NULL) { + p += (uintptr_t)(sizeof(struct iovec) * (msg1->msg_iovlen)); + + for (i = 0; i < msg1->msg_iovlen; i++) { + memcpy(msg->msg_iov[i].iov_base, (uintptr_t)p + (char *)msg1, + (size_t)(msg1->msg_iov[i].iov_len)); + p += msg1->msg_iov[i].iov_len; + } + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int shutdown(int sockfd, int how) +{ + int ret; + + if (ocall_shutdown(&ret, sockfd, how) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} diff --git a/wamr/core/shared/platform/linux-sgx/sgx_socket.h b/wamr/core/shared/platform/linux-sgx/sgx_socket.h new file mode 100644 index 0000000..c070354 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_socket.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_SOCKET_H +#define _SGX_SOCKET_H + +#include "sgx_file.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SOL_SOCKET 1 + +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 + +#define SO_TYPE 3 + +#define MSG_OOB 0x0001 +#define MSG_PEEK 0x0002 +#define MSG_DONTROUTE 0x0004 +#define MSG_CTRUNC 0x0008 +#define MSG_PROXY 0x0010 +#define MSG_TRUNC 0x0020 +#define MSG_DONTWAIT 0x0040 +#define MSG_EOR 0x0080 +#define MSG_WAITALL 0x0100 +#define MSG_FIN 0x0200 +#define MSG_SYN 0x0400 +#define MSG_CONFIRM 0x0800 +#define MSG_RST 0x1000 +#define MSG_ERRQUEUE 0x2000 +#define MSG_NOSIGNAL 0x4000 +#define MSG_MORE 0x8000 +#define MSG_WAITFORONE 0x10000 +#define MSG_BATCH 0x40000 +#define MSG_FASTOPEN 0x20000000 +#define MSG_CMSG_CLOEXEC 0x40000000 + +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 + +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + void *msg_control; + socklen_t msg_controllen; + int msg_flags; +}; + + +int socket(int domain, int type, int protocol); + +int getsockopt(int sockfd, int level, int optname, + void *optval, socklen_t *optlen); + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); + +ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); + +int shutdown(int sockfd, int how); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_SOCKET_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_thread.c b/wamr/core/shared/platform/linux-sgx/sgx_thread.c new file mode 100644 index 0000000..5d102e1 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_thread.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +typedef struct { + thread_start_routine_t start; + void *arg; +} thread_wrapper_arg; + +static void *os_thread_wrapper(void *arg) +{ + thread_wrapper_arg * targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; + os_printf("THREAD CREATED %p\n", &targ); + BH_FREE(targ); + start_func(thread_arg); + return NULL; +} + +int os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + thread_wrapper_arg *targ; + + assert(tid); + assert(start); + + targ = (thread_wrapper_arg *) BH_MALLOC(sizeof(*targ)); + if (!targ) { + return BHT_ERROR; + } + + targ->start = start; + targ->arg = arg; + + if (pthread_create(tid, NULL, os_thread_wrapper, targ) != 0) { + BH_FREE(targ); + return BHT_ERROR; + } + + return BHT_OK; +} + +int os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +korp_tid os_self_thread() +{ + return pthread_self(); +} + +int os_mutex_init(korp_mutex *mutex) +{ + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + *mutex = m; + return BHT_OK; +} + +int os_mutex_destroy(korp_mutex *mutex) +{ + pthread_mutex_destroy(mutex); + return BHT_OK; +} + +int os_mutex_lock(korp_mutex *mutex) +{ + return pthread_mutex_lock(mutex); +} + +int os_mutex_unlock(korp_mutex *mutex) +{ + return pthread_mutex_unlock(mutex); +} + +int os_cond_init(korp_cond *cond) +{ + pthread_cond_t c = PTHREAD_COND_INITIALIZER; + *cond = c; + return BHT_OK; +} + +int os_cond_destroy(korp_cond *cond) +{ + pthread_cond_destroy(cond); + return BHT_OK; +} + +int os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + assert(cond); + assert(mutex); + + if (pthread_cond_wait(cond, mutex) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds) +{ + os_printf("warning: SGX pthread_cond_timedwait isn't supported, " + "calling pthread_cond_wait instead!\n"); + return BHT_ERROR; +} + +int os_cond_signal(korp_cond *cond) +{ + assert(cond); + + if (pthread_cond_signal(cond) != BHT_OK) + return BHT_ERROR; + + return BHT_OK; +} + +int os_thread_join(korp_tid thread, void **value_ptr) +{ + return pthread_join(thread, value_ptr); +} + +int os_thread_detach(korp_tid thread) +{ + /* SGX pthread_detach isn't provided, return directly. */ + return 0; +} + +void os_thread_exit(void *retval) +{ + return pthread_exit(retval); +} + +uint8 *os_thread_get_stack_boundary() +{ + /* TODO: get sgx stack boundary */ + return NULL; +} + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_time.c b/wamr/core/shared/platform/linux-sgx/sgx_time.c new file mode 100644 index 0000000..40239d5 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_time.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +#define TRACE_FUNC() os_printf("undefined %s\n", __FUNCTION__) +#define TRACE_OCALL_FAIL() os_printf("ocall %s failed!\n", __FUNCTION__) + +int ocall_clock_gettime(int *p_ret, unsigned clock_id, + void *tp_buf, unsigned int tp_buf_size); +int ocall_clock_getres(int *p_ret, int clock_id, + void *res_buf, unsigned int res_buf_size); +int ocall_utimensat(int *p_ret, int dirfd, const char *pathname, + const void *times_buf, unsigned int times_buf_size, + int flags); +int ocall_futimens(int *p_ret, int fd, + const void *times_buf, unsigned int times_buf_size); +int ocall_clock_nanosleep(int *p_ret, unsigned clock_id, int flags, + const void *req_buf, unsigned int req_buf_size, + const void *rem_buf, unsigned int rem_buf_size); + +uint64 +os_time_get_boot_microsecond() +{ + /* TODO */ + return 0; +} + +int clock_getres(int clock_id, struct timespec *res) +{ + int ret; + + if (ocall_clock_getres(&ret, clock_id, (void *)res, + sizeof(struct timespec)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + int ret; + + if(ocall_clock_gettime(&ret, clock_id, (void *)tp, + sizeof(struct timespec)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int utimensat(int dirfd, const char *pathname, + const struct timespec times[2], int flags) +{ + int ret; + + if (ocall_utimensat(&ret, dirfd, pathname, (void *)times, + sizeof(struct timespec) * 2, + flags) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int futimens(int fd, const struct timespec times[2]) +{ + int ret; + + if (ocall_futimens(&ret, fd, (void *)times, + sizeof(struct timespec) * 2) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} + +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain) +{ + int ret; + + if (ocall_clock_nanosleep(&ret, clock_id, flags, (void *)request, + sizeof(struct timespec), + (void *)remain, + sizeof(struct timespec)) != SGX_SUCCESS) { + TRACE_OCALL_FAIL(); + return -1; + } + + if (ret == -1) + errno = get_errno(); + + return ret; +} diff --git a/wamr/core/shared/platform/linux-sgx/sgx_time.h b/wamr/core/shared/platform/linux-sgx/sgx_time.h new file mode 100644 index 0000000..2aa83b8 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_time.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _SGX_TIME_H +#define _SGX_TIME_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +#define CLOCK_PROCESS_CPUTIME_ID 2 +#define CLOCK_THREAD_CPUTIME_ID 3 + +#define UTIME_NOW 0x3fffffff +#define UTIME_OMIT 0x3ffffffe +#define TIMER_ABSTIME 1 + +typedef long int time_t; + +typedef int clockid_t; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +int clock_getres(int clock_id, struct timespec *res); + +int clock_gettime(clockid_t clock_id, struct timespec *tp); + +int utimensat(int dirfd, const char *pathname, + const struct timespec times[2], int flags); +int futimens(int fd, const struct timespec times[2]); +int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _SGX_TIME_H */ + diff --git a/wamr/core/shared/platform/linux-sgx/sgx_wamr.edl b/wamr/core/shared/platform/linux-sgx/sgx_wamr.edl new file mode 100644 index 0000000..6eb1717 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/sgx_wamr.edl @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +enclave { + include "stdint.h" + include "stdbool.h" + include "unistd.h" + + untrusted { + int ocall_open([in, string]const char *pathname, int flags, + bool has_mode, unsigned mode); + int ocall_openat(int dirfd, + [in, string]const char *pathname, int flags, + bool has_mode, unsigned mode); + int ocall_close(int fd); + ssize_t ocall_read(int fd, [out, size=read_size]void *buf, + size_t read_size); + off_t ocall_lseek(int fd, off_t offset, int whence); + int ocall_ftruncate(int fd, off_t length); + int ocall_fsync(int fd); + int ocall_fdatasync(int fd); + int ocall_isatty(int fd); + void ocall_fdopendir(int fd, [out]void **p_dirp); + /* implementation related to multiple thread */ + void *ocall_readdir([user_check]void *dirp); + void ocall_rewinddir([user_check]void *dirp); + void ocall_seekdir([user_check]void *dirp, long loc); + long ocall_telldir([user_check]void *dirp); + int ocall_closedir([user_check]void *dirp); + + int ocall_fstat(int fd, [out, size=buf_len]void *buf, + unsigned int buf_len); + int ocall_fstatat(int dirfd, [in, string]const char *pathname, + [out, size=buf_len]void *buf, + unsigned int buf_len, int flags); + + int ocall_mkdirat(int dirfd, [in, string]const char *pathname, + unsigned mode); + int ocall_link([in, string] const char *oldpath, + [in, string] const char *newpath); + int ocall_linkat(int olddirfd, [in, string]const char *oldpath, + int newdirfd, [in, string]const char *newpath, + int flags); + int ocall_unlinkat(int dirfd, [in, string]const char *pathname, + int flags); + ssize_t ocall_readlinkat(int dirfd, + [in, string]const char *pathname, + [out, size=bufsiz]char *buf, + size_t bufsiz); + int ocall_renameat(int olddirfd, + [in, string]const char *oldpath, + int newdirfd, + [in, string]const char *newpath); + int ocall_symlinkat([in ,string]const char *target, + int newdirfd, + [in, string]const char *linkpath); + + int ocall_ioctl(int fd, unsigned long request, + [out, size=arg_len]void *arg, + unsigned int arg_len); + int ocall_fcntl(int fd, int cmd); + int ocall_fcntl_long(int fd, int cmd, long arg); + + int ocall_realpath([in, string]const char *path, + [out, size=buf_len]char *buf, + unsigned int buf_len); + int ocall_posix_fallocate(int fd, off_t offset, off_t len); + int ocall_poll([out, size=fds_len]void *fds, unsigned nfds, + int timeout, unsigned int fds_len); + + int ocall_getopt(int argc, + [in, size=argv_buf_len]char *argv_buf, + unsigned int argv_buf_len, + [in, string]const char *optstring); + ssize_t ocall_getrandom([out, size=buflen]void *buf, size_t buflen, + unsigned int flags); + ssize_t ocall_readv(int fd, + [in, out, size=buf_size]char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); + ssize_t ocall_writev(int fd, + [in, size=buf_size]char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset); + + /* time clock */ + int ocall_clock_gettime(unsigned clock_id, + [out, size=tp_buf_size]void *tp_buf, + unsigned int tp_buf_size); + int ocall_clock_getres(int clock_id, + [out, size=res_buf_size]void *res_buf, + unsigned int res_buf_size); + int ocall_utimensat(int dirfd, [in, string]const char *pathname, + [in, size=times_buf_size]const void *times_buf, + unsigned int times_buf_size, int flags); + int ocall_futimens(int fd, [in, size=times_buf_size]const void *times_buf, + unsigned int times_buf_size); + int ocall_clock_nanosleep(unsigned clock_id, int flags, + [in, size=req_buf_size]const void *req_buf, + unsigned int req_buf_size, + [out, size=rem_buf_size]void *rem_buf, + unsigned int rem_buf_size); + + int ocall_raise(int sig); + + int ocall_sched_yield(); + + int ocall_pthread_rwlock_init([out]void **rwlock, [user_check]void *attr); + int ocall_pthread_rwlock_destroy([user_check]void *rwlock); + int ocall_pthread_rwlock_rdlock([user_check]void *rwlock); + int ocall_pthread_rwlock_wrlock([user_check]void *rwlock); + int ocall_pthread_rwlock_unlock([user_check]void *rwlock); + + int ocall_get_errno(); + int ocall_socket(int domain, int type, int protocol); + int ocall_getsockopt(int sockfd, int level, int optname, + [out, size=val_buf_size]void *val_buf, + unsigned int val_buf_size, + [in, out, size=4]void *len_buf); + ssize_t ocall_sendmsg(int sockfd, + [in, size=msg_buf_size]void *msg_buf, + unsigned int msg_buf_size, + int flags); + ssize_t ocall_recvmsg(int sockfd, + [in, out, size=msg_buf_size]void *msg_buf, + unsigned int msg_buf_size, + int flags); + int ocall_shutdown(int sockfd, int how); + }; +}; diff --git a/wamr/core/shared/platform/linux-sgx/shared_platform.cmake b/wamr/core/shared/platform/linux-sgx/shared_platform.cmake new file mode 100644 index 0000000..58862b5 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/shared_platform.cmake @@ -0,0 +1,30 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_LINUX_SGX) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +if ("$ENV{SGX_SDK}" STREQUAL "") + set (SGX_SDK_DIR "/opt/intel/sgxsdk") +else() + set (SGX_SDK_DIR $ENV{SGX_SDK}) +endif() + +include_directories (${SGX_SDK_DIR}/include) +if (NOT BUILD_UNTRUST_PART EQUAL 1) + include_directories (${SGX_SDK_DIR}/include/tlibc + ${SGX_SDK_DIR}/include/libcxx) +endif () + +file (GLOB source_all ${PLATFORM_SHARED_DIR}/*.c) + +file (GLOB source_all_untrusted ${PLATFORM_SHARED_DIR}/untrusted/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all}) + +set (PLATFORM_SHARED_SOURCE_UNTRUSTED ${source_all_untrusted}) + diff --git a/wamr/core/shared/platform/linux-sgx/untrusted/file.c b/wamr/core/shared/platform/linux-sgx/untrusted/file.c new file mode 100644 index 0000000..d19c8ed --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/untrusted/file.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int ocall_open(const char *pathname, int flags, + bool has_mode, unsigned mode) +{ + if (has_mode) { + return open(pathname, flags, (mode_t)mode); + } + else { + return open(pathname, flags); + } +} + +int ocall_openat(int dirfd, + const char *pathname, int flags, + bool has_mode, unsigned mode) +{ + if (has_mode) { + return openat(dirfd, pathname, flags, (mode_t)mode); + } + else { + return openat(dirfd, pathname, flags); + } +} + +int ocall_close(int fd) +{ + return close(fd); +} + +ssize_t ocall_read(int fd, void *buf, size_t read_size) +{ + if (buf != NULL) { + return read(fd, buf, read_size); + } + else { + return -1; + } +} + +off_t ocall_lseek(int fd, off_t offset, int whence) +{ + return lseek(fd, offset, whence); +} + +int ocall_ftruncate(int fd, off_t length) +{ + return ftruncate(fd, length); +} + +int ocall_fsync(int fd) +{ + return fsync(fd); +} + +int ocall_fdatasync(int fd) +{ + return fdatasync(fd); +} + +int ocall_isatty(int fd) +{ + return isatty(fd); +} + +void ocall_fdopendir(int fd, void **dirp) +{ + if (dirp) { + *(DIR **)dirp = fdopendir(fd); + } +} + +void *ocall_readdir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + return readdir(p_dirp); +} + +void ocall_rewinddir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + rewinddir(p_dirp); + } +} + +void ocall_seekdir(void *dirp, long loc) +{ + DIR *p_dirp = (DIR *)dirp; + + if (p_dirp) { + seekdir(p_dirp, loc); + } +} + +long ocall_telldir(void *dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + return telldir(p_dirp); + } + return -1; +} + +int ocall_closedir(void* dirp) +{ + DIR *p_dirp = (DIR *)dirp; + if (p_dirp) { + return closedir(p_dirp); + } + return -1; +} + +int ocall_fstat(int fd, void *buf, unsigned int buf_len) +{ + return fstat(fd, (struct stat *)buf); +} + +int ocall_fstatat(int dirfd, const char *pathname, void *buf, + unsigned int buf_len, int flags) +{ + return fstatat(dirfd, pathname, (struct stat *)buf, flags); +} + +int ocall_mkdirat(int dirfd, const char *pathname, unsigned mode) +{ + return mkdirat(dirfd, pathname, (mode_t)mode); +} + +int ocall_link(const char *oldpath, const char *newpath) +{ + return link(oldpath, newpath); +} + +int ocall_linkat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, int flags) +{ + return linkat(olddirfd, oldpath, newdirfd, newpath, flags); +} + +int ocall_unlinkat(int dirfd, const char *pathname, int flags) +{ + return unlinkat(dirfd, pathname, flags); +} + +ssize_t ocall_readlinkat(int dirfd, const char *pathname, + char *buf, size_t bufsiz) +{ + return readlinkat(dirfd, pathname, buf, bufsiz); +} + +int ocall_renameat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath) +{ + return renameat(olddirfd, oldpath, newdirfd, newpath); +} + +int ocall_symlinkat(const char *target, int newdirfd, + const char *linkpath) +{ + return symlinkat(target, newdirfd, linkpath); +} + +int ocall_ioctl(int fd, unsigned long request, + void *arg, unsigned int arg_len) +{ + /* support just int *arg temporally */ + return ioctl(fd, request, (int *)arg); +} + +int ocall_fcntl(int fd, int cmd) +{ + return fcntl(fd, cmd); +} + +int ocall_fcntl_long(int fd, int cmd, long arg) +{ + return fcntl(fd, cmd, arg); +} + +ssize_t ocall_readv(int fd, char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset) +{ + struct iovec *iov = (struct iovec *)iov_buf; + ssize_t ret; + int i; + + for (i = 0; i < iovcnt; i++) { + iov[i].iov_base = iov_buf + (unsigned)(uintptr_t)iov[i].iov_base; + } + + if (has_offset) + ret = preadv(fd, iov, iovcnt, offset); + else + ret = readv(fd, iov, iovcnt); + + return ret; +} + +ssize_t ocall_writev(int fd, char *iov_buf, + unsigned int buf_size, int iovcnt, + bool has_offset, off_t offset) +{ + struct iovec *iov = (struct iovec *)iov_buf; + int i; + ssize_t ret; + + for (i = 0; i < iovcnt; i++) { + iov[i].iov_base = iov_buf + (unsigned)(uintptr_t)iov[i].iov_base; + } + + if (has_offset) + ret = pwritev(fd, iov, iovcnt, offset); + else + ret = writev(fd, iov, iovcnt); + + return ret; +} + +int ocall_realpath(const char *path, char *buf, unsigned int buf_len) +{ + char* val = NULL; + val = realpath(path, buf); + if (val != NULL) { + return 0; + } + return -1; +} + +int ocall_posix_fallocate(int fd, off_t offset, off_t len) +{ + return posix_fallocate(fd, offset, len); +} + +int ocall_poll(void *fds, unsigned nfds, int timeout, + unsigned int fds_len) +{ + return poll((struct pollfd *)fds, (nfds_t)nfds, timeout); +} + +int ocall_getopt(int argc, char *argv_buf, unsigned int argv_buf_len, + const char *optstring) +{ + int ret; + int i; + char **argv = (char **)argv_buf; + + for (i=0; i < argc; i++) { + argv[i] = argv_buf + (uintptr_t)argv[i]; + } + + return getopt(argc, argv, optstring); +} + +ssize_t ocall_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + return getrandom(buf, buflen, flags); +} + +int ocall_sched_yield() +{ + return sched_yield(); +} + +int ocall_get_errno() +{ + return errno; +} diff --git a/wamr/core/shared/platform/linux-sgx/untrusted/pthread.c b/wamr/core/shared/platform/linux-sgx/untrusted/pthread.c new file mode 100644 index 0000000..f302687 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/untrusted/pthread.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int ocall_pthread_rwlock_init(void **rwlock, void *attr) +{ + int ret = 0; + + *rwlock = malloc(sizeof(pthread_rwlock_t)); + if (*rwlock == NULL) + return -1; + + ret = pthread_rwlock_init((pthread_rwlock_t *)*rwlock, NULL); + if (ret != 0) { + free(*rwlock); + *rwlock = NULL; + } + (void)attr; + return ret; +} + +int ocall_pthread_rwlock_destroy(void *rwlock) +{ + pthread_rwlock_t *lock = (pthread_rwlock_t *)rwlock; + int ret; + + ret = pthread_rwlock_destroy(lock); + free(lock); + return ret; +} + +int ocall_pthread_rwlock_rdlock(void *rwlock) +{ + return pthread_rwlock_rdlock((pthread_rwlock_t *)rwlock); +} + +int ocall_pthread_rwlock_wrlock(void *rwlock) +{ + return pthread_rwlock_wrlock((pthread_rwlock_t *)rwlock); +} + +int ocall_pthread_rwlock_unlock(void *rwlock) +{ + return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock); +} + diff --git a/wamr/core/shared/platform/linux-sgx/untrusted/signal.c b/wamr/core/shared/platform/linux-sgx/untrusted/signal.c new file mode 100644 index 0000000..115fbfa --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/untrusted/signal.c @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include + +int ocall_raise(int sig) +{ + return raise(sig); +} \ No newline at end of file diff --git a/wamr/core/shared/platform/linux-sgx/untrusted/socket.c b/wamr/core/shared/platform/linux-sgx/untrusted/socket.c new file mode 100644 index 0000000..34b5d46 --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/untrusted/socket.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include + +int ocall_socket(int domain, int type, int protocol) +{ + return socket(domain, type, protocol); +} + +int ocall_getsockopt(int sockfd, int level, int optname, void *val_buf, + unsigned int val_buf_size, void *len_buf) +{ + return getsockopt(sockfd, level, optname, val_buf, + (socklen_t *)len_buf); +} + +ssize_t ocall_sendmsg(int sockfd, void *msg_buf, + unsigned int msg_buf_size, int flags) +{ + struct msghdr *msg = (struct msghdr *)msg_buf; + int i; + ssize_t ret; + + if (msg->msg_name != NULL) + msg->msg_name = msg_buf + (unsigned)(uintptr_t)msg->msg_name; + + if (msg->msg_control != NULL) + msg->msg_control = msg_buf + (unsigned)(uintptr_t)msg->msg_control; + + if (msg->msg_iov != NULL) { + msg->msg_iov = msg_buf + (unsigned)(uintptr_t)msg->msg_iov; + for (i = 0; i < msg->msg_iovlen; i++) { + msg->msg_iov[i].iov_base = msg_buf + (unsigned)(uintptr_t) + msg->msg_iov[i].iov_base; + } + } + + return sendmsg(sockfd, msg, flags); +} + +ssize_t ocall_recvmsg(int sockfd, void *msg_buf, unsigned int msg_buf_size, + int flags) +{ + struct msghdr *msg = (struct msghdr *)msg_buf; + int i; + ssize_t ret; + + if (msg->msg_name != NULL) + msg->msg_name = msg_buf + (unsigned)(uintptr_t)msg->msg_name; + + if (msg->msg_control != NULL) + msg->msg_control = msg_buf + (unsigned)(uintptr_t)msg->msg_control; + + if (msg->msg_iov != NULL) { + msg->msg_iov = msg_buf + (unsigned)(uintptr_t)msg->msg_iov; + for (i = 0; i msg_iovlen; i++) { + msg->msg_iov[i].iov_base = msg_buf + (unsigned)(uintptr_t) + msg->msg_iov[i].iov_base; + } + } + + return recvmsg(sockfd, msg, flags); +} + +int ocall_shutdown(int sockfd, int how) +{ + return shutdown(sockfd, how); +} \ No newline at end of file diff --git a/wamr/core/shared/platform/linux-sgx/untrusted/time.c b/wamr/core/shared/platform/linux-sgx/untrusted/time.c new file mode 100644 index 0000000..1d15e8d --- /dev/null +++ b/wamr/core/shared/platform/linux-sgx/untrusted/time.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include +#include + + +/** time clock **/ +int ocall_clock_gettime(unsigned clock_id, void *tp_buf, + unsigned int tp_buf_size) +{ + return clock_gettime((clockid_t)clock_id, + (struct timespec *)tp_buf); +} + +int ocall_clock_getres(int clock_id, void *res_buf, + unsigned int res_buf_size) +{ + return clock_getres(clock_id, (struct timespec *)res_buf); +} + +int ocall_utimensat(int dirfd, const char *pathname, + const void *times_buf, + unsigned int times_buf_size, int flags) +{ + return utimensat(dirfd, pathname, (struct timespec *)times_buf, flags); + +} + +int ocall_futimens(int fd, const void *times_buf, + unsigned int times_buf_size) +{ + return futimens(fd, (struct timespec *)times_buf); +} + +int ocall_clock_nanosleep(unsigned clock_id, int flags, + const void *req_buf, unsigned int req_buf_size, + const void *rem_buf, unsigned int rem_buf_size) +{ + return clock_nanosleep((clockid_t)clock_id, flags, + (struct timespec *)req_buf, + (struct timespec *)rem_buf); + +} diff --git a/wamr/core/shared/platform/linux/platform_init.c b/wamr/core/shared/platform/linux/platform_init.c new file mode 100644 index 0000000..4f80608 --- /dev/null +++ b/wamr/core/shared/platform/linux/platform_init.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); + ret += vprintf(format, ap); + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ + return vprintf(format, ap); +} + diff --git a/wamr/core/shared/platform/linux/platform_internal.h b/wamr/core/shared/platform/linux/platform_internal.h new file mode 100644 index 0000000..7a15f85 --- /dev/null +++ b/wamr/core/shared/platform/linux/platform_internal.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_LINUX +#define BH_PLATFORM_LINUX +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +#define os_thread_local_attribute __thread + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +#define os_getpagesize getpagesize + +typedef void (*os_signal_handler)(void *sig_addr); + +int os_signal_init(os_signal_handler handler); + +void os_signal_destroy(); + +void os_signal_unmask(); + +void os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/linux/shared_platform.cmake b/wamr/core/shared/platform/linux/shared_platform.cmake new file mode 100644 index 0000000..9a87260 --- /dev/null +++ b/wamr/core/shared/platform/linux/shared_platform.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_LINUX) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/shared/platform/vxworks/platform_init.c b/wamr/core/shared/platform/vxworks/platform_init.c new file mode 100644 index 0000000..4f80608 --- /dev/null +++ b/wamr/core/shared/platform/vxworks/platform_init.c @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + +int +os_printf(const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start(ap, format); + ret += vprintf(format, ap); + va_end(ap); + + return ret; +} + +int +os_vprintf(const char *format, va_list ap) +{ + return vprintf(format, ap); +} + diff --git a/wamr/core/shared/platform/vxworks/platform_internal.h b/wamr/core/shared/platform/vxworks/platform_internal.h new file mode 100644 index 0000000..a847936 --- /dev/null +++ b/wamr/core/shared/platform/vxworks/platform_internal.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_VXWORKS +#define BH_PLATFORM_VXWORKS +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef pthread_t korp_tid; +typedef pthread_mutex_t korp_mutex; +typedef pthread_cond_t korp_cond; +typedef pthread_t korp_thread; + +#if WASM_DISABLE_HW_BOUND_CHECK == 0 +#if defined(BUILD_TARGET_X86_64) \ + || defined(BUILD_TARGET_AMD_64) \ + || defined(BUILD_TARGET_AARCH64) + +#include + +#define OS_ENABLE_HW_BOUND_CHECK + +#define os_thread_local_attribute __thread + +typedef jmp_buf korp_jmpbuf; + +#define os_setjmp setjmp +#define os_longjmp longjmp +#define os_alloca alloca + +#define os_getpagesize getpagesize + +typedef void (*os_signal_handler)(void *sig_addr); + +int os_signal_init(os_signal_handler handler); + +void os_signal_destroy(); + +void os_signal_unmask(); + +void os_sigreturn(); +#endif /* end of BUILD_TARGET_X86_64/AMD_64/AARCH64 */ +#endif /* end of WASM_DISABLE_HW_BOUND_CHECK */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/vxworks/shared_platform.cmake b/wamr/core/shared/platform/vxworks/shared_platform.cmake new file mode 100644 index 0000000..6979ce2 --- /dev/null +++ b/wamr/core/shared/platform/vxworks/shared_platform.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_VXWORKS) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/posix/platform_api_posix.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_POSIX_SOURCE}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/shared/platform/windows/platform_init.c b/wamr/core/shared/platform/windows/platform_init.c new file mode 100644 index 0000000..76594b8 --- /dev/null +++ b/wamr/core/shared/platform/windows/platform_init.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +int +bh_platform_init() +{ + return 0; +} + +void +bh_platform_destroy() +{ +} + diff --git a/wamr/core/shared/platform/windows/platform_internal.h b/wamr/core/shared/platform/windows/platform_internal.h new file mode 100644 index 0000000..65a933a --- /dev/null +++ b/wamr/core/shared/platform/windows/platform_internal.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BH_PLATFORM_WINDOWS +#define BH_PLATFORM_WINDOWS +#endif + +/* Stack size of applet threads's native part. */ +#define BH_APPLET_PRESERVED_STACK_SIZE (32 * 1024) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 0 + +typedef void *korp_tid; +typedef void *korp_mutex; +typedef void *korp_sem; +typedef void *korp_thread; + +typedef struct { + korp_sem s; + unsigned int waiting_count; +} korp_cond; + +#define os_printf printf +#define os_vprintf vprintf + +static inline size_t +getpagesize() +{ + SYSTEM_INFO S; + GetNativeSystemInfo(&S); + return S.dwPageSize; +} + +#ifdef __cplusplus +} +#endif + +#endif /* end of _PLATFORM_INTERNAL_H */ + diff --git a/wamr/core/shared/platform/windows/posix_malloc.c b/wamr/core/shared/platform/windows/posix_malloc.c new file mode 100644 index 0000000..e83fc7d --- /dev/null +++ b/wamr/core/shared/platform/windows/posix_malloc.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * +os_malloc(unsigned size) +{ + return malloc(size); +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return realloc(ptr, size); +} + +void +os_free(void *ptr) +{ + free(ptr); +} + + + diff --git a/wamr/core/shared/platform/windows/shared_platform.cmake b/wamr/core/shared/platform/windows/shared_platform.cmake new file mode 100644 index 0000000..6ab29c8 --- /dev/null +++ b/wamr/core/shared/platform/windows/shared_platform.cmake @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_WINDOWS) +add_definitions(-DHAVE_STRUCT_TIMESPEC) + + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all}) + +file (GLOB header ${PLATFORM_SHARED_DIR}/../include/*.h) +LIST (APPEND RUNTIME_LIB_HEADER_LIST ${header}) diff --git a/wamr/core/shared/platform/windows/win_memmap.c b/wamr/core/shared/platform/windows/win_memmap.c new file mode 100644 index 0000000..6bcf6b3 --- /dev/null +++ b/wamr/core/shared/platform/windows/win_memmap.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +void * os_mmap(void *hint, size_t size, int prot, int flags) +{ + DWORD AllocType = MEM_RESERVE | MEM_COMMIT; + DWORD flProtect = PAGE_NOACCESS; + size_t request_size, page_size; + void *addr; + + page_size = getpagesize(); + request_size = (size + page_size - 1) & ~(page_size - 1); + + if (request_size < size) + /* integer overflow */ + return NULL; + + if (prot & MMAP_PROT_EXEC) { + if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_EXECUTE_READ; + } + else if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_READWRITE; + else if (prot & MMAP_PROT_READ) + flProtect = PAGE_READONLY; + + + addr = VirtualAlloc((LPVOID)hint, request_size, AllocType, + flProtect); + return addr; +} + +void +os_munmap(void *addr, size_t size) +{ + size_t page_size = getpagesize(); + size_t request_size = (size + page_size - 1) & ~(page_size - 1); + + if (addr) { + if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { + if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { + os_printf("os_munmap error addr:%p, size:0x%lx, errno:%d\n", + addr, request_size, errno); + } + } + } +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + DWORD AllocType = MEM_RESERVE | MEM_COMMIT; + DWORD flProtect = PAGE_NOACCESS; + + if (!addr) + return 0; + + if (prot & MMAP_PROT_EXEC) { + if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_EXECUTE_READWRITE; + else + flProtect = PAGE_EXECUTE_READ; + } + else if (prot & MMAP_PROT_WRITE) + flProtect = PAGE_READWRITE; + else if (prot & MMAP_PROT_READ) + flProtect = PAGE_READONLY; + + return VirtualProtect((LPVOID)addr, size, flProtect, NULL); +} + +void +os_dcache_flush(void) +{ + +} diff --git a/wamr/core/shared/platform/windows/win_thread.c b/wamr/core/shared/platform/windows/win_thread.c new file mode 100644 index 0000000..896137e --- /dev/null +++ b/wamr/core/shared/platform/windows/win_thread.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +typedef struct { + thread_start_routine_t start; + void* stack; + uint32 stack_size; + void* arg; +} thread_wrapper_arg; + +static void *os_thread_wrapper(void *arg) +{ + thread_wrapper_arg * targ = arg; + thread_start_routine_t start_func = targ->start; + void *thread_arg = targ->arg; + os_printf("THREAD CREATED %p\n", &targ); + targ->stack = (void *)((uintptr_t)(&arg) & (uintptr_t)~0xfff); + BH_FREE(targ); + start_func(thread_arg); + return NULL; +} + +int os_thread_create_with_prio(korp_tid *tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + return BHT_ERROR; +} + +int os_thread_create(korp_tid *tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +korp_tid os_self_thread() +{ + return NULL; +} + +int os_mutex_init(korp_mutex *mutex) +{ + return BHT_OK; +} + +int os_recursive_mutex_init(korp_mutex *mutex) +{ + return BHT_OK; +} + +int os_mutex_destroy(korp_mutex *mutex) +{ + return BHT_OK; +} + +/* Returned error (EINVAL, EAGAIN and EDEADLK) from + locking the mutex indicates some logic error present in + the program somewhere. + Don't try to recover error for an existing unknown error.*/ +int os_mutex_lock(korp_mutex *mutex) +{ + return BHT_ERROR; +} + +/* Returned error (EINVAL, EAGAIN and EPERM) from + unlocking the mutex indicates some logic error present + in the program somewhere. + Don't try to recover error for an existing unknown error.*/ +int os_mutex_unlock(korp_mutex *mutex) +{ + return BHT_OK; +} + +int os_cond_init(korp_cond *cond) +{ + return BHT_OK; +} + +int os_cond_destroy(korp_cond *cond) +{ + return BHT_OK; +} + +int os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return BHT_OK; +} + + +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + /* Note: some broken versions only have 8 trailing zero's, + the correct epoch has 9 trailing zero's + This magic number is the number of 100 nanosecond intervals + since January 1, 1601 (UTC) until 00:00:00 January 1, 1970 */ + static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + + return 0; +} + +static void msec_nsec_to_abstime(struct timespec *ts, int usec) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + ts->tv_sec = (long int)(tv.tv_sec + usec / 1000000); + ts->tv_nsec = (long int)(tv.tv_usec * 1000 + (usec % 1000000) * 1000); + + if (ts->tv_nsec >= 1000000000L) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} + +int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds) +{ + return BHT_OK; +} + +int os_cond_signal(korp_cond *cond) +{ + return BHT_OK; +} + +int os_thread_join(korp_tid thread, void **value_ptr) +{ + return BHT_OK; +} + +int os_thread_detach(korp_tid thread) +{ + return BHT_OK; +} + +void os_thread_exit(void *retval) +{ + return BHT_OK; +} + +uint8 *os_thread_get_stack_boundary() +{ + return NULL; +} diff --git a/wamr/core/shared/platform/windows/win_time.c b/wamr/core/shared/platform/windows/win_time.c new file mode 100644 index 0000000..a48e419 --- /dev/null +++ b/wamr/core/shared/platform/windows/win_time.c @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_microsecond() +{ + struct timespec ts; + timespec_get(&ts, TIME_UTC); + + return ((uint64)ts.tv_sec) * 1000 * 1000 + ((uint64)ts.tv_nsec) / 1000; +} + diff --git a/wamr/core/shared/platform/zephyr/platform_internal.h b/wamr/core/shared/platform/zephyr/platform_internal.h new file mode 100644 index 0000000..c208bac --- /dev/null +++ b/wamr/core/shared/platform/zephyr/platform_internal.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _PLATFORM_INTERNAL_H +#define _PLATFORM_INTERNAL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef CONFIG_NET_BUF_USER_DATA_SIZE +#define CONFIG_NET_BUF_USER_DATA_SIZE 0 +#endif +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARM_MPU +#include +#endif + + +#ifndef BH_PLATFORM_ZEPHYR +#define BH_PLATFORM_ZEPHYR +#endif + +#define BH_APPLET_PRESERVED_STACK_SIZE (2 * BH_KB) + +/* Default thread priority */ +#define BH_THREAD_DEFAULT_PRIORITY 7 + +typedef struct k_thread korp_thread; +typedef korp_thread *korp_tid; +typedef struct k_mutex korp_mutex; + +struct os_thread_wait_node; +typedef struct os_thread_wait_node *os_thread_wait_list; +typedef struct korp_cond { + struct k_mutex wait_list_lock; + os_thread_wait_list thread_wait_list; +} korp_cond; + +#define os_printf printf + +/* math functions which are not provided by os */ +double sqrt(double x); +double floor(double x); +double ceil(double x); +double fmin(double x, double y); +double fmax(double x, double y); +double rint(double x); +double fabs(double x); +double trunc(double x); +float floorf(float x); +float ceilf(float x); +float fminf(float x, float y); +float fmaxf(float x, float y); +float rintf(float x); +float truncf(float x); +int signbit(double x); +int isnan(double x); + +unsigned long long int strtoull(const char *nptr, char **endptr, int base); +double strtod(const char *nptr, char **endptr); +float strtof(const char *nptr, char **endptr); + +/** + * @brief Allocate executable memroy + * + * @param size size of the memory to be allocated + * + * @return the address of the allocated memory if not NULL + */ +typedef void* (*exec_mem_alloc_func_t)(unsigned int size); + +/** + * @brief Release executable memroy + * + * @param the address of the executable memory to be released + */ +typedef void (*exec_mem_free_func_t)(void *addr); + +/* Below function are called by external project to set related function pointers that + * will be used to malloc/free executable memory. Otherwise default mechanise will be used. */ +void set_exec_mem_alloc_func(exec_mem_alloc_func_t alloc_func, exec_mem_free_func_t free_func); + +#endif diff --git a/wamr/core/shared/platform/zephyr/shared_platform.cmake b/wamr/core/shared/platform/zephyr/shared_platform.cmake new file mode 100644 index 0000000..9b043b5 --- /dev/null +++ b/wamr/core/shared/platform/zephyr/shared_platform.cmake @@ -0,0 +1,16 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (PLATFORM_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_definitions(-DBH_PLATFORM_ZEPHYR) + +include_directories(${PLATFORM_SHARED_DIR}) +include_directories(${PLATFORM_SHARED_DIR}/../include) + +include (${CMAKE_CURRENT_LIST_DIR}/../common/math/platform_api_math.cmake) + +file (GLOB_RECURSE source_all ${PLATFORM_SHARED_DIR}/*.c) + +set (PLATFORM_SHARED_SOURCE ${source_all} ${PLATFORM_COMMON_MATH_SOURCE}) + diff --git a/wamr/core/shared/platform/zephyr/zephyr_platform.c b/wamr/core/shared/platform/zephyr/zephyr_platform.c new file mode 100644 index 0000000..8c701a2 --- /dev/null +++ b/wamr/core/shared/platform/zephyr/zephyr_platform.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +/* function pointers for executable memory management */ +static exec_mem_alloc_func_t exec_mem_alloc_func = NULL; +static exec_mem_free_func_t exec_mem_free_func = NULL; + +#if WASM_ENABLE_AOT != 0 +#ifdef CONFIG_ARM_MPU +/** + * This function will allow execute from sram region. + * This is needed for AOT code because by default all soc will + * disable the execute from SRAM. + */ +static void +disable_mpu_rasr_xn(void) +{ + uint32 index; + /* Kept the max index as 8 (irrespective of soc) because the sram + would most likely be set at index 2. */ + for (index = 0U; index < 8; index++) { + MPU->RNR = index; + if (MPU->RASR & MPU_RASR_XN_Msk) { + MPU->RASR |= ~MPU_RASR_XN_Msk; + } + } + +} +#endif /* end of CONFIG_ARM_MPU */ +#endif + +static int +_stdout_hook_iwasm(int c) +{ + printk("%c", (char)c); + return 1; +} + +int +os_thread_sys_init(); + +void +os_thread_sys_destroy(); + +int +bh_platform_init() +{ + extern void __stdout_hook_install(int (*hook)(int)); + /* Enable printf() in Zephyr */ + __stdout_hook_install(_stdout_hook_iwasm); + +#if WASM_ENABLE_AOT != 0 +#ifdef CONFIG_ARM_MPU + /* Enable executable memory support */ + disable_mpu_rasr_xn(); +#endif +#endif + + return os_thread_sys_init(); +} + +void +bh_platform_destroy() +{ + os_thread_sys_destroy(); +} + +void * +os_malloc(unsigned size) +{ + return NULL; +} + +void * +os_realloc(void *ptr, unsigned size) +{ + return NULL; +} + +void +os_free(void *ptr) +{ +} + +struct out_context { + int count; +}; + +typedef int (*out_func_t)(int c, void *ctx); + +static int +char_out(int c, void *ctx) +{ + struct out_context *out_ctx = (struct out_context*)ctx; + out_ctx->count++; + return _stdout_hook_iwasm(c); +} + +int +os_vprintf(const char *fmt, va_list ap) +{ + struct out_context ctx = { 0 }; + z_vprintk(char_out, &ctx, fmt, ap); + return ctx.count; +} + +void * +os_mmap(void *hint, size_t size, int prot, int flags) +{ + if ((uint64)size >= UINT32_MAX) + return NULL; + if (exec_mem_alloc_func) + return exec_mem_alloc_func((uint32)size); + else + return BH_MALLOC(size); +} + +void +os_munmap(void *addr, size_t size) +{ + if (exec_mem_free_func) + exec_mem_free_func(addr); + else + BH_FREE(addr); +} + +int +os_mprotect(void *addr, size_t size, int prot) +{ + return 0; +} + +void +os_dcache_flush() +{ +#if defined(CONFIG_CPU_CORTEX_M7) && defined(CONFIG_ARM_MPU) + uint32 key; + key = irq_lock(); + SCB_CleanDCache(); + irq_unlock(key); +#endif +} + +void set_exec_mem_alloc_func(exec_mem_alloc_func_t alloc_func, + exec_mem_free_func_t free_func) +{ + exec_mem_alloc_func = alloc_func; + exec_mem_free_func = free_func; +} + diff --git a/wamr/core/shared/platform/zephyr/zephyr_thread.c b/wamr/core/shared/platform/zephyr/zephyr_thread.c new file mode 100644 index 0000000..ebcb37b --- /dev/null +++ b/wamr/core/shared/platform/zephyr/zephyr_thread.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" +#include "platform_api_extension.h" + +#define bh_assert(v) do { \ + if (!(v)) { \ + int _count; \ + printf("\nASSERTION FAILED: %s, at %s, line %d\n", \ + #v, __FILE__, __LINE__); \ + _count = printf(" "); \ + /* divived by 0 to make it abort */ \ + printf("%d\n", _count / (_count - 1)); \ + while (1); \ + } \ + } while (0) + +typedef struct os_thread_wait_node { + struct k_sem sem; + os_thread_wait_list next; +} os_thread_wait_node; + +typedef struct os_thread_data { + /* Next thread data */ + struct os_thread_data *next; + /* Zephyr thread handle */ + korp_tid tid; + /* Jeff thread local root */ + void *tlr; + /* Lock for waiting list */ + struct k_mutex wait_list_lock; + /* Waiting list of other threads who are joining this thread */ + os_thread_wait_list thread_wait_list; + /* Thread stack size */ + unsigned stack_size; + /* Thread stack */ + char stack[1]; +} os_thread_data; + +typedef struct os_thread_obj { + struct k_thread thread; + /* Whether the thread is terminated and this thread object is to + be freed in the future. */ + bool to_be_freed; + struct os_thread_obj *next; +} os_thread_obj; + +static bool is_thread_sys_inited = false; + +/* Thread data of supervisor thread */ +static os_thread_data supervisor_thread_data; + +/* Lock for thread data list */ +static struct k_mutex thread_data_lock; + +/* Thread data list */ +static os_thread_data *thread_data_list = NULL; + +/* Lock for thread object list */ +static struct k_mutex thread_obj_lock; + +/* Thread object list */ +static os_thread_obj *thread_obj_list = NULL; + +static void thread_data_list_add(os_thread_data *thread_data) +{ + k_mutex_lock(&thread_data_lock, K_FOREVER); + if (!thread_data_list) + thread_data_list = thread_data; + else { + /* If already in list, just return */ + os_thread_data *p = thread_data_list; + while (p) { + if (p == thread_data) { + k_mutex_unlock(&thread_data_lock); + return; + } + p = p->next; + } + + /* Set as head of list */ + thread_data->next = thread_data_list; + thread_data_list = thread_data; + } + k_mutex_unlock(&thread_data_lock); +} + +static void thread_data_list_remove(os_thread_data *thread_data) +{ + k_mutex_lock(&thread_data_lock, K_FOREVER); + if (thread_data_list) { + if (thread_data_list == thread_data) + thread_data_list = thread_data_list->next; + else { + /* Search and remove it from list */ + os_thread_data *p = thread_data_list; + while (p && p->next != thread_data) + p = p->next; + if (p && p->next == thread_data) + p->next = p->next->next; + } + } + k_mutex_unlock(&thread_data_lock); +} + +static os_thread_data * +thread_data_list_lookup(k_tid_t tid) +{ + k_mutex_lock(&thread_data_lock, K_FOREVER); + if (thread_data_list) { + os_thread_data *p = thread_data_list; + while (p) { + if (p->tid == tid) { + /* Found */ + k_mutex_unlock(&thread_data_lock); + return p; + } + p = p->next; + } + } + k_mutex_unlock(&thread_data_lock); + return NULL; +} + +static void thread_obj_list_add(os_thread_obj *thread_obj) +{ + k_mutex_lock(&thread_obj_lock, K_FOREVER); + if (!thread_obj_list) + thread_obj_list = thread_obj; + else { + /* Set as head of list */ + thread_obj->next = thread_obj_list; + thread_obj_list = thread_obj; + } + k_mutex_unlock(&thread_obj_lock); +} + +static void thread_obj_list_reclaim() +{ + os_thread_obj *p, *p_prev; + k_mutex_lock(&thread_obj_lock, K_FOREVER); + p_prev = NULL; + p = thread_obj_list; + while (p) { + if (p->to_be_freed) { + if (p_prev == NULL) { /* p is the head of list */ + thread_obj_list = p->next; + BH_FREE(p); + p = thread_obj_list; + } else { /* p is not the head of list */ + p_prev->next = p->next; + BH_FREE(p); + p = p_prev->next; + } + } else { + p_prev = p; + p = p->next; + } + } + k_mutex_unlock(&thread_obj_lock); +} + +int os_thread_sys_init() +{ + if (is_thread_sys_inited) + return BHT_OK; + + k_mutex_init(&thread_data_lock); + k_mutex_init(&thread_obj_lock); + + /* Initialize supervisor thread data */ + memset(&supervisor_thread_data, 0, sizeof(supervisor_thread_data)); + supervisor_thread_data.tid = k_current_get(); + /* Set as head of thread data list */ + thread_data_list = &supervisor_thread_data; + + is_thread_sys_inited = true; + return BHT_OK; +} + +void os_thread_sys_destroy(void) +{ + if (is_thread_sys_inited) { + is_thread_sys_inited = false; + } +} + +static os_thread_data * +thread_data_current() +{ + k_tid_t tid = k_current_get(); + return thread_data_list_lookup(tid); +} + +static void os_thread_cleanup(void) +{ + os_thread_data *thread_data = thread_data_current(); + + bh_assert(thread_data != NULL); + k_mutex_lock(&thread_data->wait_list_lock, K_FOREVER); + if (thread_data->thread_wait_list) { + /* Signal each joining thread */ + os_thread_wait_list head = thread_data->thread_wait_list; + while (head) { + os_thread_wait_list next = head->next; + k_sem_give(&head->sem); + /* head will be freed by joining thread */ + head = next; + } + thread_data->thread_wait_list = NULL; + } + k_mutex_unlock(&thread_data->wait_list_lock); + + thread_data_list_remove(thread_data); + /* Set flag to true for the next thread creating to + free the thread object */ + ((os_thread_obj*) thread_data->tid)->to_be_freed = true; + BH_FREE(thread_data); +} + +static void os_thread_wrapper(void *start, void *arg, void *thread_data) +{ + /* Set thread custom data */ + ((os_thread_data*) thread_data)->tid = k_current_get(); + thread_data_list_add(thread_data); + + ((thread_start_routine_t) start)(arg); + os_thread_cleanup(); +} + +int os_thread_create(korp_tid *p_tid, thread_start_routine_t start, void *arg, + unsigned int stack_size) +{ + return os_thread_create_with_prio(p_tid, start, arg, stack_size, + BH_THREAD_DEFAULT_PRIORITY); +} + +int os_thread_create_with_prio(korp_tid *p_tid, thread_start_routine_t start, + void *arg, unsigned int stack_size, int prio) +{ + korp_tid tid; + os_thread_data *thread_data; + unsigned thread_data_size; + + if (!p_tid || !stack_size) + return BHT_ERROR; + + /* Free the thread objects of terminated threads */ + thread_obj_list_reclaim(); + + /* Create and initialize thread object */ + if (!(tid = BH_MALLOC(sizeof(os_thread_obj)))) + return BHT_ERROR; + + memset(tid, 0, sizeof(os_thread_obj)); + + /* Create and initialize thread data */ + thread_data_size = offsetof(os_thread_data, stack) + stack_size; + if (!(thread_data = BH_MALLOC(thread_data_size))) { + BH_FREE(tid); + return BHT_ERROR; + } + + memset(thread_data, 0, thread_data_size); + k_mutex_init(&thread_data->wait_list_lock); + thread_data->stack_size = stack_size; + thread_data->tid = tid; + + /* Create the thread */ + if (!((tid = k_thread_create(tid, (k_thread_stack_t *) thread_data->stack, + stack_size, os_thread_wrapper, start, arg, thread_data, prio, 0, + K_NO_WAIT)))) { + BH_FREE(tid); + BH_FREE(thread_data); + return BHT_ERROR; + } + + bh_assert(tid == thread_data->tid); + + /* Set thread custom data */ + thread_data_list_add(thread_data); + thread_obj_list_add((os_thread_obj*) tid); + *p_tid = tid; + return BHT_OK; +} + +korp_tid os_self_thread() +{ + return (korp_tid) k_current_get(); +} + +int os_thread_join(korp_tid thread, void **value_ptr) +{ + (void) value_ptr; + os_thread_data *thread_data; + os_thread_wait_node *node; + + /* Create wait node and append it to wait list */ + if (!(node = BH_MALLOC(sizeof(os_thread_wait_node)))) + return BHT_ERROR; + + k_sem_init(&node->sem, 0, 1); + node->next = NULL; + + /* Get thread data */ + thread_data = thread_data_list_lookup(thread); + bh_assert(thread_data != NULL); + + k_mutex_lock(&thread_data->wait_list_lock, K_FOREVER); + if (!thread_data->thread_wait_list) + thread_data->thread_wait_list = node; + else { + /* Add to end of waiting list */ + os_thread_wait_node *p = thread_data->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + k_mutex_unlock(&thread_data->wait_list_lock); + + /* Wait the sem */ + k_sem_take(&node->sem, K_FOREVER); + + /* Wait some time for the thread to be actually terminated */ + k_sleep(Z_TIMEOUT_MS(100)); + + /* Destroy resource */ + BH_FREE(node); + return BHT_OK; +} + +int os_mutex_init(korp_mutex *mutex) +{ + k_mutex_init(mutex); + return BHT_OK; +} + +int os_recursive_mutex_init(korp_mutex *mutex) +{ + k_mutex_init(mutex); + return BHT_OK; +} + +int os_mutex_destroy(korp_mutex *mutex) +{ + (void) mutex; + return BHT_OK; +} + +int os_mutex_lock(korp_mutex *mutex) +{ + return k_mutex_lock(mutex, K_FOREVER); +} + +int os_mutex_unlock(korp_mutex *mutex) +{ + return k_mutex_unlock(mutex); +} + +int os_cond_init(korp_cond *cond) +{ + k_mutex_init(&cond->wait_list_lock); + cond->thread_wait_list = NULL; + return BHT_OK; +} + +int os_cond_destroy(korp_cond *cond) +{ + (void) cond; + return BHT_OK; +} + +static int os_cond_wait_internal(korp_cond *cond, korp_mutex *mutex, + bool timed, int mills) +{ + os_thread_wait_node *node; + + /* Create wait node and append it to wait list */ + if (!(node = BH_MALLOC(sizeof(os_thread_wait_node)))) + return BHT_ERROR; + + k_sem_init(&node->sem, 0, 1); + node->next = NULL; + + k_mutex_lock(&cond->wait_list_lock, K_FOREVER); + if (!cond->thread_wait_list) + cond->thread_wait_list = node; + else { + /* Add to end of wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next) + p = p->next; + p->next = node; + } + k_mutex_unlock(&cond->wait_list_lock); + + /* Unlock mutex, wait sem and lock mutex again */ + k_mutex_unlock(mutex); + k_sem_take(&node->sem, timed ? Z_TIMEOUT_MS(mills) : K_FOREVER); + k_mutex_lock(mutex, K_FOREVER); + + /* Remove wait node from wait list */ + k_mutex_lock(&cond->wait_list_lock, K_FOREVER); + if (cond->thread_wait_list == node) + cond->thread_wait_list = node->next; + else { + /* Remove from the wait list */ + os_thread_wait_node *p = cond->thread_wait_list; + while (p->next != node) + p = p->next; + p->next = node->next; + } + BH_FREE(node); + k_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +int os_cond_wait(korp_cond *cond, korp_mutex *mutex) +{ + return os_cond_wait_internal(cond, mutex, false, 0); +} + +int os_cond_reltimedwait(korp_cond *cond, korp_mutex *mutex, int useconds) +{ + + if (useconds == BHT_WAIT_FOREVER) + return os_cond_wait_internal(cond, mutex, false, 0); + else + return os_cond_wait_internal(cond, mutex, true, useconds / 1000); +} + +int os_cond_signal(korp_cond *cond) +{ + /* Signal the head wait node of wait list */ + k_mutex_lock(&cond->wait_list_lock, K_FOREVER); + if (cond->thread_wait_list) + k_sem_give(&cond->thread_wait_list->sem); + k_mutex_unlock(&cond->wait_list_lock); + + return BHT_OK; +} + +uint8 *os_thread_get_stack_boundary() +{ +#if defined(CONFIG_THREAD_STACK_INFO) + korp_tid thread = k_current_get(); + return (uint8*)thread->stack_info.start; +#else + return NULL; +#endif +} + diff --git a/wamr/core/shared/platform/zephyr/zephyr_time.c b/wamr/core/shared/platform/zephyr/zephyr_time.c new file mode 100644 index 0000000..b6c03f5 --- /dev/null +++ b/wamr/core/shared/platform/zephyr/zephyr_time.c @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "platform_api_vmcore.h" + +uint64 +os_time_get_boot_microsecond() +{ + return k_uptime_get_32() * 1000; +} + diff --git a/wamr/core/shared/utils/bh_assert.c b/wamr/core/shared/utils/bh_assert.c new file mode 100644 index 0000000..b9ac28e --- /dev/null +++ b/wamr/core/shared/utils/bh_assert.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_assert.h" + +void bh_assert_internal(int v, const char *file_name, int line_number, + const char *expr_string) +{ + int i; + + if (v) + return; + + if (!file_name) + file_name = "NULL FILENAME"; + + if (!expr_string) + expr_string = "NULL EXPR_STRING"; + + os_printf("\nASSERTION FAILED: %s, at file %s, line %d\n", + expr_string, file_name, line_number); + + i = os_printf(" "); + + /* divived by 0 to make it abort */ + os_printf("%d\n", i / (i - 1)); + while (1); +} + diff --git a/wamr/core/shared/utils/bh_assert.h b/wamr/core/shared/utils/bh_assert.h new file mode 100644 index 0000000..3a83d62 --- /dev/null +++ b/wamr/core/shared/utils/bh_assert.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_ASSERT_H +#define _BH_ASSERT_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if BH_DEBUG != 0 +void bh_assert_internal(int v, const char *file_name, int line_number, + const char *expr_string); +#define bh_assert(expr) bh_assert_internal((int)(uintptr_t)(expr), \ + __FILE__, __LINE__, #expr) +#else +#define bh_assert(expr) (void)0 +#endif /* end of BH_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_ASSERT_H */ + diff --git a/wamr/core/shared/utils/bh_common.c b/wamr/core/shared/utils/bh_common.c new file mode 100644 index 0000000..44bdbed --- /dev/null +++ b/wamr/core/shared/utils/bh_common.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_common.h" + +#ifdef RSIZE_MAX +#undef RSIZE_MAX +#endif + +#define RSIZE_MAX 0x7FFFFFFF + +int +b_memcpy_s(void * s1, unsigned int s1max, + const void * s2, unsigned int n) +{ + char *dest = (char*)s1; + char *src = (char*)s2; + if (n == 0) { + return 0; + } + + if (s1 == NULL || s1max > RSIZE_MAX) { + return -1; + } + if (s2 == NULL || n > s1max) { + memset(dest, 0, s1max); + return -1; + } + memcpy(dest, src, n); + return 0; +} + +int b_memmove_s(void * s1, unsigned int s1max, + const void * s2, unsigned int n) +{ + char *dest = (char*)s1; + char *src = (char*)s2; + if (n == 0) { + return 0; + } + + if (s1 == NULL || s1max > RSIZE_MAX) { + return -1; + } + if (s2 == NULL || n > s1max) { + memset(dest, 0, s1max); + return -1; + } + memmove(dest, src, n); + return 0; +} + +int +b_strcat_s(char * s1, unsigned int s1max, const char * s2) +{ + if (NULL == s1 || NULL == s2 + || s1max < (strlen(s1) + strlen(s2) + 1) + || s1max > RSIZE_MAX) { + return -1; + } + + memcpy(s1 + strlen(s1), s2, strlen(s2) + 1); + return 0; +} + +int +b_strcpy_s(char * s1, unsigned int s1max, const char * s2) +{ + if (NULL == s1 || NULL == s2 + || s1max < (strlen(s2) + 1) + || s1max > RSIZE_MAX) { + return -1; + } + + memcpy(s1, s2, strlen(s2) + 1); + return 0; +} + +char * +bh_strdup(const char *s) +{ + uint32 size; + char *s1 = NULL; + + if (s) { + size = (uint32)(strlen(s) + 1); + if ((s1 = BH_MALLOC(size))) + bh_memcpy_s(s1, size, s, size); + } + return s1; +} + +char * +wa_strdup(const char *s) +{ + uint32 size; + char *s1 = NULL; + + if (s) { + size = (uint32)(strlen(s) + 1); + if ((s1 = WA_MALLOC(size))) + bh_memcpy_s(s1, size, s, size); + } + return s1; +} + diff --git a/wamr/core/shared/utils/bh_common.h b/wamr/core/shared/utils/bh_common.h new file mode 100644 index 0000000..cd3bcda --- /dev/null +++ b/wamr/core/shared/utils/bh_common.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_COMMON_H +#define _BH_COMMON_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define bh_memcpy_s(dest, dlen, src, slen) do { \ + int _ret = slen == 0 ? 0 : b_memcpy_s (dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert (_ret == 0); \ + } while (0) + +#define bh_memmove_s(dest, dlen, src, slen) do { \ + int _ret = slen == 0 ? 0 : b_memmove_s (dest, dlen, src, slen); \ + (void)_ret; \ + bh_assert (_ret == 0); \ + } while (0) + +#define bh_strcat_s(dest, dlen, src) do { \ + int _ret = b_strcat_s (dest, dlen, src); \ + (void)_ret; \ + bh_assert (_ret == 0); \ + } while (0) + +#define bh_strcpy_s(dest, dlen, src) do { \ + int _ret = b_strcpy_s (dest, dlen, src); \ + (void)_ret; \ + bh_assert (_ret == 0); \ + } while (0) + +int b_memcpy_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); +int b_memmove_s(void * s1, unsigned int s1max, const void * s2, unsigned int n); +int b_strcat_s(char * s1, unsigned int s1max, const char * s2); +int b_strcpy_s(char * s1, unsigned int s1max, const char * s2); + +/* strdup with string allocated by BH_MALLOC */ +char *bh_strdup(const char *s); + +/* strdup with string allocated by WA_MALLOC */ +char *wa_strdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/core/shared/utils/bh_hashmap.c b/wamr/core/shared/utils/bh_hashmap.c new file mode 100644 index 0000000..5c7b9e8 --- /dev/null +++ b/wamr/core/shared/utils/bh_hashmap.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_hashmap.h" + +typedef struct HashMapElem { + void *key; + void *value; + struct HashMapElem *next; +} HashMapElem; + +struct HashMap { + /* size of element array */ + uint32 size; + /* lock for elements */ + korp_mutex *lock; + /* hash function of key */ + HashFunc hash_func; + /* key equal function */ + KeyEqualFunc key_equal_func; + KeyDestroyFunc key_destroy_func; + ValueDestroyFunc value_destroy_func; + HashMapElem *elements[1]; +}; + +HashMap* +bh_hash_map_create(uint32 size, bool use_lock, + HashFunc hash_func, + KeyEqualFunc key_equal_func, + KeyDestroyFunc key_destroy_func, + ValueDestroyFunc value_destroy_func) +{ + HashMap *map; + uint64 total_size; + + if (size > HASH_MAP_MAX_SIZE) { + LOG_ERROR("HashMap create failed: size is too large.\n"); + return NULL; + } + + if (!hash_func || !key_equal_func) { + LOG_ERROR("HashMap create failed: hash function or key equal function " + " is NULL.\n"); + return NULL; + } + + total_size = offsetof(HashMap, elements) + + sizeof(HashMapElem) * (uint64)size + + (use_lock ? sizeof(korp_mutex) : 0); + + if (total_size >= UINT32_MAX + || !(map = BH_MALLOC((uint32)total_size))) { + LOG_ERROR("HashMap create failed: alloc memory failed.\n"); + return NULL; + } + + memset(map, 0, (uint32)total_size); + + if (use_lock) { + map->lock = (korp_mutex*) + ((uint8*)map + offsetof(HashMap, elements) + + sizeof(HashMapElem) * size); + if (os_mutex_init(map->lock)) { + LOG_ERROR("HashMap create failed: init map lock failed.\n"); + BH_FREE(map); + return NULL; + } + } + + map->size = size; + map->hash_func = hash_func; + map->key_equal_func = key_equal_func; + map->key_destroy_func = key_destroy_func; + map->value_destroy_func = value_destroy_func; + return map; +} + +bool +bh_hash_map_insert(HashMap *map, void *key, void *value) +{ + uint32 index; + HashMapElem *elem; + + if (!map || !key) { + LOG_ERROR("HashMap insert elem failed: map or key is NULL.\n"); + return false; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + index = map->hash_func(key) % map->size; + elem = map->elements[index]; + while (elem) { + if (map->key_equal_func(elem->key, key)) { + LOG_ERROR("HashMap insert elem failed: duplicated key found.\n"); + goto fail; + } + elem = elem->next; + } + + if (!(elem = BH_MALLOC(sizeof(HashMapElem)))) { + LOG_ERROR("HashMap insert elem failed: alloc memory failed.\n"); + goto fail; + } + + elem->key = key; + elem->value = value; + elem->next = map->elements[index]; + map->elements[index] = elem; + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return true; + +fail: + if (map->lock) { + os_mutex_unlock(map->lock); + } + return false; +} + +void* +bh_hash_map_find(HashMap *map, void *key) +{ + uint32 index; + HashMapElem *elem; + void *value; + + if (!map || !key) { + LOG_ERROR("HashMap find elem failed: map or key is NULL.\n"); + return NULL; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + index = map->hash_func(key) % map->size; + elem = map->elements[index]; + + while (elem) { + if (map->key_equal_func(elem->key, key)) { + value = elem->value; + if (map->lock) { + os_mutex_unlock(map->lock); + } + return value; + } + elem = elem->next; + } + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return NULL; +} + +bool +bh_hash_map_update(HashMap *map, void *key, void *value, + void **p_old_value) +{ + uint32 index; + HashMapElem *elem; + + if (!map || !key) { + LOG_ERROR("HashMap update elem failed: map or key is NULL.\n"); + return false; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + index = map->hash_func(key) % map->size; + elem = map->elements[index]; + + while (elem) { + if (map->key_equal_func(elem->key, key)) { + if (p_old_value) + *p_old_value = elem->value; + elem->value = value; + if (map->lock) { + os_mutex_unlock(map->lock); + } + return true; + } + elem = elem->next; + } + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return false; +} + +bool +bh_hash_map_remove(HashMap *map, void *key, + void **p_old_key, void **p_old_value) +{ + uint32 index; + HashMapElem *elem, *prev; + + if (!map || !key) { + LOG_ERROR("HashMap remove elem failed: map or key is NULL.\n"); + return false; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + index = map->hash_func(key) % map->size; + prev = elem = map->elements[index]; + + while (elem) { + if (map->key_equal_func(elem->key, key)) { + if (p_old_key) + *p_old_key = elem->key; + if (p_old_value) + *p_old_value = elem->value; + + if (elem == map->elements[index]) + map->elements[index] = elem->next; + else + prev->next = elem->next; + + BH_FREE(elem); + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return true; + } + + prev = elem; + elem = elem->next; + } + + if (map->lock) { + os_mutex_unlock(map->lock); + } + return false; +} + +bool +bh_hash_map_destroy(HashMap *map) +{ + uint32 index; + HashMapElem *elem, *next; + + if (!map) { + LOG_ERROR("HashMap destroy failed: map is NULL.\n"); + return false; + } + + if (map->lock) { + os_mutex_lock(map->lock); + } + + for (index = 0; index < map->size; index++) { + elem = map->elements[index]; + while (elem) { + next = elem->next; + + if (map->key_destroy_func) { + map->key_destroy_func(elem->key); + } + if (map->value_destroy_func) { + map->value_destroy_func(elem->value); + } + BH_FREE(elem); + + elem = next; + } + } + + if (map->lock) { + os_mutex_unlock(map->lock); + os_mutex_destroy(map->lock); + } + BH_FREE(map); + return true; +} diff --git a/wamr/core/shared/utils/bh_hashmap.h b/wamr/core/shared/utils/bh_hashmap.h new file mode 100644 index 0000000..633c727 --- /dev/null +++ b/wamr/core/shared/utils/bh_hashmap.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef WASM_HASHMAP_H +#define WASM_HASHMAP_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Maximum initial size of hash map */ +#define HASH_MAP_MAX_SIZE 65536 + +struct HashMap; +typedef struct HashMap HashMap; + +/* Hash function: to get the hash value of key. */ +typedef uint32 (*HashFunc)(const void *key); + +/* Key equal function: to check whether two keys are equal. */ +typedef bool (*KeyEqualFunc)(void *key1, void *key2); + +/* Key destroy function: to destroy the key, auto called + when an hash element is removed. */ +typedef void (*KeyDestroyFunc)(void *key); + +/* Value destroy function: to destroy the value, auto called + when an hash element is removed. */ +typedef void (*ValueDestroyFunc)(void *key); + +/** + * Create a hash map. + * + * @param size: the initial size of the hash map + * @param use_lock whether to lock the hash map when operating on it + * @param hash_func hash function of the key, must be specified + * @param key_equal_func key equal function, check whether two keys + * are equal, must be specified + * @param key_destroy_func key destroy function, called when an hash element + * is removed if it is not NULL + * @param value_destroy_func value destroy function, called when an hash + * element is removed if it is not NULL + * + * @return the hash map created, NULL if failed + */ +HashMap* +bh_hash_map_create(uint32 size, bool use_lock, + HashFunc hash_func, + KeyEqualFunc key_equal_func, + KeyDestroyFunc key_destroy_func, + ValueDestroyFunc value_destroy_func); + +/** + * Insert an element to the hash map + * + * @param map the hash map to insert element + * @key the key of the element + * @value the value of the element + * + * @return true if success, false otherwise + * Note: fail if key is NULL or duplicated key exists in the hash map, + */ +bool +bh_hash_map_insert(HashMap *map, void *key, void *value); + +/** + * Find an element in the hash map + * + * @param map the hash map to find element + * @key the key of the element + * + * @return the value of the found element if success, NULL otherwise + */ +void* +bh_hash_map_find(HashMap *map, void *key); + +/** + * Update an element in the hash map with new value + * + * @param map the hash map to update element + * @key the key of the element + * @value the new value of the element + * @p_old_value if not NULL, copies the old value to it + * + * @return true if success, false otherwise + * Note: the old value won't be destroyed by value destroy function, + * it will be copied to p_old_value for user to process. + */ +bool +bh_hash_map_update(HashMap *map, void *key, void *value, + void **p_old_value); + +/** + * Remove an element from the hash map + * + * @param map the hash map to remove element + * @key the key of the element + * @p_old_key if not NULL, copies the old key to it + * @p_old_value if not NULL, copies the old value to it + * + * @return true if success, false otherwise + * Note: the old key and old value won't be destroyed by key destroy + * function and value destroy function, they will be copied to + * p_old_key and p_old_value for user to process. + */ +bool +bh_hash_map_remove(HashMap *map, void *key, + void **p_old_key, void **p_old_value); + +/** + * Destroy the hashmap + * + * @param map the hash map to destroy + * + * @return true if success, false otherwise + * Note: the key destroy function and value destroy function will be + * called to destroy each element's key and value if they are + * not NULL. + */ +bool +bh_hash_map_destroy(HashMap *map); + +#ifdef __cplusplus +} +#endif + +#endif /* endof WASM_HASHMAP_H */ + diff --git a/wamr/core/shared/utils/bh_list.c b/wamr/core/shared/utils/bh_list.c new file mode 100644 index 0000000..79f46ad --- /dev/null +++ b/wamr/core/shared/utils/bh_list.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_list.h" + +#if BH_DEBUG != 0 +/** + * Test whehter a pointer value has exist in given list. + * + * @param list pointer to list. + * @param elem pointer to elem that will be inserted into list. + * @return true if the pointer has been in the list; + * false otherwise. + */ +static bool bh_list_is_elem_exist(bh_list *list, void *elem); +#endif + +bh_list_status bh_list_init(bh_list *list) +{ + if (!list) + return BH_LIST_ERROR; + + (list->head).next = NULL; + list->len = 0; + return BH_LIST_SUCCESS; +} + +bh_list_status bh_list_insert(bh_list *list, void *elem) +{ + bh_list_link *p = NULL; + + if (!list || !elem) + return BH_LIST_ERROR; +#if BH_DEBUG != 0 + bh_assert (!bh_list_is_elem_exist(list, elem)); +#endif + p = (bh_list_link *) elem; + p->next = (list->head).next; + (list->head).next = p; + list->len++; + return BH_LIST_SUCCESS; +} + +bh_list_status bh_list_remove(bh_list *list, void *elem) +{ + bh_list_link *cur = NULL; + bh_list_link *prev = NULL; + + if (!list || !elem) + return BH_LIST_ERROR; + + cur = (list->head).next; + + while (cur) { + if (cur == elem) { + if (prev) + prev->next = cur->next; + else + (list->head).next = cur->next; + + list->len--; + return BH_LIST_SUCCESS; + } + + prev = cur; + cur = cur->next; + } + + return BH_LIST_ERROR; +} + +uint32 bh_list_length(bh_list *list) +{ + return (list ? list->len : 0); +} + +void* bh_list_first_elem(bh_list *list) +{ + return (list ? (list->head).next : NULL); +} + +void* bh_list_elem_next(void *node) +{ + return (node ? ((bh_list_link *) node)->next : NULL); +} + +#if BH_DEBUG != 0 +static bool bh_list_is_elem_exist(bh_list *list, void *elem) +{ + bh_list_link *p = NULL; + + if (!list || !elem) return false; + + p = (list->head).next; + while (p && p != elem) p = p->next; + + return (p != NULL); +} +#endif + diff --git a/wamr/core/shared/utils/bh_list.h b/wamr/core/shared/utils/bh_list.h new file mode 100644 index 0000000..f47b937 --- /dev/null +++ b/wamr/core/shared/utils/bh_list.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_LIST_H +#define _BH_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" + +/* List user should embedded bh_list_link into list elem data structure + * definition. And bh_list_link data field should be the first field. + * For example, if we would like to use bh_list for our own data type A, + * A must be defined as a structure like below: + * struct A { + * bh_list_link l; + * ... + * }; + * + * bh_list_link is defined as a structure (not typedef void*). + * It will make extend list into bi-direction easy. + */ +typedef struct bh_list_link { + struct bh_list_link *next; +} bh_list_link; + +typedef struct bh_list { + bh_list_link head; + uint32 len; +} bh_list; + +/* list operation return value */ +typedef enum bh_list_status { + BH_LIST_SUCCESS = 0, + BH_LIST_ERROR = -1 +} bh_list_status; + +/** + * Initialize a list. + * + * @param list pointer to list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if list pointer is NULL. + */ +bh_list_status bh_list_init(bh_list *list); + +/** + * Insert an elem pointer into list. The list node memory is maintained by list while + * elem memory is the responsibility of list user. + * + * @param list pointer to list. + * @param elem pointer to elem that will be inserted into list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if input is invalid or no memory available. + */ +extern bh_list_status bh_list_insert(bh_list *list, void *elem); + +/** + * Remove an elem pointer from list. The list node memory is maintained by list while + * elem memory is the responsibility of list user. + * + * @param list pointer to list. + * @param elem pointer to elem that will be inserted into list. + * @return BH_LIST_ERROR if OK; + * BH_LIST_ERROR if element does not exist in given list. + */ +bh_list_status bh_list_remove(bh_list *list, void *elem); + +/** + * Get the list length. + * + * @param list pointer to list. + * @return the length of the list. + */ +uint32 bh_list_length(bh_list *list); + +/** + * Get the first elem in the list. + * + * @param list pointer to list. + * @return pointer to the first node. + */ +void* bh_list_first_elem(bh_list *list); + +/** + * Get the next elem of given list input elem. + * + * @param node pointer to list node. + * @return pointer to next list node. + */ +void* bh_list_elem_next(void *node); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _BH_LIST_H */ + diff --git a/wamr/core/shared/utils/bh_log.c b/wamr/core/shared/utils/bh_log.c new file mode 100644 index 0000000..e1a3eb2 --- /dev/null +++ b/wamr/core/shared/utils/bh_log.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_log.h" + +/** + * The verbose level of the log system. Only those verbose logs whose + * levels are less than or equal to this value are outputed. + */ +static uint32 log_verbose_level = BH_LOG_LEVEL_WARNING; + +void +bh_log_set_verbose_level(uint32 level) +{ + log_verbose_level = level; +} + +void +bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...) +{ + va_list ap; + korp_tid self; + char buf[32] = { 0 }; + uint64 usec; + uint32 t, h, m, s, mills; + + if (log_level > log_verbose_level) + return; + + self = os_self_thread(); + + usec = os_time_get_boot_microsecond(); + t = (uint32)(usec / 1000000) % (24 * 60 * 60); + h = t / (60 * 60); + t = t % (60 * 60); + m = t / 60; + s = t % 60; + mills = (uint32)(usec % 1000); + + snprintf(buf, sizeof(buf), "%02u:%02u:%02u:%03u", h, m, s, mills); + + os_printf("[%s - %X]: ", buf, (uint32)(uintptr_t)self); + + if (file) + os_printf("%s, line %d, ", file, line); + + va_start(ap, fmt); + os_vprintf(fmt, ap); + va_end(ap); + + os_printf("\n"); +} + +static uint32 last_time_ms = 0; +static uint32 total_time_ms = 0; + +void +bh_print_time(const char *prompt) +{ + uint32 curr_time_ms; + + if (log_verbose_level < 3) + return; + + curr_time_ms = (uint32)bh_get_tick_ms(); + + if (last_time_ms == 0) + last_time_ms = curr_time_ms; + + total_time_ms += curr_time_ms - last_time_ms; + + os_printf("%-48s time of last stage: %u ms, total time: %u ms\n", + prompt, curr_time_ms - last_time_ms, total_time_ms); + + last_time_ms = curr_time_ms; +} + diff --git a/wamr/core/shared/utils/bh_log.h b/wamr/core/shared/utils/bh_log.h new file mode 100644 index 0000000..6a5ead4 --- /dev/null +++ b/wamr/core/shared/utils/bh_log.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +/** + * @file bh_log.h + * @date Tue Nov 8 18:19:10 2011 + * + * @brief This log system supports wrapping multiple outputs into one + * log message. This is useful for outputting variable-length logs + * without additional memory overhead (the buffer for concatenating + * the message), e.g. exception stack trace, which cannot be printed + * by a single log calling without the help of an additional buffer. + * Avoiding additional memory buffer is useful for resource-constraint + * systems. It can minimize the impact of log system on applications + * and logs can be printed even when no enough memory is available. + * Functions with prefix "_" are private functions. Only macros that + * are not start with "_" are exposed and can be used. + */ + +#ifndef _BH_LOG_H +#define _BH_LOG_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + BH_LOG_LEVEL_FATAL = 0, + BH_LOG_LEVEL_ERROR = 1, + BH_LOG_LEVEL_WARNING = 2, + BH_LOG_LEVEL_DEBUG = 3, + BH_LOG_LEVEL_VERBOSE = 4 +} LogLevel; + +void +bh_log_set_verbose_level(uint32 level); + +void +bh_log(LogLevel log_level, const char *file, int line, const char *fmt, ...); + +#if BH_DEBUG == 1 +#define LOG_FATAL(...) bh_log(BH_LOG_LEVEL_FATAL, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_FATAL(...) bh_log(BH_LOG_LEVEL_FATAL, __FUNCTION__, __LINE__, __VA_ARGS__) +#endif + +#define LOG_ERROR(...) bh_log(BH_LOG_LEVEL_ERROR, NULL, 0, __VA_ARGS__) +#define LOG_WARNING(...) bh_log(BH_LOG_LEVEL_WARNING, NULL, 0, __VA_ARGS__) +#define LOG_VERBOSE(...) bh_log(BH_LOG_LEVEL_VERBOSE, NULL, 0, __VA_ARGS__) + +#if BH_DEBUG == 1 +#define LOG_DEBUG(...) bh_log(BH_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#else +#define LOG_DEBUG(...) (void)0 +#endif + +void +bh_print_time(const char *prompt); + +#ifdef __cplusplus +} +#endif + +#endif /* _BH_LOG_H */ diff --git a/wamr/core/shared/utils/bh_platform.h b/wamr/core/shared/utils/bh_platform.h new file mode 100644 index 0000000..94b156f --- /dev/null +++ b/wamr/core/shared/utils/bh_platform.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_PLATFORM_H +#define _BH_PLATFORM_H + +#include "../platform/include/platform_common.h" +#include "../platform/include/platform_api_vmcore.h" +#include "../platform/include/platform_api_extension.h" +#include "bh_assert.h" +#include "bh_common.h" +#include "bh_hashmap.h" +#include "bh_list.h" +#include "bh_log.h" +#include "bh_queue.h" +#include "bh_vector.h" +#include "runtime_timer.h" + + + +/** + * WA_MALLOC/WA_FREE need to be redefined for both + * runtime native and WASM app respectively. + * + * Some source files are shared for building native and WASM, + * and this the mem allocator API for these files. + * + * Here we define it for the native world + */ +#ifndef WA_MALLOC +#define WA_MALLOC wasm_runtime_malloc +#endif + +#ifndef WA_FREE +#define WA_FREE wasm_runtime_free +#endif + +#endif /* #ifndef _BH_PLATFORM_H */ + diff --git a/wamr/core/shared/utils/bh_queue.c b/wamr/core/shared/utils/bh_queue.c new file mode 100644 index 0000000..60d8736 --- /dev/null +++ b/wamr/core/shared/utils/bh_queue.c @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_queue.h" + +typedef struct bh_queue_node { + struct bh_queue_node * next; + struct bh_queue_node * prev; + unsigned short tag; + unsigned int len; + void * body; + bh_msg_cleaner msg_cleaner; +} bh_queue_node; + +struct bh_queue { + bh_queue_mutex queue_lock; + bh_queue_cond queue_wait_cond; + unsigned int cnt; + unsigned int max; + unsigned int drops; + bh_queue_node * head; + bh_queue_node * tail; + + bool exit_loop_run; +}; + +char * bh_message_payload(bh_message_t message) +{ + return message->body; +} + +uint32 bh_message_payload_len(bh_message_t message) +{ + return message->len; +} + +int bh_message_type(bh_message_t message) +{ + return message->tag; +} + +bh_queue * +bh_queue_create() +{ + int ret; + bh_queue *queue = bh_queue_malloc(sizeof(bh_queue)); + + if (queue) { + memset(queue, 0, sizeof(bh_queue)); + queue->max = DEFAULT_QUEUE_LENGTH; + + ret = bh_queue_mutex_init(&queue->queue_lock); + if (ret != 0) { + bh_queue_free(queue); + return NULL; + } + + ret = bh_queue_cond_init(&queue->queue_wait_cond); + if (ret != 0) { + bh_queue_mutex_destroy(&queue->queue_lock); + bh_queue_free(queue); + return NULL; + } + } + + return queue; +} + +void bh_queue_destroy(bh_queue *queue) +{ + bh_queue_node *node; + + if (!queue) + return; + + bh_queue_mutex_lock(&queue->queue_lock); + while (queue->head) { + node = queue->head; + queue->head = node->next; + + bh_free_msg(node); + } + bh_queue_mutex_unlock(&queue->queue_lock); + + bh_queue_cond_destroy(&queue->queue_wait_cond); + bh_queue_mutex_destroy(&queue->queue_lock); + bh_queue_free(queue); +} + +bool bh_post_msg2(bh_queue *queue, bh_queue_node *msg) +{ + if (queue->cnt >= queue->max) { + queue->drops++; + bh_free_msg(msg); + return false; + } + + bh_queue_mutex_lock(&queue->queue_lock); + + if (queue->cnt == 0) { + bh_assert(queue->head == NULL); + bh_assert(queue->tail == NULL); + queue->head = queue->tail = msg; + msg->next = msg->prev = NULL; + queue->cnt = 1; + + bh_queue_cond_signal(&queue->queue_wait_cond); + } else { + msg->next = NULL; + msg->prev = queue->tail; + queue->tail->next = msg; + queue->tail = msg; + queue->cnt++; + } + + bh_queue_mutex_unlock(&queue->queue_lock); + + return true; +} + +bool bh_post_msg(bh_queue *queue, unsigned short tag, void *body, + unsigned int len) +{ + bh_queue_node *msg = bh_new_msg(tag, body, len, NULL); + if (msg == NULL) { + queue->drops++; + if (len != 0 && body) + BH_FREE(body); + return false; + } + + if (!bh_post_msg2(queue, msg)) { + // bh_post_msg2 already freed the msg for failure + return false; + } + + return true; +} + +bh_queue_node * bh_new_msg(unsigned short tag, void *body, unsigned int len, + void * handler) +{ + bh_queue_node *msg = (bh_queue_node*) + bh_queue_malloc(sizeof(bh_queue_node)); + if (msg == NULL) + return NULL; + memset(msg, 0, sizeof(bh_queue_node)); + msg->len = len; + msg->body = body; + msg->tag = tag; + msg->msg_cleaner = (bh_msg_cleaner) handler; + + return msg; +} + +void bh_free_msg(bh_queue_node *msg) +{ + if (msg->msg_cleaner) { + msg->msg_cleaner(msg->body); + bh_queue_free(msg); + return; + } + + // note: sometime we just use the payload pointer for a integer value + // len!=0 is the only indicator about the body is an allocated buffer. + if (msg->body && msg->len) + bh_queue_free(msg->body); + + bh_queue_free(msg); +} + +bh_message_t bh_get_msg(bh_queue *queue, int timeout) +{ + bh_queue_node *msg = NULL; + bh_queue_mutex_lock(&queue->queue_lock); + + if (queue->cnt == 0) { + bh_assert(queue->head == NULL); + bh_assert(queue->tail == NULL); + + if (timeout == 0) { + bh_queue_mutex_unlock(&queue->queue_lock); + return NULL; + } + + bh_queue_cond_timedwait(&queue->queue_wait_cond, &queue->queue_lock, + timeout); + } + + if (queue->cnt == 0) { + bh_assert(queue->head == NULL); + bh_assert(queue->tail == NULL); + } else if (queue->cnt == 1) { + bh_assert(queue->head == queue->tail); + + msg = queue->head; + queue->head = queue->tail = NULL; + queue->cnt = 0; + } else { + msg = queue->head; + queue->head = queue->head->next; + queue->head->prev = NULL; + queue->cnt--; + } + + bh_queue_mutex_unlock(&queue->queue_lock); + + return msg; +} + +unsigned bh_queue_get_message_count(bh_queue *queue) +{ + if (!queue) + return 0; + + return queue->cnt; +} + +void bh_queue_enter_loop_run(bh_queue *queue, + bh_queue_handle_msg_callback handle_cb, + void *arg) +{ + if (!queue) + return; + + while (!queue->exit_loop_run) { + bh_queue_node * message = bh_get_msg(queue, (int)BHT_WAIT_FOREVER); + + if (message) { + handle_cb(message, arg); + bh_free_msg(message); + } + } +} + +void bh_queue_exit_loop_run(bh_queue *queue) +{ + if (queue) { + queue->exit_loop_run = true; + bh_queue_cond_signal(&queue->queue_wait_cond); + } +} diff --git a/wamr/core/shared/utils/bh_queue.h b/wamr/core/shared/utils/bh_queue.h new file mode 100644 index 0000000..8a98ac4 --- /dev/null +++ b/wamr/core/shared/utils/bh_queue.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_QUEUE_H +#define _BH_QUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bh_platform.h" + +struct bh_queue_node; +typedef struct bh_queue_node * bh_message_t; +struct bh_queue; +typedef struct bh_queue bh_queue; + +typedef void (*bh_queue_handle_msg_callback)(void *message, void *arg); + +#define bh_queue_malloc BH_MALLOC +#define bh_queue_free BH_FREE + +#define bh_queue_mutex korp_mutex +#define bh_queue_cond korp_cond + +#define bh_queue_mutex_init os_mutex_init +#define bh_queue_mutex_destroy os_mutex_destroy +#define bh_queue_mutex_lock os_mutex_lock +#define bh_queue_mutex_unlock os_mutex_unlock + +#define bh_queue_cond_init os_cond_init +#define bh_queue_cond_destroy os_cond_destroy +#define bh_queue_cond_wait os_cond_wait +#define bh_queue_cond_timedwait os_cond_reltimedwait +#define bh_queue_cond_signal os_cond_signal +#define bh_queue_cond_broadcast os_cond_broadcast + +typedef void (*bh_msg_cleaner)(void *msg); + +bh_queue * +bh_queue_create(); + +void +bh_queue_destroy(bh_queue *queue); + +char * bh_message_payload(bh_message_t message); +uint32 bh_message_payload_len(bh_message_t message); +int bh_message_type(bh_message_t message); + +bh_message_t bh_new_msg(unsigned short tag, void *body, unsigned int len, + void * handler); +void bh_free_msg(bh_message_t msg); +bool bh_post_msg(bh_queue *queue, unsigned short tag, void *body, + unsigned int len); +bool bh_post_msg2(bh_queue *queue, bh_message_t msg); + +bh_message_t bh_get_msg(bh_queue *queue, int timeout); + +unsigned +bh_queue_get_message_count(bh_queue *queue); + +void +bh_queue_enter_loop_run(bh_queue *queue, + bh_queue_handle_msg_callback handle_cb, + void *arg); +void +bh_queue_exit_loop_run(bh_queue *queue); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef _BH_QUEUE_H */ + diff --git a/wamr/core/shared/utils/bh_vector.c b/wamr/core/shared/utils/bh_vector.c new file mode 100644 index 0000000..dfb8339 --- /dev/null +++ b/wamr/core/shared/utils/bh_vector.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_vector.h" + +static uint8* +alloc_vector_data(uint32 length, uint32 size_elem) +{ + uint64 total_size = ((uint64)size_elem) * length; + uint8 *data; + + if (total_size > UINT32_MAX) { + return NULL; + } + + if ((data = BH_MALLOC((uint32)total_size))) { + memset(data, 0, (uint32)total_size); + } + + return data; +} + +static bool +extend_vector(Vector *vector, uint32 length) +{ + uint8 *data; + + if (length <= vector->max_elems) + return true; + + if (length < vector->size_elem * 3 / 2) + length = vector->size_elem * 3 / 2; + + if (!(data = alloc_vector_data(length, vector->size_elem))) { + return false; + } + + memcpy(data, vector->data, vector->size_elem * vector->max_elems); + BH_FREE(vector->data); + vector->data = data; + vector->max_elems = length; + return true; +} + +bool +bh_vector_init(Vector *vector, uint32 init_length, uint32 size_elem) +{ + if (!vector) { + LOG_ERROR("Init vector failed: vector is NULL.\n"); + return false; + } + + if (init_length == 0) { + init_length = 4; + } + + if (!(vector->data = alloc_vector_data(init_length, size_elem))) { + LOG_ERROR("Init vector failed: alloc memory failed.\n"); + return false; + } + + vector->size_elem = size_elem; + vector->max_elems = init_length; + vector->num_elems = 0; + return true; +} + +bool +bh_vector_set(Vector *vector, uint32 index, const void *elem_buf) +{ + if (!vector || !elem_buf) { + LOG_ERROR("Set vector elem failed: vector or elem buf is NULL.\n"); + return false; + } + + if (index >= vector->num_elems) { + LOG_ERROR("Set vector elem failed: invalid elem index.\n"); + return false; + } + + memcpy(vector->data + vector->size_elem * index, + elem_buf, vector->size_elem); + return true; +} + +bool bh_vector_get(const Vector *vector, uint32 index, void *elem_buf) +{ + if (!vector || !elem_buf) { + LOG_ERROR("Get vector elem failed: vector or elem buf is NULL.\n"); + return false; + } + + if (index >= vector->num_elems) { + LOG_ERROR("Get vector elem failed: invalid elem index.\n"); + return false; + } + + memcpy(elem_buf, vector->data + vector->size_elem * index, + vector->size_elem); + return true; +} + +bool bh_vector_insert(Vector *vector, uint32 index, const void *elem_buf) +{ + uint32 i; + uint8 *p; + + if (!vector || !elem_buf) { + LOG_ERROR("Insert vector elem failed: vector or elem buf is NULL.\n"); + return false; + } + + if (index >= vector->num_elems) { + LOG_ERROR("Insert vector elem failed: invalid elem index.\n"); + return false; + } + + if (!extend_vector(vector, vector->num_elems + 1)) { + LOG_ERROR("Insert vector elem failed: extend vector failed.\n"); + return false; + } + + p = vector->data + vector->size_elem * vector->num_elems; + for (i = vector->num_elems - 1; i > index; i--) { + memcpy(p, p - vector->size_elem, vector->size_elem); + p -= vector->size_elem; + } + + memcpy(p, elem_buf, vector->size_elem); + vector->num_elems++; + return true; +} + +bool bh_vector_append(Vector *vector, const void *elem_buf) +{ + if (!vector || !elem_buf) { + LOG_ERROR("Append vector elem failed: vector or elem buf is NULL.\n"); + return false; + } + + if (!extend_vector(vector, vector->num_elems + 1)) { + LOG_ERROR("Append ector elem failed: extend vector failed.\n"); + return false; + } + + memcpy(vector->data + vector->size_elem * vector->num_elems, + elem_buf, vector->size_elem); + vector->num_elems++; + return true; +} + +bool +bh_vector_remove(Vector *vector, uint32 index, void *old_elem_buf) +{ + uint32 i; + uint8 *p; + + if (!vector) { + LOG_ERROR("Remove vector elem failed: vector is NULL.\n"); + return false; + } + + if (index >= vector->num_elems) { + LOG_ERROR("Remove vector elem failed: invalid elem index.\n"); + return false; + } + + p = vector->data + vector->size_elem * index; + + if (old_elem_buf) { + memcpy(old_elem_buf, p, vector->size_elem); + } + + for (i = index; i < vector->num_elems - 1; i++) { + memcpy(p, p + vector->size_elem, vector->size_elem); + p += vector->size_elem; + } + + vector->num_elems--; + return true; +} + +uint32 +bh_vector_size(const Vector *vector) +{ + return vector ? vector->num_elems : 0; +} + +bool +bh_vector_destroy(Vector *vector) +{ + if (!vector) { + LOG_ERROR("Destroy vector elem failed: vector is NULL.\n"); + return false; + } + + if (vector->data) + BH_FREE(vector->data); + memset(vector, 0, sizeof(Vector)); + return true; +} diff --git a/wamr/core/shared/utils/bh_vector.h b/wamr/core/shared/utils/bh_vector.h new file mode 100644 index 0000000..1ae7c27 --- /dev/null +++ b/wamr/core/shared/utils/bh_vector.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WASM_VECTOR_H +#define _WASM_VECTOR_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFAULT_VECTOR_INIT_SIZE 8 + +typedef struct Vector { + /* max element number */ + size_t max_elems; + /* vector data allocated */ + uint8 *data; + /* current element num */ + size_t num_elems; + /* size of each element */ + size_t size_elem; +} Vector; + +/** + * Initialize vector + * + * @param vector the vector to init + * @param init_length the initial length of the vector + * @param size_elem size of each element + * + * @return true if success, false otherwise + */ +bool +bh_vector_init(Vector *vector, uint32 init_length, uint32 size_elem); + +/** + * Set element of vector + * + * @param vector the vector to set + * @param index the index of the element to set + * @param elem_buf the element buffer which stores the element data + * + * @return true if success, false otherwise + */ +bool +bh_vector_set(Vector *vector, uint32 index, const void *elem_buf); + +/** + * Get element of vector + * + * @param vector the vector to get + * @param index the index of the element to get + * @param elem_buf the element buffer to store the element data, + * whose length must be no less than element size + * + * @return true if success, false otherwise + */ +bool +bh_vector_get(const Vector *vector, uint32 index, void *elem_buf); + +/** + * Insert element of vector + * + * @param vector the vector to insert + * @param index the index of the element to insert + * @param elem_buf the element buffer which stores the element data + * + * @return true if success, false otherwise + */ +bool +bh_vector_insert(Vector *vector, uint32 index, const void *elem_buf); + +/** + * Append element to the end of vector + * + * @param vector the vector to append + * @param elem_buf the element buffer which stores the element data + * + * @return true if success, false otherwise + */ +bool +bh_vector_append(Vector *vector, const void *elem_buf); + +/** + * Remove element from vector + * + * @param vector the vector to remove element + * @param index the index of the element to remove + * @param old_elem_buf if not NULL, copies the element data to the buffer + * + * @return true if success, false otherwise + */ +bool +bh_vector_remove(Vector *vector, uint32 index, void *old_elem_buf); + +/** + * Return the size of the vector + * + * @param vector the vector to get size + * + * @return return the size of the vector + */ +uint32 +bh_vector_size(const Vector *vector); + +/** + * Destroy the vector + * + * @param vector the vector to destroy + * + * @return true if success, false otherwise + */ +bool +bh_vector_destroy(Vector *vector); + +#ifdef __cplusplus +} +#endif + +#endif /* endof _WASM_VECTOR_H */ + diff --git a/wamr/core/shared/utils/runtime_timer.c b/wamr/core/shared/utils/runtime_timer.c new file mode 100644 index 0000000..68b25f8 --- /dev/null +++ b/wamr/core/shared/utils/runtime_timer.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "runtime_timer.h" + +#define PRINT(...) +//#define PRINT printf + +typedef struct _app_timer { + struct _app_timer * next; + uint32 id; + unsigned int interval; + uint64 expiry; + bool is_periodic; +} app_timer_t; + +struct _timer_ctx { + app_timer_t * g_app_timers; + app_timer_t * idle_timers; + app_timer_t * free_timers; + unsigned int g_max_id; + int pre_allocated; + unsigned int owner; + + //add mutext and conditions + korp_cond cond; + korp_mutex mutex; + + timer_callback_f timer_callback; + check_timer_expiry_f refresh_checker; +}; + +uint64 bh_get_tick_ms() +{ + return os_time_get_boot_microsecond() / 1000; +} + +uint32 bh_get_elpased_ms(uint32 * last_system_clock) +{ + uint32 elpased_ms; + + // attention: the bh_get_tick_ms() return 64 bits integer. + // but the bh_get_elpased_ms() is designed to use 32 bits clock count. + uint32 now = (uint32)bh_get_tick_ms(); + + // system clock overrun + if (now < *last_system_clock) { + elpased_ms = now + (0xFFFFFFFF - *last_system_clock) + 1; + } else { + elpased_ms = now - *last_system_clock; + } + + *last_system_clock = now; + + return elpased_ms; +} + +static app_timer_t * remove_timer_from(timer_ctx_t ctx, uint32 timer_id, + bool active_list) +{ + os_mutex_lock(&ctx->mutex); + app_timer_t ** head; + if (active_list) + head = &ctx->g_app_timers; + else + head = &ctx->idle_timers; + + app_timer_t * t = *head; + app_timer_t * prev = NULL; + + while (t) { + if (t->id == timer_id) { + if (prev == NULL) { + *head = t->next; + PRINT("removed timer [%d] at head from list %d\n", t->id, active_list); + } else { + prev->next = t->next; + PRINT("removed timer [%d] after [%d] from list %d\n", t->id, prev->id, active_list); + } + os_mutex_unlock(&ctx->mutex); + + if (active_list && prev == NULL && ctx->refresh_checker) + ctx->refresh_checker(ctx); + + return t; + } else { + prev = t; + t = t->next; + } + } + + os_mutex_unlock(&ctx->mutex); + + return NULL; +} + +static app_timer_t * remove_timer(timer_ctx_t ctx, uint32 timer_id, + bool * active) +{ + app_timer_t* t = remove_timer_from(ctx, timer_id, true); + if (t) { + if (active) + *active = true; + return t; + } + + if (active) + *active = false; + return remove_timer_from(ctx, timer_id, false); +} + +static void reschedule_timer(timer_ctx_t ctx, app_timer_t * timer) +{ + + os_mutex_lock(&ctx->mutex); + app_timer_t * t = ctx->g_app_timers; + app_timer_t * prev = NULL; + + timer->next = NULL; + timer->expiry = bh_get_tick_ms() + timer->interval; + + while (t) { + if (timer->expiry < t->expiry) { + if (prev == NULL) { + timer->next = ctx->g_app_timers; + ctx->g_app_timers = timer; + PRINT("rescheduled timer [%d] at head\n", timer->id); + } else { + timer->next = t; + prev->next = timer; + PRINT("rescheduled timer [%d] after [%d]\n", timer->id, prev->id); + } + + os_mutex_unlock(&ctx->mutex); + + // ensure the refresh_checker() is called out of the lock + if (prev == NULL && ctx->refresh_checker) + ctx->refresh_checker(ctx); + + return; + } else { + prev = t; + t = t->next; + } + } + + if (prev) { + // insert to the list end + prev->next = timer; + PRINT("rescheduled timer [%d] at end, after [%d]\n", timer->id, prev->id); + } else { + // insert at the begin + bh_assert(ctx->g_app_timers == NULL); + ctx->g_app_timers = timer; + PRINT("rescheduled timer [%d] as first\n", timer->id); + } + + os_mutex_unlock(&ctx->mutex); + + // ensure the refresh_checker() is called out of the lock + if (prev == NULL && ctx->refresh_checker) + ctx->refresh_checker(ctx); + +} + +static void release_timer(timer_ctx_t ctx, app_timer_t * t) +{ + if (ctx->pre_allocated) { + os_mutex_lock(&ctx->mutex); + t->next = ctx->free_timers; + ctx->free_timers = t; + PRINT("recycle timer :%d\n", t->id); + os_mutex_unlock(&ctx->mutex); + } else { + PRINT("destroy timer :%d\n", t->id); + BH_FREE(t); + } +} + +void release_timer_list(app_timer_t ** p_list) +{ + app_timer_t *t = *p_list; + while (t) { + app_timer_t *next = t->next; + PRINT("destroy timer list:%d\n", t->id); + BH_FREE(t); + t = next; + } + + *p_list = NULL; +} + +/* + * + * API exposed + * + */ + +timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, + check_timer_expiry_f expiery_checker, int prealloc_num, + unsigned int owner) +{ + timer_ctx_t ctx = (timer_ctx_t) BH_MALLOC(sizeof(struct _timer_ctx)); + if (ctx == NULL) + return NULL; + memset(ctx, 0, sizeof(struct _timer_ctx)); + + ctx->timer_callback = timer_handler; + ctx->pre_allocated = prealloc_num; + ctx->refresh_checker = expiery_checker; + ctx->owner = owner; + + while (prealloc_num > 0) { + app_timer_t *timer = (app_timer_t*) BH_MALLOC(sizeof(app_timer_t)); + if (timer == NULL) + goto cleanup; + + memset(timer, 0, sizeof(*timer)); + timer->next = ctx->free_timers; + ctx->free_timers = timer; + prealloc_num--; + } + + os_cond_init(&ctx->cond); + os_mutex_init(&ctx->mutex); + + PRINT("timer ctx created. pre-alloc: %d\n", ctx->pre_allocated); + + return ctx; + +cleanup: + + if (ctx) { + release_timer_list(&ctx->free_timers); + BH_FREE(ctx); + } + PRINT("timer ctx create failed\n"); + return NULL; +} + +void destroy_timer_ctx(timer_ctx_t ctx) +{ + while (ctx->free_timers) { + void * tmp = ctx->free_timers; + ctx->free_timers = ctx->free_timers->next; + BH_FREE(tmp); + } + + cleanup_app_timers(ctx); + + os_cond_destroy(&ctx->cond); + os_mutex_destroy(&ctx->mutex); + BH_FREE(ctx); +} + +unsigned int timer_ctx_get_owner(timer_ctx_t ctx) +{ + return ctx->owner; +} + +void add_idle_timer(timer_ctx_t ctx, app_timer_t * timer) +{ + os_mutex_lock(&ctx->mutex); + timer->next = ctx->idle_timers; + ctx->idle_timers = timer; + os_mutex_unlock(&ctx->mutex); +} + +uint32 sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, + bool auto_start) +{ + + app_timer_t *timer; + + if (ctx->pre_allocated) { + if (ctx->free_timers == NULL) + return (uint32)-1; + else { + timer = ctx->free_timers; + ctx->free_timers = timer->next; + } + } else { + timer = (app_timer_t*) BH_MALLOC(sizeof(app_timer_t)); + if (timer == NULL) + return (uint32)-1; + } + + memset(timer, 0, sizeof(*timer)); + + ctx->g_max_id++; + if (ctx->g_max_id == (uint32)-1) + ctx->g_max_id++; + timer->id = ctx->g_max_id; + timer->interval = (uint32)interval; + timer->is_periodic = is_period; + + if (auto_start) + reschedule_timer(ctx, timer); + else + add_idle_timer(ctx, timer); + + return timer->id; +} + +bool sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id) +{ + bool from_active; + app_timer_t * t = remove_timer(ctx, timer_id, &from_active); + if (t == NULL) + return false; + + add_idle_timer(ctx, t); + + PRINT("sys_timer_stop called\n"); + return from_active; +} + +bool sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id) +{ + bool from_active; + app_timer_t * t = remove_timer(ctx, timer_id, &from_active); + if (t == NULL) + return false; + + release_timer(ctx, t); + + PRINT("sys_timer_destroy called\n"); + return true; +} + +bool sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval) +{ + app_timer_t * t = remove_timer(ctx, timer_id, NULL); + if (t == NULL) + return false; + + if (interval > 0) + t->interval = (uint32)interval; + + reschedule_timer(ctx, t); + + PRINT("sys_timer_restart called\n"); + return true; +} + +/* + * + * + * API called by the timer manager from another thread or the kernel timer handler + * + * + */ + +// lookup the app queue by the module name +//post a timeout message to the app queue +// +static void handle_expired_timers(timer_ctx_t ctx, app_timer_t * expired) +{ + while (expired) { + app_timer_t * t = expired; + ctx->timer_callback(t->id, ctx->owner); + + expired = expired->next; + if (t->is_periodic) { + // if it is repeating, then reschedule it; + reschedule_timer(ctx, t); + + } else { + // else move it to idle list + add_idle_timer(ctx, t); + } + } +} + +int get_expiry_ms(timer_ctx_t ctx) +{ + int ms_to_next_expiry; + uint64 now = bh_get_tick_ms(); + + os_mutex_lock(&ctx->mutex); + if (ctx->g_app_timers == NULL) + ms_to_next_expiry = 7 * 24 * 60 * 60 * 1000; // 1 week + else if (ctx->g_app_timers->expiry >= now) + ms_to_next_expiry = (int)(ctx->g_app_timers->expiry - now); + else + ms_to_next_expiry = 0; + os_mutex_unlock(&ctx->mutex); + + return ms_to_next_expiry; +} + +int check_app_timers(timer_ctx_t ctx) +{ + os_mutex_lock(&ctx->mutex); + + app_timer_t * t = ctx->g_app_timers; + app_timer_t * expired = NULL; + + uint64 now = bh_get_tick_ms(); + + while (t) { + if (now >= t->expiry) { + ctx->g_app_timers = t->next; + + t->next = expired; + expired = t; + + t = ctx->g_app_timers; + } else { + break; + } + } + os_mutex_unlock(&ctx->mutex); + + handle_expired_timers(ctx, expired); + + return get_expiry_ms(ctx); +} + +void cleanup_app_timers(timer_ctx_t ctx) +{ + os_mutex_lock(&ctx->mutex); + + release_timer_list(&ctx->g_app_timers); + release_timer_list(&ctx->idle_timers); + + os_mutex_unlock(&ctx->mutex); +} + diff --git a/wamr/core/shared/utils/runtime_timer.h b/wamr/core/shared/utils/runtime_timer.h new file mode 100644 index 0000000..8e8cc59 --- /dev/null +++ b/wamr/core/shared/utils/runtime_timer.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef LIB_BASE_RUNTIME_TIMER_H_ +#define LIB_BASE_RUNTIME_TIMER_H_ + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint64 bh_get_tick_ms(); +uint32 bh_get_elpased_ms(uint32 *last_system_clock); + +struct _timer_ctx; +typedef struct _timer_ctx * timer_ctx_t; +typedef void (*timer_callback_f)(unsigned int id, unsigned int owner); +typedef void (*check_timer_expiry_f)(timer_ctx_t ctx); + +timer_ctx_t create_timer_ctx(timer_callback_f timer_handler, + check_timer_expiry_f, int prealloc_num, + unsigned int owner); +void destroy_timer_ctx(timer_ctx_t); +unsigned int timer_ctx_get_owner(timer_ctx_t ctx); + +uint32 sys_create_timer(timer_ctx_t ctx, int interval, bool is_period, + bool auto_start); +bool sys_timer_destroy(timer_ctx_t ctx, uint32 timer_id); +bool sys_timer_cancel(timer_ctx_t ctx, uint32 timer_id); +bool sys_timer_restart(timer_ctx_t ctx, uint32 timer_id, int interval); +void cleanup_app_timers(timer_ctx_t ctx); +int check_app_timers(timer_ctx_t ctx); +int get_expiry_ms(timer_ctx_t ctx); + +#ifdef __cplusplus +} +#endif +#endif /* LIB_BASE_RUNTIME_TIMER_H_ */ diff --git a/wamr/core/shared/utils/shared_utils.cmake b/wamr/core/shared/utils/shared_utils.cmake new file mode 100644 index 0000000..5b7d02d --- /dev/null +++ b/wamr/core/shared/utils/shared_utils.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (UTILS_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${UTILS_SHARED_DIR}) + +file (GLOB source_all ${UTILS_SHARED_DIR}/*.c) + +set (UTILS_SHARED_SOURCE ${source_all}) + +LIST (APPEND RUNTIME_LIB_HEADER_LIST "${UTILS_SHARED_DIR}/runtime_timer.h") diff --git a/wamr/core/shared/utils/uncommon/bh_read_file.c b/wamr/core/shared/utils/uncommon/bh_read_file.c new file mode 100644 index 0000000..e13b388 --- /dev/null +++ b/wamr/core/shared/utils/uncommon/bh_read_file.c @@ -0,0 +1,104 @@ +#include "bh_read_file.h" + +#include +#include +#if defined(_WIN32) || defined(_WIN32_) +#include +#else +#include +#endif + +#if defined(_WIN32) || defined(_WIN32_) +char* +bh_read_file_to_buffer(const char *filename, uint32 *ret_size) +{ + char *buffer; + int file; + uint32 file_size, read_size; + struct stat stat_buf; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if (_sopen_s(&file, filename, _O_RDONLY| _O_BINARY, _SH_DENYNO, 0)) { + printf("Read file to buffer failed: open file %s failed.\n", + filename); + return NULL; + } + + if (fstat(file, &stat_buf) != 0) { + printf("Read file to buffer failed: fstat file %s failed.\n", + filename); + _close(file); + return NULL; + } + file_size = (uint32)stat_buf.st_size; + + if (!(buffer = (char *)BH_MALLOC(file_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + _close(file); + return NULL; + } + + read_size = _read(file, buffer, file_size); + _close(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + BH_FREE(buffer); + return NULL; + } + + *ret_size = file_size; + return buffer; +} +#else /* else of defined(_WIN32) || defined(_WIN32_) */ +char* +bh_read_file_to_buffer(const char *filename, uint32 *ret_size) +{ + char *buffer; + int file; + uint32 file_size, read_size; + struct stat stat_buf; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if ((file = open(filename, O_RDONLY, 0)) == -1) { + printf("Read file to buffer failed: open file %s failed.\n", + filename); + return NULL; + } + + if (fstat(file, &stat_buf) != 0) { + printf("Read file to buffer failed: fstat file %s failed.\n", + filename); + close(file); + return NULL; + } + + file_size = (uint32)stat_buf.st_size; + + if (!(buffer = BH_MALLOC(file_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + close(file); + return NULL; + } + + read_size = (uint32)read(file, buffer, file_size); + close(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + BH_FREE(buffer); + return NULL; + } + + *ret_size = file_size; + return buffer; +} +#endif /* end of defined(_WIN32) || defined(_WIN32_) */ diff --git a/wamr/core/shared/utils/uncommon/bh_read_file.h b/wamr/core/shared/utils/uncommon/bh_read_file.h new file mode 100644 index 0000000..7c947d6 --- /dev/null +++ b/wamr/core/shared/utils/uncommon/bh_read_file.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _BH_FILE_H +#define _BH_FILE_H + +#include "bh_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +char * +bh_read_file_to_buffer(const char *filename, uint32 *ret_size); + +#ifdef __cplusplus +} +#endif + +#endif /* end of _BH_FILE_H */ + diff --git a/wamr/core/shared/utils/uncommon/shared_uncommon.cmake b/wamr/core/shared/utils/uncommon/shared_uncommon.cmake new file mode 100644 index 0000000..0a15b87 --- /dev/null +++ b/wamr/core/shared/utils/uncommon/shared_uncommon.cmake @@ -0,0 +1,11 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +set (UNCOMMON_SHARED_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${UNCOMMON_SHARED_DIR}) + +file (GLOB_RECURSE source_all ${UNCOMMON_SHARED_DIR}/*.c) + +set (UNCOMMON_SHARED_SOURCE ${source_all}) + diff --git a/wamr/doc/build_wamr.md b/wamr/doc/build_wamr.md new file mode 100644 index 0000000..c0b36f2 --- /dev/null +++ b/wamr/doc/build_wamr.md @@ -0,0 +1,321 @@ + +Build WAMR vmcore (iwasm) +========================= +It is recommended to use the [WAMR SDK](../wamr-sdk) tools to build a project that integrates the WAMR. This document introduces how to build the WAMR minimal product which is vmcore only (no app-framework and app-mgr) for multiple platforms. + +## WAMR vmcore cmake building configurations + +By including the script `runtime_lib.cmake` under folder [build-scripts](../build-scripts) in CMakeList.txt, it is easy to build minimal product with cmake. + +```cmake +# add this into your CMakeList.txt +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +``` + +The script `runtime_lib.cmake` defines a number of variables for configuring the WAMR runtime features. You can set these variables in your CMakeList.txt or pass the configurations from cmake command line. + +#### **Configure platform and architecture** + +- **WAMR_BUILD_PLATFORM**: set the target platform. It can be set to any platform name (folder name) under folder [core/shared/platform](../core/shared/platform). + +- **WAMR_BUILD_TARGET**: set the target CPU architecture. Current supported targets are: X86_64, X86_32, AArch64, ARM, THUMB, XTENSA and MIPS. For AArch64, ARM and THUMB, the format is \\[\]\[_VFP] where \ is the ARM sub-architecture and the "_VFP" suffix means VFP coprocessor registers s0-s15 (d0-d7) are used for passing arguments or returning results in standard procedure-call. Both \ and "_VFP" are optional, e.g. AARCH64, AARCH64V8, AARCHV8.1, ARMV7, ARMV7_VFP, THUMBV7, THUMBV7_VFP and so on. + +```bash +cmake -DWAMR_BUILD_PLATFORM=linux -DWAMR_BUILD_TARGET=ARM +``` + +#### **Configure interpreter** + +- **WAMR_BUILD_INTERP**=1/0: enable or disable WASM interpreter + +- **WAMR_BUILD_FAST_INTERP**=1/0:build fast (default) or classic WASM interpreter. + + NOTE: the fast interpreter runs ~2X faster than classic interpreter, but consumes about 2X memory to hold the WASM bytecode code. + +#### **Configure AoT and JIT** + +- **WAMR_BUILD_AOT**=1/0, default to enable if not set +- **WAMR_BUILD_JIT**=1/0 , default to disable if not set + +#### **Configure LIBC** + +- **WAMR_BUILD_LIBC_BUILTIN**=1/0, default to enable if not set + +- **WAMR_BUILD_LIBC_WASI**=1/0, default to enable if not set + +#### **Enable Multi-Module feature** + +- **WAMR_BUILD_MULTI_MODULE**=1/0, default to disable if not set + +#### **Enable WASM mini loader** + +- **WAMR_BUILD_MINI_LOADER**=1/0, default to disable if not set + +> Note: the mini loader doesn't check the integrity of the WASM binary file, developer must ensure that the WASM file is well-formed. + +#### **Enable shared memory feature** +- **WAMR_BUILD_SHARED_MEMORY**=1/0, default to disable if not set + +#### **Enable thread manager** +- **WAMR_BUILD_THREAD_MGR**=1/0, default to disable if not set + +#### **Enable lib-pthread** +- **WAMR_BUILD_LIB_PTHREAD**=1/0, default to disable if not set +> Note: The dependent feature of lib pthread such as the `shared memory` and `thread manager` will be enabled automatically. + +#### **Disable boundary check with hardware trap in AOT or JIT mode** +- **WAMR_DISABLE_HW_BOUND_CHECK**=1/0, default to enable if not set and supported by platform +> Note: by default only platform linux/darwin/android/vxworks 64-bit will enable boundary check with hardware trap in AOT or JIT mode, and the wamrc tool will generate AOT code without boundary check instructions in all 64-bit targets except SGX to improve performance. + +#### **Set maximum app thread stack size** +- **WAMR_APP_THREAD_STACK_SIZE_MAX**=n, default to 8 MB (8388608) if not set +> Note: the AOT boundary check with hardware trap mechanism might consume large stack since the OS may lazily grow the stack mapping as a guard page is hit, we may use this configuration to reduce the total stack usage, e.g. -DWAMR_APP_THREAD_STACK_SIZE_MAX=131072 (128 KB). + +**Combination of configurations:** + +We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: + +``` Bash +cmake .. -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_PLATFORM=linux +``` + +Or if we want to enable interpreter, disable AOT and WASI, and build as X86_32, we can run command: + +``` Bash +cmake .. -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_AOT=0 -DWAMR_BUILD_LIBC_WASI=0 -DWAMR_BUILD_TARGET=X86_32 +``` + +## Cross compilation + +If you are building for ARM architecture on a X86 development machine, you can use the `CMAKE_TOOLCHAIN_FILE` to set the toolchain file for cross compling. + +``` +cmake .. -DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE \ + -DWAMR_BUILD_PLATFORM=linux \ + -DWAMR_BUILD_TARGET=ARM +``` + +Refer to toolchain sample file [`samples/simple/profiles/arm-interp/toolchain.cmake`](../samples/simple/profiles/arm-interp/toolchain.cmake) for how to build mini product for ARM target architecture. + +Linux +------------------------- +First of all please install the dependent packages. +Run command below in Ubuntu-18.04: + +``` Bash +sudo apt install build-essential cmake g++-multilib libgcc-8-dev lib32gcc-8-dev +``` +Or in Ubuntu-16.04: +``` Bash +sudo apt install build-essential cmake g++-multilib libgcc-5-dev lib32gcc-5-dev +``` +Or in Fedora: +``` Bash +sudo dnf install glibc-devel.i686 +``` + +After installing dependencies, build the source code: +``` Bash +cd product-mini/platforms/linux/ +mkdir build +cd build +cmake .. +make +``` + + +By default in Linux, the interpreter, AOT and WASI are enabled, and JIT is disabled. And the build target is +set to X86_64 or X86_32 depending on the platform's bitwidth. + +To enable WASM JIT, firstly we should build LLVM: + +``` Bash +cd product-mini/platforms/linux/ +./build_llvm.sh (The llvm source code is cloned under /core/deps/llvm and auto built) +``` + +Then pass argument `-DWAMR_BUILD_JIT=1` to cmake to enable WASM JIT: + +``` Bash +mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +make +``` + +Linux SGX (Intel Software Guard Extension) +------------------------- + +Please see [Build and Port WAMR vmcore for Linux SGX](./linux_sgx.md) for the details. + +MacOS +------------------------- + +Make sure to install Xcode from App Store firstly, and install cmake. + +If you use Homebrew, install cmake from the command line: +``` Bash +brew install cmake +``` + +Then build the source codes: +``` +cd product-mini/platforms/darwin/ +mkdir build +cd build +cmake .. +make +``` +Note: +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](./build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in MacOS, interpreter, AoT, and builtin libc are enabled by default. + +VxWorks +------------------------- +VxWorks 7 SR0620 release is validated. + +First you need to build a VSB. Make sure *UTILS_UNIX* layer is added in the VSB. +After the VSB is built, export the VxWorks toolchain path by: +```bash +export /host/vx-compiler/bin:$PATH +``` +Now switch to iwasm source tree to build the source code: +```bash +cd product-mini/platforms/vxworks/ +mkdir build +cd build +cmake .. +make +``` +Create a VIP based on the VSB. Make sure the following components are added: +* INCLUDE_POSIX_PTHREADS +* INCLUDE_POSIX_PTHREAD_SCHEDULER +* INCLUDE_SHARED_DATA +* INCLUDE_SHL + +Copy the generated iwasm executable, the test WASM binary as well as the needed +shared libraries (libc.so.1, libllvm.so.1 or libgnu.so.1 depending on the VSB, +libunix.so.1) to a supported file system (eg: romfs). + +Note: +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](./build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in VxWorks, interpreter and builtin libc are enabled by default. + +Zephyr +------------------------- +You need to download the Zephyr source code first and embed WAMR into it. +``` Bash +git clone https://github.com/zephyrproject-rtos/zephyr.git +cd zephyr/samples/ +cp -a /product-mini/platforms/zephyr/simple . +cd simple +ln -s wamr +source ../../zephyr-env.sh +# Execute the ./build_and_run.sh script with board name as parameter. Here take x86 as example: +./build_and_run.sh x86 + +``` + +Note: +WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](./build_wamr.md#wamr-vmcore-cmake-building-configurations) for details. Currently in Zephyr, interpreter, AoT and builtin libc are enabled by default. + + +AliOS-Things +------------------------- +1. a developerkit board id needed for testing +2. download the AliOS-Things code + ``` Bash + git clone https://github.com/alibaba/AliOS-Things.git + ``` +3. copy /product-mini/platforms/alios-things directory to AliOS-Things/middleware, and rename it as iwasm + ``` Bash + cp -a /product-mini/platforms/alios-things middleware/iwasm + ``` +4. create a link to in middleware/iwasm/ and rename it to wamr + ``` Bash + ln -s middleware/iwasm/wamr + ``` +5. modify file app/example/helloworld/helloworld.c, patch as: + ``` C + #include + #include + extern bool iwasm_init(); + int application_start(int argc, char *argv[]) + { + int count = 0; + iwasm_init(); + ... + } + ``` +6. modify file app/example/helloworld/aos.mk + ``` C + $(NAME)_COMPONENTS := osal_aos iwasm + ``` +7. build source code and run + For linux host: + + ``` Bash + aos make helloworld@linuxhost -c config + aos make + ./out/helloworld@linuxhost/binary/helloworld@linuxhost.elf + ``` + + For developerkit: + Modify file middleware/iwasm/aos.mk, patch as: + + ``` C + WAMR_BUILD_TARGET := THUMBV7M + ``` + + ``` Bash + aos make helloworld@developerkit -c config + aos make + ``` + download the binary to developerkit board, check the output from serial port + +Android +------------------------- +able to generate a shared library support Android platform. +- need an [android SDK](https://developer.android.com/studio). Go and get the "Command line tools only" +- look for a command named *sdkmanager* and download below components. version numbers might need to check and pick others + - "build-tools;29.0.3" + - "cmake;3.10.2.4988404" + - "ndk;21.0.6113669" + - "patcher;v4" + - "platform-tools" + - "platforms;android-29" +- add bin/ of the downloaded cmake to $PATH +- export ANDROID_SDK_HOME=/the/path/of/downloaded/sdk/ +- export ANDROID_NDK_HOME=/the/path/of/downloaded/sdk/ndk/ +- ready to go + +Use such commands, you are able to compile with default configurations. Any compiling requirement should be satisfied by modifying product-mini/platforms/android/CMakeList.txt. For example, chaning ${WAMR_BUILD_TARGET} in CMakeList could get different libraries support different ABIs. + +``` shell +$ cd product-mini/platforms/android/ +$ mkdir build +$ cd build +$ cmake .. +$ make +$ # check output in distribution/wasm +$ # include/ includes all necesary head files +$ # lib includes libiwasm.so +``` + + +Docker +------------------------- +[Docker](https://www.docker.com/) will download all the dependencies and build WAMR Core on your behalf. + +Make sure you have Docker installed on your machine: [macOS](https://docs.docker.com/docker-for-mac/install/), [Windows](https://docs.docker.com/docker-for-windows/install/) or [Linux](https://docs.docker.com/install/linux/docker-ce/ubuntu/). + +Build the Docker image: + +``` Bash +docker build --rm -f "Dockerfile" -t wamr:latest . +``` +Run the image in interactive mode: +``` Bash +docker run --rm -it wamr:latest +``` +You'll now enter the container at `/root`. + diff --git a/wamr/doc/build_wasm_app.md b/wamr/doc/build_wasm_app.md new file mode 100644 index 0000000..b110de3 --- /dev/null +++ b/wamr/doc/build_wasm_app.md @@ -0,0 +1,171 @@ + + +# Prepare WASM building environments + +WASI-SDK version 8.0+ is the major tool supported by WAMR to build WASM applications. There are some other WASM compilers such as the standard clang compiler and Emscripten might also work [here](./other_wasm_compilers.md). + +Install WASI SDK: Download the [wasi-sdk](https://github.com/CraneStation/wasi-sdk/releases) and extract the archive to default path `/opt/wasi-sdk` + + +Build WASM applications +========================= + +You can write a simple ```test.c``` as the first sample. + +``` C +#include +#include + +int main(int argc, char **argv) +{ + char *buf; + + printf("Hello world!\n"); + + buf = malloc(1024); + if (!buf) { + printf("malloc buf failed\n"); + return -1; + } + + printf("buf ptr: %p\n", buf); + + sprintf(buf, "%s", "1234\n"); + printf("buf: %s", buf); + + free(buf); + return 0; +} +``` + +To build the source file to WASM bytecode, we can input the following command: + +``` Bash +/opt/wasi-sdk/bin/clang -O3 -o test.wasm test.c +``` + +There are some useful options which can be specified to build the source code: + +- **-nostdlib** Do not use the standard system startup files or libraries when linking. In this mode, the libc-builtin library of WAMR must be built to run the wasm app, otherwise, the libc-wasi library must be built. You can specify **-DWAMR_BUILD_LIBC_BUILTIN** or **-DWAMR_BUILD_LIBC_WASI** for cmake to build WAMR with libc-builtin support or libc-wasi support. + +- **-Wl,--no-entry** Do not output any entry point + +- **-Wl,--export=** Force a symbol to be exported, e.g. **-Wl,--export=main** to export main function + +- **-Wl,--export-all** Export all symbols (normally combined with --no-gc-sections) + +- **-Wl,--initial-memory=** Initial size of the linear memory, which must be a multiple of 65536 + +- **-Wl,--max-memory=** Maximum size of the linear memory, which must be a multiple of 65536 + +- **-z stack-size=** The auxiliary stack size, which is an area of linear memory, and must be smaller than initial memory size. + +- **-Wl,--strip-all** Strip all symbols + +- **-Wl,--shared-memory** Use shared linear memory + +- **-Wl,--threads** or **-Wl,--no-threads** Run or do not run the linker multi-threaded + +- **-Wl,--allow-undefined** Allow undefined symbols in linked binary + +- **-Wl,--allow-undefined-file=** Allow symbols listed in to be undefined in linked binary + +For example, we can build the wasm app with command: +``` Bash +/opt/wasi-sdk/bin/clang -O3 -nostdlib \ + -z stack-size=8192 -Wl,--initial-memory=65536 \ + -Wl,--export=main -o test.wasm test.c \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--no-entry -Wl,--strip-all -Wl,--allow-undefined +``` +to generate a wasm binary with small footprint. + +# Build a project with cmake + +If you have complex WASM application project which contains dozens of source files, you can consider using cmake for project building. + +You can cross compile your project by using the toolchain provided by WAMR. + +We can generate a `CMakeLists.txt` file for `test.c`: + +``` cmake +cmake_minimum_required (VERSION 3.5) +project(hello_world) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--export=main") +add_executable(hello_world test.c) +``` + +It is simple to build this project by cmake: + +``` Bash +mkdir build && cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=$WAMR_ROOT/wamr-sdk/app/wamr_toolchain.cmake +make +``` + +You will get ```hello_world``` which is the WASM app binary. + +> Note: If you have already built a SDK profile, then the **DCMAKE_TOOLCHAIN_FILE** should be changed into `$WAMR_ROOT/wamr-sdk/out/${PROFILE}/app-sdk/wamr_toolchain.cmake` + + +# Compile WASM to AoT module + +Please ensure the wamrc was already generated and available in your shell PATH. Then we can use wamrc to compile WASM app binary to WAMR AoT binary. + +``` Bash +wamrc -o test.aot test.wasm +``` + +wamrc supports a number of compilation options through the command line arguments: + +``` Bash +wamrc --help +Usage: wamrc [options] -o output_file wasm_file + --target= Set the target arch, which has the general format: + = x86_64, i386, aarch64, arm, thumb, xtensa, mips. + Default is host arch, e.g. x86_64 + = for ex. on arm or thumb: v5, v6m, v7a, v7m, etc. + Use --target=help to list supported targets + --target-abi= Set the target ABI, e.g. gnu, eabi, gnueabihf, etc. (default: gnu) + Use --target-abi=help to list all the ABI supported + --cpu= Set the target CPU (default: host CPU, e.g. skylake) + Use --cpu=help to list all the CPU supported + --cpu-features= Enable or disable the CPU features + Use +feature to enable a feature, or -feature to disable it + For example, --cpu-features=+feature1,-feature2 + Use --cpu-features=+help to list all the features supported + --opt-level=n Set the optimization level (0 to 3, default: 3, which is fastest) + --size-level=n Set the code size level (0 to 3, default: 3, which is smallest) + -sgx Generate code for SGX platform (Intel Software Guard Extention) + --format= Specifies the format of the output file + The format supported: + aot (default) AoT file + object Native object file + llvmir-unopt Unoptimized LLVM IR + llvmir-opt Optimized LLVM IR +Examples: wamrc -o test.aot test.wasm + wamrc --target=i386 -o test.aot test.wasm + wamrc --target=i386 --format=object -o test.o test.wasm + +``` + + +Run WASM app in WAMR mini product build +======================== + +Run the test.wasm or test.aot with WAMR mini product build: +``` Bash +./iwasm test.wasm or +./iwasm test.aot +``` +You will get the following output: +``` +Hello world! +buf ptr: 0xffffc2c8 +buf: 1234 +``` +If you would like to run the test app on Zephyr, we have embedded a test sample into its OS image. You will need to execute: +``` +ninja run +``` diff --git a/wamr/doc/embed_wamr.md b/wamr/doc/embed_wamr.md new file mode 100644 index 0000000..d0c8e36 --- /dev/null +++ b/wamr/doc/embed_wamr.md @@ -0,0 +1,312 @@ +Embedding WAMR guideline +===================================== + + +**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 runtime initialization + +``` C + char *buffer, error_buf[128]; + wasm_module_t module; + wasm_module_inst_t module_inst; + wasm_function_inst_t func; + wasm_exec_env_t exec_env; + uint32 size, stack_size = 8092, heap_size = 8092; + + /* initialize the wasm runtime by default configurations */ + wasm_runtime_init(); + + /* read WASM file into a memory buffer */ + buffer = read_wasm_binary_to_buffer(…, &size); + + /* add line below if we want to export native functions to WASM app */ + wasm_runtime_register_natives(...); + + /* parse the WASM file from buffer and create a WASM module */ + module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); + + /* 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)); +``` + +The `wasm_runtime_init()` uses the default memory allocator os_malloc/os_free function from the [`core/shared/platform`](../core/shared/platform) for the runtime memory management. + +WAMR supports to restrict its all memory allocations in a raw buffer. It ensures the dynamic memories used by the WASM applications won't harm the system availability, which is extremely important for embedded systems. This can be done by using `wasm_runtime_full_init()`. This function also allows you to configure the native API's for exporting to WASM app, and set the maximum thread number when multi-thread feature is enabled. + +Refer to the following sample: + +```c +/* the native functions that will be exported to WASM app */ +static NativeSymbol native_symbols[] = { + EXPORT_WASM_API_WITH_SIG(display_input_read, "(*)i"), + EXPORT_WASM_API_WITH_SIG(display_flush, "(iiii*)") +}; + +/* all the runtime memory allocations are retricted in the global_heap_buf array */ +static char global_heap_buf[512 * 1024]; +RuntimeInitArgs init_args; +memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +/* configure the memory allocator for the runtime */ +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); + +/* configure the native functions being exported to WASM app */ +init_args.native_module_name = "env"; +init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); +init_args.native_symbols = native_symbols; + +/* set maximum thread number if needed when multi-thread is enabled, + the default value is 4 */ +init_args.max_thread_num = max_thread_num; + +/* initialize runtime environment with user configurations*/ +if (!wasm_runtime_full_init(&init_args)) { + return -1; +} +``` + +## Native calls WASM functions and passes parameters + +After a module is instantiated, the runtime embedder can lookup the target WASM function by name, and create execution environment to call the function. + +```c + /* lookup a WASM function by its name + The function signature can NULL here */ + func = wasm_runtime_lookup_function(module_inst, "fib", NULL); + + /* creat an execution environment to execute the WASM functions */ + exec_env = wasm_runtime_create_exec_env(module_inst, stack_size); +``` + +There are several ways to call WASM function: + +1. Function call with parameters in an array of 32 bits elements and size: + +```c + unit32 argv[2]; + + /* arguments are always transferred in 32-bit element */ + argv[0] = 8; + + /* 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 { + /* exception is thrown if call fails */ + 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-byte parameters rather than + * *(double*)(&argv[2]) = arg3 here because some archs + * like ARM, MIPS require the address must be 8-byte aligned. + * Or use the aligned malloc or compiler align attribute + * to ensure the array address is 8-byte 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)); +``` + +2. Function call with results and arguments both in `wasm_val_t` struct and size: + +```c + unit32 num_args = 1, num_results = 1; + wasm_val_t args[1], results[1]; + + /* set the argument type and value */ + args[0].kind = WASM_I32; + args[0].of.i32 = 8; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_a(exec_env, func, num_results, results, num_args, args)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + +3. Function call with variant argument support: + +```c + unit32 num_args = 1, num_results = 1; + wasm_val_t results[1]; + + /* call the WASM function */ + if (wasm_runtime_call_wasm_v(exec_env, func, 1, results, 1, 8)) { + /* the return value is stored in results */ + printf("fib function return: %d\n", results[0].of.i32); + } + else { + /* exception is thrown if call fails */ + printf("%s\n", wasm_runtime_get_exception(module_inst)); + } +``` + +## 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 +/** + * 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 + */ +uint32_t +wasm_runtime_module_malloc(wasm_module_inst_t module_inst, + uint32_t size, void **p_native_addr); + +/** + * 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 + */ +uint32_t +wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst, + const char *src, uint32_t size); + +/* free the memory allocated from module memory space */ +void +wasm_runtime_module_free(wasm_module_inst_t module_inst, uint32_t ptr); +``` + +Usage sample: + +```c +char * buffer = NULL; +uint32_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); + + /* it is runtime embedder's responsibility to release the memory, + unless the WASM app will free the passed pointer in its code */ + wasm_runtime_module_free(module_inst, buffer_for_wasm); +} +``` + +## 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. + +## Execute wasm functions in multiple threads + +The `exec_env` is not thread safety, it will cause unexpected behavior if the same `exec_env` is used in multiple threads. However, we've provided two ways to execute wasm functions concurrently: + +- You can use `pthread` APIs in your wasm application, see [pthread library](./pthread_library.md) for more details. + +- The `spawn exec_env` and `spawn thread` APIs are available, you can use these APIs to manage the threads in native: + + *spawn exec_env:* + + `spawn exec_env` API spawns a `new_exec_env` base on the original `exec_env`, use can use it in other threads: + + ```C + new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + + /* Then you can use new_exec_env in your new thread */ + module_inst = wasm_runtime_get_module_inst(new_exec_env); + func_inst = wasm_runtime_lookup_function(module_inst, ...); + wasm_runtime_call_wasm(new_exec_env, func_inst, ...); + + /* you need to use this API to manually destroy the spawned exec_env */ + wasm_runtime_destroy_spawned_exec_env(new_exec_env); + ``` + + *spawn thread:* + + You can also use `spawn thread` API to avoid manually manage the spawned exec_env: + + ```C + wasm_thread_t wasm_tid; + void *wamr_thread_cb(wasm_exec_env_t exec_env, void *arg) + { + module_inst = wasm_runtime_get_module_inst(exec_env); + func_inst = wasm_runtime_lookup_function(module_inst, ...); + wasm_runtime_call_wasm(exec_env, func_inst, ...); + } + wasm_runtime_spawn_thread(exec_env, &wasm_tid, wamr_thread_cb, NULL); + /* Use wasm_runtime_join_thread to join the spawned thread */ + wasm_runtime_join_thread(wasm_tid, NULL); + ``` + +**Note1: You can manage the maximum number of threads can be created:** + +```C +init_args.max_thread_num = THREAD_NUM; +/* If this init argument is not set, the default maximum thread number is 4 */ +``` + +**Note2: The wasm application should be built with `--shared-memory` and `-pthread` enabled:** + +```bash + /opt/wasi-sdk/bin/clang -o test.wasm test.c -nostdlib -pthread \ + -Wl,--shared-memory,--max-memory=131072 \ + -Wl,--no-entry,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors,--export=${your_func_name} +``` + + **Note3: The pthread library feature should be enabled while building the runtime:** + + ```bash + cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 + ``` + +[Here](../samples/spawn-thread) is a sample to show how to use these APIs. + +## The deinitialization procedure + +``` + wasm_runtime_destroy_exec_env(exec_env); + wasm_runtime_deinstantiate(module_inst); + wasm_runtime_unload(module); + wasm_runtime_destroy(); + +``` + +## Native calling WASM function working flow + +![WAMR embed diagram](./pics/embed.PNG "WAMR embed architecture diagram") diff --git a/wamr/doc/export_native_api.md b/wamr/doc/export_native_api.md new file mode 100644 index 0000000..8b21d4a --- /dev/null +++ b/wamr/doc/export_native_api.md @@ -0,0 +1,245 @@ + +Export native API to WASM application +======================================================= + + + +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 + +Then we should define the native functions in runtime source tree for handling the calls from the WASM app. The native function can be any name, for example **foo_native** and **foo2** here: + +``` C +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 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 + } +}; + +// initialize the runtime before registering the native functions +wasm_runtime_init(); + +int n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); +if (!wasm_runtime_register_natives("env", + native_symbols, + n_native_symbols)) { + goto fail1; +} + +// natives registeration must be done before loading WASM modules +module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf)); + +``` + +**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 + +The signature can defined as NULL, then all function parameters are assumed as i32 data type. + +**Use EXPORT_WASM_API_WITH_SIG** + +The `NativeSymbol` element for `foo2 ` above 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, "($*~)") // wasm symbol name will be "foo2" +}; +``` + +​ + +## Call exported API in WASM application + +Now we can call the exported native API in wasm application like this: +``` C +#include +#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]; + + int c = foo(a, b); // call into native foo_native() + foo2(msg, buffer, sizeof(buffer)); // call into native foo2() + + return 0; +} +``` + + + +## 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 +// +// If the function signature used i32 data type ("i") +// for buffer address or string parameters, here +// is how to do address conversation and boundary check manually +// +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/wamr/doc/linux_sgx.md b/wamr/doc/linux_sgx.md new file mode 100644 index 0000000..b602dca --- /dev/null +++ b/wamr/doc/linux_sgx.md @@ -0,0 +1,197 @@ + +Build and Port WAMR vmcore (iwasm) for Linux SGX +================================================ + +Build WAMR vmcore (iwasm) for Linux SGX +--------------------------------------- + +First of all please install the [Intel SGX SDK](https://software.intel.com/en-us/sgx/sdk), v2.8 or later is required, and it is recommended to install the SDK to /opt/intel/sgxsdk. + +After installing the dependencies, build the source code: +``` Bash +source /environment +cd product-mini/platforms/linux-sgx/ +mkdir build +cd build +cmake .. +make +``` + +This builds two libraries required by SGX application: + - libvmlib.a for Enclave part + - libvmlib_untrusted.a for App part + +**Note:** WAMR provides some features which can be easily configured by passing options to cmake, please see [WAMR vmcore cmake building configurations](./build_wamr.md#wamr-vmcore-cmake-building-configurations) for the details. Currently in Linux SGX, fast interpreter, AOT, libc-builtin, libc-WASI and lib-pthread are enabled by default. + +Then build the enclave sample: +``` Bash +source /environment +cd enclave-sample +make +``` + +The binary file iwasm will be generated. To run the sample: + +``` Bash +source /environment +iwasm [-options] wasm_file [args...] +or: +iwasm [-options] aot_file [args...] +``` + +Port WAMR vmcore for Linux SGX +------------------------------ + +The enclave-sample creates a sample to embed wamr vmlib of Enclave part and App part to an SGX application. To port WAMR vmcore lib to SGX application, there are some steps to do: + +**Step 1: Add "sgx_wamr.edl" and "sgx_pthread.edl" into EDL file, e.g. Enclave.edl:** + +```bash +from "sgx_pthread.edl" import *; +from "sgx_wamr.edl" import *; +``` + +The sgx_wamr.edl is under ${WAMR_ROOT}/core/shared/platform/linux-sgx, so please **add it to the search path list** when generating Enclave_u.c and Enclave_t.c from Enclave.edl: + +```bash +@cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx +``` + +```bash +@cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx +``` + +**Step 2: Link libvmlib.a to Enclave part and link libvmlib_untrusted.a to App part:** + +```makefile +Enclave_Link_Flags := ... libvmlib.a ... +``` + +```makefile +App_Link_Flags := ... libvmlib_untrusted.a ... +``` + +**And link SGX pthread lib to Enclave part:** + +```makefile +Enclave_Link_Flags := ... -lsgx_pthread ... +``` + +**Step 3: Add WAMR folders and SGX SDK folders to Enclave include path:** + +```makefile +Enclave_Include_Paths := ... -I$(WAMR_ROOT)/core/iwasm/include \ + -I$(WAMR_ROOT)/core/shared/utils \ + -I$(WAMR_ROOT)/core/shared/platform/linux-sgx \ + -I$(SGX_SDK)/include \ + -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/stlport +``` + +**Step 4: Configure reserved memory and thread info in file Enclave config file (e.g. Enclave.config.xml) to support WAMR AOT and multi-thread, e.g:** + +```xml +0x400000 +1 +10 +``` + +**Step 5: To support log output and os_printf() function in Enclave, please implement an ocall_print function, e.g. in Enclave.edl, add:** + +```cpp +untrusted { + void ocall_print([in, string]const char* str); +}; +``` + +In App part, add: + +```cpp +void +ocall_print(const char* str) +{ + printf("%s", str); +} +``` + +And in Enclave part, set the print function: + +```cpp +#include "wasm_export.h" +#include "bh_platform.h" + +extern "C" { + typedef void (*os_print_function_t)(const char* message); + extern void os_set_print_function(os_print_function_t pf); + + void + enclave_print(const char *message) + { + ocall_print(message); + } +} + +// In the beginning of Enclave initialization, add: +os_set_print_function(enclave_print); +``` + +Embed WAMR vmcore in Linux SGX +------------------------------ + +Normally we can embed WAMR vmcore in Linux SGX by calling the vmcore exported API's, see [Embed WAMR guide](./embed_wamr.md) for the details. And the the ecall_iwasm_main() function in file Enclave.cpp of enclave-sample also provides sample to invoke wasm app main function with wasm file buffer: + +```cpp +void +ecall_iwasm_main(uint8_t *wasm_file_buf, uint32_t wasm_file_size); +``` + +The enclave-sample also wraps an ecall function to receive commands from App to Enclave, and handle the commands in Enclave by calling the related WAMR vmcore API. The commands and related API's are: + +```cpp +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ +}; +``` + +Others +------ + +- Please add "-sgx" option when generating AoT file for SGX platform, e.g.: + + ```bash + wamrc -sgx -o test.aot test.wasm + ``` + +- The default max heap size of Enclave is 16 MB, it might be not enough when executing some workloads, please modify it in Enclave/Enclave.config.xml with a larger size when exception was thrown: + + ```bash + Exception: fail to enlarge memory. + or + Exception: allocate memory failed. + ``` + + Enclave/Enclave.config.xml, default max heap size is 16 MB: + + ```xml + 0x1000000 + ``` + diff --git a/wamr/doc/multi_module.md b/wamr/doc/multi_module.md new file mode 100644 index 0000000..08e827e --- /dev/null +++ b/wamr/doc/multi_module.md @@ -0,0 +1,228 @@ +Multiple Modules as Dependencies +========================= + +It is allowed that one WASM module can *import* *functions*, *globals*, *memories* and *tables* from other modules as its dependencies, and also one module can *export* those entities for other modules to *access* and may *write*. + +WAMR loads all dependencies recursively according to the *import section* of a module. + +> Currently WAMR only implements the load-time dynamic linking. Please refer to [dynamic linking](https://webassembly.org/docs/dynamic-linking/) for more details. + +## Multi-Module Related APIs + +### Register a module + +``` c +bool +wasm_runtime_register_module(const char *module_name, + wasm_module_t module, + char *error_buf, + uint32_t error_buf_size); +``` + +It is used to register a *module* with a *module_name* to WASM runtime, especially for the root module, which is loaded by `wasm_runtime_load()` and doesn't have a chance to tell runtime its *module name*. + +Fot all the sub modules, WAMR will get their names and load the .wasm files from the filesystem or stream, so no need to register the sub modules again. + +### Find a registered module + +``` c +wasm_module_t +wasm_runtime_find_module_registered( + const char *module_name); +``` + +It is used to check if a module with a given *module_name* has been registered, if yes return the module. + +### Module reader and destroyer + +``` c +typedef bool (*module_reader)(const char *module_name, + uint8_t **p_buffer, + uint32_t *p_size); + +typedef void (*module_destroyer)(uint8_t *buffer, + uint32_t size); + +void +wasm_runtime_set_module_reader(const module_reader reader, + const module_destroyer destroyer); +``` + +WAMR hopes that the native host or embedding environment loads/unloads the module WASM files by themselves and only passes runtime the binary content without worrying filesystem or storage issues. `module_reader` and `module_destroyer` are two callbacks called when dynamic-loading/unloading the sub modules. Developers must implement the two callbacks by themselves. + +### Call function of sub module + +```c +wasm_function_inst_t +wasm_runtime_lookup_function(wasm_module_inst_t const module_inst, + const char *name, + const char *signature); +``` + +Multi-module allows to lookup the function of sub module and call it. There are two ways to indicate the function *name*: + +- parent function name only by default, used to lookup the function of parent module +- sub module name, function name of sub module and two $ symbols, e.g. `$sub_module_name$function_name`, used to lookup function of sub module + +## Example + +### WASM modules +Suppose we have three C files, *mA.c*, *mB.c* and *mC.c*. Each of them has some exported functions and import some from others except mA. + +Undefined symbols can be marked in the source code with the *import_name* clang attribute which means that they are expected to be undefined at static link time. Without the *import_module* clang attribute, undefined symbols will be marked from the *env* module. + +``` C +// mA.c +int A() { return 10; } +``` + +``` C +// mB.c +__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A(); +int B() { return 11; } +int call_A() { return A(); } +``` + +``` C +// mC.c +__attribute__((import_module("mA"))) __attribute__((import_name("A"))) extern int A(); +__attribute__((import_module("mB"))) __attribute__((import_name("B"))) extern int B(); +int C() { return 12; } +int call_A() { return A(); } +int call_B() { return B(); } +``` + +By default no undefined symbols are allowed in the final binary. The flag *--allow-undefined* results in a WebAssembly import being defined for each undefined symbol. It is then up to the runtime to provide such symbols. + +When building an executable, only the entry point (_start) and symbols with the *export_name* attribute exported by default. in addition, symbols can be exported via the linker command line using *--export*. + +In the example, another linked command option *--export-all* is used. + +> with more detail, please refer to [WebAssembly lld port][https://lld.llvm.org/WebAssembly.html] + +Here is an example how to compile a *.c* to a *.wasm* with clang. Since there is no *start* function, we use *--no-entry* option. + +``` shell +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mA.wasm mA.c +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mB.wasm mB.c +$ clang --target=wasm32 -nostdlib \ + -Wl,--no-entry,--allow-undefined,--export-all \ + -o mC.wasm mC.c +``` + +put *mA.wasm*, *mB.wasm* and *mC.wasm* in the directory *wasm-apps* + +``` shell +$ # copy mA.wasm, mB.wasm and mC.wasm into wasm-apps +$ tree wasm-apps/ +wasm-apps/ +├── mA.wasm +├── mB.wasm +└── mC.wasm +``` + +eventually, their *import relationships* will be like: + +![import relationships](./pics/multi_module_pic1.png) + +### libvmlib + +We need to enable *WAMR_BUILD_MULTI_MODULE* option when building WAMR vmlib. Please ref to [Build WAMR core](./build_wamr.md) for a thoughtful guide. + +### code + +After all above preparation, we can call some functions from native code with APIs + +first, create two callbacks to load WASM module files into memory and unload them later + +``` c +static bool +module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + // ... + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + // ... +} + +static void +module_destroyer_cb(uint8 *buffer, uint32 size) +{ + BH_FREE(buffer); +} +``` + +second, create a large buffer and tell WAMR malloc any resource only from this buffer later + +``` c +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +``` + +third, put all together + +``` c +int main() +{ + /* all malloc() only from the given buffer */ + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space; + init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space); + + /* initialize runtime environment */ + wasm_runtime_full_init(&init_args); + + /* set module reader and destroyer */ + wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb); + + /* load WASM byte buffer from WASM bin file */ + module_reader_cb("mC", &file_buf, &file_buf_size)); + + /* load mC and let WAMR load mA and mB */ + module = wasm_runtime_load(file_buf, file_buf_size, + error_buf, sizeof(error_buf)); + + /* instantiate the module */ + module_inst = + wasm_runtime_instantiate(module, stack_size, + heap_size, error_buf, sizeof(error_buf))); + + + printf("call \"C\", it will return 0xc:i32, ===> "); + wasm_application_execute_func(module_inst, "C", 0, &args[0]); + printf("call \"call_B\", it will return 0xb:i32, ===> "); + wasm_application_execute_func(module_inst, "call_B", 0, &args[0]); + printf("call \"call_A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "call_A", 0, &args[0]); + + /* call some functions of mB */ + printf("call \"mB.B\", it will return 0xb:i32, ===>"); + wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]); + printf("call \"mB.call_A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]); + + /* call some functions of mA */ + printf("call \"mA.A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]); + + // ... +} +``` + +> please refer to [main.c](../samples/multi_modules/src/main.c) + +The output of the main.c will like: + +``` shell +$ ./a.out + +call "C", it will return 0xc:i32, ===> 0xc:i32 +call "call_B", it will return 0xb:i32, ===> 0xb:i32 +call "call_A", it will return 0xa:i32, ===>0xa:i32 +call "mB.B", it will return 0xb:i32, ===>0xb:i32 +call "mB.call_A", it will return 0xa:i32, ===>0xa:i32 +call "mA.A", it will return 0xa:i32, ===>0xa:i32 + +``` diff --git a/wamr/doc/other_wasm_compilers.md b/wamr/doc/other_wasm_compilers.md new file mode 100644 index 0000000..ac1ac17 --- /dev/null +++ b/wamr/doc/other_wasm_compilers.md @@ -0,0 +1,115 @@ + + +## Use clang compiler + +The recommended method to build a WASM binary is to use clang compiler ```clang-8```. You can refer to [apt.llvm.org](https://apt.llvm.org) for the detailed instructions. Here are referenced steps to install clang-8 in Ubuntu 16.04 and Ubuntu 18.04. + +(1) Add source to your system source list from llvm website + +For Ubuntu 16.04, add the following lines to /etc/apt/sources.list: + +``` Bash +deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial main +deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial main +# 8 +deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main +deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main +# 9 +deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main +deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main +``` + +For Ubuntu 18.04, add the following lines to /etc/apt/sources.list: + +``` Bash +# i386 not available +deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic main +deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic main +# 8 +deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main +deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main +# 9 +deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main +deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-9 main +``` + +(2) Download and install clang-8 tool-chain using following commands: + +``` Bash +sudo wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - +# Fingerprint: 6084 F3CF 814B 57C1 CF12 EFD5 15CF 4D18 AF4F 7421 +sudo apt-get update +sudo apt-get install llvm-8 lld-8 clang-8 +``` + +(3) Create a soft link under /usr/bin: + +``` Bash +cd /usr/bin +sudo ln -s wasm-ld-8 wasm-ld +``` + +(4) Use the clang-8 command below to build the WASM C source code into the WASM binary. + +``` Bash +clang-8 --target=wasm32 -O3 \ + -z stack-size=4096 -Wl,--initial-memory=65536 \ + -Wl,--allow-undefined,--export=main \ + -Wl,--strip-all,--no-entry -nostdlib \ + -o test.wasm test.c +``` + +You will get ```test.wasm``` which is the WASM app binary. + + + + + +## Use Emscripten tool + +The last method to build a WASM binary is to use Emscripten tool ```emcc```. +Assuming you are using Linux, you may install emcc from Emscripten EMSDK following the steps below: + +``` +git clone https://github.com/emscripten-core/emsdk.git +cd emsdk +./emsdk install latest-fastcomp +./emsdk activate latest-fastcomp +``` + +The Emscripten website provides other installation methods beyond Linux. + +Use the emcc command below to build the WASM C source code into the WASM binary. + +``` Bash +cd emsdk +source emsdk_env.sh (or add it to ~/.bashrc if you don't want to run it each time) +cd +EMCC_ONLY_FORCED_STDLIBS=1 emcc -g -O3 -s WASM=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0 \ + -s TOTAL_MEMORY=65536 -s TOTAL_STACK=4096 \ + -s ASSERTIONS=1 -s STACK_OVERFLOW_CHECK=2 \ + -s "EXPORTED_FUNCTIONS=['_main']" -o test.wasm test.c +``` + +You will get ```test.wasm``` which is the WASM app binary. + + + + + +## Using Docker + +Another method availble is using [Docker](https://www.docker.com/). We assume you've already configured Docker (see Platform section above) and have a running interactive shell. Currently the Dockerfile only supports compiling apps with clang, with Emscripten planned for the future. + +Use the clang-8 command below to build the WASM C source code into the WASM binary. + +``` Bash +clang-8 --target=wasm32 -O3 \ + -z stack-size=4096 -Wl,--initial-memory=65536 \ + -Wl,--allow-undefined,--export=main \ + -Wl,--strip-all,--no-entry -nostdlib \ + -o test.wasm test.c +``` + +You will get ```test.wasm``` which is the WASM app binary. + diff --git a/wamr/doc/pics/app_framework.PNG b/wamr/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/wamr/doc/pics/embed.PNG b/wamr/doc/pics/embed.PNG new file mode 100644 index 0000000000000000000000000000000000000000..68340ee935a8d7d1be0a3ecf36dbd5af443ef2a9 GIT binary patch literal 26261 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 literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/native_call_wasm.PNG b/wamr/doc/pics/native_call_wasm.PNG new file mode 100644 index 0000000000000000000000000000000000000000..934af72f5d52d09c7b16657013d19a3c9cc6722a GIT binary patch literal 49074 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 aLC@UkRoy8&AZ4$iv1ga-PUiN*7ykinxaXn( literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/request.PNG b/wamr/doc/pics/request.PNG new file mode 100644 index 0000000000000000000000000000000000000000..281115be2604745923cf75138f8f1dc017c465f8 GIT binary patch literal 19974 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 S@QX_{G-|i+-a;r_h5bKv6?*Rg literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/safe.PNG b/wamr/doc/pics/safe.PNG new file mode 100644 index 0000000000000000000000000000000000000000..ee3ed21e652b3918a87c62fa7bef29d2de2f11fd GIT binary patch literal 67786 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
v6yRk&pM%=xR^h|;{P`+ zEsa|KK!*t^4+M~=90>+yexfKk3;*nA9hk~ciieByCf<+2@H?aVXB^lJr@b!QZPaT) z8gg}372uesIZ)qh|KXrbeL&*OJq-(r9&3$?DCC>k^Ni1{{ zqS}TDtc|i*9(pJhZD8fOUHXFxj@T9wT)%8Y$09+W%En~QZH2X(Kz|OQ5AL=8{cvE@ ztmvtxLHt=M8?;lLUzAjga8oO3g{N5o00LM2H10S!EE|y}x99f?6l^-r#)O_BS^`)B zCRjRF{=b4cXm)5eeZ(PL*!30?xF}cPfQO{z)zlrn9-zs&mX9$cMt7m!(~i`j{qc6j`fN!`aVQ#+#{5x~4tbmNAy^o6KTy-h zdaeGu;2Udr^&?9JN#BFoN&EIomz+Rnvt=}WLP)0~aKHS_v#D!^uO0HvdEM(5qNm<4 zGVVpbThoHEVbCKDo*gTzL7&sU8LzIAV5!%2nIJd-4WTF+3F54Wt@ztQlp8cifV}C4 zq8v2N3bx}!+bb;2WrhEN#nH+kjTFY~WFQs)DDD;+6$JagKtNV05M_JI8;FU-%FUOp zr;EJ~yE29%K?j*+g@D^BPm#Ne!J`?lU*z@!@q{QJ&<;=l*!{5&T9ZL56_}K650Gh)4S2JW_0QgAUlTeC zn{YaIbJBaG!((Ry(mfzQq}a;7+rr266*a!LQ(wjq%t7-NEW5*lCwzqf_JR$i5r|*$ zkKG$mbl=}y{C}WAWgwyPC)||97WQ`UugjgkP8Pa4G`%$sK^DG8c!ICpoZ;Qu zpj$`0qgN>G4IJg=C3EFaDrT(nak}DpJJ#2O0{U&z^_VFG)h(0u(_~gvUQzK0I-Fvx z@ekkn7ur#ZwVsEPFl17doe;yr9bjhS0ot~9#PUhgsiv&k3cx>dnQCEr7S1NYxbv0i z?%r6}{T&bupgoZPn6v^8k7+5W_YaoMA9p+-JV}r&5#b!9@bgma){*a?V+_xnt%Cix zK4%-8FwHUz*my@rsy3>mvigz12h#3vc%L;s`PcnYaFNFNZ2j@+zWY`ULgtmS_P5C=xXCLFdzjW;CoHLIwI#K*MrEErd z*l|T_p7v*-oUNl*_3!p}=knRQ3zq^|^JKWb*7ieGq!xkZc`(DrM{T+MVo7X9u zeu4J#f8Np+@$R-q_?Ei5=hu#>iwtrq93je&)^}a`QW>sg>o)m=Q?^Zvuj#Nzfon*LkGKki0 zVp4J-^|oGz7m1ZH(j9Un?z6#P7}r!i>+6-UB7t6TBYI^2BXfYnNx^364*%tuK()hw z2poar2b^a&AGtF*n*Z}nzXDf#^@HPMi6HCu4YsxHbX2LqCI7I$`i24?ovN9DDQlAqnhPCu^?)o$0UnNA4%pa$Fnh1Gku zFpNSE0)VC+t$n3DfG7cdO2ziNxi|i+K&D+{6`E3bWo6 zTRgmp%#y+KaTE8CFokoBOhqTO6^QA8OxS0|*>bnV!2gfLx9q-`H}GsKeUnSr!tHg3 zdAk`QgWdsW;}R=tu7-H?+^%=;SsM+Hk+6e{TP+U$eBXe;Ql^743)X9cQk_M-@-W0A z7xEJy34L9T-E5eGDY)h)hU9PW7F|j<9_tfF^}ntd_oQ7WA~>+S*WJckoz% zmWZNH4J#nZCSG8XOgXyq?uD4!*859$eaF{((p~Gry5sU|B}T8nUcy-Qe5c68HiDf1 z>_Z0ex8@}QWv1hDKSmPoM) zA!8iuph(nu+i1tk)eB6Rtweq9S|+jIPw$P_9W(!J_#MC+POw4OI)~cq?-%k%aPI4C z2}r=gLHjMPl9J}eIvi$5!6o&VAE3hL=WPngAGA@X_&>0+i#Q}wB1wT2b=olill0beId zllRj;8YI$Ww{GYjtKn~o+STZt1O3FNfFZQTv|Vh}a0?yHoR0jwg=eD~L-@^@kQt;L`o_P-hFv7PI`9^!knloKYEXB4=_Wm>K%@J7T+Z~$p zN+Rr`YSs+#*;yQ+Dq^xLoH2`Owg-)W*T0UH5x`rw%xB6Vips}NiXHVVQ&K$BG@on3 zX}`S4$rb-c?8Bimv0{mBW0VM&ZRiMzg>se za-=M0@yxJ#QO+`GV9lcp1+uianxmeKF**6O`}mk26+O~MFV=I<&Tzm{g<<1G?FPH@ zAkep!rA_fXV&;97xA?dDP#ad!Bk|Jhl7*iKKx|j4Q6yY>5QI*jOnn|RcoE5QQlS;m zgD5V`P6#pjs2T!qkOGN)j8wAp!EIFa!q#cBCCAYkRojB^b+p({hsaD>;C?(J;42f8 zFvcaX^kj_nC}lw#1v>ar>LWNT8z)L{5o^KaJ07$H-j)GE%P2)bq7p)f&_-22T>h-u z&TngtF-V{uwg{7;H?@G=VrjbywYUkQMR~Y%#Ewy2`<`wo?$Lhz-KEaV6|ajHj`gvY zt;HbZSc}AsRL`3;7I*5BD^`_FEjEaMpyn7LFL$8A}7qU5agS%Bv$ z0kWx+fovQRKw&u-1XjT>QWc;7e43Ug*qs#W{4wkScvT?j^DwS!4B%6emXF3)%w?|Y zr2hfXqamzCT0$8*&kL~BC(rO~e}2kU=j^h4dot=BDW{Z9ct(oK)PH+YVimhlTJR#e zoIbi{p{#f&=QGf4t8|sUUwEnetjZwMmY9e?-!14x5~29B)G()aUqfbxJwSLgn4YSv zXJ%t~78v89{ttcer9*{Lawr2ue(q!|e#nM;*=( zb#E)E4{bnZu%dCRm1y-}djBnJ&i+)n!Iv*isaWdL&Qhd^zb1)P{QhUk|&!GS<%lrcIntar73m$@- zEcEI1v4UC@)Tu5{_cE5B>)(!`QhHAr8JM0$tx0e&W&2ncWN^typ%6A{P}xSwKZcp6 zE+(lVrj1En7hlFKgk#qw$D(Gx)D2FUs#qm@G>`L z3AO&T`h-Z_uGI^hmjpT10i7>5b1BPmN{_5(I?;~dVAx09rVaRJ3lm&^pVwh2VDjkP zH-h@N{MJ(k(_J~PV!&2`097TuIpPxVODpEf4729#5HjFsN{ z)Y_w?Tpt^C3O`$)9;S#4LmSb=h@BM;#GOisyW(Z_ z;y#6Bf%0Ub(}=>AK|W$)-|5snZ?cqfk#7ESNACK4jiqZg&N+oiZc8zMN|nZ%FaD8v zNZQ7H94+z`m#Ra3D7qw{ne~V*y1u*o_=3td9=A%CZQktt`(Qr(y$Q<~aq~o`%LR)l zv}gx1dYyc(y)sdWtj{?alX_xqDhpdGll^FTuB|Nh?nk|Rx+;|#gN?S71FvIh#0)@v zvsb5i6A$>}V%l9t{j0Yp_+XU%akaj05>v=j#?FpDOIeH}5cXO8zY6QfS;NCkYeID6r3D|(~ zk!Qrx?7RM|Yo!mH!fnpI?2D&7s;=gg5$w3S*4!geA67*1UB~xp4 zMncQ=CtFOH+3cs#e8;Pw*WXU%M83$)C{R({bQX5rpV~x`UL3u$R8U=+$gCFB_oukg zpXygeptgT-ami*aP>$Ls_C%g*{}OqEo(m@3&N(UvFVH=Xt0+)N$@2`qbJ1(F+a;iK)rY{zLhTM9y*ANn}+}bqNwk z6p+iR-($|Wa$D~D&Rr*m#8GUzUH(W5FW@%^f8DEmaRkLea;b~bk|K!qm(9<|? z#hr0895ePn`%IHO%jZutOA5Jiio#54Kv}D6YJJ~CIO%W@N#$`p$BfNtbB&CR(CP7? ze>qG}dI-qw{4azp;A^24Bs}hJ6Vi7qpEV4qZ}AUj{6FYq&G?%75Ukpld{@wYCHxPW zd;ckF1*l2n--Yt0d{uu9^w+-2XN^5V0osi3=)}=U6IMQyFYj74pV}+_hey|vq!mX_ z)J~?gI0(ry5zRQV)BrJ{J_&VukuU#HydIQWPTQPytoAtv#SW(o% z=PH~PK@P_Bhr`h_Ksf^TKzPHMLnf32K5fl7WvB`%r%cpS6wY0-mj<*wEudi6`+WO7 zLpwFgO`SfHtjN@%0?LV_K)3cR-+I!A$vBik%lj1}T6_`g&Qx2%6iLDMf|Yx=3PY*% zI@9F-&Xg%&qEJn{5emO?AJ2NI0DDY%Pzq3Wb$3#R=gz)l|u@s)(d3*U#AwFCN> zH8wdSFC&z#jcx?>YhDVLN!&b+})Ea)Sze6ELXR@klH5)0Xz1O#p`KI4F$nUco=4Wym* zrwgiUsJupX40fK14dx_Ft*Ik=1F77r?(V9aOlViGe9nsb5L@)kY@>J~#<@$Joz~d5B`$r7$M1Nl*J8kltS96avEEUa? z1MtCC)NcH(f!#! z4RgK1wIQ@x?*P3kw;VEC1-y`Z<>}$o~dXgj6O!tRCCgswZ!~*zwi#0!>LyoR+_{gKjJekE(F$a(!5> zJ@dSTQ)Q5Q?F3NiA_8S%#KAeab>AQPS(;e zHi!wv{^^xT_o0+d^uuAnqwkai(g_a4vjZ#GvHG8kMO^$zW@7mz6ZV~ zXEPdF3lmW!L(1ilv(TG_>EKqGuRk4VXHl1@@tz zfvP?NnLgIJj*-PVw5k^pByrY50+a=v{gM40u@?C@n^Lp1;D&Zx8)0aahbc-J(Oh1#M|_;NmCb98^7KdK<+>)l%(fc=F+3j& zN=O9AmjDsMCtI^2L0Ny_=u4HuJ>__y5Btx~+B}`k79_6mTcc;ez08tsWTB>i`q?cn z9kgD>k_(%CeMRgI;f`ww194u|eM-dMw-G_Z>f2USsd)=;g3`8s8|N~Z1l%XGLz zh_92Y2a0?uoqKM0n3z0K$XP(+h`$V|v#~D)bG3mE@HnY15lX1l$`3s&XrueP;FP5wHb(gJ~{6!DTBNdC@JYBj@Nq=uCr z6uS_*aqhqM*3tTW`ZQxmcPlcp4(eTSPeyy;{mR%`#$erw zk?i!2r+QBF&2+TOw#k^zxBRUeWr5wtVyv9?Mznufdol(pbh$*{rObb>KY8YrSs!Lo zO^h}F?z0_1u^_SVVS4rJ|5^`^&5oT8Vhb}iE;0L8gJ*OS>z8fdY3(h)4)Gyb;V+Bt zP$}`31$D?{{7d&8%2xk#Rs2wV{@Dzm+6T7N!~O!j&VDFuWkyr5Bc%?*ml=XwqR_d(63< zY7Y7U=Y2y4*d2KmB;|;8=tsLpRsVcoRg|)Rm60d79pG%rgI)wtD`*lah*Ol7;M)IZ zS^Q(n7l97Dg)6B#7s+9IQ&Jb9NhEp+QTBMuM`z{WLN-$KUtzct4;hO=X)+H$X%P?Q zG}#)gN!v0G^t_$_{(x|V$4vvXySZNA{Ov2T-E4Gc3%Qho`Do6L*2A+(+OLOs=*87m zVHJk8?pt)*BkcLa9!h0v>E);#Q^Omu0yw+NywuKj^zevzSjJz=^#`uw9-o8v0m6-XAQYTuPVv!Rg0IzE*XCT zPV4hsZ~FIO@YtiPN73w~ck`T7dccm~kPLv&3It9outNg{7VLFQRe^u;Wg!A%UYAp^cCEEm z*pBQQlc|uOE+duweAPqmHT^gs(Eg)YQj#B#g2L2&#>0ak=dHi}DxgHJ!#7cPXJX8k zQGH18EMNpTO(97ooK9RfVD|$pWY7|Whle{*+uK>{(W@Ft%c2qRpqbM&4Xk17IMTy_ zk~#_Y#lnX#QRCqO~T%^O)D zgtWN)Yt$rZU}T*U?^vb0lp2w_1|v&56TmQ(6^C~}$`rQy9t5^&S>ca$ zvvv==-24~_M<(qYTHQZ7ePpxMJkSj*Z)%4g(9@a|F}{JC(BH$a=0p)$?S!S10t z?5`|#^mr)w%ZFHqzp+8Mz1&iRRNjvM9+Ut>R8!1uYJMz%AD3Q^`u5eR_{B~V72-2?nwlH>Bwo6r!TLQI*Aih6|k*z$*nb>W3r%) z_d-g4xog)a_l_iH+b)~Wbu}f0@6)RtrNzܯ|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 0 HcmV?d00001 diff --git a/wamr/doc/pics/sub.PNG b/wamr/doc/pics/sub.PNG new file mode 100644 index 0000000000000000000000000000000000000000..25607a538aa0e400c3d16cb6c36d484372a2caf5 GIT binary patch literal 18622 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 G@Bcs1_;$Pi literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/vgl_demo.png b/wamr/doc/pics/vgl_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..3cb7eb4648624c79ac07027d7f271fc753c2978d GIT binary patch literal 133339 zcmV))K#ISKP)_`M1L_X!;lveUS!LZ zZOI};ibKwjGdAKjTZGH(_TGA<$fNB{Se|L%`|`di<7_rLrnzk8O-a=YFRgPZ3j&F=zf zu2_8j=;9~8SUtZPZVEKDpt!!eIe&K1_ftPg04&H^8q&pLmQoTC5lJZvb0AblAk17< zBew)nRe+e=Qi_@NeLs#PAZ{GNpaKK{009t?bDqy;$1^5^s`kUs7gn{lZHWj#&e_bS zY03=|FpT43vAB2tc)QuKh%i5YenCXv`ObT*_2$cGOZNZ)0RheD&3w^r`*punX3i<6 zoKlV^rwH`@FlbV>h#-I?Ots$hn+*G=7k=F!aN-%#hG9wPMVQ+$6X}YYE{FI2inwDnsL}zV8v%D^s7u%`C@$vlr z{nN8Mr!CE9ExT`ksY~+seDg2=-=F-S|N75A|6)6q05<_)@DILw=fC*J|KUIQ-CuwA ze%CaKnE|*(QFV79>Jplns=3q9k7lN->wa1;x106U_fyfWnGWN0e6+a#aB+Hi7M8BA zmdnj*G@RajN1ARZWwqLV`Q-e|XXl@Od2xPzQB;S)!UUj*@Q8>=N$MO3yUmLTGfyd{ zl$oMU(|q1_v##s9u<&qZN-3H7VaswvN~wOkAhz4_=4!p|%jh7c2(U;*BoPmH4kT&^?K~u+0o);yBW9Z zzD|$h`FxgBJM^lPF%V{OkBD!s{5S~j-(V0?%Bk&A+jb<4 zku!=Ha}7|WWV22(2SSBYVtfDH(|`P*{{4$#`QQHY|NY{6GD7CE=w#jsyi8%?o`?lP z3_=hB2zOhq`^)9_{ARdW`DXO(WPK@vnj%dOW3j>rwtx{uc4Ks1+jTSHX5P*hT|1vi zQNO-kEw6{MC-LOw0XB!J9?$2$`Gb#t<9qM665H+SdfgW=!&a;D$A9^YzxeEn)OOuT znjeXI5W<{+q~U}BpsH$RitV~zt~TrI<$8IuxxQX)HsgBTU$2I3ujAkjmPWx`ELNNT z{QT+gIOtv5cUWQQ&)=u z1AvH#h}*kgh5XIWZx0b+=7?YxNXkjPjAOsOb9(gAhwq8_#6%(uIZjUJ-Mn>(LriVb z*-w6c{>vvX?w!oP_tC?gWT3^?(zfvRfX^=CFFwEci_gxlwkT5sFpk4CjcVF9`RwH6 zt;+6L1xkBS<}pzxM|w9ZB^ABs#=$O zu~>-6G)<}sm{15~G4IaKj)il-)y-xo(-aZ->J9js@wcM?B9fbIW?D>D!_LCzsDMZu%uGZgEFw9}@$vlVcz$x+rOf80reRIIJ&PoEqskv%<%Tw*92V*FFFAvzsq3Zx%;K-Qwtrr``e%=>e(`L2^n9}&s1(vt%(RBV!kz^}L9P)rsaE?`EoGHgL?#GGB6zSECfyFh zdb{29<9a(xY8Fu)Fn~9ep8$Z`CZ8QQoshW0CN=j6qcC?jb`NHD_qx#45SZFDmZ2ZD zc)uOOF^*~$L=d5zbIzGaN>MXo=C*C!z3&HAZ6p?5dvuhy!LZxuvT!&*vF)o`b<8k;iHe3r8?hbN}Hckcb(fASBvPXEU>{n6gC)Co}Z@bhBLD ztcUC6_VRjtaeY~eM$k0sa=W>_x!Dd{3KM4*K?+VWZ~Qy&-TSQ{e)7o&_l9X)-K^IA zz)ju`)03y`fA^<9-)!~x z-H-jG+g`73#$P_Z{_OK-7uRcbq&5e~G}v;ve){CumtUT5`hh|3-M#bSyAOZx!|#9a z(fg-o$4O$?u5?na(haXUK@BadjO}DI$Q@;}VX`3RBw%LW+XxWD!#$;xB(b3Fa?|Fv zO=l;~-8&~oM|0+I9bF5I$R#)cnE5^n`4v9Dj%u4~j36Qr;h;n;96~gCT(6eW2quq7 zK}M7I?iwM}IF_+ouIy)D+?@A;c5%M!HDMHPr$->Os%p#oN z2uC;rLE`Q;aA9F0Qd2Wm1MMX#_cpto6T0$rm0L* ziHO;3MnuCffY3Dg&Pn(1{@v5#BhzwzvAn)smni^=&~KxyuZ-LJe|tN>mHuNN2NDs8 zc>k+R#2iUDOpg|`-~au85R(6!|KSgxJi8_rSXwL=ZO3pWAg6SEauSlpeh?yrAFOT! zRN)5q*uUJ|Rn48<$t(h-REnA#m*Nfu(D%c|<;|1x_4DiPv&-%Ci}lUTYI)P^Bu#tN z+pz7&{L?Qt z)AjKQFOKH>h|$&}L3>MxqgNbdo)2EVdd)2QTI)=IG+-$bPI97YG%i=;n0%F8#%3p+n2p$yV)a53f9Ldofog_Ch&+1}q4kj0<*xrTK zj67;0yMKWw2w)1Ppa|?Bjd3h}KaAsKW<=C9O|>8doSmKBe|Y8;H=FhO)zx}EsTxs$ zJfdPh>8pU5uiK$pFCnTgCbIZ;Py!JhF6Ld_M8v6;8Q{%Ke&_dphZfyG|DXQnFP~hp z=LG9Aw}~hs+}yqGy8HL4ekLE&r`?ofhLIH8z<)8wumPGks!YnZx+vpYGY z1g2@+40^rR>#eUhdb28**Zp#>H#gH{Jeschbw3ONlrpY2*Vot2uWqjUUZ;r>2}mV; zwCUui{oZ#!`0Zc&X|Nv<}-s-L#1MZ?i-NAv^tV{d^1(m(_@oBqoeS6@6i zzj$#ojdnbrz4zeG_dj|6;}1SO?iS;=zZlm@APx&Br-M&srj(@Ga6*K+P8P-<3?>pX z7?H3@utP$kVV0E9%^Gf+#k@P2%h`-i@14B+?mNeeg=yLLy?F_7;~?j6C>FP4gk4;T z;OZkph=2&Wdv;7n5wa;Ust=Pd`{`=4dbXaPZKjQS<|gH4+mD;=aR1?hwwrHAg?a>teuc#(@J@s1Fsr6%nx?5ib<;Gn z*=(GK>+9umwb9~jGq0FpN>Vj{AO8I<3<4a1ZyyEJ!Khy)CdwT1MfV^5o!`29@BaV# zFaGH9qYG04xXY4rBB~LUo7>sh*}Lz(cX@rYUae6>#{g>P5BPo}?#_~5vvl>rYnWAG z`Ej((Mu#zmDf)i9-t_%g#$wZC!&uhae!c0p!?@WF=463k7&rZNeY3v0-fWiI51~dD z$&E93)vCH`mL@ zPo6ya@~N8LyL0^T;e(GpeE;5qljUZ$-K>kb2N-hLthzX6R;8lGo~BlcnVE+%GdUv= zFaR9FOes;yl5##dp3l4HWU*KrpB&AP9zJ+@_s*FJOsC>riql@8{~F?vbILiVlq4mB zM}!F2l*ds4Q!V{CZ6@CuUvF(YMJaHXu~@Kd`mryg1z%oo*4tq-P5n6a<1|dub{IB& zzglm%!?@{()n>ci_Wf8k{jeRTVbV!GEUNQ8B=$S$x%Cup)qet*BOzwOC-d3jxH;-l zE6j$e6nD5o)hrZ2TCAu$#~~#CCK%hB_Z5-ZY^JKF>gMKFLjwfOW{a-tnx<*nwrLs> zu7*QIs^O^Nz~9IsFnz1t^;@NfTh9_lbNQ{`{?T{7^TGf1ul~)?KYeCK%+lnhYZC8% zpFjrSop;_jK0bc(?D;s3%q${8%tS0ABEhszaRKa5m0cNZUt2j$9DoH|P%$4TEvl3H zP<&GB2b&DrVH%1}>SKxRp#5N4d@9)Xdb8>;FV|Pg_0`R4x!#r%Q_;=1anjQ}^B?`_ z`yYMqAoKYA>T2lK(QbzL;_-{0{OpS-&##NOHq!a|_WARh%d5?GU$&DCh8I^i*Q?EP z-EW366s^Ee5+Nq3024C+BXW>|i-iU`WK<_f0aB04<;~}hetG?3IqPN*-#Po>!w2u& zy}!J-N6aH&rm)C?ZT1L77@2jsSt0RPonHnNv<}PA5n6#jKlmN3*W$ znmq5)eAcBbOhRDQ_-lxknTbeMer6`17Ayn?RREM?Vg$k`HM6D^4C}F9_3L5S4CA&R zr((gsxRz1$3=Zu)UMOjEIO(qWj!NsD?>FBZg7v|`2r;`$=gm+bL70Ek!hpWc?- zLjFCu3<6c-N(gjq^T{Xge)!Rc zfAS}P_St8T#wj5RO3o>R&xC*GmcEQ}rKS0U@Xb8CBq zsg7Nxx?(g5lp@H3!yM+M9#K8bRL0`Pyr>tAe$;XDVJPEdlR^z{<5Y&Ln~U?y7v~q3 z%hj+Qd@6Z#Os044&p!U-{=>T`HkI|NFIo!uy4OcfmOuN&`I9H-FRr#1>uK4SUiJKX z{rvJ~wHf)KhnSj=Zl zqJFztua~RUX0t6!jpVjEn{O^%6{O5LH#z5&00aUAcN1X-1(63)KmzO@WluI3rlM-5 z?iTLhic+kWgsUIt2##PP-lwlszXc?FEULQulFZ2g5JJs}f|-b5uz**lNzm?vnzK`g zkO<#BnH?V;r<~P&7<4MO9jDRE!49%8i^9xA5CPZ=i322JwFtPY&>rj#nN^T7n3)Ly zCkhryiF3;-Q_9@5l2Q`ku4{;(P=`|@hmAT;MaL3Tan*4gM|U@~$~>uzgZkHRr^km4 zBjDAF!0o!gZEx=tf{q=CfB=%#Aoe*ar;33??ur ziDYJx1|VT3ViyV_jVMl|`Dif|Q_yzM;yfx%3Y&V>m_>HPBM{PO1fVs&w~UatGG zSD&Ke#XG0(ee%(V_wF7sP2;xLQbtFwc73zDT5mUH?9HCO*uJ<}je|uHlQ1!}G%2T` zMp7aU^FrnnNCYM#5Tgp7S^-5}V2VNnHwThXx7^%3dip50`R=_rOM3S7`uWpK`v%Al zkw+Lai{wE!hGOBI5(1J_Fj7uJl3BW(7qc0+IY7hUS2x?o&z?U&UtZpfo53P>`Wz88 z%p{^)=;O<=uT6r8s`4MPWFf#pe8{XpI}%dtHKQh;0Uw6G>$>@3uBv_CV^4TG+~oG% zUcd8gDEsgxf&&6ubh&Bgl2Y}wim3d**VhXGz|6B**Af93$FY7AQ47_(6etjf=LA5; zOj(VYSgl6VbUS4&rA!e>8GuK0ZQHTVx;pBuf|wBil4@}e)jYXLlJKYtF$`rIz26qq zQj@(pw>pPm7;42)M1F-_yfMCEU`GMMt}wjAokaiyO53FO9z6K?;}1Xm^wUp&{z#P~ zqTx8|vPUGL`dyL)zz8QNCn=@p&!01V(KRiIhzyhx4pkQdB3LL0WFR6Easy-!ozzJ& z?_-Myk3axmh1cgY0H9i02#~5VQ|U(lS}MIV4In0M+Li#dDPknLna^Jwo#u!4&fdF! z=ib@zd-v`iot*y1f9FU4!4E$EA{F3gECNU(eipbKDLm`Bo+LCDIlU3jk7eNhkttE=fRf=ic#0AHVnL z@#mj@@ue0aB4L_0lwlSI)g*amIwMk(yRKUgEnO$f_(Q5$s@Hmc%JB z!abZ*WFk|eJ)mxGNfLlrZgSqGIkRS-bDqs+-K?D*OVgyqQI~W6?9t`pr!T&E_H0{5 z=KR_oebpc$Gp}Zb38p#~?RA>X8yA_(0jTPxAM&;a&8 znYR-K_9=I@))WY&lu{B^jR34LAv^$LB1AC!AZy37=AC!$-@DU_@Xm6X9-d_!W3^fp zH6~6X<@R+sICQ3n> zp-xGtYK|}mDz^ZNsD&*ex}Dy*Q<9y%NJ%PKsHzcURDlV~l0dYhSK7{Y0Z`sq5Fi{D z5pEFy^-`a=*AbD**41Y>$At__mAfk_T|;kPwt#24N)^Sj{teKDwW42%x&B5 zm)5&k^WedQCg)k^>NbferBricB9fG<*w1IPd6(O!$vFctVkD+%)T^tTPe1>~=a0U8 zdVcLS8``gFbQWom63y zDLLHyV6zT>zOHK3{Q4fZtiz6^NX5Zg3Z$fFOHtB6`21x66V4$K;!GY>3@Il8ck^~s zpT?;sx0zqoA%nSuznUKa2)H34B@xCaA3pr2|MWlm`@i>hpFMm0zx^-&a{J}D9>(ny zTT@iA0=XFgkI+(xIi=*G2si~rctlD`X$MabZWZwkFgZ7~(92KWvyo%w6ha@pGkwe9<*7uDFO-fZR zB?|&wmp=LUyB745zx>5|@PIeMM~%!gnM;rmgCZn~6PHMw8yC7>FIU$$outzyWu}No zLWwhgnx^648wkn{0ueKN?Tx7ul|(ruVh#i_r6dS|IYLsCRMp`~4TxE)rwWvv-GA2+^*K^>#OUViQUAGyT*oR@7M(?*ejvg9tG>|~N3|QU( znBM%PYHPin1&J`cz}8kapxBkEm_X3j2LM#EgbPf7HX$XrtByq|%^D7%R21Tna(kgF z)~{+@BRrdRIp?lxKl$XlzxHdt9xi|SCqMa%zxw&pColTGjDr?`>3jDmK-3n%oKuDG zYE}>)l9MC$y-=?>OOAt}?rqu2KXyw2HlJb`blY1)<*3U5sWO3waTF1Naz0+2Y}Tt6 zIu)kY01=5AWAEY)3b2Pa4Ni|w9=vmO@6OS?ch45H_R-a_{TF|Hvs}}zR`$yF*E-Ox z6$;;@9t1R!utyN~cz(6Zo=TAQVIHl&tKmO@@Gnzw4kGjR8 z6%I=*B1z2%XmT=eAaTxSw%KfmXfc~L*;DO2OXALldr3-%8!LPVxDbft5b#yE84(Du zqyeHP(e(K5JI_pYV|6tT;_UrX$0M{*DQCK+GUga_j=ML^3ijj>$zk{rR) z6bPxHGBXo6UemE}Gh$coBshW!fK;0}5)1hrlLvJv9nJE|T;^RuPV>d0@3&8$JbSU3 zh&d-|XH7A#g#VPJ%{j>qf%AxPXOW}D;_P^Kc6!>j?KqAR_6PstA3l3_{&(G~2v#i<073*vjdxWWef`-Et~7RPx6fU<;sTB+F$uL^`142C zKc1yo+kjxYL+^;G6rCt-r!oP^EFono5s^fUk(=!15n;{*1IFB9-ulU0=B>|L&pg1k zEfaPu=AHigEr%{~i1mqXsdNGpp>~OQSnPv@L;^$Ngm{S{5J75W0U|=nG@ErFefZIA zKL0m=`lrj~mZ@oTb28&ao7ko-0A(R&3Q0nwAZBMb8^&?yhmKR%%`#0j^{Yklx5^OV zHd*Q=C|u2xNFoYQRE|K(iPfr|%8eAW*`^HR001BWNklrV-&~>QhP}jbPV`>BfO%VQPOpUI~Gi;o(dI=70l{h)9@oN-4B!o0QY6>t>wp zpUqDf#2AAY5p>oa&BdoLF8Zl>DU$=K>$(=iO-}buTGgo(o6S0R2O;O2Q<^uKT&G^O zl+Qmu|M8E1(r>L4-(rg3a90g>sDdc%W7}QWZedgaz??#)idhXYUXq~W<-_?)wE_ny zYDx*WK)^i$QIeVqJAL-(#pTm;3J|fHGVLN-Rkajn5AHNavml$BaqMbrM!Qg|AI3;c zqB5J|XZ0pd3p8+*Q01!jVX+Qby0R^iGWH~HnZ)x;toQB@X=WYma2 z*occ%U*mum4clJHiiS>3Bux?^+#?ASM-sRz0>r#%lO^v5h2))N07|0;%&6tI z*!F7GH={FCiZHb*fDBO=L{v^A5WJH>0U)`ei3x-%IlykO>&$~Fi3D~T#6Cv&m9YSY zb0EwNv3FuMUE}~>wx#~6-}%}Rp{5Auq(G#z#f+JooGF@CkU18aQ_Ju%tZ@hN(BuY$ zd&jfi`N2DzQG25oSF7cEJx+s5GLt@rNqur!ZWJhO+qiF-Sy*bg(&VPsejG<49?ImN z%?x1y6au8yUU0isCR9xng!usDN5#hX^7C44?I2b!uOUdl_9;eI0ZMq7rxi&x*x)Q8 zY6=4=xw>CXemO+ME;hSWN=YJ<7Nu27{NUqvfAHaVKl%91-FtTd=v3yPbv#jGw&Y|C zgsK6-1^~Md>DHug&%YYC+I5@cwo*9&k^sQs*yCj6fj}b6=Zp8h_k%Q>{kuQ?lP@o? zSz1BNvoBgr9wK5MB?QShnR}?05w1>S#M+{Gb2QJ322E^2(qR>=w>fyQeS<^5G}M`L zitIE3{z@X|w)2yKsi!0X^4Jv;_>D^OVQn+Z0pUee=$t1uQDWo(d#A%VttKteZp%cW zZI(t@byp7-5s4|WI5-eS2qI=`fFY!4ZBE0SXPgO%rfGyjCx@4=>x>8~5rRMxCQR-w zB7qtcRfM<7E!7PXP9BZ`Q`9mP_DQgr1%iYRK=5t#za1AGYC7(=3nB>Swy)1rANcKb z{>^&Rt%zeEj4A-6oKh`QaUd8G-fuUHbflooc6-z{?mJdX5;<+@v}-xH-#P1UmPc2s ze$|(CKV7f3qlI&06o`U;s(|VNeoSc;4tI>xK%j7|wefFMGd&0e-V~p2H*N_S)D(oA z2q1M;(<;NvJPd=oBT#C!767oP^g>M|yM{cwSl;xuyed9zKp>*ab?J+P1!2{*N#d75 z${VKpn>|jv_FfVbz=7~MK0bc;z4z4Z$AA3C7Z;ZzLhz%mo41t0Dv=nV2nrS_aq<8e z^0pth!{ET3dH3M-D05*KcY1AxZ<%tXXknjCzCJ_jtSTspBuQqrM-SgJ($25G{;zHx z6fOubQ(|tehShS}hvlLMx5cb&$xNr5a*exFV)C3soE?BrJ(!&!`=v_&7I(6-n5B5yluN3oj$zD zlHiPx=6KK;4eNgT;@Q>t^>T7?Vn7cNMeV*Z3nI>KQ~THm(s7RBm9j=s&}f`01?zoKu~kysVE0aO2aVLXK>DL;4&>{*)TXF znC3_6yK#K~xGlj(%%ni%lp|sohpy|UsXTvi{bFnCM3nv}3C!Ot^?7BygxTN5ntI8( zMc`Itfj}Taz=H;((PPt36cd1yB{M2k@tTN;NUkXckR1-EH0DKvK#O@hZ@{p`iGu?iKsZUF zpyV}a4+vKXA|k+{0Ep@IsB2WV+rIC65$U?_=;(-u#&M{JKA4%(u1ZsD9J@Ftb}8z& z3PJbq25`V`Dz@WJ`%hwrgjRVdd$1EJrJQs18T$g>D?j_%js9ALlG%>)G(lBkE{#)i zk4$MQ0H_jeL4=5m0v08i(1eb9Bs~+9RWgU9zSmX=- z$(76y6ffPc+}iEx!@aH!-D3C&A3hhn-TdD6^EHa)uMpkJ)vsz_*^#A`s_ZGrzOq2( zYWzAL<~te4APTRkFV|wIn@o^VU?%lh;IKBJ$;_~sMC&Q#H+z^htwCFg@!J?>VDJVdWJe8QZ3>Ll8Xemo{!gm+E z$i?jCI;rU)GOBUuj*svPpR8u84k*7Fx8L%C_539ODW%)a?Z8)!Jz#eGyCDg1RV6}0 zv*micTu&qNSSEJ8bFx@;No|v}5Cjl6QMKBrCy7F>#_Hy#s!R;pNrF{+tw?TTn#M`J zaR#Vug}F(IhH(tnsaTO}FtC%Z-U$2ZxH>QTSy*IxTXL;^9{1PD>% z;Ptj#-mEQH)f$%L#o}b%3XC~P5;a9sn2Fr?Cm_AN^V{5YL}uoc*j=ZxvSruJ>eco1 zw`rRCeu#*^ABiaE#6kd=WmiwS^;@sKxsB@G9=E4(E2|1E^{Myg^8p7&+pgrX`#QBL z?;q-0u*ko51S;HJ)3BP3CfOYYhX_?glbDE8-nF#^7#Ts#Y+5v&Nb2Jqo{EX55vdwJ z4GYOlQk~22RiX5F>2eY>_xNva;# z2>&(`Qx8ot^PKYz=z7&Oi6phD5^rfj8RIT*jL7W!T30yL16W^w-UIJ2_Pt!M z@6uj>h$!bgKR!A+IZ@S5fAQ(X&BiQ5NKz6>%wk?B+%q`^Fk?6wFb}+p6wS zDi0(g67kSa4G9vjH;Kn^*J1;=`GfcGtJ3vq8W{c1ziP1eYw-UU(y{w_pq@`yEp83L zFileh_JLpuA}})oQ7i(7wwAhvOwkCERG*KgBK$;2q^JwmlMg}ehicHPMemp3D1dzp z|LtQB6M9PdixYO1Pt@7^`Do9pXvpLLy@Wi6#>DOU44Rh`Yc8uPz;!vi+- z4F&i0>Au|k9kK*d-8m-!l_c)Y0Kb$I0_vHd$Z1F6Gp5=Ib9-8c`_*i>a>Boz4*lbHFh7hF)yzFepuc})dK)Y|Y+%Kbm01P2Xge9fqQGj|(5ZDC)4^NU3 zc~12d!T{O6ngw&?~8^*D}9JjbQ<9WyLO5#p{k4^m+w1vlMpQOlQPq6tc(@IAP_ot|CNj=xRJy!e=|pO=#kLcA z?`Uz_Hi+q^>BO$5x2S4r1EhPj;;_62Svmj>`>~7C18|~1lv0k5j=uZBN1M(1(W6I| z!xVrpZ-;R;Ap$Hi>pG5-q_+A*W1v7iCvC!X%LG)|lCk5nzXIDu#Jm}@~vaDY@YR#M-;pPF4f6$o&9S~78Q$Wn`U`)b8~d{;Rhci zk;ji8kE&Iz=3UDT_x<3mQ@<7_w7l|9 zZ{qxiU%7S5c;l)_l2S@Fj!0=wtlBkEP)aEfMur;y@1&4-r-+#Q;K*PixG{w#1R&c< zYZsabB}rrToLqh5PNq#UQ6zW;Dh}KR)l$L`2@yrLprkB;U>4>b8gFj5?e|xWZ>#yY z^Vm!R3lAqp5=u!_4G{v*B1nmVSwc>gxgE8tTrG4xHp#2-h9r`RNZ3KXOdJG3*sWmx zt@Z7@aWYRz;SZPv~1pLB~ZM~n{3 zwEJIVZm%S>Fth4Tat@5T3Tf5%^}rZ?H43ZoAnz3eL{FYPX`1HY!-wVnZE~8)u`l$0O_BCG!j$+jr;`x z3WcQ8NvDHqAgLg3SG8SEO&(=SmTgJ4HA@ynii0>k@=f>NbIu-Cepu(6cS%aFLLU&q z(;Lpccb~oXTI*Zi`c?? zM^F0qFQ7MMty40ayFTNGU3c;<1VHqz%3IR|1@)&hugIqF>1rS1Zm_3Zo%ZfPQBzi= z7()bswpzx3972j9EQ}yNBoHv7Q630DBoQ*fhiNtKZVe%ZND3E72r;VINrd1bP}0rX zD*^XW)IHaS|77=L?7D-VyMoHz18}W>R*#(wv2zwcEOvD-5zA`-Eziv`B}^a!F(H6u z0s$@Ri3k%14KKSQ8Uomswh;kDYrH`j-2ie=h5WA11^@t*Wma`Wo7%6Br;}+TkpYP? z6-BQ7h*7w%0}-bP0uVw9F{Th5wN*I+0!huZp%lxE61IJ5?QYY<0@1YlpSzs|MA#yN ztv{EP1Q1h<>aTn81$|&D4QLEAJ8NxOmPMYkF?p6}nKP~_ik#VGS!S)#CWsJ1hyfAn zs?IZW@X(%5J@x4)pT7J@fBdiByK*aYW-yprUR)Z=4nz+u0wP@^_92V*_q^}}z7i;l zBGk>rix;yj+qb&f#IUik5kf!!OaTBgMs^(0PNsDT5db`b4n-k=oM*gx+YtEyZ|$x) z%Nip9($4u))9CEz!}mD$yGYs^hhd#i2+*?(;_hyo{lmV+X7^pL&ChA?{*M#VUc;Uv;~LP`mt_4$HRYg;)D zilPrG_(13oi4fbelQohAK#&z%Mnpn%n#|)&GYIqUXaFCAr`?CEP;AyXG*4UpX^cP<0tqKJqfOsFYKL7WR zcJnv;9i!EP_C2;m{-{wf0Hzd!52}CE zRdJz@zPk7%`VfH(0CtT{3<5F65CdV$gcbEOou6#(>3NtH`6D6_!6b%_GPDts0`
  • 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
    ekDU7W-NV!x9@9bcaGb!TyG`@mg8 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 z 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%0ArY4l385@jei(s2iVmP+Q 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%0DVb$aiPXQi~j}y2F!6%wbd2N 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 literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/vgl_linux.PNG b/wamr/doc/pics/vgl_linux.PNG new file mode 100644 index 0000000000000000000000000000000000000000..65ce063070aa4f0d159cd6a8f8e3eee4c4eac191 GIT binary patch literal 25976 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

    |0m9%KV|+Cfj<%W6M;Vw_!EJ@ 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

    ;t4Nb@ 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 literal 0 HcmV?d00001 diff --git a/wamr/doc/pics/wamr_menu_config.png b/wamr/doc/pics/wamr_menu_config.png new file mode 100644 index 0000000000000000000000000000000000000000..c85409584e929db8b16ab66237e5f7833269efa7 GIT binary patch literal 19135 zcmZ_0b9AOZ)CKy~wvCzEw(Y5H+jcvh+P0?c)V6KT)V6Kk{=RSByVlh|ub!-&^R88qtp?AS2t?I*kO&$mNdW!rBuc7;CZ_nLfH{Zm}0)C^6HUv>uEvlE) zdNR^${5-TvmVApfC7qJz&hcM$GlBb9`=FMbtp}L`(l#9}$Bz>Viw;1K;`=KFje-`# ztM67t(IZl#QJ0xS#+dv2jf^}R*>ua}2Tpv)Mq135!7zVDPGDZ~YB2>B?~eq-8LT`I z025?nTu@k0D8e0#B7ufNK!A~rOk&JLcF4qIII$OUoA{i^Q)j4vkZ~yTU-uAb9h&f0 zl`p_&Yq$zUII%u-`F0LMz5zNbXAIqI>(Zj~o#zi47tiBKjZE&t4`+$y2#8yy#-Z{V zO#D(qpn&;l{O0FK>0DFh*>?jU*NHfozvSO(G_W>W7!`MtEEUQ8RdSY;c@L_DNOLy_ z|MTZH5~TaFH0&y{S1JI%*AxygZN3TQP@*e1&aZOx#B0G721 z-5+!n|F?;t?ix1efW*kX7e{t^XVV63KnQ$H@)ls@Y7_(yXwhg9V0|L*hQLcBg9vIz zU+i%HbQaSAYBJA{-eLZm3Ku<^I_v;vC1UkA2)EG)6bSVFi%k%~q0e@y5FZl?sPV9A zc=`EN5ZZmq#xoAQXF+9i`{wO*N_)VN#0Mz*P%(vqNa|$jUIKs zh`^iym7^jmqHQ!HuqTwDcMd7~Rm*??CZucClE35fq+V!`l}el-k&7){i)?K zryaUsCrl3+1e^Z#g>&>_xs68$Ae5XWU&Vt`s@`GJ7`?`JB!nN$a}lKD?hoF>QREG* zUXXd%O!{gZKw#znYnB19`nfyL{yz~MHZEAr^xuzH5`q{zNOGLA>0;;rMH!n!zP*W-W7E$9Yh8_~MQQ{CSOt8zH5HAXPBBOw4l zP5HQG!l{;&+}qA4b8(nOA8ku5as=9pY}4~vxra^9B*MMP=<4I72NMq@O_|b4UWPA$ z-0fMp^+Pd7;u>ZfQ_XgxVggd_X-2LYoD%>fkMqx{Bqn~rpt%*__#*Kp(fRaW+-bqK zki1^t{o7G^|4I5pH!B_5PgTlv7wtrD8mZB4tzoSGr~h6xQ4Lc{EWZ6hYOWRt*jZp3 zCjjK+tdw=|4y`(-6q{<~URkJll9w1mRTdbDi;3lIk9Bp(abeA>Dz_sF-8K^cbW@i4 z(J`AXh#e*kCN+jvqStuX&Tfb77G^?pnMb((L;NgM5d$Sd#(nBx(ga7B2YC`x1r6Br z=$aP%uPd@g(jl zWI+8+;DZNn2EpD{AVXAlh{FQxaP6n8{u5bQ3D#w3W5U#?>ed~^%i=u152NtFnvuJ< ztKeakViZlZy-r2U9?CYTv=18Q4aMB+(@|tN09lPHwJrn0T+4H}z&nvV-#R`K=nLz7 z{BFIxUi~DZ3i=d!_=@U}ONjgdF61&)0(VZ(5A+Nk`xv-{OOGo#TY6-xbh*V92R z777Psky_sWHl!%zM874&Wx0xZY3P;?&ZM=0

    $cwzXA)+u%2t`H%|4Tni4la6I}| zH4@%XV|5_Ui#o*pNKxYS@e{50?h-dC(H_jeFzbZ7HMxY-_BSWCS+lcl>I50P14il; z8R|>%Q}Di`Dr6TJ04*c=6wei2sncWg@~1!9P}SBcI~xFc@Nc#XZ=u+)nDVmf`gZ<( zyDvk;auACK+r1UHklpBnu^cu#i#EQFjFsD5pr$NOjf!us&FW3p{AS(D=+`&Z>!UUp z&z0GbMryd)XYoxwLLmn?Ws!V-6&f&r%YY)!!p5*5rKeYy*x#F`1_1)JyA0l>9Km$s z`Q}qYhYqH0o3_>=NH85jzF%acmF^k=-<#LB&nir{Bs|sWmG6Htlz{+(3GSPJU^`+; zsi{IcYFPJ0gS8HO0B2Y++iMt!p)y2@+-JJGw3uA%=cF;+U&J__mJC2#w zKi9>&XxXKy8+Pcomv0m^r*qo(e&j4{6PFQUFN}f8s|Mfcxa_24<0tA?ANBBn?ymqK zmdw}J&MXvnsYeq%HUuZ)aG4Z#hY5Y$m1*DDQR$@&9V=4qOh^!H9BrXNwp3KpVSu&Y z>KedO1UH$nIaxL@4<~5wiY=X@X(Nj5*#S05K%#jeWzZsSsRc~D#`=0_vugD7zNk}r z;H5$Oet{cRZ(ZFPEdWd`q2{jsIl*lL0*tgBdGNr92U{g5E-DmVcAk$f% z8pMv6(9qXoZ5l^wurMyu0fOejQMm2QEgdp{o`M>fs4&(q>C-dWy5sFtC%(^?Y=z?IiA@ zMoDw=d}zC{UOY@to$I>9NYmIgjif>&CRmP0@f5VSOqJfbjJTziOC|HqJo48VFG~V( zBrs6+Ff6FR??{Mj-^35pPMm!JFp$DeBT(XYb}r}tcK+x6>8_K!_POuR!G^Gw#*1jk zsVrP~ofxCEk-a4fSe%fM7kwd`6vZ%1kNQ9Alf*|AfTKpmav>nwzC^H?+ch!5jD8ly zBTr@j= z<%$p)CdaQG)2`$U;NlzRs8HsTdG3%d>Ek z99q#wxFy8MISW8HgQH~vg{)YCv;+WTL`(y-&M|19DU5Cz1?!?FlJzX~D9Nm&vHAz0T&w7x$j3>ms4+?SzAC)D<`^>Eb`;RY|5N!iTu zg6zx*4ZwTI)%NY-C$g}LPE&(9IHnISWEBUHV*%xc9%-*z-~hJarOwU}bbA2s6?dtt z0Hdbr_gGK|D$r9hbJP_%-0XUlDYsln4u@<#7yx*9>h&JFx44FBwh*OfL}7;u{i$bH z(bCa{jfW>A z=>=1bq?VR6cmQ?G`jgb6sbXtNE(!bg7Hrt6Z#D0YiJXUJV%t$v+aG49x1;yqknb~k z1=tfUS8-vAx~+d_*M-GBA|fc&exx%Mv?Qw^0&xo-D}u3H1hhb&gRzqlp`DZHC;=P# z*XB#d&m>oLZ{^KD-|8ee8~bnFGMkMnl#|a_8SK!e{vV;mwjm&Bhz*4BNFhmWal#0? zXs4UAXE{Dx)x8*5*)+s~#zO>L9{orgF+<|ec z$&WD)pR%-6putosz{4rA>I;7vayb3|JGNt%Bk!K#r?*2&GF(=h7mS21A(2jz8>C7G zpux^A4)dFvAJ9q;12?t>68d$SwtKn~#X3 zJqvQE0eYy89EFg|4nO1S(Qq;t&C3HmVMv zg+ZHm7>HhMjZtCyCzg|AViopq4{o6;;tn}?u5I70*s>B3(nbyfxWYn7P)IOE58YVo=PHI z-E?FPJTga}n_xn(4{6WBYQakd=tl#57OWmwF4X=NkB%6Sgx+@efci=s_@%m?_^}#Y zt>va#SQC+<1rvGEwF61}#E(OCW-OZtQRudLSH5 zLMu4YZS9ck9M#|)pL??$$>`fPEHDjhbfFLhr9;^90m&5^@JgJGyT@_5vo^sitzfRa z`ts)UY|k{2wBPKY}f3=qZpxg>2!4+}u^guC*e z>~t*rptrCsJAXC9H>{05+X*R@G0hs9b+k2?LBA(U?BX^~zRB9!SpqM)82e+kWHOS1 zVLsCTa2)ix!Y4Ckic>)gXe;$;mM$tBa(;oPITQSlC>Qk^ad*Cul4dR?=j= zA4jc^0#mE+q2}@1yrNdDCfMhfXjY}78vh}BVVStUw-)C-iooBRiri3k^#@mb;uqMQ zLH^&3sqQB)1wMW1cIJ&En*a7dKJe8EFu+Zu?jJ}R8FK{!(Bi`b0;E}P%@EWZov`wj z^iwl0L01V<^g6%-4l*^;3})^4n6*>Cu0^B!=iC%vY7Eg!x}-n>Cspr5D`=UeiatPR z3>FQ<01nPl#W4)Au%c81+Q-&t=fg6P9X{X~H5dneM$;pIZ)G+(vj3Uh;rd=pn%XIA z(^vtzd@lJrQ1h>di8ode*_qq1#bOHL+ZS#fem2(<<}i{pY?y(O58Sv=8L!|30%S0< zeHY^jI7>s$cW`7$gAsP?VGdMjdZL3v&(qP1F^qYGlLs-_5oK2SG;L2uId|d1XlDV= zjKqjZ&Hz56o?fQFa~%WMGzj(y5=;)UaDt8RBS<6Js0-vhJ$4w!m1&Y&En5(eV#UMF zT9D=jZ#k>VW8Xx-3eTj2%jeLmSq|nY;O`?g@ApNQ_SYSJ&yj+ykb!5A*#iSy&iU_0 zJF%PHw6q+PMlM)O%QC_6D(JiN=o(ga`5xXdZp4WKrx+xNJ3^fPJYEpF&sh<$8m$iK z`SwP%@aJie^N?+|F#--{4<@k!mabw*tIQI<#~t{6bQXR{fmhIGWq6%cSqjVF&*itk zv!qk=w|-v1o5HPj@Ki=`((nFID{NSY=&8h^mFOMS{=$;Pyg`|=yopjf;3YcBthNS~ zCD|y@-~b@j3Yv6I0O?o{0k&2Q`$t}Zm*5wqZguZ3zNhi|rY}&cBU9mF*7Jo=9d!e~ zji?-epy3SbH8YIVVt)J64Uoi@1mJ5 zHWN@g-#i!e=1)jCK0uRtUOdIJaMHaZ(r?UdP+V8V_O?)sD)9@x0U=JIT}`JmdHzit zK5Srza;?r!h2gZuQnSw8D(YXTSh)y6w^ne_R^W94sszr4I7&+M`o|wx^~Sxz9U~rO z(!mZqG<&IdO6VHAJMTwKf6pJA=g%OCKOxtdxlK%>Sd~n)-;V=xadC`!d*ujFyoX16 zNUYc)t5Px!CRZaClb5pqAdQrK=VH`ww;+VnfqjwHeo3jJw(RznV(drK=g8y3{-fAb z8%_VmeLe#Xug}&`K^^<2*Ea!8af2kOrG+TAfto=i`@*Ham)y z_xjH5M4U8gHd857xD1+OdlfH33h z#oeni8EpBrOJ#b(Sn)>M$4-y)@V>#>9uK5KGPe>2x1@GS)o?Eo>ZlndChsw{oo8!qVoLgb814r~7g+C$6xJq$Ro>*^!B zdGo9vS=k0%u*AVBBH^wz}eW&ij7uScnyj1wh+r@ovj=h3+#FP1C;+my5}OUV(nxnrTtH z_&jg)Zmx!*Aj$jDVDhxHy~gvISw2d22xU>*m#{J^dJHHx4@JS51kW#QCksA{q5Vqr z49*`(qvXA-s_n&@P56QG1v0s;6|%J2s+I*ohEk`p2n85aQK)+YT&yw?yqk00ikIZ) z7O?7ZqR0%w?b6D2f^3z|#H8FAT4giY)z+UpOm6#0F3!Q(%_H} z#f$tSd^SVh$YVd;zIv51&1G5-QjDE+Go`|{xzc1Mx7}uxxcqgTbVz6JUb}ApyZ;+_ zI$2CXAQ;COkaU=7l@#gsU%Y`lwQ5pbmCD&EJ!Ccu$50m5T)jJ#5o^T2^j9Uk@{wIy z2OHBLyMO8E^a~(sbMoHv?{= z*#!=r7m^UAut^edK-Bs3q&#YmDWjJbVF9gKM4i@{HUWJgrDA1^;RmC(@xx*x{lO}C zy!4+HOzk)=Mi-@()1fnSB*vu3x>HvPY8{^{2LnkY@@vNYm*ZEo;cGzlK#PLoU-{F& zbgXO+?1-pR)WQQqRG0A_GB>UYbU>zID-h)|#K?dJBWY#e3&#uY*9bni54t6Ebzx5$ z1Qq>@QU`6Aw=M1bSGbUIqu?y^VJ~?F*9-()4-1@T@|4WuKd|!v5($J;JbKcx24BZt z-O=?|1mJ`OV#Id`g<=~HOqy4WKp_zlWo~d0{Lu22PPQ^3p`Z{EyLK^eXZuyVi7Es( zPVVqdkryGpMgv-};}<}zV9WP$gHoRj8YE)y6$5q-|K3Mw85cV5T{XiAekw{4ZB|L) zl+NOpRumF_;#`y+mJz1qZv~wGOX;^aY0jhs0;axk5tt5Er=WlxM1R_Im+Xu7GFPW^ zPnh2jc@Y_3FQKwq_e>V*$!sG;Zu{z6-|URez1>p0`r8G~IDYd>qm#nLK~GZF6Zt%i zsoPN>4?xShdRJD&y|e)Xq-+MT2?O%Ijzh&ld*GpX>hf4 z18SF@QvO2rz(rY*qw*igM|I0L0OAQJ}z#EFs2S zKSij&v7SG-`DhKL;-Np>PkfY9qf(S6hnKfr7EuER25ntu%S?6-t5afPExNxoxCouA zYk|91W>QoUq*R*>T(~rPFAL1h9!nEK33s!j$AyX7G}as!l~09BYcyym(ACGoPkXHA z{2NU6bsjToj_JFr5Bm#t-2Z;qKmoFt^y(l1lzaE)BJ^*1-{W=!iE`5}tW-;PmVt!> z)zJ1Jv^ym^sIZxa->C;bhupo*w_qF?)MY6Fpzb-U3FbR)`u6|``0G&$2h$n=s%E}X zWeI>TV=&R;#Y%VKoS+AQv26=y03croBLilZ;uamlA-d+7S+Lbd`amYcOWMI1J(I}4 zF3-RnNrHb?LhqM7)=Etp*!z^JyHf8N=s30lnTgn*F`l65e)?a%5CP347O|AxC5ZDGi8m{;w% zk+JPJZECY&X8{~q-!|yLcj~UDQ^R@;oi_biPM3gF{D-VbIU0>HmSE6rl9UWVJ zv=_}gd??P_@sD%bcy_^^)`$2DqqNxwS>S6tUQ$lmk%}-UT*MfI1WT7_GgjPko=(MQ z{stJMWXkKE?37zm!w#~ z$3(!TWH~K_rS*_0PaoLYuqgMy#Q2#tKwDF4HWk#R7*WI!z05}S6y0jY_$x17IB|gh zBW{V~+l*#J*8bd5BuY2W*dFyWlC4>C^RC?l(HX2_$e5Fd7tGusj4^vLuc4cGF?cc& zIr^%)cFxY(Rgj1ij1WPu#^xKWgs|U(oVl_L_1_ zKgt?jtWyjQ-i>o&)Ye!vYCJ;~)y)|LG^kTU)5e=pS9Jl-R2mZ7Ly8lr(0)XK*N+&B zrbZbY?pro`ziyhE_Yh7_FMon?SRXYB!FD*!CSE|50WVp$Bxy{Ihqg# z2ILibp66s3K@s9%E^=u+{(h=x=>@R@ENrbEfEs){OaR0`;8%3(t7v!wPX*9}1%6ZG z!!u~KOE0mQ6vbm7SJ_Aob+sICC^A^-?ac|YyMV`!k$z&q+7PIMg3tb~!qS$7L&L#v zW5k&5u|WywSy|YCg>}tSk+NXWgZ*waOrd}hz}0=UTiL;pqacqP@3I>x(#@8L-zo|r zxSZ_<8{nd%-h?MuoD^=tX|cMBCkc&AEdhaAVqN=3g9OsR=C&aO$sB4tuXJ)76bRy= zOhE#?D*Iso!S8Yk?Hu^-Y8VXft!)`*7cu1tP#}Vt`9ZuwLTND?P zSr{9wl{1P7>hG@~^{-ZE7Ukdtn@7V>11a==-;GfhXV+|I6VGah@dIouYTJP-rXV~`X0t%h}#8(RaM`0-6oz4j+7lz;=9Dh`3MN5@`6Zfd$Ky0+K?-Fr<-RM#Qb z$_lkkzR1A#r2yTOy(4UJ=Zc>LV(;6lxI*7^J}Q1sm$kRZH<&HP3p!_68B-*E)(i7W zk-Uz(#sSNj?6Uyq+PCM!A%=#Uq?8+pkHtpZ!v#D9^Reb5RgQ5`Jx%uQ95tnj_c4rU zjxk^Ay2`4P#fX{VLb6E0%!Z>K88xKFwOwftz+2|8qVXQB+=!^_{Pen7IJDTv?k+e` zJV8)tYx^{Y<~SZ2ahX=N@gkF0+I7uZijB-Y%QElj*LVBR+}C*>yQBcGF`t1AP5Ue z*eS8TZ|B>8*uA)*?=fzUgo{E9%IRDucd1ZKPS1mb-52P#-URlwomy5gz9u?`oq~~i zJmzrM{GVEYqcy)VSPD_2E^%^r)vF|#^N*(f?Q)anPmZ3-w&b2T8!w; z=1y)SaGh7DqB-eTkG;Owe$4MCk*!tr`*g4J&F(GO7X^3o2;O)?Rmc{5ysdHH4&vw# zY+qqlcv8h|Y+uN0Z3KxGCd?z3O_V$BHb|7q z;32Ycr>9IY=@5K}L#r<`s!D<*kgM$e%z3=GEQbREY$z93lj=MW6zC0{bz%<4*8|sn zp{+Q#4@zp!YBG~bV^VL2X+F#)9GvaMRAC7uqCaq7@PBiD1$n_D|;KZFkj z$t(u0Z7tww!HL8(Y05rjSu~Xw`K?RcirGUX6|#0;M?qw%S-YBAhX`U$LfJarr_>{k zDgRHb0y+;`vcAe$9)fwc3RU^#71-8S2qG8oB=|*Qm*E?`=$ zI*(;DH!{-x!2Z51*lzf_(dS;bvO)zApUP-wJQY|(Q~nUuXskS4Ja|{xHee4#hVBP@ zqHbjrox(tiX^VKt^&9`}oDu^u@OiFGWU~T&`g;mGPziVWi%kT!xKr zJpMbeQt#(_6VjjH-*!<_srp(b+ZKt??)e+b+a5VgFZc_$-%(-bBL__#6#$GmJJ;sS zc1dkcYG4NFkY05;Q`gY<2N7dvjL~74Ch|nhp5J-S%anyoZsoFTO`ns=?1!%NZZh@(liH6ztXd}r z5n^GwO^UPHwD*nvgk)F7rkQ1D|J`e+27hTr^lb6|yAKa+LH8FR!u(Heut@PmH6vd* zSjC#!jm*Am4&a&6FL%(F&$SnZql8)G`0M+VHhSxOd%0o&z?B5=?eZ=R-^)!H;@F&u zOPgj^5-NGi&I;;#y836@Y^Jm!YyYy3N5A_iA_iN!RLT#uM90O%<|3);p>Ij`u9xPQ zeu*U=mRnKV()Mp+jky}vV-(Lqktv;wWLsly)au@*LJ!lY<16~Q+#F4*6r?1`e$n0C z0^4&=AOWoP3iw#oi}PuDLxBL2JtkLG&s#^UzWA-(YG;~&%fo0@p+yQ8T& z-uKJib_94tv@O)+R(w4?5oN-kxrIN9Svv_2G!#if7*SkmlxeHpMeePdkJ-GZDm z95d{IwQn)C-gGbx*1B)Y4hW<3-sF@J;*xx;D9RPM-xgv4dNQyG^}zr@sQJVCc~WSi z%b}6*>2AGoII|KRP)KAwZW3!YrCgpkU^*C#Betacpm84F?i=H-oIL;~-uss0bskP4 z3=iN}N^rcjnW+l};{=ewL(8PyZ28vrFUf9PRgV1LUYg#l;MbY|w++5UxyxeLcDf*? zm=kKV;>46Td#-dK6Kf6%Yz61M9}V{U0D#14{KUMq8z#FJjHc{Mv^!lI=Bg=Vb|L<} zHvf}{wqbcRUR)Z=AvLFtSyJ(Y_et-vBH?OMAvaCA67#`*I4;90)b(<9{)y5WL+c^*x)1qLW^f1Du-n&M1@NWfYVg z-$rQ%*rfc2D724Hrv0Z7+V8iEz~=Ew6t$~UN4XVNi)qEWJGy;xJzjKUHwi@9@RvJo z+GBN3nh9mid{|A|#^>uU%S|^iprI7BuOwyje%2hjAd`^-0eG!flm3&mnva*4xD@=i zOM3t)u%y&DY~1arc=z=DsGuy}GS$>qS-$A^5dG4RRZ@yWk%xV2BssI3(rxRWZ*pRb zgb1H>&6qhm5xHXw2&T(_JZ%iWdKB9E8z}ysQLMjQRV|5dsaiI)A-8{0GnrG;_9&L( zza5o8gIM@woXPC(QQzD3mYyC+xB_q6iap`r40BlS0RTx%_JiKJXIL@qx7+P(3p?G5 z?{{v6x7mm0Q1#gO`^+lIR%~#39dd1t<@Pb)o3(dRR)5v4{djqI?ysp2jCe9*a%RSV zMMvFuNg<>b3B2odtS{HycIRHU{|jAM=}X|AZIHN>L0!#^{j4>c3{@GCKtjS`X|xt* z+le2R)_XZRwMW~~V4iqe#7ducY-Y$UYke{d`%w}btJi7f-)d@-5kOv44w%}~FL#^0 zmd$4f?V3$xvDyV>ME0TgslPDsD^A6*vxN00DDCWuatgZ4wbGcZ)^56`|Ke}8nPp^x zjKeNr%JsADpDJrlOvDz4DQ>UWPw&Qv3%>AnK6ZE}Ljm6%0S)%B^}%1N>*+y8vX@)G z*o7#H{?Z%*b)Li^t;fF+UU>lcqjAz4)FRHVZtC*dTn} z)T_v-?R!7`suqI;nyBer4M>Slp~$JDrhTu6i^rsphM2}*=29Yi;a_Cv&uj0O&*R-Y zuXS&mO(*S5e=r9zhIZOviWY26=Q1Zh>`phR!>a56_N}%bL)A-tZQt0`{bfNhrZ@rehn!4; z`$0Gi>0E#9d$KB|I{q|VDGfXr01Naxz)Mb-MgLwmDb!#FIhBqow$Tj3ukfnJ(pF*A zd#gm6=6Sxnhr0d=l@g=aWm4oEdK9DeI zG7&x6@_g~D!`8Z6@4lO0`BuQo8rHd9h?u@a;P8>rZt1tVfIN*rO1=GNAcnpDJDqFq zxD5F(M@9fuko~xn!pz0nVW>rENE6whC9;nZfPazALGD>@xbIh3Y(#2(5A>w6iYa_P zQqMKh4FQ)*0FT<&Yg*(W@?9|#L;bQ4RcnE zVaJv&VSKPTvAOjv3o8D3aPYW&?^uqQ98@1qg(4eqJcq%~T!CHDY5$XbOZhL})h{mL z=$;I4Xi`5oA6rRI*0oC-mdnT@zJAQ^gZBU3%^___t9cfNQ~Rx$fl{meEPFy9PP`+I z+`t+&Xj{|~p~*rc?)6*&dy1sTuE}*%e%vLMlw4era9#V4jEws$Y)Ea3P0jt*?B9(f zeXxAEgDG50LEuLOxqE1B>#(mLKiP=VFB+0d7f&c0H_KYymzrU*E^upIJp?LjWO_aU zfJC@h0EwRRqr0FfFSDZ4ZV3Z zeNS{rYkrk6*_wjgN~>5)E(pEi4wlp<7Y==#eUQDn^Mdl!pE}L7?ph9uuBipS_ZS2r!5wy>|FW#}$pHrMK*d zCg*-0W$}rcRdPB0hRIV7pD9H0)-m}yQJ{Zz`n6MO?w744x3+&B7b}k7XoBo$29;aA ztRVAcb2%QWKPx`Yw5_JD0PE|AiT9L0JoZ<9ufj-%mcNQdKX@BHCkU8EQ>*E?C*>L& zcbp?7u(fgjpt#eGBJYGr=AUjIAb696EN%=DaT>-&jdr%Mnwnt0r97{T8)qW4t|_>!-v|3$QYao_Xy&Br<%wyc3D&hYDQ zL3t=Z8#mtX@edQ$4%$^B)*4rr8_ z%Rsxi$D`+S3UPi$%O2I=2z`#~H!BKYiA!8?%fEsn

    #7kYPWMajvaBB0a5AFhe%hb5O$4c;MVt3yK{$41?Juxyd;dxmHBbFsyl=Fd zf9k!!?$f?}O#16TDLeOjk01VYTkea_ZMLv_FSFhBz2=bNcd)zTJa_`H$UBauovqYq zd=7m)2e8#K6R;`B07~oSVte<*UH!CF>LEa*R~c))<8iaQdW7)tW$#voGpUx6rCyl# zV3`E<6BZ=RVkRM0>D9rEz>)6@Ep`8@O@6<*(|#`8@bOl3sXp|eA6{;`-sfP4qDoC|&tgx>rxrRtU%Tz46LP#R1Eb2FKF~mC8S;jF<%Be; z-AvE9l|0&7?o_58Fg7pj)e#7AoFIb!x*~AAqzn_`TCG3#LY2eH`MJZFe?f-BOW(wj zFX!nVf1#L<*{Ans@vw`y;*0_?>lpU0!T8DPveiK5J{fNKkpj65MtlE6p?6@vbpG#d z^x%y@1Q}c$Nt@Ms>TaprYl9KgabM`x`+sG+GjG6u%KSH!FO9G|)R;z>?|p_XTTHnv z^p}WN`Hhc1exnU(rW-apa4u5RU<;BAnj6*=*ipX1?mXxf$MJQu~3@u%c73qo8Kql9K{*P zds#y07X(vB|E&PMmCaA0t4H{6&!;9%uQf(!2g&@$D418c03-;X*V)90|D97%Qjyu= zbhi!X4^v{UGRE+aw{U)bjsQQf(F+pfcf>i0JNb%Oeg<+ju1#8#fDMl*A+lJZooNaX zKJt5kED{FaDRrU>&eEKQt8LbhqLY!P30(WKIR?QxZ)nla;5(Sk8=x?x!ODqWYXk_s zzN7XeB>V89&yy(spjeze7_{SiIE2wY+Zo1GP}%~(L{^XpSI$DbeWCs{zsM??J43^( zAz4ViI1L-&!YK8o`!-GpedJ2+n*Q*sZU3`55FEn# zKPD@54k7HCa=cwP2SL4Z9IHPhoCP4UfBb$m(uHvp``#WZsIyA!D_m{R6elk?B>{(% ze9DV`DNhzFwNvfBDWzNWF&w8}!r^q=cuNY(VCj~F7r;Qg%m==+^2_5aT;hd_X3WRy z-Ja(Jf2%IjCE8q`fJetDJe%3|PejP=yw!VrrFMPfswVANfiKCTBITwe~`XBs3Teq-L<^rNZbLT#r%DD|OG zIj_l$UY`rX$4@xH|43Il9J;iYy(~`kL#LI5tI7*YJGOjUc{u{g5lrbd@Av6f^U@=U ze6KEWM0P;M{ETryuVae@b`y)Lt{fJc&b907+%G4&=BWwXZadwhe>gJ&QH3~3IX$*? zoHgdFRU%M``|2*xJIs(FdLDwNG=+O@f4NrO2zg!V7Em}%OoDzIw83GC6(JynyeNmd zUyN+HZ%N847RvBhhz9+(*8NF@T|Se&(amG6Fx8ugpfa+Ba*ZmU%-hz{qt&$CnS76< zz{gG#6%0Zj6IBpnx2!=-$VadB^VN)mK1Lp~QSEHA%XoMrOXS)1E$(L&6HfTy-UW8F zIaL8@6q!Vw8zHAh!4S^c{+}Kp5#e)F?`E5E?jJLhxXhp7uV&&mi~;-h!bETgDaM$8vpH%+XT0s@q)(%s(Albu6T0{@)#UZ-vIW%X5bNG2#Y z{hlHVssu>CScanT!|r+aMs-Y+&yKE&_xU1ccaz4&{DIl8a^z}dVSH+rY zK}#5XH?B8sHu>Mke4gKr-wC?kL;QF*M}p^w=N3-EbfT9xKbO&+hr163bN$x_mwed8 zx!dF(6o2=JID%(wGMyWliBAs|^%4jd!6Ztnr3S7iU5JsTz ztXf4-FYj238wbzG>eli0&($5CBDqszb=MK=^&oBO6DZ?X?jxx5On4@;2v^l{QV*d@ znkp@aOyBT5qGzyA&#+_j#ny@{Q2`o*b}ytu2$}DDou&+a&7{sgV>?f$w*+Ghcg}oM zL!8na9nX7*Y+hIjECwrmV$F#0?#pr%*TZQ9m0wydMq*3Ohg*(vBy~1dP7uzqr>M+} zHdiH;k^!Q3G6U4pZwQ0_rWc$#8k!|+D>}eWp8@4+J|@wy=ZZY&n3IYM>Cv=B3ahqv zRAacMEvGj62@_@dP`ITx`g}fdLqVTD0)vg zi;L^7L22-UWY#63bMoL?xvXt5!W@5h%t;INCV@ZNxCNRr&d?ryLwzFynL`vR+Bw^N;1S6Ni#zCZUmbvGED0Rr2Xl!jncCwBNg^~Uu2*&EOr}!7p zd9<8om%XE4=CIQ9Q+#iK*)tQtgC_H9hTT^f?)s>z8xM-j?jCLm$Wd)`ufjH0>Afy< zoFM6E6%VDidFqdAG2f%-*?)Pga1r_B?s~R-@ry{Y?%z$;xewno*l6b$V-j+0T*(B~pjfL&GIi!nyVKYU*b z8(9#W&+2YuDUu@JN9hMRP{W>T$s}jgL#8*-YD74HMmkyIuk*1HAJ8t~Mc*rA#0!J; zNc+F;splH5WO|gi4k#jI)_9X}%lDh(B%3<=Z^8xY!6}BYXiCD7$Pl2xmLuh|M-fmA zpWjo^WZl$)p%eacmyK(C3V+u3d)#+sup!_#9EyVZW>jPo3&ZRY+?C>cC(dBSG;?;Wf&3V2P8j8+-mZ9Itvafm9=)puCIRrj0k}}?W zdFs@uwjT`d`rhna^O#|7i@K+9L!K zkl2;^*op(s2NBRU$G-KG3c|!_Tl6BMA4<;yhj#R@3TAf7;LsSLpaEy`j9Z@5 zJh-t3$F`N5{9cdEhb#g=jBq)GqnKJ3u>!~7HrZPjg@4z^p$Id1Xzi?2yXVYBOH%W0 zP~)Z%UY!33itJM|rz2#w?E6}6x?WbnsHzoIwq#+L+1R~aM)3HzDg{mTPo=msoBW=R z+a5@o1sEB`CLLLc-cSBkI3F{+-14&r=a(=xJy!{=H$`s}$g>`VQO8-;ae4kru&VKAs1FDHQj>syz6b8R2^4u7=VmqSGf z3S5C2CD)YvS!PmN^X(XXW+pM@?bN0r;$xr{BB=g6XIv$dL_-CBF7^ei^Ih8TdREXG zOu-?=o@ZdN<7!>*E0!axl&c8Q5@9mNq}d z07|^@V(%p0gD`A$XS9GrUtX0=OiDtBkB&i=z{lbcSjwLe4$*5M0E@+4uWvhfv-3mo z9-(jtbns`rh`VBtUH(MLqIpgdd68(@&z7S6ydh|v^ti9AwIZr-{aeiwm+Yv2cgUvN z>cs;t{<_C}(cJbNWaT=^?ac1Y)^=GKlg3@Y|9=RPup2X0lXBzWS}^ZL``8TJ(Pj(n zXE2JQF}?w|6f=#CvuTmn={1zZHQz@@ncv$D7iG}ecWFUeaO4B-9sd8~S(Sf+8I0Yn zl7Dpi^oNck)Kio|hWE^A*?+O7><4cTA{;psnQ+dTKOIGOL`2#SGP5)?UQ)Yb)K`_D z9FEszE*w*=5r>eG^VTKb~IPQyJgs?WqTf_-d@IeJz}n{$xquSjX5b zR~{nBD`w-dS)iI&*Z_j+uumYp()Q%NFQdXS)FjBv z?l#LPoaAa#ZdTUZK$|eS2*;%}i-kAra^K9sGQ4fS`}EiKQt9L*J;e@TbgQ&J*o>fN z`R7KOwM_eSQHgkwMb(x$=DR@mu!5e$;Zrf2zt25a4F;HfIH5C1mc#BLU4~Y-)vCMx z+hhV#79O8nTvU%IiPUUaR>kzjCS;G=konr{N(WVMwpl%EB9Xs(o$F+=NVjjgDOAr- zkYF>3=xgNMEH|ax_UqS4g71JsvSR;*$J^A?l=v`g#>WWi`j~2_zsDwGfTJl%xZ}FM zn}hPl5Yfj)UJD4Aole`)f@DmnOq#{j4pvH-+I!Q-YA4-3DgeN8|M$NDHazs<(%q-Y z5G+BRH(c}z-Jc(G3=1|NZ(yjLW&c{4(0GR^3?$^Uag!S_O1de!U1g3ScA+i9zy;Zv z<-);+VhM5wc(gEyjSat&>7h32Z8UpcTs85!myQ7gnQwEKgly#E0U+6jR3f9>+_Wa* z%@S%U%uM19T~`{}#)9ab-Z;F|29Sxd*el``chS*k+`e{wo1000%oL&?4m<-%ncQAS z1=K(C8JD6=l{=y_Q&z*7FUwKkCiWVR}xVo z*SE2`zDz>NmzYu3RVlQ%^K}YS88M}GF{JsHjEdZmO`^tpoy)kAFB3)@*Jifa_F1>b z$)h4Cz{IsN30*EJ19K~d zI*dP%y;*;_cR(}4r_bP@UqaT=_iU1-}tTLq6!H|P%gh!6$5!*y2Q~pF3^bY zKM32VX?v~zi{(IwR4_e(RqEz_*L76v_&!BGf5T*N@mlKA4=&PvUAbiS89Kk$fPW9n zvHS5~rzyz~$<}eXQVT@9TkwEv)>+*USN)pSi{GB)8MEN!5;n%2IsqL>K$L$qggbfg z>;1-eUpq;;&w5@hF*C3@tkpBNP7CfBO!*+n2A4vq)+ofjRZ9 z(PbSY!V-e-MzQR3NaFrdXc3pKbl)erD^y?yvgrc%F-iUYE;WN`CuSYXJs8Dm9{^8V z_f{M4OQhP&jZuL~uL{L$RH5}((bOP+C8*wd;Us;n^MxuiD1WER+JTOk8z-(DU|FuGvx*ZNR}mp^v>^-cqIu@n8hzQ3yJ_@8o3(~CQ7P-o4__1VZO^$-Rg zT9ge1-dA$MS@;M3EJh#aj!i~QMHt<<^&=>H&yF_Z#8-u$742krSZf<-`{YwFXH!A9 zijPdPkbKwC#KycoB(fwI^(B7M_0S85LVQq(mG@q@hR1!C!rcLSqbt7H?ASx-gtWX( zEfoys`I|Ixa64Q;#%Yur7Sc-J0tuhZObB~`o7zk$F8d#s>_#Vkl$=zM#7=BuTv9x@LP3rmAPRf zA1JdS7IrjjQ2g*>udQi-A~n5xFvnX&+#65&1}E>~^}A#TCKKnAXxixadYGAJ9C9kS zPyfvt5jo!~%J_f|q+py)WNFLhpu)=Yp@ZJDf2MfF*S2bY3iNN|!VZgai&*_x!9_GrK zde(8#HXs-C?4WFB`LDo{OVQ_4?95zJF^`5m zTnwK4$=ORqShId3Z0_l+1I<4vJidTJN?jpP50@Uuvy1rly24pMXB!*zB`-fOz^;H z4_TB#ow%)r1A>oR4LTzaPLqszdhZ@1*)=!61GMQul>v|nc{I!Gt%EZ4#CL%1Dxf$u z@m@Kaii59bK6UpQ-CAgt8MwzpZOAP+zyd zjV!@oti65*!;`t?$U|DEDr@)1)Qgs{=9VPEdAo?`#Ee9vo)ORZJ%C%=ls6usYC4o| z>*U9VG`ykD(2It{^R}6!q=B9SCBnyUAr-D^Z*1-*H1cX`2I{7+KqP@OOQHGYW&D0G z0P80bW%Z0Wpoa2xI_G_ik;VCf_?g%Y54RmCK;KOP-8k-W3`4QYJ77-T_Lx`!tfVCqs|A;k8P%Er(?GluK!(8^KaOJ5TSme7I1nCFwZa;$Xu^<8!-E)TaZwkrh_ zyH*wF#`sd!3bRH_V3(?!4A$C?O|AYyEFbUG70is{OM1Jf6(vE?9vEUl5AM|FjeB`c z5i^J;FGbzpRu(K8(#FOqAS+XHKqSmHg_gXP0YymuDNHpR+@mlI#K4$K45V e7H|B&G&kvuMi_(#4wfl~0>B;Jf1$vFlm7>CF!U|} literal 0 HcmV?d00001 diff --git a/wamr/doc/port_wamr.md b/wamr/doc/port_wamr.md new file mode 100644 index 0000000..44ac50e --- /dev/null +++ b/wamr/doc/port_wamr.md @@ -0,0 +1,66 @@ + +WAMR porting guide +========================= + + +This document describes how to port WAMR to a new platform "**new-os**" + + + +# Step 1: Implement platform API layer + +------------------------- +Firstly create the folder **`core/shared/platform/new-os`** for platform API layer implementations. In the folder you just created, you must provide the following files: + +- `platform_internal.h`: It can be used for any platform specific definitions such as macros, data types and internal APIs. + +- `shared_platform.cmake`: the cmake file will be included by the building script. It is recommended to add a definition for your platform: + + - ```cmake + add_definitions(-DBH_PLATFORM_YOUR_NAME) + ``` + +Then go to implement the APIs defined in following header files for the platform abstraction layer: + +- [`platform_api_vmcore.h`](../core/shared/platform/include/platform_api_vmcore.h): mandatory for building mini-product (vmcore only). Part of APIs are needed only for Ahead of Time compilation support. +- [`platform_api_extension.h`](../core/shared/platform/include/platform_api_extension.h): mandatory for app-mgr and app-framework. Given that the app-mgr and app-framework are not required for your target platform, you won't have to implement the API defined in the `platform_api_extension.h`. + + + +**common/posix:** + +There is posix based implementation of the platform API located in the `platform/common/posix` folder. You can include it if your platform support posix API. refer to platform linux implementation. + + + +**common/math:** + +Some platforms such as ZephyrOS don't provide math functions e.g. sqrt, fabs and isnan, then you should include source files under the folder `platform/common/math`. + + + +# Step 2: Create the mini product for the platform + +------------------------- +You can build a mini WAMR product which is only the vmcore for you platform. Normally you need to implement the main function which loads a WASM file and run it with the WASM runtime. You don't have to do this step if there is no mini-product need for your platform porting. + + + +Firstly create folder **product-mini/platforms/new-os** for the platform mini product build, then refer to the linux platform mini-product for creating the CMakeList.txt and the C implementations. + + + +You should set cmake variable `WAMR_BUILD_PLATFORM` to your platform name while building the mini product. It can be done in the mini product CMakeList.txt file, or pass arguments to cmake command line like: + +``` +mkdir build +cd build +cmake .. -DWAMR_BUILD_PLATFORM=new-os +``` + + + +Refer to [build_wamr.md](./build_wamr.md) for the building configurations and parameters. + + + diff --git a/wamr/doc/pthread_library.md b/wamr/doc/pthread_library.md new file mode 100644 index 0000000..cfc6bec --- /dev/null +++ b/wamr/doc/pthread_library.md @@ -0,0 +1,172 @@ +# WAMR pthread library + +WAMR provides a built-in library to support pthread APIs. You can call pthread APIs in your application source code. + +## Build and run +Suppose you have written a C program calling pthread_create() to create a thread, and the file name is main.c +``` C +#include +#include + +void *thread_routine(void *arg) +{ + printf("Enter thread\n"); + pthread_exit(NULL); + return NULL; +} + +int main(int argc, char** argv) +{ + pthread_t tid; + + if (0 != pthread_create(&tid, NULL, thread_routine, NULL)) { + printf("Failed to create thread\n"); + } + + if (0 != pthread_join(tid, NULL)) { + printf("Failed to join thread %d.\n", tid); + } + + printf("Exit\n"); + + return 0; +} +``` +**Build with libc-builtin** + +To build this C program into WebAssembly app with libc-builtin, you can use this command: +``` bash +/opt/wasi-sdk/bin/clang --target=wasm32 \ + --sysroot=${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot \ + -O3 -pthread -nostdlib -z stack-size=32768 \ + -Wl,--shared-memory \ + -Wl,--initial-memory=131072,--max-memory=131072 \ + -Wl,--allow-undefined-file=${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-entry -Wl,--export=main \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + main.c -o test.wasm +# -pthread: it will enable some dependent WebAssembly features for thread +# -nostdlib: disable the WASI standard library as we are using WAMR builtin-libc +# -z stack-size=: specify the total aux stack size +# -Wl,--export=__heap_base,--export=__data_end: export these globals so the runtime can resolve the total aux stack size and the start offset of the stack top +# -Wl,--export=__wasm_call_ctors: export the init function to initialize the passive data segments +``` + +**Build with libc-WASI** + +You can also build this program with WASI, but we need to make some changes to wasi-sysroot: + +1. disable malloc / free of wasi as they don't support shared memory + ``` bash + /opt/wasi-sdk/bin/llvm-ar -d /opt/wasi-sdk/share/wasi-sysroot/lib/wasm32-wasi/libc.a dlmalloc.o + ``` +2. copy the pthread.h to wasi-sysroot so the compiler can find it: + ``` bash + cp ${WAMR_ROOT}/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h /opt/wasi-sdk/share/wasi-sysroot/include + ``` +> Note:
    +>1. Remember to back up the original sysroot files + +Then build the program with this command: +``` bash +/opt/wasi-sdk/bin/clang -pthread -O3 \ + -Wl,--shared-memory,--max-memory=196608 \ + -Wl,--allow-undefined,--no-check-features \ + -Wl,--export=__heap_base,--export=__data_end \ + main.c -o test.wasm +# -Wl,--no-check-features: the errno.o in wasi-sysroot is not compatible with pthread feature, pass this option to avoid errors +``` + +**Build AoT module** + +You can build the wasm module into AoT module with pthread support, please pass option `--enable-multi-thread` to wamrc: +``` bash +wamrc --enable-multi-thread -o test.aot test.wasm +``` + +Currently WAMR disables pthread library by default. To run the module with pthread support, please build the runtime with `-DWAMR_BUILD_LIB_PTHREAD=1` +``` bash +cd ${WAMR_ROOT}/product-mini/platforms/linux +mkdir build && cd build +cmake .. -DWAMR_BUILD_LIB_PTHREAD=1 +make +# Then you can run the wasm module above: +./iwasm test.wasm +# Or the AoT module: +# ./iwasm test.aot +``` + +[Here](../samples/multi-thread) is also a sample to show how wasm-apps use pthread APIs to create threads, and how to build it with cmake. You can build this sample and have a try: +``` bash +cd ${WAMR_ROOT}/samples/multi-thread +mkdir build && cd build +cmake .. +make +# Run wasm application +./iwasm wasm-apps/test.wasm +``` + + +## Aux stack seperation +The compiler may use some spaces in the linear memory as an auxiliary stack. When pthread is enabled, every thread should have its own aux stack space, so the total aux stack space reserved by the compiler will be divided into N + 1 parts, where N is the maximum number of threads that can be created by the user code. + +The default value of N is 4, which means you can create 4 threads at most. This value can be changed by an option if you are using product-mini: +``` bash +./iwasm --max-threads=n test.wasm +``` +If you are going to develop your own runtime product, you can use the API `wasm_runtime_set_max_thread_num` or init arg `init_args.max_thread_num` to set the value, or you can change the macro `CLUSTER_MAX_THREAD_NUM` in [config.h](../core/config.h). + +> Note: the total size of aux stack reserved by compiler can be set with `-z stack-size` option during compilation. If you need to create more threads, please set a larger value, otherwise it is easy to cause aux stack overflow. + +## Supported APIs +``` C +/* Thread APIs */ +int pthread_create(pthread_t *thread, const void *attr, + void *(*start_routine) (void *), void *arg); + +int pthread_join(pthread_t thread, void **retval); + +int pthread_detach(pthread_t thread); + +int pthread_cancel(pthread_t thread); + +pthread_t pthread_self(void); + +void pthread_exit(void *retval); + +/* Mutex APIs */ +int pthread_mutex_init(pthread_mutex_t *mutex, const void *attr); + +int pthread_mutex_lock(pthread_mutex_t *mutex); + +int pthread_mutex_unlock(pthread_mutex_t *mutex); + +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +/* Cond APIs */ +int pthread_cond_init(pthread_cond_t *cond, const void *attr); + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + unsigned int useconds); + +int pthread_cond_signal(pthread_cond_t *cond); + +int pthread_cond_destroy(pthread_cond_t *cond); + +/* Pthread key APIs */ +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); + +int pthread_setspecific(pthread_key_t key, const void *value); + +void *pthread_getspecific(pthread_key_t key); + +int pthread_key_delete(pthread_key_t key); +``` + +## Known limits +- `pthread_attr_t`, `pthread_mutexattr_t` and `pthread_condattr_t` are not supported yet, so please pass `NULL` as the second argument of `pthread_create`, `pthread_mutex_init` and `pthread_cond_init`. +- The `errno.o` in wasi-sysroot is not compatible with this feature, so using errno in multi-thread may cause unexpected behavior. +- Currently `struct timespec` is not supported, so the prototype of `pthread_cond_timedwait` is different from the native one, it takes an unsigned int argument `useconds` to indicate the waiting time. \ No newline at end of file diff --git a/wamr/doc/release_ack.md b/wamr/doc/release_ack.md new file mode 100644 index 0000000..a0adfff --- /dev/null +++ b/wamr/doc/release_ack.md @@ -0,0 +1,61 @@ +Major feature releases and contributors +========================================= + + +**May 07, 2019: WAMR first GitHub release** + +- Contributors: Wenyong Huang, Weining Lu, Lei Shi, Li Tian, Jizhao Zhang, Yi Zhang, Daoming Qiu, Xin Wang (Intel) + +**May 17, 2019: Application manager, WASM APP API, samples and test tools** + +- Contributors: Wenyong Huang, Weining Lu, Lei Shi, Li Tian, Jizhao Zhang, Yi Zhang, Daoming Qiu, Xin Wang (Intel) + + +**May 23, 2019: Support AliOS Things** + +- Contributor: JinZhou Zhu (Alibaba) + +**May 24, 2019: Support memory usage profiler** + +- Contributors Wenyong Huang (Intel) + +**Jun 11, 2019: Add WASM APP API connection** + + +- Contributor: Weining Lu (Intel) + +**Jun 10, 2019: Support VxWorks** + +- Contributor: Yiting Wang (WindRiver) + +**Aug 1, 2019: Add WGL graphic user interface API** + +- Contributor: Weining Lu + +**Aug 14, 2019: Add Docker support** + + +- Contributor: beriberikix + + +**Aug 14, 2019: WASM IoT app store demo** + + +- Contributor: Luhanzhi Li, Jun Xu (Intel) + + +**Aug 28, 2019: SGX support** + + +- Contributor: Mic Bowman (Intel) + + +**Sep 6, 2019: Mac platform support** + + +- Contributor: Jonathan Dong (Alibaba) + +**Nov 2019: WASI support** (Intel) + +**Jan 2020: Ahead of time and Just-in-Time compilation support** (Intel) + diff --git a/wamr/doc/roadmap.md b/wamr/doc/roadmap.md new file mode 100644 index 0000000..c5c7b84 --- /dev/null +++ b/wamr/doc/roadmap.md @@ -0,0 +1,23 @@ + +# WebAssembly Micro Runtime Roadmap + + + +## Data serialization +Evaluating using cbor as the default data serialization + +No plan yet. + + + +## Threading +Plan: 2020 Q1 + + + +## AssemblyScript Support and API + +Currently under evaluation + + + diff --git a/wamr/doc/wamr_api.md b/wamr/doc/wamr_api.md new file mode 100644 index 0000000..b3cc45b --- /dev/null +++ b/wamr/doc/wamr_api.md @@ -0,0 +1,351 @@ + +WAMR application framework +======================== + +## Application system callbacks +The `on_init` and `on_destroy` functions are wamr application system callbacks which must be implemented in the wasm application if you want to use the APP framework. +``` C +void on_init() +{ + /* + Your init functions here, for example: + * platform initialization + * timer registration + * service / event registration + * ...... + */ +} + +void on_destroy() +{ + /* + your destroy functions here + */ +} +``` + +## Base App library + +The base library of application framework supports the essential API for WASM applications, such as inter-app communication, timers, etc. Other application framework components rely on the base library. + +When building the WAMR SDK, once application framework is enabled, the base library will automatically enabled. + +### Timer +The *timer* API's can be used to create some `soft timers` with single-shot mode or periodic mode. Here is a reference of how to use timer API's to execute a function every one second. +``` C +/* User global variable */ +static int num = 0; + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + printf("Timer update %d\n", num++); +} + +void on_init() +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_destroy() +{ + +} +``` + +### Micro-service model (request/response) +The microservice model is also known as request and response model. One WASM application acts as the server which provides a specific service. Other WASM applications or host/cloud applications request that service and get the response. + +

    + +Below is the reference implementation of the server application. It provides room temperature measurement service. + +``` C +void on_init() +{ + api_register_resource_handler("/room_temp", room_temp_handler); +} + +void on_destroy() +{ +} + +void room_temp_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + payload = attr_container_create("room_temp payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "temp unit", "centigrade"); + attr_container_set_int(&payload, "value", 26); + + make_response_for_request(request, response); + set_response(response, + CONTENT_2_05, + FMT_ATTR_CONTAINER, + payload, + attr_container_get_serialize_length(payload)); + + api_response_send(response); + attr_container_destroy(payload); +} +``` + + +### Pub/sub model +One WASM application acts as the event publisher. It publishes events to notify WASM applications or host/cloud applications which subscribe to the events. + +
    + +Below is the reference implementation of the pub application. It utilizes a timer to repeatedly publish an overheat alert event to the subscriber applications. Then the subscriber applications receive the events immediately. + +``` C +/* Timer callback */ +void timer_update(user_timer_t timer +{ + attr_container_t *event; + + event = attr_container_create("event"); + attr_container_set_string(&event, + "warning", + "temperature is over high"); + + api_publish_event("alert/overheat", + FMT_ATTR_CONTAINER, + event, + attr_container_get_serialize_length(event)); + + attr_container_destroy(event); +} + +void on_init() +{ + user_timer_t timer; + timer = api_timer_create(1000, true, true, timer_update); +} + +void on_destroy() +{ +} +``` + +Below is the reference implementation of the sub application. +``` C +void overheat_handler(request_t *event) +{ + printf("Event: %s\n", event->url); + + if (event->payload != NULL && event->fmt == FMT_ATTR_CONTAINER) + attr_container_dump((attr_container_t *) event->payload); +} + +void on_init( +{ + api_subscribe_event ("alert/overheat", overheat_handler); +} + +void on_destroy() +{ +} +``` +**Note:** You can also subscribe this event from host side by using host tool. Please refer `samples/simple` project for detail usage. + + +## Sensor API + +The API set is defined in the header file ```core/app-framework/sensor/app/wa-inc/sensor.h```. + +Here is a reference of how to use sensor API's: + +``` C +static sensor_t sensor = NULL; + +/* Sensor event callback*/ +void sensor_event_handler(sensor_t sensor, attr_container_t *event, + void *user_data) +{ + printf("### app get sensor event\n"); + attr_container_dump(event); +} + +void on_init() +{ + char *user_data; + attr_container_t *config; + + printf("### app on_init 1\n"); + /* open a sensor */ + user_data = malloc(100); + printf("### app on_init 2\n"); + sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); + printf("### app on_init 3\n"); + + /* config the sensor */ + sensor_config(sensor, 1000, 0, 0); + printf("### app on_init 4\n"); +} + +void on_destroy() +{ + if (NULL != sensor) { + sensor_config(sensor, 0, 0, 0); + } +} +``` + +## Connection API: + +The API set is defined in the header file `core/app-framework/connection/app/wa-inc/connection.h` + +Here is a reference of how to use connection API's: +``` C +/* User global variable */ +static int num = 0; +static user_timer_t g_timer; +static connection_t *g_conn = NULL; + +void on_data1(connection_t *conn, + conn_event_type_t type, + const char *data, + uint32 len, + void *user_data) +{ + if (type == CONN_EVENT_TYPE_DATA) { + char message[64] = {0}; + memcpy(message, data, len); + printf("Client got a message from server -> %s\n", message); + } else if (type == CONN_EVENT_TYPE_DISCONNECT) { + printf("connection is close by server!\n"); + } else { + printf("error: got unknown event type!!!\n"); + } +} + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + char message[64] = {0}; + /* Reply to server */ + snprintf(message, sizeof(message), "Hello %d", num++); + api_send_on_connection(g_conn, message, strlen(message)); +} + +void my_close_handler(request_t * request) +{ + response_t response[1]; + + if (g_conn != NULL) { + api_timer_cancel(g_timer); + api_close_connection(g_conn); + } + + make_response_for_request(request, response); + set_response(response, DELETED_2_02, 0, NULL, 0); + api_response_send(response); +} + +void on_init() +{ + user_timer_t timer; + attr_container_t *args; + char *str = "this is client!"; + + api_register_resource_handler("/close", my_close_handler); + + args = attr_container_create(""); + attr_container_set_string(&args, "address", "127.0.0.1"); + attr_container_set_uint16(&args, "port", 7777); + + g_conn = api_open_connection("TCP", args, on_data1, NULL); + if (g_conn == NULL) { + printf("connect to server fail!\n"); + return; + } + + printf("connect to server success! handle: %p\n", g_conn); + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_destroy() +{ + +} +``` + +## GUI API + +The API's is listed in header file ```core/app-framework/wgl/app/wa-inc/wgl.h``` which is implemented based on open soure 2D graphic library [LittlevGL](https://docs.littlevgl.com/en/html/index.html). + +``` C +static void btn_event_cb(wgl_obj_t btn, wgl_event_t event); + +uint32_t count = 0; +char count_str[11] = { 0 }; +wgl_obj_t hello_world_label; +wgl_obj_t count_label; +wgl_obj_t btn1; +wgl_obj_t label_count1; +int label_count1_value = 0; +char label_count1_str[11] = { 0 }; + +void timer1_update(user_timer_t timer1) +{ + if ((count % 100) == 0) { + snprintf(count_str, sizeof(count_str), "%d", count / 100); + wgl_label_set_text(count_label, count_str); + } + ++count; +} + +void on_init() +{ + hello_world_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); + wgl_label_set_text(hello_world_label, "Hello world!"); + wgl_obj_align(hello_world_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_LEFT, 0, 0); + + count_label = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); + wgl_obj_align(count_label, (wgl_obj_t)NULL, WGL_ALIGN_IN_TOP_MID, 0, 0); + + btn1 = wgl_btn_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); /*Create a button on the currently loaded screen*/ + wgl_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ + wgl_obj_align(btn1, (wgl_obj_t)NULL, WGL_ALIGN_CENTER, 0, 0); /*Align below the label*/ + + /*Create a label on the button*/ + wgl_obj_t btn_label = wgl_label_create(btn1, (wgl_obj_t)NULL); + wgl_label_set_text(btn_label, "Click ++"); + + label_count1 = wgl_label_create((wgl_obj_t)NULL, (wgl_obj_t)NULL); + wgl_label_set_text(label_count1, "0"); + wgl_obj_align(label_count1, (wgl_obj_t)NULL, WGL_ALIGN_IN_BOTTOM_MID, 0, 0); + + /* set up a timer */ + user_timer_t timer; + timer = api_timer_create(10, true, false, timer1_update); + if (timer) + api_timer_restart(timer, 10); + else + printf("Fail to create timer.\n"); +} + +static void btn_event_cb(wgl_obj_t btn, wgl_event_t event) +{ + if(event == WGL_EVENT_RELEASED) { + label_count1_value++; + snprintf(label_count1_str, sizeof(label_count1_str), + "%d", label_count1_value); + wgl_label_set_text(label_count1, label_count1_str); + } +} + +``` + +Currently supported widgets include button, label, list and check box and more widgets would be provided in future. diff --git a/wamr/doc/wasm_c_api.md b/wamr/doc/wasm_c_api.md new file mode 100644 index 0000000..6d22787 --- /dev/null +++ b/wamr/doc/wasm_c_api.md @@ -0,0 +1,77 @@ +All samples come from the commit 340fd9528cc3b26d22fe30ee1628c8c3f2b8c53b +of [wasm-c-api][https://github.com/WebAssembly/wasm-c-api]. + +Every user should be familiar with *APIs* listed in +[wasm.h][https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h]. + +all [examples][https://github.com/WebAssembly/wasm-c-api/tree/master/example] are +very helpful for learning. + +Currently, we support partial of *APIs* and are going to support the rest of +them in next releases. + +Supported APIs: + +``` c +/* wasm_bytevec_t APIs ... */ + +wasm_engine_t *wasm_engine_new(); +wasm_engine_t *wasm_engine_new_with_args(mem_alloc_type_t, const MemAllocOption*, runtime_mode_e); +void wasm_engine_delete(wasm_engine_t *); + +wasm_store_t *wasm_store_new(wasm_engine_t *); +void wasm_store_delete(wasm_store_t *); + +/* wasm_valtype_t APIs ... */ +/* wasm_valtype_vec_t APIs ... */ +/* wasm_functype_vec_t APIs ... */ +/* wasm_globaltype_vec_t APIs ... */ +/* wasm_val_t APIs ... */ +/* wasm_trap_t partial APIs ... */ + +wasm_module_t *wasm_module_new(wasm_store_t *, const wasm_byte_vec_t *); +void wasm_module_delete(wasm_module_t *); + +wasm_func_t *wasm_func_new(wasm_store_t *, const wasm_functype_t *, wasm_func_callback_t); +wasm_func_t *wasm_func_new_with_env(wasm_store_t *store, const wasm_functype_t *, wasm_func_callback_with_env_t, void *env, void (*finalizer)(void *)); +void wasm_func_delete(wasm_func_t *); +wasm_fucn_t *wasm_func_copy(const wasm_func_t *); +wasm_functype_t *wasm_func_type(const wasm_func_t *); +wasm_trap_t * wasm_func_call(const wasm_func_t *, const wasm_val_t params[], wasm_val_t results[]); +size_t wasm_func_param_arity(const wasm_func_t *); +size_t wasm_func_result_arity(const wasm_func_t *); + +wasm_global_t *wasm_global_new(wasm_store_t *, const wasm_globaltype_t *, const wasm_val_t *); +wasm_global_t * wasm_global_copy(const wasm_global_t *); +void wasm_global_delete(wasm_global_t *); +bool wasm_global_same(const wasm_global_t *, const wasm_global_t *); +void wasm_global_set(wasm_global_t *, const wasm_val_t *); +void wasm_global_get(const wasm_global_t *, wasm_val_t *out); +wasm_globaltype_t * wasm_global_type(const wasm_global_t *); + +wasm_instance_t *wasm_instance_new(wasm_store_t *, const wasm_module_t *, const wasm_extern_t *const imports[], wasm_trap_t **traps); +void wasm_instance_delete(wasm_instance_t *); +void wasm_instance_exports(const wasm_instance_t *, wasm_extern_vec_t *out); + +/* wasm_extern_t APIs */ +``` + +Unsupported APIs: + +``` c +/* wasm_tabletype_t APIs */ +/* wasm_memorytype_t APIs */ +/* wasm_externtype_t APIs */ +/* wasm_importtype_t APIs */ +/* wasm_exporttype_t APIs */ +/* wasm_ref_t APIs */ +/* wasm_shared_##name##_t APIs */ + +WASM_API_EXTERN bool wasm_module_validate(wasm_store_t*, const wasm_byte_vec_t* binary); +WASM_API_EXTERN void wasm_module_imports(const wasm_module_t*, own wasm_importtype_vec_t* out); +WASM_API_EXTERN void wasm_module_serialize(const wasm_module_t*, own wasm_byte_vec_t* out); +WASM_API_EXTERN own wasm_module_t* wasm_module_deserialize(wasm_store_t*, const wasm_byte_vec_t*); + +/* wasm_table_t APIs */ +/* wasm_memory_t APIs */ +``` diff --git a/wamr/product-mini/app-samples/hello-world-cmake/CMakeLists.txt b/wamr/product-mini/app-samples/hello-world-cmake/CMakeLists.txt new file mode 100644 index 0000000..b41fe0a --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world-cmake/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.5) +project(hello_world) + +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-unused-command-line-argument") +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--export=main") + +add_library(print print.c) + +add_executable(hello_world main.c) +target_link_libraries(hello_world print) \ No newline at end of file diff --git a/wamr/product-mini/app-samples/hello-world-cmake/build.sh b/wamr/product-mini/app-samples/hello-world-cmake/build.sh new file mode 100755 index 0000000..7b37eb2 --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world-cmake/build.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -rf build +mkdir build +cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=../../../../wamr-sdk/app/wamr_toolchain.cmake +make \ No newline at end of file diff --git a/wamr/product-mini/app-samples/hello-world-cmake/main.c b/wamr/product-mini/app-samples/hello-world-cmake/main.c new file mode 100644 index 0000000..1c356c4 --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world-cmake/main.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "stdio.h" + +void print_line(char* str); + +int main() +{ + print_line("Hello World!"); + print_line("Wasm Micro Runtime"); + return 0; +} \ No newline at end of file diff --git a/wamr/product-mini/app-samples/hello-world-cmake/print.c b/wamr/product-mini/app-samples/hello-world-cmake/print.c new file mode 100644 index 0000000..8eee6b6 --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world-cmake/print.c @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "stdio.h" +#include "string.h" + +void print_line(char* str) +{ + printf("%s\n", str); +} \ No newline at end of file diff --git a/wamr/product-mini/app-samples/hello-world/build.sh b/wamr/product-mini/app-samples/hello-world/build.sh new file mode 100755 index 0000000..9407c70 --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world/build.sh @@ -0,0 +1,14 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +WAMR_DIR=${PWD}/../../.. + +/opt/wasi-sdk/bin/clang \ + --target=wasm32 -O3 \ + -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,--export=main, \ + -Wl,--no-threads,--strip-all,--no-entry \ + -nostdlib -o test.wasm *.c +#./jeffdump -o test_wasm.h -n wasm_test_file test.wasm diff --git a/wamr/product-mini/app-samples/hello-world/main.c b/wamr/product-mini/app-samples/hello-world/main.c new file mode 100644 index 0000000..d392fb1 --- /dev/null +++ b/wamr/product-mini/app-samples/hello-world/main.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +int main(int argc, char **argv) +{ + char *buf; + + printf("Hello world!\n"); + + buf = malloc(1024); + if (!buf) { + printf("malloc buf failed\n"); + return -1; + } + + printf("buf ptr: %p\n", buf); + + snprintf(buf, 1024, "%s", "1234\n"); + printf("buf: %s", buf); + + free(buf); + return 0; +} diff --git a/wamr/product-mini/platforms/alios-things/aos.mk b/wamr/product-mini/platforms/alios-things/aos.mk new file mode 100644 index 0000000..4cdf95d --- /dev/null +++ b/wamr/product-mini/platforms/alios-things/aos.mk @@ -0,0 +1,115 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +NAME := iwasm +CORE_ROOT := wamr/core +IWASM_ROOT := wamr/core/iwasm +SHARED_ROOT := wamr/core/shared + +GLOBAL_DEFINES += BH_MALLOC=wasm_runtime_malloc +GLOBAL_DEFINES += BH_FREE=wasm_runtime_free + +# Change it to THUMBV7M if you want to build for developerkit +WAMR_BUILD_TARGET := X86_32 + +WAMR_BUILD_PLATFORM := alios-things + +ifeq (${WAMR_BUILD_TARGET}, X86_32) + GLOBAL_DEFINES += BUILD_TARGET_X86_32 + INVOKE_NATIVE := invokeNative_ia32.s + AOT_RELOC := aot_reloc_x86_32.c +else ifeq (${WAMR_BUILD_TARGET}, X86_64) + GLOBAL_DEFINES += BUILD_TARGET_X86_64 + INVOKE_NATIVE := invokeNative_em64.s + AOT_RELOC := aot_reloc_x86_64.c +else ifeq ($(findstring ARM,$(WAMR_BUILD_TARGET)), ARM) + GLOBAL_DEFINES += BUILD_TARGET_ARM + GLOBAL_DEFINES += BUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" + INVOKE_NATIVE := invokeNative_arm.s + AOT_RELOC := aot_reloc_arm.c +else ifeq ($(findstring THUMB,$(WAMR_BUILD_TARGET)), THUMB) + GLOBAL_DEFINES += BUILD_TARGET_THUMB + GLOBAL_DEFINES += BUILD_TARGET=\"$(WAMR_BUILD_TARGET)\" + INVOKE_NATIVE := invokeNative_thumb.s + AOT_RELOC := aot_reloc_thumb.c +else ifeq (${WAMR_BUILD_TARGET}, MIPS) + GLOBAL_DEFINES += BUILD_TARGET_MIPS + INVOKE_NATIVE := invokeNative_mips.s + AOT_RELOC := aot_reloc_mips.c +else ifeq (${WAMR_BUILD_TARGET}, XTENSA) + GLOBAL_DEFINES += BUILD_TARGET_XTENSA + INVOKE_NATIVE := invokeNative_xtensa.s + AOT_RELOC := aot_reloc_xtensa.c +else + $(error Build target isn't set) +endif + +# Enable Interpreter by default. +WAMR_BUILD_INTERP = 1 + +# Enable AOT by default. +WAMR_BUILD_AOT = 1 + +ifeq (${WAMR_BUILD_INTERP}, 1) +GLOBAL_DEFINES += WASM_ENABLE_INTERP=1 +endif + +ifeq (${WAMR_BUILD_AOT}, 1) +GLOBAL_DEFINES += WASM_ENABLE_AOT=1 +endif + +GLOBAL_DEFINES += WASM_ENABLE_LIBC_BUILTIN=1 + +GLOBAL_INCLUDES += ${CORE_ROOT} \ + ${IWASM_ROOT}/include \ + ${IWASM_ROOT}/common \ + ${SHARED_ROOT}/include \ + ${SHARED_ROOT}/platform/include \ + ${SHARED_ROOT}/utils \ + ${SHARED_ROOT}/mem-alloc \ + ${SHARED_ROOT}/platform/alios + +ifeq (${WAMR_BUILD_INTERP}, 1) +GLOBAL_INCLUDES += ${IWASM_ROOT}/interpreter +endif + +ifeq (${WAMR_BUILD_AOT}, 1) +GLOBAL_INCLUDES += ${IWASM_ROOT}/aot +endif + +$(NAME)_SOURCES := ${SHARED_ROOT}/platform/alios/alios_platform.c \ + ${SHARED_ROOT}/platform/alios/alios_thread.c \ + ${SHARED_ROOT}/platform/alios/alios_time.c \ + ${SHARED_ROOT}/platform/common/math/math.c \ + ${SHARED_ROOT}/mem-alloc/mem_alloc.c \ + ${SHARED_ROOT}/mem-alloc/ems/ems_kfc.c \ + ${SHARED_ROOT}/mem-alloc/ems/ems_alloc.c \ + ${SHARED_ROOT}/mem-alloc/ems/ems_hmu.c \ + ${SHARED_ROOT}/utils/bh_assert.c \ + ${SHARED_ROOT}/utils/bh_common.c \ + ${SHARED_ROOT}/utils/bh_hashmap.c \ + ${SHARED_ROOT}/utils/bh_list.c \ + ${SHARED_ROOT}/utils/bh_log.c \ + ${SHARED_ROOT}/utils/bh_queue.c \ + ${SHARED_ROOT}/utils/bh_vector.c \ + ${SHARED_ROOT}/utils/runtime_timer.c \ + ${IWASM_ROOT}/libraries/libc-builtin/libc_builtin_wrapper.c \ + ${IWASM_ROOT}/common/wasm_runtime_common.c \ + ${IWASM_ROOT}/common/wasm_native.c \ + ${IWASM_ROOT}/common/wasm_exec_env.c \ + ${IWASM_ROOT}/common/wasm_memory.c \ + ${IWASM_ROOT}/common/arch/${INVOKE_NATIVE} \ + src/main.c + +ifeq (${WAMR_BUILD_INTERP}, 1) +$(NAME)_SOURCES += ${IWASM_ROOT}/interpreter/wasm_interp_classic.c \ + ${IWASM_ROOT}/interpreter/wasm_loader.c \ + ${IWASM_ROOT}/interpreter/wasm_runtime.c +endif + +ifeq (${WAMR_BUILD_AOT}, 1) +$(NAME)_SOURCES += ${IWASM_ROOT}/aot/aot_loader.c \ + ${IWASM_ROOT}/aot/arch/${AOT_RELOC} \ + ${IWASM_ROOT}/aot/aot_runtime.c +endif + diff --git a/wamr/product-mini/platforms/alios-things/src/main.c b/wamr/product-mini/platforms/alios-things/src/main.c new file mode 100644 index 0000000..1c14843 --- /dev/null +++ b/wamr/product-mini/platforms/alios-things/src/main.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "bh_platform.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "test_wasm.h" + +static int app_argc; +static char **app_argv; + +/** + * Find the unique main function from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the main function is called, false otherwise. + */ +bool +wasm_application_execute_main(wasm_module_inst_t module_inst, + int32_t argc, char *argv[]); + +static void* +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static char global_heap_buf[256 * 1024] = { 0 }; + +void iwasm_main(void *arg1) +{ + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + + (void) arg1; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8*) wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail1; + } + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + 8 * 1024, + 8 * 1024, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); +} + +#define DEFAULT_THREAD_STACKSIZE (6 * 1024) +#define DEFAULT_THREAD_PRIORITY 50 + +bool iwasm_init(void) +{ + int ret = aos_task_new("wasm-main", iwasm_main, NULL, + DEFAULT_THREAD_STACKSIZE); + return ret == 0 ? true : false; +} + diff --git a/wamr/product-mini/platforms/alios-things/src/test_wasm.h b/wamr/product-mini/platforms/alios-things/src/test_wasm.h new file mode 100644 index 0000000..65b8347 --- /dev/null +++ b/wamr/product-mini/platforms/alios-things/src/test_wasm.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * The byte array buffer is the file content of a test wasm binary file, + * which is compiled by emcc or clang toolchain from C source file of: + * core/iwasm/app-samples/hello-world/main.c. + */ +unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, + 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, + 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, + 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, + 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, + 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, + 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, + 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, + 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, + 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, + 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, + 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, + 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, + 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, + 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, + 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, + 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, + 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, + 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, + 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, + 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, + 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, + 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, + 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, + 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, + 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, + 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, + 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, + 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, + 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, + 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, + 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, + 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, + 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, + 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, + 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, + 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; diff --git a/wamr/product-mini/platforms/android/CMakeLists.txt b/wamr/product-mini/platforms/android/CMakeLists.txt new file mode 100644 index 0000000..26f9c73 --- /dev/null +++ b/wamr/product-mini/platforms/android/CMakeLists.txt @@ -0,0 +1,105 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 3.4.1) + +set (CMAKE_VERBOSE_MAKEFILE on) +set (CMAKE_BUILD_TYPE Release) + +set (CMAKE_TOOLCHAIN_FILE "$ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake") +set (ANDROID_NDK $ENV{ANDROID_NDK_HOME}) +set (ANDROID_SDK $ENV{ANDROID_SDK_HOME}) +set (ANDROID_ABI "x86") +set (ANDROID_LD lld) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "android") +set (WAMR_BUILD_TARGET "X86_32") +set (WAMR_BUILD_TYPE Release) +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 0) + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +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} -Wconversion -Wsign-conversion") + +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#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_library (iwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) +if (CMAKE_BUILD_TYPE STREQUAL Release) +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -landroid -llog -s) +else() +target_link_libraries (iwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -landroid -llog) +endif() + +set (distribution_DIR ${CMAKE_CURRENT_SOURCE_DIR}/build/distribution) +set_target_properties (iwasm PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${distribution_DIR}/wasm/lib") + +add_custom_command (TARGET iwasm POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${WAMR_ROOT_DIR}/core/iwasm/include" "${distribution_DIR}/wasm/include/" + COMMENT "Copying iwasm to output directory") diff --git a/wamr/product-mini/platforms/android/build_jit.sh b/wamr/product-mini/platforms/android/build_jit.sh new file mode 100755 index 0000000..908d156 --- /dev/null +++ b/wamr/product-mini/platforms/android/build_jit.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -fr build && mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +make +cd .. diff --git a/wamr/product-mini/platforms/android/build_llvm.sh b/wamr/product-mini/platforms/android/build_llvm.sh new file mode 100755 index 0000000..d585792 --- /dev/null +++ b/wamr/product-mini/platforms/android/build_llvm.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +DEPS_DIR=${PWD}/../../../core/deps + +cd ${DEPS_DIR} +if [ ! -d "llvm" ]; then + echo "Clone llvm to core/deps/ .." + git clone https://github.com/llvm-mirror/llvm.git +fi + +cd llvm +mkdir -p build +cd build + +if [ ! -f bin/llvm-lto ]; then + + CORE_NUM=$(nproc --all) + if [ -z "${CORE_NUM}" ]; then + CORE_NUM=1 + fi + + echo "Build llvm with" ${CORE_NUM} "cores" + + cmake .. \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ + -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_APPEND_VC_REV:BOOL=OFF + make -j ${CORE_NUM} + +else + echo "llvm has already been built" +fi + +cd ${PWD} + diff --git a/wamr/product-mini/platforms/android/wasm-jni.cpp b/wamr/product-mini/platforms/android/wasm-jni.cpp new file mode 100644 index 0000000..8272236 --- /dev/null +++ b/wamr/product-mini/platforms/android/wasm-jni.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LOGI(...) \ + ((void)__android_log_print(ANDROID_LOG_INFO, "wasm_jni::", __VA_ARGS__)) + +static void * +app_instance_main(wasm_module_inst_t module_inst) { + const char *exception; + + wasm_application_execute_main(module_inst, 0, NULL); + if ((exception = wasm_runtime_get_exception(module_inst))) + LOGI("%s\n", exception); + return NULL; +} + +// WARNING! CAN NOT BE READ ONLY!!! +static unsigned char wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, + 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, + 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, + 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, + 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, + 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, + 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, + 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, + 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, + 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, + 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, + 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, + 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, + 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, + 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, + 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, + 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, + 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, + 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, + 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, + 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, + 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, + 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, + 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, + 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, + 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, + 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, + 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, + 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, + 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, + 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, + 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, + 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, + 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, + 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, + 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, + 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; + + +extern "C" JNIEXPORT void JNICALL +Java_com_intel_wasm_api_Runtime_run(JNIEnv *env, jclass thiz) { + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + uint wasm_file_size = 0; + uint8_t *wasm_file_buf = NULL; + char error_buf[128] = {0}; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = (void*)malloc; + init_args.mem_alloc_option.allocator.realloc_func = (void*)realloc; + init_args.mem_alloc_option.allocator.free_func = (void*)free; + + LOGI("wasm_runtime_full_init"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + LOGI("Init runtime failed.\n"); + return; + } + + /* load WASM byte buffer from a preinstall WASM bin file */ + LOGI("use an internal test file, gona to output Hello World in logcat\n"); + wasm_file_buf = (uint8_t*) wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + + /* load WASM module */ + LOGI("wasm_runtime_load"); + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + LOGI("in wasm_runtime_load %s\n", error_buf); + LOGI("goto fail1\n"); + goto fail1; + } + + /* instantiate the module */ + LOGI("wasm_runtime_instantiate"); + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + 64 * 1024, /* stack size */ + 64 * 1024, /* heap size */ + error_buf, + sizeof(error_buf)))) { + LOGI("%s\n", error_buf); + LOGI("goto fail2\n"); + goto fail2; + } + + LOGI("run main() of the application"); + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + LOGI("wasm_runtime_deinstantiate"); + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + LOGI("wasm_runtime_unload"); + wasm_runtime_unload(wasm_module); + +fail1: + // in our case, we don't need a free, but it is not a typical one + /* free the file buffer */ + //bh_free((void *) wasm_file_buf); + + /* destroy runtime environment */ + LOGI("wasm_runtime_destroy"); + wasm_runtime_destroy(); + return; +} diff --git a/wamr/product-mini/platforms/darwin/CMakeLists.txt b/wamr/product-mini/platforms/darwin/CMakeLists.txt new file mode 100644 index 0000000..352b6fb --- /dev/null +++ b/wamr/product-mini/platforms/darwin/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "darwin") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-U,_get_ext_lib_export_apis") +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + +set (CMAKE_MACOSX_RPATH True) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) + +add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) + diff --git a/wamr/product-mini/platforms/darwin/main.c b/wamr/product-mini/platforms/darwin/main.c new file mode 100644 index 0000000..defc0d9 --- /dev/null +++ b/wamr/product-mini/platforms/darwin/main.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +static int app_argc; +static char **app_argv; + +static int print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify function name to run in module\n" + " rather than main\n"); +#if WASM_ENABLE_LOG != 0 + printf(" -v=n Set log verbose level (0 to 5, default is 2),\n" + " larger level with more log\n"); +#endif + printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); +#if WASM_ENABLE_LIBC_WASI != 0 + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir=
    Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); +#endif + + return 1; +} + +static void* +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static void* +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return NULL; +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + if (count) { + *count = idx - 1; + } + return res; +} + +static void* +app_instance_repl(wasm_module_inst_t module_inst) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { + bh_assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} +#endif + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +int main(int argc, char *argv[]) +{ + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; +#if WASM_ENABLE_LIBC_WASI != 0 + const char *dir_list[8] = { NULL }; + uint32 dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32 env_list_size = 0; +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + print_help(); + return 0; + } + func_name = argv[0]; + } +#if WASM_ENABLE_LOG != 0 + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) + is_repl_mode = true; + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_LIBC_WASI != 0 + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char*))); + return -1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char*)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char*))); + return -1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", + tmp_env); + return print_help(); + } + } +#endif + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = argv; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + 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); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + bh_log_set_verbose_level(log_verbose_level); + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, + &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_runtime_set_wasi_args(wasm_module, + dir_list, dir_list_size, + NULL, 0, + env_list, env_list_size, + argv, argc); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + stack_size, + heap_size, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst); + else if (func_name) + app_instance_func(wasm_module_inst, func_name); + else + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} + diff --git a/wamr/product-mini/platforms/linux-sgx/CMakeLists.txt b/wamr/product-mini/platforms/linux-sgx/CMakeLists.txt new file mode 100644 index 0000000..4ea3879 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/CMakeLists.txt @@ -0,0 +1,90 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "linux-sgx") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default + # Please install Intel SGX SDKv2.8 or later. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Enable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 1) +endif () + +if (COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") +endif () + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -ffunction-sections -fdata-sections \ + -Wall -Wno-unused-parameter -Wno-pedantic \ + -nostdinc -fvisibility=hidden -fpie" ) + +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_custom_command ( + OUTPUT libvmlib_untrusted.a + COMMAND mkdir -p untrusted && cd untrusted && + ${CMAKE_C_COMPILER} -c ${PLATFORM_SHARED_SOURCE_UNTRUSTED} + COMMAND ${CMAKE_AR} rc libvmlib_untrusted.a untrusted/*.o) + +add_custom_target (vmlib_untrusted ALL DEPENDS libvmlib_untrusted.a) diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp b/wamr/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp new file mode 100644 index 0000000..3509661 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/App/App.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Enclave_u.h" +#include "sgx_urts.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define TOKEN_FILENAME "enclave.token" +#define ENCLAVE_FILENAME "enclave.signed.so" +#define MAX_PATH 1024 + +#define TEST_OCALL_API 0 + +static sgx_enclave_id_t g_eid = 0; + +void +ocall_print(const char* str) +{ + printf("%s", str); +} + +static char * +get_exe_path(char *path_buf, unsigned path_buf_size) +{ + ssize_t i; + ssize_t size = readlink("/proc/self/exe", + path_buf, path_buf_size - 1); + + if (size < 0 || (size >= path_buf_size - 1)) { + return NULL; + } + + path_buf[size] = '\0'; + for (i = size - 1; i >= 0; i--) { + if (path_buf[i] == '/') { + path_buf[i + 1] = '\0'; + break; + } + } + return path_buf; +} + +/* Initialize the enclave: + * Step 1: try to retrieve the launch token saved by last transaction + * Step 2: call sgx_create_enclave to initialize an enclave instance + * Step 3: save the launch token if it is updated + */ +static int +enclave_init(sgx_enclave_id_t *p_eid) + +{ + char token_path[MAX_PATH] = { '\0' }; + char enclave_path[MAX_PATH] = { '\0' }; + const char *home_dir; + sgx_launch_token_t token = { 0 }; + sgx_status_t ret = SGX_ERROR_UNEXPECTED; + int updated = 0; + size_t write_num, enc_file_len; + FILE *fp; + + enc_file_len = strlen(ENCLAVE_FILENAME); + if (!get_exe_path(enclave_path, sizeof(enclave_path) - enc_file_len)) { + printf("Failed to get exec path\n"); + return -1; + } + memcpy(enclave_path + strlen(enclave_path), ENCLAVE_FILENAME, enc_file_len); + + /* Step 1: try to retrieve the launch token saved by last transaction + * if there is no token, then create a new one. + */ + /* try to get the token saved in $HOME */ + home_dir = getpwuid(getuid())->pw_dir; + + if (home_dir != NULL && + (strlen(home_dir) + strlen("/") + sizeof(TOKEN_FILENAME) + 1) <= MAX_PATH) { + /* compose the token path */ + strncpy(token_path, home_dir, strlen(home_dir)); + strncat(token_path, "/", strlen("/")); + strncat(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME) + 1); + } + else { + /* if token path is too long or $HOME is NULL */ + strncpy(token_path, TOKEN_FILENAME, sizeof(TOKEN_FILENAME)); + } + + fp = fopen(token_path, "rb"); + if (fp == NULL && (fp = fopen(token_path, "wb")) == NULL) { + printf("Warning: Failed to create/open the launch token file \"%s\".\n", + token_path); + } + + if (fp != NULL) { + /* read the token from saved file */ + size_t read_num = fread(token, 1, sizeof(sgx_launch_token_t), fp); + if (read_num != 0 && read_num != sizeof(sgx_launch_token_t)) { + /* if token is invalid, clear the buffer */ + memset(&token, 0x0, sizeof(sgx_launch_token_t)); + printf("Warning: Invalid launch token read from \"%s\".\n", token_path); + } + } + + /* Step 2: call sgx_create_enclave to initialize an enclave instance */ + /* Debug Support: set 2nd parameter to 1 */ + ret = sgx_create_enclave(ENCLAVE_FILENAME, SGX_DEBUG_FLAG, + &token, &updated, p_eid, NULL); + if (ret != SGX_SUCCESS) + /* Try to load enclave.sign.so from the path of exe file */ + ret = sgx_create_enclave(enclave_path, SGX_DEBUG_FLAG, + &token, &updated, p_eid, NULL); + if (ret != SGX_SUCCESS) { + printf("Failed to create enclave from %s, error code: %d\n", + ENCLAVE_FILENAME, ret); + if (fp != NULL) + fclose(fp); + return -1; + } + + /* Step 3: save the launch token if it is updated */ + if (updated == FALSE || fp == NULL) { + /* if the token is not updated, or file handler is invalid, do not perform saving */ + if (fp != NULL) fclose(fp); + return 0; + } + + /* reopen the file with write capablity */ + fp = freopen(token_path, "wb", fp); + if (fp == NULL) + return 0; + + write_num = fwrite(token, 1, sizeof(sgx_launch_token_t), fp); + if (write_num != sizeof(sgx_launch_token_t)) + printf("Warning: Failed to save launch token to \"%s\".\n", token_path); + + fclose(fp); + return 0; +} + +static unsigned char * +read_file_to_buffer(const char *filename, uint32_t *ret_size) +{ + unsigned char *buffer; + FILE *file; + int file_size, read_size; + + if (!filename || !ret_size) { + printf("Read file to buffer failed: invalid filename or ret size.\n"); + return NULL; + } + + if (!(file = fopen(filename, "r"))) { + printf("Read file to buffer failed: open file %s failed.\n", + filename); + return NULL; + } + + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (!(buffer = (unsigned char*)malloc(file_size))) { + printf("Read file to buffer failed: alloc memory failed.\n"); + fclose(file); + return NULL; + } + + read_size = fread(buffer, 1, file_size, file); + fclose(file); + + if (read_size < file_size) { + printf("Read file to buffer failed: read file content failed.\n"); + free(buffer); + return NULL; + } + + *ret_size = file_size; + + return buffer; +} + +static int +print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); + printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" + " level with more log\n"); + printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir= Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); + printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); + return 1; +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char **)realloc(res, sizeof(char *) * (unsigned)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * since the function name, + * res[0] might be contains a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ +} EcallCmd; + +static void +app_instance_func(void *wasm_module_inst, const char *func_name, + int app_argc, char **app_argv); + +static void * +app_instance_repl(void *module_inst, int app_argc, char **app_argv) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { + assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + printf("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + app_instance_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} + +static bool +set_log_verbose_level(int log_verbose_level) +{ + uint64_t ecall_args[1]; + + /* Set log verbose level */ + if (log_verbose_level != 2) { + ecall_args[0] = log_verbose_level; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_SET_LOG_LEVEL, + (uint8_t *)ecall_args, + sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + return false; + } + } + return true; +} + +static bool +init_runtime(bool alloc_with_pool, uint32_t max_thread_num) +{ + uint64_t ecall_args[2]; + + ecall_args[0] = alloc_with_pool; + ecall_args[1] = max_thread_num; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_INIT_RUNTIME, + (uint8_t *)ecall_args, + sizeof(uint64_t) * 2)) { + printf("Call ecall_handle_command() failed.\n"); + return false; + } + if (!(bool)ecall_args[0]) { + printf("Init runtime environment failed.\n"); + return false; + } + return true; +} + +static void +destroy_runtime() +{ + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_DESTROY_RUNTIME, + NULL, 0)) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static void * +load_module(uint8_t *wasm_file_buf, uint32_t wasm_file_size, + char *error_buf, uint32_t error_buf_size) +{ + uint64_t ecall_args[4]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_file_buf; + ecall_args[1] = wasm_file_size; + ecall_args[2] = (uint64_t)(uintptr_t)error_buf; + ecall_args[3] = error_buf_size; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_LOAD_MODULE, + (uint8_t *)ecall_args, + sizeof(uint64_t) * 4)) { + printf("Call ecall_handle_command() failed.\n"); + return NULL; + } + + return (void *)(uintptr_t)ecall_args[0]; +} + +static void +unload_module(void *wasm_module) +{ + uint64_t ecall_args[1]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_UNLOAD_MODULE, + (uint8_t *)ecall_args, + sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static void * +instantiate_module(void *wasm_module, + uint32_t stack_size, uint32_t heap_size, + char *error_buf, uint32_t error_buf_size) +{ + uint64_t ecall_args[5]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + ecall_args[1] = stack_size; + ecall_args[2] = heap_size; + ecall_args[3] = (uint64_t)(uintptr_t)error_buf; + ecall_args[4] = error_buf_size; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_INSTANTIATE_MODULE, + (uint8_t *)ecall_args, + sizeof(uint64_t) * 5)) { + printf("Call ecall_handle_command() failed.\n"); + return NULL; + } + + return (void *)(uintptr_t)ecall_args[0]; +} + +static void +deinstantiate_module(void *wasm_module_inst) +{ + uint64_t ecall_args[1]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module_inst; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_DEINSTANTIATE_MODULE, + (uint8_t *)ecall_args, + sizeof(uint64_t))) { + printf("Call ecall_handle_command() failed.\n"); + } +} + +static bool +get_exception(void *wasm_module_inst, + char *exception, uint32_t exception_size) +{ + uint64_t ecall_args[3]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module_inst; + ecall_args[1] = (uint64_t)(uintptr_t)exception; + ecall_args[2] = exception_size; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_GET_EXCEPTION, + (uint8_t *)ecall_args, + sizeof(uint64_t) * 3)) { + printf("Call ecall_handle_command() failed.\n"); + } + + return (bool)ecall_args[0]; +} + +static void +app_instance_main(void *wasm_module_inst, + int app_argc, char **app_argv) +{ + char exception[128]; + uint64_t ecall_args_buf[16], *ecall_args = ecall_args_buf; + int i, size; + + if (app_argc + 2 > sizeof(ecall_args_buf) / sizeof(uint64_t)) { + if (!(ecall_args = (uint64_t *) + malloc(sizeof(uint64_t) * (app_argc + 2)))) { + printf("Allocate memory failed.\n"); + return; + } + } + + ecall_args[0] = (uintptr_t)wasm_module_inst; + ecall_args[1] = app_argc; + for (i = 0; i < app_argc; i++) { + ecall_args[i + 2] = (uintptr_t)app_argv[i]; + } + + size = (uint32_t)sizeof(uint64_t) * (app_argc + 2); + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_EXEC_APP_MAIN, + (uint8_t *)ecall_args, + size)) { + printf("Call ecall_handle_command() failed.\n"); + } + + if (get_exception(wasm_module_inst, exception, sizeof(exception))) { + printf("%s\n", exception); + } + + if (ecall_args != ecall_args_buf) { + free(ecall_args); + } +} + +static void +app_instance_func(void *wasm_module_inst, const char *func_name, + int app_argc, char **app_argv) +{ + uint64_t ecall_args_buf[16], *ecall_args = ecall_args_buf; + int i, size; + + if (app_argc + 3 > sizeof(ecall_args_buf) / sizeof(uint64_t)) { + if (!(ecall_args = (uint64_t *) + malloc(sizeof(uint64_t) * (app_argc + 3)))) { + printf("Allocate memory failed.\n"); + return; + } + } + + ecall_args[0] = (uintptr_t)wasm_module_inst; + ecall_args[1] = (uintptr_t)func_name; + ecall_args[2] = (uintptr_t)app_argc; + for (i = 0; i < app_argc; i++) { + ecall_args[i + 3] = (uintptr_t)app_argv[i]; + } + + size = (uint32_t)sizeof(uint64_t) * (app_argc + 3); + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_EXEC_APP_FUNC, + (uint8_t *)ecall_args, + size)) { + printf("Call ecall_handle_command() failed.\n"); + } + + if (ecall_args != ecall_args_buf) { + free(ecall_args); + } +} + +static bool +set_wasi_args(void *wasm_module, + const char **dir_list, uint32_t dir_list_size, + const char **env_list, uint32_t env_list_size, + char **argv, uint32_t argc) +{ + uint64_t ecall_args[7]; + + ecall_args[0] = (uint64_t)(uintptr_t)wasm_module; + ecall_args[1] = (uint64_t)(uintptr_t)dir_list; + ecall_args[2] = dir_list_size; + ecall_args[3] = (uint64_t)(uintptr_t)env_list; + ecall_args[4] = env_list_size; + ecall_args[5] = (uint64_t)(uintptr_t)argv; + ecall_args[6] = argc; + if (SGX_SUCCESS != ecall_handle_command(g_eid, CMD_SET_WASI_ARGS, + (uint8_t *)ecall_args, + sizeof(uint64_t) * 7)) { + printf("Call ecall_handle_command() failed.\n"); + } + + return (bool)ecall_args[0]; +} + +int +main(int argc, char *argv[]) +{ + char *wasm_file = NULL; + const char *func_name = NULL; + uint8_t *wasm_file_buf = NULL; + uint32_t wasm_file_size; + uint32_t stack_size = 16 * 1024, heap_size = 16 * 1024; + void *wasm_module = NULL; + void *wasm_module_inst = NULL; + char error_buf[128] = { 0 }; + int log_verbose_level = 2; + bool is_repl_mode = false, alloc_with_pool = false; + const char *dir_list[8] = { NULL }; + uint32_t dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32_t env_list_size = 0; + uint32_t max_thread_num = 4; + + if (enclave_init(&g_eid) < 0) { + std::cout << "Fail to initialize enclave." << std::endl; + return 1; + } + +#if TEST_OCALL_API != 0 + { + if (!init_runtime(alloc_with_pool, max_thread_num)) { + return -1; + } + ecall_iwasm_test(g_eid); + destroy_runtime(); + return 0; + } +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + print_help(); + return 0; + } + func_name = argv[0]; + } + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char *))); + return -1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char *)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char *))); + return -1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", + tmp_env); + return print_help(); + } + } + else if (!strncmp(argv[0], "--max-threads=", 14)) { + if (argv[0][14] == '\0') + return print_help(); + max_thread_num = atoi(argv[0] + 14); + } + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + + /* Init runtime */ + if (!init_runtime(alloc_with_pool, max_thread_num)) { + return -1; + } + + /* Set log verbose level */ + if (!set_log_verbose_level(log_verbose_level)) { + goto fail1; + } + + /* Load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8_t *)read_file_to_buffer(wasm_file, &wasm_file_size))) { + goto fail1; + } + + /* Load module */ + if (!(wasm_module = load_module(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* Set wasi arguments */ + if (!set_wasi_args(wasm_module, dir_list, dir_list_size, + env_list, env_list_size, argv, argc)) { + printf("%s\n", "set wasi arguments failed.\n"); + goto fail3; + } + + /* Instantiate module */ + if (!(wasm_module_inst = instantiate_module(wasm_module, + stack_size, heap_size, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst, argc, argv); + else if (func_name) + app_instance_func(wasm_module_inst, func_name, + argc - 1, argv + 1); + else + app_instance_main(wasm_module_inst, argc, argv); + + /* Deinstantiate module */ + deinstantiate_module(wasm_module_inst); + +fail3: + /* Unload module */ + unload_module(wasm_module); + +fail2: + /* Free the file buffer */ + free(wasm_file_buf); + +fail1: + /* Destroy runtime environment */ + destroy_runtime(); + + return 0; +} + diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml new file mode 100644 index 0000000..6a48563 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.config.xml @@ -0,0 +1,14 @@ + + + 0 + 0 + 0x100000 + 0x1000000 + 0x400000 + 1 + 10 + 1 + 0 + 0 + 0xFFFFFFFF + diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp new file mode 100644 index 0000000..72bae7b --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.cpp @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +#include "Enclave_t.h" +#include "wasm_export.h" +#include "bh_platform.h" + +extern "C" { + typedef void (*os_print_function_t)(const char* message); + extern void os_set_print_function(os_print_function_t pf); + + void + enclave_print(const char *message) + { + ocall_print(message); + } +} + +typedef enum EcallCmd { + CMD_INIT_RUNTIME = 0, /* wasm_runtime_init/full_init() */ + CMD_LOAD_MODULE, /* wasm_runtime_load() */ + CMD_INSTANTIATE_MODULE, /* wasm_runtime_instantiate() */ + CMD_LOOKUP_FUNCTION, /* wasm_runtime_lookup_function() */ + CMD_CREATE_EXEC_ENV, /* wasm_runtime_create_exec_env() */ + CMD_CALL_WASM, /* wasm_runtime_call_wasm */ + CMD_EXEC_APP_FUNC, /* wasm_application_execute_func() */ + CMD_EXEC_APP_MAIN, /* wasm_application_execute_main() */ + CMD_GET_EXCEPTION, /* wasm_runtime_get_exception() */ + CMD_DEINSTANTIATE_MODULE, /* wasm_runtime_deinstantiate() */ + CMD_UNLOAD_MODULE, /* wasm_runtime_unload() */ + CMD_DESTROY_RUNTIME, /* wasm_runtime_destroy() */ + CMD_SET_WASI_ARGS, /* wasm_runtime_set_wasi_args() */ + CMD_SET_LOG_LEVEL, /* bh_log_set_verbose_level() */ +} EcallCmd; + +typedef struct EnclaveModule { + wasm_module_t module; + uint8 *wasm_file; + uint32 wasm_file_size; + char *wasi_arg_buf; + char **wasi_dir_list; + uint32 wasi_dir_list_size; + char **wasi_env_list; + uint32 wasi_env_list_size; + char **wasi_argv; + uint32 wasi_argc; +} EnclaveModule; + +#if WASM_ENABLE_SPEC_TEST == 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#else +static char global_heap_buf[100 * 1024 * 1024] = { 0 }; +#endif + +static void +set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) +{ + if (error_buf != NULL) + snprintf(error_buf, error_buf_size, "%s", string); +} + +static void +handle_cmd_init_runtime(uint64 *args, uint32 argc) +{ + bool alloc_with_pool; + uint32 max_thread_num; + RuntimeInitArgs init_args; + + bh_assert(argc == 2); + + os_set_print_function(enclave_print); + +#if WASM_ENABLE_SPEC_TEST == 0 + alloc_with_pool = (bool)args[0]; +#else + alloc_with_pool = true; +#endif + max_thread_num = (uint32)args[1]; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.max_thread_num = max_thread_num; + + if (alloc_with_pool) { + 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); + } + else { + init_args.mem_alloc_type = Alloc_With_System_Allocator; + } + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERROR("Init runtime environment failed.\n"); + args[0] = false; + return; + } + + args[0] = true; + + LOG_VERBOSE("Init runtime environment success.\n"); +} + +static void +handle_cmd_destroy_runtime() +{ + wasm_runtime_destroy(); + + LOG_VERBOSE("Destroy runtime success.\n"); +} + +static void +handle_cmd_load_module(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + char *wasm_file = *(char **)args++; + uint32 wasm_file_size = *(uint32 *)args++; + char *error_buf = *(char **)args++; + uint32 error_buf_size = *(uint32 *)args++; + uint64 total_size = sizeof(EnclaveModule) + (uint64)wasm_file_size; + EnclaveModule *enclave_module; + + bh_assert(argc == 4); + + if (total_size >= UINT32_MAX + || !(enclave_module = (EnclaveModule *) + wasm_runtime_malloc((uint32)total_size))) { + set_error_buf(error_buf, error_buf_size, + "WASM module load failed: " + "allocate memory failed."); + *(void **)args_org = NULL; + return; + } + + memset(enclave_module, 0, (uint32)total_size); + enclave_module->wasm_file = (uint8 *)enclave_module + + sizeof(EnclaveModule); + bh_memcpy_s(enclave_module->wasm_file, wasm_file_size, + wasm_file, wasm_file_size); + + if (!(enclave_module->module = + wasm_runtime_load(enclave_module->wasm_file, wasm_file_size, + error_buf, error_buf_size))) { + *(void **)args_org = NULL; + return; + } + + *(EnclaveModule **)args_org = enclave_module; + + LOG_VERBOSE("Load module success.\n"); +} + +static void +handle_cmd_unload_module(uint64 *args, uint32 argc) +{ + EnclaveModule *enclave_module = *(EnclaveModule **)args++; + uint32 i; + + bh_assert(argc == 1); + + if (enclave_module->wasi_arg_buf) + wasm_runtime_free(enclave_module->wasi_arg_buf); + + wasm_runtime_unload(enclave_module->module); + wasm_runtime_free(enclave_module); + + LOG_VERBOSE("Unload module success.\n"); +} + +static void +handle_cmd_instantiate_module(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + EnclaveModule *enclave_module = *(EnclaveModule **)args++; + uint32 stack_size = *(uint32 *)args++; + uint32 heap_size = *(uint32 *)args++; + char *error_buf = *(char **)args++; + uint32 error_buf_size = *(uint32 *)args++; + wasm_module_inst_t module_inst; + + bh_assert(argc == 5); + + if (!(module_inst = + wasm_runtime_instantiate(enclave_module->module, + stack_size, heap_size, + error_buf, error_buf_size))) { + *(void **)args_org = NULL; + return; + } + + *(wasm_module_inst_t *)args_org = module_inst; + + LOG_VERBOSE("Instantiate module success.\n"); +} + +static void +handle_cmd_deinstantiate_module(uint64 *args, uint32 argc) +{ + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args++; + + bh_assert(argc == 1); + + wasm_runtime_deinstantiate(module_inst); + + LOG_VERBOSE("Deinstantiate module success.\n"); +} + +static void +handle_cmd_get_exception(uint64 *args, uint32 argc) +{ + uint64 *args_org = args; + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args++; + char *exception = *(char **)args++; + uint32 exception_size = *(uint32 *)args++; + const char *exception1; + + bh_assert(argc == 3); + + if ((exception1 = wasm_runtime_get_exception(module_inst))) { + snprintf(exception, exception_size, + "%s", exception1); + args_org[0] = true; + } + else { + args_org[0] = false; + } +} + +static void +handle_cmd_exec_app_main(uint64 *args, int32 argc) +{ + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args++; + uint32 app_argc = *(uint32 *)args++; + char **app_argv = NULL; + uint64 total_size; + int32 i; + + bh_assert(argc >= 3); + bh_assert(app_argc >= 1); + + total_size = sizeof(char *) * (app_argc > 2 ? (uint64)app_argc : 2); + + if (total_size >= UINT32_MAX + || !(app_argv = (char **)wasm_runtime_malloc(total_size))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed."); + return; + } + + for (i = 0; i < app_argc; i++) { + app_argv[i] = (char *)(uintptr_t)args[i]; + } + + wasm_application_execute_main(module_inst, app_argc - 1, app_argv + 1); + + wasm_runtime_free(app_argv); +} + +static void +handle_cmd_exec_app_func(uint64 *args, int32 argc) +{ + wasm_module_inst_t module_inst = *(wasm_module_inst_t *)args++; + char *func_name = *(char **)args++; + uint32 app_argc = *(uint32 *)args++; + char **app_argv = NULL; + uint64 total_size; + int32 i, func_name_len = strlen(func_name); + + bh_assert(argc == app_argc + 3); + + total_size = sizeof(char *) * (app_argc > 2 ? (uint64)app_argc : 2); + + if (total_size >= UINT32_MAX + || !(app_argv = (char **)wasm_runtime_malloc(total_size))) { + wasm_runtime_set_exception(module_inst, "allocate memory failed."); + return; + } + + for (i = 0; i < app_argc; i++) { + app_argv[i] = (char *)(uintptr_t)args[i]; + } + + wasm_application_execute_func(module_inst, func_name, app_argc, app_argv); + + wasm_runtime_free(app_argv); +} + +static void +handle_cmd_set_log_level(uint64 *args, uint32 argc) +{ +#if WASM_ENABLE_LOG != 0 + LOG_VERBOSE("Set log verbose level to %d.\n", (int)args[0]); + bh_log_set_verbose_level((int)args[0]); +#endif +} + +static void +handle_cmd_set_wasi_args(uint64 *args, int32 argc) +{ + uint64 *args_org = args; + EnclaveModule *enclave_module = *(EnclaveModule **)args++; + char **dir_list = *(char ***)args++; + uint32 dir_list_size = *(uint32 *)args++; + char **env_list = *(char ***)args++; + uint32 env_list_size = *(uint32 *)args++; + char **wasi_argv = *(char ***)args++; + char *p, *p1; + uint32 wasi_argc = *(uint32 *)args++; + uint64 total_size = 0; + int32 i, str_len; + + bh_assert(argc == 7); + + total_size += sizeof(char *) * (uint64)dir_list_size + + sizeof(char *) * (uint64)env_list_size + + sizeof(char *) * (uint64)wasi_argc; + + for (i = 0; i < dir_list_size; i++) { + total_size += strlen(dir_list[i]) + 1; + } + + for (i = 0; i < env_list_size; i++) { + total_size += strlen(env_list[i]) + 1; + } + + for (i = 0; i < wasi_argc; i++) { + total_size += strlen(wasi_argv[i]) + 1; + } + + if (total_size >= UINT32_MAX + || !(enclave_module->wasi_arg_buf = p = (char *) + wasm_runtime_malloc((uint32)total_size))) { + *args_org = false; + return; + } + + p1 = p + sizeof(char *) * dir_list_size + + sizeof(char *) * env_list_size + + sizeof(char *) * wasi_argc; + + if (dir_list_size > 0) { + enclave_module->wasi_dir_list = (char **)p; + enclave_module->wasi_dir_list_size = dir_list_size; + for (i = 0; i < dir_list_size; i++) { + enclave_module->wasi_dir_list[i] = p1; + str_len = strlen(dir_list[i]); + bh_memcpy_s(p1, str_len + 1, dir_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * dir_list_size; + } + + if (env_list_size > 0) { + enclave_module->wasi_env_list = (char **)p; + enclave_module->wasi_env_list_size = env_list_size; + for (i = 0; i < env_list_size; i++) { + enclave_module->wasi_env_list[i] = p1; + str_len = strlen(env_list[i]); + bh_memcpy_s(p1, str_len + 1, env_list[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * env_list_size; + } + + if (wasi_argc > 0) { + enclave_module->wasi_argv = (char **)p; + enclave_module->wasi_argc = wasi_argc; + for (i = 0; i < wasi_argc; i++) { + enclave_module->wasi_argv[i] = p1; + str_len = strlen(wasi_argv[i]); + bh_memcpy_s(p1, str_len + 1, wasi_argv[i], str_len + 1); + p1 += str_len + 1; + } + p += sizeof(char *) * wasi_argc; + } + + wasm_runtime_set_wasi_args(enclave_module->module, + (const char **)enclave_module->wasi_dir_list, + dir_list_size, + NULL, 0, + (const char **)enclave_module->wasi_env_list, + env_list_size, + enclave_module->wasi_argv, + enclave_module->wasi_argc); + + *args_org = true; +} + +void +ecall_handle_command(unsigned cmd, + unsigned char *cmd_buf, + unsigned cmd_buf_size) +{ + uint64 *args = (uint64 *)cmd_buf; + uint32 argc = cmd_buf_size / sizeof(uint64); + + switch (cmd) { + case CMD_INIT_RUNTIME: + handle_cmd_init_runtime(args, argc); + break; + case CMD_LOAD_MODULE: + handle_cmd_load_module(args, argc); + break; + case CMD_SET_WASI_ARGS: + handle_cmd_set_wasi_args(args, argc); + break; + case CMD_INSTANTIATE_MODULE: + handle_cmd_instantiate_module(args, argc); + break; + case CMD_LOOKUP_FUNCTION: + break; + case CMD_CREATE_EXEC_ENV: + break; + case CMD_CALL_WASM: + break; + case CMD_EXEC_APP_FUNC: + handle_cmd_exec_app_func(args, argc); + break; + case CMD_EXEC_APP_MAIN: + handle_cmd_exec_app_main(args, argc); + break; + case CMD_GET_EXCEPTION: + handle_cmd_get_exception(args, argc); + break; + case CMD_DEINSTANTIATE_MODULE: + handle_cmd_deinstantiate_module(args, argc); + break; + case CMD_UNLOAD_MODULE: + handle_cmd_unload_module(args, argc); + break; + case CMD_DESTROY_RUNTIME: + handle_cmd_destroy_runtime(); + break; + case CMD_SET_LOG_LEVEL: + handle_cmd_set_log_level(args, argc); + break; + default: + LOG_ERROR("Unknown command %d\n", cmd); + break; + } +} + +void +ecall_iwasm_main(uint8_t *wasm_file_buf, uint32_t wasm_file_size) +{ + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; + const char *exception; + + os_set_print_function(enclave_print); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + ocall_print("Init runtime environment failed."); + ocall_print("\n"); + return; + } + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + ocall_print(error_buf); + ocall_print("\n"); + goto fail1; + } + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + 16 * 1024, + 16 * 1024, + error_buf, + sizeof(error_buf)))) { + ocall_print(error_buf); + ocall_print("\n"); + goto fail2; + } + + /* execute the main function of wasm app */ + wasm_application_execute_main(wasm_module_inst, 0, NULL); + if ((exception = wasm_runtime_get_exception(wasm_module_inst))) { + ocall_print(exception); + ocall_print("\n"); + } + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); +} diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl new file mode 100644 index 0000000..752e135 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave.edl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +enclave { + from "sgx_tstdc.edl" import *; + from "sgx_pthread.edl" import *; + from "sgx_wamr.edl" import *; + + trusted { + /* define ECALLs here. */ + public void ecall_handle_command(unsigned cmd, + [in, out, size=cmd_buf_size]uint8_t *cmd_buf, + unsigned cmd_buf_size); + public void ecall_iwasm_main([user_check]uint8_t *wasm_file_buf, + uint32_t wasm_file_size); + public void ecall_iwasm_test(); + }; + + untrusted { + /* define OCALLs here. */ + void ocall_print([in, string]const char* str); + }; +}; diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_private.pem b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_private.pem new file mode 100644 index 0000000..529d07b --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_private.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp new file mode 100644 index 0000000..12632a0 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Enclave/Enclave_test.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include "Enclave_t.h" +#include "wasm_export.h" +#include "bh_platform.h" + +void ecall_iwasm_test() +{ + ocall_print(" Prepare to invoke sgx_open ... ==>\n\n"); + + /* creat test.txt firstly */ + char file[] = "test.txt"; + char file_hd_ln[] = "test_hd_ln.txt"; + char file_sf_ln[] = "test_sf_ln.txt"; + char rlt_dir_path[] = "./tmp"; + char rlt_dir_path_new[] = "./tmp_new"; + char *str0 = (char *)"good luck, "; + char *str1 = (char *)"have fun!"; + + char buf0[4], buf1[4]; + struct iovec iov_w[2]; + struct iovec iov_r[2]; + + char buf[2048]; + ssize_t total_size; + ssize_t s_ret; + int fd; + int dird; + int file_buf; + int ret; + long ret_l; + DIR *dirp; + struct dirent *p_dirent; + struct stat *statbuf; + struct pollfd fds[1]; + char *res; + + /** getopt value **/ + int argc = 3; + char n_1[] = "./main"; + char n_2[] = "-a"; + char n_3[] = "test.txt"; + + char *argv[3]; + argv[0] = n_1; + argv[1] = n_2; + argv[2] = n_3; + + /* time clock */ + struct timespec ts; + struct timespec t_res; + struct timespec times[2]; + struct stat statbuf_2; + times[0] = statbuf_2.st_atim; + times[1] = statbuf_2.st_mtim; + struct timespec rqtp; + struct timespec rmtp; + rqtp.tv_sec = 0; + rqtp.tv_nsec = 0; + + /** mkdirat **/ + /* mkdir tmp in current directory for test + * if the ./tmp directory has exits, mkdirat will fail + */ + ret = mkdirat(AT_FDCWD, rlt_dir_path, 0755); + if (ret == 0) { + ocall_print("\tOperation mkdirat success.\n"); + } + + /* flags: + 0100: - O_CREAT + 02 : - O_RDWR */ + /** 1. open **/ + fd = open(file, O_RDWR); + if (fd !=-1) { + ocall_print("\tOperation open test_open.txt success.\n"); + } + + /** read **/ + total_size = read(fd, buf, 2048); + if (total_size != -1) { + ocall_print("\tOperation read test_open.txt success.\n"); + ocall_print("\t\tthe details of the file: "); + ocall_print(buf); + ocall_print("\n"); + } + + /** lseek **/ + ret = lseek(fd, 1, SEEK_CUR); + if (ret != -1) { + ocall_print("\tOperation lseek success.\n"); + } + + /** ftruncate **/ + ret = ftruncate(fd, 0); + if (ret == 0) { + ocall_print("\tOperation ftruncate success.\n"); + } + + /** fdatasync **/ + ret = fdatasync(fd); + if (ret == 0) { + ocall_print("\tOperation fdatasync success.\n"); + } + + /** isatty **/ + ret = isatty(fd); + if (ret == 0) { + ocall_print("\tOperation fisatty success.\n"); + } + + /** fsync **/ + ret = fsync(fd); + if (ret == 0) { + ocall_print("\tOperation fsync success.\n"); + } + + /** 1. close **/ + ret = close(fd); + if (ret != -1) { + ocall_print("\tOperation close success.\n"); + } + + /*----------------------------------------------------------------*/ + + /**-- DIR --**/ + /** fdopendir **/ + /* 2. open */ + dird = open(rlt_dir_path, O_RDONLY); + dirp = fdopendir(dird); + if (dirp != NULL) { + ocall_print("\tOperation fdopendir success.\n"); + } + + /** readdir **/ + p_dirent = readdir(dirp); + if (p_dirent != NULL) { + ocall_print("\tOperation readdir success.\t"); + ocall_print(p_dirent -> d_name); + ocall_print("\n"); + } + + /** rewinddir **/ + rewinddir(dirp); + + /** seekdir **/ + seekdir(dirp, 1); + + /** telldir **/ + ret_l = telldir(dirp); + if (ret_l != -1) { + ocall_print("\tOperation telldir success. \n"); + } + + /** closedir **/ + ret = closedir(dirp); + if (ret == 0 ) { + ocall_print("\tOperation closedir success. \n"); + } + /* 2. close */ + close(dird); + + /*----------------------------------------------------------------*/ + + /** fstat **/ + /** 3. open file firstly **/ + fd = open(file, O_RDWR); + statbuf = (stat *)malloc(sizeof(stat)); + ret = fstat(fd, statbuf); + if (ret == 0) { + ocall_print("\tOperation fstat success. \n"); + } + free(statbuf); + /* 3. close */ + close(fd); + + /*----------------------------------------------------------------*/ + + /** fstatat **/ + /* 4. open */ + dird = open(rlt_dir_path, O_RDONLY); + ret = fstatat(AT_FDCWD, rlt_dir_path, statbuf, 0); + if (ret == 0) { + ocall_print("\tOperation fstatat success. \n"); + } + + /** renameat **/ + ret = renameat(AT_FDCWD, rlt_dir_path, AT_FDCWD, rlt_dir_path_new); + if (ret == 0) { + ocall_print("\tOperation renameat ./tmp to " + "./tmp_new success. \n"); + } + renameat(AT_FDCWD, rlt_dir_path_new, AT_FDCWD, rlt_dir_path); + + /** link **/ + ret = link(file, file_hd_ln); + if (ret == 0) { + ocall_print("\tOperation link success. \n"); + } + + /** unlinkat **/ + ret = unlinkat(AT_FDCWD, file_hd_ln, 0); + if (ret == 0) { + ocall_print("\tOperation unlinkat success. \n"); + } + + /** linkat **/ + ret = linkat(AT_FDCWD, file, AT_FDCWD, file_hd_ln, 0); + if (ret == 0) { + ocall_print("\tOperation linkat success. \n"); + } + /* delete hard link file */ + unlinkat(AT_FDCWD, file_hd_ln, 0); + + /** symlinkat **/ + ret = symlinkat(file, AT_FDCWD, file_sf_ln); + if (ret == 0) { + ocall_print("\tOperation symlinkat from test.txt " + "to text_sf_ln.txt success. \n"); + } + /** readlinkat **/ + total_size = readlinkat(AT_FDCWD, file_sf_ln, buf, sizeof(buf)); + if (total_size != -1) { + ocall_print("\tOperation readlinkat success. \n"); + ocall_print("\t\t the link details of the file is: "); + ocall_print(buf); + ocall_print("\n"); + } + /* delete soft link file */ + unlinkat(AT_FDCWD, file_sf_ln, 0); + /* 4. close */ + close(dird); + + /*----------------------------------------------------------------*/ + + /* 5. open */ + fd = open(file, O_RDWR); + /** ioctl **/ + ret = ioctl(fd, FIONREAD, &file_buf); + if (ret == 0) { + ocall_print("\tOperation ioctl success. \n"); + } + /** fcntl(fd, cmd) **/ + ret = fcntl(fd, F_GETFD); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation fcntl_1 success. \n"); + } + /** fcntl(fd, cmd, long) **/ + ret = fcntl(fd, F_SETFD, ret); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation fcntl_2 success. \n"); + } + + /* 5. close */ + close(fd); + + /*----------------------------------------------------------------*/ + + /** posix_fallocate **/ + /* 6. open */ + fd = open(file, O_RDWR); + ret = posix_fallocate(fd, 1, 1); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation posix_fallocate success. \n"); + } + /* 6. close */ + close(fd); + + /** poll **/ + ret = poll(fds, 1, 10); + if (ret != 0 || ret != -1) { + ocall_print("\tOperation poll success. \n"); + } + + /** realpath **/ + res = realpath(file, res); + if (res) { + ocall_print("\tOperation realpath success. \n"); + ocall_print("\t\t the absolute path of the file is: "); + ocall_print(res); + ocall_print("\n"); + } + + /** getrandom **/ + total_size = getrandom(buf, 1024, 0); + if (ret != -1) { + ocall_print("\tOperation getrandom success. \n"); + } + + /** writev **/ + /* 7. open */ + fd = open(file, O_RDWR); + iov_w[0].iov_base = str0; + iov_w[0].iov_len = strlen(str0); + iov_w[1].iov_base = str1; + iov_w[1].iov_len = strlen(str1); + + s_ret = writev(fd, iov_w, 2); + if (s_ret != -1) { + ocall_print("\tOperation writev success. \n"); + } + + /** readv **/ + iov_r[0].iov_base = buf0; + iov_r[0].iov_len = sizeof(buf0) - 1; + iov_r[1].iov_base = buf1; + iov_r[1].iov_len = sizeof(buf1) - 1; + + s_ret = readv(fd, iov_r, 2); + if (s_ret != -1) { + ocall_print("\tOperation readv success. \n"); + ocall_print("\t\t"); + ocall_print(buf0); + ocall_print(buf1); + ocall_print("\n"); + } + + iov_r[0].iov_base = buf0; + iov_r[0].iov_len = sizeof(buf0) - 1; + iov_r[1].iov_base = buf1; + iov_r[1].iov_len = sizeof(buf1) - 1; + + s_ret = preadv(fd, iov_r, 2, 2); + if (s_ret != -1) { + ocall_print("\tOperation readv success. \n"); + ocall_print("\t\t"); + ocall_print(buf0); + ocall_print(buf1); + ocall_print("\n"); + } + /* 7. close */ + close(fd); + + /** getopt **/ + while((ret = getopt(argc, argv, "f:abc")) != -1){ //get option from the getopt() method + switch(ret){ + //For option i, r, l, print that these are options + case 'a': + case 'b': + case 'c': + ocall_print("\tGiven Option operation success. \n"); + break; + case 'f': //here f is used for some file name + ocall_print("\tGiven File operation success.\n"); + break; + case '?': //used for some unknown options + ocall_print("\tunknown option trigger success.\n"); + break; + } + } + + /** sched_yield **/ + ret = sched_yield(); + if (ret == 0) { + ocall_print("\tOperation sched_yield success. \n"); + } + + /** clock_gettime **/ + ret = clock_gettime(CLOCK_REALTIME, &ts); + if (ret == 0) { + ocall_print("\tOperation clock_gettime success. \n"); + } + + /** clock_getres **/ + ret = clock_getres(CLOCK_REALTIME, &t_res); + if (ret == 0) { + ocall_print("\tOperation clock_getres success. \n"); + } + + /** futimens **/ + /* 8. open */ + fd = open(file, O_RDWR); + ret = futimens(fd, NULL); + if (ret == 0) { + ocall_print("\tOperation futimens NULL success. \n"); + } + + ret = futimens(fd, times); + if (ret == 0) { + ocall_print("\tOperation futimens times[2] success. \n"); + } + /* 8. close */ + close(fd); + + /** utimensat **/ + /* 9. open */ + dird = open(rlt_dir_path, O_RDONLY); + ret = utimensat(AT_FDCWD, file, NULL, AT_SYMLINK_NOFOLLOW); + if (ret == 0) { + ocall_print("\tOperation utimensat NULL success. \n"); + } + + ret = utimensat(AT_FDCWD, file, times, AT_SYMLINK_NOFOLLOW); + if (ret == 0) { + ocall_print("\tOperation utimensat times[2] success. \n"); + } + /* 9. close */ + close(fd); + + /** clock_nanosleep **/ + ret = clock_nanosleep(CLOCK_REALTIME, 0, &rqtp, NULL); + if (ret == 0) { + ocall_print("\tOperation clock_nanosleep NULL success. \n"); + } + + ret = clock_nanosleep(CLOCK_REALTIME, 0, &rqtp, &rmtp); + if (ret == 0) { + ocall_print("\tOperation clock_nanosleep 2 success. \n"); + } + + ocall_print("\n<== ... End test\n"); +} \ No newline at end of file diff --git a/wamr/product-mini/platforms/linux-sgx/enclave-sample/Makefile b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Makefile new file mode 100644 index 0000000..f546731 --- /dev/null +++ b/wamr/product-mini/platforms/linux-sgx/enclave-sample/Makefile @@ -0,0 +1,210 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +######## SGX SDK Settings ######## + +SGX_SDK ?= /opt/intel/sgxsdk +SGX_MODE ?= SIM +SGX_ARCH ?= x64 +SGX_DEBUG ?= 0 +SPEC_TEST ?= 0 + +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g +else + SGX_COMMON_CFLAGS += -O2 +endif + +######## App Settings ######## + +ifneq ($(SGX_MODE), HW) + Urts_Library_Name := sgx_urts_sim +else + Urts_Library_Name := sgx_urts +endif + +App_Cpp_Files := App/App.cpp +App_Include_Paths := -IApp -I$(SGX_SDK)/include + +App_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(App_Include_Paths) + +# Three configuration modes - Debug, prerelease, release +# Debug - Macro DEBUG enabled. +# Prerelease - Macro NDEBUG and EDEBUG enabled. +# Release - Macro NDEBUG enabled. +ifeq ($(SGX_DEBUG), 1) + App_C_Flags += -DDEBUG -UNDEBUG -UEDEBUG +else ifeq ($(SGX_PRERELEASE), 1) + App_C_Flags += -DNDEBUG -DEDEBUG -UDEBUG +else + App_C_Flags += -DNDEBUG -UEDEBUG -UDEBUG +endif + +App_Cpp_Flags := $(App_C_Flags) -std=c++11 +App_Link_Flags := $(SGX_COMMON_CFLAGS) libvmlib_untrusted.a -L$(SGX_LIBRARY_PATH) -l$(Urts_Library_Name) -lpthread + +ifneq ($(SGX_MODE), HW) + App_Link_Flags += -lsgx_uae_service_sim +else + App_Link_Flags += -lsgx_uae_service +endif + +App_Cpp_Objects := $(App_Cpp_Files:.cpp=.o) + +App_Name := iwasm + +######## Enclave Settings ######## + +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif +Crypto_Library_Name := sgx_tcrypto + +WAMR_ROOT := $(CURDIR)/../../../../ + +Enclave_Cpp_Files := Enclave/Enclave.cpp Enclave/Enclave_test.cpp + +Enclave_Include_Paths := -IEnclave -I$(WAMR_ROOT)/core/iwasm/include \ + -I$(WAMR_ROOT)/core/shared/utils \ + -I$(WAMR_ROOT)/core/shared/platform/linux-sgx \ + -I$(SGX_SDK)/include \ + -I$(SGX_SDK)/include/tlibc \ + -I$(SGX_SDK)/include/stlport + +Enclave_C_Flags := $(SGX_COMMON_CFLAGS) -nostdinc -fvisibility=hidden -fpie -fstack-protector $(Enclave_Include_Paths) +ifeq ($(SPEC_TEST), 1) + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=1 +else + Enclave_C_Flags += -DWASM_ENABLE_SPEC_TEST=0 +endif +Enclave_Cpp_Flags := $(Enclave_C_Flags) -std=c++03 -nostdinc++ +Enclave_Link_Flags := $(SGX_COMMON_CFLAGS) -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + libvmlib.a \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -lsgx_pthread -l$(Crypto_Library_Name) -l$(Service_Library_Name) -Wl,--end-group \ + -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--defsym,__ImageBase=0 + +Enclave_Cpp_Objects := $(Enclave_Cpp_Files:.cpp=.o) + +Enclave_Name := enclave.so +Signed_Enclave_Name := enclave.signed.so +Enclave_Config_File := Enclave/Enclave.config.xml + +ifeq ($(SGX_MODE), HW) +ifneq ($(SGX_DEBUG), 1) +ifneq ($(SGX_PRERELEASE), 1) +Build_Mode = HW_RELEASE +endif +endif +endif + + +.PHONY: all run + +ifeq ($(Build_Mode), HW_RELEASE) +all: $(App_Name) $(Enclave_Name) + @echo "The project has been built in release hardware mode." + @echo "Please sign the $(Enclave_Name) first with your signing key before you run the $(App_Name) to launch and access the enclave." + @echo "To sign the enclave use the command:" + @echo " $(SGX_ENCLAVE_SIGNER) sign -key -enclave $(Enclave_Name) -out <$(Signed_Enclave_Name)> -config $(Enclave_Config_File)" + @echo "You can also sign the enclave using an external signing tool. See User's Guide for more details." + @echo "To build the project in simulation mode set SGX_MODE=SIM. To build the project in prerelease mode set SGX_PRERELEASE=1 and SGX_MODE=HW." +else +all: $(App_Name) $(Signed_Enclave_Name) +endif + +run: all +ifneq ($(Build_Mode), HW_RELEASE) + @$(CURDIR)/$(App_Name) + @echo "RUN => $(App_Name) [$(SGX_MODE)|$(SGX_ARCH), OK]" +endif + +######## App Objects ######## + +App/Enclave_u.c: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd App && $(SGX_EDGER8R) --untrusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx + @echo "GEN => $@" + +App/Enclave_u.o: App/Enclave_u.c + @$(CC) $(App_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +App/%.o: App/%.cpp + @$(CXX) $(App_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +libvmlib_untrusted.a: ../build/libvmlib_untrusted.a + @cp $< $@ + @echo "CP $@ <= $<" + +$(App_Name): App/Enclave_u.o $(App_Cpp_Objects) libvmlib_untrusted.a + @$(CXX) $^ -o $@ $(App_Link_Flags) + @echo "LINK => $@" + + +######## Enclave Objects ######## + +Enclave/Enclave_t.c: $(SGX_EDGER8R) Enclave/Enclave.edl + @cd Enclave && $(SGX_EDGER8R) --trusted ../Enclave/Enclave.edl \ + --search-path ../Enclave \ + --search-path $(SGX_SDK)/include \ + --search-path $(WAMR_ROOT)/core/shared/platform/linux-sgx + @echo "GEN => $@" + +Enclave/Enclave_t.o: Enclave/Enclave_t.c + @$(CC) $(Enclave_C_Flags) -c $< -o $@ + @echo "CC <= $<" + +Enclave/%.o: Enclave/%.cpp + @$(CXX) $(Enclave_Cpp_Flags) -c $< -o $@ + @echo "CXX <= $<" + +libvmlib.a: ../build/libvmlib.a + @cp $< $@ + @echo "CP $@ <= $<" + +$(Enclave_Name): Enclave/Enclave_t.o $(Enclave_Cpp_Objects) libvmlib.a + @$(CXX) $^ -o $@ $(Enclave_Link_Flags) + @echo "LINK => $@" + +$(Signed_Enclave_Name): $(Enclave_Name) + @$(SGX_ENCLAVE_SIGNER) sign -key Enclave/Enclave_private.pem -enclave $(Enclave_Name) -out $@ -config $(Enclave_Config_File) + @echo "SIGN => $@" + +.PHONY: clean + +clean: + @rm -f $(App_Name) $(Enclave_Name) $(Signed_Enclave_Name) $(App_Cpp_Objects) App/Enclave_u.* $(Enclave_Cpp_Objects) Enclave/Enclave_t.* libvmlib.a libvmlib_untrusted.a diff --git a/wamr/product-mini/platforms/linux/CMakeLists.txt b/wamr/product-mini/platforms/linux/CMakeLists.txt new file mode 100644 index 0000000..6b1b6e7 --- /dev/null +++ b/wamr/product-mini/platforms/linux/CMakeLists.txt @@ -0,0 +1,120 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (iwasm) +# set (CMAKE_VERBOSE_MAKEFILE 1) + +set (WAMR_BUILD_PLATFORM "linux") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (CMAKE_C_STANDARD 99) + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 1) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +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} -Wconversion -Wsign-conversion") + +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#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") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) + +add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) diff --git a/wamr/product-mini/platforms/linux/build_jit.sh b/wamr/product-mini/platforms/linux/build_jit.sh new file mode 100755 index 0000000..908d156 --- /dev/null +++ b/wamr/product-mini/platforms/linux/build_jit.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +rm -fr build && mkdir build +cd build +cmake .. -DWAMR_BUILD_JIT=1 +make +cd .. diff --git a/wamr/product-mini/platforms/linux/build_llvm.sh b/wamr/product-mini/platforms/linux/build_llvm.sh new file mode 100755 index 0000000..7821eee --- /dev/null +++ b/wamr/product-mini/platforms/linux/build_llvm.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# Copyright (C) 2020 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +DEPS_DIR=${PWD}/../../../core/deps + +cd ${DEPS_DIR} +if [ ! -d "llvm" ]; then + echo "Clone llvm to core/deps/ .." + git clone --depth 1 --branch release/10.x https://github.com/llvm/llvm-project.git llvm +fi + +cd llvm +mkdir -p build +cd build + +if [ ! -f bin/llvm-lto ]; then + + CORE_NUM=$(nproc --all) + if [ -z "${CORE_NUM}" ]; then + CORE_NUM=1 + fi + + echo "Build llvm with" ${CORE_NUM} "cores" + + cmake ../llvm \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;ARM;AArch64;Mips" \ + -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ + -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ + -DLLVM_ENABLE_ZLIB:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_APPEND_VC_REV:BOOL=OFF + make -j ${CORE_NUM} + +else + echo "llvm has already been built" +fi + +cd ${PWD} + diff --git a/wamr/product-mini/platforms/linux/main.c b/wamr/product-mini/platforms/linux/main.c new file mode 100644 index 0000000..1d1a828 --- /dev/null +++ b/wamr/product-mini/platforms/linux/main.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +static int app_argc; +static char **app_argv; + +#define MODULE_PATH ("--module-path=") + +static int +print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); +#if WASM_ENABLE_LOG != 0 + printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" + " level with more log\n"); +#endif + printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); +#if WASM_ENABLE_LIBC_WASI != 0 + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir= Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + printf(" --module-path= Indicate a module search path. default is current\n" + " directory('./')\n"); +#endif +#if WASM_ENABLE_LIB_PTHREAD != 0 + printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); +#endif + return 1; +} + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static void * +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return NULL; +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char **)realloc(res, sizeof(char *) * (uint32)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * since the function name, + * res[0] might be contains a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +static void * +app_instance_repl(wasm_module_inst_t module_inst) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { + bh_assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} +#endif + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +static char * +handle_module_path(const char *module_path) +{ + // next character after = + return (strchr(module_path, '=')) + 1; +} + +static char *module_search_path = "."; +static bool +module_reader_callback(const char *module_name, uint8 **p_buffer, + uint32 *p_size) +{ + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return false; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +moudle_destroyer(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +int +main(int argc, char *argv[]) +{ + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; +#if WASM_ENABLE_LIBC_WASI != 0 + const char *dir_list[8] = { NULL }; + uint32 dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32 env_list_size = 0; +#endif + + /* Process options. */ + // TODO: use a option name and option handler pair table to + // optimize + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + print_help(); + return 0; + } + func_name = argv[0]; + } +#if WASM_ENABLE_LOG != 0 + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_LIBC_WASI != 0 + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char *))); + return -1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char *)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char *))); + return -1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", + tmp_env); + return print_help(); + } + } +#endif /* WASM_ENABLE_LIBC_WASI */ +#if WASM_ENABLE_MULTI_MODULE != 0 + else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) { + module_search_path = handle_module_path(argv[0]); + if (!strlen(module_search_path)) { + return print_help(); + } + } +#endif +#if WASM_ENABLE_LIB_PTHREAD != 0 + else if (!strncmp(argv[0], "--max-threads=", 14)) { + if (argv[0][14] == '\0') + return print_help(); + wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); + } +#endif + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = argv; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + 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); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer); +#endif + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0, + env_list, env_list_size, argv, argc); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst); + else if (func_name) + app_instance_func(wasm_module_inst, func_name); + else + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} diff --git a/wamr/product-mini/platforms/vxworks/CMakeLists.txt b/wamr/product-mini/platforms/vxworks/CMakeLists.txt new file mode 100644 index 0000000..37e71d8 --- /dev/null +++ b/wamr/product-mini/platforms/vxworks/CMakeLists.txt @@ -0,0 +1,84 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (iwasm) + +set (WAMR_BUILD_PLATFORM "vxworks") + +# Specify the compiler driver provided in the VSB +SET(CMAKE_C_COMPILER vx-cc) +SET(CMAKE_AR vx-ar) +SET(CMAKE_RANLIB vx-ranlib) + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +#set (WAMR_BUILD_TARGET "X86_64") + +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Disable AOT by default. + set (WAMR_BUILD_AOT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS} -lm -ldl -lunix) + +add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS} -lm -ldl -lunix) + diff --git a/wamr/product-mini/platforms/vxworks/main.c b/wamr/product-mini/platforms/vxworks/main.c new file mode 100644 index 0000000..defc0d9 --- /dev/null +++ b/wamr/product-mini/platforms/vxworks/main.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +static int app_argc; +static char **app_argv; + +static int print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify function name to run in module\n" + " rather than main\n"); +#if WASM_ENABLE_LOG != 0 + printf(" -v=n Set log verbose level (0 to 5, default is 2),\n" + " larger level with more log\n"); +#endif + printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); +#if WASM_ENABLE_LIBC_WASI != 0 + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir= Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); +#endif + + return 1; +} + +static void* +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static void* +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return NULL; +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char**) realloc(res, sizeof(char*) * (uint32)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + if (count) { + *count = idx - 1; + } + return res; +} + +static void* +app_instance_repl(wasm_module_inst_t module_inst) +{ + char *cmd = NULL; + size_t len = 0; + ssize_t n; + + while ((printf("webassembly> "), n = getline(&cmd, &len, stdin)) != -1) { + bh_assert(n > 0); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + } + free(app_argv); + } + free(cmd); + return NULL; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} +#endif + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +int main(int argc, char *argv[]) +{ + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; +#if WASM_ENABLE_LIBC_WASI != 0 + const char *dir_list[8] = { NULL }; + uint32 dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32 env_list_size = 0; +#endif + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + print_help(); + return 0; + } + func_name = argv[0]; + } +#if WASM_ENABLE_LOG != 0 + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) + is_repl_mode = true; + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_LIBC_WASI != 0 + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char*)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char*))); + return -1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char*)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char*))); + return -1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", got \"%s\"\n", + tmp_env); + return print_help(); + } + } +#endif + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = argv; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + 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); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + bh_log_set_verbose_level(log_verbose_level); + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = (uint8*) bh_read_file_to_buffer(wasm_file, + &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_runtime_set_wasi_args(wasm_module, + dir_list, dir_list_size, + NULL, 0, + env_list, env_list_size, + argv, argc); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + stack_size, + heap_size, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst); + else if (func_name) + app_instance_func(wasm_module_inst, func_name); + else + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} + diff --git a/wamr/product-mini/platforms/windows/CMakeLists.txt b/wamr/product-mini/platforms/windows/CMakeLists.txt new file mode 100644 index 0000000..3bc67d4 --- /dev/null +++ b/wamr/product-mini/platforms/windows/CMakeLists.txt @@ -0,0 +1,121 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (iwasm C ASM) +enable_language(ASM_MASM) +# set (CMAKE_VERBOSE_MAKEFILE 1) + +set (WAMR_BUILD_PLATFORM "windows") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (CMAKE_C_STANDARD 99) + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS", "XTENSA" +if (NOT DEFINED WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Enable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (NOT DEFINED WAMR_BUILD_FAST_INTERP) + # Enable fast interpreter + set (WAMR_BUILD_FAST_INTERP 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MULTI_MODULE) + # Enable multiple modules + set (WAMR_BUILD_MULTI_MODULE 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIB_PTHREAD) + # Disable pthread library by default + set (WAMR_BUILD_LIB_PTHREAD 0) +endif () + +if (NOT DEFINED WAMR_BUILD_MINI_LOADER) + # Disable wasm mini loader by default + set (WAMR_BUILD_MINI_LOADER 0) +endif () + +if (COLLECT_CODE_COVERAGE EQUAL 1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wsign-conversion") + +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang" OR MSVC)) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# The following flags are to enhance security, but it may impact performance, +# we disable them by default. +#if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftrapv -D_FORTIFY_SOURCE=2") +#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") + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable (iwasm main.c ${UNCOMMON_SHARED_SOURCE}) + +install (TARGETS iwasm DESTINATION bin) + +target_link_libraries (iwasm vmlib ${LLVM_AVAILABLE_LIBS}) + +add_library (libiwasm SHARED ${WAMR_RUNTIME_LIB_SOURCE}) + +install (TARGETS libiwasm DESTINATION lib) + +set_target_properties (libiwasm PROPERTIES OUTPUT_NAME iwasm) + +target_link_libraries (libiwasm ${LLVM_AVAILABLE_LIBS}) diff --git a/wamr/product-mini/platforms/windows/main.c b/wamr/product-mini/platforms/windows/main.c new file mode 100644 index 0000000..bed5ee4 --- /dev/null +++ b/wamr/product-mini/platforms/windows/main.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +static int app_argc; +static char **app_argv; + +#define MODULE_PATH ("--module-path=") + +static int +print_help() +{ + printf("Usage: iwasm [-options] wasm_file [args...]\n"); + printf("options:\n"); + printf(" -f|--function name Specify a function name of the module to run rather\n" + " than main\n"); +#if WASM_ENABLE_LOG != 0 + printf(" -v=n Set log verbose level (0 to 5, default is 2) larger\n" + " level with more log\n"); +#endif + printf(" --stack-size=n Set maximum stack size in bytes, default is 16 KB\n"); + printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n"); + printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n" + " that runs commands in the form of `FUNC ARG...`\n"); +#if WASM_ENABLE_LIBC_WASI != 0 + printf(" --env= Pass wasi environment variables with \"key=value\"\n"); + printf(" to the program, for example:\n"); + printf(" --env=\"key1=value1\" --env=\"key2=value2\"\n"); + printf(" --dir= Grant wasi access to the given host directories\n"); + printf(" to the program, for example:\n"); + printf(" --dir= --dir=\n"); +#endif +#if WASM_ENABLE_MULTI_MODULE != 0 + printf(" --module-path= Indicate a module search path. default is current\n" + " directory('./')\n"); +#endif +#if WASM_ENABLE_LIB_PTHREAD != 0 + printf(" --max-threads=n Set maximum thread number per cluster, default is 4\n"); +#endif + return 1; +} + +static void * +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static void * +app_instance_func(wasm_module_inst_t module_inst, const char *func_name) +{ + wasm_application_execute_func(module_inst, func_name, app_argc - 1, + app_argv + 1); + /* The result of wasm function or exception info was output inside + wasm_application_execute_func(), here we don't output them again. */ + return NULL; +} + +/** + * Split a space separated strings into an array of strings + * Returns NULL on failure + * Memory must be freed by caller + * Based on: http://stackoverflow.com/a/11198630/471795 + */ +static char ** +split_string(char *str, int *count) +{ + char **res = NULL; + char *p; + int idx = 0; + + /* split string and append tokens to 'res' */ + do { + p = strtok(str, " "); + str = NULL; + res = (char **)realloc(res, sizeof(char *) * (uint32)(idx + 1)); + if (res == NULL) { + return NULL; + } + res[idx++] = p; + } while (p); + + /** + * since the function name, + * res[0] might be contains a '\' to indicate a space + * func\name -> func name + */ + p = strchr(res[0], '\\'); + while (p) { + *p = ' '; + p = strchr(p, '\\'); + } + + if (count) { + *count = idx - 1; + } + return res; +} + +static void * +app_instance_repl(wasm_module_inst_t module_inst) +{ + char buffer[4096]; + char *cmd; + size_t n; + + while ((printf("webassembly> "), + cmd = fgets(buffer, sizeof(buffer), stdin)) != -1) { + bh_assert(cmd); + n = strlen(cmd); + if (cmd[n - 1] == '\n') { + if (n == 1) + continue; + else + cmd[n - 1] = '\0'; + } + if (!strcmp(cmd, "__exit__")) { + printf("exit repl mode\n"); + break; + } + app_argv = split_string(cmd, &app_argc); + if (app_argv == NULL) { + LOG_ERROR("Wasm prepare param failed: split string failed.\n"); + break; + } + if (app_argc != 0) { + wasm_application_execute_func(module_inst, app_argv[0], + app_argc - 1, app_argv + 1); + } + free(app_argv); + } + + return NULL; +} + +#if WASM_ENABLE_LIBC_WASI != 0 +static bool +validate_env_str(char *env) +{ + char *p = env; + int key_len = 0; + + while (*p != '\0' && *p != '=') { + key_len++; + p++; + } + + if (*p != '=' || key_len == 0) + return false; + + return true; +} +#endif + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +#if WASM_ENABLE_MULTI_MODULE != 0 +static char * +handle_module_path(const char *module_path) +{ + // next character after = + return (strchr(module_path, '=')) + 1; +} + +static char *module_search_path = "."; +static bool +module_reader_callback(const char *module_name, uint8 **p_buffer, + uint32 *p_size) +{ + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return false; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_name, p_size); + + wasm_runtime_free(wasm_file_name); + return *p_buffer != NULL; +} + +static void +moudle_destroyer(uint8 *buffer, uint32 size) +{ + if (!buffer) { + return; + } + + wasm_runtime_free(buffer); + buffer = NULL; +} +#endif /* WASM_ENABLE_MULTI_MODULE */ + +int +main(int argc, char *argv[]) +{ + char *wasm_file = NULL; + const char *func_name = NULL; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + bool is_repl_mode = false; +#if WASM_ENABLE_LIBC_WASI != 0 + const char *dir_list[8] = { NULL }; + uint32 dir_list_size = 0; + const char *env_list[8] = { NULL }; + uint32 env_list_size = 0; +#endif + + /* Process options. */ + // TODO: use a option name and option handler pair table to + // optimize + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-f") || !strcmp(argv[0], "--function")) { + argc--, argv++; + if (argc < 2) { + print_help(); + return 0; + } + func_name = argv[0]; + } +#if WASM_ENABLE_LOG != 0 + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } +#endif + else if (!strcmp(argv[0], "--repl")) { + is_repl_mode = true; + } + else if (!strncmp(argv[0], "--stack-size=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + stack_size = atoi(argv[0] + 13); + } + else if (!strncmp(argv[0], "--heap-size=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + heap_size = atoi(argv[0] + 12); + } +#if WASM_ENABLE_LIBC_WASI != 0 + else if (!strncmp(argv[0], "--dir=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + if (dir_list_size >= sizeof(dir_list) / sizeof(char *)) { + printf("Only allow max dir number %d\n", + (int)(sizeof(dir_list) / sizeof(char *))); + return -1; + } + dir_list[dir_list_size++] = argv[0] + 6; + } + else if (!strncmp(argv[0], "--env=", 6)) { + char *tmp_env; + + if (argv[0][6] == '\0') + return print_help(); + if (env_list_size >= sizeof(env_list) / sizeof(char *)) { + printf("Only allow max env number %d\n", + (int)(sizeof(env_list) / sizeof(char *))); + return -1; + } + tmp_env = argv[0] + 6; + if (validate_env_str(tmp_env)) + env_list[env_list_size++] = tmp_env; + else { + printf("Wasm parse env string failed: expect \"key=value\", " + "got \"%s\"\n", + tmp_env); + return print_help(); + } + } +#endif /* WASM_ENABLE_LIBC_WASI */ +#if WASM_ENABLE_MULTI_MODULE != 0 + else if (!strncmp(argv[0], MODULE_PATH, strlen(MODULE_PATH))) { + module_search_path = handle_module_path(argv[0]); + if (!strlen(module_search_path)) { + return print_help(); + } + } +#endif +#if WASM_ENABLE_LIB_PTHREAD != 0 + else if (!strncmp(argv[0], "--max-threads=", 14)) { + if (argv[0][14] == '\0') + return print_help(); + wasm_runtime_set_max_thread_num(atoi(argv[0] + 14)); + } +#endif + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + wasm_file = argv[0]; + app_argc = argc; + app_argv = argv; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + 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); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + +#if WASM_ENABLE_MULTI_MODULE != 0 + wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer); +#endif + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + +#if WASM_ENABLE_LIBC_WASI != 0 + wasm_runtime_set_wasi_args(wasm_module, dir_list, dir_list_size, NULL, 0, + env_list, env_list_size, argv, argc); +#endif + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + if (is_repl_mode) + app_instance_repl(wasm_module_inst); + else if (func_name) + app_instance_func(wasm_module_inst, func_name); + else + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} diff --git a/wamr/product-mini/platforms/zephyr/simple/CMakeLists.txt b/wamr/product-mini/platforms/zephyr/simple/CMakeLists.txt new file mode 100644 index 0000000..affa755 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +enable_language (ASM) + +set (WAMR_BUILD_PLATFORM "zephyr") + +# Build as X86_32 by default, change to "AARCH64[sub]", "ARM[sub]", "THUMB[sub]", "MIPS" or "XTENSA" +# if we want to support arm, thumb, mips or xtensa +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "X86_32") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + # Enable AOT by default. + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) + +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + src/main.c) + diff --git a/wamr/product-mini/platforms/zephyr/simple/build_and_run.sh b/wamr/product-mini/platforms/zephyr/simple/build_and_run.sh new file mode 100755 index 0000000..e222366 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/build_and_run.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +X86_TARGET="x86" +STM32_TARGET="stm32" +QEMU_CORTEX_A53="qemu_cortex_a53" +XTENSA_QEMU_TARGET="xtensa-qemu" +ESP32_TARGET="esp32" + +usage () +{ + echo "USAGE:" + echo "$0 $X86_TARGET|$STM32_TARGET|$QEMU_CORTEX_A53|$XTENSA_QEMU_TARGET|$ESP32_TARGET" + echo "Example:" + echo " $0 $X86_TARGET" + echo " $0 $STM32_TARGET" + echo " $0 $QEMU_CORTEX_A53" + echo " $0 $XTENSA_QEMU_TARGET" + echo " $0 $ESP32_TARGET" + exit 1 +} + +if [ $# != 1 ] ; then + usage +fi + +TARGET=$1 + +case $TARGET in + $X86_TARGET) + west build -b qemu_x86_nommu \ + . -p always -- \ + -DCONF_FILE=prj_qemu_x86_nommu.conf \ + -DWAMR_BUILD_TARGET=X86_32 + west build -t run + ;; + $STM32_TARGET) + west build -b nucleo_f767zi \ + . -p always -- \ + -DCONF_FILE=prj_nucleo767zi.conf \ + -DWAMR_BUILD_TARGET=THUMBV7 + west flash + ;; + $XTENSA_QEMU_TARGET) + west build -b qemu_xtensa \ + . -p always -- \ + -DCONF_FILE=prj_qemu_xtensa.conf \ + -DWAMR_BUILD_TARGET=XTENSA + west build -t run + ;; + $ESP32_TARGET) + # suppose you have set environment variable ESP_IDF_PATH + west build -b esp32 \ + . -p always -- \ + -DESP_IDF_PATH=$ESP_IDF_PATH \ + -DCONF_FILE=prj_esp32.conf \ + -DWAMR_BUILD_TARGET=XTENSA + # suppose the serial port is /dev/ttyUSB1 and you should change to + # the real name accordingly + west flash --esp-device /dev/ttyUSB1 + ;; + $QEMU_CORTEX_A53) + west build -b qemu_cortex_a53 \ + . -p always -- \ + -DCONF_FILE=prj_qemu_cortex_a53.conf \ + -DWAMR_BUILD_TARGET=AARCH64 + west build -t run + ;; + *) + echo "unsupported target: $TARGET" + usage + exit 1 + ;; +esac + diff --git a/wamr/product-mini/platforms/zephyr/simple/esp32_custom_linker.ld b/wamr/product-mini/platforms/zephyr/simple/esp32_custom_linker.ld new file mode 100644 index 0000000..3593205 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/esp32_custom_linker.ld @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016 Cadence Design Systems, Inc. + * Copyright (c) 2017 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Linker command/script file + * + * Linker script for the Xtensa platform. + */ + +#include +#include +#include +#include +#include + +#define RAMABLE_REGION dram0_0_seg :dram0_0_phdr +#define RAMABLE_REGION1 dram0_1_seg :dram0_0_phdr +#define ROMABLE_REGION iram0_0_seg :iram0_0_phdr + +PROVIDE ( __stack = 0x3ffe3f20 ); + +PROVIDE ( esp32_rom_uart_tx_one_char = 0x40009200 ); +PROVIDE ( esp32_rom_uart_rx_one_char = 0x400092d0 ); +PROVIDE ( esp32_rom_uart_attach = 0x40008fd0 ); +PROVIDE ( esp32_rom_intr_matrix_set = 0x4000681c ); +PROVIDE ( esp32_rom_gpio_matrix_in = 0x40009edc ); +PROVIDE ( esp32_rom_gpio_matrix_out = 0x40009f0c ); +PROVIDE ( esp32_rom_Cache_Flush = 0x40009a14 ); +PROVIDE ( esp32_rom_Cache_Read_Enable = 0x40009a84 ); +PROVIDE ( esp32_rom_ets_set_appcpu_boot_addr = 0x4000689c ); + +MEMORY +{ + iram0_0_seg(RX): org = 0x40080000, len = 0x20000 + iram0_2_seg(RX): org = 0x400D0018, len = 0x330000 + dram0_0_seg(RW): org = 0x3FFB0000, len = 0x30000 + dram0_1_seg(RWX):org = 0x400A0000, len = 0x20000 + drom0_0_seg(R): org = 0x3F400010, len = 0x800000 + rtc_iram_seg(RWX): org = 0x400C0000, len = 0x2000 + rtc_slow_seg(RW): org = 0x50000000, len = 0x1000 +#ifdef CONFIG_GEN_ISR_TABLES + IDT_LIST(RW): org = 0x3ebfe010, len = 0x2000 +#endif +} + +PHDRS +{ + iram0_0_phdr PT_LOAD; + dram0_0_phdr PT_LOAD; +} + +/* Default entry point: */ +PROVIDE ( _ResetVector = 0x40000400 ); +ENTRY(CONFIG_KERNEL_ENTRY) + +_rom_store_table = 0; + +PROVIDE(_memmap_vecbase_reset = 0x40000450); +PROVIDE(_memmap_reset_vector = 0x40000400); + +SECTIONS +{ + +#include + + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : ALIGN(4) + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } GROUP_LINK_IN(ROMABLE_REGION) + +#include +#include + + SECTION_PROLOGUE(_TEXT_SECTION_NAME, , ALIGN(4)) + { + /* Code marked as running out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + *(.iram0.literal .iram.literal .iram.text.literal .iram0.text .iram.text) + *(.literal .text .literal.* .text.*) + _iram_text_end = ABSOLUTE(.); + } GROUP_LINK_IN(ROMABLE_REGION) + + .dram0.text : + { + _data_start = ABSOLUTE(.); + *(.aot_code_buf) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } GROUP_LINK_IN(RAMABLE_REGION1) + + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + KEEP(*(.jcr)) + *(.dram1 .dram1.*) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } GROUP_LINK_IN(RAMABLE_REGION) + + SECTION_PROLOGUE(_RODATA_SECTION_NAME,,ALIGN(4)) + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); + KEEP (*(.xt_except_table)) + KEEP (*(.gcc_except_table .gcc_except_table.*)) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + KEEP (*(.eh_frame)) + /* C++ constructor and destructor tables, properly ordered: */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS__ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + . = ALIGN(4); /* this table MUST be 4-byte aligned */ + _rodata_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + /* Shared RAM */ + SECTION_DATA_PROLOGUE(_BSS_SECTION_NAME,(NOLOAD),) + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + SECTION_DATA_PROLOGUE(_APP_NOINIT_SECTION_NAME, (NOLOAD),) + { + . = ALIGN (8); + *(.app_noinit) + *("app_noinit.*") + . = ALIGN (8); + _app_end = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + + + SECTION_DATA_PROLOGUE(_NOINIT_SECTION_NAME, (NOLOAD),) + { + . = ALIGN (8); + *(.noinit) + *(".noinit.*") + . = ALIGN (8); + _heap_start = ABSOLUTE(.); + } GROUP_LINK_IN(RAMABLE_REGION) + +#ifdef CONFIG_GEN_ISR_TABLES +#include +#endif + +#include + + SECTION_PROLOGUE(.xtensa.info, 0,) + { + *(.xtensa.info) + } + +} diff --git a/wamr/product-mini/platforms/zephyr/simple/prj_esp32.conf b/wamr/product-mini/platforms/zephyr/simple/prj_esp32.conf new file mode 100644 index 0000000..5d6a67f --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/prj_esp32.conf @@ -0,0 +1,8 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y +CONFIG_HAVE_CUSTOM_LINKER_SCRIPT=y +CONFIG_CUSTOM_LINKER_SCRIPT="esp32_custom_linker.ld" diff --git a/wamr/product-mini/platforms/zephyr/simple/prj_nucleo767zi.conf b/wamr/product-mini/platforms/zephyr/simple/prj_nucleo767zi.conf new file mode 100644 index 0000000..c495644 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/prj_nucleo767zi.conf @@ -0,0 +1,7 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MPU=y +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/wamr/product-mini/platforms/zephyr/simple/prj_qemu_cortex_a53.conf b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_cortex_a53.conf new file mode 100644 index 0000000..d248565 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_cortex_a53.conf @@ -0,0 +1,7 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_ARM_MMU=n +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/wamr/product-mini/platforms/zephyr/simple/prj_qemu_x86_nommu.conf b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_x86_nommu.conf new file mode 100644 index 0000000..7f4a328 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_x86_nommu.conf @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/wamr/product-mini/platforms/zephyr/simple/prj_qemu_xtensa.conf b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_xtensa.conf new file mode 100644 index 0000000..7f4a328 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/prj_qemu_xtensa.conf @@ -0,0 +1,6 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CONFIG_STACK_SENTINEL=y +CONFIG_PRINTK=y +CONFIG_LOG=y diff --git a/wamr/product-mini/platforms/zephyr/simple/src/main.c b/wamr/product-mini/platforms/zephyr/simple/src/main.c new file mode 100644 index 0000000..5079888 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/src/main.c @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" +#include "test_wasm.h" + +#include +#include + +#define CONFIG_GLOBAL_HEAP_BUF_SIZE 131072 +#define CONFIG_APP_STACK_SIZE 8192 +#define CONFIG_APP_HEAP_SIZE 8192 + +#ifdef CONFIG_NO_OPTIMIZATIONS +#define CONFIG_MAIN_THREAD_STACK_SIZE 8192 +#else +#define CONFIG_MAIN_THREAD_STACK_SIZE 4096 +#endif + +static int app_argc; +static char **app_argv; + +/** + * Find the unique main function from a WASM module instance + * and execute that function. + * + * @param module_inst the WASM module instance + * @param argc the number of arguments + * @param argv the arguments array + * + * @return true if the main function is called, false otherwise. + */ +bool +wasm_application_execute_main(wasm_module_inst_t module_inst, + int argc, char *argv[]); + +static void* +app_instance_main(wasm_module_inst_t module_inst) +{ + const char *exception; + + wasm_application_execute_main(module_inst, app_argc, app_argv); + if ((exception = wasm_runtime_get_exception(module_inst))) + printf("%s\n", exception); + return NULL; +} + +static char global_heap_buf[CONFIG_GLOBAL_HEAP_BUF_SIZE] = { 0 }; + +#ifdef CONFIG_BOARD_ESP32 +#include "mem_alloc.h" +/* +esp32_technical_reference_manual: +" +The capacity of Internal SRAM 1 is 128 KB. Either CPU can read and write this memory at addresses +0x3FFE_0000 ~ 0x3FFF_FFFF of the data bus, and also at addresses 0x400A_0000 ~ 0x400B_FFFF of the +instruction bus. +" + +The custom linker script defines dram0_1_seg and map it to 0x400A_0000 ~ 0x400B_FFFF for instruction bus access. +Here we define the buffer that will be placed to dram0_1_seg. +*/ +static char esp32_executable_memory_buf[100 * 1024] __attribute__((section (".aot_code_buf"))) = { 0 }; + +/* the poll allocator for executable memory */ +static mem_allocator_t esp32_exec_mem_pool_allocator; + +static int +esp32_exec_mem_init() +{ + if (!(esp32_exec_mem_pool_allocator = + mem_allocator_create(esp32_executable_memory_buf, + sizeof(esp32_executable_memory_buf)))) + return -1; + + return 0; +} + +static void +esp32_exec_mem_destroy() +{ + mem_allocator_destroy(esp32_exec_mem_pool_allocator); +} + +static void * +esp32_exec_mem_alloc(unsigned int size) +{ + return mem_allocator_malloc(esp32_exec_mem_pool_allocator, size); +} + +static void +esp32_exec_mem_free(void *addr) +{ + mem_allocator_free(esp32_exec_mem_pool_allocator, addr); +} +#endif /* end of #ifdef CONFIG_BOARD_ESP32 */ + +void iwasm_main(void *arg1, void *arg2, void *arg3) +{ + int start, end; + start = k_uptime_get_32(); + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + RuntimeInitArgs init_args; + char error_buf[128]; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + + (void) arg1; + (void) arg2; + (void) arg3; + + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return; + } + +#ifdef CONFIG_BOARD_ESP32 + /* Initialize executable memory */ + if (esp32_exec_mem_init() != 0) { + printf("Init executable memory failed.\n"); + goto fail1; + } + /* Set hook functions for executable memory management */ + set_exec_mem_alloc_func(esp32_exec_mem_alloc, esp32_exec_mem_free); +#endif + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from byte buffer of include file */ + wasm_file_buf = (uint8*) wasm_test_file; + wasm_file_size = sizeof(wasm_test_file); + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); +#ifdef CONFIG_BOARD_ESP32 + goto fail1_1; +#else + goto fail1; +#endif + } + + /* instantiate the module */ + if (!(wasm_module_inst = wasm_runtime_instantiate(wasm_module, + CONFIG_APP_STACK_SIZE, + CONFIG_APP_HEAP_SIZE, + error_buf, + sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* invoke the main function */ + app_instance_main(wasm_module_inst); + + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail2: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +#ifdef CONFIG_BOARD_ESP32 +fail1_1: + /* destroy executable memory */ + esp32_exec_mem_destroy(); +#endif + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + + end = k_uptime_get_32(); + + printf("elpase: %d\n", (end - start)); +} + +#define MAIN_THREAD_STACK_SIZE (CONFIG_MAIN_THREAD_STACK_SIZE) +#define MAIN_THREAD_PRIORITY 5 + +K_THREAD_STACK_DEFINE(iwasm_main_thread_stack, MAIN_THREAD_STACK_SIZE); +static struct k_thread iwasm_main_thread; + +bool iwasm_init(void) +{ + k_tid_t tid = k_thread_create(&iwasm_main_thread, iwasm_main_thread_stack, + MAIN_THREAD_STACK_SIZE, + iwasm_main, NULL, NULL, NULL, + MAIN_THREAD_PRIORITY, 0, K_NO_WAIT); + return tid ? true : false; +} +void main(void) +{ + iwasm_init(); +} + diff --git a/wamr/product-mini/platforms/zephyr/simple/src/test_wasm.h b/wamr/product-mini/platforms/zephyr/simple/src/test_wasm.h new file mode 100644 index 0000000..23e87b5 --- /dev/null +++ b/wamr/product-mini/platforms/zephyr/simple/src/test_wasm.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * The byte array buffer is the file content of a test wasm binary file, + * which is compiled by emcc or clang toolchain from C source file of: + * core/iwasm/app-samples/hello-world/main.c. + */ +unsigned char __aligned(4) wasm_test_file[] = { 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x0D, 0x06, 0x64, 0x79, 0x6C, 0x69, 0x6E, 0x6B, 0xC0, 0x80, + 0x04, 0x04, 0x00, 0x00, 0x01, 0x13, 0x04, 0x60, 0x01, 0x7F, 0x00, 0x60, + 0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x00, + 0x00, 0x02, 0x58, 0x06, 0x03, 0x65, 0x6E, 0x76, 0x05, 0x5F, 0x66, 0x72, + 0x65, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, + 0x70, 0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x02, 0x03, 0x65, 0x6E, 0x76, + 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, + 0x0D, 0x5F, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x5F, 0x62, 0x61, + 0x73, 0x65, 0x03, 0x7F, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D, 0x65, + 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x01, 0x03, 0x04, 0x03, 0x02, 0x03, + 0x03, 0x06, 0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, + 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x1B, 0x0B, 0x07, 0x33, 0x04, 0x12, 0x5F, + 0x5F, 0x70, 0x6F, 0x73, 0x74, 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, + 0x74, 0x69, 0x61, 0x74, 0x65, 0x00, 0x06, 0x05, 0x5F, 0x6D, 0x61, 0x69, + 0x6E, 0x00, 0x04, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, 0x53, + 0x65, 0x74, 0x73, 0x00, 0x05, 0x04, 0x5F, 0x73, 0x74, 0x72, 0x03, 0x03, + 0x0A, 0xBA, 0x01, 0x03, 0x9E, 0x01, 0x01, 0x01, 0x7F, 0x23, 0x01, 0x21, + 0x00, 0x23, 0x01, 0x41, 0x10, 0x6A, 0x24, 0x01, 0x20, 0x00, 0x41, 0x08, + 0x6A, 0x21, 0x02, 0x23, 0x00, 0x41, 0x1B, 0x6A, 0x10, 0x03, 0x1A, 0x41, + 0x80, 0x08, 0x10, 0x01, 0x21, 0x01, 0x20, 0x01, 0x04, 0x7F, 0x20, 0x00, + 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x20, 0x00, 0x10, 0x02, 0x1A, + 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x0D, 0x3A, 0x00, 0x00, 0x20, 0x01, + 0x23, 0x00, 0x2C, 0x00, 0x0E, 0x3A, 0x00, 0x01, 0x20, 0x01, 0x23, 0x00, + 0x2C, 0x00, 0x0F, 0x3A, 0x00, 0x02, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, + 0x10, 0x3A, 0x00, 0x03, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x11, 0x3A, + 0x00, 0x04, 0x20, 0x01, 0x23, 0x00, 0x2C, 0x00, 0x12, 0x3A, 0x00, 0x05, + 0x20, 0x02, 0x20, 0x01, 0x36, 0x02, 0x00, 0x23, 0x00, 0x41, 0x13, 0x6A, + 0x20, 0x02, 0x10, 0x02, 0x1A, 0x20, 0x01, 0x10, 0x00, 0x20, 0x00, 0x24, + 0x01, 0x41, 0x00, 0x05, 0x23, 0x00, 0x41, 0x28, 0x6A, 0x10, 0x03, 0x1A, + 0x20, 0x00, 0x24, 0x01, 0x41, 0x7F, 0x0B, 0x0B, 0x03, 0x00, 0x01, 0x0B, + 0x14, 0x00, 0x23, 0x00, 0x41, 0x40, 0x6B, 0x24, 0x01, 0x23, 0x01, 0x41, + 0x80, 0x80, 0x04, 0x6A, 0x24, 0x02, 0x10, 0x05, 0x0B, 0x0B, 0x3F, 0x01, + 0x00, 0x23, 0x00, 0x0B, 0x39, 0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, + 0x3A, 0x20, 0x25, 0x70, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A, 0x00, + 0x62, 0x75, 0x66, 0x3A, 0x20, 0x25, 0x73, 0x00, 0x48, 0x65, 0x6C, 0x6C, + 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00, 0x6D, 0x61, 0x6C, + 0x6C, 0x6F, 0x63, 0x20, 0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, + 0x65, 0x64, 0x00, 0x50, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x01, 0x49, 0x07, + 0x00, 0x05, 0x5F, 0x66, 0x72, 0x65, 0x65, 0x01, 0x07, 0x5F, 0x6D, 0x61, + 0x6C, 0x6C, 0x6F, 0x63, 0x02, 0x07, 0x5F, 0x70, 0x72, 0x69, 0x6E, 0x74, + 0x66, 0x03, 0x05, 0x5F, 0x70, 0x75, 0x74, 0x73, 0x04, 0x05, 0x5F, 0x6D, + 0x61, 0x69, 0x6E, 0x05, 0x0B, 0x72, 0x75, 0x6E, 0x50, 0x6F, 0x73, 0x74, + 0x53, 0x65, 0x74, 0x73, 0x06, 0x12, 0x5F, 0x5F, 0x70, 0x6F, 0x73, 0x74, + 0x5F, 0x69, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74, 0x69, 0x61, 0x74, 0x65, + 0x00, 0x20, 0x10, 0x73, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x4D, 0x61, 0x70, + 0x70, 0x69, 0x6E, 0x67, 0x55, 0x52, 0x4C, 0x0E, 0x61, 0x2E, 0x6F, 0x75, + 0x74, 0x2E, 0x77, 0x61, 0x73, 0x6D, 0x2E, 0x6D, 0x61, 0x70 }; diff --git a/wamr/samples/basic/.gitignore b/wamr/samples/basic/.gitignore new file mode 100644 index 0000000..0fa8a76 --- /dev/null +++ b/wamr/samples/basic/.gitignore @@ -0,0 +1 @@ +/out/ \ No newline at end of file diff --git a/wamr/samples/basic/CMakeLists.txt b/wamr/samples/basic/CMakeLists.txt new file mode 100644 index 0000000..1c7db65 --- /dev/null +++ b/wamr/samples/basic/CMakeLists.txt @@ -0,0 +1,56 @@ +# 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 ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set (WAMR_BUILD_TARGET "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} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# 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}) + +if (APPLE) + target_link_libraries (basic vmlib -lm -ldl -lpthread) +else () + target_link_libraries (basic vmlib -lm -ldl -lpthread -lrt) +endif () diff --git a/wamr/samples/basic/README.md b/wamr/samples/basic/README.md new file mode 100644 index 0000000..32e7ed6 --- /dev/null +++ b/wamr/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 -f 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/wamr/samples/basic/build.sh b/wamr/samples/basic/build.sh new file mode 100755 index 0000000..7e3442c --- /dev/null +++ b/wamr/samples/basic/build.sh @@ -0,0 +1,58 @@ +#!/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,--export=calculate\ + -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/wamr/samples/basic/run.sh b/wamr/samples/basic/run.sh new file mode 100755 index 0000000..a5fb291 --- /dev/null +++ b/wamr/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/wamr/samples/basic/src/main.c b/wamr/samples/basic/src/main.c new file mode 100644 index 0000000..e2b5dd6 --- /dev/null +++ b/wamr/samples/basic/src/main.c @@ -0,0 +1,204 @@ + +/* + * 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); +int32_t calculate_native(int32_t n, int32_t func1, int32_t func2); + +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; + uint32_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/main/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 + NULL // attachment is NULL + }, + { + "get_pow", // the name of WASM function name + get_pow, // the native function pointer + "(ii)i", // the function prototype signature, avoid to use i32 + NULL // attachment is NULL + }, + { + "calculate_native", + calculate_native, + "(iii)i", + NULL + } + }; + + 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; + } + + wasm_function_inst_t func3 = wasm_runtime_lookup_function(module_inst, + "calculate", + NULL); + if (!func3) { + printf("The wasm function calculate is not found.\n"); + goto fail; + } + + uint32_t argv3[1] = {3}; + if (wasm_runtime_call_wasm(exec_env, func3, 1, argv3)) { + uint32_t result = *(uint32_t*)argv3; + printf("Native finished calling wasm function: calculate, return: %d\n", result); + } else { + printf("call wasm function calculate 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/wamr/samples/basic/src/native_impl.c b/wamr/samples/basic/src/native_impl.c new file mode 100644 index 0000000..b246d0e --- /dev/null +++ b/wamr/samples/basic/src/native_impl.c @@ -0,0 +1,96 @@ +/* + * 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" + +extern bool +wasm_runtime_call_indirect(wasm_exec_env_t exec_env, + uint32_t element_indices, + uint32_t argc, uint32_t argv[]); + +// 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); +} + +int32_t +calculate_native(wasm_exec_env_t exec_env, int32_t n, int32_t func1, + int32_t func2) +{ + printf("calling into native function: %s, n=%d, func1=%d, func2=%d\n", + __FUNCTION__, n, func1, func2); + + uint32_t argv[] = { n }; + if (!wasm_runtime_call_indirect(exec_env, func1, 1, argv)) { + printf("call func1 failed\n"); + return 0xDEAD; + } + + uint32_t n1 = argv[0]; + printf("call func1 and return n1=%d\n", n1); + + if (!wasm_runtime_call_indirect(exec_env, func2, 1, argv)) { + printf("call func2 failed\n"); + return 0xDEAD; + } + + uint32_t n2 = argv[0]; + printf("call func2 and return n2=%d\n", n2); + return n1 + n2; +} diff --git a/wamr/samples/basic/wasm-apps/testapp.c b/wamr/samples/basic/wasm-apps/testapp.c new file mode 100644 index 0000000..e5d4883 --- /dev/null +++ b/wamr/samples/basic/wasm-apps/testapp.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include + +int intToStr(int x, char* str, int str_len, int digit); +int get_pow(int x, int y); +int32_t calculate_native(int32_t n, int32_t func1, int32_t func2); + +// +// 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 +`./build.sh`
    + All binaries are in "out", which contains "host_tool", "ui_decrease.wasm", "ui_increase.wasm" and "wasm_runtime_wgl". + +- Run WASM VM Linux applicaton & install WASM APP
    + First start wasm_runtime_wgl in server mode.
    +`./wasm_runtime_wgl -s`
    + Then install wasm APP use host tool.
    +`./host_tool -i inc -f ui_increase.wasm`
    +`./host_tool -i dec -f ui_decrease.wasm`
    + + + +Test on Zephyr +================================ + +We can use a STM32 NUCLEO_F767ZI board with ILI9341 display and XPT2046 touch screen to run the test. Then use host_tool to remotely install wasm app into STM32. +- Build WASM VM into Zephyr system
    + a. clone zephyr source code
    +Refer to Zephyr getting started.
    +https://docs.zephyrproject.org/latest/getting_started/index.html
    +`west init zephyrproject`
    +`cd zephyrproject`
    +`west update`
    + b. copy samples
    + `cd zephyr/samples/`
    + `cp -a samples/gui/wasm-runtime-wgl wasm-runtime-wgl`
    + `cd wasm-runtime-wgl/zephyr_build`
    + c. create a link to wamr root dir
    + ` ln -s wamr`
    + d. build source code
    + `mkdir build && cd build`
    + `source ../../../../zephyr-env.sh`
    + `cmake -GNinja -DBOARD=nucleo_f767zi ..`
    + ` ninja flash`
    + +- Hardware Connections + +``` ++-------------------+-+------------------+ +|NUCLEO-F767ZI | ILI9341 Display | ++-------------------+-+------------------+ +| CN7.10 | CLK | ++-------------------+-+------------------+ +| CN7.12 | MISO | ++-------------------+-+------------------+ +| CN7.14 | MOSI | ++-------------------+-+------------------+ +| CN11.1 | CS1 for ILI9341 | ++-------------------+-+------------------+ +| CN11.2 | D/C | ++-------------------+-+------------------+ +| CN11.3 | RESET | ++-------------------+-+------------------+ +| CN9.25 | PEN interrupt | ++-------------------+-+------------------+ +| CN9.27 | CS2 for XPT2046 | ++-------------------+-+------------------+ +| CN10.14 | PC UART RX | ++-------------------+-+------------------+ +| CN11.16 | PC UART RX | ++-------------------+-+------------------+ +``` + + +- Install WASM application to Zephyr using host_tool
    +First, connect PC and STM32 with UART. Then install to use host_tool.
    +`./host_tool -D /dev/ttyUSBXXX -i inc -f ui_increase.wasm` + +- Install AOT version WASM application +`wamrc --target=thumbv7 --target-abi=eabi --cpu=cortex-m7 -o ui_app.aot ui_increase.wasm` +`./host_tool -D /dev/ttyUSBXXX -i inc -f ui_app.aot` + + + +The graphic user interface demo photo: + +![WAMR samples diagram](../../doc/pics/vgl_demo.png "WAMR samples diagram") diff --git a/wamr/samples/gui/build.sh b/wamr/samples/gui/build.sh new file mode 100755 index 0000000..a57aa48 --- /dev/null +++ b/wamr/samples/gui/build.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +PROJECT_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out +BUILD_DIR=${PWD}/build +WAMR_RUNTIME_CFG=${PROJECT_DIR}/wamr_config_gui.cmake +LV_CFG_PATH=${PROJECT_DIR}/lv_config + +if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then + echo "Local Build Env" + cmakewrap="cmake" + makewrap="make" +else + echo "Klocwork Build Env" + cmakewrap="cmake -DCMAKE_BUILD_TYPE=Debug" + makewrap="kwinject -o $KW_OUT_FILE make" +fi + +if [ ! -d $BUILD_DIR ]; then + mkdir ${BUILD_DIR} +fi + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} + + +echo -e "\n\n" +echo "##################### 1. build wamr-sdk gui start#####################" +cd ${WAMR_DIR}/wamr-sdk +./build_sdk.sh -n gui -x ${WAMR_RUNTIME_CFG} -e ${LV_CFG_PATH} +[ $? -eq 0 ] || exit $? + +echo "#####################build wamr-sdk success" + + + +echo "##################### 2. build wasm runtime start#####################" +cd $BUILD_DIR +mkdir -p wasm-runtime-wgl +cd wasm-runtime-wgl +$cmakewrap ${PROJECT_DIR}/wasm-runtime-wgl/linux-build -DWAMR_BUILD_SDK_PROFILE=gui +[ $? -eq 0 ] || exit $? +$makewrap +[ $? -eq 0 ] || exit $? +cp wasm_runtime_wgl ${OUT_DIR}/ + +echo "##################### build littlevgl wasm runtime end#####################" +echo -e "\n\n" + + +echo "#####################build host-tool" +cd $BUILD_DIR +mkdir -p host-tool +cd host-tool +$cmakewrap ${WAMR_DIR}/test-tools/host-tool +$makewrap +if [ $? != 0 ];then + echo "BUILD_FAIL host tool exit as $?\n" + exit 2 +fi +cp host_tool ${OUT_DIR} +echo "#####################build host-tool success" +echo -e "\n\n" + +echo "##################### 3. build wasm ui app start#####################" +cd ${PROJECT_DIR}/wasm-apps +export OUT_DIR=${OUT_DIR} +./build_apps.sh + diff --git a/wamr/samples/gui/lv_config/lv_conf.h b/wamr/samples/gui/lv_config/lv_conf.h new file mode 100644 index 0000000..2f9fc77 --- /dev/null +++ b/wamr/samples/gui/lv_config/lv_conf.h @@ -0,0 +1,498 @@ +/** + * @file lv_conf.h + * + */ + +/* + * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER + */ + +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H +/* clang-format off */ + +#include + +/*==================== + Graphical settings + *====================*/ + +/* Maximal horizontal and vertical resolution to support by the library.*/ +#define LV_HOR_RES_MAX (320) +#define LV_VER_RES_MAX (240) + +/* Color depth: + * - 1: 1 byte per pixel + * - 8: RGB233 + * - 16: RGB565 + * - 32: ARGB8888 + */ +#define LV_COLOR_DEPTH 32 + +/* Swap the 2 bytes of RGB565 color. + * Useful if the display has a 8 bit interface (e.g. SPI)*/ +#define LV_COLOR_16_SWAP 0 + +/* 1: Enable screen transparency. + * Useful for OSD or other overlapping GUIs. + * Requires `LV_COLOR_DEPTH = 32` colors and the screen's style should be modified: `style.body.opa = ...`*/ +#define LV_COLOR_SCREEN_TRANSP 0 + +/*Images pixels with this color will not be drawn (with chroma keying)*/ +#define LV_COLOR_TRANSP LV_COLOR_LIME /*LV_COLOR_LIME: pure green*/ + +/* Enable anti-aliasing (lines, and radiuses will be smoothed) */ +#define LV_ANTIALIAS 1 + +/* Default display refresh period. + * Can be changed in the display driver (`lv_disp_drv_t`).*/ +#define LV_DISP_DEF_REFR_PERIOD 30 /*[ms]*/ + +/* Dot Per Inch: used to initialize default sizes. + * E.g. a button with width = LV_DPI / 2 -> half inch wide + * (Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI 100 /*[px]*/ + +/* Type of coordinates. Should be `int16_t` (or `int32_t` for extreme cases) */ +typedef int16_t lv_coord_t; + +/*========================= + Memory manager settings + *=========================*/ + +/* LittelvGL's internal memory manager's settings. + * The graphical objects and other related data are stored here. */ + +/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */ +#ifndef LV_MEM_CUSTOM +#define LV_MEM_CUSTOM 0 +#endif + +#if LV_MEM_CUSTOM == 0 +/* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ +# define LV_MEM_SIZE (128U * 1024U) + +/* Complier prefix for a big array declaration */ +# define LV_MEM_ATTR + +/* Set an address for the memory pool instead of allocating it as an array. + * Can be in external SRAM too. */ +# define LV_MEM_ADR 0 + +/* Automatically defrag. on free. Defrag. means joining the adjacent free cells. */ +# define LV_MEM_AUTO_DEFRAG 1 +#else /*LV_MEM_CUSTOM*/ +# define LV_MEM_CUSTOM_INCLUDE "bh_platform.h" /*Header for the dynamic memory function*/ +# define LV_MEM_CUSTOM_ALLOC BH_MALLOC /*Wrapper to malloc*/ +# define LV_MEM_CUSTOM_FREE BH_FREE /*Wrapper to free*/ +#endif /*LV_MEM_CUSTOM*/ + +/* Garbage Collector settings + * Used if lvgl is binded to higher level language and the memory is managed by that language */ +#define LV_ENABLE_GC 0 +#if LV_ENABLE_GC != 0 +# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ +# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/ +# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/ +#endif /* LV_ENABLE_GC */ + +/*======================= + Input device settings + *=======================*/ + +/* Input device default settings. + * Can be changed in the Input device driver (`lv_indev_drv_t`)*/ + +/* Input device read period in milliseconds */ +#define LV_INDEV_DEF_READ_PERIOD 30 + +/* Drag threshold in pixels */ +#define LV_INDEV_DEF_DRAG_LIMIT 10 + +/* Drag throw slow-down in [%]. Greater value -> faster slow-down */ +#define LV_INDEV_DEF_DRAG_THROW 20 + +/* Long press time in milliseconds. + * Time to send `LV_EVENT_LONG_PRESSSED`) */ +#define LV_INDEV_DEF_LONG_PRESS_TIME 400 + +/* Repeated trigger period in long press [ms] + * Time between `LV_EVENT_LONG_PRESSED_REPEAT */ +#define LV_INDEV_DEF_LONG_PRESS_REP_TIME 100 + +/*================== + * Feature usage + *==================*/ + +/*1: Enable the Animations */ +#define LV_USE_ANIMATION 1 +#if LV_USE_ANIMATION + +/*Declare the type of the user data of animations (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_anim_user_data_t; + +#endif + +/* 1: Enable shadow drawing*/ +#define LV_USE_SHADOW 1 + +/* 1: Enable object groups (for keyboard/encoder navigation) */ +#define LV_USE_GROUP 1 +#if LV_USE_GROUP +typedef void * lv_group_user_data_t; +#endif /*LV_USE_GROUP*/ + +/* 1: Enable GPU interface*/ +#define LV_USE_GPU 1 + +/* 1: Enable file system (might be required for images */ +#define LV_USE_FILESYSTEM 1 +#if LV_USE_FILESYSTEM +/*Declare the type of the user data of file system drivers (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_fs_drv_user_data_t; +#endif + +/*1: Add a `user_data` to drivers and objects*/ +#define LV_USE_USER_DATA 1 + +/*======================== + * Image decoder and cache + *========================*/ + +/* 1: Enable indexed (palette) images */ +#define LV_IMG_CF_INDEXED 1 + +/* 1: Enable alpha indexed images */ +#define LV_IMG_CF_ALPHA 1 + +/* Default image cache size. Image caching keeps the images opened. + * If only the built-in image formats are used there is no real advantage of caching. + * (I.e. no new image decoder is added) + * With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. + * However the opened images might consume additional RAM. + * LV_IMG_CACHE_DEF_SIZE must be >= 1 */ +#define LV_IMG_CACHE_DEF_SIZE 1 + +/*Declare the type of the user data of image decoder (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_img_decoder_user_data_t; + +/*===================== + * Compiler settings + *====================*/ +/* Define a custom attribute to `lv_tick_inc` function */ +#define LV_ATTRIBUTE_TICK_INC + +/* Define a custom attribute to `lv_task_handler` function */ +#define LV_ATTRIBUTE_TASK_HANDLER + +/* With size optimization (-Os) the compiler might not align data to + * 4 or 8 byte boundary. This alignment will be explicitly applied where needed. + * E.g. __attribute__((aligned(4))) */ +#define LV_ATTRIBUTE_MEM_ALIGN + +/* Attribute to mark large constant arrays for example + * font's bitmaps */ +#define LV_ATTRIBUTE_LARGE_CONST + +/*=================== + * HAL settings + *==================*/ + +/* 1: use a custom tick source. + * It removes the need to manually update the tick with `lv_tick_inc`) */ +#define LV_TICK_CUSTOM 1 +#if LV_TICK_CUSTOM == 1 +#define LV_TICK_CUSTOM_INCLUDE "system_header.h" /*Header for the sys time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (time_get_ms()) /*Expression evaluating to current systime in ms*/ +#endif /*LV_TICK_CUSTOM*/ + +typedef void * lv_disp_drv_user_data_t; /*Type of user data in the display driver*/ +typedef void * lv_indev_drv_user_data_t; /*Type of user data in the input device driver*/ + +/*================ + * Log settings + *===============*/ + +/*1: Enable the log module*/ +#define LV_USE_LOG 1 +#if LV_USE_LOG +/* How important log should be added: + * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + * LV_LOG_LEVEL_INFO Log important events + * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem + * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + * LV_LOG_LEVEL_NONE Do not log anything + */ +# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + +/* 1: Print the log with 'printf'; + * 0: user need to register a callback with `lv_log_register_print`*/ +# define LV_LOG_PRINTF 1 +#endif /*LV_USE_LOG*/ + +/*================ + * THEME USAGE + *================*/ +#define LV_THEME_LIVE_UPDATE 1 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/ + +#define LV_USE_THEME_TEMPL 1 /*Just for test*/ +#define LV_USE_THEME_DEFAULT 1 /*Built mainly from the built-in styles. Consumes very few RAM*/ +#define LV_USE_THEME_ALIEN 1 /*Dark futuristic theme*/ +#define LV_USE_THEME_NIGHT 1 /*Dark elegant theme*/ +#define LV_USE_THEME_MONO 1 /*Mono color theme for monochrome displays*/ +#define LV_USE_THEME_MATERIAL 1 /*Flat theme with bold colors and light shadows*/ +#define LV_USE_THEME_ZEN 1 /*Peaceful, mainly light theme */ +#define LV_USE_THEME_NEMO 1 /*Water-like theme based on the movie "Finding Nemo"*/ + +/*================== + * FONT USAGE + *===================*/ + +/* The built-in fonts contains the ASCII range and some Symbols with 4 bit-per-pixel. + * The symbols are available via `LV_SYMBOL_...` defines + * More info about fonts: https://docs.littlevgl.com/#Fonts + * To create a new font go to: https://littlevgl.com/ttf-font-to-c-array + */ + +/* Robot fonts with bpp = 4 + * https://fonts.google.com/specimen/Roboto */ +#define LV_FONT_ROBOTO_12 1 +#define LV_FONT_ROBOTO_16 1 +#define LV_FONT_ROBOTO_22 1 +#define LV_FONT_ROBOTO_28 1 + +/*Pixel perfect monospace font + * http://pelulamu.net/unscii/ */ +#define LV_FONT_UNSCII_8 1 + +/* Optionally declare your custom fonts here. + * You can use these fonts as default font too + * and they will be available globally. E.g. + * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \ + * LV_FONT_DECLARE(my_font_2) + */ +#define LV_FONT_CUSTOM_DECLARE + +/*Always set a default font from the built-in fonts*/ +#define LV_FONT_DEFAULT &lv_font_roboto_16 + +/* Enable it if you have fonts with a lot of characters. + * The limit depends on the font size, font face and bpp + * but with > 10,000 characters if you see issues probably you need to enable it.*/ +#define LV_FONT_FMT_TXT_LARGE 1 + +/*Declare the type of the user data of fonts (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_font_user_data_t; + +/*================= + * Text settings + *=================*/ + +/* Select a character encoding for strings. + * Your IDE or editor should have the same character encoding + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + * */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + + /*Can break (wrap) texts on these chars*/ +#define LV_TXT_BREAK_CHARS " ,.;:-_" + +/*=================== + * LV_OBJ SETTINGS + *==================*/ + +/*Declare the type of the user data of object (can be e.g. `void *`, `int`, `struct`)*/ +typedef void * lv_obj_user_data_t; + +/*1: enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ +#define LV_USE_OBJ_REALIGN 1 + +/* Enable to make the object clickable on a larger area. + * LV_EXT_CLICK_AREA_OFF or 0: Disable this feature + * LV_EXT_CLICK_AREA_TINY: The extra area can be adjusted horizontally and vertically (0..255 px) + * LV_EXT_CLICK_AREA_FULL: The extra area can be adjusted in all 4 directions (-32k..+32k px) + */ +#define LV_USE_EXT_CLICK_AREA LV_EXT_CLICK_AREA_FULL + +/*================== + * LV OBJ X USAGE + *================*/ +/* + * Documentation of the object types: https://docs.littlevgl.com/#Object-types + */ + +/*Arc (dependencies: -)*/ +#define LV_USE_ARC 1 + +/*Bar (dependencies: -)*/ +#define LV_USE_BAR 1 + +/*Button (dependencies: lv_cont*/ +#define LV_USE_BTN 1 +#if LV_USE_BTN != 0 +/*Enable button-state animations - draw a circle on click (dependencies: LV_USE_ANIMATION)*/ +# define LV_BTN_INK_EFFECT 1 +#endif + +/*Button matrix (dependencies: -)*/ +#define LV_USE_BTNM 1 + +/*Calendar (dependencies: -)*/ +#define LV_USE_CALENDAR 1 + +/*Canvas (dependencies: lv_img)*/ +#define LV_USE_CANVAS 1 + +/*Check box (dependencies: lv_btn, lv_label)*/ +#define LV_USE_CB 1 + +/*Chart (dependencies: -)*/ +#define LV_USE_CHART 1 +#if LV_USE_CHART +# define LV_CHART_AXIS_TICK_LABEL_MAX_LEN 20 +#endif + +/*Container (dependencies: -*/ +#define LV_USE_CONT 1 + +/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ +#define LV_USE_DDLIST 1 +#if LV_USE_DDLIST != 0 +/*Open and close default animation time [ms] (0: no animation)*/ +# define LV_DDLIST_DEF_ANIM_TIME 200 +#endif + +/*Gauge (dependencies:lv_bar, lv_lmeter)*/ +#define LV_USE_GAUGE 1 + +/*Image (dependencies: lv_label*/ +#define LV_USE_IMG 1 + +/*Image Button (dependencies: lv_btn*/ +#define LV_USE_IMGBTN 1 +#if LV_USE_IMGBTN +/*1: The imgbtn requires left, mid and right parts and the width can be set freely*/ +# define LV_IMGBTN_TILED 0 +#endif + +/*Keyboard (dependencies: lv_btnm)*/ +#define LV_USE_KB 1 + +/*Label (dependencies: -*/ +#define LV_USE_LABEL 1 +#if LV_USE_LABEL != 0 +/*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_ROLL/ROLL_CIRC' mode*/ +# define LV_LABEL_DEF_SCROLL_SPEED 25 + +/* Waiting period at beginning/end of animation cycle */ +# define LV_LABEL_WAIT_CHAR_COUNT 3 + +/*Enable selecting text of the label */ +# define LV_LABEL_TEXT_SEL 1 + +/*Store extra some info in labels (12 bytes) to speed up drawing of very long texts*/ +# define LV_LABEL_LONG_TXT_HINT 0 +#endif + +/*LED (dependencies: -)*/ +#define LV_USE_LED 1 + +/*Line (dependencies: -*/ +#define LV_USE_LINE 1 + +/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/ +#define LV_USE_LIST 1 +#if LV_USE_LIST != 0 +/*Default animation time of focusing to a list element [ms] (0: no animation) */ +# define LV_LIST_DEF_ANIM_TIME 100 +#endif + +/*Line meter (dependencies: *;)*/ +#define LV_USE_LMETER 1 + +/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/ +#define LV_USE_MBOX 1 + +/*Page (dependencies: lv_cont)*/ +#define LV_USE_PAGE 1 +#if LV_USE_PAGE != 0 +/*Focus default animation time [ms] (0: no animation)*/ +# define LV_PAGE_DEF_ANIM_TIME 400 +#endif + +/*Preload (dependencies: lv_arc, lv_anim)*/ +#define LV_USE_PRELOAD 1 +#if LV_USE_PRELOAD != 0 +# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ +# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ +# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC +#endif + +/*Roller (dependencies: lv_ddlist)*/ +#define LV_USE_ROLLER 1 +#if LV_USE_ROLLER != 0 +/*Focus animation time [ms] (0: no animation)*/ +# define LV_ROLLER_DEF_ANIM_TIME 200 + +/*Number of extra "pages" when the roller is infinite*/ +# define LV_ROLLER_INF_PAGES 7 +#endif + +/*Slider (dependencies: lv_bar)*/ +#define LV_USE_SLIDER 1 + +/*Spinbox (dependencies: lv_ta)*/ +#define LV_USE_SPINBOX 1 + +/*Switch (dependencies: lv_slider)*/ +#define LV_USE_SW 1 + +/*Text area (dependencies: lv_label, lv_page)*/ +#define LV_USE_TA 1 +#if LV_USE_TA != 0 +# define LV_TA_DEF_CURSOR_BLINK_TIME 400 /*ms*/ +# define LV_TA_DEF_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +/*Table (dependencies: lv_label)*/ +#define LV_USE_TABLE 1 +#if LV_USE_TABLE +# define LV_TABLE_COL_MAX 12 +#endif + +/*Tab (dependencies: lv_page, lv_btnm)*/ +#define LV_USE_TABVIEW 1 +# if LV_USE_TABVIEW != 0 +/*Time of slide animation [ms] (0: no animation)*/ +# define LV_TABVIEW_DEF_ANIM_TIME 300 +#endif + +/*Tileview (dependencies: lv_page) */ +#define LV_USE_TILEVIEW 1 +#if LV_USE_TILEVIEW +/*Time of slide animation [ms] (0: no animation)*/ +# define LV_TILEVIEW_DEF_ANIM_TIME 300 +#endif + +/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/ +#define LV_USE_WIN 1 + +/*================== + * Non-user section + *==================*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/ +# define _CRT_SECURE_NO_WARNINGS +#endif + +/*--END OF LV_CONF_H--*/ + +/*Be sure every define has a default value*/ +#include "lvgl/src/lv_conf_checker.h" + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/samples/gui/lv_config/lv_drv_conf.h b/wamr/samples/gui/lv_config/lv_drv_conf.h new file mode 100644 index 0000000..d216a3e --- /dev/null +++ b/wamr/samples/gui/lv_config/lv_drv_conf.h @@ -0,0 +1,310 @@ +/** + * @file lv_drv_conf.h + * + */ + +/* + * COPY THIS FILE AS lv_drv_conf.h + */ + +#if 1 /*Set it to "1" to enable the content*/ + +#ifndef LV_DRV_CONF_H +#define LV_DRV_CONF_H + +#include "lv_conf.h" + +/********************* + * DELAY INTERFACE + *********************/ +#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ +#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ + +/********************* + * DISPLAY INTERFACE + *********************/ + +/*------------ + * Common + *------------*/ +#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ +#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ +#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ + +/*------------------ + * Parallel port + *-----------------*/ +#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ +#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ +#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ +#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ +#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ + +/*************************** + * INPUT DEVICE INTERFACE + ***************************/ + +/*---------- + * Common + *----------*/ +#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ +#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ +#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ + +/*--------- + * I2C + *---------*/ +#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ +#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ +#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ +#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ +#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ + + +/********************* + * DISPLAY DRIVERS + *********************/ + +/*------------------- + * Monitor of PC + *-------------------*/ +#ifndef USE_MONITOR +# define USE_MONITOR 1 +#endif + +#if USE_MONITOR +# define MONITOR_HOR_RES LV_HOR_RES_MAX +# define MONITOR_VER_RES LV_VER_RES_MAX + +/* Scale window by this factor (useful when simulating small screens) */ +# define MONITOR_ZOOM 1 + +/* Used to test true double buffering with only address changing. + * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ +# define MONITOR_DOUBLE_BUFFERED 0 + +/*Eclipse: Visual Studio: */ +# define MONITOR_SDL_INCLUDE_PATH + +/*Different rendering might be used if running in a Virtual machine*/ +# define MONITOR_VIRTUAL_MACHINE 0 + +/*Open two windows to test multi display support*/ +# define MONITOR_DUAL 0 +#endif + +/*----------------------------------- + * Native Windows (including mouse) + *----------------------------------*/ +#ifndef USE_WINDOWS +# define USE_WINDOWS 0 +#endif + +#define USE_WINDOWS 0 +#if USE_WINDOWS +# define WINDOW_HOR_RES 480 +# define WINDOW_VER_RES 320 +#endif + +/*---------------- + * SSD1963 + *--------------*/ +#ifndef USE_SSD1963 +# define USE_SSD1963 0 +#endif + +#if USE_SSD1963 +# define SSD1963_HOR_RES LV_HOR_RES +# define SSD1963_VER_RES LV_VER_RES +# define SSD1963_HT 531 +# define SSD1963_HPS 43 +# define SSD1963_LPS 8 +# define SSD1963_HPW 10 +# define SSD1963_VT 288 +# define SSD1963_VPS 12 +# define SSD1963_FPS 4 +# define SSD1963_VPW 10 +# define SSD1963_HS_NEG 0 /*Negative hsync*/ +# define SSD1963_VS_NEG 0 /*Negative vsync*/ +# define SSD1963_ORI 0 /*0, 90, 180, 270*/ +# define SSD1963_COLOR_DEPTH 16 +#endif + +/*---------------- + * R61581 + *--------------*/ +#ifndef USE_R61581 +# define USE_R61581 0 +#endif + +#if USE_R61581 +# define R61581_HOR_RES LV_HOR_RES +# define R61581_VER_RES LV_VER_RES +# define R61581_HSPL 0 /*HSYNC signal polarity*/ +# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ +# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ +# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ +# define R61581_VSPL 0 /*VSYNC signal polarity*/ +# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ +# define R61581_VFP 8 /*Vertical Front poarch*/ +# define R61581_VBP 8 /*Vertical Back poarch */ +# define R61581_DPL 0 /*DCLK signal polarity*/ +# define R61581_EPL 1 /*ENABLE signal polarity*/ +# define R61581_ORI 0 /*0, 180*/ +# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ +#endif + +/*------------------------------ + * ST7565 (Monochrome, low res.) + *-----------------------------*/ +#ifndef USE_ST7565 +# define USE_ST7565 0 +#endif + +#if USE_ST7565 +/*No settings*/ +#endif /*USE_ST7565*/ + +/*----------------------------------------- + * Linux frame buffer device (/dev/fbx) + *-----------------------------------------*/ +#ifndef USE_FBDEV +# define USE_FBDEV 1 +#endif + +#if USE_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/********************* + * INPUT DEVICES + *********************/ + +/*-------------- + * XPT2046 + *--------------*/ +#ifndef USE_XPT2046 +# define USE_XPT2046 0 +#endif + +#if USE_XPT2046 +# define XPT2046_HOR_RES 480 +# define XPT2046_VER_RES 320 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_INV 0 +#endif + +/*----------------- + * FT5406EE8 + *-----------------*/ +#ifndef USE_FT5406EE8 +# define USE_FT5406EE8 0 +#endif + +#if USE_FT5406EE8 +# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ +#endif + +/*--------------- + * AD TOUCH + *--------------*/ +#ifndef USE_AD_TOUCH +# define USE_AD_TOUCH 0 +#endif + +#if USE_AD_TOUCH +/*No settings*/ +#endif + + +/*--------------------------------------- + * Mouse or touchpad on PC (using SDL) + *-------------------------------------*/ +#ifndef USE_MOUSE +# define USE_MOUSE 1 +#endif + +#if USE_MOUSE +/*No settings*/ +#endif + +/*------------------------------------------- + * Mousewheel as encoder on PC (using SDL) + *------------------------------------------*/ +#ifndef USE_MOUSEWHEEL +# define USE_MOUSEWHEEL 1 +#endif + +#if USE_MOUSEWHEEL +/*No settings*/ +#endif + +/*------------------------------------------------- + * Touchscreen as libinput interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_LIBINPUT +# define USE_LIBINPUT 0 +#endif + +#if USE_LIBINPUT +# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +#endif /*USE_LIBINPUT*/ + +/*------------------------------------------------- + * Mouse or touchpad as evdev interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_EVDEV +# define USE_EVDEV 0 +#endif + +#if USE_EVDEV +# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ + +# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ +# if EVDEV_SCALE +# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ +# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ +# endif /*EVDEV_SCALE*/ + +# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ +# if EVDEV_CALIBRATE +# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ +# define EVDEV_HOR_MAX 200 +# define EVDEV_VER_MIN 200 +# define EVDEV_VER_MAX 3800 +# endif /*EVDEV_SCALE*/ +#endif /*USE_EVDEV*/ + +/*------------------------------- + * Keyboard of a PC (using SDL) + *------------------------------*/ +#ifndef USE_KEYBOARD +# define USE_KEYBOARD 1 +#endif + +#if USE_KEYBOARD +/*No settings*/ +#endif + +#endif /*LV_DRV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/samples/gui/lv_config/system_header.h b/wamr/samples/gui/lv_config/system_header.h new file mode 100644 index 0000000..41d9238 --- /dev/null +++ b/wamr/samples/gui/lv_config/system_header.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +int time_get_ms(); diff --git a/wamr/samples/gui/wamr_config_gui.cmake b/wamr/samples/gui/wamr_config_gui.cmake new file mode 100644 index 0000000..3b33d33 --- /dev/null +++ b/wamr/samples/gui/wamr_config_gui.cmake @@ -0,0 +1,9 @@ +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_ALL) diff --git a/wamr/samples/gui/wasm-apps/build_apps.sh b/wamr/samples/gui/wasm-apps/build_apps.sh new file mode 100755 index 0000000..32b7ed4 --- /dev/null +++ b/wamr/samples/gui/wasm-apps/build_apps.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +APPS_ROOT=$(cd "$(dirname "$0")/" && pwd) +cd ${APPS_ROOT} + +echo "OUT_DIR: ${OUT_DIR}" + +if [ -z ${OUT_DIR} ]; then + OUT_DIR=${APPS_ROOT}/out + echo "set the wasm app folder: ${OUT_DIR}" + + if [ -d ${OUT_DIR} ]; then + rm -rf ${OUT_DIR} + echo "removed the present output folder: ${OUT_DIR}" + fi + mkdir ${OUT_DIR} + +fi + +if [ -z ${WAMR_DIR} ]; then + WAMR_DIR=${APPS_ROOT}/../../.. +fi + + +cd ${APPS_ROOT}/increase + +rm -rf build +mkdir build && cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=${WAMR_DIR}/wamr-sdk/out/gui/app-sdk/wamr_toolchain.cmake +make +[ $? -eq 0 ] || exit $? +mv ui_increase.wasm ${OUT_DIR}/ + +# $makewrap +# mv ui_app.wasm ${OUT_DIR}/ + +cd ${APPS_ROOT}/decrease +make +[ $? -eq 0 ] || exit $? +mv ui_decrease.wasm ${OUT_DIR}/ + +echo "WASM files generated in folder ${OUT_DIR}" + +echo "##################### build WASM APPs finished #####################" diff --git a/wamr/samples/gui/wasm-apps/decrease/Makefile b/wamr/samples/gui/wasm-apps/decrease/Makefile new file mode 100644 index 0000000..6e18fc7 --- /dev/null +++ b/wamr/samples/gui/wasm-apps/decrease/Makefile @@ -0,0 +1,29 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CC = /opt/wasi-sdk/bin/clang +APP_DIR = ${shell pwd} +IWASM_DIR = $(APP_DIR)/../../../../core/iwasm +SDK_DIR = $(APP_DIR)/../../../../wamr-sdk/out/gui/app-sdk +APP_FRAMEWORK_DIR = $(APP_DIR)/../../../../wamr-sdk/out/gui/app-sdk/wamr-app-framework +DEPS_DIR = $(APP_DIR)/../../../../core/deps + +CFLAGS += -O3 \ + -Wno-int-conversion \ + -I$(APP_DIR)/src \ + -I$(APP_FRAMEWORK_DIR)/include \ + -I${DEPS_DIR} + +SRCS += $(APP_DIR)/src/main.c + +all: + @$(CC) $(CFLAGS) $(SRCS) \ + --target=wasm32 -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ + --sysroot=$(SDK_DIR)/libc-builtin-sysroot \ + -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ + -Wl,--allow-undefined-file=$(SDK_DIR)/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=on_init -Wl,--export=on_timer_callback \ + -Wl,--export=on_widget_event \ + -Wl,--export=__heap_base,--export=__data_end \ + -o ui_decrease.wasm diff --git a/wamr/samples/gui/wasm-apps/decrease/src/main.c b/wamr/samples/gui/wasm-apps/decrease/src/main.c new file mode 100644 index 0000000..03eb556 --- /dev/null +++ b/wamr/samples/gui/wasm-apps/decrease/src/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include "wasm_app.h" +#include "wa-inc/lvgl/lvgl.h" +#include "wa-inc/timer_wasm_app.h" + +extern char g_widget_text[]; + +static void btn_event_cb(lv_obj_t *btn, lv_event_t event); + +uint32_t count = 0; +char count_str[11] = { 0 }; +lv_obj_t *hello_world_label; +lv_obj_t *count_label; +lv_obj_t *btn1; +lv_obj_t *label_count1; +int label_count1_value = 100; +char label_count1_str[11] = { 0 }; + +void timer1_update(user_timer_t timer1) +{ + if ((count % 100) == 0) { + snprintf(count_str, sizeof(count_str), "%d", count / 100); + lv_label_set_text(count_label, count_str); + } + ++count; +} + +void on_init() +{ + char *text; + + hello_world_label = lv_label_create(NULL, NULL); + lv_label_set_text(hello_world_label, "Hello world!"); + text = lv_label_get_text(hello_world_label); + printf("Label text %lu %s \n", strlen(text), text); + lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + + count_label = lv_label_create(NULL, NULL); + lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); + + btn1 = lv_btn_create(NULL, NULL); /*Create a button on the currently loaded screen*/ + lv_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ + lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align below the label*/ + + /*Create a label on the button*/ + lv_obj_t *btn_label = lv_label_create(btn1, NULL); + lv_label_set_text(btn_label, "Click --"); + + label_count1 = lv_label_create(NULL, NULL); + lv_label_set_text(label_count1, "100"); + lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + /* set up a timer */ + user_timer_t timer; + timer = api_timer_create(10, true, false, timer1_update); + if (timer) + api_timer_restart(timer, 10); + else + printf("Fail to create timer.\n"); +} + +static void btn_event_cb(lv_obj_t *btn, lv_event_t event) +{ + if(event == LV_EVENT_RELEASED) { + label_count1_value--; + snprintf(label_count1_str, sizeof(label_count1_str), + "%d", label_count1_value); + lv_label_set_text(label_count1, label_count1_str); + if (label_count1_value == 0) + label_count1_value = 100; + } +} diff --git a/wamr/samples/gui/wasm-apps/increase/CMakeLists.txt b/wamr/samples/gui/wasm-apps/increase/CMakeLists.txt new file mode 100644 index 0000000..ce55fb1 --- /dev/null +++ b/wamr/samples/gui/wasm-apps/increase/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) + +project(wgl) + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../) + +include_directories( + ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/app-sdk/wamr-app-framework/include + ${WAMR_ROOT_DIR}/core/deps +) + +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},-L${WAMR_ROOT_DIR}/wamr-sdk/out/gui/app-sdk/wamr-app-framework/lib") +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS},--export=on_init,--export=on_timer_callback,--export=on_widget_event,--export=__heap_base,--export=__data_end") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-unused-command-line-argument") + +add_executable(ui_increase.wasm + ${CMAKE_CURRENT_LIST_DIR}/src/main.c +) + +target_link_libraries(ui_increase.wasm app_framework) diff --git a/wamr/samples/gui/wasm-apps/increase/Makefile b/wamr/samples/gui/wasm-apps/increase/Makefile new file mode 100644 index 0000000..3e49913 --- /dev/null +++ b/wamr/samples/gui/wasm-apps/increase/Makefile @@ -0,0 +1,34 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CC = /opt/wasi-sdk/bin/clang +APP_DIR = ${shell pwd} +IWASM_DIR = ../../../../core/iwasm +APP_FRAMEWORK_DIR = ../../../../core/app-framework +DEPS_DIR = ../../../../core/deps + +CFLAGS += -O3 \ + -Wno-int-conversion \ + -I$(APP_DIR)/src \ + -I$(APP_FRAMEWORK_DIR)/base/app \ + -I$(APP_FRAMEWORK_DIR)/app-native-shared \ + -I$(APP_FRAMEWORK_DIR)/sensor/app \ + -I$(APP_FRAMEWORK_DIR)/wgl/app \ + -I$(APP_FRAMEWORK_DIR)/connection/app \ + -I${DEPS_DIR} + +SRCS += $(APP_DIR)/src/main.c + +# For app size consideration, not all but necessary app libs are included +SRCS += $(APP_FRAMEWORK_DIR)/base/app/timer.c +SRCS += $(APP_FRAMEWORK_DIR)/wgl/app/src/*.c + +all: + @$(CC) $(CFLAGS) $(SRCS) \ + --target=wasm32 -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ + -Wl,--allow-undefined \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=on_init -Wl,--export=on_timer_callback \ + -Wl,--export=on_widget_event \ + -Wl,--export=__heap_base,--export=__data_end \ + -o ui_app.wasm diff --git a/wamr/samples/gui/wasm-apps/increase/src/main.c b/wamr/samples/gui/wasm-apps/increase/src/main.c new file mode 100644 index 0000000..1b1c05d --- /dev/null +++ b/wamr/samples/gui/wasm-apps/increase/src/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include "wasm_app.h" +#include "wa-inc/lvgl/lvgl.h" +#include "wa-inc/timer_wasm_app.h" + +extern char g_widget_text[]; + +static void btn_event_cb(lv_obj_t *btn, lv_event_t event); + +uint32_t count = 0; +char count_str[11] = { 0 }; +lv_obj_t *hello_world_label; +lv_obj_t *count_label; +lv_obj_t *btn1; +lv_obj_t *label_count1; +int label_count1_value = 1; +char label_count1_str[11] = { 0 }; + +void timer1_update(user_timer_t timer1) +{ + if ((count % 100) == 0) { + snprintf(count_str, sizeof(count_str), "%d", count / 100); + lv_label_set_text(count_label, count_str); + } + ++count; +} + +void on_init() +{ + char *text; + + hello_world_label = lv_label_create(NULL, NULL); + lv_label_set_text(hello_world_label, "Hello world!"); + text = lv_label_get_text(hello_world_label); + printf("Label text %lu %s \n", strlen(text), text); + lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + + count_label = lv_label_create(NULL, NULL); + lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); + + btn1 = lv_btn_create(NULL, NULL); /*Create a button on the currently loaded screen*/ + lv_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/ + lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 0); /*Align below the label*/ + + /*Create a label on the button*/ + lv_obj_t *btn_label = lv_label_create(btn1, NULL); + lv_label_set_text(btn_label, "Click ++"); + + label_count1 = lv_label_create(NULL, NULL); + lv_label_set_text(label_count1, "1"); + lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + /* set up a timer */ + user_timer_t timer; + timer = api_timer_create(10, true, false, timer1_update); + if (timer) + api_timer_restart(timer, 10); + else + printf("Fail to create timer.\n"); +} + +static void btn_event_cb(lv_obj_t *btn, lv_event_t event) +{ + if(event == LV_EVENT_RELEASED) { + label_count1_value++; + snprintf(label_count1_str, sizeof(label_count1_str), + "%d", label_count1_value); + lv_label_set_text(label_count1, label_count1_str); + if (label_count1_value == 100) + label_count1_value = 0; + } + +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt b/wamr/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt new file mode 100644 index 0000000..2214325 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/linux-build/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (wasm_runtime_wgl) + +set (WAMR_BUILD_PLATFORM "linux") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +################ wamr runtime settings ################ + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../..) +set (DEPS_DIR ${WAMR_ROOT_DIR}/core/deps) + + +add_definitions(-DLV_CONF_INCLUDE_SIMPLE) + +## use library and headers in the SDK +link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/lib) +include_directories( + ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/include + ${WAMR_ROOT_DIR}/wamr-sdk/out/gui/runtime-sdk/include/bi-inc/deps + ${WAMR_ROOT_DIR}/core/shared/utils + ${WAMR_ROOT_DIR}/core/shared/platform/${WAMR_BUILD_PLATFORM} +) + +################ application related ################ + +set (LV_DRIVERS_DIR ${WAMR_ROOT_DIR}/core/deps/lv_drivers) +file (GLOB_RECURSE LV_DRIVERS_SOURCES "${LV_DRIVERS_DIR}/*.c") + +set (PROJECT_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/../src/platform/${WAMR_BUILD_PLATFORM}) +include_directories( + ${PROJECT_SRC_DIR} + ${DEPS_DIR} + ${DEPS_DIR}/lvgl + ${DEPS_DIR}/lvgl/src +) + +set (SOURCES + ${PROJECT_SRC_DIR}/main.c + ${PROJECT_SRC_DIR}/iwasm_main.c + ${LV_DRIVERS_SOURCES} + ) + +add_executable (wasm_runtime_wgl ${SOURCES}) + +target_link_libraries (wasm_runtime_wgl vmlib -lm -ldl -lpthread -lSDL2) +#target_link_libraries(wasm_runtime_wgl PRIVATE SDL2 ) + diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c new file mode 100644 index 0000000..856a1ca --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/iwasm_main.c @@ -0,0 +1,517 @@ + +#ifndef CONNECTION_UART +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runtime_lib.h" +#include "runtime_timer.h" +#include "native_interface.h" +#include "app_manager_export.h" +#include "bh_platform.h" +#include "runtime_sensor.h" +#include "bi-inc/attr_container.h" +#include "module_wasm_app.h" +#include "wasm_export.h" +#include "wgl.h" + +#include "lv_drivers/display/monitor.h" +#include "lv_drivers/indev/mouse.h" + +#define MAX 2048 + +#ifndef CONNECTION_UART +#define SA struct sockaddr +static char *host_address = "127.0.0.1"; +static int port = 8888; +#else +static char *uart_device = "/dev/ttyS2"; +static int baudrate = B115200; +#endif + +extern void init_sensor_framework(); +extern void exit_sensor_framework(); +extern void exit_connection_framework(); +extern int aee_host_msg_callback(void *msg, uint16_t msg_len); +extern bool init_connection_framework(); + +#ifndef CONNECTION_UART +int listenfd = -1; +int sockfd = -1; +static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; +#else +int uartfd = -1; +#endif + +#ifndef CONNECTION_UART +static bool server_mode = false; + +// Function designed for chat between client and server. +void* func(void* arg) +{ + char buff[MAX]; + int n; + struct sockaddr_in servaddr; + + while (1) { + if (sockfd != -1) + close(sockfd); + // socket create and verification + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + printf("socket creation failed...\n"); + return NULL; + } else + printf("Socket successfully created..\n"); + bzero(&servaddr, sizeof(servaddr)); + // assign IP, PORT + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(host_address); + servaddr.sin_port = htons(port); + + // connect the client socket to server socket + if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { + printf("connection with the server failed...\n"); + sleep(10); + continue; + } else { + printf("connected to the server..\n"); + } + + // infinite loop for chat + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + // print buffer which contains the client contents + //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); + + // socket disconnected + if (n <= 0) + break; + + aee_host_msg_callback(buff, n); + } + } + + // After chatting close the socket + close(sockfd); +} + +static bool host_init() +{ + return true; +} + +int host_send(void * ctx, const char *buf, int size) +{ + int ret; + + if (pthread_mutex_trylock(&sock_lock) == 0) { + if (sockfd == -1) { + pthread_mutex_unlock(&sock_lock); + return 0; + } + + ret = write(sockfd, buf, size); + + pthread_mutex_unlock(&sock_lock); + return ret; + } + + return -1; +} + +void host_destroy() +{ + if (server_mode) + close(listenfd); + + pthread_mutex_lock(&sock_lock); + close(sockfd); + pthread_mutex_unlock(&sock_lock); +} + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy + }; + +void* func_server_mode(void* arg) +{ + int clilent; + struct sockaddr_in serv_addr, cli_addr; + int n; + char buff[MAX]; + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, 0); + + /* First call to socket() function */ + listenfd = socket(AF_INET, SOCK_STREAM, 0); + + if (listenfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + /* Initialize socket structure */ + bzero((char *) &serv_addr, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + /* Now bind the host address using bind() call.*/ + if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + exit(1); + } + + listen(listenfd, 5); + clilent = sizeof(cli_addr); + + while (1) { + pthread_mutex_lock(&sock_lock); + + sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); + + pthread_mutex_unlock(&sock_lock); + + if (sockfd < 0) { + perror("ERROR on accept"); + exit(1); + } + + printf("connection established!\n"); + + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + + // socket disconnected + if (n <= 0) { + pthread_mutex_lock(&sock_lock); + close(sockfd); + sockfd = -1; + pthread_mutex_unlock(&sock_lock); + + sleep(2); + break; + } + + aee_host_msg_callback(buff, n); + } + } +} + +#else +static int parse_baudrate(int baud) +{ + switch (baud) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} +static bool uart_init(const char *device, int baudrate, int *fd) +{ + int uart_fd; + struct termios uart_term; + + uart_fd = open(device, O_RDWR | O_NOCTTY); + + if (uart_fd <= 0) + return false; + + memset(&uart_term, 0, sizeof(uart_term)); + uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; + uart_term.c_iflag = IGNPAR; + uart_term.c_oflag = 0; + + /* set noncanonical mode */ + uart_term.c_lflag = 0; + uart_term.c_cc[VTIME] = 30; + uart_term.c_cc[VMIN] = 1; + tcflush(uart_fd, TCIFLUSH); + + if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { + close(uart_fd); + return false; + } + + *fd = uart_fd; + + return true; +} + +static void *func_uart_mode(void *arg) +{ + int n; + char buff[MAX]; + + if (!uart_init(uart_device, baudrate, &uartfd)) { + printf("open uart fail! %s\n", uart_device); + return NULL; + } + + for (;;) { + bzero(buff, MAX); + + n = read(uartfd, buff, sizeof(buff)); + + if (n <= 0) { + close(uartfd); + uartfd = -1; + break; + } + + aee_host_msg_callback(buff, n); + } + + return NULL; +} + +static int uart_send(void * ctx, const char *buf, int size) +{ + int ret; + + ret = write(uartfd, buf, size); + + return ret; +} + +static void uart_destroy() +{ + close(uartfd); +} + +static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; + +#endif + +static char global_heap_buf[270 * 1024] = { 0 }; + +static void showUsage() +{ +#ifndef CONNECTION_UART + printf("Usage:\n"); + printf("\nWork as TCP server mode:\n"); + printf("\tvgl_wasm_runtime -s|--server_mode -p|--port \n"); + printf("where\n"); + printf("\t represents the port that would be listened on and the default is 8888\n"); + printf("\nWork as TCP client mode:\n"); + printf("\tvgl_wasm_runtime -a|--host_address -p|--port \n"); + printf("where\n"); + printf("\t represents the network address of host and the default is 127.0.0.1\n"); + printf("\t represents the listen port of host and the default is 8888\n"); +#else + printf("Usage:\n"); + printf("\tvgl_wasm_runtime -u -b \n\n"); + printf("where\n"); + printf("\t represents the UART device name and the default is /dev/ttyS2\n"); + printf("\t represents the UART device baudrate and the default is 115200\n"); +#endif +} + +static bool parse_args(int argc, char *argv[]) +{ + int c; + + while (1) { + int optIndex = 0; + static struct option longOpts[] = { +#ifndef CONNECTION_UART + { "server_mode", no_argument, NULL, 's' }, + { "host_address", required_argument, NULL, 'a' }, + { "port", required_argument, NULL, 'p' }, +#else + { "uart", required_argument, NULL, 'u' }, + { "baudrate", required_argument, NULL, 'b' }, +#endif + { "help", required_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "sa:p:u:b:h", longOpts, &optIndex); + if (c == -1) + break; + + switch (c) { +#ifndef CONNECTION_UART + case 's': + server_mode = true; + break; + case 'a': + host_address = optarg; + printf("host address: %s\n", host_address); + break; + case 'p': + port = atoi(optarg); + printf("port: %d\n", port); + break; +#else + case 'u': + uart_device = optarg; + printf("uart device: %s\n", uart_device); + break; + case 'b': + baudrate = parse_baudrate(atoi(optarg)); + printf("uart baudrate: %s\n", optarg); + break; +#endif + case 'h': + showUsage(); + return false; + default: + showUsage(); + return false; + } + } + + return true; +} + +/** + * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library + */ +static void hal_init(void) +{ + /* Use the 'monitor' driver which creates window on PC's monitor to simulate a display*/ + monitor_init(); + + /*Create a display buffer*/ + static lv_disp_buf_t disp_buf1; + static lv_color_t buf1_1[480*10]; + lv_disp_buf_init(&disp_buf1, buf1_1, NULL, 480*10); + + /*Create a display*/ + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); /*Basic initialization*/ + disp_drv.buffer = &disp_buf1; + disp_drv.flush_cb = monitor_flush; + // disp_drv.hor_res = 200; + // disp_drv.ver_res = 100; + lv_disp_drv_register(&disp_drv); + + /* Add the mouse as input device + * Use the 'mouse' driver which reads the PC's mouse*/ + mouse_init(); + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); /*Basic initialization*/ + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = mouse_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ + lv_indev_drv_register(&indev_drv); +} + +// Driver function +int iwasm_main(int argc, char *argv[]) +{ + RuntimeInitArgs init_args; + korp_tid tid; + + if (!parse_args(argc, argv)) + return -1; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + if (!init_connection_framework()) { + goto fail1; + } + + wgl_init(); + + hal_init(); + + init_sensor_framework(); + + // timer manager + init_wasm_timer(); + +#ifndef CONNECTION_UART + if (server_mode) + os_thread_create(&tid, func_server_mode, NULL, + BH_APPLET_PRESERVED_STACK_SIZE); + else + os_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#else + os_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#endif + + app_manager_startup(&interface); + + exit_wasm_timer(); + exit_sensor_framework(); + wgl_exit(); + exit_connection_framework(); + +fail1: + wasm_runtime_destroy(); + return -1; +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h new file mode 100644 index 0000000..d216a3e --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/lv_drv_conf.h @@ -0,0 +1,310 @@ +/** + * @file lv_drv_conf.h + * + */ + +/* + * COPY THIS FILE AS lv_drv_conf.h + */ + +#if 1 /*Set it to "1" to enable the content*/ + +#ifndef LV_DRV_CONF_H +#define LV_DRV_CONF_H + +#include "lv_conf.h" + +/********************* + * DELAY INTERFACE + *********************/ +#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ +#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ + +/********************* + * DISPLAY INTERFACE + *********************/ + +/*------------ + * Common + *------------*/ +#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ +#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ +#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ + +/*------------------ + * Parallel port + *-----------------*/ +#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ +#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ +#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ +#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ +#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ + +/*************************** + * INPUT DEVICE INTERFACE + ***************************/ + +/*---------- + * Common + *----------*/ +#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ +#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ +#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ + +/*--------- + * I2C + *---------*/ +#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ +#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ +#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ +#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ +#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ + + +/********************* + * DISPLAY DRIVERS + *********************/ + +/*------------------- + * Monitor of PC + *-------------------*/ +#ifndef USE_MONITOR +# define USE_MONITOR 1 +#endif + +#if USE_MONITOR +# define MONITOR_HOR_RES LV_HOR_RES_MAX +# define MONITOR_VER_RES LV_VER_RES_MAX + +/* Scale window by this factor (useful when simulating small screens) */ +# define MONITOR_ZOOM 1 + +/* Used to test true double buffering with only address changing. + * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ +# define MONITOR_DOUBLE_BUFFERED 0 + +/*Eclipse: Visual Studio: */ +# define MONITOR_SDL_INCLUDE_PATH + +/*Different rendering might be used if running in a Virtual machine*/ +# define MONITOR_VIRTUAL_MACHINE 0 + +/*Open two windows to test multi display support*/ +# define MONITOR_DUAL 0 +#endif + +/*----------------------------------- + * Native Windows (including mouse) + *----------------------------------*/ +#ifndef USE_WINDOWS +# define USE_WINDOWS 0 +#endif + +#define USE_WINDOWS 0 +#if USE_WINDOWS +# define WINDOW_HOR_RES 480 +# define WINDOW_VER_RES 320 +#endif + +/*---------------- + * SSD1963 + *--------------*/ +#ifndef USE_SSD1963 +# define USE_SSD1963 0 +#endif + +#if USE_SSD1963 +# define SSD1963_HOR_RES LV_HOR_RES +# define SSD1963_VER_RES LV_VER_RES +# define SSD1963_HT 531 +# define SSD1963_HPS 43 +# define SSD1963_LPS 8 +# define SSD1963_HPW 10 +# define SSD1963_VT 288 +# define SSD1963_VPS 12 +# define SSD1963_FPS 4 +# define SSD1963_VPW 10 +# define SSD1963_HS_NEG 0 /*Negative hsync*/ +# define SSD1963_VS_NEG 0 /*Negative vsync*/ +# define SSD1963_ORI 0 /*0, 90, 180, 270*/ +# define SSD1963_COLOR_DEPTH 16 +#endif + +/*---------------- + * R61581 + *--------------*/ +#ifndef USE_R61581 +# define USE_R61581 0 +#endif + +#if USE_R61581 +# define R61581_HOR_RES LV_HOR_RES +# define R61581_VER_RES LV_VER_RES +# define R61581_HSPL 0 /*HSYNC signal polarity*/ +# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ +# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ +# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ +# define R61581_VSPL 0 /*VSYNC signal polarity*/ +# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ +# define R61581_VFP 8 /*Vertical Front poarch*/ +# define R61581_VBP 8 /*Vertical Back poarch */ +# define R61581_DPL 0 /*DCLK signal polarity*/ +# define R61581_EPL 1 /*ENABLE signal polarity*/ +# define R61581_ORI 0 /*0, 180*/ +# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ +#endif + +/*------------------------------ + * ST7565 (Monochrome, low res.) + *-----------------------------*/ +#ifndef USE_ST7565 +# define USE_ST7565 0 +#endif + +#if USE_ST7565 +/*No settings*/ +#endif /*USE_ST7565*/ + +/*----------------------------------------- + * Linux frame buffer device (/dev/fbx) + *-----------------------------------------*/ +#ifndef USE_FBDEV +# define USE_FBDEV 1 +#endif + +#if USE_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/********************* + * INPUT DEVICES + *********************/ + +/*-------------- + * XPT2046 + *--------------*/ +#ifndef USE_XPT2046 +# define USE_XPT2046 0 +#endif + +#if USE_XPT2046 +# define XPT2046_HOR_RES 480 +# define XPT2046_VER_RES 320 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_INV 0 +#endif + +/*----------------- + * FT5406EE8 + *-----------------*/ +#ifndef USE_FT5406EE8 +# define USE_FT5406EE8 0 +#endif + +#if USE_FT5406EE8 +# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ +#endif + +/*--------------- + * AD TOUCH + *--------------*/ +#ifndef USE_AD_TOUCH +# define USE_AD_TOUCH 0 +#endif + +#if USE_AD_TOUCH +/*No settings*/ +#endif + + +/*--------------------------------------- + * Mouse or touchpad on PC (using SDL) + *-------------------------------------*/ +#ifndef USE_MOUSE +# define USE_MOUSE 1 +#endif + +#if USE_MOUSE +/*No settings*/ +#endif + +/*------------------------------------------- + * Mousewheel as encoder on PC (using SDL) + *------------------------------------------*/ +#ifndef USE_MOUSEWHEEL +# define USE_MOUSEWHEEL 1 +#endif + +#if USE_MOUSEWHEEL +/*No settings*/ +#endif + +/*------------------------------------------------- + * Touchscreen as libinput interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_LIBINPUT +# define USE_LIBINPUT 0 +#endif + +#if USE_LIBINPUT +# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +#endif /*USE_LIBINPUT*/ + +/*------------------------------------------------- + * Mouse or touchpad as evdev interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_EVDEV +# define USE_EVDEV 0 +#endif + +#if USE_EVDEV +# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ + +# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ +# if EVDEV_SCALE +# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ +# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ +# endif /*EVDEV_SCALE*/ + +# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ +# if EVDEV_CALIBRATE +# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ +# define EVDEV_HOR_MAX 200 +# define EVDEV_VER_MIN 200 +# define EVDEV_VER_MAX 3800 +# endif /*EVDEV_SCALE*/ +#endif /*USE_EVDEV*/ + +/*------------------------------- + * Keyboard of a PC (using SDL) + *------------------------------*/ +#ifndef USE_KEYBOARD +# define USE_KEYBOARD 1 +#endif + +#if USE_KEYBOARD +/*No settings*/ +#endif + +#endif /*LV_DRV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c new file mode 100644 index 0000000..607e775 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/linux/main.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include + + +extern int iwasm_main(int argc, char *argv[]); +int main(int argc, char *argv[]) +{ + return iwasm_main(argc,argv); +} + +int time_get_ms() +{ + static struct timeval tv; + gettimeofday(&tv, NULL); + long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; + + return (int) time_in_mill; +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c new file mode 100644 index 0000000..11d3562 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.c @@ -0,0 +1,339 @@ +/** + * @file XPT2046.c +*/ +/********************* + * INCLUDES + *********************/ +#include "XPT2046.h" +#include "board_config.h" +#include "stdio.h" +#include +#include "drivers/spi.h" + +#include "zephyr.h" +#include "kernel.h" + +#if USE_XPT2046 + +#include + +#define abs(x) ((x) < 0 ? -(x) : (x)) + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y); +#if 0 +static void xpt2046_avg(int16_t * x, int16_t * y); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +int16_t avg_buf_x[XPT2046_AVG]; +int16_t avg_buf_y[XPT2046_AVG]; +uint8_t avg_last; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the XPT2046 + */ +struct device *input_dev; + +struct spi_config spi_conf_xpt2046; +struct spi_cs_control xpt2046_cs_ctrl; +struct device *xpt2046_pen_gpio_dev; +static struct gpio_callback gpio_cb; +lv_indev_data_t touch_point; +lv_indev_data_t last_touch_point; + +#define TOUCH_READ_THREAD_STACK_SIZE 4096 +static K_THREAD_STACK_DEFINE(touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE); +static struct k_thread touch_thread_data; +static struct k_sem sem_touch_read; + +K_MUTEX_DEFINE( spi_display_touch_mutex); + +int cnt = 0; +int touch_read_times = 0; +int last_pen_interrupt_time = 0; +void xpt2046_pen_gpio_callback(struct device *port, struct gpio_callback *cb, + u32_t pins) +{ + cnt++; + if ((k_uptime_get_32() - last_pen_interrupt_time) > 500) { + k_sem_give(&sem_touch_read); + touch_read_times++; + last_pen_interrupt_time = k_uptime_get_32(); + } + +} + +void disable_pen_interrupt() +{ + int ret = 0; + ret = gpio_disable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret != 0) { + printf("gpio_pin_configure GPIO_INPUT failed\n"); + } +} +void enable_pen_interrupt() +{ + int ret = 0; + ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret != 0) { + printf("gpio_pin_configure failed\n"); + } +} + +void touch_screen_read_thread() +{ + int i; + bool ret = false; + + for (;;) { + k_sem_take(&sem_touch_read, K_FOREVER); + memset(&last_touch_point, 0, sizeof(lv_indev_data_t)); + memset(&touch_point, 0, sizeof(lv_indev_data_t)); + memset(avg_buf_x, 0, sizeof(avg_buf_x)); + memset(avg_buf_y, 0, sizeof(avg_buf_y)); + k_mutex_lock(&spi_display_touch_mutex, K_FOREVER); + disable_pen_interrupt(); + for (i = 0; i < 100; i++) { + ret = xpt2046_read(&touch_point); + if (ret) { + if ((abs(last_touch_point.point.x - touch_point.point.x) < 4) + && (abs(last_touch_point.point.y - touch_point.point.y) + < 4)) { + break; + } + last_touch_point = touch_point; + + } + } + enable_pen_interrupt(); + k_mutex_unlock(&spi_display_touch_mutex); + } +} + +void xpt2046_init(void) +{ + int ret; + input_dev = device_get_binding(XPT2046_SPI_DEVICE_NAME); + + if (input_dev == NULL) { + printf("device not found. Aborting test."); + return; + } + memset((void *) &touch_point, 0, sizeof(lv_indev_data_t)); + + spi_conf_xpt2046.frequency = XPT2046_SPI_MAX_FREQUENCY; + spi_conf_xpt2046.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); + spi_conf_xpt2046.slave = 0; + spi_conf_xpt2046.cs = NULL; +#ifdef XPT2046_CS_GPIO_CONTROLLER + xpt2046_cs_ctrl.gpio_dev = device_get_binding(XPT2046_CS_GPIO_CONTROLLER); + if (xpt2046_cs_ctrl.gpio_dev == NULL) { + printk("Cannot find %s!\n", XPT2046_CS_GPIO_CONTROLLER); + return; + } + gpio_pin_configure(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, + GPIO_OUTPUT); + gpio_pin_set(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, 1); + xpt2046_cs_ctrl.gpio_pin = XPT2046_CS_GPIO_PIN; + xpt2046_cs_ctrl.delay = 0; + spi_conf_xpt2046.cs = &xpt2046_cs_ctrl; + +#endif + +#ifdef XPT2046_PEN_GPIO_CONTROLLER + + xpt2046_pen_gpio_dev = device_get_binding(XPT2046_PEN_GPIO_CONTROLLER); + if (!xpt2046_pen_gpio_dev) { + printk("Cannot find %s!\n", XPT2046_PEN_GPIO_CONTROLLER); + return; + } + /* Setup GPIO input */ + ret = gpio_pin_configure(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN, + (GPIO_INPUT | GPIO_INT_ENABLE | GPIO_INT_EDGE + | GPIO_INT_LOW_0 | GPIO_INT_DEBOUNCE) + ); + if (ret) { + printk("Error configuring pin %d!\n", XPT2046_PEN_GPIO_PIN); + } + + gpio_init_callback(&gpio_cb, xpt2046_pen_gpio_callback, + BIT(XPT2046_PEN_GPIO_PIN)); + + ret = gpio_add_callback(xpt2046_pen_gpio_dev, &gpio_cb); + if (ret) { + printk("gpio_add_callback error\n"); + } + ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret) { + printk("gpio_enable_callback error\n"); + } +#endif + + k_sem_init(&sem_touch_read, 0, 1); + + k_thread_create(&touch_thread_data, touch_read_thread_stack, + TOUCH_READ_THREAD_STACK_SIZE, touch_screen_read_thread, + NULL, NULL, NULL, 5, + 0, K_NO_WAIT); + printf("xpt2046_init ok \n"); +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no ore data to be read + */ +bool xpt2046_read(lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + bool valid = true; + int s32_ret = 0; + + int16_t x = 0; + int16_t y = 0; + + char tx1[16] = { 0 }; + char rx1[16] = { 0 }; + + struct spi_buf tx_buf = { .buf = &tx1, .len = 3 }; + struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; + struct spi_buf rx_buf = { .buf = &rx1, .len = 3 }; + struct spi_buf_set rx_bufs = { .buffers = &rx_buf, .count = 1 }; + + tx1[0] = CMD_X_READ; + s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); + if (s32_ret != 0) { + printf("spi_transceive return failed:%d\n", s32_ret); + } + x = rx1[1] << 8; + x += rx1[2]; + + tx1[0] = CMD_Y_READ; + s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); + if (s32_ret != 0) { + printf("spi_transceive return failed:%d\n", s32_ret); + } + y = rx1[1] << 8; + y += rx1[2]; + x = x >> 3; + y = y >> 3; + + xpt2046_corr(&x, &y); + if (y <= 0 || (x > 320)) { + valid = false; + } + + last_x = x; + last_y = y; + + data->point.x = x; + data->point.y = y; + data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + + return valid; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y) +{ +#if XPT2046_XY_SWAP != 0 + int16_t swap_tmp; + swap_tmp = *x; + *x = *y; + *y = swap_tmp; +#endif + + if ((*x) > XPT2046_X_MIN) + (*x) -= XPT2046_X_MIN; + else + (*x) = 0; + + if ((*y) > XPT2046_Y_MIN) + (*y) -= XPT2046_Y_MIN; + else + (*y) = 0; + + (*x) = (uint32_t)((uint32_t)(*x) * XPT2046_HOR_RES) + / (XPT2046_X_MAX - XPT2046_X_MIN); + + (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) + / (XPT2046_Y_MAX - XPT2046_Y_MIN); + +#if XPT2046_X_INV != 0 + (*x) = XPT2046_HOR_RES - (*x); +#endif + +#if XPT2046_Y_INV != 0 + (*y) = XPT2046_VER_RES - (*y); +#endif + +} + +#if 0 +static void xpt2046_avg(int16_t * x, int16_t * y) +{ + /*Shift out the oldest data*/ + uint8_t i; + for (i = XPT2046_AVG - 1; i > 0; i--) { + avg_buf_x[i] = avg_buf_x[i - 1]; + avg_buf_y[i] = avg_buf_y[i - 1]; + } + + /*Insert the new point*/ + avg_buf_x[0] = *x; + avg_buf_y[0] = *y; + if (avg_last < XPT2046_AVG) + avg_last++; + + /*Sum the x and y coordinates*/ + int32_t x_sum = 0; + int32_t y_sum = 0; + for (i = 0; i < avg_last; i++) { + x_sum += avg_buf_x[i]; + y_sum += avg_buf_y[i]; + } + + /*Normalize the sums*/ + (*x) = (int32_t) x_sum / avg_last; + (*y) = (int32_t) y_sum / avg_last; +} +#endif + +bool touchscreen_read(lv_indev_data_t * data) +{ + /*Store the collected data*/ + data->point.x = last_touch_point.point.x; + data->point.y = last_touch_point.point.y; + data->state = last_touch_point.state; + + if (last_touch_point.state == LV_INDEV_STATE_PR) { + last_touch_point.state = LV_INDEV_STATE_REL; + } + return false; +} + +#endif diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h new file mode 100644 index 0000000..e6d4e5c --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/XPT2046.h @@ -0,0 +1,64 @@ +/** + * @file XPT2046.h + * + */ + +#ifndef XPT2046_H +#define XPT2046_H + +#define USE_XPT2046 1 + +# define XPT2046_HOR_RES 320 +# define XPT2046_VER_RES 240 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_INV 0 + +#define CMD_X_READ 0b10010000 +#define CMD_Y_READ 0b11010000 + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + + +#if USE_XPT2046 +#include +#include +#include +#include "lv_hal/lv_hal_indev.h" +#include "device.h" +#include "drivers/gpio.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void xpt2046_init(void); +bool xpt2046_read(lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#endif /* USE_XPT2046 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* XPT2046_H */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h new file mode 100644 index 0000000..d7ea279 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/board_config.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __BOARD_CONFIG_H__ +#define __BOARD_CONFIG_H__ +#include "pin_config_stm32.h" + +#endif /* __BOARD_CONFIG_H__ */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h new file mode 100644 index 0000000..b820b9c --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display.h @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API for display drivers and applications + */ + +#ifndef ZEPHYR_INCLUDE_DISPLAY_H_ +#define ZEPHYR_INCLUDE_DISPLAY_H_ + +/** + * @brief Display Interface + * @defgroup display_interface Display Interface + * @ingroup display_interfaces + * @{ + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum display_pixel_format { + PIXEL_FORMAT_RGB_888 = BIT(0), PIXEL_FORMAT_MONO01 = BIT(1), /* 0=Black 1=White */ + PIXEL_FORMAT_MONO10 = BIT(2), /* 1=Black 0=White */ + PIXEL_FORMAT_ARGB_8888 = BIT(3), PIXEL_FORMAT_RGB_565 = BIT(4), +}; + +enum display_screen_info { + /** + * If selected, one octet represents 8 pixels ordered vertically, + * otherwise ordered horizontally. + */ + SCREEN_INFO_MONO_VTILED = BIT(0), + /** + * If selected, the MSB represents the first pixel, + * otherwise MSB represents the last pixel. + */ + SCREEN_INFO_MONO_MSB_FIRST = BIT(1), + /** + * Electrophoretic Display. + */ + SCREEN_INFO_EPD = BIT(2), + /** + * Screen has two alternating ram buffers + */ + SCREEN_INFO_DOUBLE_BUFFER = BIT(3), +}; + +/** + * @enum display_orientation + * @brief Enumeration with possible display orientation + * + */ +enum display_orientation { + DISPLAY_ORIENTATION_NORMAL, + DISPLAY_ORIENTATION_ROTATED_90, + DISPLAY_ORIENTATION_ROTATED_180, + DISPLAY_ORIENTATION_ROTATED_270, +}; + +/** + * @struct display_capabilities + * @brief Structure holding display capabilities + * + * @var u16_t display_capabilities::x_resolution + * Display resolution in the X direction + * + * @var u16_t display_capabilities::y_resolution + * Display resolution in the Y direction + * + * @var u32_t display_capabilities::supported_pixel_formats + * Bitwise or of pixel formats supported by the display + * + * @var u32_t display_capabilities::screen_info + * Information about display panel + * + * @var enum display_pixel_format display_capabilities::current_pixel_format + * Currently active pixel format for the display + * + * @var enum display_orientation display_capabilities::current_orientation + * Current display orientation + * + */ +struct display_capabilities { + u16_t x_resolution; + u16_t y_resolution; + u32_t supported_pixel_formats; + u32_t screen_info; + enum display_pixel_format current_pixel_format; + enum display_orientation current_orientation; +}; + +/** + * @struct display_buffer_descriptor + * @brief Structure to describe display data buffer layout + * + * @var u32_t display_buffer_descriptor::buf_size + * Data buffer size in bytes + * + * @var u16_t display_buffer_descriptor::width + * Data buffer row width in pixels + * + * @var u16_t display_buffer_descriptor::height + * Data buffer column height in pixels + * + * @var u16_t display_buffer_descriptor::pitch + * Number of pixels between consecutive rows in the data buffer + * + */ +struct display_buffer_descriptor { + u32_t buf_size; + u16_t width; + u16_t height; + u16_t pitch; +}; + +/** + * @typedef display_blanking_on_api + * @brief Callback API to turn on display blanking + * See display_blanking_on() for argument description + */ +typedef int (*display_blanking_on_api)(const struct device *dev); + +/** + * @typedef display_blanking_off_api + * @brief Callback API to turn off display blanking + * See display_blanking_off() for argument description + */ +typedef int (*display_blanking_off_api)(const struct device *dev); + +/** + * @typedef display_write_api + * @brief Callback API for writing data to the display + * See display_write() for argument description + */ +typedef int (*display_write_api)(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, + const void *buf); + +/** + * @typedef display_read_api + * @brief Callback API for reading data from the display + * See display_read() for argument description + */ +typedef int (*display_read_api)(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, void *buf); + +/** + * @typedef display_get_framebuffer_api + * @brief Callback API to get framebuffer pointer + * See display_get_framebuffer() for argument description + */ +typedef void *(*display_get_framebuffer_api)(const struct device *dev); + +/** + * @typedef display_set_brightness_api + * @brief Callback API to set display brightness + * See display_set_brightness() for argument description + */ +typedef int (*display_set_brightness_api)(const struct device *dev, + const u8_t brightness); + +/** + * @typedef display_set_contrast_api + * @brief Callback API to set display contrast + * See display_set_contrast() for argument description + */ +typedef int (*display_set_contrast_api)(const struct device *dev, + const u8_t contrast); + +/** + * @typedef display_get_capabilities_api + * @brief Callback API to get display capabilities + * See display_get_capabilities() for argument description + */ +typedef void (*display_get_capabilities_api)(const struct device *dev, + struct display_capabilities * capabilities); + +/** + * @typedef display_set_pixel_format_api + * @brief Callback API to set pixel format used by the display + * See display_set_pixel_format() for argument description + */ +typedef int (*display_set_pixel_format_api)(const struct device *dev, + const enum display_pixel_format pixel_format); + +/** + * @typedef display_set_orientation_api + * @brief Callback API to set orientation used by the display + * See display_set_orientation() for argument description + */ +typedef int (*display_set_orientation_api)(const struct device *dev, + const enum display_orientation orientation); + +/** + * @brief Display driver API + * API which a display driver should expose + */ +struct display_driver_api { + display_blanking_on_api blanking_on; + display_blanking_off_api blanking_off; + display_write_api write; + display_read_api read; + display_get_framebuffer_api get_framebuffer; + display_set_brightness_api set_brightness; + display_set_contrast_api set_contrast; + display_get_capabilities_api get_capabilities; + display_set_pixel_format_api set_pixel_format; + display_set_orientation_api set_orientation; +}; +extern struct ili9340_data ili9340_data1; +extern struct display_driver_api ili9340_api1; +/** + * @brief Write data to display + * + * @param dev Pointer to device structure + * @param x x Coordinate of the upper left corner where to write the buffer + * @param y y Coordinate of the upper left corner where to write the buffer + * @param desc Pointer to a structure describing the buffer layout + * @param buf Pointer to buffer array + * + * @retval 0 on success else negative errno code. + */ +static inline int display_write(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, + const void *buf) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->write(dev, x, y, desc, buf); +} + +/** + * @brief Read data from display + * + * @param dev Pointer to device structure + * @param x x Coordinate of the upper left corner where to read from + * @param y y Coordinate of the upper left corner where to read from + * @param desc Pointer to a structure describing the buffer layout + * @param buf Pointer to buffer array + * + * @retval 0 on success else negative errno code. + */ +static inline int display_read(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, void *buf) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->read(dev, x, y, desc, buf); +} + +/** + * @brief Get pointer to framebuffer for direct access + * + * @param dev Pointer to device structure + * + * @retval Pointer to frame buffer or NULL if direct framebuffer access + * is not supported + * + */ +static inline void *display_get_framebuffer(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->get_framebuffer(dev); +} + +/** + * @brief Turn display blanking on + * + * @param dev Pointer to device structure + * + * @retval 0 on success else negative errno code. + */ +static inline int display_blanking_on(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->blanking_on(dev); +} + +/** + * @brief Turn display blanking off + * + * @param dev Pointer to device structure + * + * @retval 0 on success else negative errno code. + */ +static inline int display_blanking_off(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->blanking_off(dev); +} + +/** + * @brief Set the brightness of the display + * + * Set the brightness of the display in steps of 1/256, where 255 is full + * brightness and 0 is minimal. + * + * @param dev Pointer to device structure + * @param brightness Brightness in steps of 1/256 + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_brightness(const struct device *dev, + u8_t brightness) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_brightness(dev, brightness); +} + +/** + * @brief Set the contrast of the display + * + * Set the contrast of the display in steps of 1/256, where 255 is maximum + * difference and 0 is minimal. + * + * @param dev Pointer to device structure + * @param contrast Contrast in steps of 1/256 + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_contrast(const struct device *dev, u8_t contrast) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_contrast(dev, contrast); +} + +/** + * @brief Get display capabilities + * + * @param dev Pointer to device structure + * @param capabilities Pointer to capabilities structure to populate + */ +static inline void display_get_capabilities(const struct device *dev, + struct display_capabilities * capabilities) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + api->get_capabilities(dev, capabilities); +} + +/** + * @brief Set pixel format used by the display + * + * @param dev Pointer to device structure + * @param pixel_format Pixel format to be used by display + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_pixel_format(dev, pixel_format); +} + +/** + * @brief Set display orientation + * + * @param dev Pointer to device structure + * @param orientation Orientation to be used by display + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_orientation(dev, orientation); +} + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DISPLAY_H_*/ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c new file mode 100644 index 0000000..4d4ed39 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_ili9340.h" +#include + +//#define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL +//#include +//LOG_MODULE_REGISTER(display_ili9340); +#define LOG_ERR printf +#define LOG_DBG printf +#define LOG_WRN printf + +#include +#include +#include +#include +#include + +struct ili9340_data { + struct device *reset_gpio; + struct device *command_data_gpio; + struct device *spi_dev; + struct spi_config spi_config; +#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER +struct spi_cs_control cs_ctrl; +#endif +}; + +struct ili9340_data ili9340_data1; + +#define ILI9340_CMD_DATA_PIN_COMMAND 0 +#define ILI9340_CMD_DATA_PIN_DATA 1 + +static void ili9340_exit_sleep(struct ili9340_data *data) +{ + ili9340_transmit(data, ILI9340_CMD_EXIT_SLEEP, NULL, 0); + //k_sleep(Z_TIMEOUT_MS(120)); +} + +int ili9340_init() +{ + struct ili9340_data *data = &ili9340_data1; + printf("Initializing display driver\n"); + data->spi_dev = device_get_binding(DT_ILITEK_ILI9340_0_BUS_NAME); + if (data->spi_dev == NULL) { + return -EPERM; + } + data->spi_config.frequency = DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY; + data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); //SPI_OP_MODE_MASTER | SPI_WORD_SET(8); + data->spi_config.slave = DT_ILITEK_ILI9340_0_BASE_ADDRESS; + +#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER + data->cs_ctrl.gpio_dev = + device_get_binding(DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER); + data->cs_ctrl.gpio_pin = DT_ILITEK_ILI9340_0_CS_GPIO_PIN; + data->cs_ctrl.delay = 0; + data->spi_config.cs = &(data->cs_ctrl); +#else + data->spi_config.cs = NULL; +#endif + data->reset_gpio = device_get_binding( + DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER); + if (data->reset_gpio == NULL) { + return -EPERM; + } + + gpio_pin_configure(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, + GPIO_OUTPUT); + + data->command_data_gpio = device_get_binding( + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER); + if (data->command_data_gpio == NULL) { + return -EPERM; + } + + gpio_pin_configure(data->command_data_gpio, + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, GPIO_OUTPUT); + + LOG_DBG("Resetting display driver\n"); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); + k_sleep(Z_TIMEOUT_MS(1)); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 0); + k_sleep(Z_TIMEOUT_MS(1)); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); + k_sleep(Z_TIMEOUT_MS(5)); + + LOG_DBG("Initializing LCD\n"); + ili9340_lcd_init(data); + + LOG_DBG("Exiting sleep mode\n"); + ili9340_exit_sleep(data); + + return 0; +} + +static void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x, + const u16_t y, const u16_t w, const u16_t h) +{ + u16_t spi_data[2]; + + spi_data[0] = sys_cpu_to_be16(x); + spi_data[1] = sys_cpu_to_be16(x + w - 1); + ili9340_transmit(data, ILI9340_CMD_COLUMN_ADDR, &spi_data[0], 4); + + spi_data[0] = sys_cpu_to_be16(y); + spi_data[1] = sys_cpu_to_be16(y + h - 1); + ili9340_transmit(data, ILI9340_CMD_PAGE_ADDR, &spi_data[0], 4); +} + +static int ili9340_write(const struct device *dev, const u16_t x, const u16_t y, + const struct display_buffer_descriptor *desc, const void *buf) +{ + struct ili9340_data *data = (struct ili9340_data *) &ili9340_data1; + const u8_t *write_data_start = (u8_t *) buf; + struct spi_buf tx_buf; + struct spi_buf_set tx_bufs; + u16_t write_cnt; + u16_t nbr_of_writes; + u16_t write_h; + + __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width"); + __ASSERT((3 * desc->pitch * desc->height) <= desc->buf_size, + "Input buffer to small"); + ili9340_set_mem_area(data, x, y, desc->width, desc->height); + + if (desc->pitch > desc->width) { + write_h = 1U; + nbr_of_writes = desc->height; + } else { + write_h = desc->height; + nbr_of_writes = 1U; + } + ili9340_transmit(data, ILI9340_CMD_MEM_WRITE, (void *) write_data_start, + 3 * desc->width * write_h); + + tx_bufs.buffers = &tx_buf; + tx_bufs.count = 1; + + write_data_start += (3 * desc->pitch); + for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) { + tx_buf.buf = (void *) write_data_start; + tx_buf.len = 3 * desc->width * write_h; + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + write_data_start += (3 * desc->pitch); + } + + return 0; +} + +static int ili9340_read(const struct device *dev, const u16_t x, const u16_t y, + const struct display_buffer_descriptor *desc, void *buf) +{ + LOG_ERR("Reading not supported\n"); + return -ENOTSUP; +} + +static void *ili9340_get_framebuffer(const struct device *dev) +{ + LOG_ERR("Direct framebuffer access not supported\n"); + return NULL; +} + +static int ili9340_display_blanking_off(const struct device *dev) +{ + struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; + + LOG_DBG("Turning display blanking off\n"); + ili9340_transmit(data, ILI9340_CMD_DISPLAY_ON, NULL, 0); + return 0; +} + +static int ili9340_display_blanking_on(const struct device *dev) +{ + struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; + + LOG_DBG("Turning display blanking on\n"); + ili9340_transmit(data, ILI9340_CMD_DISPLAY_OFF, NULL, 0); + return 0; +} + +static int ili9340_set_brightness(const struct device *dev, + const u8_t brightness) +{ + LOG_WRN("Set brightness not implemented\n"); + return -ENOTSUP; +} + +static int ili9340_set_contrast(const struct device *dev, const u8_t contrast) +{ + LOG_ERR("Set contrast not supported\n"); + return -ENOTSUP; +} + +static int ili9340_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + if (pixel_format == PIXEL_FORMAT_RGB_888) { + return 0; +} + LOG_ERR("Pixel format change not implemented\n"); + return -ENOTSUP; +} + +static int ili9340_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + return 0; + } + LOG_ERR("Changing display orientation not implemented\n"); + return -ENOTSUP; +} + +static void ili9340_get_capabilities(const struct device *dev, + struct display_capabilities *capabilities) +{ + memset(capabilities, 0, sizeof(struct display_capabilities)); + capabilities->x_resolution = 320; + capabilities->y_resolution = 240; + capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; + capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; + capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, + size_t tx_len) +{ + data = (struct ili9340_data *) &ili9340_data1; + struct spi_buf tx_buf = { .buf = &cmd, .len = 1 }; + struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; + + gpio_pin_set(data->command_data_gpio, DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, + ILI9340_CMD_DATA_PIN_COMMAND); + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + if (tx_data != NULL) { + tx_buf.buf = tx_data; + tx_buf.len = tx_len; + gpio_pin_set(data->command_data_gpio, + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, + ILI9340_CMD_DATA_PIN_DATA); + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + } +} + +struct display_driver_api ili9340_api1 = { + .blanking_on = ili9340_display_blanking_on, + .blanking_off = ili9340_display_blanking_off, + .write = ili9340_write, + .read = ili9340_read, + .get_framebuffer = ili9340_get_framebuffer, + .set_brightness = ili9340_set_brightness, + .set_contrast = ili9340_set_contrast, + .get_capabilities = ili9340_get_capabilities, + .set_pixel_format = ili9340_set_pixel_format, + .set_orientation = ili9340_set_orientation +}; + +/* + DEVICE_AND_API_INIT(ili9340, DT_ILITEK_ILI9340_0_LABEL, &ili9340_init, + &ili9340_data, NULL, APPLICATION, + CONFIG_APPLICATION_INIT_PRIORITY, &ili9340_api); + */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h new file mode 100644 index 0000000..8aab97a --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ +#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ +#include "board_config.h" +#include +#include + +#define ILI9340_CMD_ENTER_SLEEP 0x10 +#define ILI9340_CMD_EXIT_SLEEP 0x11 +#define ILI9340_CMD_GAMMA_SET 0x26 +#define ILI9340_CMD_DISPLAY_OFF 0x28 +#define ILI9340_CMD_DISPLAY_ON 0x29 +#define ILI9340_CMD_COLUMN_ADDR 0x2a +#define ILI9340_CMD_PAGE_ADDR 0x2b +#define ILI9340_CMD_MEM_WRITE 0x2c +#define ILI9340_CMD_MEM_ACCESS_CTRL 0x36 +#define ILI9340_CMD_PIXEL_FORMAT_SET 0x3A +#define ILI9340_CMD_FRAME_CTRL_NORMAL_MODE 0xB1 +#define ILI9340_CMD_DISPLAY_FUNCTION_CTRL 0xB6 +#define ILI9340_CMD_POWER_CTRL_1 0xC0 +#define ILI9340_CMD_POWER_CTRL_2 0xC1 +#define ILI9340_CMD_VCOM_CTRL_1 0xC5 +#define ILI9340_CMD_VCOM_CTRL_2 0xC7 +#define ILI9340_CMD_POSITVE_GAMMA_CORRECTION 0xE0 +#define ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1 + +#define ILI9340_DATA_MEM_ACCESS_CTRL_MY 0x80 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MX 0x40 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MV 0x20 +#define ILI9340_DATA_MEM_ACCESS_CTRL_ML 0x10 +#define ILI9340_DATA_MEM_ACCESS_CTRL_BGR 0x08 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MH 0x04 + +#define ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT 0x60 +#define ILI9340_DATA_PIXEL_FORMAT_RGB_16_BIT 0x50 +#define ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT 0x06 +#define ILI9340_DATA_PIXEL_FORMAT_MCU_16_BIT 0x05 + +struct ili9340_data; + +/** + * Send data to ILI9340 display controller + * + * @param data Device data structure + * @param cmd Command to send to display controller + * @param tx_data Data to transmit to the display controller + * In case no data should be transmitted pass a NULL pointer + * @param tx_len Number of bytes in tx_data buffer + * + */ +void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, + size_t tx_len); + +/** + * Perform LCD specific initialization + * + * @param data Device data structure + */ +void ili9340_lcd_init(struct ili9340_data *data); + +#define DT_ILITEK_ILI9340_0_LABEL "DISPLAY" +#define CONFIG_DISPLAY_LOG_LEVEL 0 + +#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c new file mode 100644 index 0000000..a8581a7 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/display_ili9340_adafruit_1480.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_ili9340.h" + +void ili9340_lcd_init(struct ili9340_data *data) +{ + u8_t tx_data[15]; + + tx_data[0] = 0x23; + ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_1, tx_data, 1); + + tx_data[0] = 0x10; + ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_2, tx_data, 1); + + tx_data[0] = 0x3e; + tx_data[1] = 0x28; + ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_1, tx_data, 2); + + tx_data[0] = 0x86; + ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_2, tx_data, 1); + + tx_data[0] = + ILI9340_DATA_MEM_ACCESS_CTRL_MV | ILI9340_DATA_MEM_ACCESS_CTRL_BGR; + ili9340_transmit(data, ILI9340_CMD_MEM_ACCESS_CTRL, tx_data, 1); + + tx_data[0] = ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT | + ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT; + ili9340_transmit(data, ILI9340_CMD_PIXEL_FORMAT_SET, tx_data, 1); + + tx_data[0] = 0x00; + tx_data[1] = 0x18; + ili9340_transmit(data, ILI9340_CMD_FRAME_CTRL_NORMAL_MODE, tx_data, 2); + + tx_data[0] = 0x08; + tx_data[1] = 0x82; + tx_data[2] = 0x27; + ili9340_transmit(data, ILI9340_CMD_DISPLAY_FUNCTION_CTRL, tx_data, 3); + + tx_data[0] = 0x01; + ili9340_transmit(data, ILI9340_CMD_GAMMA_SET, tx_data, 1); + + tx_data[0] = 0x0F; + tx_data[1] = 0x31; + tx_data[2] = 0x2B; + tx_data[3] = 0x0C; + tx_data[4] = 0x0E; + tx_data[5] = 0x08; + tx_data[6] = 0x4E; + tx_data[7] = 0xF1; + tx_data[8] = 0x37; + tx_data[9] = 0x07; + tx_data[10] = 0x10; + tx_data[11] = 0x03; + tx_data[12] = 0x0E; + tx_data[13] = 0x09; + tx_data[14] = 0x00; + ili9340_transmit(data, ILI9340_CMD_POSITVE_GAMMA_CORRECTION, tx_data, 15); + + tx_data[0] = 0x00; + tx_data[1] = 0x0E; + tx_data[2] = 0x14; + tx_data[3] = 0x03; + tx_data[4] = 0x11; + tx_data[5] = 0x07; + tx_data[6] = 0x31; + tx_data[7] = 0xC1; + tx_data[8] = 0x48; + tx_data[9] = 0x08; + tx_data[10] = 0x0F; + tx_data[11] = 0x0C; + tx_data[12] = 0x31; + tx_data[13] = 0x36; + tx_data[14] = 0x0F; + ili9340_transmit(data, ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION, tx_data, 15); +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c new file mode 100644 index 0000000..26e04b8 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/iwasm_main.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "bh_platform.h" +#include "runtime_lib.h" +#include "native_interface.h" +#include "app_manager_export.h" +#include "board_config.h" +#include "bh_common.h" +#include "bh_queue.h" +#include "runtime_sensor.h" +#include "bi-inc/attr_container.h" +#include "module_wasm_app.h" +#include "wasm_export.h" +#include "display.h" +#include "lvgl.h" + +extern void init_sensor_framework(); +extern void exit_sensor_framework(); +extern int aee_host_msg_callback(void *msg, uint16_t msg_len); +extern bool touchscreen_read(lv_indev_data_t * data); +extern int ili9340_init(); +extern void xpt2046_init(void); +extern void wgl_init(); + +#include +#include +#include + +int uart_char_cnt = 0; + +static void uart_irq_callback(struct device *dev) +{ + unsigned char ch; + + while (uart_poll_in(dev, &ch) == 0) { + uart_char_cnt++; + aee_host_msg_callback(&ch, 1); + } +} + +struct device *uart_dev = NULL; + +static bool host_init() +{ + uart_dev = device_get_binding(HOST_DEVICE_COMM_UART_NAME); + if (!uart_dev) { + printf("UART: Device driver not found.\n"); + return false; + } + uart_irq_rx_enable(uart_dev); + uart_irq_callback_set(uart_dev, uart_irq_callback); + return true; +} + +int host_send(void * ctx, const char *buf, int size) +{ + if (!uart_dev) + return 0; + + for (int i = 0; i < size; i++) + uart_poll_out(uart_dev, buf[i]); + + return size; +} + +void host_destroy() +{ +} + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy +}; + +timer_ctx_t timer_ctx; + +static char global_heap_buf[270 * 1024] = { 0 }; + +static uint8_t color_copy[320 * 10 * 3]; + +static void display_flush(lv_disp_drv_t *disp_drv, + const lv_area_t *area, + lv_color_t *color) +{ + u16_t w = area->x2 - area->x1 + 1; + u16_t h = area->y2 - area->y1 + 1; + struct display_buffer_descriptor desc; + int i; + uint8_t *color_p = color_copy; + + desc.buf_size = 3 * w * h; + desc.width = w; + desc.pitch = w; + desc.height = h; + + for (i = 0; i < w * h; i++, color++) { + color_p[i * 3] = color->ch.red; + color_p[i * 3 + 1] = color->ch.green; + color_p[i * 3 + 2] = color->ch.blue; + } + + display_write(NULL, area->x1, area->y1, &desc, (void *) color_p); + + lv_disp_flush_ready(disp_drv); /* in v5.3 is lv_flush_ready */ +} + +static bool display_input_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) +{ + return touchscreen_read(data); +} + +/** + * Initialize the Hardware Abstraction Layer (HAL) for the Littlev graphics library + */ +static void hal_init(void) +{ + xpt2046_init(); + ili9340_init(); + display_blanking_off(NULL); + + /*Create a display buffer*/ + static lv_disp_buf_t disp_buf1; + static lv_color_t buf1_1[320*10]; + lv_disp_buf_init(&disp_buf1, buf1_1, NULL, 320*10); + + /*Create a display*/ + lv_disp_drv_t disp_drv; + lv_disp_drv_init(&disp_drv); /*Basic initialization*/ + disp_drv.buffer = &disp_buf1; + disp_drv.flush_cb = display_flush; + // disp_drv.hor_res = 200; + // disp_drv.ver_res = 100; + lv_disp_drv_register(&disp_drv); + + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); /*Basic initialization*/ + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read_cb = display_input_read; + lv_indev_drv_register(&indev_drv); +} + +int iwasm_main() +{ + RuntimeInitArgs init_args; + host_init(); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + wgl_init(); + hal_init(); + + // timer manager + init_wasm_timer(); + + // TODO: + app_manager_startup(&interface); + + wasm_runtime_destroy(); + return -1; +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c new file mode 100644 index 0000000..5b31d00 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/main.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" + +extern int iwasm_main(); + +void main(void) +{ + iwasm_main(); + for(;;){ + k_sleep(Z_TIMEOUT_MS(1000)); + } +} + +int time_get_ms() +{ + return k_uptime_get_32(); +} diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h new file mode 100644 index 0000000..3cde4ec --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_jlf.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __PIN_CONFIG_JLF_H__ +#define __PIN_CONFIG_JLF_H__ + +#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_2" +#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 10*1000 + +#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIO_0" +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 5 +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIO_0" +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 4 + +#define XPT2046_SPI_DEVICE_NAME "SPI_2" +#define XPT2046_SPI_MAX_FREQUENCY 10*1000 +#define XPT2046_CS_GPIO_CONTROLLER "GPIO_0" +#define XPT2046_CS_GPIO_PIN 6 + +#define XPT2046_PEN_GPIO_CONTROLLER "GPIO_0" +#define XPT2046_PEN_GPIO_PIN 7 + +#define HOST_DEVICE_COMM_UART_NAME "UART_1" +#endif /* __PIN_CONFIG_JLF_H__ */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h new file mode 100644 index 0000000..fba36fa --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/src/platform/zephyr/pin_config_stm32.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __PIN_CONFIG_STM32_H__ +#define __PIN_CONFIG_STM32_H__ + +#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_1" +#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 24*1000*1000 + +#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 12 +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 11 + +#define DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_CS_GPIO_PIN 10 + +#define XPT2046_SPI_DEVICE_NAME "SPI_1" +#define XPT2046_SPI_MAX_FREQUENCY 12*1000*1000 +#define XPT2046_CS_GPIO_CONTROLLER "GPIOD" +#define XPT2046_CS_GPIO_PIN 0 + +#define XPT2046_PEN_GPIO_CONTROLLER "GPIOD" +#define XPT2046_PEN_GPIO_PIN 1 + +#define HOST_DEVICE_COMM_UART_NAME "UART_6" + +#endif /* __PIN_CONFIG_STM32_H__ */ diff --git a/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt b/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt new file mode 100644 index 0000000..005358c --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/CMakeLists.txt @@ -0,0 +1,78 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +set (WAMR_BUILD_PLATFORM "zephyr") + +enable_language (ASM) + +add_definitions(-DWA_MALLOC=wasm_runtime_malloc) +add_definitions(-DWA_FREE=wasm_runtime_free) + +# Build as THUMB by default +# change to "ARM[sub]", "THUMB[sub]", "X86_32", "MIPS" or "XTENSA" +# if we want to support arm_32, x86, mips or xtensa +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "THUMBV7") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + # Enable libc builtin support by default + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + # Disable libc wasi support by default + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (NOT DEFINED WAMR_BUILD_APP_FRAMEWORK) + set (WAMR_BUILD_APP_FRAMEWORK 1) +endif () + +if (NOT DEFINED WAMR_BUILD_APP_LIST) + set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_ALL) +endif () + +################ wamr runtime settings ################ +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +################ sample project related ################ +add_definitions(-DLV_CONF_INCLUDE_SIMPLE) +add_definitions(-DLV_MEM_CUSTOM=1) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr) +include_directories(${WAMR_ROOT_DIR}/samples/gui/lv_config) + +set (LVGL_DRV_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340_adafruit_1480.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/XPT2046.c + ) + +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + ${LVGL_DRV_SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c + ) diff --git a/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf b/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf new file mode 100644 index 0000000..f9f13f9 --- /dev/null +++ b/wamr/samples/gui/wasm-runtime-wgl/zephyr-build/prj.conf @@ -0,0 +1,10 @@ +CONFIG_SPI=y +CONFIG_SPI_STM32=y +CONFIG_SPI_1=y +CONFIG_PRINTK=y +CONFIG_LOG=y +#CONFIG_UART_2=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_STACK_SENTINEL=y +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ARM_MPU=y diff --git a/wamr/samples/littlevgl/LICENCE.txt b/wamr/samples/littlevgl/LICENCE.txt new file mode 100644 index 0000000..beaef1d --- /dev/null +++ b/wamr/samples/littlevgl/LICENCE.txt @@ -0,0 +1,8 @@ +MIT licence +Copyright (c) 2016 Gábor Kiss-Vámosi + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wamr/samples/littlevgl/README.md b/wamr/samples/littlevgl/README.md new file mode 100644 index 0000000..b5e3124 --- /dev/null +++ b/wamr/samples/littlevgl/README.md @@ -0,0 +1,159 @@ +"littlevgl" sample introduction +============== +This sample demonstrates that a graphic user interface application in WebAssembly by compiling the LittlevGL v5.3, an open-source embedded 2d graphic library into the WASM bytecode. + +In this sample, the whole LittlevGL v5.3 source code is built into the WebAssembly code with the user application. The platform interfaces defined by LittlevGL is implemented in the runtime and registered for WASM application through calling wasm_runtime_full_init(). + +``` +static NativeSymbol native_symbols[] = { + 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") +}; +``` + +The runtime component supports building target for Linux and Zephyr/STM Nucleo board. The beauty of this sample is the WebAssembly application can have identical display and behavior when running from both runtime environments. That implies we can do majority of application validation from desktop environment as long as two runtime distributions support the same set of application interface. + + +Below pictures show the WASM application is running on an STM board with an LCD touch panel. + +![WAMR UI SAMPLE](../../doc/pics/vgl_demo2.png "WAMR UI DEMO STM32") + +![WAMR UI SAMPLE](../../doc/pics/vgl_demo_linux.png "WAMR UI DEMO LINUX") + + +The number on top will plus one each second, and the number on the bottom will plus one when clicked. When users click the blue button, the WASM application increases the counter, and the latest counter value is displayed on the top banner of the touch panel. + +The sample also provides the native Linux version of application without the runtime under folder "vgl-native-ui-app". It can help to check differences between the implementations in native and WebAssembly. + +Test on Linux +================================ + +Install required SDK and libraries +-------------- +- 32 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_32` when building WAMR runtime) +Use apt-get: + `sudo apt-get install libsdl2-dev:i386` +Or download source from www.libsdl.org: +``` +./configure C_FLAGS=-m32 CXX_FLAGS=-m32 LD_FLAGS=-m32 +make +sudo make install +``` +- 64 bit SDL(simple directmedia layer) (Note: only necessary when `WAMR_BUILD_TARGET` is set to `X86_64` when building WAMR runtime) +Use apt-get: + `sudo apt-get install libsdl2-dev` +Or download source from www.libsdl.org: +``` +./configure +make +sudo make install +``` + + +Build and Run +-------------- + +- Build
    +`./build.sh`
    + All binaries are in "out", which contains "host_tool", "vgl_native_ui_app", "ui_app.wasm" "ui_app_no_wasi.wasm "and "vgl_wasm_runtime". +- Run the native Linux build of the lvgl sample (no wasm)
    +`./vgl_native_ui_app`
    + +- Run WASM VM Linux applicaton & install WASM APP
    + First start vgl_wasm_runtime in server mode.
    +`./vgl_wasm_runtime -s`
    + Then install and uninstall wasm APPs by using host tool.
    +`./host_tool -i ui_wasi -f ui_app_wasi.wasm`
    +`./host_tool -q`
    +`./host_tool -u ui_wasi`
    +`./host_tool -i ui_no_wasi -f ui_app_builtin_libc.wasm`
    +`./host_tool -q`
    +`./host_tool -u ui_no_wasi`
    + + + +Test on Zephyr +================================ +We can use a STM32 NUCLEO_F767ZI board with ILI9341 display and XPT2046 touch screen to run the test. Then use host_tool to remotely install wasm app into STM32. +- Build WASM VM into Zephyr system + a. clone zephyr source code + Refer to Zephyr getting started. + https://docs.zephyrproject.org/latest/getting_started/index.html + + ```bash + west init zephyrproject + cd zephyrproject + west update + ``` + + b. copy samples + ```bash + cd zephyr/samples/ + cp -a samples/littlevgl/vgl-wasm-runtime vgl-wasm-runtime + cd vgl-wasm-runtime/zephyr_build + ``` + c. create a link to wamr root dir + ```bash + ln -s wamr + ``` + + d. build source code + Since ui_app incorporated LittlevGL source code, so it needs more RAM on the device to install the application. It is recommended that RAM SIZE not less than 380KB. In our test use nucleo_f767zi, which is supported by Zephyr. Since the littlevgl wasm app is quite big (~100KB in wasm format and ~200KB in AOT format ), there isn't enough SRAM to build interpreter and AOT together. You can only choose one of them: + + - Interpreter + ``` Bash + mkdir build && cd build + source ../../../../zephyr-env.sh + cmake -GNinja -DBOARD=nucleo_f767zi -DWAMR_BUILD_INTERP=1 -DWAMR_BUILD_AOT=0 .. + ninja flash + ``` + + - AOT + ``` Bash + mkdir build && cd build + source ../../../../zephyr-env.sh + cmake -GNinja -DBOARD=nucleo_f767zi -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 .. + ninja flash + ``` + +- Hardware Connections + +``` ++-------------------+-+------------------+ +|NUCLEO-F767ZI | ILI9341 Display | ++-------------------+-+------------------+ +| CN7.10 | CLK | ++-------------------+-+------------------+ +| CN7.12 | MISO | ++-------------------+-+------------------+ +| CN7.14 | MOSI | ++-------------------+-+------------------+ +| CN11.1 | CS1 for ILI9341 | ++-------------------+-+------------------+ +| CN11.2 | D/C | ++-------------------+-+------------------+ +| CN11.3 | RESET | ++-------------------+-+------------------+ +| CN9.25 | PEN interrupt | ++-------------------+-+------------------+ +| CN9.27 | CS2 for XPT2046 | ++-------------------+-+------------------+ +| CN10.14 | PC UART RX | ++-------------------+-+------------------+ +| CN11.16 | PC UART RX | ++-------------------+-+------------------+ +``` + + +- Install WASM application to Zephyr using host_tool
    +First, connect PC and STM32 with UART. Then install to use host_tool.
    +`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app_builtin_libc.wasm` +**Note**: WASI is unavailable on zephyr currently, so you have to use the ui_app_builtin_libc.wasm which doesn't depend on WASI. + +- Install AOT version WASM application +`wamrc --target=thumbv7 --target-abi=eabi --cpu=cortex-m7 -o ui_app_no_wasi.aot ui_app_builtin_libc.wasm` +`./host_tool -D /dev/ttyUSBXXX -i ui_app -f ui_app_no_wasi.aot` diff --git a/wamr/samples/littlevgl/build.sh b/wamr/samples/littlevgl/build.sh new file mode 100755 index 0000000..9e12fe1 --- /dev/null +++ b/wamr/samples/littlevgl/build.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +PROJECT_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out +BUILD_DIR=${PWD}/build +LV_CFG_PATH=${PROJECT_DIR}/lv_config + + + +if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then + echo "Local Build Env" + cmakewrap="cmake" + makewrap="make" +else + echo "Klocwork Build Env" + cmakewrap="cmake -DCMAKE_BUILD_TYPE=Debug" + makewrap="kwinject -o $KW_OUT_FILE make" +fi + +if [ ! -d $BUILD_DIR ]; then + mkdir ${BUILD_DIR} +fi + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} + + +cd ${BUILD_DIR} +if [ ! -d "lvgl" ]; then + echo "starting download lvgl for v5.3 ..." + git clone https://github.com/littlevgl/lvgl.git --branch v5.3 + if [ $? != 0 ];then + echo "download lvgl repo failed: $?\n" + exit 2 + fi +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} -c +[ $? -eq 0 ] || exit $? +echo "#####################build wamr-sdk littlevgl success" + +echo -e "\n\n" +echo "##################### 1. build native-ui-app start#####################" +cd $BUILD_DIR +mkdir -p vgl-native-ui-app +cd vgl-native-ui-app +$cmakewrap ${PROJECT_DIR}/vgl-native-ui-app +$makewrap +if [ $? != 0 ];then + echo "BUILD_FAIL native-ui-app $?\n" + exit 2 +fi +echo $PWD +cp vgl_native_ui_app ${OUT_DIR} +echo "#####################build native-ui-app success" + +echo -e "\n\n" +echo "##################### 2. build littlevgl wasm runtime start#####################" +cd $BUILD_DIR +mkdir -p vgl-wasm-runtime +cd vgl-wasm-runtime +$cmakewrap ${PROJECT_DIR}/vgl-wasm-runtime +$makewrap +[ $? -eq 0 ] || exit $? +cp vgl_wasm_runtime ${OUT_DIR}/ + +echo "##################### build littlevgl wasm runtime end#####################" + +echo -e "\n\n" +echo "#####################build host-tool" +cd $BUILD_DIR +mkdir -p host-tool +cd host-tool +$cmakewrap ${WAMR_DIR}/test-tools/host-tool +$makewrap +if [ $? != 0 ];then + echo "BUILD_FAIL host tool exit as $?\n" + exit 2 +fi +cp host_tool ${OUT_DIR} +echo "#####################build host-tool success" + +echo -e "\n\n" +echo "##################### 3. build wasm ui app start#####################" +cd ${PROJECT_DIR}/wasm-apps +if [ ! -d "${PROJECT_DIR}/wasm-apps/lvgl" ]; then + if [ -d "$BUILD_DIR/vgl-native-ui-app/lvgl" ]; then + cp -fr $BUILD_DIR/vgl-native-ui-app/lvgl ${PROJECT_DIR}/wasm-apps + fi +fi +./build_wasm_app.sh +mv *.wasm ${OUT_DIR}/ + +echo "##################### build wasm ui app end#####################" diff --git a/wamr/samples/littlevgl/lv_config/lv_conf.h b/wamr/samples/littlevgl/lv_config/lv_conf.h new file mode 100644 index 0000000..76533a8 --- /dev/null +++ b/wamr/samples/littlevgl/lv_config/lv_conf.h @@ -0,0 +1,389 @@ +/** + * @file lv_conf.h + * + */ + +#if 1 /*Set it to "1" to enable content*/ + +#ifndef LV_CONF_H +#define LV_CONF_H +/*=================== + Dynamic memory + *===================*/ + +/* Memory size which will be used by the library + * to store the graphical objects and other data */ +#define LV_MEM_CUSTOM 1 /*1: use custom malloc/free, 0: use the built-in lv_mem_alloc/lv_mem_free*/ +#if LV_MEM_CUSTOM == 0 +# define LV_MEM_SIZE (64U * 1024U) /*Size memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ +# define LV_MEM_ATTR /*Complier prefix for big array declaration*/ +# define LV_MEM_ADR 0 /*Set an address for memory pool instead of allocation it as an array. Can be in external SRAM too.*/ +# define LV_MEM_AUTO_DEFRAG 1 /*Automatically defrag on free*/ +#else /*LV_MEM_CUSTOM*/ +# define LV_MEM_CUSTOM_INCLUDE /*Header for the dynamic memory function*/ +# define LV_MEM_CUSTOM_ALLOC malloc /*Wrapper to malloc*/ +# define LV_MEM_CUSTOM_FREE free /*Wrapper to free*/ +#endif /*LV_MEM_CUSTOM*/ + +/* Garbage Collector settings + * Used if lvgl is binded to higher language and the memory is managed by that language */ +#define LV_ENABLE_GC 0 +#if LV_ENABLE_GC != 0 +# define LV_MEM_CUSTOM_REALLOC your_realloc /*Wrapper to realloc*/ +# define LV_MEM_CUSTOM_GET_SIZE your_mem_get_size /*Wrapper to lv_mem_get_size*/ +# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/ +#endif /* LV_ENABLE_GC */ + +/*=================== + Graphical settings + *===================*/ + +/* Horizontal and vertical resolution of the library.*/ +#define LV_HOR_RES (320) +#define LV_VER_RES (240) + +/* Dot Per Inch: used to initialize default sizes. E.g. a button with width = LV_DPI / 2 -> half inch wide + * (Not so important, you can adjust it to modify default sizes and spaces)*/ +#define LV_DPI 100 + +/* Enable anti-aliasing (lines, and radiuses will be smoothed) */ +#define LV_ANTIALIAS 0 /*1: Enable anti-aliasing*/ + +/*Screen refresh period in milliseconds*/ +#define LV_REFR_PERIOD 30 + +/*----------------- + * VDB settings + *----------------*/ + +/* VDB (Virtual Display Buffer) is an internal graphics buffer. + * The GUI will be drawn into this buffer first and then + * the buffer will be passed to your `disp_drv.disp_flush` function to + * copy it to your frame buffer. + * VDB is required for: buffered drawing, opacity, anti-aliasing and shadows + * Learn more: https://docs.littlevgl.com/#Drawing*/ + +/* Size of the VDB in pixels. Typical size: ~1/10 screen. Must be >= LV_HOR_RES + * Setting it to 0 will disable VDB and `disp_drv.disp_fill` and `disp_drv.disp_map` functions + * will be called to draw to the frame buffer directly*/ +#define LV_VDB_SIZE ((LV_VER_RES * LV_HOR_RES) / 10) + +/* Bit-per-pixel of VDB. Useful for monochrome or non-standard color format displays. + * Special formats are handled with `disp_drv.vdb_wr`)*/ +#define LV_VDB_PX_BPP LV_COLOR_SIZE /*LV_COLOR_SIZE comes from LV_COLOR_DEPTH below to set 8, 16 or 32 bit pixel size automatically */ + +/* Place VDB to a specific address (e.g. in external RAM) + * 0: allocate automatically into RAM + * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/ +#define LV_VDB_ADR 0 + +/* Use two Virtual Display buffers (VDB) to parallelize rendering and flushing + * The flushing should use DMA to write the frame buffer in the background */ +#define LV_VDB_DOUBLE 0 + +/* Place VDB2 to a specific address (e.g. in external RAM) + * 0: allocate automatically into RAM + * LV_VDB_ADR_INV: to replace it later with `lv_vdb_set_adr()`*/ +#define LV_VDB2_ADR 0 + +/* Using true double buffering in `disp_drv.disp_flush` you will always get the image of the whole screen. + * Your only task is to set the rendered image (`color_p` parameter) as frame buffer address or send it to your display. + * The best if you do in the blank period of you display to avoid tearing effect. + * Requires: + * - LV_VDB_SIZE = LV_HOR_RES * LV_VER_RES + * - LV_VDB_DOUBLE = 1 + */ +#define LV_VDB_TRUE_DOUBLE_BUFFERED 0 + +/*================= + Misc. setting + *=================*/ + +/*Input device settings*/ +#define LV_INDEV_READ_PERIOD 50 /*Input device read period in milliseconds*/ +#define LV_INDEV_POINT_MARKER 0 /*Mark the pressed points (required: USE_LV_REAL_DRAW = 1)*/ +#define LV_INDEV_DRAG_LIMIT 10 /*Drag threshold in pixels */ +#define LV_INDEV_DRAG_THROW 20 /*Drag throw slow-down in [%]. Greater value means faster slow-down */ +#define LV_INDEV_LONG_PRESS_TIME 400 /*Long press time in milliseconds*/ +#define LV_INDEV_LONG_PRESS_REP_TIME 100 /*Repeated trigger period in long press [ms] */ + +/*Color settings*/ +#define LV_COLOR_DEPTH 32 /*Color depth: 1/8/16/32*/ +#define LV_COLOR_16_SWAP 0 /*Swap the 2 bytes of RGB565 color. Useful if the display has a 8 bit interface (e.g. SPI)*/ +#define LV_COLOR_SCREEN_TRANSP 0 /*1: Enable screen transparency. Useful for OSD or other overlapping GUIs. Requires ARGB8888 colors*/ +#define LV_COLOR_TRANSP LV_COLOR_LIME /*Images pixels with this color will not be drawn (with chroma keying)*/ + +/*Text settings*/ +#define LV_TXT_UTF8 1 /*Enable UTF-8 coded Unicode character usage */ +#define LV_TXT_BREAK_CHARS " ,.;:-_" /*Can break texts on these chars*/ +#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */ + +/*Feature usage*/ +#define USE_LV_ANIMATION 1 /*1: Enable all animations*/ +#define USE_LV_SHADOW 1 /*1: Enable shadows*/ +#define USE_LV_GROUP 1 /*1: Enable object groups (for keyboards)*/ +#define USE_LV_GPU 0 /*1: Enable GPU interface*/ +#define USE_LV_REAL_DRAW 1 /*1: Enable function which draw directly to the frame buffer instead of VDB (required if LV_VDB_SIZE = 0)*/ +#define USE_LV_FILESYSTEM 0 /*1: Enable file system (might be required for images*/ +#define USE_LV_MULTI_LANG 0 /* Number of languages for labels to store (0: to disable this feature)*/ + +/*Compiler settings*/ +#define LV_ATTRIBUTE_TICK_INC /* Define a custom attribute to `lv_tick_inc` function */ +#define LV_ATTRIBUTE_TASK_HANDLER /* Define a custom attribute to `lv_task_handler` function */ +#define LV_COMPILER_VLA_SUPPORTED 1 /* 1: Variable length array is supported*/ +#define LV_COMPILER_NON_CONST_INIT_SUPPORTED 1 /* 1: Initialization with non constant values are supported */ + +/*HAL settings*/ +#define LV_TICK_CUSTOM 1 /*1: use a custom tick source (removing the need to manually update the tick with `lv_tick_inc`) */ +#if LV_TICK_CUSTOM == 1 +#define LV_TICK_CUSTOM_INCLUDE "system_header.h" /*Header for the sys time function*/ +#define LV_TICK_CUSTOM_SYS_TIME_EXPR (time_get_ms()) /*Expression evaluating to current systime in ms*/ +#endif /*LV_TICK_CUSTOM*/ + +/*Log settings*/ +#define USE_LV_LOG 1 /*Enable/disable the log module*/ +#if USE_LV_LOG +/* How important log should be added: + * LV_LOG_LEVEL_TRACE A lot of logs to give detailed information + * LV_LOG_LEVEL_INFO Log important events + * LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't caused problem + * LV_LOG_LEVEL_ERROR Only critical issue, when the system may fail + */ +# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN +/* 1: Print the log with 'printf'; 0: user need to register a callback*/ + +# define LV_LOG_PRINTF 0 +#endif /*USE_LV_LOG*/ + +/*================ + * THEME USAGE + *================*/ +#define LV_THEME_LIVE_UPDATE 1 /*1: Allow theme switching at run time. Uses 8..10 kB of RAM*/ + +#define USE_LV_THEME_TEMPL 0 /*Just for test*/ +#define USE_LV_THEME_DEFAULT 1 /*Built mainly from the built-in styles. Consumes very few RAM*/ +#define USE_LV_THEME_ALIEN 0 /*Dark futuristic theme*/ +#define USE_LV_THEME_NIGHT 0 /*Dark elegant theme*/ +#define USE_LV_THEME_MONO 0 /*Mono color theme for monochrome displays*/ +#define USE_LV_THEME_MATERIAL 0 /*Flat theme with bold colors and light shadows*/ +#define USE_LV_THEME_ZEN 0 /*Peaceful, mainly light theme */ +#define USE_LV_THEME_NEMO 0 /*Water-like theme based on the movie "Finding Nemo"*/ + +/*================== + * FONT USAGE + *===================*/ + +/* More info about fonts: https://docs.littlevgl.com/#Fonts + * To enable a built-in font use 1,2,4 or 8 values + * which will determine the bit-per-pixel. Higher value means smoother fonts */ +#define USE_LV_FONT_DEJAVU_10 0 +#define USE_LV_FONT_DEJAVU_10_LATIN_SUP 0 +#define USE_LV_FONT_DEJAVU_10_CYRILLIC 0 +#define USE_LV_FONT_SYMBOL_10 0 + +#define USE_LV_FONT_DEJAVU_20 4 +#define USE_LV_FONT_DEJAVU_20_LATIN_SUP 0 +#define USE_LV_FONT_DEJAVU_20_CYRILLIC 0 +#define USE_LV_FONT_SYMBOL_20 0 + +#define USE_LV_FONT_DEJAVU_30 0 +#define USE_LV_FONT_DEJAVU_30_LATIN_SUP 0 +#define USE_LV_FONT_DEJAVU_30_CYRILLIC 0 +#define USE_LV_FONT_SYMBOL_30 0 + +#define USE_LV_FONT_DEJAVU_40 0 +#define USE_LV_FONT_DEJAVU_40_LATIN_SUP 0 +#define USE_LV_FONT_DEJAVU_40_CYRILLIC 0 +#define USE_LV_FONT_SYMBOL_40 0 + +#define USE_LV_FONT_MONOSPACE_8 1 + +/* Optionally declare your custom fonts here. + * You can use these fonts as default font too + * and they will be available globally. E.g. + * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) \ + * LV_FONT_DECLARE(my_font_2) \ + */ +#define LV_FONT_CUSTOM_DECLARE + +#define LV_FONT_DEFAULT &lv_font_dejavu_20 /*Always set a default font from the built-in fonts*/ + +/*=================== + * LV_OBJ SETTINGS + *==================*/ +#define LV_OBJ_FREE_NUM_TYPE uint32_t /*Type of free number attribute (comment out disable free number)*/ +#define LV_OBJ_FREE_PTR 1 /*Enable the free pointer attribute*/ +#define LV_OBJ_REALIGN 1 /*Enable `lv_obj_realaign()` based on `lv_obj_align()` parameters*/ + +/*================== + * LV OBJ X USAGE + *================*/ +/* + * Documentation of the object types: https://docs.littlevgl.com/#Object-types + */ + +/***************** + * Simple object + *****************/ + +/*Label (dependencies: -*/ +#define USE_LV_LABEL 1 +#if USE_LV_LABEL != 0 +# define LV_LABEL_SCROLL_SPEED 25 /*Hor, or ver. scroll speed [px/sec] in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/ +#endif + +/*Image (dependencies: lv_label*/ +#define USE_LV_IMG 1 +#if USE_LV_IMG != 0 +# define LV_IMG_CF_INDEXED 1 /*Enable indexed (palette) images*/ +# define LV_IMG_CF_ALPHA 1 /*Enable alpha indexed images*/ +#endif + +/*Line (dependencies: -*/ +#define USE_LV_LINE 1 + +/*Arc (dependencies: -)*/ +#define USE_LV_ARC 1 + +/******************* + * Container objects + *******************/ + +/*Container (dependencies: -*/ +#define USE_LV_CONT 1 + +/*Page (dependencies: lv_cont)*/ +#define USE_LV_PAGE 1 + +/*Window (dependencies: lv_cont, lv_btn, lv_label, lv_img, lv_page)*/ +#define USE_LV_WIN 1 + +/*Tab (dependencies: lv_page, lv_btnm)*/ +#define USE_LV_TABVIEW 1 +# if USE_LV_TABVIEW != 0 +# define LV_TABVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/ +#endif + +/*Tileview (dependencies: lv_page) */ +#define USE_LV_TILEVIEW 1 +#if USE_LV_TILEVIEW +# define LV_TILEVIEW_ANIM_TIME 300 /*Time of slide animation [ms] (0: no animation)*/ +#endif + +/************************* + * Data visualizer objects + *************************/ + +/*Bar (dependencies: -)*/ +#define USE_LV_BAR 1 + +/*Line meter (dependencies: *;)*/ +#define USE_LV_LMETER 1 + +/*Gauge (dependencies:lv_bar, lv_lmeter)*/ +#define USE_LV_GAUGE 1 + +/*Chart (dependencies: -)*/ +#define USE_LV_CHART 1 + +/*Table (dependencies: lv_label)*/ +#define USE_LV_TABLE 1 +#if USE_LV_TABLE +# define LV_TABLE_COL_MAX 12 +#endif + +/*LED (dependencies: -)*/ +#define USE_LV_LED 1 + +/*Message box (dependencies: lv_rect, lv_btnm, lv_label)*/ +#define USE_LV_MBOX 1 + +/*Text area (dependencies: lv_label, lv_page)*/ +#define USE_LV_TA 1 +#if USE_LV_TA != 0 +# define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/ +# define LV_TA_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +/*Spinbox (dependencies: lv_ta)*/ +#define USE_LV_SPINBOX 1 + +/*Calendar (dependencies: -)*/ +#define USE_LV_CALENDAR 1 + +/*Preload (dependencies: lv_arc)*/ +#define USE_LV_PRELOAD 1 +#if USE_LV_PRELOAD != 0 +# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ +# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ +# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC +#endif + +/*Canvas (dependencies: lv_img)*/ +#define USE_LV_CANVAS 1 +/************************* + * User input objects + *************************/ + +/*Button (dependencies: lv_cont*/ +#define USE_LV_BTN 1 +#if USE_LV_BTN != 0 +# define LV_BTN_INK_EFFECT 1 /*Enable button-state animations - draw a circle on click (dependencies: USE_LV_ANIMATION)*/ +#endif + +/*Image Button (dependencies: lv_btn*/ +#define USE_LV_IMGBTN 1 +#if USE_LV_IMGBTN +# define LV_IMGBTN_TILED 0 /*1: The imgbtn requires left, mid and right parts and the width can be set freely*/ +#endif + +/*Button matrix (dependencies: -)*/ +#define USE_LV_BTNM 1 + +/*Keyboard (dependencies: lv_btnm)*/ +#define USE_LV_KB 1 + +/*Check box (dependencies: lv_btn, lv_label)*/ +#define USE_LV_CB 1 + +/*List (dependencies: lv_page, lv_btn, lv_label, (lv_img optionally for icons ))*/ +#define USE_LV_LIST 1 +#if USE_LV_LIST != 0 +# define LV_LIST_FOCUS_TIME 100 /*Default animation time of focusing to a list element [ms] (0: no animation) */ +#endif + +/*Drop down list (dependencies: lv_page, lv_label, lv_symbol_def.h)*/ +#define USE_LV_DDLIST 1 +#if USE_LV_DDLIST != 0 +# define LV_DDLIST_ANIM_TIME 200 /*Open and close default animation time [ms] (0: no animation)*/ +#endif + +/*Roller (dependencies: lv_ddlist)*/ +#define USE_LV_ROLLER 1 +#if USE_LV_ROLLER != 0 +# define LV_ROLLER_ANIM_TIME 200 /*Focus animation time [ms] (0: no animation)*/ +#endif + +/*Slider (dependencies: lv_bar)*/ +#define USE_LV_SLIDER 1 + +/*Switch (dependencies: lv_slider)*/ +#define USE_LV_SW 1 + +/************************* + * Non-user section + *************************/ +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) /* Disable warnings for Visual Studio*/ +# define _CRT_SECURE_NO_WARNINGS +#endif + +/*--END OF LV_CONF_H--*/ + +/*Be sure every define has a default value*/ +#include "lvgl/lv_conf_checker.h" + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/samples/littlevgl/lv_config/lv_drv_conf.h b/wamr/samples/littlevgl/lv_config/lv_drv_conf.h new file mode 100644 index 0000000..d216a3e --- /dev/null +++ b/wamr/samples/littlevgl/lv_config/lv_drv_conf.h @@ -0,0 +1,310 @@ +/** + * @file lv_drv_conf.h + * + */ + +/* + * COPY THIS FILE AS lv_drv_conf.h + */ + +#if 1 /*Set it to "1" to enable the content*/ + +#ifndef LV_DRV_CONF_H +#define LV_DRV_CONF_H + +#include "lv_conf.h" + +/********************* + * DELAY INTERFACE + *********************/ +#define LV_DRV_DELAY_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DELAY_US(us) /*delay_us(us)*/ /*Delay the given number of microseconds*/ +#define LV_DRV_DELAY_MS(ms) /*delay_ms(ms)*/ /*Delay the given number of milliseconds*/ + +/********************* + * DISPLAY INTERFACE + *********************/ + +/*------------ + * Common + *------------*/ +#define LV_DRV_DISP_INCLUDE /*Dummy include by default*/ +#define LV_DRV_DISP_CMD_DATA(val) /*pin_x_set(val)*/ /*Set the command/data pin to 'val'*/ +#define LV_DRV_DISP_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_DISP_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_DISP_SPI_WR_BYTE(data) /*spi_wr(data)*/ /*Write a byte the SPI bus*/ +#define LV_DRV_DISP_SPI_WR_ARRAY(adr, n) /*spi_wr_mem(adr, n)*/ /*Write 'n' bytes to SPI bus from 'adr'*/ + +/*------------------ + * Parallel port + *-----------------*/ +#define LV_DRV_DISP_PAR_CS(val) /*par_cs_set(val)*/ /*Set the Parallel port's Chip select to 'val'*/ +#define LV_DRV_DISP_PAR_SLOW /*par_slow()*/ /*Set low speed on the parallel port*/ +#define LV_DRV_DISP_PAR_FAST /*par_fast()*/ /*Set high speed on the parallel port*/ +#define LV_DRV_DISP_PAR_WR_WORD(data) /*par_wr(data)*/ /*Write a word to the parallel port*/ +#define LV_DRV_DISP_PAR_WR_ARRAY(adr, n) /*par_wr_mem(adr,n)*/ /*Write 'n' bytes to Parallel ports from 'adr'*/ + +/*************************** + * INPUT DEVICE INTERFACE + ***************************/ + +/*---------- + * Common + *----------*/ +#define LV_DRV_INDEV_INCLUDE /*Dummy include by default*/ +#define LV_DRV_INDEV_RST(val) /*pin_x_set(val)*/ /*Set the reset pin to 'val'*/ +#define LV_DRV_INDEV_IRQ_READ 0 /*pn_x_read()*/ /*Read the IRQ pin*/ + +/*--------- + * SPI + *---------*/ +#define LV_DRV_INDEV_SPI_CS(val) /*spi_cs_set(val)*/ /*Set the SPI's Chip select to 'val'*/ +#define LV_DRV_INDEV_SPI_XCHG_BYTE(data) 0 /*spi_xchg(val)*/ /*Write 'val' to SPI and give the read value*/ + +/*--------- + * I2C + *---------*/ +#define LV_DRV_INDEV_I2C_START /*i2c_start()*/ /*Make an I2C start*/ +#define LV_DRV_INDEV_I2C_STOP /*i2c_stop()*/ /*Make an I2C stop*/ +#define LV_DRV_INDEV_I2C_RESTART /*i2c_restart()*/ /*Make an I2C restart*/ +#define LV_DRV_INDEV_I2C_WR(data) /*i2c_wr(data)*/ /*Write a byte to the I1C bus*/ +#define LV_DRV_INDEV_I2C_READ(last_read) 0 /*i2c_rd()*/ /*Read a byte from the I2C bud*/ + + +/********************* + * DISPLAY DRIVERS + *********************/ + +/*------------------- + * Monitor of PC + *-------------------*/ +#ifndef USE_MONITOR +# define USE_MONITOR 1 +#endif + +#if USE_MONITOR +# define MONITOR_HOR_RES LV_HOR_RES_MAX +# define MONITOR_VER_RES LV_VER_RES_MAX + +/* Scale window by this factor (useful when simulating small screens) */ +# define MONITOR_ZOOM 1 + +/* Used to test true double buffering with only address changing. + * Set LV_VDB_SIZE = (LV_HOR_RES * LV_VER_RES) and LV_VDB_DOUBLE = 1 and LV_COLOR_DEPTH = 32" */ +# define MONITOR_DOUBLE_BUFFERED 0 + +/*Eclipse: Visual Studio: */ +# define MONITOR_SDL_INCLUDE_PATH + +/*Different rendering might be used if running in a Virtual machine*/ +# define MONITOR_VIRTUAL_MACHINE 0 + +/*Open two windows to test multi display support*/ +# define MONITOR_DUAL 0 +#endif + +/*----------------------------------- + * Native Windows (including mouse) + *----------------------------------*/ +#ifndef USE_WINDOWS +# define USE_WINDOWS 0 +#endif + +#define USE_WINDOWS 0 +#if USE_WINDOWS +# define WINDOW_HOR_RES 480 +# define WINDOW_VER_RES 320 +#endif + +/*---------------- + * SSD1963 + *--------------*/ +#ifndef USE_SSD1963 +# define USE_SSD1963 0 +#endif + +#if USE_SSD1963 +# define SSD1963_HOR_RES LV_HOR_RES +# define SSD1963_VER_RES LV_VER_RES +# define SSD1963_HT 531 +# define SSD1963_HPS 43 +# define SSD1963_LPS 8 +# define SSD1963_HPW 10 +# define SSD1963_VT 288 +# define SSD1963_VPS 12 +# define SSD1963_FPS 4 +# define SSD1963_VPW 10 +# define SSD1963_HS_NEG 0 /*Negative hsync*/ +# define SSD1963_VS_NEG 0 /*Negative vsync*/ +# define SSD1963_ORI 0 /*0, 90, 180, 270*/ +# define SSD1963_COLOR_DEPTH 16 +#endif + +/*---------------- + * R61581 + *--------------*/ +#ifndef USE_R61581 +# define USE_R61581 0 +#endif + +#if USE_R61581 +# define R61581_HOR_RES LV_HOR_RES +# define R61581_VER_RES LV_VER_RES +# define R61581_HSPL 0 /*HSYNC signal polarity*/ +# define R61581_HSL 10 /*HSYNC length (Not Implemented)*/ +# define R61581_HFP 10 /*Horitontal Front poarch (Not Implemented)*/ +# define R61581_HBP 10 /*Horitontal Back poarch (Not Implemented */ +# define R61581_VSPL 0 /*VSYNC signal polarity*/ +# define R61581_VSL 10 /*VSYNC length (Not Implemented)*/ +# define R61581_VFP 8 /*Vertical Front poarch*/ +# define R61581_VBP 8 /*Vertical Back poarch */ +# define R61581_DPL 0 /*DCLK signal polarity*/ +# define R61581_EPL 1 /*ENABLE signal polarity*/ +# define R61581_ORI 0 /*0, 180*/ +# define R61581_LV_COLOR_DEPTH 16 /*Fix 16 bit*/ +#endif + +/*------------------------------ + * ST7565 (Monochrome, low res.) + *-----------------------------*/ +#ifndef USE_ST7565 +# define USE_ST7565 0 +#endif + +#if USE_ST7565 +/*No settings*/ +#endif /*USE_ST7565*/ + +/*----------------------------------------- + * Linux frame buffer device (/dev/fbx) + *-----------------------------------------*/ +#ifndef USE_FBDEV +# define USE_FBDEV 1 +#endif + +#if USE_FBDEV +# define FBDEV_PATH "/dev/fb0" +#endif + +/********************* + * INPUT DEVICES + *********************/ + +/*-------------- + * XPT2046 + *--------------*/ +#ifndef USE_XPT2046 +# define USE_XPT2046 0 +#endif + +#if USE_XPT2046 +# define XPT2046_HOR_RES 480 +# define XPT2046_VER_RES 320 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_INV 0 +#endif + +/*----------------- + * FT5406EE8 + *-----------------*/ +#ifndef USE_FT5406EE8 +# define USE_FT5406EE8 0 +#endif + +#if USE_FT5406EE8 +# define FT5406EE8_I2C_ADR 0x38 /*7 bit address*/ +#endif + +/*--------------- + * AD TOUCH + *--------------*/ +#ifndef USE_AD_TOUCH +# define USE_AD_TOUCH 0 +#endif + +#if USE_AD_TOUCH +/*No settings*/ +#endif + + +/*--------------------------------------- + * Mouse or touchpad on PC (using SDL) + *-------------------------------------*/ +#ifndef USE_MOUSE +# define USE_MOUSE 1 +#endif + +#if USE_MOUSE +/*No settings*/ +#endif + +/*------------------------------------------- + * Mousewheel as encoder on PC (using SDL) + *------------------------------------------*/ +#ifndef USE_MOUSEWHEEL +# define USE_MOUSEWHEEL 1 +#endif + +#if USE_MOUSEWHEEL +/*No settings*/ +#endif + +/*------------------------------------------------- + * Touchscreen as libinput interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_LIBINPUT +# define USE_LIBINPUT 0 +#endif + +#if USE_LIBINPUT +# define LIBINPUT_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +#endif /*USE_LIBINPUT*/ + +/*------------------------------------------------- + * Mouse or touchpad as evdev interface (for Linux based systems) + *------------------------------------------------*/ +#ifndef USE_EVDEV +# define USE_EVDEV 0 +#endif + +#if USE_EVDEV +# define EVDEV_NAME "/dev/input/event0" /*You can use the "evtest" Linux tool to get the list of devices and test them*/ +# define EVDEV_SWAP_AXES 0 /*Swap the x and y axes of the touchscreen*/ + +# define EVDEV_SCALE 0 /* Scale input, e.g. if touchscreen resolution does not match display resolution */ +# if EVDEV_SCALE +# define EVDEV_SCALE_HOR_RES (4096) /* Horizontal resolution of touchscreen */ +# define EVDEV_SCALE_VER_RES (4096) /* Vertical resolution of touchscreen */ +# endif /*EVDEV_SCALE*/ + +# define EVDEV_CALIBRATE 0 /*Scale and offset the touchscreen coordinates by using maximum and minimum values for each axis*/ +# if EVDEV_CALIBRATE +# define EVDEV_HOR_MIN 3800 /*If EVDEV_XXX_MIN > EVDEV_XXX_MAX the XXX axis is automatically inverted*/ +# define EVDEV_HOR_MAX 200 +# define EVDEV_VER_MIN 200 +# define EVDEV_VER_MAX 3800 +# endif /*EVDEV_SCALE*/ +#endif /*USE_EVDEV*/ + +/*------------------------------- + * Keyboard of a PC (using SDL) + *------------------------------*/ +#ifndef USE_KEYBOARD +# define USE_KEYBOARD 1 +#endif + +#if USE_KEYBOARD +/*No settings*/ +#endif + +#endif /*LV_DRV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt b/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt new file mode 100644 index 0000000..f43b9c1 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt @@ -0,0 +1,137 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8.2) +message ("vgl_native_ui_app...") +project (vgl_native_ui_app) + + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPLATFORM_NATIVE_LINUX -DUSE_MONITOR -DUSE_MOUSE=1") + +# Currently build as 64-bit by default. Set to "NO" to build 32-bit binaries. +set (BUILD_AS_64BIT_SUPPORT "YES") + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + if (${BUILD_AS_64BIT_SUPPORT} STREQUAL "YES") + # Add -fPIC flag if build as 64-bit + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + else () + add_definitions (-m32) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") + endif () +endif () + +set(lv_name lvgl) +set(LVGL_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../build/lvgl) +set(LVGL_DRIVER_DIR ${CMAKE_CURRENT_LIST_DIR}/lv-drivers) + +message(${LVGL_SOURCE_DIR}) +include( ExternalProject ) + +add_definitions(-DLV_CONF_INCLUDE_SIMPLE) + +SET (LVGL_SOURCES + ${LVGL_SOURCE_DIR}/lv_core/lv_group.c + ${LVGL_SOURCE_DIR}/lv_core/lv_indev.c + ${LVGL_SOURCE_DIR}/lv_core/lv_lang.c + ${LVGL_SOURCE_DIR}/lv_core/lv_obj.c + ${LVGL_SOURCE_DIR}/lv_core/lv_refr.c + ${LVGL_SOURCE_DIR}/lv_core/lv_style.c + ${LVGL_SOURCE_DIR}/lv_core/lv_vdb.c + + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_arc.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_img.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_label.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_line.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_rbasic.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_rect.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_triangle.c + ${LVGL_SOURCE_DIR}/lv_draw/lv_draw_vbasic.c + + ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_disp.c + ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_indev.c + ${LVGL_SOURCE_DIR}/lv_hal/lv_hal_tick.c + + ${LVGL_SOURCE_DIR}/lv_misc/lv_anim.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_area.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_circ.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_color.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_font.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_fs.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_gc.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_ll.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_log.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_math.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_mem.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_task.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_templ.c + ${LVGL_SOURCE_DIR}/lv_misc/lv_txt.c + + ${LVGL_SOURCE_DIR}/lv_objx/lv_arc.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_bar.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_btn.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_btnm.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_calendar.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_canvas.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_cb.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_chart.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_cont.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_ddlist.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_gauge.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_img.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_imgbtn.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_kb.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_label.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_led.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_line.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_list.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_lmeter.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_mbox.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_objx_templ.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_page.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_preload.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_roller.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_slider.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_spinbox.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_sw.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_ta.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_table.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_tabview.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_tileview.c + ${LVGL_SOURCE_DIR}/lv_objx/lv_win.c + + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_alien.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_default.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_material.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_mono.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_nemo.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_night.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_templ.c + ${LVGL_SOURCE_DIR}/lv_themes/lv_theme_zen.c + + ${LVGL_SOURCE_DIR}/lv_fonts/lv_font_builtin.c + ${LVGL_SOURCE_DIR}/lv_fonts/lv_font_dejavu_20.c + ${LVGL_DRIVER_DIR}/linux_display_indev.c + ${LVGL_DRIVER_DIR}/indev/mouse.c + +) +SET(SOURCES + ${LVGL_SOURCES} + ${CMAKE_CURRENT_LIST_DIR}/main.c + ) +include_directories( + ${LVGL_DRIVER_DIR} + ${LVGL_DRIVER_DIR}/display + ${LVGL_DRIVER_DIR}/indev + ${LVGL_SOURCE_DIR} + ${LVGL_SOURCE_DIR}/.. + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/../lv_config +) +add_executable(vgl_native_ui_app ${SOURCES} ) +target_link_libraries( vgl_native_ui_app -lSDL2) diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in b/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in new file mode 100644 index 0000000..7cc57c3 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/CMakeLists.txt.in @@ -0,0 +1,18 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8.2) + +project(lvgl_download NONE) + +include(ExternalProject) +ExternalProject_Add(${lv_name} + GIT_REPOSITORY https://github.com/littlevgl/lvgl.git + GIT_TAG v5.3 + BINARY_DIR "" + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/../build/lvgl" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore new file mode 100644 index 0000000..2372cca --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/.gitignore @@ -0,0 +1 @@ +**/*.o \ No newline at end of file diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h new file mode 100644 index 0000000..3aa6e16 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/display_indev.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef DISPLAY_INDEV_H_ +#define DISPLAY_INDEV_H_ +#include +#include +#include "mouse.h" +#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 int time_get_ms(); + +#endif diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c new file mode 100644 index 0000000..58f7be0 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.c @@ -0,0 +1,95 @@ +/** + * @file mouse.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "mouse.h" +#if USE_MOUSE != 0 + +/********************* + * DEFINES + *********************/ +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static bool left_button_down = false; +static int16_t last_x = 0; +static int16_t last_y = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the mouse + */ +void mouse_init(void) +{ + +} + +/** + * Get the current position and state of the mouse + * @param data store the mouse data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool mouse_read(lv_indev_data_t * data) +{ + /*Store the collected data*/ + data->point.x = last_x; + data->point.y = last_y; + data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + + return false; +} + +/** + * It will be called from the main SDL thread + */ +void mouse_handler(SDL_Event * event) +{ + switch (event->type) { + case SDL_MOUSEBUTTONUP: + if (event->button.button == SDL_BUTTON_LEFT) + left_button_down = false; + break; + case SDL_MOUSEBUTTONDOWN: + if (event->button.button == SDL_BUTTON_LEFT) { + left_button_down = true; + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + } + break; + case SDL_MOUSEMOTION: + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + + break; + } + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h new file mode 100644 index 0000000..2aedd0f --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/indev/mouse.h @@ -0,0 +1,69 @@ +/** + * @file mouse.h + * + */ + +#ifndef MOUSE_H +#define MOUSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "lv_drv_conf.h" + +#if USE_MOUSE + +#include +#include +#include "lvgl/lv_hal/lv_hal_indev.h" + +#ifndef MONITOR_SDL_INCLUDE_PATH +#define MONITOR_SDL_INCLUDE_PATH +#endif + +#include MONITOR_SDL_INCLUDE_PATH + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the mouse + */ +void mouse_init(void); +/** + * Get the current position and state of the mouse + * @param data store the mouse data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool mouse_read(lv_indev_data_t * data); + +/** + * It will be called from the main SDL thread + */ +void mouse_handler(SDL_Event *event); + +/********************** + * MACROS + **********************/ + +#endif /* USE_MOUSE */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* MOUSE_H */ diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c new file mode 100644 index 0000000..cf6ff82 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/linux_display_indev.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "display_indev.h" +#include "sys/time.h" +#include "SDL2/SDL.h" +#define MONITOR_HOR_RES 320 +#define MONITOR_VER_RES 240 +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif +#define SDL_REFR_PERIOD 50 +void monitor_sdl_init(void); +void monitor_sdl_refr_core(void); +void monitor_sdl_clean_up(void); +static uint32_t tft_fb[MONITOR_HOR_RES * MONITOR_VER_RES]; + +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) +{ + 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); + } + */ +} +int time_get_ms() +{ + static struct timeval tv; + gettimeofday(&tv, NULL); + long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; + + return (int) time_in_mill; +} + +SDL_Window * window; +SDL_Renderer * renderer; +SDL_Texture * texture; +static volatile bool sdl_inited = false; +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) +{ + /*Return if the area is out the screen*/ + if (x2 < 0 || y2 < 0 || x1 > MONITOR_HOR_RES - 1 + || y1 > MONITOR_VER_RES - 1) { + return; + } + + int32_t y; + uint32_t w = x2 - x1 + 1; + for (y = y1; y <= y2; y++) { + memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color_p, + w * sizeof(lv_color_t)); + + color_p += w; + } + sdl_refr_qry = true; + + /*IMPORTANT! It must be called to tell the system the flush is ready*/ + +} + +/** + * Fill out the marked area with a color + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @param color fill color + */ +void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t color) +{ + /*Return if the area is out the screen*/ + if (x2 < 0) + return; + if (y2 < 0) + return; + if (x1 > MONITOR_HOR_RES - 1) + return; + if (y1 > MONITOR_VER_RES - 1) + return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; + + int32_t x; + int32_t y; + 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++) { + tft_fb[y * MONITOR_HOR_RES + x] = color32; + } + } + + sdl_refr_qry = true; +} + +/** + * Put a color map to the marked area + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @param color_p 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) +{ + /*Return if the area is out the screen*/ + if (x2 < 0) + return; + if (y2 < 0) + return; + if (x1 > MONITOR_HOR_RES - 1) + return; + if (y1 > MONITOR_VER_RES - 1) + return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; + + int32_t x; + int32_t y; + + 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++; + } + + color_p += x2 - act_x2; + } + + sdl_refr_qry = true; +} + +void display_init(void) +{ +} + +void display_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + const lv_color_t * color_p) +{ + monitor_flush(x1, y1, x2, y2, color_p); +} +void display_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t color_p) +{ + monitor_fill(x1, y1, x2, y2, color_p); +} +void display_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + const lv_color_t * color_p) +{ + monitor_map(x1, y1, x2, y2, color_p); +} + +bool display_input_read(lv_indev_data_t * data) +{ + return mouse_read(data); +} + +void display_deinit(void) +{ + +} + +int monitor_sdl_refr_thread(void * param) +{ + (void) param; + + /*If not OSX initialize SDL in the Thread*/ + monitor_sdl_init(); + /*Run until quit event not arrives*/ + while (sdl_quit_qry == false) { + /*Refresh handling*/ + monitor_sdl_refr_core(); + } + + monitor_sdl_clean_up(); + exit(0); + + return 0; +} + +void monitor_sdl_refr_core(void) +{ + if (sdl_refr_qry != false) { + sdl_refr_qry = false; + + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + SDL_RenderClear(renderer); + /*Test: Draw a background to test transparent screens (LV_COLOR_SCREEN_TRANSP)*/ +// SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff); +// SDL_Rect r; +// r.x = 0; r.y = 0; r.w = MONITOR_HOR_RES; r.w = MONITOR_VER_RES; +// SDL_RenderDrawRect(renderer, &r); + /*Update the renderer with the texture containing the rendered image*/ + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + } + + SDL_Event event; + while (SDL_PollEvent(&event)) { +#if USE_MOUSE != 0 + mouse_handler(&event); +#endif + if ((&event)->type == SDL_WINDOWEVENT) { + switch ((&event)->window.event) { +#if SDL_VERSION_ATLEAST(2, 0, 5) + case SDL_WINDOWEVENT_TAKE_FOCUS: +#endif + case SDL_WINDOWEVENT_EXPOSED: + + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + break; + default: + break; + } + } + } + + /*Sleep some time*/ + SDL_Delay(SDL_REFR_PERIOD); + +} +int quit_filter(void * userdata, SDL_Event * event) +{ + (void) userdata; + + if (event->type == SDL_QUIT) { + sdl_quit_qry = true; + } + + return 1; +} + +void monitor_sdl_clean_up(void) +{ + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +void monitor_sdl_init(void) +{ + /*Initialize the SDL*/ + SDL_Init(SDL_INIT_VIDEO); + + SDL_SetEventFilter(quit_filter, NULL); + + window = SDL_CreateWindow("TFT Simulator", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + MONITOR_HOR_RES * MONITOR_ZOOM, MONITOR_VER_RES * MONITOR_ZOOM, 0); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ + + renderer = SDL_CreateRenderer(window, -1, 0); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, MONITOR_HOR_RES, MONITOR_VER_RES); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + /*Initialize the frame buffer to gray (77 is an empirical value) */ + memset(tft_fb, 0x44, MONITOR_HOR_RES * MONITOR_VER_RES * sizeof(uint32_t)); + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + sdl_refr_qry = true; + sdl_inited = true; +} + +void display_SDL_init() +{ + SDL_CreateThread(monitor_sdl_refr_thread, "sdl_refr", NULL); + while (sdl_inited == false) + ; /*Wait until 'sdl_refr' initializes the SDL*/ +} + diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h new file mode 100644 index 0000000..41d9238 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/lv-drivers/system_header.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +int time_get_ms(); diff --git a/wamr/samples/littlevgl/vgl-native-ui-app/main.c b/wamr/samples/littlevgl/vgl-native-ui-app/main.c new file mode 100644 index 0000000..4d403c2 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-native-ui-app/main.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * @file main + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include +#include "lvgl/lvgl.h" +#include "display_indev.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void hal_init(void); +//static int tick_thread(void * data); +//static void memory_monitor(void * param); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +uint32_t count = 0; +char count_str[11] = { 0 }; +lv_obj_t *hello_world_label; +lv_obj_t *count_label; +lv_obj_t * btn1; + +lv_obj_t * label_count1; +int label_count1_value = 0; +char label_count1_str[11] = { 0 }; +static lv_res_t btn_rel_action(lv_obj_t * btn) +{ + label_count1_value++; + snprintf(label_count1_str, sizeof(label_count1_str), + "%d", label_count1_value); + lv_label_set_text(label_count1, label_count1_str); + return LV_RES_OK; +} + + +int main() +{ + void display_SDL_init(); + display_SDL_init(); + + /*Initialize LittlevGL*/ + lv_init(); + + /*Initialize the HAL (display, input devices, tick) for LittlevGL*/ + hal_init(); + + hello_world_label = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(hello_world_label, "Hello world!"); + lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + + count_label = lv_label_create(lv_scr_act(), NULL); + lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); + + btn1 = lv_btn_create(lv_scr_act(), NULL); /*Create a button on the currently loaded screen*/ + lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, btn_rel_action); /*Set function to be called when the button is released*/ + lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 20); /*Align below the label*/ + + /*Create a label on the button*/ + lv_obj_t * btn_label = lv_label_create(btn1, NULL); + lv_label_set_text(btn_label, "Click ++"); + + label_count1 = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(label_count1, "0"); + lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + while(1) { + /* Periodically call the lv_task handler. + * It could be done in a timer interrupt or an OS task too.*/ + if ((count % 100) == 0) { + snprintf(count_str, sizeof(count_str), "%d", count/ 100); + lv_label_set_text(count_label, count_str); + } + lv_task_handler(); + ++count; + usleep(10 * 1000); /*Just to let the system breath*/ + } + + return 0; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * 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) +{ + 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) +{ + 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); +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_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; +#endif + lv_disp_drv_register(&disp_drv); + + /* Add the mouse as input device + * Use the 'mouse' driver which reads the PC's mouse*/ +// mouse_init(); + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); /*Basic initialization*/ + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read = display_input_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ + lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv); + +} + diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt b/wamr/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt new file mode 100644 index 0000000..2c4c810 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (vgl_wasm_runtime) + +set (WAMR_BUILD_PLATFORM "linux") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +################ wamr runtime settings ################ + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../..) + +## use library and headers in the SDK +link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/lib) +include_directories( + ${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/include + ${WAMR_ROOT_DIR}/wamr-sdk/out/littlevgl/runtime-sdk/include/bi-inc/deps + ${WAMR_ROOT_DIR}/core/shared/utils + ${WAMR_ROOT_DIR}/core/shared/platform/${WAMR_BUILD_PLATFORM} +) + +############### application related ############### +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/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/wamr/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h new file mode 100644 index 0000000..c87c3df --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/display_indev.h @@ -0,0 +1,90 @@ +#ifndef DISPLAY_INDEV_H_ +#define DISPLAY_INDEV_H_ +#include +#include +#include +#include "bh_platform.h" +#include "wasm_export.h" + +#define USE_MOUSE 1 +typedef union { + struct { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; + }; + uint32_t full; +} lv_color32_t; + +typedef lv_color32_t lv_color_t; +typedef uint8_t lv_indev_state_t; +typedef int16_t lv_coord_t; +typedef uint8_t lv_opa_t; +typedef struct { + lv_coord_t x; + lv_coord_t y; +} lv_point_t; + +typedef struct { + union { + lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/ + uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/ + uint32_t btn; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/ + int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/ + }; + void *user_data; /*'lv_indev_drv_t.priv' for this driver*/ + lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/ +} lv_indev_data_t; + +enum { + LV_INDEV_STATE_REL = 0, LV_INDEV_STATE_PR +}; +enum { + LV_OPA_TRANSP = 0, + LV_OPA_0 = 0, + LV_OPA_10 = 25, + LV_OPA_20 = 51, + LV_OPA_30 = 76, + LV_OPA_40 = 102, + LV_OPA_50 = 127, + LV_OPA_60 = 153, + LV_OPA_70 = 178, + LV_OPA_80 = 204, + LV_OPA_90 = 229, + LV_OPA_100 = 255, + LV_OPA_COVER = 255, +}; + +extern void xpt2046_init(void); + +extern bool touchscreen_read(lv_indev_data_t *data); + +extern bool mouse_read(lv_indev_data_t *data); + +extern void display_init(void); + +extern void display_deinit(wasm_exec_env_t exec_env); + +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, + 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); + +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); + +extern bool display_input_read(wasm_exec_env_t exec_env, void *data); + +void display_vdb_write(wasm_exec_env_t exec_env, + 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/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c new file mode 100644 index 0000000..93ee7a4 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/display_indev.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "display_indev.h" +#include "SDL2/SDL.h" +#include "sys/time.h" +#include "wasm_export.h" +#include "app_manager_export.h" + +#define MONITOR_HOR_RES 320 +#define MONITOR_VER_RES 240 +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif +#define SDL_REFR_PERIOD 50 +void monitor_sdl_init(void); +void monitor_sdl_refr_core(void); +void monitor_sdl_clean_up(void); + +static uint32_t tft_fb[MONITOR_HOR_RES * MONITOR_VER_RES]; + +int +time_get_ms(wasm_exec_env_t exec_env) +{ + static struct timeval tv; + gettimeofday(&tv, NULL); + long long time_in_mill = (tv.tv_sec) * 1000 + (tv.tv_usec) / 1000; + + return (int) time_in_mill; +} + +SDL_Window * window; +SDL_Renderer * renderer; +SDL_Texture * texture; +static volatile bool sdl_inited = false; +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) +{ + /*Return if the area is out the screen*/ + if (x2 < 0 || y2 < 0 || x1 > MONITOR_HOR_RES - 1 + || y1 > MONITOR_VER_RES - 1) { + return; + } + + int32_t y; + uint32_t w = x2 - x1 + 1; + + for (y = y1; y <= y2; y++) { + memcpy(&tft_fb[y * MONITOR_HOR_RES + x1], color, + w * sizeof(lv_color_t)); + + color += w; + } + sdl_refr_qry = true; + + /*IMPORTANT! It must be called to tell the system the flush is ready*/ +} + +/** + * Fill out the marked area with a color + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @param color fill color + */ +void monitor_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t *color) +{ + /*Return if the area is out the screen*/ + if (x2 < 0) + return; + if (y2 < 0) + return; + if (x1 > MONITOR_HOR_RES - 1) + return; + if (y1 > MONITOR_VER_RES - 1) + return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; + + int32_t x; + int32_t y; + 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++) { + tft_fb[y * MONITOR_HOR_RES + x] = color32; + } + } + + sdl_refr_qry = true; +} + +/** + * Put a color map to the marked area + * @param x1 left coordinate + * @param y1 top coordinate + * @param x2 right coordinate + * @param y2 bottom coordinate + * @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) +{ + /*Return if the area is out the screen*/ + if (x2 < 0) + return; + if (y2 < 0) + return; + if (x1 > MONITOR_HOR_RES - 1) + return; + if (y1 > MONITOR_VER_RES - 1) + return; + + /*Truncate the area to the screen*/ + int32_t act_x1 = x1 < 0 ? 0 : x1; + int32_t act_y1 = y1 < 0 ? 0 : y1; + int32_t act_x2 = x2 > MONITOR_HOR_RES - 1 ? MONITOR_HOR_RES - 1 : x2; + int32_t act_y2 = y2 > MONITOR_VER_RES - 1 ? MONITOR_VER_RES - 1 : y2; + + int32_t x; + int32_t y; + + for (y = act_y1; y <= act_y2; y++) { + for (x = act_x1; x <= act_x2; x++) { + tft_fb[y * MONITOR_HOR_RES + x] = color->full; //lv_color_to32(*color); + color++; + } + + color += x2 - act_x2; + } + + sdl_refr_qry = true; +} + + +void +display_init(void) +{ +} + +void +display_flush(wasm_exec_env_t exec_env, + int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t *color) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + + 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) +{ + 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) +{ + monitor_map(x1, y1, x2, y2, color); +} + +typedef struct display_input_data { + lv_point_t point; + uint32 user_data_offset; + uint8 state; +} display_input_data; + +bool +display_input_read(wasm_exec_env_t exec_env, + 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_native_addr(module_inst, + data_app, + sizeof(display_input_data))) + return false; + + + lv_indev_data_t data = {0}; + + ret = mouse_read(&data); + + data_app->point = data.point; + data_app->user_data_offset = + wasm_runtime_addr_native_to_app(module_inst, data.user_data); + data_app->state = data.state; + + return ret; +} + +void +display_deinit(wasm_exec_env_t exec_env) +{ +} + +void +display_vdb_write(wasm_exec_env_t exec_env, + 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); + 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 *)buf_xy = *color; +} + +int monitor_sdl_refr_thread(void * param) +{ + (void) param; + + /*If not OSX initialize SDL in the Thread*/ + monitor_sdl_init(); + /*Run until quit event not arrives*/ + while (sdl_quit_qry == false) { + /*Refresh handling*/ + monitor_sdl_refr_core(); + } + + monitor_sdl_clean_up(); + exit(0); + + return 0; +} +extern void mouse_handler(SDL_Event *event); +void monitor_sdl_refr_core(void) +{ + if (sdl_refr_qry != false) { + sdl_refr_qry = false; + + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + SDL_RenderClear(renderer); + /*Update the renderer with the texture containing the rendered image*/ + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + } + + SDL_Event event; + while (SDL_PollEvent(&event)) { + + mouse_handler(&event); + + if ((&event)->type == SDL_WINDOWEVENT) { + switch ((&event)->window.event) { +#if SDL_VERSION_ATLEAST(2, 0, 5) + case SDL_WINDOWEVENT_TAKE_FOCUS: +#endif + case SDL_WINDOWEVENT_EXPOSED: + + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + break; + default: + break; + } + } + } + + /*Sleep some time*/ + SDL_Delay(SDL_REFR_PERIOD); + +} +int quit_filter(void * userdata, SDL_Event * event) +{ + (void) userdata; + + if (event->type == SDL_QUIT) { + sdl_quit_qry = true; + } + + return 1; +} + +void monitor_sdl_clean_up(void) +{ + SDL_DestroyTexture(texture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +void monitor_sdl_init(void) +{ + /*Initialize the SDL*/ + SDL_Init(SDL_INIT_VIDEO); + + SDL_SetEventFilter(quit_filter, NULL); + + window = SDL_CreateWindow("TFT Simulator", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + MONITOR_HOR_RES * MONITOR_ZOOM, MONITOR_VER_RES * MONITOR_ZOOM, 0); /*last param. SDL_WINDOW_BORDERLESS to hide borders*/ + + renderer = SDL_CreateRenderer(window, -1, 0); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, MONITOR_HOR_RES, MONITOR_VER_RES); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + + /*Initialize the frame buffer to gray (77 is an empirical value) */ + memset(tft_fb, 0x44, MONITOR_HOR_RES * MONITOR_VER_RES * sizeof(uint32_t)); + SDL_UpdateTexture(texture, NULL, tft_fb, + MONITOR_HOR_RES * sizeof(uint32_t)); + sdl_refr_qry = true; + sdl_inited = true; +} + +void display_SDL_init() +{ + SDL_CreateThread(monitor_sdl_refr_thread, "sdl_refr", NULL); + while (sdl_inited == false) + ; /*Wait until 'sdl_refr' initializes the SDL*/ +} + diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c new file mode 100644 index 0000000..9b5fa28 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/iwasm_main.c @@ -0,0 +1,513 @@ + +#ifndef CONNECTION_UART +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runtime_lib.h" +#include "runtime_timer.h" +#include "native_interface.h" +#include "app_manager_export.h" +#include "bh_platform.h" +#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 +#define SA struct sockaddr +static char *host_address = "127.0.0.1"; +static int port = 8888; +#else +static char *uart_device = "/dev/ttyS2"; +static int baudrate = B115200; +#endif + +extern void init_sensor_framework(); +extern void exit_sensor_framework(); +extern void exit_connection_framework(); +extern int aee_host_msg_callback(void *msg, uint16_t msg_len); +extern bool init_connection_framework(); + +#ifndef CONNECTION_UART +int listenfd = -1; +int sockfd = -1; +static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; +#else +int uartfd = -1; +#endif + +#ifndef CONNECTION_UART +static bool server_mode = false; + +// Function designed for chat between client and server. +void* func(void* arg) +{ + char buff[MAX]; + int n; + struct sockaddr_in servaddr; + + while (1) { + if (sockfd != -1) + close(sockfd); + // socket create and verification + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + printf("socket creation failed...\n"); + return NULL; + } else + printf("Socket successfully created..\n"); + bzero(&servaddr, sizeof(servaddr)); + // assign IP, PORT + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(host_address); + servaddr.sin_port = htons(port); + + // connect the client socket to server socket + if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { + printf("connection with the server failed...\n"); + sleep(10); + continue; + } else { + printf("connected to the server..\n"); + } + + // infinite loop for chat + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + // print buffer which contains the client contents + //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); + + // socket disconnected + if (n <= 0) + break; + + aee_host_msg_callback(buff, n); + } + } + + // After chatting close the socket + close(sockfd); +} + +static bool host_init() +{ + return true; +} + +int host_send(void * ctx, const char *buf, int size) +{ + int ret; + + if (pthread_mutex_trylock(&sock_lock) == 0) { + if (sockfd == -1) { + pthread_mutex_unlock(&sock_lock); + return 0; + } + + ret = write(sockfd, buf, size); + + pthread_mutex_unlock(&sock_lock); + return ret; + } + + return -1; +} + +void host_destroy() +{ + if (server_mode) + close(listenfd); + + pthread_mutex_lock(&sock_lock); + close(sockfd); + pthread_mutex_unlock(&sock_lock); +} + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy + }; + +void* func_server_mode(void* arg) +{ + int clilent; + struct sockaddr_in serv_addr, cli_addr; + int n; + char buff[MAX]; + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, 0); + + /* First call to socket() function */ + listenfd = socket(AF_INET, SOCK_STREAM, 0); + + if (listenfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + /* Initialize socket structure */ + bzero((char *) &serv_addr, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + /* Now bind the host address using bind() call.*/ + if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + exit(1); + } + + listen(listenfd, 5); + clilent = sizeof(cli_addr); + + while (1) { + pthread_mutex_lock(&sock_lock); + + sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); + + pthread_mutex_unlock(&sock_lock); + + if (sockfd < 0) { + perror("ERROR on accept"); + exit(1); + } + + printf("connection established!\n"); + + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + + // socket disconnected + if (n <= 0) { + pthread_mutex_lock(&sock_lock); + close(sockfd); + sockfd = -1; + pthread_mutex_unlock(&sock_lock); + + sleep(2); + break; + } + + aee_host_msg_callback(buff, n); + } + } +} + +#else +static int parse_baudrate(int baud) +{ + switch (baud) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} +static bool uart_init(const char *device, int baudrate, int *fd) +{ + int uart_fd; + struct termios uart_term; + + uart_fd = open(device, O_RDWR | O_NOCTTY); + + if (uart_fd <= 0) + return false; + + memset(&uart_term, 0, sizeof(uart_term)); + uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; + uart_term.c_iflag = IGNPAR; + uart_term.c_oflag = 0; + + /* set noncanonical mode */ + uart_term.c_lflag = 0; + uart_term.c_cc[VTIME] = 30; + uart_term.c_cc[VMIN] = 1; + tcflush(uart_fd, TCIFLUSH); + + if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { + close(uart_fd); + return false; + } + + *fd = uart_fd; + + return true; +} + +static void *func_uart_mode(void *arg) +{ + int n; + char buff[MAX]; + + if (!uart_init(uart_device, baudrate, &uartfd)) { + printf("open uart fail! %s\n", uart_device); + return NULL; + } + + for (;;) { + bzero(buff, MAX); + + n = read(uartfd, buff, sizeof(buff)); + + if (n <= 0) { + close(uartfd); + uartfd = -1; + break; + } + + aee_host_msg_callback(buff, n); + } + + return NULL; +} + +static int uart_send(void * ctx, const char *buf, int size) +{ + int ret; + + ret = write(uartfd, buf, size); + + return ret; +} + +static void uart_destroy() +{ + close(uartfd); +} + +static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; + +#endif + +#ifdef __x86_64__ +static char global_heap_buf[400 * 1024] = { 0 }; +#else +static char global_heap_buf[270 * 1024] = { 0 }; +#endif + +static void showUsage() +{ +#ifndef CONNECTION_UART + printf("Usage:\n"); + printf("\nWork as TCP server mode:\n"); + printf("\tvgl_wasm_runtime -s|--server_mode -p|--port \n"); + printf("where\n"); + printf("\t represents the port that would be listened on and the default is 8888\n"); + printf("\nWork as TCP client mode:\n"); + printf("\tvgl_wasm_runtime -a|--host_address -p|--port \n"); + printf("where\n"); + printf("\t represents the network address of host and the default is 127.0.0.1\n"); + printf("\t represents the listen port of host and the default is 8888\n"); +#else + printf("Usage:\n"); + printf("\tvgl_wasm_runtime -u -b \n\n"); + printf("where\n"); + printf("\t represents the UART device name and the default is /dev/ttyS2\n"); + printf("\t represents the UART device baudrate and the default is 115200\n"); +#endif + printf("\nNote:\n"); + printf("\tUse -w|--wasi_root to specify the root dir (default to '.') of WASI wasm modules. \n"); +} + +static bool parse_args(int argc, char *argv[]) +{ + int c; + + while (1) { + int optIndex = 0; + static struct option longOpts[] = { +#ifndef CONNECTION_UART + { "server_mode", no_argument, NULL, 's' }, + { "host_address", required_argument, NULL, 'a' }, + { "port", required_argument, NULL, 'p' }, +#else + { "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 } + }; + + c = getopt_long(argc, argv, "sa:p:u:b:w:h", longOpts, &optIndex); + if (c == -1) + break; + + switch (c) { +#ifndef CONNECTION_UART + case 's': + server_mode = true; + break; + case 'a': + host_address = optarg; + printf("host address: %s\n", host_address); + break; + case 'p': + port = atoi(optarg); + printf("port: %d\n", port); + break; +#else + case 'u': + uart_device = optarg; + printf("uart device: %s\n", uart_device); + break; + case 'b': + baudrate = parse_baudrate(atoi(optarg)); + 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; + default: + showUsage(); + return false; + } + } + + return true; +} + +static NativeSymbol native_symbols[] = { + 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[]) +{ + RuntimeInitArgs init_args; + korp_tid tid; + uint32 n_native_symbols; + + if (!parse_args(argc, argv)) + return -1; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + init_args.native_module_name = "env"; + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_symbols = native_symbols; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + if (!init_connection_framework()) { + goto fail1; + } + + extern void display_SDL_init(); + display_SDL_init(); + + init_sensor_framework(); + + // timer manager + init_wasm_timer(); + +#ifndef CONNECTION_UART + if (server_mode) + os_thread_create(&tid, func_server_mode, NULL, + BH_APPLET_PRESERVED_STACK_SIZE); + else + os_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#else + os_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#endif + + app_manager_startup(&interface); + + exit_wasm_timer(); + exit_sensor_framework(); + exit_connection_framework(); + +fail1: + wasm_runtime_destroy(); + + return -1; +} diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c new file mode 100644 index 0000000..8c94d0e --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/main.c @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +extern int iwasm_main(int argc, char *argv[]); +int main(int argc, char *argv[]) +{ + return iwasm_main(argc,argv); +} diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c new file mode 100644 index 0000000..f5d071d --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/linux/mouse.c @@ -0,0 +1,96 @@ +/** + * @file mouse.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "display_indev.h" +#include "SDL2/SDL.h" +#if USE_MOUSE != 0 + +/********************* + * DEFINES + *********************/ +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static bool left_button_down = false; +static int16_t last_x = 0; +static int16_t last_y = 0; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the mouse + */ +void mouse_init(void) +{ + +} + +/** + * Get the current position and state of the mouse + * @param data store the mouse data here + * @return false: because the points are not buffered, so no more data to be read + */ +bool mouse_read(lv_indev_data_t * data) +{ + /*Store the collected data*/ + data->point.x = last_x; + data->point.y = last_y; + data->state = left_button_down ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL; + + return false; +} + +/** + * It will be called from the main SDL thread + */ +void mouse_handler(SDL_Event * event) +{ + switch (event->type) { + case SDL_MOUSEBUTTONUP: + if (event->button.button == SDL_BUTTON_LEFT) + left_button_down = false; + break; + case SDL_MOUSEBUTTONDOWN: + if (event->button.button == SDL_BUTTON_LEFT) { + left_button_down = true; + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + } + break; + case SDL_MOUSEMOTION: + last_x = event->motion.x / MONITOR_ZOOM; + last_y = event->motion.y / MONITOR_ZOOM; + + break; + } + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE new file mode 100644 index 0000000..8f71f43 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c new file mode 100644 index 0000000..11d3562 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.c @@ -0,0 +1,339 @@ +/** + * @file XPT2046.c +*/ +/********************* + * INCLUDES + *********************/ +#include "XPT2046.h" +#include "board_config.h" +#include "stdio.h" +#include +#include "drivers/spi.h" + +#include "zephyr.h" +#include "kernel.h" + +#if USE_XPT2046 + +#include + +#define abs(x) ((x) < 0 ? -(x) : (x)) + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y); +#if 0 +static void xpt2046_avg(int16_t * x, int16_t * y); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +int16_t avg_buf_x[XPT2046_AVG]; +int16_t avg_buf_y[XPT2046_AVG]; +uint8_t avg_last; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the XPT2046 + */ +struct device *input_dev; + +struct spi_config spi_conf_xpt2046; +struct spi_cs_control xpt2046_cs_ctrl; +struct device *xpt2046_pen_gpio_dev; +static struct gpio_callback gpio_cb; +lv_indev_data_t touch_point; +lv_indev_data_t last_touch_point; + +#define TOUCH_READ_THREAD_STACK_SIZE 4096 +static K_THREAD_STACK_DEFINE(touch_read_thread_stack, TOUCH_READ_THREAD_STACK_SIZE); +static struct k_thread touch_thread_data; +static struct k_sem sem_touch_read; + +K_MUTEX_DEFINE( spi_display_touch_mutex); + +int cnt = 0; +int touch_read_times = 0; +int last_pen_interrupt_time = 0; +void xpt2046_pen_gpio_callback(struct device *port, struct gpio_callback *cb, + u32_t pins) +{ + cnt++; + if ((k_uptime_get_32() - last_pen_interrupt_time) > 500) { + k_sem_give(&sem_touch_read); + touch_read_times++; + last_pen_interrupt_time = k_uptime_get_32(); + } + +} + +void disable_pen_interrupt() +{ + int ret = 0; + ret = gpio_disable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret != 0) { + printf("gpio_pin_configure GPIO_INPUT failed\n"); + } +} +void enable_pen_interrupt() +{ + int ret = 0; + ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret != 0) { + printf("gpio_pin_configure failed\n"); + } +} + +void touch_screen_read_thread() +{ + int i; + bool ret = false; + + for (;;) { + k_sem_take(&sem_touch_read, K_FOREVER); + memset(&last_touch_point, 0, sizeof(lv_indev_data_t)); + memset(&touch_point, 0, sizeof(lv_indev_data_t)); + memset(avg_buf_x, 0, sizeof(avg_buf_x)); + memset(avg_buf_y, 0, sizeof(avg_buf_y)); + k_mutex_lock(&spi_display_touch_mutex, K_FOREVER); + disable_pen_interrupt(); + for (i = 0; i < 100; i++) { + ret = xpt2046_read(&touch_point); + if (ret) { + if ((abs(last_touch_point.point.x - touch_point.point.x) < 4) + && (abs(last_touch_point.point.y - touch_point.point.y) + < 4)) { + break; + } + last_touch_point = touch_point; + + } + } + enable_pen_interrupt(); + k_mutex_unlock(&spi_display_touch_mutex); + } +} + +void xpt2046_init(void) +{ + int ret; + input_dev = device_get_binding(XPT2046_SPI_DEVICE_NAME); + + if (input_dev == NULL) { + printf("device not found. Aborting test."); + return; + } + memset((void *) &touch_point, 0, sizeof(lv_indev_data_t)); + + spi_conf_xpt2046.frequency = XPT2046_SPI_MAX_FREQUENCY; + spi_conf_xpt2046.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); + spi_conf_xpt2046.slave = 0; + spi_conf_xpt2046.cs = NULL; +#ifdef XPT2046_CS_GPIO_CONTROLLER + xpt2046_cs_ctrl.gpio_dev = device_get_binding(XPT2046_CS_GPIO_CONTROLLER); + if (xpt2046_cs_ctrl.gpio_dev == NULL) { + printk("Cannot find %s!\n", XPT2046_CS_GPIO_CONTROLLER); + return; + } + gpio_pin_configure(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, + GPIO_OUTPUT); + gpio_pin_set(xpt2046_cs_ctrl.gpio_dev, XPT2046_CS_GPIO_PIN, 1); + xpt2046_cs_ctrl.gpio_pin = XPT2046_CS_GPIO_PIN; + xpt2046_cs_ctrl.delay = 0; + spi_conf_xpt2046.cs = &xpt2046_cs_ctrl; + +#endif + +#ifdef XPT2046_PEN_GPIO_CONTROLLER + + xpt2046_pen_gpio_dev = device_get_binding(XPT2046_PEN_GPIO_CONTROLLER); + if (!xpt2046_pen_gpio_dev) { + printk("Cannot find %s!\n", XPT2046_PEN_GPIO_CONTROLLER); + return; + } + /* Setup GPIO input */ + ret = gpio_pin_configure(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN, + (GPIO_INPUT | GPIO_INT_ENABLE | GPIO_INT_EDGE + | GPIO_INT_LOW_0 | GPIO_INT_DEBOUNCE) + ); + if (ret) { + printk("Error configuring pin %d!\n", XPT2046_PEN_GPIO_PIN); + } + + gpio_init_callback(&gpio_cb, xpt2046_pen_gpio_callback, + BIT(XPT2046_PEN_GPIO_PIN)); + + ret = gpio_add_callback(xpt2046_pen_gpio_dev, &gpio_cb); + if (ret) { + printk("gpio_add_callback error\n"); + } + ret = gpio_enable_callback(xpt2046_pen_gpio_dev, XPT2046_PEN_GPIO_PIN); + if (ret) { + printk("gpio_enable_callback error\n"); + } +#endif + + k_sem_init(&sem_touch_read, 0, 1); + + k_thread_create(&touch_thread_data, touch_read_thread_stack, + TOUCH_READ_THREAD_STACK_SIZE, touch_screen_read_thread, + NULL, NULL, NULL, 5, + 0, K_NO_WAIT); + printf("xpt2046_init ok \n"); +} + +/** + * Get the current position and state of the touchpad + * @param data store the read data here + * @return false: because no ore data to be read + */ +bool xpt2046_read(lv_indev_data_t * data) +{ + static int16_t last_x = 0; + static int16_t last_y = 0; + bool valid = true; + int s32_ret = 0; + + int16_t x = 0; + int16_t y = 0; + + char tx1[16] = { 0 }; + char rx1[16] = { 0 }; + + struct spi_buf tx_buf = { .buf = &tx1, .len = 3 }; + struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; + struct spi_buf rx_buf = { .buf = &rx1, .len = 3 }; + struct spi_buf_set rx_bufs = { .buffers = &rx_buf, .count = 1 }; + + tx1[0] = CMD_X_READ; + s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); + if (s32_ret != 0) { + printf("spi_transceive return failed:%d\n", s32_ret); + } + x = rx1[1] << 8; + x += rx1[2]; + + tx1[0] = CMD_Y_READ; + s32_ret = spi_transceive(input_dev, &spi_conf_xpt2046, &tx_bufs, &rx_bufs); + if (s32_ret != 0) { + printf("spi_transceive return failed:%d\n", s32_ret); + } + y = rx1[1] << 8; + y += rx1[2]; + x = x >> 3; + y = y >> 3; + + xpt2046_corr(&x, &y); + if (y <= 0 || (x > 320)) { + valid = false; + } + + last_x = x; + last_y = y; + + data->point.x = x; + data->point.y = y; + data->state = valid == false ? LV_INDEV_STATE_REL : LV_INDEV_STATE_PR; + + return valid; +} + +/********************** + * STATIC FUNCTIONS + **********************/ +static void xpt2046_corr(int16_t * x, int16_t * y) +{ +#if XPT2046_XY_SWAP != 0 + int16_t swap_tmp; + swap_tmp = *x; + *x = *y; + *y = swap_tmp; +#endif + + if ((*x) > XPT2046_X_MIN) + (*x) -= XPT2046_X_MIN; + else + (*x) = 0; + + if ((*y) > XPT2046_Y_MIN) + (*y) -= XPT2046_Y_MIN; + else + (*y) = 0; + + (*x) = (uint32_t)((uint32_t)(*x) * XPT2046_HOR_RES) + / (XPT2046_X_MAX - XPT2046_X_MIN); + + (*y) = (uint32_t)((uint32_t)(*y) * XPT2046_VER_RES) + / (XPT2046_Y_MAX - XPT2046_Y_MIN); + +#if XPT2046_X_INV != 0 + (*x) = XPT2046_HOR_RES - (*x); +#endif + +#if XPT2046_Y_INV != 0 + (*y) = XPT2046_VER_RES - (*y); +#endif + +} + +#if 0 +static void xpt2046_avg(int16_t * x, int16_t * y) +{ + /*Shift out the oldest data*/ + uint8_t i; + for (i = XPT2046_AVG - 1; i > 0; i--) { + avg_buf_x[i] = avg_buf_x[i - 1]; + avg_buf_y[i] = avg_buf_y[i - 1]; + } + + /*Insert the new point*/ + avg_buf_x[0] = *x; + avg_buf_y[0] = *y; + if (avg_last < XPT2046_AVG) + avg_last++; + + /*Sum the x and y coordinates*/ + int32_t x_sum = 0; + int32_t y_sum = 0; + for (i = 0; i < avg_last; i++) { + x_sum += avg_buf_x[i]; + y_sum += avg_buf_y[i]; + } + + /*Normalize the sums*/ + (*x) = (int32_t) x_sum / avg_last; + (*y) = (int32_t) y_sum / avg_last; +} +#endif + +bool touchscreen_read(lv_indev_data_t * data) +{ + /*Store the collected data*/ + data->point.x = last_touch_point.point.x; + data->point.y = last_touch_point.point.y; + data->state = last_touch_point.state; + + if (last_touch_point.state == LV_INDEV_STATE_PR) { + last_touch_point.state = LV_INDEV_STATE_REL; + } + return false; +} + +#endif diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h new file mode 100644 index 0000000..196ef7c --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/XPT2046.h @@ -0,0 +1,86 @@ +/** + * @file XPT2046.h + * + */ + +#ifndef XPT2046_H +#define XPT2046_H + +#define USE_XPT2046 1 + + +# define XPT2046_HOR_RES 320 +# define XPT2046_VER_RES 240 +# define XPT2046_X_MIN 200 +# define XPT2046_Y_MIN 200 +# define XPT2046_X_MAX 3800 +# define XPT2046_Y_MAX 3800 +# define XPT2046_AVG 4 +# define XPT2046_INV 0 + +#define CMD_X_READ 0b10010000 +#define CMD_Y_READ 0b11010000 + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#if USE_XPT2046 +#include +#include +#include +//#include "lvgl/lv_hal/lv_hal_indev.h" +#include "device.h" +#include "drivers/gpio.h" +#if 1 +enum { + LV_INDEV_STATE_REL = 0, LV_INDEV_STATE_PR +}; +typedef uint8_t lv_indev_state_t; +typedef int16_t lv_coord_t; +typedef struct { + lv_coord_t x; + lv_coord_t y; +} lv_point_t; + +typedef struct { + union { + lv_point_t point; /*For LV_INDEV_TYPE_POINTER the currently pressed point*/ + uint32_t key; /*For LV_INDEV_TYPE_KEYPAD the currently pressed key*/ + uint32_t btn; /*For LV_INDEV_TYPE_BUTTON the currently pressed button*/ + int16_t enc_diff; /*For LV_INDEV_TYPE_ENCODER number of steps since the previous read*/ + }; + void *user_data; /*'lv_indev_drv_t.priv' for this driver*/ + lv_indev_state_t state; /*LV_INDEV_STATE_REL or LV_INDEV_STATE_PR*/ +} lv_indev_data_t; +#endif + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void xpt2046_init(void); +bool xpt2046_read(lv_indev_data_t * data); + +/********************** + * MACROS + **********************/ + +#endif /* USE_XPT2046 */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* XPT2046_H */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h new file mode 100644 index 0000000..d7ea279 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/board_config.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __BOARD_CONFIG_H__ +#define __BOARD_CONFIG_H__ +#include "pin_config_stm32.h" + +#endif /* __BOARD_CONFIG_H__ */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h new file mode 100644 index 0000000..b820b9c --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display.h @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API for display drivers and applications + */ + +#ifndef ZEPHYR_INCLUDE_DISPLAY_H_ +#define ZEPHYR_INCLUDE_DISPLAY_H_ + +/** + * @brief Display Interface + * @defgroup display_interface Display Interface + * @ingroup display_interfaces + * @{ + */ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum display_pixel_format { + PIXEL_FORMAT_RGB_888 = BIT(0), PIXEL_FORMAT_MONO01 = BIT(1), /* 0=Black 1=White */ + PIXEL_FORMAT_MONO10 = BIT(2), /* 1=Black 0=White */ + PIXEL_FORMAT_ARGB_8888 = BIT(3), PIXEL_FORMAT_RGB_565 = BIT(4), +}; + +enum display_screen_info { + /** + * If selected, one octet represents 8 pixels ordered vertically, + * otherwise ordered horizontally. + */ + SCREEN_INFO_MONO_VTILED = BIT(0), + /** + * If selected, the MSB represents the first pixel, + * otherwise MSB represents the last pixel. + */ + SCREEN_INFO_MONO_MSB_FIRST = BIT(1), + /** + * Electrophoretic Display. + */ + SCREEN_INFO_EPD = BIT(2), + /** + * Screen has two alternating ram buffers + */ + SCREEN_INFO_DOUBLE_BUFFER = BIT(3), +}; + +/** + * @enum display_orientation + * @brief Enumeration with possible display orientation + * + */ +enum display_orientation { + DISPLAY_ORIENTATION_NORMAL, + DISPLAY_ORIENTATION_ROTATED_90, + DISPLAY_ORIENTATION_ROTATED_180, + DISPLAY_ORIENTATION_ROTATED_270, +}; + +/** + * @struct display_capabilities + * @brief Structure holding display capabilities + * + * @var u16_t display_capabilities::x_resolution + * Display resolution in the X direction + * + * @var u16_t display_capabilities::y_resolution + * Display resolution in the Y direction + * + * @var u32_t display_capabilities::supported_pixel_formats + * Bitwise or of pixel formats supported by the display + * + * @var u32_t display_capabilities::screen_info + * Information about display panel + * + * @var enum display_pixel_format display_capabilities::current_pixel_format + * Currently active pixel format for the display + * + * @var enum display_orientation display_capabilities::current_orientation + * Current display orientation + * + */ +struct display_capabilities { + u16_t x_resolution; + u16_t y_resolution; + u32_t supported_pixel_formats; + u32_t screen_info; + enum display_pixel_format current_pixel_format; + enum display_orientation current_orientation; +}; + +/** + * @struct display_buffer_descriptor + * @brief Structure to describe display data buffer layout + * + * @var u32_t display_buffer_descriptor::buf_size + * Data buffer size in bytes + * + * @var u16_t display_buffer_descriptor::width + * Data buffer row width in pixels + * + * @var u16_t display_buffer_descriptor::height + * Data buffer column height in pixels + * + * @var u16_t display_buffer_descriptor::pitch + * Number of pixels between consecutive rows in the data buffer + * + */ +struct display_buffer_descriptor { + u32_t buf_size; + u16_t width; + u16_t height; + u16_t pitch; +}; + +/** + * @typedef display_blanking_on_api + * @brief Callback API to turn on display blanking + * See display_blanking_on() for argument description + */ +typedef int (*display_blanking_on_api)(const struct device *dev); + +/** + * @typedef display_blanking_off_api + * @brief Callback API to turn off display blanking + * See display_blanking_off() for argument description + */ +typedef int (*display_blanking_off_api)(const struct device *dev); + +/** + * @typedef display_write_api + * @brief Callback API for writing data to the display + * See display_write() for argument description + */ +typedef int (*display_write_api)(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, + const void *buf); + +/** + * @typedef display_read_api + * @brief Callback API for reading data from the display + * See display_read() for argument description + */ +typedef int (*display_read_api)(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, void *buf); + +/** + * @typedef display_get_framebuffer_api + * @brief Callback API to get framebuffer pointer + * See display_get_framebuffer() for argument description + */ +typedef void *(*display_get_framebuffer_api)(const struct device *dev); + +/** + * @typedef display_set_brightness_api + * @brief Callback API to set display brightness + * See display_set_brightness() for argument description + */ +typedef int (*display_set_brightness_api)(const struct device *dev, + const u8_t brightness); + +/** + * @typedef display_set_contrast_api + * @brief Callback API to set display contrast + * See display_set_contrast() for argument description + */ +typedef int (*display_set_contrast_api)(const struct device *dev, + const u8_t contrast); + +/** + * @typedef display_get_capabilities_api + * @brief Callback API to get display capabilities + * See display_get_capabilities() for argument description + */ +typedef void (*display_get_capabilities_api)(const struct device *dev, + struct display_capabilities * capabilities); + +/** + * @typedef display_set_pixel_format_api + * @brief Callback API to set pixel format used by the display + * See display_set_pixel_format() for argument description + */ +typedef int (*display_set_pixel_format_api)(const struct device *dev, + const enum display_pixel_format pixel_format); + +/** + * @typedef display_set_orientation_api + * @brief Callback API to set orientation used by the display + * See display_set_orientation() for argument description + */ +typedef int (*display_set_orientation_api)(const struct device *dev, + const enum display_orientation orientation); + +/** + * @brief Display driver API + * API which a display driver should expose + */ +struct display_driver_api { + display_blanking_on_api blanking_on; + display_blanking_off_api blanking_off; + display_write_api write; + display_read_api read; + display_get_framebuffer_api get_framebuffer; + display_set_brightness_api set_brightness; + display_set_contrast_api set_contrast; + display_get_capabilities_api get_capabilities; + display_set_pixel_format_api set_pixel_format; + display_set_orientation_api set_orientation; +}; +extern struct ili9340_data ili9340_data1; +extern struct display_driver_api ili9340_api1; +/** + * @brief Write data to display + * + * @param dev Pointer to device structure + * @param x x Coordinate of the upper left corner where to write the buffer + * @param y y Coordinate of the upper left corner where to write the buffer + * @param desc Pointer to a structure describing the buffer layout + * @param buf Pointer to buffer array + * + * @retval 0 on success else negative errno code. + */ +static inline int display_write(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, + const void *buf) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->write(dev, x, y, desc, buf); +} + +/** + * @brief Read data from display + * + * @param dev Pointer to device structure + * @param x x Coordinate of the upper left corner where to read from + * @param y y Coordinate of the upper left corner where to read from + * @param desc Pointer to a structure describing the buffer layout + * @param buf Pointer to buffer array + * + * @retval 0 on success else negative errno code. + */ +static inline int display_read(const struct device *dev, const u16_t x, + const u16_t y, const struct display_buffer_descriptor *desc, void *buf) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->read(dev, x, y, desc, buf); +} + +/** + * @brief Get pointer to framebuffer for direct access + * + * @param dev Pointer to device structure + * + * @retval Pointer to frame buffer or NULL if direct framebuffer access + * is not supported + * + */ +static inline void *display_get_framebuffer(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->get_framebuffer(dev); +} + +/** + * @brief Turn display blanking on + * + * @param dev Pointer to device structure + * + * @retval 0 on success else negative errno code. + */ +static inline int display_blanking_on(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->blanking_on(dev); +} + +/** + * @brief Turn display blanking off + * + * @param dev Pointer to device structure + * + * @retval 0 on success else negative errno code. + */ +static inline int display_blanking_off(const struct device *dev) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->blanking_off(dev); +} + +/** + * @brief Set the brightness of the display + * + * Set the brightness of the display in steps of 1/256, where 255 is full + * brightness and 0 is minimal. + * + * @param dev Pointer to device structure + * @param brightness Brightness in steps of 1/256 + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_brightness(const struct device *dev, + u8_t brightness) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_brightness(dev, brightness); +} + +/** + * @brief Set the contrast of the display + * + * Set the contrast of the display in steps of 1/256, where 255 is maximum + * difference and 0 is minimal. + * + * @param dev Pointer to device structure + * @param contrast Contrast in steps of 1/256 + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_contrast(const struct device *dev, u8_t contrast) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_contrast(dev, contrast); +} + +/** + * @brief Get display capabilities + * + * @param dev Pointer to device structure + * @param capabilities Pointer to capabilities structure to populate + */ +static inline void display_get_capabilities(const struct device *dev, + struct display_capabilities * capabilities) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + api->get_capabilities(dev, capabilities); +} + +/** + * @brief Set pixel format used by the display + * + * @param dev Pointer to device structure + * @param pixel_format Pixel format to be used by display + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_pixel_format(dev, pixel_format); +} + +/** + * @brief Set display orientation + * + * @param dev Pointer to device structure + * @param orientation Orientation to be used by display + * + * @retval 0 on success else negative errno code. + */ +static inline int display_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + struct display_driver_api *api = &ili9340_api1; + //(struct display_driver_api *)dev->driver_api; + + return api->set_orientation(dev, orientation); +} + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_DISPLAY_H_*/ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c new file mode 100644 index 0000000..4d4ed39 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_ili9340.h" +#include + +//#define LOG_LEVEL CONFIG_DISPLAY_LOG_LEVEL +//#include +//LOG_MODULE_REGISTER(display_ili9340); +#define LOG_ERR printf +#define LOG_DBG printf +#define LOG_WRN printf + +#include +#include +#include +#include +#include + +struct ili9340_data { + struct device *reset_gpio; + struct device *command_data_gpio; + struct device *spi_dev; + struct spi_config spi_config; +#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER +struct spi_cs_control cs_ctrl; +#endif +}; + +struct ili9340_data ili9340_data1; + +#define ILI9340_CMD_DATA_PIN_COMMAND 0 +#define ILI9340_CMD_DATA_PIN_DATA 1 + +static void ili9340_exit_sleep(struct ili9340_data *data) +{ + ili9340_transmit(data, ILI9340_CMD_EXIT_SLEEP, NULL, 0); + //k_sleep(Z_TIMEOUT_MS(120)); +} + +int ili9340_init() +{ + struct ili9340_data *data = &ili9340_data1; + printf("Initializing display driver\n"); + data->spi_dev = device_get_binding(DT_ILITEK_ILI9340_0_BUS_NAME); + if (data->spi_dev == NULL) { + return -EPERM; + } + data->spi_config.frequency = DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY; + data->spi_config.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8); //SPI_OP_MODE_MASTER | SPI_WORD_SET(8); + data->spi_config.slave = DT_ILITEK_ILI9340_0_BASE_ADDRESS; + +#ifdef DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER + data->cs_ctrl.gpio_dev = + device_get_binding(DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER); + data->cs_ctrl.gpio_pin = DT_ILITEK_ILI9340_0_CS_GPIO_PIN; + data->cs_ctrl.delay = 0; + data->spi_config.cs = &(data->cs_ctrl); +#else + data->spi_config.cs = NULL; +#endif + data->reset_gpio = device_get_binding( + DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER); + if (data->reset_gpio == NULL) { + return -EPERM; + } + + gpio_pin_configure(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, + GPIO_OUTPUT); + + data->command_data_gpio = device_get_binding( + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER); + if (data->command_data_gpio == NULL) { + return -EPERM; + } + + gpio_pin_configure(data->command_data_gpio, + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, GPIO_OUTPUT); + + LOG_DBG("Resetting display driver\n"); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); + k_sleep(Z_TIMEOUT_MS(1)); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 0); + k_sleep(Z_TIMEOUT_MS(1)); + gpio_pin_set(data->reset_gpio, DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN, 1); + k_sleep(Z_TIMEOUT_MS(5)); + + LOG_DBG("Initializing LCD\n"); + ili9340_lcd_init(data); + + LOG_DBG("Exiting sleep mode\n"); + ili9340_exit_sleep(data); + + return 0; +} + +static void ili9340_set_mem_area(struct ili9340_data *data, const u16_t x, + const u16_t y, const u16_t w, const u16_t h) +{ + u16_t spi_data[2]; + + spi_data[0] = sys_cpu_to_be16(x); + spi_data[1] = sys_cpu_to_be16(x + w - 1); + ili9340_transmit(data, ILI9340_CMD_COLUMN_ADDR, &spi_data[0], 4); + + spi_data[0] = sys_cpu_to_be16(y); + spi_data[1] = sys_cpu_to_be16(y + h - 1); + ili9340_transmit(data, ILI9340_CMD_PAGE_ADDR, &spi_data[0], 4); +} + +static int ili9340_write(const struct device *dev, const u16_t x, const u16_t y, + const struct display_buffer_descriptor *desc, const void *buf) +{ + struct ili9340_data *data = (struct ili9340_data *) &ili9340_data1; + const u8_t *write_data_start = (u8_t *) buf; + struct spi_buf tx_buf; + struct spi_buf_set tx_bufs; + u16_t write_cnt; + u16_t nbr_of_writes; + u16_t write_h; + + __ASSERT(desc->width <= desc->pitch, "Pitch is smaller then width"); + __ASSERT((3 * desc->pitch * desc->height) <= desc->buf_size, + "Input buffer to small"); + ili9340_set_mem_area(data, x, y, desc->width, desc->height); + + if (desc->pitch > desc->width) { + write_h = 1U; + nbr_of_writes = desc->height; + } else { + write_h = desc->height; + nbr_of_writes = 1U; + } + ili9340_transmit(data, ILI9340_CMD_MEM_WRITE, (void *) write_data_start, + 3 * desc->width * write_h); + + tx_bufs.buffers = &tx_buf; + tx_bufs.count = 1; + + write_data_start += (3 * desc->pitch); + for (write_cnt = 1U; write_cnt < nbr_of_writes; ++write_cnt) { + tx_buf.buf = (void *) write_data_start; + tx_buf.len = 3 * desc->width * write_h; + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + write_data_start += (3 * desc->pitch); + } + + return 0; +} + +static int ili9340_read(const struct device *dev, const u16_t x, const u16_t y, + const struct display_buffer_descriptor *desc, void *buf) +{ + LOG_ERR("Reading not supported\n"); + return -ENOTSUP; +} + +static void *ili9340_get_framebuffer(const struct device *dev) +{ + LOG_ERR("Direct framebuffer access not supported\n"); + return NULL; +} + +static int ili9340_display_blanking_off(const struct device *dev) +{ + struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; + + LOG_DBG("Turning display blanking off\n"); + ili9340_transmit(data, ILI9340_CMD_DISPLAY_ON, NULL, 0); + return 0; +} + +static int ili9340_display_blanking_on(const struct device *dev) +{ + struct ili9340_data *data = (struct ili9340_data *) dev->driver_data; + + LOG_DBG("Turning display blanking on\n"); + ili9340_transmit(data, ILI9340_CMD_DISPLAY_OFF, NULL, 0); + return 0; +} + +static int ili9340_set_brightness(const struct device *dev, + const u8_t brightness) +{ + LOG_WRN("Set brightness not implemented\n"); + return -ENOTSUP; +} + +static int ili9340_set_contrast(const struct device *dev, const u8_t contrast) +{ + LOG_ERR("Set contrast not supported\n"); + return -ENOTSUP; +} + +static int ili9340_set_pixel_format(const struct device *dev, + const enum display_pixel_format pixel_format) +{ + if (pixel_format == PIXEL_FORMAT_RGB_888) { + return 0; +} + LOG_ERR("Pixel format change not implemented\n"); + return -ENOTSUP; +} + +static int ili9340_set_orientation(const struct device *dev, + const enum display_orientation orientation) +{ + if (orientation == DISPLAY_ORIENTATION_NORMAL) { + return 0; + } + LOG_ERR("Changing display orientation not implemented\n"); + return -ENOTSUP; +} + +static void ili9340_get_capabilities(const struct device *dev, + struct display_capabilities *capabilities) +{ + memset(capabilities, 0, sizeof(struct display_capabilities)); + capabilities->x_resolution = 320; + capabilities->y_resolution = 240; + capabilities->supported_pixel_formats = PIXEL_FORMAT_RGB_888; + capabilities->current_pixel_format = PIXEL_FORMAT_RGB_888; + capabilities->current_orientation = DISPLAY_ORIENTATION_NORMAL; +} + +void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, + size_t tx_len) +{ + data = (struct ili9340_data *) &ili9340_data1; + struct spi_buf tx_buf = { .buf = &cmd, .len = 1 }; + struct spi_buf_set tx_bufs = { .buffers = &tx_buf, .count = 1 }; + + gpio_pin_set(data->command_data_gpio, DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, + ILI9340_CMD_DATA_PIN_COMMAND); + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + if (tx_data != NULL) { + tx_buf.buf = tx_data; + tx_buf.len = tx_len; + gpio_pin_set(data->command_data_gpio, + DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN, + ILI9340_CMD_DATA_PIN_DATA); + spi_transceive(data->spi_dev, &data->spi_config, &tx_bufs, NULL); + } +} + +struct display_driver_api ili9340_api1 = { + .blanking_on = ili9340_display_blanking_on, + .blanking_off = ili9340_display_blanking_off, + .write = ili9340_write, + .read = ili9340_read, + .get_framebuffer = ili9340_get_framebuffer, + .set_brightness = ili9340_set_brightness, + .set_contrast = ili9340_set_contrast, + .get_capabilities = ili9340_get_capabilities, + .set_pixel_format = ili9340_set_pixel_format, + .set_orientation = ili9340_set_orientation +}; + +/* + DEVICE_AND_API_INIT(ili9340, DT_ILITEK_ILI9340_0_LABEL, &ili9340_init, + &ili9340_data, NULL, APPLICATION, + CONFIG_APPLICATION_INIT_PRIORITY, &ili9340_api); + */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h new file mode 100644 index 0000000..8aab97a --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ +#define ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ +#include "board_config.h" +#include +#include + +#define ILI9340_CMD_ENTER_SLEEP 0x10 +#define ILI9340_CMD_EXIT_SLEEP 0x11 +#define ILI9340_CMD_GAMMA_SET 0x26 +#define ILI9340_CMD_DISPLAY_OFF 0x28 +#define ILI9340_CMD_DISPLAY_ON 0x29 +#define ILI9340_CMD_COLUMN_ADDR 0x2a +#define ILI9340_CMD_PAGE_ADDR 0x2b +#define ILI9340_CMD_MEM_WRITE 0x2c +#define ILI9340_CMD_MEM_ACCESS_CTRL 0x36 +#define ILI9340_CMD_PIXEL_FORMAT_SET 0x3A +#define ILI9340_CMD_FRAME_CTRL_NORMAL_MODE 0xB1 +#define ILI9340_CMD_DISPLAY_FUNCTION_CTRL 0xB6 +#define ILI9340_CMD_POWER_CTRL_1 0xC0 +#define ILI9340_CMD_POWER_CTRL_2 0xC1 +#define ILI9340_CMD_VCOM_CTRL_1 0xC5 +#define ILI9340_CMD_VCOM_CTRL_2 0xC7 +#define ILI9340_CMD_POSITVE_GAMMA_CORRECTION 0xE0 +#define ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION 0xE1 + +#define ILI9340_DATA_MEM_ACCESS_CTRL_MY 0x80 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MX 0x40 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MV 0x20 +#define ILI9340_DATA_MEM_ACCESS_CTRL_ML 0x10 +#define ILI9340_DATA_MEM_ACCESS_CTRL_BGR 0x08 +#define ILI9340_DATA_MEM_ACCESS_CTRL_MH 0x04 + +#define ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT 0x60 +#define ILI9340_DATA_PIXEL_FORMAT_RGB_16_BIT 0x50 +#define ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT 0x06 +#define ILI9340_DATA_PIXEL_FORMAT_MCU_16_BIT 0x05 + +struct ili9340_data; + +/** + * Send data to ILI9340 display controller + * + * @param data Device data structure + * @param cmd Command to send to display controller + * @param tx_data Data to transmit to the display controller + * In case no data should be transmitted pass a NULL pointer + * @param tx_len Number of bytes in tx_data buffer + * + */ +void ili9340_transmit(struct ili9340_data *data, u8_t cmd, void *tx_data, + size_t tx_len); + +/** + * Perform LCD specific initialization + * + * @param data Device data structure + */ +void ili9340_lcd_init(struct ili9340_data *data); + +#define DT_ILITEK_ILI9340_0_LABEL "DISPLAY" +#define CONFIG_DISPLAY_LOG_LEVEL 0 + +#endif /* ZEPHYR_DRIVERS_DISPLAY_DISPLAY_ILI9340_H_ */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c new file mode 100644 index 0000000..a8581a7 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_ili9340_adafruit_1480.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Jan Van Winkel + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "display_ili9340.h" + +void ili9340_lcd_init(struct ili9340_data *data) +{ + u8_t tx_data[15]; + + tx_data[0] = 0x23; + ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_1, tx_data, 1); + + tx_data[0] = 0x10; + ili9340_transmit(data, ILI9340_CMD_POWER_CTRL_2, tx_data, 1); + + tx_data[0] = 0x3e; + tx_data[1] = 0x28; + ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_1, tx_data, 2); + + tx_data[0] = 0x86; + ili9340_transmit(data, ILI9340_CMD_VCOM_CTRL_2, tx_data, 1); + + tx_data[0] = + ILI9340_DATA_MEM_ACCESS_CTRL_MV | ILI9340_DATA_MEM_ACCESS_CTRL_BGR; + ili9340_transmit(data, ILI9340_CMD_MEM_ACCESS_CTRL, tx_data, 1); + + tx_data[0] = ILI9340_DATA_PIXEL_FORMAT_MCU_18_BIT | + ILI9340_DATA_PIXEL_FORMAT_RGB_18_BIT; + ili9340_transmit(data, ILI9340_CMD_PIXEL_FORMAT_SET, tx_data, 1); + + tx_data[0] = 0x00; + tx_data[1] = 0x18; + ili9340_transmit(data, ILI9340_CMD_FRAME_CTRL_NORMAL_MODE, tx_data, 2); + + tx_data[0] = 0x08; + tx_data[1] = 0x82; + tx_data[2] = 0x27; + ili9340_transmit(data, ILI9340_CMD_DISPLAY_FUNCTION_CTRL, tx_data, 3); + + tx_data[0] = 0x01; + ili9340_transmit(data, ILI9340_CMD_GAMMA_SET, tx_data, 1); + + tx_data[0] = 0x0F; + tx_data[1] = 0x31; + tx_data[2] = 0x2B; + tx_data[3] = 0x0C; + tx_data[4] = 0x0E; + tx_data[5] = 0x08; + tx_data[6] = 0x4E; + tx_data[7] = 0xF1; + tx_data[8] = 0x37; + tx_data[9] = 0x07; + tx_data[10] = 0x10; + tx_data[11] = 0x03; + tx_data[12] = 0x0E; + tx_data[13] = 0x09; + tx_data[14] = 0x00; + ili9340_transmit(data, ILI9340_CMD_POSITVE_GAMMA_CORRECTION, tx_data, 15); + + tx_data[0] = 0x00; + tx_data[1] = 0x0E; + tx_data[2] = 0x14; + tx_data[3] = 0x03; + tx_data[4] = 0x11; + tx_data[5] = 0x07; + tx_data[6] = 0x31; + tx_data[7] = 0xC1; + tx_data[8] = 0x48; + tx_data[9] = 0x08; + tx_data[10] = 0x0F; + tx_data[11] = 0x0C; + tx_data[12] = 0x31; + tx_data[13] = 0x36; + tx_data[14] = 0x0F; + ili9340_transmit(data, ILI9340_CMD_NEGATIVE_GAMMA_CORRECTION, tx_data, 15); +} diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c new file mode 100644 index 0000000..6701cee --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/display_indev.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include +#include +#include "display_indev.h" +#include "display.h" +#include "wasm_export.h" +#include "app_manager_export.h" + +#define MONITOR_HOR_RES 320 +#define MONITOR_VER_RES 240 +#ifndef MONITOR_ZOOM +#define MONITOR_ZOOM 1 +#endif + +extern int ili9340_init(); + +static int lcd_initialized = 0; + +void +display_init(void) +{ + if (lcd_initialized != 0) { + return; + } + lcd_initialized = 1; + xpt2046_init(); + ili9340_init(); + display_blanking_off(NULL); +} + +void +display_flush(wasm_exec_env_t exec_env, + int32_t x1, int32_t y1, int32_t x2, int32_t y2, + lv_color_t *color) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + struct display_buffer_descriptor desc; + + if (!wasm_runtime_validate_native_addr(module_inst, + color, sizeof(lv_color_t))) + return; + + u16_t w = x2 - x1 + 1; + u16_t h = y2 - y1 + 1; + + desc.buf_size = 3 * w * h; + desc.width = w; + desc.pitch = w; + desc.height = h; + display_write(NULL, x1, y1, &desc, (void *)color); + + /*lv_flush_ready();*/ +} + +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) +{ +} + +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) +{ +} + +bool +display_input_read(wasm_exec_env_t exec_env, void *data) +{ + wasm_module_inst_t module_inst = get_module_inst(exec_env); + lv_indev_data_t *lv_data = (lv_indev_data_t*)data; + + if (!wasm_runtime_validate_native_addr(module_inst, + lv_data, sizeof(lv_indev_data_t))) + return false; + + return touchscreen_read(lv_data); +} + +void +display_deinit(wasm_exec_env_t exec_env) +{ +} + +void +display_vdb_write(wasm_exec_env_t exec_env, + 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); + 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; + + *buf_xy = color->red; + *(buf_xy + 1) = color->green; + *(buf_xy + 2) = color->blue; +} + +int +time_get_ms(wasm_exec_env_t exec_env) +{ + return k_uptime_get_32(); +} + diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c new file mode 100644 index 0000000..cc0111e --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/iwasm_main.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#include "bh_platform.h" +#include "runtime_lib.h" +#include "native_interface.h" +#include "app_manager_export.h" +#include "board_config.h" +#include "bh_platform.h" +#include "runtime_sensor.h" +#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 +#include + + +extern void init_sensor_framework(); +extern void exit_sensor_framework(); +extern int aee_host_msg_callback(void *msg, uint16_t msg_len); + +int uart_char_cnt = 0; + +static void uart_irq_callback(struct device *dev) +{ + unsigned char ch; + + while (uart_poll_in(dev, &ch) == 0) { + uart_char_cnt++; + aee_host_msg_callback(&ch, 1); + } +} + +struct device *uart_dev = NULL; + +static bool host_init() +{ + uart_dev = device_get_binding(HOST_DEVICE_COMM_UART_NAME); + if (!uart_dev) { + printf("UART: Device driver not found.\n"); + return false; + } + uart_irq_rx_enable(uart_dev); + uart_irq_callback_set(uart_dev, uart_irq_callback); + return true; +} + +int host_send(void * ctx, const char *buf, int size) +{ + if (!uart_dev) + return 0; + + for (int i = 0; i < size; i++) + uart_poll_out(uart_dev, buf[i]); + + return size; +} + +void host_destroy() +{ +} + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy +}; + +timer_ctx_t timer_ctx; + +static char global_heap_buf[368 * 1024] = { 0 }; + +static NativeSymbol native_symbols[] = { + 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() +{ + RuntimeInitArgs init_args; + + host_init(); + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + 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); + + init_args.native_module_name = "env"; + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_symbols = native_symbols; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + display_init(); + + // timer manager + init_wasm_timer(); + + // TODO: + app_manager_startup(&interface); + + wasm_runtime_destroy(); + return -1; +} diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c new file mode 100644 index 0000000..a6fc332 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/main.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include "bh_platform.h" +#include "bh_assert.h" +#include "bh_log.h" +#include "wasm_export.h" + +extern void display_init(void); +extern int iwasm_main(); + +void main(void) +{ + display_init(); + iwasm_main(); + for(;;){ + k_sleep(Z_TIMEOUT_MS(1000)); + } +} + diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h new file mode 100644 index 0000000..3cde4ec --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_jlf.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __PIN_CONFIG_JLF_H__ +#define __PIN_CONFIG_JLF_H__ + +#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_2" +#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 10*1000 + +#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIO_0" +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 5 +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIO_0" +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 4 + +#define XPT2046_SPI_DEVICE_NAME "SPI_2" +#define XPT2046_SPI_MAX_FREQUENCY 10*1000 +#define XPT2046_CS_GPIO_CONTROLLER "GPIO_0" +#define XPT2046_CS_GPIO_PIN 6 + +#define XPT2046_PEN_GPIO_CONTROLLER "GPIO_0" +#define XPT2046_PEN_GPIO_PIN 7 + +#define HOST_DEVICE_COMM_UART_NAME "UART_1" +#endif /* __PIN_CONFIG_JLF_H__ */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h new file mode 100644 index 0000000..fba36fa --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/src/platform/zephyr/pin_config_stm32.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +#ifndef __PIN_CONFIG_STM32_H__ +#define __PIN_CONFIG_STM32_H__ + +#define DT_ILITEK_ILI9340_0_BUS_NAME "SPI_1" +#define DT_ILITEK_ILI9340_0_SPI_MAX_FREQUENCY 24*1000*1000 + +#define DT_ILITEK_ILI9340_0_BASE_ADDRESS 1 +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_RESET_GPIOS_PIN 12 +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_CMD_DATA_GPIOS_PIN 11 + +#define DT_ILITEK_ILI9340_0_CS_GPIO_CONTROLLER "GPIOC" +#define DT_ILITEK_ILI9340_0_CS_GPIO_PIN 10 + +#define XPT2046_SPI_DEVICE_NAME "SPI_1" +#define XPT2046_SPI_MAX_FREQUENCY 12*1000*1000 +#define XPT2046_CS_GPIO_CONTROLLER "GPIOD" +#define XPT2046_CS_GPIO_PIN 0 + +#define XPT2046_PEN_GPIO_CONTROLLER "GPIOD" +#define XPT2046_PEN_GPIO_PIN 1 + +#define HOST_DEVICE_COMM_UART_NAME "UART_6" + +#endif /* __PIN_CONFIG_STM32_H__ */ diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt b/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt new file mode 100644 index 0000000..723ff8f --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/CMakeLists.txt @@ -0,0 +1,71 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 3.8.2) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(NONE) + +set (WAMR_BUILD_PLATFORM "zephyr") + +enable_language (ASM) + +add_definitions(-DWA_MALLOC=wasm_runtime_malloc) +add_definitions(-DWA_FREE=wasm_runtime_free) + +# Build as THUMB by default +# change to "ARM[sub]", "THUMB[sub]", "X86_32", "MIPS_32" or "XTENSA_32" +# if we want to support arm_32, x86, mips or xtensa +if (NOT DEFINED WAMR_BUILD_TARGET) + set (WAMR_BUILD_TARGET "THUMBV7") +endif () + +if (NOT DEFINED WAMR_BUILD_INTERP) + # Enable Interpreter by default + set (WAMR_BUILD_INTERP 1) +endif () + +if (NOT DEFINED WAMR_BUILD_AOT) + set (WAMR_BUILD_AOT 1) +endif () + +if (NOT DEFINED WAMR_BUILD_JIT) + # Disable JIT by default. + set (WAMR_BUILD_JIT 0) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + set (WAMR_BUILD_LIBC_BUILTIN 1) +endif () + +if (NOT DEFINED WAMR_BUILD_LIBC_WASI) + set (WAMR_BUILD_LIBC_WASI 0) +endif () + +if (NOT DEFINED WAMR_BUILD_APP_FRAMEWORK) + set (WAMR_BUILD_APP_FRAMEWORK 1) +endif () + +if (NOT DEFINED WAMR_BUILD_APP_LIST) + set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_SENSOR WAMR_APP_BUILD_CONNECTION) +endif () + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/wamr) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr) + +set (LVGL_DRV_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340_adafruit_1480.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_ili9340.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/display_indev.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/XPT2046.c + ) + +target_sources(app PRIVATE + ${WAMR_RUNTIME_LIB_SOURCE} + ${LVGL_DRV_SRCS} + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/../src/platform/zephyr/iwasm_main.c + ) diff --git a/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf b/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf new file mode 100644 index 0000000..f9f13f9 --- /dev/null +++ b/wamr/samples/littlevgl/vgl-wasm-runtime/zephyr-build/prj.conf @@ -0,0 +1,10 @@ +CONFIG_SPI=y +CONFIG_SPI_STM32=y +CONFIG_SPI_1=y +CONFIG_PRINTK=y +CONFIG_LOG=y +#CONFIG_UART_2=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_STACK_SENTINEL=y +CONFIG_MAIN_STACK_SIZE=2048 +CONFIG_ARM_MPU=y diff --git a/wamr/samples/littlevgl/wamr_config_littlevgl.cmake b/wamr/samples/littlevgl/wamr_config_littlevgl.cmake new file mode 100644 index 0000000..7a9065a --- /dev/null +++ b/wamr/samples/littlevgl/wamr_config_littlevgl.cmake @@ -0,0 +1,9 @@ +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 1) +set (WAMR_BUILD_APP_FRAMEWORK 1) +set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_SENSOR WAMR_APP_BUILD_CONNECTION) diff --git a/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app b/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app new file mode 100644 index 0000000..adbeddc --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app @@ -0,0 +1,57 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CC = /opt/wasi-sdk/bin/clang +LVGL_DIR = ${shell pwd} +SDK_DIR = $(LVGL_DIR)/../../../wamr-sdk/out/littlevgl/app-sdk +APP_FRAMEWORK_DIR = $(SDK_DIR)/wamr-app-framework +LVGL_REPO_PATH=../build/lvgl + +CFLAGS += -O3 \ + -I$(LVGL_DIR) \ + -I$(LVGL_DIR)/../build \ + -I$(LVGL_DIR)/lv_drivers \ + -I$(LVGL_DIR)/src \ + -I$(LVGL_DIR)/../lv_config \ + -I$(APP_FRAMEWORK_DIR)/include + +SRCS += ${LVGL_REPO_PATH}/lv_draw/lv_draw_line.c ${LVGL_REPO_PATH}/lv_draw/lv_draw_rbasic.c +SRCS += ${LVGL_REPO_PATH}/lv_draw/lv_draw_img.c ${LVGL_REPO_PATH}/lv_draw/lv_draw_arc.c +SRCS += ${LVGL_REPO_PATH}/lv_draw/lv_draw_rect.c ${LVGL_REPO_PATH}/lv_draw/lv_draw_triangle.c +SRCS += ${LVGL_REPO_PATH}/lv_draw/lv_draw.c ${LVGL_REPO_PATH}/lv_draw/lv_draw_label.c +SRCS += ${LVGL_REPO_PATH}/lv_draw/lv_draw_vbasic.c ${LVGL_REPO_PATH}/lv_fonts/lv_font_builtin.c +SRCS += ${LVGL_REPO_PATH}/lv_fonts/lv_font_dejavu_20.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_img.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_roller.c ${LVGL_REPO_PATH}/lv_objx/lv_cb.c ${LVGL_REPO_PATH}/lv_objx/lv_led.c ${LVGL_REPO_PATH}/lv_objx/lv_cont.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_calendar.c ${LVGL_REPO_PATH}/lv_objx/lv_gauge.c ${LVGL_REPO_PATH}/lv_objx/lv_page.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_list.c ${LVGL_REPO_PATH}/lv_objx/lv_bar.c ${LVGL_REPO_PATH}/lv_objx/lv_tabview.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_mbox.c ${LVGL_REPO_PATH}/lv_objx/lv_objx_templ.c ${LVGL_REPO_PATH}/lv_objx/lv_sw.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_label.c ${LVGL_REPO_PATH}/lv_objx/lv_slider.c ${LVGL_REPO_PATH}/lv_objx/lv_ddlist.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_imgbtn.c ${LVGL_REPO_PATH}/lv_objx/lv_line.c ${LVGL_REPO_PATH}/lv_objx/lv_chart.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_btnm.c ${LVGL_REPO_PATH}/lv_objx/lv_arc.c ${LVGL_REPO_PATH}/lv_objx/lv_preload.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_win.c ${LVGL_REPO_PATH}/lv_objx/lv_lmeter.c ${LVGL_REPO_PATH}/lv_objx/lv_btn.c +SRCS += ${LVGL_REPO_PATH}/lv_objx/lv_ta.c ${LVGL_REPO_PATH}/lv_misc/lv_log.c ${LVGL_REPO_PATH}/lv_misc/lv_fs.c +SRCS += ${LVGL_REPO_PATH}/lv_misc/lv_task.c ${LVGL_REPO_PATH}/lv_misc/lv_circ.c ${LVGL_REPO_PATH}/lv_misc/lv_anim.c +SRCS += ${LVGL_REPO_PATH}/lv_misc/lv_color.c ${LVGL_REPO_PATH}/lv_misc/lv_txt.c ${LVGL_REPO_PATH}/lv_misc/lv_math.c +SRCS += ${LVGL_REPO_PATH}/lv_misc/lv_mem.c ${LVGL_REPO_PATH}/lv_misc/lv_font.c ${LVGL_REPO_PATH}/lv_misc/lv_ll.c +SRCS += ${LVGL_REPO_PATH}/lv_misc/lv_area.c ${LVGL_REPO_PATH}/lv_misc/lv_templ.c ${LVGL_REPO_PATH}/lv_misc/lv_ufs.c +SRCS += ${LVGL_REPO_PATH}/lv_misc/lv_gc.c +SRCS += ${LVGL_REPO_PATH}/lv_hal/lv_hal_tick.c ${LVGL_REPO_PATH}/lv_hal/lv_hal_indev.c ${LVGL_REPO_PATH}/lv_hal/lv_hal_disp.c +SRCS += ${LVGL_REPO_PATH}/lv_themes/lv_theme_mono.c ${LVGL_REPO_PATH}/lv_themes/lv_theme_templ.c +SRCS += ${LVGL_REPO_PATH}/lv_themes/lv_theme_material.c ${LVGL_REPO_PATH}/lv_themes/lv_theme.c +SRCS += ${LVGL_REPO_PATH}/lv_themes/lv_theme_night.c ${LVGL_REPO_PATH}/lv_themes/lv_theme_zen.c ${LVGL_REPO_PATH}/lv_themes/lv_theme_nemo.c +SRCS += ${LVGL_REPO_PATH}/lv_themes/lv_theme_alien.c ${LVGL_REPO_PATH}/lv_themes/lv_theme_default.c +SRCS += ${LVGL_REPO_PATH}/lv_core/lv_group.c ${LVGL_REPO_PATH}/lv_core/lv_style.c ${LVGL_REPO_PATH}/lv_core/lv_indev.c +SRCS += ${LVGL_REPO_PATH}/lv_core/lv_vdb.c ${LVGL_REPO_PATH}/lv_core/lv_obj.c ${LVGL_REPO_PATH}/lv_core/lv_refr.c +SRCS += $(LVGL_DIR)/src/main.c + +all: + @$(CC) $(CFLAGS) $(SRCS) \ + -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ + -DLV_CONF_INCLUDE_SIMPLE \ + -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ + -Wl,--allow-undefined \ + -Wl,--no-threads,--strip-all,--no-entry \ + -Wl,--export=on_init -Wl,--export=on_timer_callback \ + -Wl,--export=__heap_base,--export=__data_end \ + -o ui_app_wasi.wasm diff --git a/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi b/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi new file mode 100644 index 0000000..435934a --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/Makefile_wasm_app_no_wasi @@ -0,0 +1,59 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +CC = /opt/wasi-sdk/bin/clang +LVGL_DIR = ${shell pwd} +WAMR_DIR = ${LVGL_DIR}/../../.. +SDK_DIR = $(LVGL_DIR)/../../../wamr-sdk/out/littlevgl/app-sdk +APP_FRAMEWORK_DIR = $(SDK_DIR)/wamr-app-framework +LVGL_REPO_PATH=../build/lvgl + +CFLAGS += -O3 \ + -I$(LVGL_DIR) \ + -I$(LVGL_DIR)/../build \ + -I$(LVGL_DIR)/lv_drivers \ + -I$(LVGL_DIR)/src \ + -I$(LVGL_DIR)/../lv_config \ + -I$(APP_FRAMEWORK_DIR)/include + +SRCS += $(LVGL_REPO_PATH)/lv_draw/lv_draw_line.c $(LVGL_REPO_PATH)/lv_draw/lv_draw_rbasic.c +SRCS += $(LVGL_REPO_PATH)/lv_draw/lv_draw_img.c $(LVGL_REPO_PATH)/lv_draw/lv_draw_arc.c +SRCS += $(LVGL_REPO_PATH)/lv_draw/lv_draw_rect.c $(LVGL_REPO_PATH)/lv_draw/lv_draw_triangle.c +SRCS += $(LVGL_REPO_PATH)/lv_draw/lv_draw.c $(LVGL_REPO_PATH)/lv_draw/lv_draw_label.c +SRCS += $(LVGL_REPO_PATH)/lv_draw/lv_draw_vbasic.c $(LVGL_REPO_PATH)/lv_fonts/lv_font_builtin.c +SRCS += $(LVGL_REPO_PATH)/lv_fonts/lv_font_dejavu_20.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_img.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_roller.c $(LVGL_REPO_PATH)/lv_objx/lv_cb.c $(LVGL_REPO_PATH)/lv_objx/lv_led.c $(LVGL_REPO_PATH)/lv_objx/lv_cont.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_calendar.c $(LVGL_REPO_PATH)/lv_objx/lv_gauge.c $(LVGL_REPO_PATH)/lv_objx/lv_page.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_list.c $(LVGL_REPO_PATH)/lv_objx/lv_bar.c $(LVGL_REPO_PATH)/lv_objx/lv_tabview.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_mbox.c $(LVGL_REPO_PATH)/lv_objx/lv_objx_templ.c $(LVGL_REPO_PATH)/lv_objx/lv_sw.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_label.c $(LVGL_REPO_PATH)/lv_objx/lv_slider.c $(LVGL_REPO_PATH)/lv_objx/lv_ddlist.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_imgbtn.c $(LVGL_REPO_PATH)/lv_objx/lv_line.c $(LVGL_REPO_PATH)/lv_objx/lv_chart.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_btnm.c $(LVGL_REPO_PATH)/lv_objx/lv_arc.c $(LVGL_REPO_PATH)/lv_objx/lv_preload.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_win.c $(LVGL_REPO_PATH)/lv_objx/lv_lmeter.c $(LVGL_REPO_PATH)/lv_objx/lv_btn.c +SRCS += $(LVGL_REPO_PATH)/lv_objx/lv_ta.c $(LVGL_REPO_PATH)/lv_misc/lv_log.c $(LVGL_REPO_PATH)/lv_misc/lv_fs.c +SRCS += $(LVGL_REPO_PATH)/lv_misc/lv_task.c $(LVGL_REPO_PATH)/lv_misc/lv_circ.c $(LVGL_REPO_PATH)/lv_misc/lv_anim.c +SRCS += $(LVGL_REPO_PATH)/lv_misc/lv_color.c $(LVGL_REPO_PATH)/lv_misc/lv_txt.c $(LVGL_REPO_PATH)/lv_misc/lv_math.c +SRCS += $(LVGL_REPO_PATH)/lv_misc/lv_mem.c $(LVGL_REPO_PATH)/lv_misc/lv_font.c $(LVGL_REPO_PATH)/lv_misc/lv_ll.c +SRCS += $(LVGL_REPO_PATH)/lv_misc/lv_area.c $(LVGL_REPO_PATH)/lv_misc/lv_templ.c $(LVGL_REPO_PATH)/lv_misc/lv_ufs.c +SRCS += $(LVGL_REPO_PATH)/lv_misc/lv_gc.c +SRCS += $(LVGL_REPO_PATH)/lv_hal/lv_hal_tick.c $(LVGL_REPO_PATH)/lv_hal/lv_hal_indev.c $(LVGL_REPO_PATH)/lv_hal/lv_hal_disp.c +SRCS += $(LVGL_REPO_PATH)/lv_themes/lv_theme_mono.c $(LVGL_REPO_PATH)/lv_themes/lv_theme_templ.c +SRCS += $(LVGL_REPO_PATH)/lv_themes/lv_theme_material.c $(LVGL_REPO_PATH)/lv_themes/lv_theme.c +SRCS += $(LVGL_REPO_PATH)/lv_themes/lv_theme_night.c $(LVGL_REPO_PATH)/lv_themes/lv_theme_zen.c $(LVGL_REPO_PATH)/lv_themes/lv_theme_nemo.c +SRCS += $(LVGL_REPO_PATH)/lv_themes/lv_theme_alien.c $(LVGL_REPO_PATH)/lv_themes/lv_theme_default.c +SRCS += $(LVGL_REPO_PATH)/lv_core/lv_group.c $(LVGL_REPO_PATH)/lv_core/lv_style.c $(LVGL_REPO_PATH)/lv_core/lv_indev.c +SRCS += $(LVGL_REPO_PATH)/lv_core/lv_vdb.c $(LVGL_REPO_PATH)/lv_core/lv_obj.c $(LVGL_REPO_PATH)/lv_core/lv_refr.c +SRCS += $(LVGL_DIR)/src/main.c + +all: + @$(CC) $(CFLAGS) $(SRCS) \ + --target=wasm32 -Wl,--allow-undefined \ + --sysroot=$(WAMR_DIR)/wamr-sdk/app/libc-builtin-sysroot \ + -O3 -z stack-size=2048 -Wl,--initial-memory=65536 \ + -DLV_CONF_INCLUDE_SIMPLE \ + -L$(APP_FRAMEWORK_DIR)/lib -lapp_framework \ + -Wl,--allow-undefined \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=on_init -Wl,--export=on_timer_callback \ + -o ui_app_builtin_libc.wasm diff --git a/wamr/samples/littlevgl/wasm-apps/build_wasm_app.sh b/wamr/samples/littlevgl/wasm-apps/build_wasm_app.sh new file mode 100755 index 0000000..a86784e --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/build_wasm_app.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +WAMR_DIR=${PWD}/../../.. + +if [ -z $KW_BUILD ] || [ -z $KW_OUT_FILE ];then + echo "Local Build Env" + makewrap="make" +else + echo "Klocwork Build Env" + makewrap="kwinject -o $KW_OUT_FILE make" +fi + +echo "make Makefile_wasm_app" +$makewrap -f Makefile_wasm_app + +echo "make Makefile_wasm_app_no_wasi" +$makewrap -f Makefile_wasm_app_no_wasi + +echo "completed." \ No newline at end of file diff --git a/wamr/samples/littlevgl/wasm-apps/src/display_indev.h b/wamr/samples/littlevgl/wasm-apps/src/display_indev.h new file mode 100644 index 0000000..0d55cea --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/src/display_indev.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef DISPLAY_INDEV_H_ +#define DISPLAY_INDEV_H_ +#include +#include + +#include "lvgl/lv_misc/lv_color.h" +#include "lvgl/lv_hal/lv_hal_indev.h" + +extern void display_init(void); + +extern void display_deinit(void); + +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/wamr/samples/littlevgl/wasm-apps/src/main.c b/wamr/samples/littlevgl/wasm-apps/src/main.c new file mode 100644 index 0000000..04da156 --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/src/main.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +/** + * @file main + * + */ + +/********************* + * INCLUDES + *********************/ +#include +//#include +#include +#include "lvgl/lvgl.h" +#include "display_indev.h" +#include "wasm_app.h" +#include "wa-inc/timer_wasm_app.h" +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void hal_init(void); +//static int tick_thread(void * data); +//static void memory_monitor(void * param); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +uint32_t count = 0; +char count_str[11] = { 0 }; +lv_obj_t *hello_world_label; +lv_obj_t *count_label; +lv_obj_t * btn1; + +lv_obj_t * label_count1; +int label_count1_value = 0; +char label_count1_str[11] = { 0 }; + +void timer1_update(user_timer_t timer1) +{ + if ((count % 100) == 0) { + snprintf(count_str, sizeof(count_str), "%d", count / 100); + lv_label_set_text(count_label, count_str); + } + lv_task_handler(); + ++count; +} + + +static lv_res_t btn_rel_action(lv_obj_t * btn) +{ + label_count1_value++; + snprintf(label_count1_str, sizeof(label_count1_str), + "%d", label_count1_value); + lv_label_set_text(label_count1, label_count1_str); + return LV_RES_OK; +} + + +void on_init() +{ + /*Initialize LittlevGL*/ + lv_init(); + + /*Initialize the HAL (display, input devices, tick) for LittlevGL*/ + hal_init(); + + hello_world_label = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(hello_world_label, "Hello world!"); + lv_obj_align(hello_world_label, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + + count_label = lv_label_create(lv_scr_act(), NULL); + lv_obj_align(count_label, NULL, LV_ALIGN_IN_TOP_MID, 0, 0); + + btn1 = lv_btn_create(lv_scr_act(), NULL); /*Create a button on the currently loaded screen*/ + lv_btn_set_action(btn1, LV_BTN_ACTION_CLICK, btn_rel_action); /*Set function to be called when the button is released*/ + lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, 20); /*Align below the label*/ + + /*Create a label on the button*/ + lv_obj_t * btn_label = lv_label_create(btn1, NULL); + lv_label_set_text(btn_label, "Click ++"); + + label_count1 = lv_label_create(lv_scr_act(), NULL); + lv_label_set_text(label_count1, "0"); + lv_obj_align(label_count1, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + + /* set up a timer */ + user_timer_t timer; + timer = api_timer_create(10, true, false, timer1_update); + if (timer) + api_timer_restart(timer, 10); + else + printf("Fail to create timer.\n"); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * 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) +{ + 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) +{ + display_vdb_write(buf, buf_w, x, y, &color, opa); +} + +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_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; +#endif + lv_disp_drv_register(&disp_drv); + + /* Add the mouse as input device + * Use the 'mouse' driver which reads the PC's mouse*/ +// mouse_init(); + lv_indev_drv_t indev_drv; + lv_indev_drv_init(&indev_drv); /*Basic initialization*/ + indev_drv.type = LV_INDEV_TYPE_POINTER; + indev_drv.read = display_input_read; /*This function will be called periodically (by the library) to get the mouse position and state*/ + lv_indev_t * mouse_indev = lv_indev_drv_register(&indev_drv); + +} + diff --git a/wamr/samples/littlevgl/wasm-apps/src/system_header.h b/wamr/samples/littlevgl/wasm-apps/src/system_header.h new file mode 100644 index 0000000..8d943c8 --- /dev/null +++ b/wamr/samples/littlevgl/wasm-apps/src/system_header.h @@ -0,0 +1,8 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include + +uint32_t time_get_ms(void); diff --git a/wamr/samples/multi-module/CMakeLists.txt b/wamr/samples/multi-module/CMakeLists.txt new file mode 100644 index 0000000..ba239d5 --- /dev/null +++ b/wamr/samples/multi-module/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(multi_module) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 0) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 0) +set(WAMR_BUILD_FAST_INTERP 0) +set(WAMR_BUILD_MULTI_MODULE 1) + +# compiling and linking flags +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + +################ application related ################ + +################ WASM MODULES +# .c -> .wasm +add_subdirectory(wasm-apps) + +################ NATIVE +include_directories(${CMAKE_CURRENT_LIST_DIR}/src) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable(multi_module src/main.c ${UNCOMMON_SHARED_SOURCE}) + +add_dependencies(multi_module vmlib wasm-modules) + +# libraries +target_link_libraries(multi_module PRIVATE vmlib -lpthread -lm) diff --git a/wamr/samples/multi-module/src/main.c b/wamr/samples/multi-module/src/main.c new file mode 100644 index 0000000..77a4437 --- /dev/null +++ b/wamr/samples/multi-module/src/main.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include "bh_read_file.h" +#include "platform_common.h" +#include "wasm_export.h" + +static char * +build_module_path(const char *module_name) +{ + const char *module_search_path = "./wasm-apps"; + const char *format = "%s/%s.wasm"; + int sz = strlen(module_search_path) + strlen("/") + strlen(module_name) + + strlen(".wasm") + 1; + char *wasm_file_name = BH_MALLOC(sz); + if (!wasm_file_name) { + return NULL; + } + + snprintf(wasm_file_name, sz, format, module_search_path, module_name); + return wasm_file_name; +} + +static bool +module_reader_cb(const char *module_name, uint8 **p_buffer, uint32 *p_size) +{ + char *wasm_file_path = build_module_path(module_name); + if (!wasm_file_path) { + return false; + } + + printf("- bh_read_file_to_buffer %s\n", wasm_file_path); + *p_buffer = (uint8_t *)bh_read_file_to_buffer(wasm_file_path, p_size); + BH_FREE(wasm_file_path); + return *p_buffer != NULL; +} + +static void +module_destroyer_cb(uint8 *buffer, uint32 size) +{ + printf("- release the read file buffer\n"); + if (!buffer) { + return; + } + + BH_FREE(buffer); + buffer = NULL; +} + +/* 10M */ +static char sandbox_memory_space[10 * 1024 * 1024] = { 0 }; +int +main() +{ + bool ret = false; + /* 16K */ + const uint32 stack_size = 16 * 1024; + const uint32 heap_size = 16 * 1024; + + RuntimeInitArgs init_args = { 0 }; + char error_buf[128] = { 0 }; + /* parameters and return values */ + char* args[1] = { 0 }; + + uint8 *file_buf = NULL; + uint32 file_buf_size = 0; + wasm_module_t module = NULL; + wasm_module_inst_t module_inst = NULL; + + /* all malloc() only from the given buffer */ + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = sandbox_memory_space; + init_args.mem_alloc_option.pool.heap_size = sizeof(sandbox_memory_space); + + printf("- wasm_runtime_full_init\n"); + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + goto EXIT; + } + +#if WASM_ENABLE_MULTI_MODULE != 0 + printf("- wasm_runtime_set_module_reader\n"); + /* set module reader and destroyer */ + wasm_runtime_set_module_reader(module_reader_cb, module_destroyer_cb); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!module_reader_cb("mC", &file_buf, &file_buf_size)) { + goto RELEASE_RUNTIME; + } + + /* load mC and let WAMR load mA and mB */ + printf("- wasm_runtime_load\n"); + if (!(module = wasm_runtime_load(file_buf, file_buf_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto RELEASE_BINARY; + } + + /* instantiate the module */ + printf("- wasm_runtime_instantiate\n"); + if (!(module_inst = + wasm_runtime_instantiate(module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto UNLOAD_MODULE; + } + + /* call some functions of mC */ + printf("\n----------------------------------------\n"); + printf("call \"C\", it will return 0xc:i32, ===> "); + wasm_application_execute_func(module_inst, "C", 0, &args[0]); + printf("call \"call_B\", it will return 0xb:i32, ===> "); + wasm_application_execute_func(module_inst, "call_B", 0, &args[0]); + printf("call \"call_A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "call_A", 0, &args[0]); + + /* call some functions of mB */ + printf("call \"mB.B\", it will return 0xb:i32, ===>"); + wasm_application_execute_func(module_inst, "$mB$B", 0, &args[0]); + printf("call \"mB.call_A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "$mB$call_A", 0, &args[0]); + + /* call some functions of mA */ + printf("call \"mA.A\", it will return 0xa:i32, ===>"); + wasm_application_execute_func(module_inst, "$mA$A", 0, &args[0]); + printf("----------------------------------------\n\n"); + ret = true; + + printf("- wasm_runtime_deinstantiate\n"); + wasm_runtime_deinstantiate(module_inst); +UNLOAD_MODULE: + printf("- wasm_runtime_unload\n"); + wasm_runtime_unload(module); +RELEASE_BINARY: + module_destroyer_cb(file_buf, file_buf_size); +RELEASE_RUNTIME: + printf("- wasm_runtime_destroy\n"); + wasm_runtime_destroy(); +EXIT: + return ret ? 0 : 1; +} diff --git a/wamr/samples/multi-module/wasm-apps/CMakeLists.txt b/wamr/samples/multi-module/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000..2691c50 --- /dev/null +++ b/wamr/samples/multi-module/wasm-apps/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 2.8) +project(wasm-apps) + +set(CMAKE_VERBOSE_MAKEFILE on) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) +set(CLANG_COMMAND "/opt/wasi-sdk/bin/clang") + +set(CLANG_FLAGS --target=wasm32 -nostdlib) +set(CLANG_FLAGS ${CLANG_FLAGS} -Wl,--no-entry,--allow-undefined,--export-all) + +set(SOURCE_A ${CMAKE_CURRENT_SOURCE_DIR}/mA.c) +add_custom_command( + OUTPUT mA.wasm + COMMENT "Transform mA.C to mA.WASM" + COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mA.wasm ${SOURCE_A} + DEPENDS ${SOURCE_A} + VERBATIM +) + +set(SOURCE_B ${CMAKE_CURRENT_SOURCE_DIR}/mB.c) +add_custom_command( + OUTPUT mB.wasm + COMMENT "Transform mB.C to mB.WASM" + COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mB.wasm ${SOURCE_B} + DEPENDS ${SOURCE_B} + VERBATIM +) + +set(SOURCE_C ${CMAKE_CURRENT_SOURCE_DIR}/mC.c) +add_custom_command( + OUTPUT mC.wasm + COMMENT "Transform mC.C to mC.WASM" + COMMAND ${CLANG_COMMAND} ${CLANG_FLAGS} -o mC.wasm ${SOURCE_C} + DEPENDS ${SOURCE_C} + VERBATIM +) + +add_custom_target(wasm-modules ALL + DEPENDS mA.wasm mB.wasm mC.wasm +) \ No newline at end of file diff --git a/wamr/samples/multi-module/wasm-apps/mA.c b/wamr/samples/multi-module/wasm-apps/mA.c new file mode 100644 index 0000000..d5e8d83 --- /dev/null +++ b/wamr/samples/multi-module/wasm-apps/mA.c @@ -0,0 +1,5 @@ +int +A() +{ + return 10; +} \ No newline at end of file diff --git a/wamr/samples/multi-module/wasm-apps/mB.c b/wamr/samples/multi-module/wasm-apps/mB.c new file mode 100644 index 0000000..76aa1e7 --- /dev/null +++ b/wamr/samples/multi-module/wasm-apps/mB.c @@ -0,0 +1,16 @@ +__attribute__((import_module("mA"))) +__attribute__((import_name("A"))) extern int +A(); + +int +B() +{ + return 11; +} + +int +call_A() +{ + return A(); +} + diff --git a/wamr/samples/multi-module/wasm-apps/mC.c b/wamr/samples/multi-module/wasm-apps/mC.c new file mode 100644 index 0000000..5a378dd --- /dev/null +++ b/wamr/samples/multi-module/wasm-apps/mC.c @@ -0,0 +1,25 @@ +__attribute__((import_module("mA"))) +__attribute__((import_name("A"))) extern int +A(); + +__attribute__((import_module("mB"))) +__attribute__((import_name("B"))) extern int +B(); + +int +C() +{ + return 12; +} + +int +call_A() +{ + return A(); +} + +int +call_B() +{ + return B(); +} \ No newline at end of file diff --git a/wamr/samples/multi-thread/CMakeLists.txt b/wamr/samples/multi-thread/CMakeLists.txt new file mode 100644 index 0000000..34151d0 --- /dev/null +++ b/wamr/samples/multi-thread/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(pthread) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIB_PTHREAD 1) + +# compiling and linking flags +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + + +################ wasm application ################ +add_subdirectory(wasm-apps) + +################ wamr runtime ################ +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/../../product-mini/platforms/linux/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (iwasm ${RUNTIME_SOURCE_ALL}) +target_link_libraries(iwasm vmlib -lpthread -lm) + diff --git a/wamr/samples/multi-thread/wasm-apps/CMakeLists.txt b/wamr/samples/multi-thread/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000..171c5e0 --- /dev/null +++ b/wamr/samples/multi-thread/wasm-apps/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(wasm-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib -pthread -Qunused-arguments") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=32768") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (DEFINED_SYMBOLS +"${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--shared-memory,--max-memory=131072, \ + -Wl,--no-entry,--strip-all,--export=main, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--allow-undefined-file=${DEFINED_SYMBOLS}" +) + +add_executable(test.wasm main.c) +target_link_libraries(test.wasm) diff --git a/wamr/samples/multi-thread/wasm-apps/main.c b/wamr/samples/multi-thread/wasm-apps/main.c new file mode 100644 index 0000000..e871e55 --- /dev/null +++ b/wamr/samples/multi-thread/wasm-apps/main.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include + +static pthread_mutex_t mutex; +static pthread_cond_t cond; + +static void *thread(void *arg) +{ + int *num = (int *)arg; + + pthread_mutex_lock(&mutex); + printf("thread start \n"); + + for (int i = 0; i < 10; i++) { + *num = *num + 1; + printf("num: %d\n", *num); + } + + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + + printf("thread exit \n"); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + pthread_t tid; + int num = 0, ret = -1; + + if (pthread_mutex_init(&mutex, NULL) != 0) { + printf("Failed to init mutex.\n"); + return -1; + } + if (pthread_cond_init(&cond, NULL) != 0) { + printf("Failed to init cond.\n"); + goto fail1; + } + + pthread_mutex_lock(&mutex); + if (pthread_create(&tid, NULL, thread, &num) != 0) { + printf("Failed to create thread.\n"); + goto fail2; + } + + printf("cond wait start\n"); + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + printf("cond wait success.\n"); + + if (pthread_join(tid, NULL) != 0) { + printf("Failed to join thread.\n"); + } + + ret = 0; + +fail2: + pthread_cond_destroy(&cond); +fail1: + pthread_mutex_destroy(&mutex); + + return ret; +} \ No newline at end of file diff --git a/wamr/samples/simple/.gitignore b/wamr/samples/simple/.gitignore new file mode 100644 index 0000000..e2e7327 --- /dev/null +++ b/wamr/samples/simple/.gitignore @@ -0,0 +1 @@ +/out diff --git a/wamr/samples/simple/CMakeLists.txt b/wamr/samples/simple/CMakeLists.txt new file mode 100644 index 0000000..5f0ce72 --- /dev/null +++ b/wamr/samples/simple/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +project (simple) + +################ wamr runtime settings ################ +message(STATUS "WAMR_BUILD_SDK_PROFILE=${WAMR_BUILD_SDK_PROFILE}") + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +set (WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) + +## use library and headers in the SDK +link_directories(${WAMR_ROOT_DIR}/wamr-sdk/out/${WAMR_BUILD_SDK_PROFILE}/runtime-sdk/lib) +include_directories( + ${WAMR_ROOT_DIR}/wamr-sdk/out/${WAMR_BUILD_SDK_PROFILE}/runtime-sdk/include + ${WAMR_ROOT_DIR}/core/shared/utils + ${WAMR_ROOT_DIR}/core/shared/platform/linux +) + +################ application related ################ + +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) +target_link_libraries (simple vmlib -lm -ldl -lpthread -lrt) + + diff --git a/wamr/samples/simple/README.md b/wamr/samples/simple/README.md new file mode 100644 index 0000000..5625212 --- /dev/null +++ b/wamr/samples/simple/README.md @@ -0,0 +1,342 @@ + + +"simple" sample introduction +============== + +This sample demonstrates following scenarios: + +- Use tool "host_tool" to remotely install/uninstall wasm applications from the WAMR runtime over either TCP socket or UART cable +- Inter-app communication programming models +- Communication between WASM applications and the remote app host_tool +- A number of WASM applications built on top of WAMR application framework API sets + + + +Directory structure +------------------------------ +``` +simple/ +├── build.sh +├── CMakeLists.txt +├── README.md +├── src +│   ├── ext_lib_export.c +│   ├── iwasm_main.c +│   └── main.c +└── wasm-apps + ├── connection.c + ├── event_publisher.c + ├── event_subscriber.c + ├── request_handler.c + ├── request_sender.c + ├── sensor.c + └── timer.c +``` + +- src/ext_lib_export.c
    + This file is used to export native APIs. See the `The mechanism of exporting Native API to WASM application` section in WAMR README.md for detail. +- src/iwam_main.c
    + This file is the implementation by platform integrator. It implements the interfaces that enable the application manager communicating with the host side. See `{WAMR_ROOT}/core/app-mgr/app-mgr-shared/app_manager_export.h` for the definition of the host interface. +## Set physical communication between device and remote + + + +``` +/* Interfaces of host communication */ +typedef struct host_interface { + host_init_func init; + host_send_fun send; + host_destroy_fun destroy; +} host_interface; + +``` +The `host_init_func` is called when the application manager starts up. And `host_send_fun` is called by the application manager to send data to the host. + +Define a global variable "interface" of the data structure: + +``` + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy +}; +``` +This interface is passed to application manager during the runtime startup: +``` +app_manager_startup(&interface); +``` + +> + +**Note:** The connection between simple and host_tool is TCP by default. The simple application works as a server and the host_tool works as a client. You can also use UART connection. To achieve this you have to uncomment the below line in CMakeLists.txt and rebuild. + +``` +#add_definitions (-DCONNECTION_UART)` +``` + +To run the UART based test, you have to set up a UART hardware connection between host_tool and the simple application. See the help of host_tool for how to specify UART device parameters. + + +Build the sample +============== +Execute the build.sh script then all binaries including wasm application files would be generated in 'out' directory. + +``` +$ ./build.sh +Enter build target profile (default=host-interp) --> +arm-interp +host-aot +host-interp +\>: + +``` + +Enter the profile name for starting your build. "host-***" profiles build the sample for executing on your development machine, and "arm-interp" profile will do cross building for ARM target platform. If "arm-interp" is entered, please ensure the ARM cross compiler toolchain is already installed in your development machine. Your should set *ARM_A7_COMPILER_DIR* and *ARM_A7_SDKTARGETSYSROOT* environment variable in your ~/.bashrc correctly. refer to the file [profiles/arm-interp/toolchain.cmake](./profiles/arm-interp/toolchain.cmake). + +``` +~/.bashrc: +export ARM_A7_COMPILER_DIR="/home/beihai/cross-toolchains/gcc-linaro-arm-linux-gnueabihf-4.7-2013.03-20130313_linux/bin" +export ARM_A7_SDKTARGETSYSROOT="/home/beihai/cross-toolchains/gcc-linaro-arm-linux-gnueabihf-4.7-2013.03-20130313_linux/arm-linux-gnueabihf/libc" + +notes: please set the value to the actual path of your cross toolchain. +``` + +If you need to create additional profile for customizing your runtime, application framework or the target platforms, a new subfolder can be created under the *profiles* folder, and place your own version of "toolchain.cmake" and "wamr_config_simple.cmake" in it. + +``` +$wamr-root/samples/simple/profiles$ ls +arm-interp host-aot host-interp +$wamr-root/samples/simple/profiles$ ls arm-interp/ +toolchain.cmake wamr_config_simple.cmake + +``` + + + + + +**Out directory structure** + +``` +out/ +├── host_tool +├── simple +└── wasm-apps + ├── connection.wasm + ├── event_publisher.wasm + ├── event_subscriber.wasm + ├── request_handler.wasm + ├── request_sender.wasm + ├── sensor.wasm + └── timer.wasm +``` + +- host_tool: + A small testing tool to interact with WAMR. See the usage of this tool by executing "./host_tool -h". + `./host_tool -h` + +- simple: + A simple testing tool running on the host side that interact with WAMR. It is used to install, uninstall and query WASM applications in WAMR, and send request or subscribe event, etc. See the usage of this application by executing "./simple -h". + `./simple -h` +> + +Run the sample +========================== +- Enter the out directory +``` +$ cd ./out/ +``` + +- Startup the 'simple' process works in TCP server mode and you would see "App Manager started." is printed. +``` +$ ./simple -s +App Manager started. +``` + +- Query all installed applications +``` +$ ./host_tool -q + +response status 69 +{ + "num": 0 +} +``` + +The `69` stands for response code SUCCESS. The payload is printed with JSON format where the `num` stands for application installations number and value `0` means currently no application is installed yet. + +- Install the request handler wasm application
    +``` +$ ./host_tool -i request_handler -f ./wasm-apps/request_handler.wasm + +response status 65 +``` +Now the request handler application is running and waiting for host or other wasm application to send a request. + +- Query again +``` +$ ./host_tool -q + +response status 69 +{ + "num": 1, + "applet1": "request_handler", + "heap1": 49152 +} +``` +In the payload, we can see `num` is 1 which means 1 application is installed. `applet1`stands for the name of the 1st application. `heap1` stands for the heap size of the 1st application. + +- Send request from host to specific wasm application +``` +$ ./host_tool -r /app/request_handler/url1 -A GET + +response status 69 +{ + "key1": "value1", + "key2": "value2" +} +``` + +We can see a response with status `69` and a payload is received. + +Output of simple application: +``` +connection established! +Send request to applet: request_handler +Send request to app request_handler success. +App request_handler got request, url url1, action 1 +[resp] ### user resource 1 handler called +sent 150 bytes to host +Wasm app process request success. +``` + +- Send a general request from host (not specify target application name)
    +``` +$ ./host_tool -r /url1 -A GET + +response status 69 +{ + "key1": "value1", + "key2": "value2" +} +``` + +Output of simple application: +``` +connection established! +Send request to app request_handler success. +App request_handler got request, url /url1, action 1 +[resp] ### user resource 1 handler called +sent 150 bytes to host +Wasm app process request success. +``` + +- Install the event publisher wasm application +``` +$ ./host_tool -i pub -f ./wasm-apps/event_publisher.wasm + +response status 65 +``` + +- Subscribe event by host_tool
    +``` +$ ./host_tool -s /alert/overheat -a 3000 + +response status 69 + +received an event alert/overheat +{ + "warning": "temperature is over high" +} +received an event alert/overheat +{ + "warning": "temperature is over high" +} +received an event alert/overheat +{ + "warning": "temperature is over high" +} +received an event alert/overheat +{ + "warning": "temperature is over high" +} +``` +We can see 4 `alert/overheat` events are received in 3 seconds which is published by the `pub` application. + +Output of simple +``` +connection established! +am_register_event adding url:(alert/overheat) +client: -3 registered event (alert/overheat) +sent 16 bytes to host +sent 142 bytes to host +sent 142 bytes to host +sent 142 bytes to host +sent 142 bytes to host +``` +- Install the event subscriber wasm application
    +``` +$ ./host_tool -i sub -f ./wasm-apps/event_subscriber.wasm + +response status 65 +``` +The `sub` application is installed. + +Output of simple +``` +connection established! +Install WASM app success! +WASM app 'sub' started +am_register_event adding url:(alert/overheat) +client: 3 registered event (alert/overheat) +sent 16 bytes to host +Send request to app sub success. +App sub got request, url alert/overheat, action 6 +### user over heat event handler called +Attribute container dump: +Tag: +Attribute list: + key: warning, type: string, value: temperature is over high + +Wasm app process request success. +``` + +We can see the `sub` application receives the `alert/overheat` event and dumps it out.
    +At device side, the event is represented by an attribute container which contains key-value pairs like below: +``` +Attribute container dump: +Tag: +Attribute list: + key: warning, type: string, value: temperature is over high +``` +`warning` is the key's name. `string` means this is a string value and `temperature is over high` is the value. + +- Uninstall the wasm application
    +``` +$ ./host_tool -u request_handler + +response status 66 + +$ ./host_tool -u pub + +response status 66 + +$ ./host_tool -u sub + +response status 66 +``` + +- Query again
    +``` +$ ./host_tool -q + +response status 69 +{ + "num": 0 +} +``` + + >**Note:** Here we only installed part of the sample WASM applications. You can try others by yourself. + + >**Note:** You have to manually kill the simple process by Ctrl+C after use. diff --git a/wamr/samples/simple/build.sh b/wamr/samples/simple/build.sh new file mode 100755 index 0000000..51ec8bc --- /dev/null +++ b/wamr/samples/simple/build.sh @@ -0,0 +1,165 @@ +#!/bin/bash + +CURR_DIR=$PWD +WAMR_DIR=${PWD}/../.. +OUT_DIR=${PWD}/out +BUILD_DIR=${PWD}/build + +IWASM_ROOT=${PWD}/../../core/iwasm +APP_FRAMEWORK_DIR=${PWD}/../../core/app-framework +NATIVE_LIBS=${APP_FRAMEWORK_DIR}/app-native-shared +APP_LIB_SRC="${APP_FRAMEWORK_DIR}/base/app/*.c ${APP_FRAMEWORK_DIR}/sensor/app/*.c \ + ${APP_FRAMEWORK_DIR}/connection/app/*.c ${NATIVE_LIBS}/*.c" +WASM_APPS=${PWD}/wasm-apps +CLEAN= +CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Release" +CM_TOOLCHAIN="" + +usage () +{ + echo "build.sh [options]" + echo " -p [profile]" + echo " -d [target]" + echo " -c, rebuild SDK" + exit 1 +} + + +while getopts "p:dch" opt +do + case $opt in + p) + PROFILE=$OPTARG + ;; + d) + CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug" + ;; + c) + CLEAN="TRUE" + ;; + h) + usage + exit 1; + ;; + ?) + echo "Unknown arg: $arg" + usage + exit 1 + ;; + esac +done + + +if [ "$CLEAN" = "TRUE" ]; then + rm -rf $CURR_DIR/cmake_build +fi + + +while [ ! -n "$PROFILE" ] +do + support_profiles=`ls -l "profiles/" |grep '^d' | awk '{print $9}'` + read -p "Enter build target profile (default=host-interp) --> +$support_profiles +\>:" read_platform + if [ ! -n "$read_platform" ]; then + PROFILE="host-interp" + else + PROFILE=$read_platform + fi +done + +ARG_TOOLCHAIN="" +TOOL_CHAIN_FILE=$CURR_DIR/profiles/$PROFILE/toolchain.cmake +if [ -f $TOOL_CHAIN_FILE ]; then + CM_TOOLCHAIN="-DCMAKE_TOOLCHAIN_FILE=$TOOL_CHAIN_FILE" + ARG_TOOLCHAIN="-t $TOOL_CHAIN_FILE" + echo "toolchain file: $TOOL_CHAIN_FILE" +fi + + +SDK_CONFIG_FILE=$CURR_DIR/profiles/$PROFILE/wamr_config_simple.cmake +if [ ! -f $SDK_CONFIG_FILE ]; then + echo "SDK config file [$SDK_CONFIG_FILE] doesn't exit. quit.." + exit 1 +fi + + + +rm -rf ${OUT_DIR} +mkdir ${OUT_DIR} +mkdir ${OUT_DIR}/wasm-apps + +cd ${WAMR_DIR}/core/shared/mem-alloc +if [ ! -d "tlsf" ]; then + git clone https://github.com/mattconte/tlsf +fi + + +PROFILE="simple-$PROFILE" + + +echo "#####################build wamr sdk" +cd ${WAMR_DIR}/wamr-sdk +./build_sdk.sh -n $PROFILE -x $SDK_CONFIG_FILE $ARG_TOOLCHAIN +[ $? -eq 0 ] || exit $? + + +echo "#####################build simple project" +cd ${CURR_DIR} +mkdir -p cmake_build/$PROFILE +cd cmake_build/$PROFILE +cmake ../.. -DWAMR_BUILD_SDK_PROFILE=$PROFILE $CM_TOOLCHAIN $CM_BUILD_TYPE +make +if [ $? != 0 ];then + echo "BUILD_FAIL simple exit as $?\n" + exit 2 +fi +cp -a simple ${OUT_DIR} +echo "#####################build simple project success" + +echo -e "\n\n" +echo "#####################build host-tool" +cd ${WAMR_DIR}/test-tools/host-tool +mkdir -p bin +cd bin +cmake .. $CM_TOOLCHAIN $CM_BUILD_TYPE +make +if [ $? != 0 ];then + echo "BUILD_FAIL host tool exit as $?\n" + exit 2 +fi +cp host_tool ${OUT_DIR} +echo "#####################build host-tool success" + +echo -e "\n\n" +echo "#####################build wasm apps" + +cd ${WASM_APPS} + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm + +/opt/wasi-sdk/bin/clang \ + -I${WAMR_DIR}/wamr-sdk/out/$PROFILE/app-sdk/wamr-app-framework/include \ + -L${WAMR_DIR}/wamr-sdk/out/$PROFILE/app-sdk/wamr-app-framework/lib \ + -lapp_framework \ + --target=wasm32 -O3 -z stack-size=4096 -Wl,--initial-memory=65536 \ + --sysroot=${WAMR_DIR}/wamr-sdk/out/$PROFILE/app-sdk/libc-builtin-sysroot \ + -Wl,--allow-undefined-file=${WAMR_DIR}/wamr-sdk/out/$PROFILE/app-sdk/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=on_init -Wl,--export=on_destroy \ + -Wl,--export=on_request -Wl,--export=on_response \ + -Wl,--export=on_sensor_event -Wl,--export=on_timer_callback \ + -Wl,--export=on_connection_data \ + -Wl,--export=__heap_base -Wl,--export=__data_end \ + -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/wamr/samples/simple/profiles/arm-interp/toolchain.cmake b/wamr/samples/simple/profiles/arm-interp/toolchain.cmake new file mode 100644 index 0000000..2bd47b4 --- /dev/null +++ b/wamr/samples/simple/profiles/arm-interp/toolchain.cmake @@ -0,0 +1,38 @@ +INCLUDE(CMakeForceCompiler) + +SET(CMAKE_SYSTEM_NAME Linux) # this one is important +SET(CMAKE_SYSTEM_VERSION 1) # this one not so much + +message(STATUS "*** ARM A7 toolchain file ***") +set(CMAKE_VERBOSE_MAKEFILE ON) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE") + + +if (NOT $ENV{ARM_A7_COMPILER_DIR} STREQUAL "") + SET (toolchain_sdk_dir $ENV{ARM_A7_COMPILER_DIR}/) +endif () + +if (NOT $ENV{ARM_A7_SDKTARGETSYSROOT} STREQUAL "") + SET(SDKTARGETSYSROOT $ENV{ARM_A7_SDKTARGETSYSROOT}) + #SET(CMAKE_SYSROOT SDKTARGETSYSROOT) +endif () + +message(STATUS "SDKTARGETSYSROOT=${SDKTARGETSYSROOT}") +message(STATUS "toolchain_sdk_dir=${toolchain_sdk_dir}") + +SET(CMAKE_C_COMPILER ${toolchain_sdk_dir}arm-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER ${toolchain_sdk_dir}arm-linux-gnueabihf-g++) + + +# this is the file system root of the target +SET(CMAKE_FIND_ROOT_PATH ${SDKTARGETSYSROOT}) + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/wamr/samples/simple/profiles/arm-interp/wamr_config_simple.cmake b/wamr/samples/simple/profiles/arm-interp/wamr_config_simple.cmake new file mode 100644 index 0000000..db33d5f --- /dev/null +++ b/wamr/samples/simple/profiles/arm-interp/wamr_config_simple.cmake @@ -0,0 +1,9 @@ +set (WAMR_BUILD_PLATFORM "linux") +set (WAMR_BUILD_TARGET ARM) +set (WAMR_BUILD_INTERP 1) +set (WAMR_BUILD_AOT 0) +set (WAMR_BUILD_JIT 0) +set (WAMR_BUILD_LIBC_BUILTIN 1) +set (WAMR_BUILD_LIBC_WASI 0) +set (WAMR_BUILD_APP_FRAMEWORK 1) +set (WAMR_BUILD_APP_LIST WAMR_APP_BUILD_BASE WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) diff --git a/wamr/samples/simple/profiles/host-aot/wamr_config_simple.cmake b/wamr/samples/simple/profiles/host-aot/wamr_config_simple.cmake new file mode 100644 index 0000000..a3317f0 --- /dev/null +++ b/wamr/samples/simple/profiles/host-aot/wamr_config_simple.cmake @@ -0,0 +1,9 @@ +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 WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) diff --git a/wamr/samples/simple/profiles/host-interp/wamr_config_simple.cmake b/wamr/samples/simple/profiles/host-interp/wamr_config_simple.cmake new file mode 100644 index 0000000..a3317f0 --- /dev/null +++ b/wamr/samples/simple/profiles/host-interp/wamr_config_simple.cmake @@ -0,0 +1,9 @@ +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 WAMR_APP_BUILD_CONNECTION WAMR_APP_BUILD_SENSOR) diff --git a/wamr/samples/simple/src/iwasm_main.c b/wamr/samples/simple/src/iwasm_main.c new file mode 100644 index 0000000..41bfbbf --- /dev/null +++ b/wamr/samples/simple/src/iwasm_main.c @@ -0,0 +1,526 @@ + +#ifndef CONNECTION_UART +#include +#include +#include +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runtime_lib.h" +#include "runtime_timer.h" +#include "native_interface.h" +#include "app_manager_export.h" +#include "bh_platform.h" +#include "runtime_sensor.h" +#include "bi-inc/attr_container.h" +#include "module_wasm_app.h" +#include "wasm_export.h" + +#define MAX 2048 + +#ifndef CONNECTION_UART +#define SA struct sockaddr +static char *host_address = "127.0.0.1"; +static int port = 8888; +#else +static char *uart_device = "/dev/ttyS2"; +static int baudrate = B115200; +#endif + +extern void init_sensor_framework(); +extern void exit_sensor_framework(); +extern void exit_connection_framework(); +extern int aee_host_msg_callback(void *msg, uint16_t msg_len); +extern bool init_connection_framework(); + +#ifndef CONNECTION_UART +int listenfd = -1; +int sockfd = -1; +static pthread_mutex_t sock_lock = PTHREAD_MUTEX_INITIALIZER; +#else +int uartfd = -1; +#endif + +#ifndef CONNECTION_UART +static bool server_mode = false; + +// Function designed for chat between client and server. +void* func(void* arg) +{ + char buff[MAX]; + int n; + struct sockaddr_in servaddr; + + while (1) { + if (sockfd != -1) + close(sockfd); + // socket create and verification + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + printf("socket creation failed...\n"); + return NULL; + } else + printf("Socket successfully created..\n"); + bzero(&servaddr, sizeof(servaddr)); + // assign IP, PORT + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(host_address); + servaddr.sin_port = htons(port); + + // connect the client socket to server socket + if (connect(sockfd, (SA*) &servaddr, sizeof(servaddr)) != 0) { + printf("connection with the server failed...\n"); + sleep(10); + continue; + } else { + printf("connected to the server..\n"); + } + + // infinite loop for chat + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + // print buffer which contains the client contents + //fprintf(stderr, "recieved %d bytes from host: %s", n, buff); + + // socket disconnected + if (n <= 0) + break; + + aee_host_msg_callback(buff, n); + } + } + + // After chatting close the socket + close(sockfd); +} + +static bool host_init() +{ + return true; +} + +int host_send(void * ctx, const char *buf, int size) +{ + int ret; + + if (pthread_mutex_trylock(&sock_lock) == 0) { + if (sockfd == -1) { + pthread_mutex_unlock(&sock_lock); + return 0; + } + + ret = write(sockfd, buf, size); + + pthread_mutex_unlock(&sock_lock); + return ret; + } + + return -1; +} + +void host_destroy() +{ + if (server_mode) + close(listenfd); + + pthread_mutex_lock(&sock_lock); + close(sockfd); + pthread_mutex_unlock(&sock_lock); +} + +host_interface interface = { + .init = host_init, + .send = host_send, + .destroy = host_destroy +}; + +/* Change it to 1 when fuzzing test */ +#define WASM_ENABLE_FUZZ_TEST 0 + +void* func_server_mode(void* arg) +{ + int clilent; + struct sockaddr_in serv_addr, cli_addr; + int n; + char buff[MAX]; + + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, 0); + + /* First call to socket() function */ + listenfd = socket(AF_INET, SOCK_STREAM, 0); + + if (listenfd < 0) { + perror("ERROR opening socket"); + exit(1); + } + + /* Initialize socket structure */ + bzero((char *) &serv_addr, sizeof(serv_addr)); + + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(port); + + /* Now bind the host address using bind() call.*/ + if (bind(listenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + perror("ERROR on binding"); + exit(1); + } + + listen(listenfd, 5); + clilent = sizeof(cli_addr); + + while (1) { + pthread_mutex_lock(&sock_lock); + + sockfd = accept(listenfd, (struct sockaddr *) &cli_addr, &clilent); + + pthread_mutex_unlock(&sock_lock); + + if (sockfd < 0) { + perror("ERROR on accept"); + exit(1); + } + + printf("connection established!\n"); + + for (;;) { + bzero(buff, MAX); + + // read the message from client and copy it in buffer + n = read(sockfd, buff, sizeof(buff)); + + // socket disconnected + if (n <= 0) { + pthread_mutex_lock(&sock_lock); + close(sockfd); + sockfd = -1; + pthread_mutex_unlock(&sock_lock); + + sleep(1); + break; + } + + aee_host_msg_callback(buff, n); + } +#if WASM_ENABLE_FUZZ_TEST != 0 + /* Exit the process when host disconnect. + * This is helpful for reproducing failure case. */ + close(sockfd); + exit(1); +#endif + } +} + +#else +static int parse_baudrate(int baud) +{ + switch (baud) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} +static bool uart_init(const char *device, int baudrate, int *fd) +{ + int uart_fd; + struct termios uart_term; + + uart_fd = open(device, O_RDWR | O_NOCTTY); + + if (uart_fd <= 0) + return false; + + memset(&uart_term, 0, sizeof(uart_term)); + uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; + uart_term.c_iflag = IGNPAR; + uart_term.c_oflag = 0; + + /* set noncanonical mode */ + uart_term.c_lflag = 0; + uart_term.c_cc[VTIME] = 30; + uart_term.c_cc[VMIN] = 1; + tcflush(uart_fd, TCIFLUSH); + + if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { + close(uart_fd); + return false; + } + + *fd = uart_fd; + + return true; +} + +static void *func_uart_mode(void *arg) +{ + int n; + char buff[MAX]; + + if (!uart_init(uart_device, baudrate, &uartfd)) { + printf("open uart fail! %s\n", uart_device); + return NULL; + } + + for (;;) { + bzero(buff, MAX); + + n = read(uartfd, buff, sizeof(buff)); + + if (n <= 0) { + close(uartfd); + uartfd = -1; + break; + } + + aee_host_msg_callback(buff, n); + } + + return NULL; +} + +static int uart_send(void * ctx, const char *buf, int size) +{ + int ret; + + ret = write(uartfd, buf, size); + + return ret; +} + +static void uart_destroy() +{ + close(uartfd); +} + +static host_interface interface = { .send = uart_send, .destroy = uart_destroy }; + +#endif + + + +static attr_container_t * read_test_sensor(void * sensor) +{ + //luc: for test + attr_container_t *attr_obj = attr_container_create("read test sensor data"); + if (attr_obj) { + bool ret = attr_container_set_string(&attr_obj, "name", "read test sensor"); + if (!ret) { + attr_container_destroy(attr_obj); + return NULL; + } + return attr_obj; + } + return NULL; +} + +static bool config_test_sensor(void * s, void * config) +{ + return false; +} + + +static char global_heap_buf[1024 * 1024] = { 0 }; + +static void showUsage() +{ +#ifndef CONNECTION_UART + printf("Usage:\n"); + printf("\nWork as TCP server mode:\n"); + printf("\tsimple -s|--server_mode -p|--port \n"); + printf("where\n"); + printf("\t represents the port that would be listened on and the default is 8888\n"); + printf("\nWork as TCP client mode:\n"); + printf("\tsimple -a|--host_address -p|--port \n"); + printf("where\n"); + printf("\t represents the network address of host and the default is 127.0.0.1\n"); + printf("\t represents the listen port of host and the default is 8888\n"); +#else + printf("Usage:\n"); + printf("\tsimple -u -b \n\n"); + printf("where\n"); + printf("\t represents the UART device name and the default is /dev/ttyS2\n"); + printf("\t represents the UART device baudrate and the default is 115200\n"); +#endif +} + +static bool parse_args(int argc, char *argv[]) +{ + int c; + + while (1) { + int optIndex = 0; + static struct option longOpts[] = { +#ifndef CONNECTION_UART + { "server_mode", no_argument, NULL, 's' }, + { "host_address", required_argument, NULL, 'a' }, + { "port", required_argument, NULL, 'p' }, +#else + { "uart", required_argument, NULL, 'u' }, + { "baudrate", required_argument, NULL, 'b' }, +#endif + { "help", required_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "sa:p:u:b:w:h", longOpts, &optIndex); + if (c == -1) + break; + + switch (c) { +#ifndef CONNECTION_UART + case 's': + server_mode = true; + break; + case 'a': + host_address = optarg; + printf("host address: %s\n", host_address); + break; + case 'p': + port = atoi(optarg); + printf("port: %d\n", port); + break; +#else + case 'u': + uart_device = optarg; + printf("uart device: %s\n", uart_device); + break; + case 'b': + baudrate = parse_baudrate(atoi(optarg)); + printf("uart baudrate: %s\n", optarg); + break; +#endif + case 'h': + showUsage(); + return false; + default: + showUsage(); + return false; + } + } + + return true; +} + +// Driver function +int iwasm_main(int argc, char *argv[]) +{ + RuntimeInitArgs init_args; + korp_tid tid; + + if (!parse_args(argc, argv)) + return -1; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + 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); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + /* timer manager */ + init_wasm_timer(); + + /* connection framework */ + if (!init_connection_framework()) { + goto fail1; + } + + /* sensor framework */ + init_sensor_framework(); + // add the sys sensor objects + add_sys_sensor("sensor_test", + "This is a sensor for test", + 0, + 1000, + read_test_sensor, + config_test_sensor); + start_sensor_framework(); + +#ifndef CONNECTION_UART + if (server_mode) + os_thread_create(&tid, func_server_mode, NULL, + BH_APPLET_PRESERVED_STACK_SIZE); + else + os_thread_create(&tid, func, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#else + os_thread_create(&tid, func_uart_mode, NULL, BH_APPLET_PRESERVED_STACK_SIZE); +#endif + + app_manager_startup(&interface); + + exit_wasm_timer(); + exit_sensor_framework(); + exit_connection_framework(); + +fail1: + wasm_runtime_destroy(); + + return -1; +} diff --git a/wamr/samples/simple/src/main.c b/wamr/samples/simple/src/main.c new file mode 100644 index 0000000..5bf24ce --- /dev/null +++ b/wamr/samples/simple/src/main.c @@ -0,0 +1,6 @@ +extern void iwasm_main(); +int main(int argc, char *argv[]) +{ + iwasm_main(argc, argv); + return 0; +} diff --git a/wamr/samples/simple/wasm-apps/connection.c b/wamr/samples/simple/wasm-apps/connection.c new file mode 100644 index 0000000..ee43a33 --- /dev/null +++ b/wamr/samples/simple/wasm-apps/connection.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/connection.h" +#include "wa-inc/timer_wasm_app.h" +#include "wa-inc/request.h" + +/* User global variable */ +static int num = 0; +static user_timer_t g_timer; +static connection_t *g_conn = NULL; + +void on_data1(connection_t *conn, + conn_event_type_t type, + const char *data, + uint32 len, + void *user_data) +{ + if (type == CONN_EVENT_TYPE_DATA) { + char message[64] = {0}; + memcpy(message, data, len); + printf("Client got a message from server -> %s\n", message); + } else if (type == CONN_EVENT_TYPE_DISCONNECT) { + printf("connection is close by server!\n"); + } else { + printf("error: got unknown event type!!!\n"); + } +} + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + char message[64] = {0}; + /* Reply to server */ + snprintf(message, sizeof(message), "Hello %d", num++); + api_send_on_connection(g_conn, message, strlen(message)); +} + +void my_close_handler(request_t * request) +{ + response_t response[1]; + + if (g_conn != NULL) { + api_timer_cancel(g_timer); + api_close_connection(g_conn); + } + + make_response_for_request(request, response); + set_response(response, DELETED_2_02, 0, NULL, 0); + api_response_send(response); +} + +void on_init() +{ + user_timer_t timer; + attr_container_t *args; + char *str = "this is client!"; + + api_register_resource_handler("/close", my_close_handler); + + args = attr_container_create(""); + attr_container_set_string(&args, "address", "127.0.0.1"); + attr_container_set_uint16(&args, "port", 7777); + + g_conn = api_open_connection("TCP", args, on_data1, NULL); + if (g_conn == NULL) { + printf("connect to server fail!\n"); + return; + } + + printf("connect to server success! handle: %p\n", g_conn); + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/event_publisher.c b/wamr/samples/simple/wasm-apps/event_publisher.c new file mode 100644 index 0000000..457f428 --- /dev/null +++ b/wamr/samples/simple/wasm-apps/event_publisher.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/request.h" +#include "wa-inc/timer_wasm_app.h" + +int num = 0; + +void publish_overheat_event() +{ + attr_container_t *event; + + event = attr_container_create("event"); + attr_container_set_string(&event, "warning", "temperature is over high"); + + api_publish_event("alert/overheat", FMT_ATTR_CONTAINER, event, + attr_container_get_serialize_length(event)); + + attr_container_destroy(event); +} + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + publish_overheat_event(); +} + +void start_timer() +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_init() +{ + start_timer(); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/event_subscriber.c b/wamr/samples/simple/wasm-apps/event_subscriber.c new file mode 100644 index 0000000..a061eff --- /dev/null +++ b/wamr/samples/simple/wasm-apps/event_subscriber.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +void over_heat_event_handler(request_t *request) +{ + printf("### user over heat event handler called\n"); + + if (request->payload != NULL && request->fmt == FMT_ATTR_CONTAINER) + attr_container_dump((attr_container_t *) request->payload); +} + +void on_init() +{ + api_subscribe_event("alert/overheat", over_heat_event_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/request_handler.c b/wamr/samples/simple/wasm-apps/request_handler.c new file mode 100644 index 0000000..e944daa --- /dev/null +++ b/wamr/samples/simple/wasm-apps/request_handler.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +static void url1_request_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + + printf("[resp] ### user resource 1 handler called\n"); + + if (request->payload != NULL && request->fmt == FMT_ATTR_CONTAINER) + attr_container_dump((attr_container_t *) request->payload); + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + make_response_for_request(request, response); + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, + (void *)payload, + attr_container_get_serialize_length(payload)); + api_response_send(response); + + attr_container_destroy(payload); +} + +static void url2_request_handler(request_t *request) +{ + response_t response[1]; + make_response_for_request(request, response); + set_response(response, DELETED_2_02, 0, NULL, 0); + api_response_send(response); + + printf("### user resource 2 handler called\n"); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/url1", url1_request_handler); + api_register_resource_handler("/url2", url2_request_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/request_sender.c b/wamr/samples/simple/wasm-apps/request_sender.c new file mode 100644 index 0000000..97dba40 --- /dev/null +++ b/wamr/samples/simple/wasm-apps/request_sender.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +static void my_response_handler(response_t *response, void *user_data) +{ + char *tag = (char *) user_data; + + if (response == NULL) { + printf("[req] request timeout!\n"); + return; + } + + printf("[req] response handler called mid:%d, status:%d, fmt:%d, payload:%p, len:%d, tag:%s\n", + response->mid, response->status, response->fmt, response->payload, + response->payload_len, tag); + + if (response->payload != NULL + && response->payload_len > 0 + && response->fmt == FMT_ATTR_CONTAINER) { + printf("[req] dump the response payload:\n"); + attr_container_dump((attr_container_t *) response->payload); + } +} + +static void test_send_request(char *url, char *tag) +{ + request_t request[1]; + + init_request(request, url, COAP_PUT, 0, NULL, 0); + api_send_request(request, my_response_handler, tag); +} + +void on_init() +{ + test_send_request("/app/request_handler/url1", "a request to target app"); + test_send_request("url1", "a general request"); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/sensor.c b/wamr/samples/simple/wasm-apps/sensor.c new file mode 100644 index 0000000..717adc5 --- /dev/null +++ b/wamr/samples/simple/wasm-apps/sensor.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/sensor.h" + +static sensor_t sensor = NULL; + +/* Sensor event callback*/ +void sensor_event_handler(sensor_t sensor, attr_container_t *event, + void *user_data) +{ + printf("### app get sensor event\n"); + attr_container_dump(event); +} + +void on_init() +{ + char *user_data; + attr_container_t *config; + + printf("### app on_init 1\n"); + /* open a sensor */ + user_data = malloc(100); + printf("### app on_init 2\n"); + sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); + printf("### app on_init 3\n"); + + /* config the sensor */ + sensor_config(sensor, 1000, 0, 0); + printf("### app on_init 4\n"); + + /* + config = attr_container_create("sensor config"); + sensor_config(sensor, config); + attr_container_destroy(config); + */ +} + +void on_destroy() +{ + if (NULL != sensor) { + sensor_config(sensor, 0, 0, 0); + } + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/simple/wasm-apps/timer.c b/wamr/samples/simple/wasm-apps/timer.c new file mode 100644 index 0000000..51438c8 --- /dev/null +++ b/wamr/samples/simple/wasm-apps/timer.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "wasm_app.h" +#include "wa-inc/timer_wasm_app.h" + +/* User global variable */ +static int num = 0; + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + printf("Timer update %d\n", num++); +} + +void on_init() +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is + accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/samples/spawn-thread/CMakeLists.txt b/wamr/samples/spawn-thread/CMakeLists.txt new file mode 100644 index 0000000..b40af9a --- /dev/null +++ b/wamr/samples/spawn-thread/CMakeLists.txt @@ -0,0 +1,52 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(spawn_thread) + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_JIT 0) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_LIB_PTHREAD 1) + +# compiling and linking flags +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + + +################ wasm application ################ +add_subdirectory(wasm-apps) + +################ wamr runtime ################ +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +set (RUNTIME_SOURCE_ALL + ${CMAKE_CURRENT_LIST_DIR}/src/main.c + ${UNCOMMON_SHARED_SOURCE} +) +add_executable (spawn_thread ${RUNTIME_SOURCE_ALL}) +target_link_libraries(spawn_thread vmlib -lpthread -lm) diff --git a/wamr/samples/spawn-thread/src/main.c b/wamr/samples/spawn-thread/src/main.c new file mode 100644 index 0000000..c1d1505 --- /dev/null +++ b/wamr/samples/spawn-thread/src/main.c @@ -0,0 +1,220 @@ +/* + * 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" +#include "pthread.h" + +#define THREAD_NUM 10 + +typedef struct ThreadArgs { + wasm_exec_env_t exec_env; + int start; + int length; +} ThreadArgs; + +void *thread(void* arg) +{ + ThreadArgs *thread_arg = (ThreadArgs *)arg; + wasm_exec_env_t exec_env = thread_arg->exec_env; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_function_inst_t func; + uint32 argv[2]; + + func = wasm_runtime_lookup_function(module_inst, "sum", NULL); + if (!func) { + printf("failed to lookup function sum"); + } + argv[0] = thread_arg->start; + argv[1] = thread_arg->length; + + /* call the WASM function */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, argv)) { + printf("%s\n", wasm_runtime_get_exception(module_inst)); + return NULL; + } + + return (void *)(uintptr_t)argv[0]; +} + +void *wamr_thread_cb(wasm_exec_env_t exec_env, void *arg) +{ + ThreadArgs *thread_arg = (ThreadArgs *)arg; + wasm_module_inst_t module_inst = get_module_inst(exec_env); + wasm_function_inst_t func; + uint32 argv[2]; + + func = wasm_runtime_lookup_function(module_inst, "sum", NULL); + if (!func) { + printf("failed to lookup function sum"); + } + argv[0] = thread_arg->start; + argv[1] = thread_arg->length; + + /* call the WASM function */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, argv)) { + printf("%s\n", wasm_runtime_get_exception(module_inst)); + return NULL; + } + + return (void *)(uintptr_t)argv[0]; +} + +int main(int argc, char *argv[]) +{ + char *wasm_file = "wasm-apps/test.wasm"; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size, wasm_argv[2], i; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + wasm_exec_env_t exec_env = NULL; + RuntimeInitArgs init_args; + ThreadArgs thread_arg[THREAD_NUM]; + pthread_t tid[THREAD_NUM]; + wasm_thread_t wasm_tid[THREAD_NUM]; + uint32 result[THREAD_NUM], sum; + wasm_function_inst_t func; + char error_buf[128] = { 0 }; + + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; + init_args.max_thread_num = THREAD_NUM; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + /* Create the first exec_env */ + if (!(exec_env = + wasm_runtime_create_exec_env(wasm_module_inst, stack_size))) { + printf("failed to create exec_env\n"); + goto fail4; + } + + func = wasm_runtime_lookup_function(wasm_module_inst, "sum", NULL); + if (!func) { + printf("failed to lookup function sum"); + } + wasm_argv[0] = 0; + wasm_argv[1] = THREAD_NUM * 10; + + /* + * Execute the wasm function in current thread, get the expect result + */ + if (!wasm_runtime_call_wasm(exec_env, func, 2, wasm_argv)) { + printf("%s\n", wasm_runtime_get_exception(wasm_module_inst)); + } + printf("expect result: %d\n", wasm_argv[0]); + + /* + * Run wasm function in multiple thread created by pthread_create + */ + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + wasm_exec_env_t new_exec_env; + thread_arg[i].start = 10 * i; + thread_arg[i].length = 10; + + /* spawn a new exec_env to be executed in other threads */ + new_exec_env = wasm_runtime_spawn_exec_env(exec_env); + if (new_exec_env) + thread_arg[i].exec_env = new_exec_env; + else { + printf("failed to spawn exec_env\n"); + break; + } + + /* If we use: + thread_arg[i].exec_env = exec_env, + we may get wrong result */ + + if (0 != pthread_create(&tid[i], NULL, thread, &thread_arg[i])) { + printf("failed to create thread.\n"); + break; + } + } + + sum = 0; + memset(result, 0, sizeof(uint32) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + pthread_join(tid[i], (void **)&result[i]); + sum += result[i]; + /* destroy the spawned exec_env */ + if (thread_arg[0].exec_env) + wasm_runtime_destroy_spawned_exec_env(thread_arg[i].exec_env); + } + + printf("[pthread]sum result: %d\n", sum); + + /* + * Run wasm function in multiple thread created by wamr spawn API + */ + memset(thread_arg, 0, sizeof(ThreadArgs) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + thread_arg[i].start = 10 * i; + thread_arg[i].length = 10; + + /* No need to spawn exec_env manually */ + if (0 != wasm_runtime_spawn_thread(exec_env, &wasm_tid[i], + wamr_thread_cb, &thread_arg[i])) { + printf("failed to spawn thread.\n"); + break; + } + } + + sum = 0; + memset(result, 0, sizeof(uint32) * THREAD_NUM); + for (i = 0; i < THREAD_NUM; i++) { + wasm_runtime_join_thread(wasm_tid[i], (void**)&result[i]); + sum += result[i]; + /* No need to destroy the spawned exec_env */ + } + printf("[spwan_thread]sum result: %d\n", sum); + + wasm_runtime_destroy_exec_env(exec_env); + +fail4: + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); +} \ No newline at end of file diff --git a/wamr/samples/spawn-thread/wasm-apps/CMakeLists.txt b/wamr/samples/spawn-thread/wasm-apps/CMakeLists.txt new file mode 100644 index 0000000..52ee7d7 --- /dev/null +++ b/wamr/samples/spawn-thread/wasm-apps/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(wasm-apps) + +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..) + +if (APPLE) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + set (CMAKE_C_LINK_FLAGS "") + set (CMAKE_CXX_LINK_FLAGS "") +endif () +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set (CMAKE_SYSROOT ${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + set (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +set (CMAKE_C_FLAGS "-nostdlib -pthread -Qunused-arguments") +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -z stack-size=32768") +set (CMAKE_C_COMPILER_TARGET "wasm32") +set (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +set (DEFINED_SYMBOLS +"${WAMR_ROOT_DIR}/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt") + +set (CMAKE_EXE_LINKER_FLAGS + "-Wl,--shared-memory,--max-memory=131072, \ + -Wl,--no-entry,--strip-all,--export=sum, \ + -Wl,--export=__heap_base,--export=__data_end \ + -Wl,--export=__wasm_call_ctors \ + -Wl,--allow-undefined-file=${DEFINED_SYMBOLS}" +) + +add_executable(test.wasm sum.c) +target_link_libraries(test.wasm) diff --git a/wamr/samples/spawn-thread/wasm-apps/sum.c b/wamr/samples/spawn-thread/wasm-apps/sum.c new file mode 100644 index 0000000..31c06e3 --- /dev/null +++ b/wamr/samples/spawn-thread/wasm-apps/sum.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +int sum(int start, int length) +{ + int sum = 0, i; + + for (i = start; i < start + length; i++) { + sum += i; + } + + return sum; +} \ No newline at end of file diff --git a/wamr/samples/wasm-c-api/CMakeLists.txt b/wamr/samples/wasm-c-api/CMakeLists.txt new file mode 100644 index 0000000..dc55540 --- /dev/null +++ b/wamr/samples/wasm-c-api/CMakeLists.txt @@ -0,0 +1,100 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) +project(c-api) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") + +if(NOT DEFINED WAMR_BUILD_INTERP) + set(WAMR_BUILD_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_JOT) + set(WAMR_BUILD_JIT 0) +endif() + +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 0) + +if(NOT DEFINED WAMR_BUILD_FAST_INTERP) + set(WAMR_BUILD_FAST_INTERP 0) +endif() + +# compiling and linking flags +set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif () +set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () +endif () + +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(WAMRC ${WAMR_ROOT_DIR}/wamr-compiler/build/wamrc) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +################################################ + +################ application related ################ +file(GLOB SOURCES src/*.c) +add_library(c-api ${SOURCES}) +target_include_directories(c-api + PRIVATE ${C_API_PATH}/include +) +target_link_libraries(c-api PRIVATE vmlib -lpthread -lm) + +foreach(SRC ${SOURCES}) + get_filename_component(APPNAME ${SRC} NAME_WE) + + # build executable for each .c + add_executable(${APPNAME} ${SRC}) + message("create executable about ${APPNAME}") + target_link_libraries(${APPNAME} c-api) + + # copy .wasm + add_custom_command(TARGET ${APPNAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/src/${APPNAME}.wasm + ${PROJECT_BINARY_DIR}/ + BYPRODUCTS ${APPNAME}.wasm + COMMENT "Copy ${SRC} to the output directory" + ) + + # generate .aot file + if(${WAMR_BUILD_AOT} EQUAL 1) + if(EXISTS ${WAMRC}) + add_custom_command(TARGET ${APPNAME} POST_BUILD + COMMAND ${WAMRC} -o ${APPNAME}.aot + ${CMAKE_CURRENT_SOURCE_DIR}/src/${APPNAME}.wasm + BYPRODUCTS ${APPNAME}.aot + COMMENT "generate a aot file ${APPNAME}.aot" + ) + endif() + endif() + +endforeach(SRC ${SOURCES}) +################################################ diff --git a/wamr/samples/wasm-c-api/README.md b/wamr/samples/wasm-c-api/README.md new file mode 100644 index 0000000..31d2aea --- /dev/null +++ b/wamr/samples/wasm-c-api/README.md @@ -0,0 +1,39 @@ +WAMR supports *wasm-c-api* in both *interpreter* mode and *aot* mode. By default, +all samples are compiled and run in "interpreter" mode. + +``` shell +$ mkdir build +$ cd build +$ cmake .. +$ make +$ # it will build a library with c-api supporting. +$ # Also copy *.wasm from ../src/ +$ # and generate executable files +$ # now, it is ok to run samples +$ ./hello +$ ... +$ ./global +$ ... +$ ./callback +$ ... +``` + +They can be compiled and run in *aot* mode when some compiling flags are given. + +``` shell +$ mkdir build +$ cd build +$ cmake -DWAMR_BUILD_INTERP=0 -DWAMR_BUILD_AOT=1 .. +$ make +$ # it will build a library with c-api supporting. +$ # Also copy *.wasm from ../src/ +$ # and transform *.wasm to *.aot +$ # and generate executable files +$ # now, it is ok to run samples +$ ./hello +$ ... +$ ./global +$ ... +$ ./callback +$ ... +``` \ No newline at end of file diff --git a/wamr/samples/wasm-c-api/src/callback.c b/wamr/samples/wasm-c-api/src/callback.c new file mode 100644 index 0000000..da3e749 --- /dev/null +++ b/wamr/samples/wasm-c-api/src/callback.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// Print a Wasm value +void wasm_val_print(wasm_val_t val) { + switch (val.kind) { + case WASM_I32: { + printf("%" PRIu32, val.of.i32); + } break; + case WASM_I64: { + printf("%" PRIu64, val.of.i64); + } break; + case WASM_F32: { + printf("%f", val.of.f32); + } break; + case WASM_F64: { + printf("%g", val.of.f64); + } break; + case WASM_ANYREF: + case WASM_FUNCREF: { + if (val.of.ref == NULL) { + printf("null"); + } else { + printf("ref(%p)", val.of.ref); + } + } break; + } +} + +// A function to be called from Wasm code. +own wasm_trap_t* print_callback( + const wasm_val_t args[], wasm_val_t results[] +) { + printf("Calling back...\n> "); + wasm_val_print(args[0]); + printf("\n"); + + wasm_val_copy(&results[0], &args[0]); + return NULL; +} + + +// A function closure. +own wasm_trap_t* closure_callback( + void* env, const wasm_val_t args[], wasm_val_t results[] +) { + int i = *(int*)env; + printf("Calling back closure...\n"); + printf("> %d\n", i); + + results[0].kind = WASM_I32; + results[0].of.i32 = (int32_t)i; + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("callback.aot", "rb"); +#else + FILE* file = fopen("callback.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* print_type = wasm_functype_new_1_1(wasm_valtype_new_i32(), wasm_valtype_new_i32()); + own wasm_func_t* print_func = wasm_func_new(store, print_type, print_callback); + + int i = 42; + own wasm_functype_t* closure_type = wasm_functype_new_0_1(wasm_valtype_new_i32()); + own wasm_func_t* closure_func = wasm_func_new_with_env(store, closure_type, closure_callback, &i, NULL); + + wasm_functype_delete(print_type); + wasm_functype_delete(closure_type); + + // Instantiate. + printf("Instantiating module...\n"); + const wasm_extern_t* imports[] = { + wasm_func_as_extern(print_func), wasm_func_as_extern(closure_func) + }; + own wasm_instance_t* instance = + wasm_instance_new(store, module, imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(print_func); + wasm_func_delete(closure_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + wasm_val_t args[2]; + args[0].kind = WASM_I32; + args[0].of.i32 = 3; + args[1].kind = WASM_I32; + args[1].of.i32 = 4; + wasm_val_t results[1]; + if (wasm_func_call(run_func, args, results)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Print result. + printf("Printing result...\n"); + printf("> %u\n", results[0].of.i32); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/wamr/samples/wasm-c-api/src/callback.wasm b/wamr/samples/wasm-c-api/src/callback.wasm new file mode 100644 index 0000000000000000000000000000000000000000..7e00b580142c1e8171c18577fca06d1a39f2b48c GIT binary patch literal 102 zcmW+sy9$6H6g~G!2(|bLewq;tNm*#lN=r*a1pRN?aNY-fSO@^!IcEq%iIPD9r{egn nEu-1|Lqn5QP-MFgPD&Y~ literal 0 HcmV?d00001 diff --git a/wamr/samples/wasm-c-api/src/callback.wat b/wamr/samples/wasm-c-api/src/callback.wat new file mode 100644 index 0000000..d86195f --- /dev/null +++ b/wamr/samples/wasm-c-api/src/callback.wat @@ -0,0 +1,10 @@ +(module + (func $print (import "" "print") (param i32) (result i32)) + (func $closure (import "" "closure") (result i32)) + (func (export "run") (param $x i32) (param $y i32) (result i32) + (i32.add + (call $print (i32.add (local.get $x) (local.get $y))) + (call $closure) + ) + ) +) diff --git a/wamr/samples/wasm-c-api/src/global.c b/wamr/samples/wasm-c-api/src/global.c new file mode 100644 index 0000000..ba73d95 --- /dev/null +++ b/wamr/samples/wasm-c-api/src/global.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +wasm_global_t* get_export_global(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_global(exports->data[i])) { + printf("> Error accessing global export %zu!\n", i); + exit(1); + } + return wasm_extern_as_global(exports->data[i]); +} + +wasm_func_t* get_export_func(const wasm_extern_vec_t* exports, size_t i) { + if (exports->size <= i || !wasm_extern_as_func(exports->data[i])) { + printf("> Error accessing function export %zu!\n", i); + exit(1); + } + return wasm_extern_as_func(exports->data[i]); +} + + +#define check(val, type, expected) \ + if (val.of.type != expected) { \ + printf("> Error reading value\n"); \ + exit(1); \ + } + +#define check_global(global, type, expected) \ + { \ + wasm_val_t val; \ + wasm_global_get(global, &val); \ + check(val, type, expected); \ + } + +#define check_call(func, type, expected) \ + { \ + wasm_val_t results[1]; \ + wasm_func_call(func, NULL, results); \ + check(results[0], type, expected); \ + } + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("global.aot", "rb"); +#else + FILE* file = fopen("global.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external globals. + printf("Creating globals...\n"); + own wasm_globaltype_t* const_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_CONST); + own wasm_globaltype_t* const_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_CONST); + own wasm_globaltype_t* var_f32_type = wasm_globaltype_new( + wasm_valtype_new(WASM_F32), WASM_VAR); + own wasm_globaltype_t* var_i64_type = wasm_globaltype_new( + wasm_valtype_new(WASM_I64), WASM_VAR); + + wasm_val_t val_f32_1 = {.kind = WASM_F32, .of = {.f32 = 1}}; + own wasm_global_t* const_f32_import = + wasm_global_new(store, const_f32_type, &val_f32_1); + wasm_val_t val_i64_2 = {.kind = WASM_I64, .of = {.i64 = 2}}; + own wasm_global_t* const_i64_import = + wasm_global_new(store, const_i64_type, &val_i64_2); + wasm_val_t val_f32_3 = {.kind = WASM_F32, .of = {.f32 = 3}}; + own wasm_global_t* var_f32_import = + wasm_global_new(store, var_f32_type, &val_f32_3); + wasm_val_t val_i64_4 = {.kind = WASM_I64, .of = {.i64 = 4}}; + own wasm_global_t* var_i64_import = + wasm_global_new(store, var_i64_type, &val_i64_4); + + wasm_globaltype_delete(const_f32_type); + wasm_globaltype_delete(const_i64_type); + wasm_globaltype_delete(var_f32_type); + wasm_globaltype_delete(var_i64_type); + + // Instantiate. + printf("Instantiating module...\n"); + const wasm_extern_t* imports[] = { + wasm_global_as_extern(const_f32_import), + wasm_global_as_extern(const_i64_import), + wasm_global_as_extern(var_f32_import), + wasm_global_as_extern(var_i64_import) + }; + own wasm_instance_t* instance = + wasm_instance_new(store, module, imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_module_delete(module); + + // Extract export. + printf("Extracting exports...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + size_t i = 0; + wasm_global_t* const_f32_export = get_export_global(&exports, i++); + wasm_global_t* const_i64_export = get_export_global(&exports, i++); + wasm_global_t* var_f32_export = get_export_global(&exports, i++); + wasm_global_t* var_i64_export = get_export_global(&exports, i++); + wasm_func_t* get_const_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* get_const_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_const_i64_export = get_export_func(&exports, i++); + wasm_func_t* get_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* get_var_i64_export = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_import = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_import = get_export_func(&exports, i++); + wasm_func_t* set_var_f32_export = get_export_func(&exports, i++); + wasm_func_t* set_var_i64_export = get_export_func(&exports, i++); + + // Try cloning. + own wasm_global_t* copy = wasm_global_copy(var_f32_import); + assert(wasm_global_same(var_f32_import, copy)); + wasm_global_delete(copy); + + // Interact. + printf("Accessing globals...\n"); + + // Check initial values. + check_global(const_f32_import, f32, 1); + check_global(const_i64_import, i64, 2); + check_global(var_f32_import, f32, 3); + check_global(var_i64_import, i64, 4); + check_global(const_f32_export, f32, 5); + check_global(const_i64_export, i64, 6); + check_global(var_f32_export, f32, 7); + check_global(var_i64_export, i64, 8); + + check_call(get_const_f32_import, f32, 1); + check_call(get_const_i64_import, i64, 2); + check_call(get_var_f32_import, f32, 3); + check_call(get_var_i64_import, i64, 4); + check_call(get_const_f32_export, f32, 5); + check_call(get_const_i64_export, i64, 6); + check_call(get_var_f32_export, f32, 7); + check_call(get_var_i64_export, i64, 8); + + // Modify variables through API and check again. + wasm_val_t val33 = {.kind = WASM_F32, .of = {.f32 = 33}}; + wasm_global_set(var_f32_import, &val33); + wasm_val_t val34 = {.kind = WASM_I64, .of = {.i64 = 34}}; + wasm_global_set(var_i64_import, &val34); + wasm_val_t val37 = {.kind = WASM_F32, .of = {.f32 = 37}}; + wasm_global_set(var_f32_export, &val37); + wasm_val_t val38 = {.kind = WASM_I64, .of = {.i64 = 38}}; + wasm_global_set(var_i64_export, &val38); + + check_global(var_f32_import, f32, 33); + check_global(var_i64_import, i64, 34); + check_global(var_f32_export, f32, 37); + check_global(var_i64_export, i64, 38); + + check_call(get_var_f32_import, f32, 33); + check_call(get_var_i64_import, i64, 34); + check_call(get_var_f32_export, f32, 37); + check_call(get_var_i64_export, i64, 38); + + // Modify variables through calls and check again. + wasm_val_t args73[] = { {.kind = WASM_F32, .of = {.f32 = 73}} }; + wasm_func_call(set_var_f32_import, args73, NULL); + wasm_val_t args74[] = { {.kind = WASM_I64, .of = {.i64 = 74}} }; + wasm_func_call(set_var_i64_import, args74, NULL); + wasm_val_t args77[] = { {.kind = WASM_F32, .of = {.f32 = 77}} }; + wasm_func_call(set_var_f32_export, args77, NULL); + wasm_val_t args78[] = { {.kind = WASM_I64, .of = {.i64 = 78}} }; + wasm_func_call(set_var_i64_export, args78, NULL); + + check_global(var_f32_import, f32, 73); + check_global(var_i64_import, i64, 74); + check_global(var_f32_export, f32, 77); + check_global(var_i64_export, i64, 78); + + check_call(get_var_f32_import, f32, 73); + check_call(get_var_i64_import, i64, 74); + check_call(get_var_f32_export, f32, 77); + check_call(get_var_i64_export, i64, 78); + + wasm_global_delete(const_f32_import); + wasm_global_delete(const_i64_import); + wasm_global_delete(var_f32_import); + wasm_global_delete(var_i64_import); + wasm_extern_vec_delete(&exports); + wasm_instance_delete(instance); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/wamr/samples/wasm-c-api/src/global.wasm b/wamr/samples/wasm-c-api/src/global.wasm new file mode 100644 index 0000000000000000000000000000000000000000..0e76863278e62f064730544392e92c0194097134 GIT binary patch literal 576 zcmZXQO-{ow5JtbT6T7Whpo#-nYEMuq)~H-06)II1rKpK2HjbhSv17rS4Od{neYgaB zY$yJaP?YG+%lMl~u&(z6fZn^VLs5Z@z1xZmDr&*Ly~ga0h(esunAu4B6tx7PJ~E`E|BuF0*OHz%H|llY}Sd z7SvjF?mdh_gai%h%jL6lIOEopM_L z-(VDFw!t{cEOU}%nyx0l{#U=aCuUFsPyiNy2PguR0Ym_)UVV +#include +#include +#include + +#include "wasm_c_api.h" + +#define own + +// A function to be called from Wasm code. +own wasm_trap_t* hello_callback( + const wasm_val_t args[], wasm_val_t results[] +) { + printf("Calling back...\n"); + printf("> Hello World!\n"); + return NULL; +} + + +int main(int argc, const char* argv[]) { + // Initialize. + printf("Initializing...\n"); + wasm_engine_t* engine = wasm_engine_new(); + wasm_store_t* store = wasm_store_new(engine); + + // Load binary. + printf("Loading binary...\n"); +#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0 + FILE* file = fopen("hello.aot", "rb"); +#else + FILE* file = fopen("hello.wasm", "rb"); +#endif + if (!file) { + printf("> Error loading module!\n"); + return 1; + } + fseek(file, 0L, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0L, SEEK_SET); + wasm_byte_vec_t binary; + wasm_byte_vec_new_uninitialized(&binary, file_size); + if (fread(binary.data, file_size, 1, file) != 1) { + printf("> Error loading module!\n"); + return 1; + } + fclose(file); + + // Compile. + printf("Compiling module...\n"); + own wasm_module_t* module = wasm_module_new(store, &binary); + if (!module) { + printf("> Error compiling module!\n"); + return 1; + } + + wasm_byte_vec_delete(&binary); + + // Create external print functions. + printf("Creating callback...\n"); + own wasm_functype_t* hello_type = wasm_functype_new_0_0(); + own wasm_func_t* hello_func = + wasm_func_new(store, hello_type, hello_callback); + + wasm_functype_delete(hello_type); + + // Instantiate. + printf("Instantiating module...\n"); + const wasm_extern_t* imports[] = { wasm_func_as_extern(hello_func) }; + own wasm_instance_t* instance = + wasm_instance_new(store, module, imports, NULL); + if (!instance) { + printf("> Error instantiating module!\n"); + return 1; + } + + wasm_func_delete(hello_func); + + // Extract export. + printf("Extracting export...\n"); + own wasm_extern_vec_t exports; + wasm_instance_exports(instance, &exports); + if (exports.size == 0) { + printf("> Error accessing exports!\n"); + return 1; + } + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + if (run_func == NULL) { + printf("> Error accessing export!\n"); + return 1; + } + + wasm_module_delete(module); + wasm_instance_delete(instance); + + // Call. + printf("Calling export...\n"); + if (wasm_func_call(run_func, NULL, NULL)) { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + + // Shut down. + printf("Shutting down...\n"); + wasm_store_delete(store); + wasm_engine_delete(engine); + + // All done. + printf("Done.\n"); + return 0; +} diff --git a/wamr/samples/wasm-c-api/src/hello.wasm b/wamr/samples/wasm-c-api/src/hello.wasm new file mode 100644 index 0000000000000000000000000000000000000000..2207c03eead3f45f13dfa58b73c6bce27716bffa GIT binary patch literal 71 zcmZQbEY4+QU|?WuX=rF*U`$|OVCn+17+5n>b8_+-7?_(NeD-!Q&0JKP$H2%1Q3Te+ IAi%&40BKwiaR2}S literal 0 HcmV?d00001 diff --git a/wamr/samples/wasm-c-api/src/hello.wat b/wamr/samples/wasm-c-api/src/hello.wat new file mode 100644 index 0000000..1c56c55 --- /dev/null +++ b/wamr/samples/wasm-c-api/src/hello.wat @@ -0,0 +1,4 @@ +(module + (func $hello (import "" "hello")) + (func (export "run") (call $hello)) +) diff --git a/wamr/test-tools/IoT-APP-Store-Demo/README.md b/wamr/test-tools/IoT-APP-Store-Demo/README.md new file mode 100644 index 0000000..06d17a6 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/README.md @@ -0,0 +1,36 @@ +# IoT Application Store +Wasm application management portal for WAMR + +# Requirement +Install django with pip3 +``` +pip3 install django +``` + +# Run +1. Start wasm server + ``` + cd wasm_django/server + python3 wasm_server.py + ``` + +2. Start IoT application management web portal + ``` + cd wasm_django + python3 manage.py runserver 0.0.0.0:80 + ``` + +3. Download WAMR runtime from [help](http://localhost/help/) page + > NOTE: You need to start web server according to *step 2* before accessing this link! + +4. Start a WAMR runtime from localhost + ``` + ./simple + ``` + or from other computers + ``` + ./simple -a [your.server.ip.address] + ``` + +# Online demo + http://39.106.110.7/ diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/db.sqlite3 new file mode 100755 index 0000000000000000000000000000000000000000..211576ca3e5992d4ec1abe50c687985dcaa1f3f0 GIT binary patch literal 45056 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 + +{% load static %} + + + + + + + Wasm-Micro-Runtime + + + + + + + + + + +
    + +
    +

    WebAssembly Micro Runtime - APP Store Demo

    +
    +
    + + + +
    +
    +
    +
    +

    + +

    +
    +
    IP :
    +
    Port :
    +
    Installed apps :
    +
    +
    +
    + + + + + +
    +

    app is downloading now

    +
    +
    +
    +
    + + +
    +
    +
    ×
    +
    HOT Applications
    + +
    +
    + +

    Product Name:

    +

    Current Version:

    + +
    +
    +
    +
    + + +
    +

    List of Installed Apps:

    + +
    + + +
    +
    + +
    Product Name:
    +
    Staus:
    +
    Current Version:
    +
    +
    +
    + +
    Copyright© intel.com
    + + + + + + + + + + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html new file mode 100644 index 0000000..46ecedf --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/appstore.html @@ -0,0 +1,98 @@ + + +{% load static %} + + + + + + + + Wasm-Micro-Runtime + + + + + + + + + + +
    +
    + +
    +
    +

    WebAssembly Micro Runtime - APP Store Demo

    +
    +
    + + + + + +
    +
    +
    +
    The products
    +
    Application List
    +
    +
    + {%csrf_token%} +
    + + Choose File +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Product Name:

    +

    Product Version:

    +

    Preloaded Apps

    + +
    +
    +
    +
    +
    + + +
    + Copyright© intel.com +
    + + + + + + + + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html new file mode 100644 index 0000000..5610a2d --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/empty.html @@ -0,0 +1,125 @@ + + + + + +wasm-micro-runtime + + + + + + + + + + +
    +

    404

    +

    Server Not Found

    +

    Github

    +
    + + + + + + + + + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html new file mode 100755 index 0000000..b36279d --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/help.html @@ -0,0 +1,102 @@ + + +{% load static %} + + + + + + + Wasm-Micro-Runtime + + + + + + + + + + + + +
    +
    +
    +
    +

    + How to use? +

    +

    + 1. Download a simple runtime (build for ubuntu 16.04 64 bits, other platforms please build + from the source code) +

    +

    + 2. In the terminal: cd ~/Download && ./simple -a 39.106.110.7 +

    +
    +
    + +
    + Notes: +
    We also have a UI-enabled runtime, please download here and enjoy. It may require + a few more setups. +

    Before running the UI-enabled runtime, please install some required softwares:

    +

    sudo apt-get install libsdl2-2.0-0:i386

    +

    For more details please refer to this guide +

    +

    cd ~/Download && ./wasm_runtime_wgl -a 39.106.110.7

    +
    +
    +

    + 3. Return to device page, find your device according to the IP address and click it, you + will enter application installation page +

    +

    + 4. In the application installation page, click the Install Application button, and chose an + app to install. (The "ui_app" is only for UI_enabled_runtimes, simple runtime can't install + this app) +

    +

    + 5. If you want to upload a new application, go to App Store page, choose a file and click + upload +

    +

    + Go Back + Download + simple_runtime + Download + UI_enabled_runtime +

    +
    +
    +
    +
    +

    Like this project?

    +

    Join us and build a powerful and interesting world for embedded + devices!

    + + +

    + View + on GitHub +

    +
    +
    +
    +
    +
    +
    + + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html new file mode 100644 index 0000000..3832791 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/templates/mysite.html @@ -0,0 +1,91 @@ + + +{% load static %} + + + + + + + Wasm-Micro-Runtime + + + + + + + + + + +
    +
    + +
    +
    +

    WebAssembly Micro Runtime - APP Store Demo

    +
    +
    + +
    +
    +
    +
    +

    + +

    +
    +

    The devices

    +
    +
    + +
    + + +
    +
    +
    +
    +

    + +

    +
    +
    IP :
    +
    Port :
    +
    Installed apps :
    +
    +
    +

    + +

    +
    +
    +
    + + + + +
    + Copyright© intel.com +
    + + + + + + + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py new file mode 100755 index 0000000..7ce503c --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py new file mode 100755 index 0000000..cc11a27 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/devices/views.py @@ -0,0 +1,273 @@ +''' + /* Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +''' + +# _*_ +from django.shortcuts import render, render_to_response +from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound +import json +import socket +import os + +# Create your views here. + + +avaliable_list = [ + {'ID': 'timer', 'Version': '1.0'}, + {'ID': 'connection', 'Version': '1.0'}, + {'ID': 'event_publisher', 'Version': '3.0'}, + {'ID': 'event_subscriber', 'Version': '1.0'}, + {'ID': 'request_handler', 'Version': '1.0'}, + {'ID': 'sensor', 'Version': '1.0'}, + {'ID': 'ui_app', 'Version': '1.0'} +] + +# Help +def help(req): +# return "Help" page + return render(req, "help.html") + +# View +def index(req): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + host = '127.0.0.1' + port = 8889 + msg = "" + err = "" + + try: + s.connect((host, port)) + s.send(bytes("query:all", encoding='utf8')) + s.settimeout(10) + msg = s.recv(1024) + except socket.timeout as e: + err = "empty" + print("no client connected") + except socket.error as e: + err = "refused" + print("server not started") + + s.close() + + device_list = [] + if msg != "": + devices = msg.decode('utf-8').split("*") + for dev in devices: + dev_info = eval(dev) + addr = dev_info['addr'] + port = dev_info['port'] + apps = dev_info['num'] + device_list.append({'IP': addr, 'Port': port, 'apps': apps}) + else: + if err == "refused": + return render(req, "empty.html") + + dlist = device_list + + return render(req, 'mysite.html', {'dlist': json.dumps(dlist)}) + + +def apps(req): + open_status = '' + search_node = [] + if req.method == "POST": + dev_search = req.POST['mykey'] + dev_addr = req.POST['voip'] + dev_port = req.POST['voport'] + open_status = 'open' + for i in avaliable_list: + if i['ID'] == dev_search: + search_node = [{'ID':dev_search, 'Version': '1.0'}] + print("search_node:",search_node) + break + else: + search_node = ["Nothing find"] + print( "final:",search_node) + else: + dev_addr = req.GET['ip'] + dev_port = req.GET['port'] + open_status = 'close' + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + host = '127.0.0.1' + port = 8889 + msg = "" + err = "" + + try: + s.connect((host, port)) + s.send(bytes("query:"+dev_addr+":"+str(dev_port), encoding='utf8')) + msg = s.recv(1024) + except socket.error as e: + print("unable to connect to server") + msg = b"fail" + s.close() + + app_list = [] + + if msg != "": + if msg.decode() == "fail": + return render(req, "empty.html") + else: + dic = eval(msg.decode(encoding='utf8')) + app_num = dic["num"] + for i in range(app_num): + app_list.append( + {'pname': dic["applet"+str(i+1)], 'status': 'Installed', 'current_version': '1.0'}) + + alist = app_list + device_info = [] + device_info.append( + {'IP': dev_addr, 'Port': str(dev_port), 'apps': app_num}) + + print(device_info) + return render(req, 'application.html', {'alist': json.dumps(alist), 'dlist': json.dumps(device_info), 'llist': json.dumps(avaliable_list), + "open_status":json.dumps(open_status),"search_node": json.dumps(search_node),}) + + +def appDownload(req): + dev_addr = req.GET['ip'] + dev_port = req.GET['port'] + app_name = req.GET['name'] + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + host = '127.0.0.1' + port = 8889 + msg = "" + + app_path = os.path.abspath(os.path.join(os.getcwd(), "static", "upload")) + if app_path[-1] != '/': + app_path += '/' + + try: + s.connect((host, port)) + s.send(bytes("install:"+dev_addr+":"+str(dev_port)+":"+app_name + + ":"+app_path + app_name + ".wasm", encoding='utf8')) + msg = s.recv(1024) + except socket.error as e: + print("unable to connect to server") + s.close() + + success = "ok" + fail = "Fail!" + status = [success, fail] + print(msg) + if msg == b"fail": + return HttpResponse(json.dumps({ + "status": fail + })) + elif msg == b"success": + return HttpResponse(json.dumps({ + "status": success + })) + else: + return HttpResponse(json.dumps({ + "status": eval(msg.decode())["error message"].split(':')[1] + })) + + +def appDelete(req): + dev_addr = req.GET['ip'] + dev_port = req.GET['port'] + app_name = req.GET['name'] + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + host = '127.0.0.1' + port = 8889 + s.connect((host, port)) + s.send(bytes("uninstall:"+dev_addr+":" + + str(dev_port)+":"+app_name, encoding='utf8')) + msg = s.recv(1024) + s.close() + r = HttpResponse("ok") + return r + +static_list = [{'ID': 'timer', 'Version': '1.0'}, {'ID': 'connection', 'Version': '1.0'}, {'ID': 'event_publisher', 'Version': '3.0'}, { + 'ID': 'event_subscriber', 'Version': '1.0'}, {'ID': 'reuqest_handler', 'Version': '1.0'}, {'ID': 'sensor', 'Version': '1.0'}, {'ID': 'ui_app', 'Version': '1.0'}] + +def store(req): + + store_path = os.path.join('static', 'upload') + status = [] + + print(user_file_list) + return render(req, 'appstore.html', {'staticlist': json.dumps(static_list), 'flist': json.dumps(user_file_list),'ulist':json.dumps(status)}) + +user_file_list = [] +files_list = [] +def uploadapps(req): + status = [] + local_list = ['timer','connection','event_publisher','event_subscriber','reuqest_handler','sensor'] + req.encoding = 'utf-8' + if req.method == 'POST': + myfile = req.FILES.get("myfile", None) + obj = req.FILES.get('myfile') + store_path = os.path.join('static', 'upload') + file_path = os.path.join('static', 'upload', obj.name) + + if not os.path.exists(store_path): + os.makedirs(store_path) + + file_name = obj.name.split(".")[0] + file_prefix = obj.name.split(".")[-1] + + + if file_prefix != "wasm": + status = ["Not a wasm file"] + elif file_name in local_list: + status = ["This App is preloaded"] + elif file_name in files_list: + status = ["This App is already uploaded"] + else: + status = [] + avaliable_list.append({'ID': file_name, 'Version': '1.0'}) + user_file_list.append({'ID': file_name, 'Version': '1.0'}) + files_list.append(file_name) + + print(user_file_list) + f = open(file_path, 'wb') + for chunk in obj.chunks(): + f.write(chunk) + f.close() + return render(req, 'appstore.html', {'staticlist': json.dumps(static_list), 'flist': json.dumps(user_file_list),'ulist':json.dumps(status)}) + +appname_list = [] + +def addapps(request): + types = '' + print("enter addapps") + request.encoding = 'utf-8' + app_dic = {'ID': '', 'Version': ''} + + # if request.method == 'get': + if "NAME" in request.GET: + a_name = request.GET['NAME'] + if a_name != "" and a_name not in appname_list: + appname_list.append(a_name) + message = request.GET['NAME'] + request.GET['Version'] + app_dic['ID'] = request.GET['NAME'] + app_dic['Version'] = request.GET['Version'] + avaliable_list.append(app_dic) + else: + types = "Exist" + print(avaliable_list) + return render(request, 'appstore.html', {'alist': json.dumps(avaliable_list)}) + +def removeapps(req): + app_name = req.GET['name'] + app_version = req.GET['version'] + remove_app = {'ID': app_name, 'Version': app_version} + avaliable_list.remove(remove_app) + user_file_list.remove(remove_app) + files_list.remove(app_name) + return render(req, 'appstore.html', {'alist': json.dumps(avaliable_list),'flist': json.dumps(user_file_list)}) + +# Test +if __name__ == "__main__": + print(device_list[0]['IP']) + print(device['IP']) diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py new file mode 100755 index 0000000..341863c --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/manage.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/__init__.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py new file mode 100755 index 0000000..7eb3685 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/settings.py @@ -0,0 +1,136 @@ +""" +Django settings for mysite project. + +Generated by 'django-admin startproject' using Django 2.2.2. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.2/ref/settings/ +""" + +import os +from django.conf.global_settings import STATIC_ROOT + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '8m05#6yx5wcygj*a+v6+=-y(#o+(z58-3!epq$u@5)64!mmu8q' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + + + 'devices', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'mysite.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'mysite.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +APPEND_SLASH = False + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ + +STATIC_URL = '/static/' +HERE = os.path.dirname(os.path.abspath(__file__)) +HERE = os.path.join(HERE,'../') +STATICFILES_DIRS = (os.path.join(HERE,'static/'),) +#STATICFILES_DIRS = (os.path.join(BASE_DIR,'static'),) +#STATIC_ROOT = (os.path.join(os.path.dirname(_file_),'static') +#templates +TEMPLATE_DIRS=[ + '/home/xujun/mysite/templates', +] + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py new file mode 100755 index 0000000..8a74b55 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/urls.py @@ -0,0 +1,41 @@ +#config:utf-8 + +"""mysite URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +#from django.conf.urls import include,url +from django.urls import path,include +from devices import views as devices_views +#from login import views as login_views + + +urlpatterns = [ + + path('admin/', admin.site.urls), + path('',devices_views.index), + path('apps/',devices_views.apps), + path('appDownload/', devices_views.appDownload), + path('appDelete/', devices_views.appDelete), + path('appstore/',devices_views.store), +## path('apps/appstore/',devices_views.storeofdevic), +## path('search/',devices_views.search), + path('upload',devices_views.uploadapps), + path('removeapps/',devices_views.removeapps), + path('help/',devices_views.help), + +] + + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py new file mode 100755 index 0000000..45e28c9 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/mysite/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for mysite project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') + +application = get_wsgi_application() diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py new file mode 100755 index 0000000..5edeb90 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/server/wasm_server.py @@ -0,0 +1,605 @@ +''' + /* Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ +''' +import select +import socket +import queue +from time import sleep +import struct +import threading +import time +from ctypes import * +import json +import logging +import os + +attr_type_list = [ + "ATTR_NONE", + "ATTR_TYPE_SHORT", + "ATTR_TYPE_INT", + "ATTR_TYPE_INT64", + "ATTR_TYPE_BYTE", + "ATTR_TYPE_UINT16", + "ATTR_TYPE_FLOAT", + "ATTR_TYPE_DOUBLE", + "ATTR_TYPE_BOOLEAN", + "ATTR_TYPE_STRING", + "ATTR_TYPE_BYTEARRAY" +] + + +Phase_Non_Start = 0 +Phase_Leading = 1 +Phase_Type = 2 +Phase_Size = 3 +Phase_Payload = 4 + + + +class imrt_link_message(object): + def __init__(self): + self.leading = bytes([0x12, 0x34]) + self.phase = Phase_Non_Start + self.size_in_phase = 0 + self.message_type = bytes() + self.message_size = bytes() + self.payload = bytes() + self.msg = bytes() + + def set_recv_phase(self, phase): + self.phase = phase + + def on_imrt_link_byte_arrive(self, ch): + self.msg += ch + if self.phase == Phase_Non_Start: + if ch == b'\x12': + self.set_recv_phase(Phase_Leading) + else: + return -1 + elif self.phase == Phase_Leading: + if ch == b'\x34': + self.set_recv_phase(Phase_Type) + else: + self.set_recv_phase(Phase_Non_Start) + return -1 + elif self.phase == Phase_Type: + self.message_type += ch + self.size_in_phase += 1 + + if self.size_in_phase == 2: + (self.message_type, ) = struct.unpack('!H', self.message_type) + self.size_in_phase = 0 + self.set_recv_phase(Phase_Size) + elif self.phase == Phase_Size: + self.message_size += ch + self.size_in_phase += 1 + + if self.size_in_phase == 4: + (self.message_size, ) = struct.unpack('!I', self.message_size) + self.size_in_phase = 0 + self.set_recv_phase(Phase_Payload) + + if self.message_size == b'\x00': + self.set_recv_phase(Phase_Non_Start) + return 0 + + self.set_recv_phase(Phase_Payload) + + elif self.phase == Phase_Payload: + self.payload += ch + self.size_in_phase += 1 + + if self.size_in_phase == self.message_size: + self.set_recv_phase(Phase_Non_Start) + return 0 + + return 2 + + return 1 + + + +def read_file_to_buffer(file_name): + file_object = open(file_name, 'rb') + buffer = None + + if not os.path.exists(file_name): + logging.error("file {} not found.".format(file_name)) + return "file not found" + + try: + buffer = file_object.read() + finally: + file_object.close() + + return buffer + +def decode_attr_container(msg): + + attr_dict = {} + + buf = msg[26 : ] + (total_len, tag_len) = struct.unpack('@IH', buf[0 : 6]) + tag_name = buf[6 : 6 + tag_len].decode() + buf = buf[6 + tag_len : ] + (attr_num, ) = struct.unpack('@H', buf[0 : 2]) + buf = buf[2 : ] + + logging.info("parsed attr:") + logging.info("total_len:{}, tag_len:{}, tag_name:{}, attr_num:{}" + .format(str(total_len), str(tag_len), str(tag_name), str(attr_num))) + + for i in range(attr_num): + (key_len, ) = struct.unpack('@H', buf[0 : 2]) + key_name = buf[2 : 2 + key_len - 1].decode() + buf = buf[2 + key_len : ] + (type_index, ) = struct.unpack('@c', buf[0 : 1]) + + attr_type = attr_type_list[int(type_index[0])] + buf = buf[1 : ] + + if attr_type == "ATTR_TYPE_SHORT": + (attr_value, ) = struct.unpack('@h', buf[0 : 2]) + buf = buf[2 : ] + # continue + elif attr_type == "ATTR_TYPE_INT": + (attr_value, ) = struct.unpack('@I', buf[0 : 4]) + buf = buf[4 : ] + # continue + elif attr_type == "ATTR_TYPE_INT64": + (attr_value, ) = struct.unpack('@q', buf[0 : 8]) + buf = buf[8 : ] + # continue + elif attr_type == "ATTR_TYPE_BYTE": + (attr_value, ) = struct.unpack('@c', buf[0 : 1]) + buf = buf[1 : ] + # continue + elif attr_type == "ATTR_TYPE_UINT16": + (attr_value, ) = struct.unpack('@H', buf[0 : 2]) + buf = buf[2 : ] + # continue + elif attr_type == "ATTR_TYPE_FLOAT": + (attr_value, ) = struct.unpack('@f', buf[0 : 4]) + buf = buf[4 : ] + # continue + elif attr_type == "ATTR_TYPE_DOUBLE": + (attr_value, ) = struct.unpack('@d', buf[0 : 8]) + buf = buf[8 : ] + # continue + elif attr_type == "ATTR_TYPE_BOOLEAN": + (attr_value, ) = struct.unpack('@?', buf[0 : 1]) + buf = buf[1 : ] + # continue + elif attr_type == "ATTR_TYPE_STRING": + (str_len, ) = struct.unpack('@H', buf[0 : 2]) + attr_value = buf[2 : 2 + str_len - 1].decode() + buf = buf[2 + str_len : ] + # continue + elif attr_type == "ATTR_TYPE_BYTEARRAY": + (byte_len, ) = struct.unpack('@I', buf[0 : 4]) + attr_value = buf[4 : 4 + byte_len] + buf = buf[4 + byte_len : ] + # continue + + attr_dict[key_name] = attr_value + + logging.info(str(attr_dict)) + return attr_dict + +class Request(): + mid = 0 + url = "" + action = 0 + fmt = 0 + payload = "" + payload_len = 0 + sender = 0 + + def __init__(self, url, action, fmt, payload, payload_len): + self.url = url + self.action = action + self.fmt = fmt + # if type(payload) == bytes: + # self.payload = bytes(payload, encoding = "utf8") + # else: + self.payload_len = payload_len + if self.payload_len > 0: + self.payload = payload + + + def pack_request(self): + url_len = len(self.url) + 1 + buffer_len = url_len + self.payload_len + + req_buffer = struct.pack('!2BH2IHI',1, self.action, self.fmt, self.mid, self.sender, url_len, self.payload_len) + for i in range(url_len - 1): + req_buffer += struct.pack('!c', bytes(self.url[i], encoding = "utf8")) + req_buffer += bytes([0]) + for i in range(self.payload_len): + req_buffer += struct.pack('!B', self.payload[i]) + + return req_buffer, len(req_buffer) + + + def send(self, conn, is_install): + leading = struct.pack('!2B', 0x12, 0x34) + + if not is_install: + msg_type = struct.pack('!H', 0x0002) + else: + msg_type = struct.pack('!H', 0x0004) + buff, buff_len = self.pack_request() + lenth = struct.pack('!I', buff_len) + + try: + conn.send(leading) + conn.send(msg_type) + conn.send(lenth) + conn.send(buff) + except socket.error as e: + logging.error("device closed") + for dev in tcpserver.devices: + if dev.conn == conn: + tcpserver.devices.remove(dev) + return -1 + + +def query(conn): + req = Request("/applet", 1, 0, "", 0) + if req.send(conn, False) == -1: + return "fail" + time.sleep(0.05) + try: + receive_context = imrt_link_message() + start = time.time() + while True: + if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: + break + elif time.time() - start >= 5.0: + return "fail" + query_resp = receive_context.msg + print(query_resp) + except OSError as e: + logging.error("OSError exception occur") + return "fail" + + res = decode_attr_container(query_resp) + + logging.info('Query device infomation success') + return res + +def install(conn, app_name, wasm_file): + wasm = read_file_to_buffer(wasm_file) + if wasm == "file not found": + return "failed to install: file not found" + + print("wasm file len:") + print(len(wasm)) + req = Request("/applet?name=" + app_name, 3, 98, wasm, len(wasm)) + if req.send(conn, True) == -1: + return "fail" + time.sleep(0.05) + try: + receive_context = imrt_link_message() + start = time.time() + while True: + if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: + break + elif time.time() - start >= 5.0: + return "fail" + msg = receive_context.msg + except OSError as e: + logging.error("OSError exception occur") + # TODO: check return message + + if len(msg) == 24 and msg[8 + 1] == 65: + logging.info('Install application success') + return "success" + else: + res = decode_attr_container(msg) + logging.warning('Install application failed: %s' % (str(res))) + print(str(res)) + + return str(res) + + +def uninstall(conn, app_name): + req = Request("/applet?name=" + app_name, 4, 99, "", 0) + if req.send(conn, False) == -1: + return "fail" + time.sleep(0.05) + try: + receive_context = imrt_link_message() + start = time.time() + while True: + if receive_context.on_imrt_link_byte_arrive(conn.recv(1)) == 0: + break + elif time.time() - start >= 5.0: + return "fail" + msg = receive_context.msg + except OSError as e: + logging.error("OSError exception occur") + # TODO: check return message + + if len(msg) == 24 and msg[8 + 1] == 66: + logging.info('Uninstall application success') + return "success" + else: + res = decode_attr_container(msg) + logging.warning('Uninstall application failed: %s' % (str(res))) + print(str(res)) + + return str(res) + +class Device: + def __init__(self, conn, addr, port): + self.conn = conn + self.addr = addr + self.port = port + self.app_num = 0 + self.apps = [] + +cmd = [] + +class TCPServer: + def __init__(self, server, server_address, inputs, outputs, message_queues): + # Create a TCP/IP + self.server = server + self.server.setblocking(False) + + # Bind the socket to the port + self.server_address = server_address + print('starting up on %s port %s' % self.server_address) + self.server.bind(self.server_address) + + # Listen for incoming connections + self.server.listen(10) + + self.cmd_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.cmd_sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + + self.cmd_sock.bind(('127.0.0.1', 8889)) + self.cmd_sock.listen(5) + + + # Sockets from which we expect to read + self.inputs = inputs + self.inputs.append(self.cmd_sock) + + # Sockets to which we expect to write + # 处理要发送的消息 + self.outputs = outputs + # Outgoing message queues (socket: Queue) + self.message_queues = message_queues + + self.devices = [] + self.conn_dict = {} + + def handler_recever(self, readable): + # Handle inputs + for s in readable: + if s is self.server: + # A "readable" socket is ready to accept a connection + connection, client_address = s.accept() + self.client_address = client_address + print('connection from', client_address) + # this is connection not server + # connection.setblocking(0) + self.inputs.append(connection) + + # Give the connection a queue for data we want to send + # self.message_queues[connection] = queue.Queue() + + res = query(connection) + + if res != "fail": + dev = Device(connection, client_address[0], client_address[1]) + self.devices.append(dev) + self.conn_dict[client_address] = connection + + dev_info = {} + dev_info['addr'] = dev.addr + dev_info['port'] = dev.port + dev_info['apps'] = 0 + + logging.info('A new client connected from ("%s":"%s")' % (dev.conn, dev.port)) + + elif s is self.cmd_sock: + connection, client_address = s.accept() + print("web server socket connected") + logging.info("Django server connected") + self.inputs.append(connection) + self.message_queues[connection] = queue.Queue() + + else: + data = s.recv(1024) + if data != b'': + # A readable client socket has data + logging.info('received "%s" from %s' % (data, s.getpeername())) + + # self.message_queues[s].put(data) + # # Add output channel for response + + # if s not in self.outputs: + # self.outputs.append(s) + + if(data.decode().split(':')[0] == "query"): + if data.decode().split(':')[1] == "all": + resp = [] + print('start query all devices') + for dev in self.devices: + dev_info = query(dev.conn) + if dev_info == "fail": + continue + dev_info["addr"] = dev.addr + dev_info["port"] = dev.port + resp.append(str(dev_info)) + + print(resp) + + if self.message_queues[s] is not None: + # '*' is used in web server to sperate the string + self.message_queues[s].put(bytes("*".join(resp), encoding = 'utf8')) + if s not in self.outputs: + self.outputs.append(s) + else: + client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) + + if client_addr in self.conn_dict.keys(): + print('start query device from (%s:%s)' % (client_addr[0], client_addr[1])) + resp = query(self.conn_dict[client_addr]) + print(resp) + + if self.message_queues[s] is not None: + self.message_queues[s].put(bytes(str(resp), encoding = 'utf8')) + if s not in self.outputs: + self.outputs.append(s) + else: # no connection + if self.message_queues[s] is not None: + self.message_queues[s].put(bytes(str("fail"), encoding = 'utf8')) + if s not in self.outputs: + self.outputs.append(s) + elif(data.decode().split(':')[0] == "install"): + client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) + app_name = data.decode().split(':')[3] + app_file = data.decode().split(':')[4] + + if client_addr in self.conn_dict.keys(): + print('start install application %s to ("%s":"%s")' % (app_name, client_addr[0], client_addr[1])) + res = install(self.conn_dict[client_addr], app_name, app_file) + if self.message_queues[s] is not None: + logging.info("response {} to cmd server".format(res)) + self.message_queues[s].put(bytes(res, encoding = 'utf8')) + if s not in self.outputs: + self.outputs.append(s) + elif(data.decode().split(':')[0] == "uninstall"): + client_addr = (data.decode().split(':')[1],int(data.decode().split(':')[2])) + app_name = data.decode().split(':')[3] + + if client_addr in self.conn_dict.keys(): + print("start uninstall") + res = uninstall(self.conn_dict[client_addr], app_name) + if self.message_queues[s] is not None: + logging.info("response {} to cmd server".format(res)) + self.message_queues[s].put(bytes(res, encoding = 'utf8')) + if s not in self.outputs: + self.outputs.append(s) + + + # if self.message_queues[s] is not None: + # self.message_queues[s].put(data) + # if s not in self.outputs: + # self.outputs.append(s) + else: + logging.warning(data) + + # Interpret empty result as closed connection + try: + for dev in self.devices: + if s == dev.conn: + self.devices.remove(dev) + # Stop listening for input on the connection + if s in self.outputs: + self.outputs.remove(s) + self.inputs.remove(s) + + # Remove message queue + if s in self.message_queues.keys(): + del self.message_queues[s] + s.close() + except OSError as e: + logging.error("OSError raised, unknown connection") + return "got it" + + def handler_send(self, writable): + # Handle outputs + for s in writable: + try: + message_queue = self.message_queues.get(s) + send_data = '' + if message_queue is not None: + send_data = message_queue.get_nowait() + except queue.Empty: + self.outputs.remove(s) + else: + # print "sending %s to %s " % (send_data, s.getpeername) + # print "send something" + if message_queue is not None: + s.send(send_data) + else: + print("client has closed") + # del message_queues[s] + # writable.remove(s) + # print "Client %s disconnected" % (client_address) + return "got it" + + def handler_exception(self, exceptional): + # # Handle "exceptional conditions" + for s in exceptional: + print('exception condition on', s.getpeername()) + # Stop listening for input on the connection + self.inputs.remove(s) + if s in self.outputs: + self.outputs.remove(s) + s.close() + + # Remove message queue + del self.message_queues[s] + return "got it" + + +def event_loop(tcpserver, inputs, outputs): + while inputs: + # Wait for at least one of the sockets to be ready for processing + print('waiting for the next event') + readable, writable, exceptional = select.select(inputs, outputs, inputs) + if readable is not None: + tcp_recever = tcpserver.handler_recever(readable) + if tcp_recever == 'got it': + print("server have received") + if writable is not None: + tcp_send = tcpserver.handler_send(writable) + if tcp_send == 'got it': + print("server have send") + if exceptional is not None: + tcp_exception = tcpserver.handler_exception(exceptional) + if tcp_exception == 'got it': + print("server have exception") + + + sleep(0.1) + +def run_wasm_server(): + server_address = ('localhost', 8888) + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + inputs = [server] + outputs = [] + message_queues = {} + tcpserver = TCPServer(server, server_address, inputs, outputs, message_queues) + + task = threading.Thread(target=event_loop,args=(tcpserver,inputs,outputs)) + task.start() + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG, + filename='wasm_server.log', + filemode='a', + format= + '%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s' + ) + server_address = ('0.0.0.0', 8888) + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) + inputs = [server] + outputs = [] + message_queues = {} + tcpserver = TCPServer(server, server_address, inputs, outputs, message_queues) + logging.info("TCP Server start at {}:{}".format(server_address[0], "8888")) + + task = threading.Thread(target=event_loop,args=(tcpserver,inputs,outputs)) + task.start() + + # event_loop(tcpserver, inputs, outputs) \ No newline at end of file diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css new file mode 100644 index 0000000..220d4b6 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/css/application.css @@ -0,0 +1,400 @@ +/* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +{% load static %} + diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js new file mode 100644 index 0000000..0510fb9 --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/application.js @@ -0,0 +1,217 @@ +/* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +/* + * Dom Location + * + */ + + function setDivCenter(divname) +// make qn element center aligned + { + var Top =($(window).height()-$(divname).height())/2; + var Left = ($(window).width()-$(divname).width())/2; + var scrollTop = $(document).scrollTop(); + var scrollLeft = $(document).scrollLeft(); + $(divname).css({posisiton:'absolute','top':Top+scrollTop,'left':Left+scrollLeft}); + +}; + +setDivCenter(".middlebox"); +setDivCenter(".deletebox"); + +function setmain(divname){ +// Set the pop-up window of apps for download at the right place + var x = $('#btn').offset().top; + var Top = x + $('#btn').height()+15; + var y = $('#btn').offset().left; + var Left = y + ($('#btn').width()/2)-($(divname).width()/2); + console.log(Top,Left) + $(divname).css({'top':Top,'left':Left}); +} +setmain(".main") + +/* + * download apps + * + */ + +function getthis(val) +//Telling background which app to be loaded from appstore_list and to be installed in the current device. +{ + + /* Get the ip adress and the port of a device, as well as the application ID to be downloaded on this device*/ + var ip,port,name,version; + var ipArr=$("#IPs").text().split(":"); + ip=ipArr[1]; + var portArr=$("#ports").text().split(":"); + port=portArr[1]; + name = $(val).parent().find("#appsinfo1").text().split(":")[1]; + version = $(val).parent().find("#appsinfo2").text().split(":")[1]; + $(".main").fadeOut(); + + for (num in alist){ + if (alist[num]['pname'].trim() == name.trim()) + {alert("This app has been downloaded."); + return;}}; + $("#loading").fadeIn(); + var sNode = document.getElementById("APPS"); + var tempNode= sNode.cloneNode(true); + sNode.parentNode.appendChild(tempNode); + $("#appinfo1").html("Product Name : "+ name); + $("#appinfo2").html("Status : "+"Installing"); + $("#appinfo3").html("Current_Version : "+ version); + + $.get("/appDownload/",{'ip':ip.trim(),'port':port.trim(),'name':name.trim(),},function (ret) { + var status = $.trim(ret.split(":")[1].split("}")[0]); + $(".loadapp").html(name+" is downloading now"); + var msg = JSON.parse(status) + console.log(msg) + if (JSON.parse(status)=="ok"){ + $(".middlebox").fadeIn(); + $(".sourceapp").fadeOut(); + $("#loading").fadeOut(); + $(".findapp").html("Download "+name +" successfully"); + $(".surebtn").click(function (){ + $(".middlebox").fadeOut(); + window.location.reload(); + })} + else if (JSON.parse(status)=="Fail!"){ + alert("Download failed!"); + $("#loading").fadeOut(); + sNode.remove(); + } + else { + alert("Install app failed:" + msg) + $("#loading").fadeOut(); + sNode.remove(); + } + }) +}; + +window.onload = function clone() +//Add & Delete apps to the device. +{ + /*Install Apps*/ + var sourceNode = document.getElementById("APPS"); + if (alist.length != 0) + { + $("#appinfo1").html("Product Name : "+ alist[0]['pname']); + $("#appinfo2").html("Status : "+ alist[0]['status']); + $("#appinfo3").html("Current_Version : "+ alist[0]['current_version']); + $("#delete").attr('class','delet0'); + $("#APPS").attr('class','app0'); + + for (var i=1; i=3){ + alert("Install app failed: exceed max app installations.") + } + $(".main").fadeOut(); + getthis(".mybtn2"); + var newurl = "?"+"ip="+ip+"&port="+port; + window.location.href= newurl; + }); + + } +} +givevalue(); + +function popbox(){ +/*Open and close the "install apps" window*/ + $(".btn").click(function(){ + $(".main").fadeIn(); + }); + $(".close").click(function(){ + $(".main").fadeOut(); + }); +}; +popbox(); diff --git a/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js new file mode 100644 index 0000000..71d029e --- /dev/null +++ b/wamr/test-tools/IoT-APP-Store-Demo/wasm_django/static/js/appstore.js @@ -0,0 +1,125 @@ +/* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +function setDivCenter(divname) +//Center a dom +{ + var Top =($(window).height()-$(divname).height())/2; + var Left = ($(window).width()-$(divname).width())/2; + var scrollTop = $(document).scrollTop(); + var scrollLeft = $(document).scrollLeft(); + $(divname).css({posisiton:'absolute','top':Top+scrollTop,'left':Left+scrollLeft}); + +}; +setDivCenter(".deletebox"); + +function setDivheight(divname) +//set the height of "appbook" to contain all its child elements. +{ + var leng = elist.length + flist.length; + var heig = 51 * leng; + $(divname).css({height:'heig'}); +}; +setDivheight(".appbook"); + +function setfooterposition(divname) +//Locate footer on the right place +{ + var Top = flist.length* $("#devices").height()+300; + var scrollTop = $(document).scrollTop(); + if (flist.length >=4){ + $(divname).css({posisiton:'absolute','top':Top+scrollTop}); + } +} +setfooterposition(".footer"); + +function deleteClick (obj) +//Remove an app from apppstore if clicks the "OK" button +{ + var indexapp = $(obj).attr('class').match(/\d+\b/); + var removeitem = $(".applic"+indexapp); + var name=removeitem.find('#appinfo1').text().split(":")[1].trim(); + var version=removeitem.find('#appinfo2').text().split(":")[1].trim(); + + if (flist.length >= 1){ + $(".deletebox").fadeIn(); + $(".findapp").html("Are you sure to delete "+name); + $(".suresbtn").click(function (){ + removeitem.remove(); + $.get("/removeapps/",{'name':name,'version':version},function (ret) { + console.log(ret);}); + $(".deletebox").fadeOut(); + window.location.href="/appstore/"; + }) + $(".delsbtn").click(function (){ + $(".deletebox").fadeOut(); })} +}; + +function upload_file() +//Make sure the uploading file is eligible +{ + var type = ulist[0]; + console.log(type); + if (type == "Not a wasm file"){ + alert(type); + window.location.href="/appstore/"; + } + if (type == "This App is preloaded"){ + alert(type); + window.location.href="/appstore/"; + } + if (type == "This App is already uploaded"){ + alert(type); + window.location.href="/appstore/"; + } +}; +upload_file(); + + +function clone() +//Render a interface that shows all the apps for installing in appstore, +//including preloaded ones and locally uploaded ones. +{ + + var sourceNode = document.getElementById("applications"); + $("#appinfo1").html("product name : "+ elist[0]['ID']); + $("#appinfo2").html("product Version : "+ elist[0]['Version']); + $("#delbutton").attr('class','del0'); + $("#applications").attr('class','applic0'); + + + for (var i=1; i=4){ + $(divname).css({posisiton:'absolute','top':Top+scrollTop}); + } +} +setfooterposition(".footer"); + +window.onload = function clone() +//Show the list of connected devices +{ + var sourceNode = document.getElementById("devices"); + $("#IPs").html("IP : "+ dlist[0]['IP']); + $("#ports").html("Port : "+ dlist[0]['Port']); + $("#installs").html("Installed Apps : "+ dlist[0]['apps']); + $("#devices").attr('class','devic0'); + $("#dbutton").attr('class','bt0'); + $("#choose").attr('class','chos0'); + + for (var i=1; i -n input_file\n"); + printf ("Options:\n"); + printf (" -o Place the output into \n"); + printf (" -n The name of array \n"); + + return -1; +} + +static bool +bin_file_dump (const unsigned char *file, int size, + const char *bin_file_output, + const char *array_name) +{ + unsigned i = 0; + const unsigned char *p = file, *p_end = file + size; + FILE *file_output = fopen(bin_file_output, "wb+"); + + if (!file_output) + return false; + + fprintf(file_output, "\nunsigned char __aligned(4) %s[] = {\n ", array_name); + + while (p < p_end) { + fprintf(file_output, "0x%02X", *p++); + + if (p == p_end) + break; + + fprintf(file_output, ","); + + if ((++i % 12) != 0) + fprintf(file_output, " "); + else + fprintf(file_output, "\n "); + } + + fprintf(file_output, "\n};\n"); + + fclose(file_output); + return true; +} + +int +main (int argc, char *argv[]) +{ + unsigned char *file; + int size; + bool ret; + const char *bin_file_input, *array_file_output = NULL, *array_name = NULL; + + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp (argv[0], "-o")) { + ++argv; + if (--argc == 0) + return print_help (); + array_file_output = *argv; + } + else if (!strcmp (argv[0], "-n")) { + ++argv; + if (--argc == 0) + return print_help (); + array_name = *argv; + } + else + return print_help (); + } + + if (!array_file_output || !array_name) + return print_help (); + + bin_file_input = *argv; + + if (!(file = read_file_to_buffer (bin_file_input, &size))) + return -1; + + ret = bin_file_dump (file, size, array_file_output, array_name); + + free (file); + + return ret ? 0 : -1; +} diff --git a/wamr/test-tools/component_test/README.md b/wamr/test-tools/component_test/README.md new file mode 100644 index 0000000..c72d0fc --- /dev/null +++ b/wamr/test-tools/component_test/README.md @@ -0,0 +1,56 @@ +# Component Test + +The purpose of this test suite is to verify the basic components of WAMR work well in combination. It is highly recommended to run pass all suites before each commitment. + +Prerequisites +============== +- clang is available to build wasm application. +- python is installed to run test script. + + +Run the test +============= +``` +start.py [-h] [-s SUITE_ID [SUITE_ID ...]] [-t CASE_ID [CASE_ID ...]] + [-n REPEAT_TIME] [--shuffle_all] + [--cases_list CASES_LIST_FILE_PATH] [--skip_proc] + [-b BINARIES] [-d] [--rebuild] +``` +It builds out the simple project binary including WAMR runtime binary ```simple``` and the testing tool ```host_tool``` before running the test suites. + +Test output is like: +``` +Test Execution Summary: + Success: 8 + Cases fails: 0 + Setup fails: 0 + Case load fails: 0 + + +------------------------------------------------------------ +The run folder is [run-03-23-16-29] +that's all. bye +kill to quit.. +Killed +``` + +The detailed report and log is generated in ```run``` folder. The binaries copy is also put in that folder. + +Usage samples +============== + +Run default test suite: +
    +```python start.py``` + +Rebuild all test apps and then run default test suite: +
    +```python start.py --rebuild``` + +Run a specified test suite: +
    +```python start.py -s 01-life-cycle``` + +Run a specified test case: +
    +```python start.py -t 01-install``` \ No newline at end of file diff --git a/wamr/test-tools/component_test/__init__.py b/wamr/test-tools/component_test/__init__.py new file mode 100644 index 0000000..fd734d5 --- /dev/null +++ b/wamr/test-tools/component_test/__init__.py @@ -0,0 +1,11 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +__all__ = [ + "net_manager", "wifi_daemon_utils" +] + +__author__ = "" +__package__ = "model" +__version__ = "1.0" diff --git a/wamr/test-tools/component_test/framework/__init__.py b/wamr/test-tools/component_test/framework/__init__.py new file mode 100644 index 0000000..fd734d5 --- /dev/null +++ b/wamr/test-tools/component_test/framework/__init__.py @@ -0,0 +1,11 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +__all__ = [ + "net_manager", "wifi_daemon_utils" +] + +__author__ = "" +__package__ = "model" +__version__ = "1.0" diff --git a/wamr/test-tools/component_test/framework/case_base.py b/wamr/test-tools/component_test/framework/case_base.py new file mode 100644 index 0000000..311de5e --- /dev/null +++ b/wamr/test-tools/component_test/framework/case_base.py @@ -0,0 +1,29 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import json +from test.test_support import _run_suite + +class CTestCaseBase(object): + def __init__(self, suite): + self.m_suite = suite + return + def on_get_case_description(self): + return "Undefined" + + def on_setup_case(self): + return True, '' + + def on_cleanup_case(self): + return True, '' + + # called by the framework + def on_run_case(self): + return True, '' + + def get_suite(self): + return self.m_suite + diff --git a/wamr/test-tools/component_test/framework/engine.py b/wamr/test-tools/component_test/framework/engine.py new file mode 100644 index 0000000..48911d1 --- /dev/null +++ b/wamr/test-tools/component_test/framework/engine.py @@ -0,0 +1,38 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import datetime +import os +import pprint +import random +import re +import shlex +import subprocess +import signal +import sys +import time + +from .test_utils import * +from .test_api import * + + + + +def read_cases_from_file(file_path): + if not os.path.exists(file_path): + return False, None + + with open(file_path, 'r') as f: + content = f.readlines() + + content = [x.strip() for x in content] + print content + if len(content) == 0: + return False, None + + return True, content + + + diff --git a/wamr/test-tools/component_test/framework/framework.py b/wamr/test-tools/component_test/framework/framework.py new file mode 100644 index 0000000..78e187f --- /dev/null +++ b/wamr/test-tools/component_test/framework/framework.py @@ -0,0 +1,287 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import datetime +import os +import pprint +import random +import re +import shlex +import subprocess +import signal +import sys +import time +import shutil + +from .test_api import * +import this + + +''' +The run evironment dir structure: + + run/ + run{date-time}/ + suites/ + {suite name}/ + -- target/ (the target software being tested) + -- tools/ (the tools for testing the target software) +''' + + +framework=None + +def get_framework(): + global framework + return framework + +def my_import(name): + mod = __import__(name) + components = name.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + return mod + + +# we maintain a root path apart from framework location +# so the suites can be located in anywhere +class CTestFramework(object): + + def __init__(self, path): + self.running_case = '' + self.running_suite = '' + self.target_suites = {} + self.target_cases = {} + self.root_path = path + self.running_folder='' + self.report = None + self.sucess_cases = 0 + self.failed_cases = 0 + self.setup_fails = 0 + self.load_fails = 0; + global framework + framework = self + + api_set_root_path(path) + + print "root_path is " + self.root_path + + def gen_execution_stats(self): + return '\nTest Execution Summary: ' \ + '\n\tSuccess: {}' \ + '\n\tCases fails: {}' \ + '\n\tSetup fails: {}' \ + '\n\tCase load fails: {}'.format( + self.sucess_cases, self.failed_cases, self.setup_fails, self.load_fails) + + def report_result(self, success, message, case_description): + if self.report is None: + return + + case_pass = "pass" + if not success: + case_pass = "fail" + + self.report.write(case_pass + ": [" + self.running_case + "]\n\treason: " + \ + message + "\n\tcase: " + case_description + "\n") + return + + def get_running_path(self): + return self.root_path + "/run/" + self.running_folder + + def load_suites(self): + self.target_suites = os.listdir(self.root_path + "/suites") + return + + def run_case(self, suite_instance, case): + # load the test case module + case_description = '' + suite = suite_instance.m_name + api_log("\n>>start run [" + case + "] >>") + module_name = 'suites.' + suite + ".cases." + case + ".case" + try: + module = my_import(module_name) + except Exception, e: + report_fail("load case fail: " + str(e)) + api_log_error("load case fail: " + str(e)) + self.load_fails = self.load_fails +1 + print(traceback.format_exc()) + return False + + try: + case = module.CTestCase(suite_instance) + except Exception, e: + report_fail("initialize case fail: " + str(e)) + api_log_error("initialize case fail: " + str(e)) + self.load_fails = self.load_fails +1 + return False + + # call the case on setup callback + try: + case_description = case.on_get_case_description() + result, message = case.on_setup_case() + except Exception, e: + result = False + message = str(e); + if not result: + api_log_error(message) + report_fail (message, case_description) + self.failed_cases = self.failed_cases+1 + return False + + # call the case execution callaback + try: + result, message = case.on_run_case() + except Exception, e: + result = False + message = str(e); + if not result: + report_fail (message, case_description) + api_log_error(message) + self.failed_cases = self.failed_cases+1 + else: + report_success(case_description) + self.sucess_cases = self.sucess_cases +1 + + # call the case cleanup callback + try: + clean_result, message = case.on_cleanup_case() + except Exception, e: + clean_result = False + message = str(e) + + if not clean_result: + api_log(message) + + return result + + def run_suite(self, suite, cases): + # suite setup + message = '' + api_log("\n>>> Suite [" + suite + "] starting >>>") + running_folder = self.get_running_path()+ "/suites/" + suite; + + module_name = 'suites.' + suite + ".suite_setup" + try: + module = my_import(module_name) + except Exception, e: + report_fail("load suite [" + suite +"] fail: " + str(e)) + self.load_fails = self.load_fails +1 + return False + + try: + suite_instance = module.CTestSuite(suite, \ + self.root_path + '/suites/' + suite, running_folder) + except Exception, e: + report_fail("initialize suite fail: " + str(e)) + self.load_fails = self.load_fails +1 + return False + + result, message = suite_instance.load_settings() + if not result: + report_fail("load settings fail: " + str(e)) + self.load_fails = self.load_fails +1 + return False + + try: + result, message = suite_instance.on_suite_setup() + except Exception, e: + result = False + message = str(e); + if not result: + api_log_error(message) + report_fail (message) + self.setup_fails = self.setup_fails + 1 + return False + + self.running_suite = suite + + cases.sort() + + # run cases + for case in cases: + if not os.path.isdir(self.root_path + '/suites/' + suite + '/cases/' + case): + continue + + self.running_case = case + self.run_case(suite_instance, case) + self.running_case = '' + + # suites cleanup + self.running_suite = '' + try: + result, message = suite_instance.on_suite_cleanup() + except Exception, e: + result = False + message = str(e); + if not result: + api_log_error(message) + report_fail (message) + self.setup_fails = self.setup_fails + 1 + return + + def start_run(self): + if self.target_suites is None: + print "\n\nstart run: no target suites, exit.." + return + + cur_time = time.localtime() + time_prefix = "{:02}-{:02}-{:02}-{:02}".format( + cur_time.tm_mon, cur_time.tm_mday, cur_time.tm_hour, cur_time.tm_min) + + debug = api_get_value('debug', False) + if debug: + self.running_folder = 'debug' + else: + self.running_folder = 'run-' + time_prefix + + folder = self.root_path + "/run/" +self.running_folder; + + if os.path.exists(folder): + shutil.rmtree(folder, ignore_errors=True) + + if not os.path.exists(folder): + os.makedirs(folder ) + os.makedirs(folder + "/suites") + + api_init_log(folder + "/test.log") + + self.report = open(folder + "/report.txt", 'a') + + self.target_suites.sort() + + for suite in self.target_suites: + if not os.path.isdir(self.root_path + '/suites/' + suite): + continue + self.report.write("suite " + suite + " cases:\n") + if self.target_cases is None: + cases = os.listdir(self.root_path + "/suites/" + suite + "/cases") + self.run_suite(suite, cases) + else: + self.run_suite(suite, self.target_cases) + self.report.write("\n") + + self.report.write("\n\n") + summary = self.gen_execution_stats() + self.report.write(summary); + self.report.flush() + self.report.close() + print summary + + +def report_fail(message, case_description=''): + global framework + if framework is not None: + framework.report_result(False, message, case_description) + + api_log_error(message) + + return + +def report_success(case_description=''): + global framework + if framework is not None: + framework.report_result(True , "OK", case_description) + return diff --git a/wamr/test-tools/component_test/framework/suite.py b/wamr/test-tools/component_test/framework/suite.py new file mode 100644 index 0000000..2b690b0 --- /dev/null +++ b/wamr/test-tools/component_test/framework/suite.py @@ -0,0 +1,40 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import json + +class CTestSuiteBase(object): + def __init__(self, name, suite_path, run_path): + self.suite_path=suite_path + self.run_path=run_path + self.m_name = name + self.settings = {} + + def get_settings_item(self, item): + if item in self.settings: + return self.settings[item] + else: + return None + + def load_settings(self): + path = self.suite_path + "/settings.cfg" + if os.path.isfile(path): + try: + fp = open(path, 'r') + self.settings = json.load(fp) + fp.close() + except Exception, e: + return False, 'Load settings fail: ' + e.message + return True, 'OK' + else: + return True, 'No file' + + def on_suite_setup(self): + return True, 'OK' + + def on_suite_cleanup(self): + return True, 'OK' + diff --git a/wamr/test-tools/component_test/framework/test_api.py b/wamr/test-tools/component_test/framework/test_api.py new file mode 100644 index 0000000..ed9391e --- /dev/null +++ b/wamr/test-tools/component_test/framework/test_api.py @@ -0,0 +1,98 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import logging +import threading +from .test_utils import * + +global logger +logger = None + +def api_init_log(log_path): + global logger + print "api_init_log: " + log_path + logger = logging.getLogger(__name__) + + logger.setLevel(level = logging.INFO) + handler = logging.FileHandler(log_path) + handler.setLevel(logging.INFO) + formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + + console = logging.StreamHandler() + console.setLevel(logging.INFO) + + logger.addHandler(handler) + logger.addHandler(console) + + return + +def api_log(message): + global logger + if logger is None: + print message + else: + logger.info (message) + return + +def api_log_error(message): + global logger + if logger is None: + print message + else: + logger.error (message) + return + +def api_logv(message): + global logger + if logger is None: + print message + else: + logger.info(message) + return + +#####################################3 +global g_case_runner_event +def api_wait_case_event(timeout): + global g_case_runner_event + g_case_runner_event.clear() + g_case_runner_event.wait(timeout) + +def api_notify_case_runner(): + global g_case_runner_event + g_case_runner_event.set() + +def api_create_case_event(): + global g_case_runner_event + g_case_runner_event = threading.Event() + +####################################### + +def api_init_globals(): + global _global_dict + _global_dict = {} + +def api_set_value(name, value): + _global_dict[name] = value + +def api_get_value(name, defValue=None): + try: + return _global_dict[name] + except KeyError: + return defValue + + +######################################### +global root_path +def api_set_root_path(root): + global root_path + root_path = root + +def api_get_root_path(): + global root_path + return root_path; + + + diff --git a/wamr/test-tools/component_test/framework/test_utils.py b/wamr/test-tools/component_test/framework/test_utils.py new file mode 100644 index 0000000..1b8bbd0 --- /dev/null +++ b/wamr/test-tools/component_test/framework/test_utils.py @@ -0,0 +1,70 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import datetime +import os +import random +import re +import shlex +import subprocess +import sys +import time +import shutil +from subprocess import check_output, CalledProcessError + +def t_getPIDs(process): + try: + pidlist = map(int, check_output(["pidof", process]).split()) + except CalledProcessError: + pidlist = [] + #print process + ':list of PIDs = ' + ', '.join(str(e) for e in pidlist) + return pidlist + + +def t_kill_process_by_name(p_keywords): + pid_list = [] + ps_info = subprocess.check_output(shlex.split("ps aux")).split("\n") + for p in ps_info: + if p_keywords in p: + tmp = p.split(" ") + tmp = [x for x in tmp if len(x) > 0] + pid_list.append(tmp[1]) + + for pid in pid_list: + cmd = "kill -9 {}".format(pid) + subprocess.call(shlex.split(cmd)) + + return pid_list + + + +#proc -> name of the process +#kill = 1 -> search for pid for kill +#kill = 0 -> search for name (default) + +def t_process_exists(proc, kill = 0): + ret = False + processes = t_getPIDs(proc) + + for pid in processes: + if kill == 0: + return True + else: + print "kill [" + proc + "], pid=" + str(pid) + os.kill((pid), 9) + ret = True + return ret + +def t_copy_files(source_dir, pattern, dest_dir): + files = os.listdir(source_dir) + for file in files: + if file is '/' or file is '.' or file is '..': + continue + + if pattern == '*' or pattern is '' or files.endswith(pattern): + shutil.copy(source_dir+"/"+ file,dest_dir) + + + diff --git a/wamr/test-tools/component_test/harness/__init__.py b/wamr/test-tools/component_test/harness/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/harness/harness_api.py b/wamr/test-tools/component_test/harness/harness_api.py new file mode 100644 index 0000000..e35aa6b --- /dev/null +++ b/wamr/test-tools/component_test/harness/harness_api.py @@ -0,0 +1,150 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import shutil +import subprocess +import json +import time + +from framework import test_api +from framework.test_utils import * + +output = "output.txt" + +def start_env(): + os.system("./start.sh") + +def stop_env(): + os.system("./stop.sh") + time.sleep(0.5) + os.chdir("../") #reset path for other cases in the same suite + +def check_is_timeout(): + line_num = 0 + ft = open(output, 'r') + lines = ft.readlines() + + for line in reversed(lines): + if (line[0:36] == "--------one operation begin.--------"): + break + line_num = line_num + 1 + + ft.close() + if (lines[-(line_num)] == "operation timeout"): + return True + else: + return False + +def parse_ret(file): + ft = open(file, 'a') + ft.writelines("\n") + ft.writelines("--------one operation finish.--------") + ft.writelines("\n") + ft.close() + + ft = open(file, 'r') + for line in reversed(ft.readlines()): + if (line[0:16] == "response status "): + ret = line[16:] + ft.close() + return int(ret) + +def run_host_tool(cmd, file): + ft = open(file, 'a') + ft.writelines("--------one operation begin.--------") + ft.writelines("\n") + ft.close() + os.system(cmd + " -o" + file) + if (check_is_timeout() == True): + return -1 + return parse_ret(file) + +def install_app(app_name, file_name): + return run_host_tool("./host_tool -i " + app_name + " -f ../test-app/" + file_name, output) + +def uninstall_app(app_name): + return run_host_tool("./host_tool -u " + app_name, output) + +def query_app(): + return run_host_tool("./host_tool -q ", output) + +def send_request(url, action, payload): + if (payload is None): + return run_host_tool("./host_tool -r " + url + " -A " + action, output) + else: + return run_host_tool("./host_tool -r " + url + " -A " + action + " -p " + payload, output) + +def register(url, timeout, alive_time): + return run_host_tool("./host_tool -s " + url + " -t " + str(timeout) + " -a " + str(alive_time), output) + +def deregister(url): + return run_host_tool("./host_tool -d " + url, output) + +def get_response_payload(): + line_num = 0 + ft = open(output, 'r') + lines = ft.readlines() + + for line in reversed(lines): + if (line[0:16] == "response status "): + break + line_num = line_num + 1 + + payload_lines = lines[-(line_num):-1] + ft.close() + + return payload_lines + +def check_query_apps(expected_app_list): + if (check_is_timeout() == True): + return False + json_lines = get_response_payload() + json_str = " ".join(json_lines) + json_dict = json.loads(json_str) + app_list = [] + + for key, value in json_dict.items(): + if key[0:6] == "applet": + app_list.append(value) + + if (sorted(app_list) == sorted(expected_app_list)): + return True + else: + return False + +def check_response_payload(expected_payload): + if (check_is_timeout() == True): + return False + json_lines = get_response_payload() + json_str = " ".join(json_lines) + + if (json_str.strip() != ""): + json_dict = json.loads(json_str) + else: + json_dict = {} + + if (json_dict == expected_payload): + return True + else: + return False + +def check_get_event(): + line_num = 0 + ft = open(output, 'r') + lines = ft.readlines() + + for line in reversed(lines): + if (line[0:16] == "response status "): + break + line_num = line_num + 1 + + payload_lines = lines[-(line_num):-1] + ft.close() + + if (payload_lines[1][0:17] == "received an event"): + return True + else: + return False diff --git a/wamr/test-tools/component_test/host-clients/src/host_app_sample.c b/wamr/test-tools/component_test/host-clients/src/host_app_sample.c new file mode 100644 index 0000000..1fb4ba1 --- /dev/null +++ b/wamr/test-tools/component_test/host-clients/src/host_app_sample.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include "host_api.h" +#include "bi-inc/attr_container.h" +#include "er-coap-constants.h" + +static char *read_file_to_buffer(const char *filename, int *ret_size); +int send_request_to_applet_success = 0; +const char *label_for_request = "request1"; +int event_listener_counter = 0; +char *applet_buf[1024 * 1024]; +const char *host_agent_ip = "127.0.0.1"; +void f_aee_response_handler(void *usr_ctx, aee_response_t *response) +{ + if (response == NULL) { + printf("########## request timeout!!! \n"); + } else { + char *str = (char *) usr_ctx; + printf("#### dump response ####\n"); + printf("#### user data: %s \n", str); + printf("#### status: %d \n", response->status); + if (response->payload != NULL) + attr_container_dump((attr_container_t *) response->payload); + } +} + +void f_aee_event_listener(const char *url, void *event, int fmt) +{ + printf("######## event is received. url: %s, fmt:%d ############\n", url, + fmt); + + attr_container_t *attr_obj = (attr_container_t *) event; + + attr_container_dump(attr_obj); + /* + if (0 == strcmp(url, "alert/overheat")) + { + event_listener_counter++; + printf("event :%d \n", event_listener_counter); + } + */ +} + +static int print_menu_and_select(void) +{ + char s[256]; + int choice; + do { + printf("\n"); + printf("1. Install TestApplet1\n"); + printf("2. Install TestApplet2\n"); + printf("3. Install TestApplet3\n"); + printf("4. Uninstall TestApplet1\n"); + printf("5. Uninstall TestApplet2\n"); + printf("6. Uninstall TestApplet3\n"); + printf("7. Send Request to TestApplet1\n"); + printf("8. Register Event to TestApplet1\n"); + printf("9. UnRegister Event to TestApplet1\n"); + printf("a. Query Applets\n"); + printf("t. Auto Test\n"); + printf("q. Exit\n"); + printf("Please Select: "); + + if (fgets(s, sizeof(s), stdin)) { + if (!strncmp(s, "q", 1)) + return 0; + if (!strncmp(s, "a", 1)) + return 10; + if (!strncmp(s, "t", 1)) + return 20; + choice = atoi(s); + if (choice >= 1 && choice <= 9) + return choice; + } + } while (1); + return 0; +} + +static void install_applet(int index) +{ + char applet_name[64]; + char applet_file_name[64]; + char *buf; + int size; + int ret; + + printf("Installing TestApplet%d...\n", index); + snprintf(applet_name, sizeof(applet_name), "TestApplet%d", index); + snprintf(applet_file_name, sizeof(applet_file_name), "./TestApplet%d.wasm", + index); + buf = read_file_to_buffer(applet_file_name, &size); + if (!buf) { + printf("Install Applet failed: read file %s error.\n", + applet_file_name); + return; + } + + //step2. install applet + ret = aee_applet_install(buf, "wasm", size, applet_name, 5000); + if (ret) { + printf("%s install success\n", applet_name); + } + free(buf); +} + +static void uninstall_applet(int index) +{ + int ret; + char applet_name[64]; + snprintf(applet_name, sizeof(applet_name), "TestApplet%d", index); + ret = aee_applet_uninstall(applet_name, "wasm", 5000); + if (ret) { + printf("uninstall %s success\n", applet_name); + } else { + printf("uninstall %s failed\n", applet_name); + } +} + +static void send_request(int index) +{ + char url[64]; + int ret; + aee_request_t req; + const char *user_context = "label for request"; + attr_container_t *attr_obj = attr_container_create( + "Send Request to Applet"); + attr_container_set_string(&attr_obj, "String key", "Hello"); + attr_container_set_int(&attr_obj, "Int key", 1000); + attr_container_set_int64(&attr_obj, "Int64 key", 0x77BBCCDD11223344LL); + + //specify the target wasm app + snprintf(url, sizeof(url), "/app/TestApplet%d/url1", index); + + //not specify the target wasm app + //snprintf(url, sizeof(url), "url1"); + aee_request_init(&req, url, COAP_PUT); + aee_request_set_payload(&req, attr_obj, + attr_container_get_serialize_length(attr_obj), + PAYLOAD_FORMAT_ATTRIBUTE_OBJECT); + ret = aee_request_send(&req, f_aee_response_handler, (void *) user_context, + 10000); + + if (ret) { + printf("send request to TestApplet1 success\n"); + } +} + +static void register_event(const char *event_path) +{ + hostclient_register_event(event_path, f_aee_event_listener); +} + +static void unregister_event(const char *event_path) +{ + hostclient_unregister_event(event_path); +} + +static void query_applets() +{ + aee_applet_list_t applet_lst; + aee_applet_list_init(&applet_lst); + aee_applet_list(5000, &applet_lst); + aee_applet_list_clean(&applet_lst); +} + +static char * +read_file_to_buffer(const char *filename, int *ret_size) +{ + FILE *fl = NULL; + char *buffer = NULL; + int file_size = 0; + if (!(fl = fopen(filename, "rb"))) { + printf("file open failed\n"); + return NULL; + } + + fseek(fl, 0, SEEK_END); + file_size = ftell(fl); + + if (file_size == 0) { + printf("file length 0\n"); + return NULL; + } + + if (!(buffer = (char *) malloc(file_size))) { + fclose(fl); + return NULL; + } + + fseek(fl, 0, SEEK_SET); + + if (!fread(buffer, 1, file_size, fl)) { + printf("file read failed\n"); + return NULL; + } + + fclose(fl); + *ret_size = file_size; + return buffer; +} + +static void auto_test() +{ + int i; + int interval = 1000; /* ms */ + while (1) { + uninstall_applet(1); + uninstall_applet(2); + uninstall_applet(3); + install_applet(1); + install_applet(2); + install_applet(3); + + for (i = 0; i < 60 * 1000 / interval; i++) { + query_applets(); + send_request(1); + send_request(2); + send_request(3); + usleep(interval * 1000); + } + } +} + +void exit_program() +{ + hostclient_shutdown(); + exit(0); +} + +int + +main() +{ + bool ret; + + //step1. host client init + ret = hostclient_initialize(host_agent_ip, 3456); + + if (!ret) { + printf("host client initialize failed\n"); + return -1; + } + + do { + int choice = print_menu_and_select(); + printf("\n"); + + if (choice == 0) + exit_program(); + if (choice <= 3) + install_applet(choice); + else if (choice <= 6) + uninstall_applet(choice - 3); + else if (choice <= 7) + send_request(1); + else if (choice <= 8) + register_event("alert/overheat"); + else if (choice <= 9) + unregister_event("alert/overheat"); + else if (choice == 10) + query_applets(); + else if (choice == 20) + auto_test(); + } while (1); + + return 0; +} + +// Run program: Ctrl + F5 or Debug > Start Without Debugging menu +// Debug program: F5 or Debug > Start Debugging menu + +// Tips for Getting Started: +// 1. Use the Solution Explorer window to add/manage files +// 2. Use the Team Explorer window to connect to source control +// 3. Use the Output window to see build output and other messages +// 4. Use the Error List window to view errors +// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project +// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file diff --git a/wamr/test-tools/component_test/host-clients/src/makefile b/wamr/test-tools/component_test/host-clients/src/makefile new file mode 100644 index 0000000..763a60c --- /dev/null +++ b/wamr/test-tools/component_test/host-clients/src/makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +CC = gcc +CFLAGS := -Wall -g + +# Add this to make compiler happy +CFLAGS += -DWASM_ENABLE_INTERP=1 + +host_api_c=../../../../host-agent/host-api-c +attr_container_dir=../../../../wamr/core/app-framework/app-native-shared +coap_dir=../../../../host-agent/coap +shared_dir=../../../../wamr/core/shared + +# core +INCLUDE_PATH = -I$(host_api_c)/src -I$(attr_container_dir)/ \ + -I$(coap_dir)/er-coap -I$(coap_dir)/er-coap/extension \ + -I$(shared_dir)/include \ + -I$(shared_dir)/utils \ + -I$(shared_dir)/platform/include/ \ + -I$(shared_dir)/platform/linux/ + +LIB := $(host_api_c)/src/libhostapi.a +EXE := ./hostapp + +App_C_Files := host_app_sample.c + +OBJS := $(App_C_Files:.c=.o) + +all: $(EXE) + +%.o: %.c + @$(CC) $(CFLAGS) -c $< -o $@ $(INCLUDE_PATH) + +$(EXE): $(OBJS) + @rm -f $(EXE) + @$(CC) $(OBJS) -o $(EXE) $(LIB) -lpthread -lrt + @rm -f $(OBJS) + +.PHONY: clean +clean: + rm -f $(OBJS) $(EXE) diff --git a/wamr/test-tools/component_test/set_dev_env.sh b/wamr/test-tools/component_test/set_dev_env.sh new file mode 100755 index 0000000..1abe23b --- /dev/null +++ b/wamr/test-tools/component_test/set_dev_env.sh @@ -0,0 +1,7 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/sh + diff --git a/wamr/test-tools/component_test/start.py b/wamr/test-tools/component_test/start.py new file mode 100755 index 0000000..062ec2e --- /dev/null +++ b/wamr/test-tools/component_test/start.py @@ -0,0 +1,151 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/usr/bin/env python + +# -*- coding: utf-8 -*- +""" +It is the entrance of the iagent test framework. + +""" + +import argparse +import datetime +import os +import pprint +import random +import re +import shlex +import subprocess +import signal +import sys +import time + +sys.path.append('../../../app-sdk/python') +from framework.test_utils import * +from framework.framework import * + + +def signal_handler(signal, frame): + print('Pressed Ctrl+C!') + sys.exit(0) + +def Register_signal_handler(): + signal.signal(signal.SIGINT, signal_handler) +# signal.pause() + + +def flatten_args_list(l): + if l is None: + return None + + return [x for y in l for x in y] + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description = "to run specific case(s) "\ + "in specific suite(s) with FC test framework") + parser.add_argument('-s', dest = 'suite_id', action = 'append', + nargs = '+', + help = 'one or multiple suite ids, which are also setup ids.'\ + 'by default if it isn\'t passed from argument, all '\ + 'suites are going to be run.') + parser.add_argument('-t', dest = 'case_id', action = 'append', + nargs = '+', + help = 'one or multiple cases ids.'\ + 'by default if it isn\'t passed from argument, all '\ + 'cases in specific suites are going to be run.') + parser.add_argument('-n', dest = 'repeat_time', action = 'store', + default = 1, + help = 'how many times do you want to run. there is 40s '\ + 'break time between two rounds. each round includs '\ + 'init_setup, run_test_case and deinit_setup.') + parser.add_argument('--shuffle_all', dest = 'shuffle_all', + default = False, action = 'store_true', + help = 'shuffle_all test cases in per test suite '\ + 'by default, all cases under per suite should '\ + 'be executed by input order.') + parser.add_argument('--cases_list', dest='cases_list_file_path', + default=None, + action='store', + help="read cases list from a flie ") + parser.add_argument('--skip_proc', dest='skip_proc', + default = False, action = 'store_true', + help='do not start the test process.'\ + 'sometimes the gw_broker process will be started in eclipse for debug purpose') + parser.add_argument('-b', dest = 'binaries', action = 'store', + help = 'The path of target folder ') + parser.add_argument('-d', dest = 'debug', action = 'store_true', + help = 'wait user to attach the target process after launch processes ') + parser.add_argument('--rebuild', dest = 'rebuild', action = 'store_true', + help = 'rebuild all test binaries') + args = parser.parse_args() + + print "------------------------------------------------------------" + print "parsing arguments ... ..." + print args + + ''' + logger = logging.getLogger('coapthon.server.coap') + logger.setLevel(logging.DEBUG) + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + logger.addHandler(console) + ''' + print "------------------------------------------------------------" + print "preparing wamr binary and test tools ... ..." + os.system("cd ../../samples/simple/ && bash build.sh -p host-interp") + + Register_signal_handler() + + api_init_globals(); + + api_create_case_event(); + + suites_list = flatten_args_list(args.suite_id) + cases_list = flatten_args_list(args.case_id) + + dirname, filename = os.path.split(os.path.abspath(sys.argv[0])) + api_set_root_path(dirname); + + framework = CTestFramework(dirname); + framework.repeat_time = int(args.repeat_time) + framework.shuffle_all = args.shuffle_all + framework.skip_proc=args.skip_proc + + api_set_value('keep_env', args.skip_proc) + api_set_value('debug', args.debug) + api_set_value('rebuild', args.rebuild) + + binary_path = args.binaries + if binary_path is None: + binary_path = os.path.abspath(dirname + '/../..') + + print "checking execution binary path: " + binary_path + if not os.path.exists(binary_path): + print "The execution binary path was not available. quit..." + os._exit(0) + api_set_value('binary_path', binary_path) + + if suites_list is not None: + framework.target_suites = suites_list + else: + framework.load_suites() + + framework.target_cases = cases_list + framework.start_run() + + print "\n\n------------------------------------------------------------" + print "The run folder is [" + framework.running_folder +"]" + print "that's all. bye" + + print "kill to quit.." + t_kill_process_by_name("start.py") + + sys.exit(0) + os._exit() + + diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/case.py new file mode 100644 index 0000000..b8d2c38 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/01-install/case.py @@ -0,0 +1,94 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #uninstall inexistent App1 + ret = uninstall_app("App1") + if (ret != 160): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps([]) + if (ret == False): + return False, '' + + #install App1 + ret = install_app("App1", "01_install.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #install App2 + ret = install_app("App2", "01_install.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1","App2"]) + if (ret == False): + return False, '' + + #uninstall App2 + ret = uninstall_app("App2") + if (ret != 66): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/case.py new file mode 100644 index 0000000..e2192d5 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/02-request/case.py @@ -0,0 +1,73 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "02_request.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #send request to App1 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + expect_response_payload = {"key1":"value1","key2":"value2"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + #send request to App1 + ret = send_request("/res2", "DELETE", None) + if (ret != 66): + return False, '' + expect_response_payload = {} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/case.py new file mode 100644 index 0000000..3886cb8 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/03-event/case.py @@ -0,0 +1,67 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "03_event.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #register event + ret = register("/alert/overheat", 2000, 5000) + if (ret != 69): + return False, '' + ret = check_get_event() + if (ret == False): + return False, '' + + #deregister event + ret = deregister("/alert/overheat") + if (ret != 69): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/case.py new file mode 100644 index 0000000..bf395f5 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/04-request-internal/case.py @@ -0,0 +1,80 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "04_request_internal_resp.wasm") + if (ret != 65): + return False, '' + + #install App2 + ret = install_app("App2", "04_request_internal_req.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1","App2"]) + if (ret == False): + return False, '' + + #send request to App2 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + time.sleep(2) + expect_response_payload = {"key1":"value1","key2":"value2"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + #send request to App2 + ret = send_request("/res2", "GET", None) + if (ret != 69): + return False, '' + time.sleep(2) + expect_response_payload = {"key1":"value1","key2":"value2"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/case.py new file mode 100644 index 0000000..79c3287 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/05-event-internal/case.py @@ -0,0 +1,70 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "05_event_internal_provider.wasm") + if (ret != 65): + return False, '' + + #install App2 + ret = install_app("App2", "05_event_internal_subscriber.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1","App2"]) + if (ret == False): + return False, '' + + #send request to App2 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + time.sleep(2) + expect_response_payload = {"key1":"value1","key2":"value2"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/case.py new file mode 100644 index 0000000..90af4d5 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/06-timer/case.py @@ -0,0 +1,70 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "06_timer.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #send request to App1 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + + time.sleep(3) + + ret = send_request("/check_timer", "GET", None) + if (ret != 69): + return False, '' + expect_response_payload = {"num":2} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/case.py new file mode 100644 index 0000000..2bb7562 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/07-sensor/case.py @@ -0,0 +1,65 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "07_sensor.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #send request to App1 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + time.sleep(2) + expect_response_payload = {"key1":"value1","key2":"value2"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/case.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/case.py new file mode 100644 index 0000000..99a4512 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/cases/08-on-destroy/case.py @@ -0,0 +1,78 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import sys +import time +import random +import logging +import json + +from framework.case_base import * +from framework.test_api import * +from harness.harness_api import * + +class CTestCase(CTestCaseBase): + def __init__(self, suite): + CTestCaseBase.__init__(self, suite) + + def get_case_name(self): + case_path = os.path.dirname(os.path.abspath( __file__ )) + return os.path.split(case_path)[1] + + def on_get_case_description(self): + return "startup the executables" + + def on_setup_case(self): + os.chdir(self.get_case_name()) + start_env() + api_log_error("on_setup_case OK") + return True, '' + + def on_cleanup_case(self): + stop_env() + api_log_error("on_cleanup_case OK") + return True, '' + + # called by the framework + def on_run_case(self): + time.sleep(0.5) + + #install App1 + ret = install_app("App1", "08_on_destroy.wasm") + if (ret != 65): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps(["App1"]) + if (ret == False): + return False, '' + + #send request to App1 + ret = send_request("/res1", "GET", None) + if (ret != 69): + return False, '' + time.sleep(2) + expect_response_payload = {"key1":"value1"} + ret = check_response_payload(expect_response_payload) + if (ret == False): + return False, '' + + #uninstall App1 + ret = uninstall_app("App1") + if (ret != 66): + return False, '' + + #query Apps + ret = query_app() + if (ret != 69): + return False, '' + ret = check_query_apps([]) + if (ret == False): + return False, '' + + return True, '' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/cases/__init__.py b/wamr/test-tools/component_test/suites/01-life-cycle/cases/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/suite_setup.py b/wamr/test-tools/component_test/suites/01-life-cycle/suite_setup.py new file mode 100644 index 0000000..2307186 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/suite_setup.py @@ -0,0 +1,56 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +import os +import shutil +import types +import time +import glob + +from framework.test_api import * +from framework.test_utils import * +from harness.harness_api import * +from framework.suite import * + +class CTestSuite(CTestSuiteBase): + setup_path = "" + def __init__(self, name, suite_path, run_path): + CTestSuiteBase.__init__(self, name, suite_path, run_path) + + def on_suite_setup(self): + global setup_path + setup_path = os.getcwd() + cases = os.listdir(self.suite_path + "/cases/") + cases.sort() + + if api_get_value("rebuild", False): + path_tmp = os.getcwd() + os.chdir(self.suite_path + "/test-app") + os.system(self.suite_path + "/test-app" + "/build.sh") + os.chdir(path_tmp) + + os.makedirs(self.run_path + "/test-app") + + for case in cases: + if case != "__init__.pyc" and case != "__init__.py": + os.makedirs(self.run_path + "/" + case) + #copy each case's host_tool, simple, wasm files, start/stop scripts to the run directory, + shutil.copy(setup_path + "/../../samples/simple/out/simple", self.run_path + "/" + case) + shutil.copy(setup_path + "/../../samples/simple/out/host_tool", self.run_path + "/" + case) + for file in glob.glob(self.suite_path + "/test-app/" + "/*.wasm"): + shutil.copy(file, self.run_path + "/test-app") + shutil.copy(self.suite_path + "/tools/product/start.sh", self.run_path + "/" + case) + shutil.copy(self.suite_path + "/tools/product/stop.sh", self.run_path + "/" + case) + + os.chdir(self.run_path) + + return True, 'OK' + + def on_suite_cleanup(self): + global setup_path + os.chdir(setup_path) + api_log("stopping env..") + + return True, 'OK' diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/01_install.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/01_install.c new file mode 100644 index 0000000..f8ec393 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/01_install.c @@ -0,0 +1,16 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" + +void on_init() +{ + printf("Hello, I was installed.\n"); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/02_request.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/02_request.c new file mode 100644 index 0000000..ee7ecaf --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/02_request.c @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +void res1_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + + printf("### user resource 1 handler called\n"); + + printf("###### dump request ######\n"); + printf("sender: %lu\n", request->sender); + printf("url: %s\n", request->url); + printf("action: %d\n", request->action); + printf("payload:\n"); + if (request->payload + != NULL&& request->payload_len > 0 && request->fmt == FMT_ATTR_CONTAINER) + attr_container_dump((attr_container_t *) request->payload); + printf("#### dump request end ###\n"); + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + make_response_for_request(request, response); + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +void res2_handler(request_t *request) +{ + response_t response[1]; + make_response_for_request(request, response); + set_response(response, DELETED_2_02, 0, NULL, 0); + api_response_send(response); + + printf("### user resource 2 handler called\n"); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); + api_register_resource_handler("/res2", res2_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/03_event.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/03_event.c new file mode 100644 index 0000000..712110a --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/03_event.c @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/timer_wasm_app.h" +#include "wa-inc/request.h" + +int num = 0; + +void publish_overheat_event() +{ + attr_container_t *event; + + event = attr_container_create("event"); + attr_container_set_string(&event, "warning", "temperature is over high"); + + printf("###app publish event begin ###\n"); + + api_publish_event("alert/overheat", FMT_ATTR_CONTAINER, event, + attr_container_get_serialize_length(event)); + + printf("###app publish event end ###\n"); + + attr_container_destroy(event); +} + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + printf("Timer update %d\n", num++); + publish_overheat_event(); +} + +void start_timer() +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_init() +{ + start_timer(); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_req.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_req.c new file mode 100644 index 0000000..3e3ad0e --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_req.c @@ -0,0 +1,66 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +uint32 mid; +unsigned long sender; + +void my_response_handler(response_t *response, void *user_data) +{ + attr_container_t *payload; + printf("### user resource 1 handler called\n"); + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + response->mid = mid; + response->reciever = sender; + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +static void test_send_request(const char *url, const char *tag) +{ + request_t request[1]; + + init_request(request, (char *)url, COAP_PUT, 0, NULL, 0); + api_send_request(request, my_response_handler, (void *)tag); +} + +void res1_handler(request_t *request) +{ + mid = request->mid; + sender = request->sender; + test_send_request("url1", "a general request"); +} + +void res2_handler(request_t *request) +{ + mid = request->mid; + sender = request->sender; + test_send_request("/app/App1/url1", "a general request"); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); + api_register_resource_handler("/res2", res2_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_resp.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_resp.c new file mode 100644 index 0000000..45b25cb --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/04_request_internal_resp.c @@ -0,0 +1,52 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +void res1_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + + printf("[resp] ### user resource 1 handler called\n"); + + printf("[resp] ###### dump request ######\n"); + printf("[resp] sender: %lu\n", request->sender); + printf("[resp] url: %s\n", request->url); + printf("[resp] action: %d\n", request->action); + printf("[resp] payload:\n"); + if (request->payload != NULL && request->fmt == FMT_ATTR_CONTAINER) + attr_container_dump((attr_container_t *) request->payload); + printf("[resp] #### dump request end ###\n"); + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + make_response_for_request(request, response); + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, attr_container_get_serialize_length(payload)); + printf("[resp] response payload len %d\n", + attr_container_get_serialize_length(payload)); + printf("[resp] reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/url1", res1_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_provider.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_provider.c new file mode 100644 index 0000000..712110a --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_provider.c @@ -0,0 +1,53 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/timer_wasm_app.h" +#include "wa-inc/request.h" + +int num = 0; + +void publish_overheat_event() +{ + attr_container_t *event; + + event = attr_container_create("event"); + attr_container_set_string(&event, "warning", "temperature is over high"); + + printf("###app publish event begin ###\n"); + + api_publish_event("alert/overheat", FMT_ATTR_CONTAINER, event, + attr_container_get_serialize_length(event)); + + printf("###app publish event end ###\n"); + + attr_container_destroy(event); +} + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + printf("Timer update %d\n", num++); + publish_overheat_event(); +} + +void start_timer() +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); +} + +void on_init() +{ + start_timer(); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_subscriber.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_subscriber.c new file mode 100644 index 0000000..dd031f8 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/05_event_internal_subscriber.c @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" + +uint32 mid; +unsigned long sender; + +void over_heat_event_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + response->mid = mid; + response->reciever = sender; + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +void res1_handler(request_t *request) +{ + mid = request->mid; + sender = request->sender; + api_subscribe_event("alert/overheat", over_heat_event_handler); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/06_timer.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/06_timer.c new file mode 100644 index 0000000..c203657 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/06_timer.c @@ -0,0 +1,76 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" +#include "wa-inc/timer_wasm_app.h" + +/* User global variable */ +int num = 0; + +/* Timer callback */ +void timer1_update(user_timer_t timer) +{ + if (num < 2) + num++; +} + +void res1_handler(request_t *request) +{ + user_timer_t timer; + + /* set up a timer */ + timer = api_timer_create(1000, true, false, timer1_update); + api_timer_restart(timer, 1000); + + response_t response[1]; + + make_response_for_request(request, response); + + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, NULL, 0); + + api_response_send(response); +} + +void res2_handler(request_t *request) +{ + response_t response[1]; + attr_container_t *payload; + + if (num == 2) { + attr_container_t *payload; + printf("### user resource 1 handler called\n"); + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_int(&payload, "num", num); + + make_response_for_request(request, response); + + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, + attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); + } + +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); + api_register_resource_handler("/check_timer", res2_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/07_sensor.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/07_sensor.c new file mode 100644 index 0000000..24af34c --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/07_sensor.c @@ -0,0 +1,69 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" +#include "wa-inc/sensor.h" + +uint32 mid; +unsigned long sender; + +/* Sensor event callback*/ +void sensor_event_handler(sensor_t sensor, attr_container_t *event, + void *user_data) +{ + printf("### app get sensor event\n"); + + response_t response[1]; + attr_container_t *payload; + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + attr_container_set_string(&payload, "key2", "value2"); + + response->mid = mid; + response->reciever = sender; + set_response(response, CONTENT_2_05, + FMT_ATTR_CONTAINER, (const char *)payload, attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +void res1_handler(request_t *request) +{ + mid = request->mid; + sender = request->sender; + + sensor_t sensor; + char *user_data; + attr_container_t *config; + + printf("### app on_init 1\n"); + /* open a sensor */ + user_data = malloc(100); + printf("### app on_init 2\n"); + sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); + printf("### app on_init 3\n"); + + /* config the sensor */ + sensor_config(sensor, 2000, 0, 0); + printf("### app on_init 4\n"); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); +} + +void on_destroy() +{ + /* real destroy work including killing timer and closing sensor is accomplished in wasm app library version of on_destroy() */ +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/08_on_destroy.c b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/08_on_destroy.c new file mode 100644 index 0000000..637e0f7 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/08_on_destroy.c @@ -0,0 +1,67 @@ +/* +* Copyright (C) 2019 Intel Corporation. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "wasm_app.h" +#include "wa-inc/request.h" +#include "wa-inc/sensor.h" + +uint32 mid; +unsigned long sender; +sensor_t sensor; + +/* Sensor event callback*/ +void sensor_event_handler(sensor_t sensor, attr_container_t *event, void *user_data) { + printf("### app get sensor event\n"); + + response_t response[1]; + attr_container_t *payload; + + payload = attr_container_create("wasm app response payload"); + if (payload == NULL) + return; + + attr_container_set_string(&payload, "key1", "value1"); + + response->mid = mid; + response->reciever = sender; + set_response(response, + CONTENT_2_05, + FMT_ATTR_CONTAINER, + (const char *)payload, + attr_container_get_serialize_length(payload)); + printf("reciver: %lu, mid:%d\n", response->reciever, response->mid); + api_response_send(response); + + attr_container_destroy(payload); +} + +void res1_handler(request_t *request) +{ + mid = request->mid; + sender = request->sender; + + char *user_data; + attr_container_t *config; + + printf("### app on_init 1\n"); + /* open a sensor */ + user_data = malloc(100); + printf("### app on_init 2\n"); + sensor = sensor_open("sensor_test", 0, sensor_event_handler, user_data); + printf("### app on_init 3\n"); +} + +void on_init() +{ + /* register resource uri */ + api_register_resource_handler("/res1", res1_handler); +} + +void on_destroy() +{ + if(NULL != sensor){ + sensor_close(sensor); + } +} diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/test-app/build.sh b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/build.sh new file mode 100755 index 0000000..7ea7f31 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/test-app/build.sh @@ -0,0 +1,39 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +. ../../../set_dev_env.sh + +CC=/opt/wasi-sdk/bin/clang +APP_DIR=$PWD +WAMR_DIR=${APP_DIR}/../../../../../ +SDK_DIR=${WAMR_DIR}/wamr-sdk/out/simple-host-interp +APP_FRAMEWORK_DIR=${SDK_DIR}/app-sdk/wamr-app-framework +DEPS_DIR=${WAMR_DIR}/core/deps + +for i in `ls *.c` +do +APP_SRC="$i" +OUT_FILE=${i%.*}.wasm +/opt/wasi-sdk/bin/clang -O3 \ + -Wno-int-conversion \ + -I${APP_FRAMEWORK_DIR}/include \ + -I${DEPS_DIR} \ + --target=wasm32 -O3 -z stack-size=4096 -Wl,--initial-memory=65536 \ + --sysroot=${SDK_DIR}/app-sdk/libc-builtin-sysroot \ + -L${APP_FRAMEWORK_DIR}/lib -lapp_framework \ + -Wl,--allow-undefined-file=${SDK_DIR}/app-sdk/libc-builtin-sysroot/share/defined-symbols.txt \ + -Wl,--no-threads,--strip-all,--no-entry -nostdlib \ + -Wl,--export=on_init -Wl,--export=on_destroy \ + -Wl,--export=on_request -Wl,--export=on_response \ + -Wl,--export=on_sensor_event -Wl,--export=on_timer_callback \ + -Wl,--export=on_connection_data \ + -o ${OUT_FILE} \ + ${APP_SRC} +if [ -f ${OUT_FILE} ]; then + echo "build ${OUT_FILE} success" +else + echo "build ${OUT_FILE} fail" +fi +done \ No newline at end of file diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/start.sh b/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/start.sh new file mode 100755 index 0000000..f83e393 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/start.sh @@ -0,0 +1,10 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +cd $(dirname "$0") + +./simple -s > /dev/null 2>&1 & diff --git a/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/stop.sh b/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/stop.sh new file mode 100755 index 0000000..b7bc2c8 --- /dev/null +++ b/wamr/test-tools/component_test/suites/01-life-cycle/tools/product/stop.sh @@ -0,0 +1,9 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/bin/bash + +ps aux | grep -ie host_tool | awk '{print $2}' | xargs kill -9 & +ps aux | grep -ie simple | awk '{print $2}' | xargs kill -9 & diff --git a/wamr/test-tools/component_test/suites/__init__.py b/wamr/test-tools/component_test/suites/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wamr/test-tools/component_test/suites/readme.txt b/wamr/test-tools/component_test/suites/readme.txt new file mode 100644 index 0000000..1e8792f --- /dev/null +++ b/wamr/test-tools/component_test/suites/readme.txt @@ -0,0 +1,19 @@ +The description of each case in the test suites, should add descriptions in this file when new cases created in the future. + +suite 01-life-cycle: +case 01-install: + install or uninstall apps for times and query apps to see if the app list is expected. +case 02-request: + send request to an app, the app will respond specific attribute objects, host side should get them. +case 03-event: + register event to an app, the app will send event back periodically, host side should get some payload. +case 04-request_internal: + install 2 apps, host sends request to app2, then app2 sends request to app1, finally app1 respond specific payload to host, host side will check it. +case 05-event_internal: + install 2 apps, host sends request to app2, then app2 subscribe app1's event, finally app1 respond specific payload to host, host side will check it. +case 06-timer: + host send request to an app, the app then start a timer, when time goes by 2 seconds, app will respond specific payload to host, host side will check it. +case 07-sensor: + open sensor in app and then config the sensor in on_init, finally app will respond specific payload to host, host side will check it. +case 08-on_destroy: + open sensor in app in on_init, and close the sensor in on_destroy, host should install and uninstall the app successfully. diff --git a/wamr/test-tools/host-tool/CMakeLists.txt b/wamr/test-tools/host-tool/CMakeLists.txt new file mode 100644 index 0000000..b5755aa --- /dev/null +++ b/wamr/test-tools/host-tool/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +cmake_minimum_required (VERSION 2.8.3) +project (host-agent) + +if (NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE Debug) +endif (NOT CMAKE_BUILD_TYPE) + +if (NOT WAMR_BUILD_PLATFORM) + set (WAMR_BUILD_PLATFORM "linux") +endif (NOT WAMR_BUILD_PLATFORM) + +message ("WAMR_BUILD_PLATFORM = " ${WAMR_BUILD_PLATFORM}) + +add_definitions(-DWA_MALLOC=malloc) +add_definitions(-DWA_FREE=free) + +set (REPO_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set (IWASM_DIR ${REPO_ROOT_DIR}/core/iwasm) +set (APP_MGR_DIR ${REPO_ROOT_DIR}/core/app-mgr) +set (SHARED_DIR ${REPO_ROOT_DIR}/core/shared) +set (APP_FRAMEWORK_DIR ${REPO_ROOT_DIR}/core/app-framework) +#TODO: use soft-plc/tools/iec-runtime/external/cJSON instead +set (CJSON_DIR ${CMAKE_CURRENT_LIST_DIR}/external/cJSON) + +include (${APP_FRAMEWORK_DIR}/app-native-shared/native_interface.cmake) +include (${APP_MGR_DIR}/app-mgr-shared/app_mgr_shared.cmake) +include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) +include (${SHARED_DIR}/utils/shared_utils.cmake) +include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) +include (${CJSON_DIR}/cjson.cmake) +include (${SHARED_DIR}/coap/lib_coap.cmake) + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +endif () + +add_definitions(-Wall -Wno-pointer-sign -DMALLOC_MEMORY_FROM_SYSTEM) + +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/src + ${IWASM_DIR}/include +) + +file (GLOB_RECURSE HOST_TOOL_SRC src/*.c) + + +SET(SOURCES + ${HOST_TOOL_SRC} + ${PLATFORM_SHARED_SOURCE} + ${UTILS_SHARED_SOURCE} + ${NATIVE_INTERFACE_SOURCE} + ${CJSON_SOURCE} + ${LIB_HOST_AGENT_SOURCE} + ) + +add_executable(host_tool ${SOURCES}) +target_link_libraries(host_tool pthread) diff --git a/wamr/test-tools/host-tool/external/cJSON/LICENSE b/wamr/test-tools/host-tool/external/cJSON/LICENSE new file mode 100644 index 0000000..78deb04 --- /dev/null +++ b/wamr/test-tools/host-tool/external/cJSON/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/wamr/test-tools/host-tool/external/cJSON/cJSON.c b/wamr/test-tools/host-tool/external/cJSON/cJSON.c new file mode 100644 index 0000000..b948d6d --- /dev/null +++ b/wamr/test-tools/host-tool/external/cJSON/cJSON.c @@ -0,0 +1,2753 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 10) +#error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + snprintf(version, sizeof(version), "%i.%i.%i", + CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, + const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) { + return 1; + } + + if (string1 == string2) { + return 0; + } + + for (; tolower(*string1) == tolower(*string2); + (void) string1++, string2++) { + if (*string1 == '\0') { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks { + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, +internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, + const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) { + return NULL; + } + + length = strlen((const char*) string) + sizeof(""); + copy = (unsigned char*) hooks->allocate(length); + if (copy == NULL) { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) + && (global_hooks.deallocate == free)) { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*) hooks->allocate(sizeof(cJSON)); + if (node) { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct { + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, + parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; + (i < (sizeof(number_c_string) - 1)) + && can_access_at_index(input_buffer, i); i++) { + switch (buffer_at_offset(input_buffer)[i]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } + loop_end: number_c_string[i] = '\0'; + + number = strtod((const char*) number_c_string, (char**) &after_end); + if (number_c_string == after_end) { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) { + item->valueint = INT_MAX; + } else if (number <= (double) INT_MIN) { + item->valueint = INT_MIN; + } else { + item->valueint = (int) number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) { + object->valueint = INT_MAX; + } else if (number <= (double) INT_MIN) { + object->valueint = INT_MIN; + } else { + object->valueint = (int) number; + } + + return object->valuedouble = number; +} + +typedef struct { + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) { + newsize = INT_MAX; + } else { + return NULL; + } + } else { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*) p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } else { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*) p->hooks.allocate(newsize); + if (!newbuffer) { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*) buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, + printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) { + length = snprintf((char*) number_buffer, sizeof(number_buffer), "null"); + } else { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*) number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*) number_buffer, "%lg", &test) != 1) + || ((double) test != d)) { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*) number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* snprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int) (sizeof(number_buffer) - 1))) { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t) length + sizeof("")); + if (output_pointer == NULL) { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t) length); i++) { + if (number_buffer[i] == decimal_point) { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t) length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) { + h += (unsigned int) input[i] - '0'; + } else if ((input[i] >= 'A') && (input[i] <= 'F')) { + h += (unsigned int) 10 + input[i] - 'A'; + } else if ((input[i] >= 'a') && (input[i] <= 'f')) { + h += (unsigned int) 10 + input[i] - 'a'; + } else /* invalid */ + { + return 0; + } + + if (i < 3) { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8( + const unsigned char * const input_pointer, + const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) { + /* invalid second half of the surrogate pair */ + goto fail; + } + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } else { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } else if (codepoint < 0x800) { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } else if (codepoint < 0x10000) { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } else if (codepoint <= 0x10FFFF) { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } else { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char) (utf8_length - 1); utf8_position > 0; + utf8_position--) { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char) ((codepoint | 0x80) + & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) { + (*output_pointer)[0] = (unsigned char) ((codepoint | first_byte_mark) + & 0xFF); + } else { + (*output_pointer)[0] = (unsigned char) (codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + + fail: return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, + parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) + < input_buffer->length) && (*input_end != '\"')) { + /* is escape sequence */ + if (input_end[0] == '\\') { + if ((size_t)(input_end + 1 - input_buffer->content) + >= input_buffer->length) { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) + || (*input_end != '\"')) { + goto fail; + /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) + - skipped_bytes; + output = (unsigned char*) input_buffer->hooks.allocate( + allocation_length + sizeof("")); + if (output == NULL) { + goto fail; + /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) { + if (*input_pointer != '\\') { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) { + goto fail; + } + + switch (input_pointer[1]) { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, + input_end, &output_pointer); + if (sequence_length == 0) { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*) output; + + input_buffer->offset = (size_t)(input_end - input_buffer->content); + input_buffer->offset++; + + return true; + + fail: if (output != NULL) { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, + printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL, *output_end; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) { + return false; + } + + /* empty string */ + if (input == NULL) { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) { + return false; + } + strcpy((char*) output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) { + switch (*input_pointer) { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) { + return false; + } + output_end = output + output_length + sizeof("\"\""); + + /* no characters have to be escaped */ + if (escape_characters == 0) { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; + (void) input_pointer++, output_pointer++) { + if ((*input_pointer > 31) && (*input_pointer != '\"') + && (*input_pointer != '\\')) { + /* normal character, copy */ + *output_pointer = *input_pointer; + } else { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*) output_pointer, output_end - output_pointer, + "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*) item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, + parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, + printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, + parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, + printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, + parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, + printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) { + buffer->offset++; + } + + if (buffer->offset == buffer->length) { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) + || (buffer->offset != 0)) { + return NULL; + } + + if (can_access_at_index(buffer, 4) + && (strncmp((const char*) buffer_at_offset(buffer), "\xEF\xBB\xBF", + 3) == 0)) { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = {0, 0, 0, 0, {0, 0, 0}}; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + + fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, + const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, + buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + memcpy(printed, buffer->buffer, + cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + + fail: if (buffer->buffer != NULL) { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = {0, 0, 0, 0, 0, 0, {0, 0, 0}}; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, + const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) { + return false; + } + + p.buffer = (unsigned char*) buf; + p.length = (size_t) len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, + parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) + && (strncmp((const char*) buffer_at_offset(input_buffer), "null", 4) + == 0)) { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) + && (strncmp((const char*) buffer_at_offset(input_buffer), "false", + 5) == 0)) { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) + && (strncmp((const char*) buffer_at_offset(input_buffer), "true", 4) + == 0)) { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == '\"')) { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) + && ((buffer_at_offset(input_buffer)[0] == '-') + || ((buffer_at_offset(input_buffer)[0] >= '0') + && (buffer_at_offset(input_buffer)[0] <= '9')))) { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == '[')) { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == '{')) { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, + printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) { + return false; + } + + switch ((item->type) & 0xFF) { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; + } + strcpy((char*) output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) { + return false; + } + strcpy((char*) output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) { + return false; + } + strcpy((char*) output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: { + size_t raw_length = 0; + if (item->valuestring == NULL) { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, + parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == ']')) { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; + /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; + /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) + || buffer_at_offset(input_buffer)[0] != ']') { + goto fail; + /* expected end of array */ + } + + success: input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + + fail: if (head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, + printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) { + if (!print_value(current_element, output_buffer)) { + return false; + } + update_offset(output_buffer); + if (current_element->next) { + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ','; + if (output_buffer->format) { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, + parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) + || (buffer_at_offset(input_buffer)[0] != '{')) { + goto fail; + /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == '}')) { + goto success; + /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) { + goto fail; + /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) { + /* start the linked list */ + current_item = head = new_item; + } else { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) { + goto fail; + /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) + || (buffer_at_offset(input_buffer)[0] != ':')) { + goto fail; + /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) { + goto fail; + /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } while (can_access_at_index(input_buffer, 0) + && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) + || (buffer_at_offset(input_buffer)[0] != '}')) { + goto fail; + /* expected end of object */ + } + + success: input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + + fail: if (head != NULL) { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, + printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) { + return false; + } + + /* Compose the output: */ + length = (size_t)(output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) { + if (output_buffer->format) { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) { + return false; + } + for (i = 0; i < output_buffer->depth; i++) { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*) current_item->string, + output_buffer)) { + return false; + } + update_offset(output_buffer); + + length = (size_t)(output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) { + return false; + } + if (current_item->next) { + *output_pointer++ = ','; + } + + if (output_buffer->format) { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, + output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) { + return false; + } + if (output_buffer->format) { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) { + return 0; + } + + child = array->child; + + while (child != NULL) { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int) size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, + const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) { + return NULL; + } + + current_element = object->child; + if (case_sensitive) { + while ((current_element != NULL) && (current_element->string != NULL) + && (strcmp(name, current_element->string) != 0)) { + current_element = current_element->next; + } + } else { + while ((current_element != NULL) + && (case_insensitive_strcmp((const unsigned char*) name, + (const unsigned char*) (current_element->string)) != 0)) { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, + const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, + const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) { + return false; + } + + child = array->child; + + if (child == NULL) { + /* list is empty, start new one */ + array->child = item; + } else { + /* append to the end */ + while (child->next) { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*) string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) +#pragma GCC diagnostic pop +#endif + +static cJSON_bool add_item_to_object(cJSON * const object, + const char * const string, cJSON * const item, + const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) { + return false; + } + + if (constant_key) { + new_key = (char*) cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } else { + new_key = (char*) cJSON_strdup((const unsigned char*) string, hooks); + if (new_key == NULL) { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, + cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, + cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, + const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), + &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, + const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, + cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) { + return; + } + + after_inserted = get_array_item(array, (size_t) which); + if (after_inserted == NULL) { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) { + array->child = newitem; + } else { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, + cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) { + return false; + } + + if (replacement == item) { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) { + replacement->prev->next = replacement; + } + if (parent->child == item) { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, + cJSON *newitem) +{ + if (which < 0) { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t) which), + newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, + cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) + && (replacement->string != NULL)) { + cJSON_free(replacement->string); + } + replacement->string = (char*) cJSON_strdup((const unsigned char*) string, + &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, + get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, + cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, + const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + + fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*) json; + + if (json == NULL) { + return; + } + + while (*json) { + if (*json == ' ') { + json++; + } else if (*json == '\t') { + /* Whitespace characters. */ + json++; + } else if (*json == '\r') { + json++; + } else if (*json == '\n') { + json++; + } else if ((*json == '/') && (json[1] == '/')) { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) { + json++; + } + } else if ((*json == '/') && (json[1] == '*')) { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) { + json++; + } + json += 2; + } else if (*json == '\"') { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char) *json++; + while (*json && (*json != '\"')) { + if (*json == '\\') { + *into++ = (unsigned char) *json++; + } + *into++ = (unsigned char) *json++; + } + *into++ = (unsigned char) *json++; + } else { + /* All other characters. */ + *into++ = (unsigned char) *json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, + const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) + || cJSON_IsInvalid(a)) { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) { + return true; + } + + switch (a->type & 0xFF) { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) { + return true; + } + + return false; + + case cJSON_Array: { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/wamr/test-tools/host-tool/external/cJSON/cJSON.h b/wamr/test-tools/host-tool/external/cJSON/cJSON.h new file mode 100644 index 0000000..82e05af --- /dev/null +++ b/wamr/test-tools/host-tool/external/cJSON/cJSON.h @@ -0,0 +1,281 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + */ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + + CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols + CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) + CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + + For *nix builds that support visibility attribute, you can define similar behavior by + + setting default visibility to hidden by adding + -fvisibility=hidden (for gcc) + or + -xldscope=hidden (for sun cc) + to CFLAGS + + then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + + */ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 10 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON { + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks { + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + need to be released. With recurse!=0, it will duplicate any children connected to the item. + The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wamr/test-tools/host-tool/external/cJSON/cjson.cmake b/wamr/test-tools/host-tool/external/cJSON/cjson.cmake new file mode 100644 index 0000000..af1a9d8 --- /dev/null +++ b/wamr/test-tools/host-tool/external/cJSON/cjson.cmake @@ -0,0 +1,10 @@ + +set (CJSON_DIR ${CMAKE_CURRENT_LIST_DIR}) + +include_directories(${CJSON_DIR}) + + +file (GLOB_RECURSE source_all ${CJSON_DIR}/*.c) + +set (CJSON_SOURCE ${source_all}) + diff --git a/wamr/test-tools/host-tool/src/host_tool_utils.c b/wamr/test-tools/host-tool/src/host_tool_utils.c new file mode 100644 index 0000000..183ed0d --- /dev/null +++ b/wamr/test-tools/host-tool/src/host_tool_utils.c @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "host_tool_utils.h" +#include "bi-inc/shared_utils.h" +#include "bh_platform.h" + +#include +#include +#include +#include +#include + +typedef union jvalue { + bool z; + int8_t b; + uint16_t c; + int16_t s; + int32_t i; + int64_t j; + float f; + double d; +} jvalue; + + + +static inline int16_t get_int16(const char *buf) +{ + int16_t ret; + bh_memcpy_s(&ret, sizeof(int16_t), buf, sizeof(int16_t)); + return ret; +} + +static inline uint16_t get_uint16(const char *buf) +{ + return get_int16(buf); +} + +static inline int32_t get_int32(const char *buf) +{ + int32_t ret; + bh_memcpy_s(&ret, sizeof(int32_t), buf, sizeof(int32_t)); + return ret; +} + +static inline uint32_t get_uint32(const char *buf) +{ + return get_int32(buf); +} + +char* attr_container_get_attr_begin(const attr_container_t *attr_cont, + uint32_t *p_total_length, + uint16_t *p_attr_num); + +cJSON *attr2json(const attr_container_t *attr_cont) +{ + uint32_t total_length; + uint16_t attr_num, i, j, type; + const char *p, *tag, *key; + jvalue value; + cJSON *root; + + if (!attr_cont) + return NULL; + + root = cJSON_CreateObject(); + if (!root) + return NULL; + + /* TODO: how to convert the tag? */ + tag = attr_container_get_tag(attr_cont); + if (!tag) + goto fail; + + p = attr_container_get_attr_begin(attr_cont, &total_length, &attr_num); + if (!p) + goto fail; + + for (i = 0; i < attr_num; i++) { + cJSON *obj; + + key = p + 2; + /* Skip key len and key */ + p += 2 + get_uint16(p); + type = *p++; + + switch (type) { + case ATTR_TYPE_SHORT: + bh_memcpy_s(&value.s, sizeof(int16_t), p, sizeof(int16_t)); + if (NULL == (obj = cJSON_CreateNumber(value.s))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + /* another approach: cJSON_AddNumberToObject(root, key, value.s) */ + p += 2; + break; + case ATTR_TYPE_INT: + bh_memcpy_s(&value.i, sizeof(int32_t), p, sizeof(int32_t)); + if (NULL == (obj = cJSON_CreateNumber(value.i))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 4; + break; + case ATTR_TYPE_INT64: + bh_memcpy_s(&value.j, sizeof(uint64_t), p, sizeof(uint64_t)); + if (NULL == (obj = cJSON_CreateNumber(value.j))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 8; + break; + case ATTR_TYPE_BYTE: + bh_memcpy_s(&value.b, 1, p, 1); + if (NULL == (obj = cJSON_CreateNumber(value.b))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p++; + break; + case ATTR_TYPE_UINT16: + bh_memcpy_s(&value.c, sizeof(uint16_t), p, sizeof(uint16_t)); + if (NULL == (obj = cJSON_CreateNumber(value.c))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 2; + break; + case ATTR_TYPE_FLOAT: + bh_memcpy_s(&value.f, sizeof(float), p, sizeof(float)); + if (NULL == (obj = cJSON_CreateNumber(value.f))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 4; + break; + case ATTR_TYPE_DOUBLE: + bh_memcpy_s(&value.d, sizeof(double), p, sizeof(double)); + if (NULL == (obj = cJSON_CreateNumber(value.d))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += 8; + break; + case ATTR_TYPE_BOOLEAN: + bh_memcpy_s(&value.z, 1, p, 1); + if (NULL == (obj = cJSON_CreateBool(value.z))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p++; + break; + case ATTR_TYPE_STRING: + if (NULL == (obj = cJSON_CreateString(p + sizeof(uint16_t)))) + goto fail; + cJSON_AddItemToObject(root, key, obj); + p += sizeof(uint16_t) + get_uint16(p); + break; + case ATTR_TYPE_BYTEARRAY: + if (NULL == (obj = cJSON_CreateArray())) + goto fail; + cJSON_AddItemToObject(root, key, obj); + for (j = 0; j < get_uint32(p); j++) { + cJSON *item = cJSON_CreateNumber(*(p + sizeof(uint32_t) + j)); + if (item == NULL) + goto fail; + cJSON_AddItemToArray(obj, item); + } + p += sizeof(uint32_t) + get_uint32(p); + break; + } + } + + return root; + + fail: cJSON_Delete(root); + return NULL; +} + +attr_container_t *json2attr(const cJSON *json_obj) +{ + attr_container_t *attr_cont; + cJSON *item; + + if (NULL == (attr_cont = attr_container_create(""))) + return NULL; + + if (!cJSON_IsObject(json_obj)) + goto fail; + + cJSON_ArrayForEach(item, json_obj) + { + + if (cJSON_IsNumber(item)) { + attr_container_set_double(&attr_cont, item->string, + item->valuedouble); + } else if (cJSON_IsTrue(item)) { + attr_container_set_bool(&attr_cont, item->string, true); + } else if (cJSON_IsFalse(item)) { + attr_container_set_bool(&attr_cont, item->string, false); + } else if (cJSON_IsString(item)) { + attr_container_set_string(&attr_cont, item->string, + item->valuestring); + } else if (cJSON_IsArray(item)) { + int8_t *array; + int i = 0, len = sizeof(int8_t) * cJSON_GetArraySize(item); + cJSON *array_item; + + if (0 == len || NULL == (array = (int8_t *) malloc(len))) + goto fail; + memset(array, 0, len); + + cJSON_ArrayForEach(array_item, item) + { + /* must be number array */ + if (!cJSON_IsNumber(array_item)) + break; + /* TODO: if array_item->valuedouble > 127 or < -128 */ + array[i++] = (int8_t) array_item->valuedouble; + } + if (i > 0) + attr_container_set_bytearray(&attr_cont, item->string, array, + i); + free(array); + } + } + + return attr_cont; + + fail: attr_container_destroy(attr_cont); + return NULL; +} + +int g_mid = 0; + +int gen_random_id() +{ + static bool init = false; + int r; + + if (!init) { + srand(time(NULL)); + init = true; + } + + r = rand(); + g_mid = r; + + return r; +} + +char * +read_file_to_buffer(const char *filename, int *ret_size) +{ + char *buffer; + int file; + int file_size, read_size; + struct stat stat_buf; + + if (!filename || !ret_size) { + return NULL; + } + + if ((file = open(filename, O_RDONLY, 0)) == -1) { + return NULL; + } + + if (fstat(file, &stat_buf) != 0) { + close(file); + return NULL; + } + + file_size = stat_buf.st_size; + + if (!(buffer = malloc(file_size))) { + close(file); + return NULL; + } + + read_size = read(file, buffer, file_size); + close(file); + + if (read_size < file_size) { + free(buffer); + return NULL; + } + + *ret_size = file_size; + return buffer; +} + +int wirte_buffer_to_file(const char *filename, const char *buffer, int size) +{ + int file, ret; + + if ((file = open(filename, O_RDWR | O_CREAT | O_APPEND, 0644)) == -1) + return -1; + + ret = write(file, buffer, size); + + close(file); + + return ret; +} diff --git a/wamr/test-tools/host-tool/src/host_tool_utils.h b/wamr/test-tools/host-tool/src/host_tool_utils.h new file mode 100644 index 0000000..aaf427b --- /dev/null +++ b/wamr/test-tools/host-tool/src/host_tool_utils.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _HOST_TOOL_UTILS_H_ +#define _HOST_TOOL_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "bi-inc/attr_container.h" +#include "cJSON.h" + +/** + * @brief Convert attribute container object to cJSON object. + * + * @param attr the attribute container object to be converted + * + * @return the created cJSON object if not NULL, NULL means fail + * + * @warning the return object should be deleted with cJSON_Delete by caller + */ +cJSON *attr2json(const attr_container_t *attr); + +/** + * @brief Convert cJSON object to attribute container object. + * + * @param json the cJSON object to be converted + * + * @return the created attribute container object if not NULL, NULL means fail + * + * @warning the return object should be deleted with attr_container_destroy + */ +attr_container_t *json2attr(const cJSON *json); + +/** + * @brief Generate a random 32 bit integer. + * + * @return the generated random integer + */ +int gen_random_id(); + +/** + * @brief Read file content to buffer. + * + * @param filename the file name to read + * @param ret_size pointer of integer to save file size once return success + * + * @return the created buffer which contains file content if not NULL, NULL means fail + * + * @warning the return buffer should be deleted with free by caller + */ +char *read_file_to_buffer(const char *filename, int *ret_size); + +/** + * @brief Write buffer content to file. + * + * @param filename name the file name to be written + * @param buffer the buffer + * @param size size of the buffer to be written + * + * @return < 0 means fail, > 0 means the number of bytes actually written + */ +int wirte_buffer_to_file(const char *filename, const char *buffer, int size); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/wamr/test-tools/host-tool/src/main.c b/wamr/test-tools/host-tool/src/main.c new file mode 100644 index 0000000..4918cc7 --- /dev/null +++ b/wamr/test-tools/host-tool/src/main.c @@ -0,0 +1,908 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "host_tool_utils.h" +#include "bi-inc/shared_utils.h" +#include "bi-inc/attr_container.h" +#include "coap_ext.h" +#include "cJSON.h" +#include "app_manager_export.h" /* for Module_WASM_App */ +#include "host_link.h" /* for REQUEST_PACKET */ +#include "transport.h" + +#define BUF_SIZE 1024 +#define TIMEOUT_EXIT_CODE 2 +#define URL_MAX_LEN 256 +#define DEFAULT_TIMEOUT_MS 5000 +#define DEFAULT_ALIVE_TIME_MS 0 + +#define CONNECTION_MODE_TCP 1 +#define CONNECTION_MODE_UART 2 + +typedef enum { + INSTALL, UNINSTALL, QUERY, REQUEST, REGISTER, UNREGISTER +} op_type; + +typedef struct { + const char *file; + const char *name; + const char *module_type; + int heap_size; + /* max timers number */ + int timers; + int watchdog_interval; +} inst_info; + +typedef struct { + const char *name; + const char *module_type; +} uninst_info; + +typedef struct { + const char *name; +} query_info; + +typedef struct { + const char *url; + int action; + const char *json_payload_file; +} req_info; + +typedef struct { + const char *urls; +} reg_info; + +typedef struct { + const char *urls; +} unreg_info; + +typedef union operation_info { + inst_info inst; + uninst_info uinst; + query_info query; + req_info req; + reg_info reg; + unreg_info unreg; +} operation_info; + +typedef struct { + op_type type; + operation_info info; +} operation; + +typedef enum REPLY_PACKET_TYPE { + REPLY_TYPE_EVENT = 0, REPLY_TYPE_RESPONSE = 1 +} REPLY_PACKET_TYPE; + +static uint32_t g_timeout_ms = DEFAULT_TIMEOUT_MS; +static uint32_t g_alive_time_ms = DEFAULT_ALIVE_TIME_MS; +static char *g_redirect_file_name = NULL; +static int g_redirect_udp_port = -1; +static int g_conn_fd; /* may be tcp or uart */ +static char *g_server_addr = "127.0.0.1"; +static int g_server_port = 8888; +static char *g_uart_dev = "/dev/ttyS2"; +static int g_baudrate = B115200; +static int g_connection_mode = CONNECTION_MODE_TCP; + +extern int g_mid; +extern unsigned char leading[2]; + +/* -1 fail, 0 success */ +static int send_request(request_t *request, uint16_t msg_type) +{ + char *req_p; + int req_size, req_size_n, ret = -1; + + if ((req_p = pack_request(request, &req_size)) == NULL) + return -1; + + /* leanding bytes */ + if (!host_tool_send_data(g_conn_fd, leading, sizeof(leading))) + goto ret; + + /* message type */ + msg_type = htons(msg_type); + if (!host_tool_send_data(g_conn_fd, (char *) &msg_type, sizeof(msg_type))) + goto ret; + + /* payload length */ + req_size_n = htonl(req_size); + if (!host_tool_send_data(g_conn_fd, (char *) &req_size_n, + sizeof(req_size_n))) + goto ret; + + /* payload */ + if (!host_tool_send_data(g_conn_fd, req_p, req_size)) + goto ret; + + ret = 0; + + ret: free_req_resp_packet(req_p); + + return ret; +} + +/* +static package_type_t get_app_package_type(const char *buf, int size) +{ + if (buf && size > 4) { + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 's' && buf[3] == 'm') + return Wasm_Module_Bytecode; + if (buf[0] == '\0' && buf[1] == 'a' && buf[2] == 'o' && buf[3] == 't') + return Wasm_Module_AoT; + } + return Package_Type_Unknown; +} +*/ + +#define url_remain_space (sizeof(url) - strlen(url)) + +/*return: + 0: success + others: fail*/ +static int install(inst_info *info) +{ + request_t request[1] = { 0 }; + char *app_file_buf; + char url[URL_MAX_LEN] = { 0 }; + int ret = -1, app_size; + + snprintf(url, sizeof(url) - 1, "/applet?name=%s", info->name); + + if (info->module_type != NULL && url_remain_space > 0) + snprintf(url + strlen(url), url_remain_space, "&type=%s", + info->module_type); + + if (info->heap_size > 0 && url_remain_space > 0) + snprintf(url + strlen(url), url_remain_space, "&heap=%d", + info->heap_size); + + if (info->timers > 0 && url_remain_space > 0) + snprintf(url + strlen(url), url_remain_space, "&timers=%d", + info->timers); + + if (info->watchdog_interval > 0 && url_remain_space > 0) + snprintf(url + strlen(url), url_remain_space, "&wd=%d", + info->watchdog_interval); + + /*TODO: permissions to access JLF resource: AUDIO LOCATION SENSOR VISION platform.SERVICE */ + + if ((app_file_buf = read_file_to_buffer(info->file, &app_size)) == NULL) + return -1; + + init_request(request, url, COAP_PUT, + FMT_APP_RAW_BINARY, app_file_buf, app_size); + request->mid = gen_random_id(); + + if (info->module_type == NULL || strcmp(info->module_type, "wasm") == 0) + ret = send_request(request, INSTALL_WASM_APP); + else + ret = send_request(request, REQUEST_PACKET); + + free(app_file_buf); + + return ret; +} + +static int uninstall(uninst_info *info) +{ + request_t request[1] = { 0 }; + char url[URL_MAX_LEN] = { 0 }; + + snprintf(url, sizeof(url) - 1, "/applet?name=%s", info->name); + + if (info->module_type != NULL && url_remain_space > 0) + snprintf(url + strlen(url), url_remain_space, "&type=%s", + info->module_type); + + init_request(request, url, COAP_DELETE, + FMT_ATTR_CONTAINER, + NULL, 0); + request->mid = gen_random_id(); + + return send_request(request, REQUEST_PACKET); +} + +static int query(query_info *info) +{ + request_t request[1] = { 0 }; + int ret = -1; + char url[URL_MAX_LEN] = { 0 }; + + if (info->name != NULL) + snprintf(url, sizeof(url) - 1, "/applet?name=%s", info->name); + else + snprintf(url, sizeof(url) - 1, "/applet"); + + init_request(request, url, COAP_GET, + FMT_ATTR_CONTAINER, + NULL, 0); + request->mid = gen_random_id(); + + ret = send_request(request, REQUEST_PACKET); + + return ret; +} + +static int request(req_info *info) +{ + request_t request[1] = { 0 }; + attr_container_t *payload = NULL; + int ret = -1, payload_len = 0; + + if (info->json_payload_file != NULL) { + char *payload_file; + cJSON *json; + int payload_file_size; + + if ((payload_file = read_file_to_buffer(info->json_payload_file, + &payload_file_size)) == NULL) + return -1; + + if (NULL == (json = cJSON_Parse(payload_file))) { + free(payload_file); + goto fail; + } + + if (NULL == (payload = json2attr(json))) { + cJSON_Delete(json); + free(payload_file); + goto fail; + } + payload_len = attr_container_get_serialize_length(payload); + + cJSON_Delete(json); + free(payload_file); + } + + init_request(request, (char *)info->url, info->action, + FMT_ATTR_CONTAINER, payload, payload_len); + request->mid = gen_random_id(); + + ret = send_request(request, REQUEST_PACKET); + + if (info->json_payload_file != NULL && payload != NULL) + attr_container_destroy(payload); + + fail: return ret; +} + +/* + TODO: currently only support 1 url. + how to handle multiple responses and set process's exit code? + */ +static int subscribe(reg_info *info) +{ + request_t request[1] = { 0 }; + int ret = -1; +#if 0 + char *p; + + p = strtok(info->urls, ","); + while(p != NULL) { + char url[URL_MAX_LEN] = {0}; + snprintf(url, URL_MAX_LEN, "%s%s", "/event/", p); + init_request(request, + url, + COAP_PUT, + FMT_ATTR_CONTAINER, + NULL, + 0); + request->mid = gen_random_id(); + ret = send_request(request, false); + p = strtok (NULL, ","); + } +#else + char url[URL_MAX_LEN] = { 0 }; + char *prefix = info->urls[0] == '/' ? "/event" : "/event/"; + snprintf(url, URL_MAX_LEN, "%s%s", prefix, info->urls); + init_request(request, url, COAP_PUT, + FMT_ATTR_CONTAINER, + NULL, 0); + request->mid = gen_random_id(); + ret = send_request(request, REQUEST_PACKET); +#endif + return ret; +} + +static int unsubscribe(unreg_info *info) +{ + request_t request[1] = { 0 }; + int ret = -1; +#if 0 + char *p; + + p = strtok(info->urls, ","); + while(p != NULL) { + char url[URL_MAX_LEN] = {0}; + snprintf(url, URL_MAX_LEN, "%s%s", "/event/", p); + init_request(request, + url, + COAP_DELETE, + FMT_ATTR_CONTAINER, + NULL, + 0); + request->mid = gen_random_id(); + ret = send_request(request, false); + p = strtok (NULL, ","); + } +#else + char url[URL_MAX_LEN] = { 0 }; + snprintf(url, URL_MAX_LEN, "%s%s", "/event/", info->urls); + init_request(request, url, COAP_DELETE, + FMT_ATTR_CONTAINER, + NULL, 0); + request->mid = gen_random_id(); + ret = send_request(request, REQUEST_PACKET); +#endif + return ret; +} + +static int init() +{ + if (g_connection_mode == CONNECTION_MODE_TCP) { + int fd; + if (!tcp_init(g_server_addr, g_server_port, &fd)) + return -1; + g_conn_fd = fd; + return 0; + } else if (g_connection_mode == CONNECTION_MODE_UART) { + int fd; + if (!uart_init(g_uart_dev, g_baudrate, &fd)) + return -1; + g_conn_fd = fd; + return 0; + } + + return -1; +} + +static void deinit() +{ + close(g_conn_fd); +} + +static int parse_action(const char *str) +{ + if (strcasecmp(str, "PUT") == 0) + return COAP_PUT; + if (strcasecmp(str, "GET") == 0) + return COAP_GET; + if (strcasecmp(str, "DELETE") == 0) + return COAP_DELETE; + if (strcasecmp(str, "POST") == 0) + return COAP_POST; + return -1; +} + +static void showUsage() +{ + printf("\n"); + /*printf("Usage: host_tool [-i|--install]|[-u|--uninstall]|[-q|--query]|[-r|--request]|[-s|--register]|[-d|--deregister] ...\n");*/ + printf("Usage:\n\thost_tool -i|-u|-q|-r|-s|-d ...\n\n"); + + printf("\thost_tool -i -f \n" + "\t\t [--type=]\n" + "\t\t [--heap=]\n" + "\t\t [--timers=]\n" + "\t\t [--watchdog=]\n" + "\t\t [ ...] \n"); + printf("\thost_tool -u [ ...]\n"); + printf("\thost_tool -q[][ ...]\n"); + printf( + "\thost_tool -r -A [-p ] [ ...]\n"); + printf("\thost_tool -s [ ...]\n"); + printf("\thost_tool -d [ ...]\n\n"); + + printf( + "\t-i, --install Install an application\n"); + printf( + "\t-u, --uninstall Uninstall an application\n"); + printf( + "\t-q, --query Query all applications\n"); + printf("\t-r, --request Send a request\n"); + printf("\t-s, --register Register event(s)\n"); + printf("\t-d, --deregister De-register event(s)\n"); + printf( + "\t-f, --file Specify app binary file path\n"); + printf( + "\t-A, --action Specify action of the request\n"); + printf( + "\t-p, --payload Specify payload of the request\n"); + printf("\n"); + + printf("\n\tControl Options:\n"); + printf(" \t-S
    |--address=
    Set server address, default to 127.0.0.1\n"); + printf(" \t-P |--port= Set server port, default to 8888\n"); + printf(" \t-D |--uart= Set uart device, default to /dev/ttyS2\n"); + printf(" \t-B |--baudrate= Set uart device baudrate, default to 115200\n"); + + printf( + "\t-t |--timeout= Operation timeout in ms, default to 5000\n"); + printf( + "\t-a |--alive= Alive time in ms after last operation done, default to 0\n"); + printf( + "\t-o |--output= Redirect the output to output a file\n"); + printf( + "\t-U |--udp= Redirect the output to an UDP port in local machine\n"); + + printf("\nNotes:\n"); + printf("\t=name of the application\n"); + printf("\t=path of the application binary file in wasm format\n"); + printf( + "\t=resource descriptor, such as /app//res1 or /res1\n"); + printf( + "\t=event url list separated by ',', such as /event1,/event2,/event3\n"); + printf( + "\t=action of the request, can be PUT, GET, DELETE or POST (case insensitive)\n"); + printf("\t=path of the payload file in json format\n"); + printf("\t=Type of app. Can be 'wasm'(default) or 'jeff'\n"); + printf("\t=Heap size of app.\n"); + printf("\t=Max timers number app can use.\n"); + printf("\t=Watchdog interval in ms.\n"); +} + +#define CHECK_DUPLICATE_OPERATION do{ \ + if (operation_parsed) \ + { \ + showUsage(); \ + return false; \ + } \ +}while(0) + +#define ERROR_RETURN do{ \ + showUsage(); \ + return false; \ +}while(0) + +#define CHECK_ARGS_UNMATCH_OPERATION(op_type) do{ \ + if (!operation_parsed || op->type != op_type) \ + { \ + showUsage(); \ + return false; \ + } \ +}while(0) + +static bool parse_args(int argc, char *argv[], operation *op) +{ + int c; + bool operation_parsed = false; + bool conn_mode_parsed = false; + + while (1) { + int optIndex = 0; + static struct option longOpts[] = { + { "install", required_argument, NULL, 'i' }, + { "uninstall", required_argument, NULL, 'u' }, + { "query", optional_argument, NULL, 'q' }, + { "request", required_argument, NULL, 'r' }, + { "register", required_argument, NULL, 's' }, + { "deregister", required_argument, NULL, 'd' }, + { "timeout", required_argument, NULL, 't' }, + { "alive", required_argument, NULL, 'a' }, + { "output", required_argument, NULL, 'o' }, + { "udp", required_argument, NULL, 'U' }, + { "action", required_argument, NULL, 'A' }, + { "file", required_argument, NULL, 'f' }, + { "payload", required_argument, NULL, 'p' }, + { "type", required_argument, NULL, 0 }, + { "heap", required_argument, NULL, 1 }, + { "timers", required_argument, NULL, 2 }, + { "watchdog", required_argument, NULL, 3 }, + { "address", required_argument, NULL, 'S' }, + { "port", required_argument, NULL, 'P' }, + { "uart_device",required_argument, NULL, 'D' }, + { "baudrate", required_argument, NULL, 'B' }, + { "help", required_argument, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "i:u:q::r:s:d:t:a:o:U:A:f:p:S:P:D:B:h", + longOpts, &optIndex); + if (c == -1) + break; + + switch (c) { + case 'i': + CHECK_DUPLICATE_OPERATION; + op->type = INSTALL; + op->info.inst.name = optarg; + operation_parsed = true; + break; + case 'u': + CHECK_DUPLICATE_OPERATION; + op->type = UNINSTALL; + op->info.uinst.name = optarg; + operation_parsed = true; + break; + case 'q': + CHECK_DUPLICATE_OPERATION; + op->type = QUERY; + op->info.query.name = optarg; + break; + case 'r': + CHECK_DUPLICATE_OPERATION; + op->type = REQUEST; + op->info.req.url = optarg; + operation_parsed = true; + break; + case 's': + CHECK_DUPLICATE_OPERATION; + op->type = REGISTER; + op->info.reg.urls = optarg; + operation_parsed = true; + break; + case 'd': + CHECK_DUPLICATE_OPERATION; + op->type = UNREGISTER; + op->info.unreg.urls = optarg; + operation_parsed = true; + break; + case 't': + g_timeout_ms = atoi(optarg); + break; + case 'a': + g_alive_time_ms = atoi(optarg); + break; + case 'o': + g_redirect_file_name = optarg; + break; + case 'U': + g_redirect_udp_port = atoi(optarg); + break; + case 'A': + CHECK_ARGS_UNMATCH_OPERATION(REQUEST); + op->info.req.action = parse_action(optarg); + break; + case 'f': + CHECK_ARGS_UNMATCH_OPERATION(INSTALL); + op->info.inst.file = optarg; + break; + case 'p': + CHECK_ARGS_UNMATCH_OPERATION(REQUEST); + op->info.req.json_payload_file = optarg; + break; + /* module type */ + case 0: + /* TODO: use bit mask */ + /* CHECK_ARGS_UNMATCH_OPERATION(INSTALL | UNINSTALL); */ + if (op->type == INSTALL) + op->info.inst.module_type = optarg; + else if (op->type == UNINSTALL) + op->info.uinst.module_type = optarg; + break; + /* heap */ + case 1: + CHECK_ARGS_UNMATCH_OPERATION(INSTALL); + op->info.inst.heap_size = atoi(optarg); + break; + /* timers */ + case 2: + CHECK_ARGS_UNMATCH_OPERATION(INSTALL); + op->info.inst.timers = atoi(optarg); + break; + /* watchdog */ + case 3: + CHECK_ARGS_UNMATCH_OPERATION(INSTALL); + op->info.inst.watchdog_interval = atoi(optarg); + break; + case 'S': + if (conn_mode_parsed) { + showUsage(); + return false; + } + g_connection_mode = CONNECTION_MODE_TCP; + g_server_addr = optarg; + conn_mode_parsed = true; + break; + case 'P': + g_server_port = atoi(optarg); + break; + case 'D': + if (conn_mode_parsed) { + showUsage(); + return false; + } + g_connection_mode = CONNECTION_MODE_UART; + g_uart_dev = optarg; + conn_mode_parsed = true; + break; + case 'B': + g_baudrate = parse_baudrate(atoi(optarg)); + break; + case 'h': + showUsage(); + return false; + default: + showUsage(); + return false; + } + } + + /* check mandatory options for the operation */ + switch (op->type) { + case INSTALL: + if (NULL == op->info.inst.file || NULL == op->info.inst.name) + ERROR_RETURN; + break; + case UNINSTALL: + if (NULL == op->info.uinst.name) + ERROR_RETURN; + break; + case QUERY: + break; + case REQUEST: + if (NULL == op->info.req.url || op->info.req.action <= 0) + ERROR_RETURN; + break; + case REGISTER: + if (NULL == op->info.reg.urls) + ERROR_RETURN; + break; + case UNREGISTER: + if (NULL == op->info.unreg.urls) + ERROR_RETURN; + break; + default: + return false; + } + + return true; +} + +/* + return value: + < 0: not complete message + REPLY_TYPE_EVENT: event(request) + REPLY_TYPE_RESPONSE: response + */ +static int preocess_reply_data(const char *buf, int len, + imrt_link_recv_context_t *ctx) +{ + int result = -1; + const char *pos = buf; + +#if DEBUG + int i = 0; + for (; i < len; i++) { + printf(" 0x%02x", buf[i]); + } + printf("\n"); +#endif + + while (len-- > 0) { + result = on_imrt_link_byte_arrive((unsigned char) *pos++, ctx); + switch (result) { + case 0: { + imrt_link_message_t *message = &ctx->message; + if (message->message_type == RESPONSE_PACKET) + return REPLY_TYPE_RESPONSE; + if (message->message_type == REQUEST_PACKET) + return REPLY_TYPE_EVENT; + break; + } + default: + break; + } + } + return -1; +} + +static response_t * +parse_response_from_imrtlink(imrt_link_message_t *message, response_t *response) +{ + if (!unpack_response(message->payload, message->payload_size, response)) + return NULL; + + return response; +} + +static request_t * +parse_event_from_imrtlink(imrt_link_message_t *message, request_t *request) +{ + if (!unpack_request(message->payload, message->payload_size, request)) + return NULL; + + return request; +} + +static void output(const char *header, attr_container_t *payload, int foramt, + int payload_len) +{ + cJSON *json = NULL; + char *json_str = NULL; + + /* output the header */ + printf("%s", header); + if (g_redirect_file_name != NULL) + wirte_buffer_to_file(g_redirect_file_name, header, strlen(header)); + if (g_redirect_udp_port > 0 && g_redirect_udp_port < 65535) + udp_send("127.0.0.1", g_redirect_udp_port, header, strlen(header)); + + if (foramt != FMT_ATTR_CONTAINER || payload == NULL || payload_len <= 0) + return; + + if ((json = attr2json(payload)) == NULL) + return; + + if ((json_str = cJSON_Print(json)) == NULL) { + cJSON_Delete(json); + return; + } + + /* output the payload as json format */ + printf("%s", json_str); + if (g_redirect_file_name != NULL) + wirte_buffer_to_file(g_redirect_file_name, json_str, strlen(json_str)); + if (g_redirect_udp_port > 0 && g_redirect_udp_port < 65535) + udp_send("127.0.0.1", g_redirect_udp_port, json_str, strlen(json_str)); + + free(json_str); + cJSON_Delete(json); +} + +static void output_response(response_t *obj) +{ + char header[32] = { 0 }; + snprintf(header, sizeof(header), "\nresponse status %d\n", obj->status); + output(header, obj->payload, obj->fmt, obj->payload_len); +} + +static void output_event(request_t *obj) +{ + char header[256] = { 0 }; + snprintf(header, sizeof(header), "\nreceived an event %s\n", obj->url); + output(header, obj->payload, obj->fmt, obj->payload_len); +} + +int main(int argc, char *argv[]) +{ + int ret; + imrt_link_recv_context_t recv_ctx = { 0 }; + char buffer[BUF_SIZE] = { 0 }; + uint32_t last_check = 0, total_elpased_ms = 0; + bool is_responsed = false; + operation op; + + memset(&op, 0, sizeof(op)); + + if (!parse_args(argc, argv, &op)) + return -1; + + //TODO: reconnect 3 times + if (init() != 0) + return -1; + + switch (op.type) { + case INSTALL: + ret = install((inst_info *) &op.info.inst); + break; + case UNINSTALL: + ret = uninstall((uninst_info *) &op.info.uinst); + break; + case QUERY: + ret = query((query_info *) &op.info.query); + break; + case REQUEST: + ret = request((req_info *) &op.info.req); + break; + case REGISTER: + ret = subscribe((reg_info *) &op.info.reg); + break; + case UNREGISTER: + ret = unsubscribe((unreg_info *) &op.info.unreg); + break; + default: + goto ret; + } + + if (ret != 0) + goto ret; + + bh_get_elpased_ms(&last_check); + + while (1) { + int result = 0; + fd_set readfds; + struct timeval tv; + + total_elpased_ms += bh_get_elpased_ms(&last_check); + + if (!is_responsed) { + if (total_elpased_ms >= g_timeout_ms) { + output("operation timeout\n", NULL, 0, 0); + ret = TIMEOUT_EXIT_CODE; + goto ret; + } + } else { + if (total_elpased_ms >= g_alive_time_ms) { + /*ret = 0;*/ + goto ret; + } + } + + if (g_conn_fd == -1) { + if (init() != 0) { + sleep(1); + continue; + } + } + + FD_ZERO(&readfds); + FD_SET(g_conn_fd, &readfds); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + result = select(FD_SETSIZE, &readfds, NULL, NULL, &tv); + + if (result < 0) { + if (errno != EINTR) { + printf("Error in select, errno: 0x%x\n", errno); + ret = -1; + goto ret; + } + } else if (result == 0) { /* select timeout */ + } else if (result > 0) { + int n; + if (FD_ISSET(g_conn_fd, &readfds)) { + int reply_type = -1; + + n = read(g_conn_fd, buffer, BUF_SIZE); + if (n <= 0) { + g_conn_fd = -1; + continue; + } + + reply_type = preocess_reply_data((char *) buffer, n, &recv_ctx); + + if (reply_type == REPLY_TYPE_RESPONSE) { + response_t response[1] = { 0 }; + + parse_response_from_imrtlink(&recv_ctx.message, response); + + if (response->mid != g_mid) { + /* ignore invalid response */ + continue; + } + + is_responsed = true; + ret = response->status; + output_response(response); + + if (op.type == REGISTER || op.type == UNREGISTER) { + /* alive time start */ + total_elpased_ms = 0; + bh_get_elpased_ms(&last_check); + } + } else if (reply_type == REPLY_TYPE_EVENT) { + request_t event[1] = { 0 }; + + parse_event_from_imrtlink(&recv_ctx.message, event); + + if (op.type == REGISTER || op.type == UNREGISTER) { + output_event(event); + } + } + } + } + } /* end of while(1) */ + + ret: if (recv_ctx.message.payload != NULL) + free(recv_ctx.message.payload); + + deinit(); + + return ret; +} diff --git a/wamr/test-tools/host-tool/src/transport.c b/wamr/test-tools/host-tool/src/transport.c new file mode 100644 index 0000000..b1fb0e9 --- /dev/null +++ b/wamr/test-tools/host-tool/src/transport.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "transport.h" + +#define SA struct sockaddr + +unsigned char leading[2] = { 0x12, 0x34 }; + +bool tcp_init(const char *address, uint16_t port, int *fd) +{ + int sock; + struct sockaddr_in servaddr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) + return false; + + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(address); + servaddr.sin_port = htons(port); + + if (connect(sock, (SA*) &servaddr, sizeof(servaddr)) != 0) { + close(sock); + return false; + } + + *fd = sock; + return true; +} + +int parse_baudrate(int baud) +{ + switch (baud) { + case 9600: + return B9600; + case 19200: + return B19200; + case 38400: + return B38400; + case 57600: + return B57600; + case 115200: + return B115200; + case 230400: + return B230400; + case 460800: + return B460800; + case 500000: + return B500000; + case 576000: + return B576000; + case 921600: + return B921600; + case 1000000: + return B1000000; + case 1152000: + return B1152000; + case 1500000: + return B1500000; + case 2000000: + return B2000000; + case 2500000: + return B2500000; + case 3000000: + return B3000000; + case 3500000: + return B3500000; + case 4000000: + return B4000000; + default: + return -1; + } +} + +bool uart_init(const char *device, int baudrate, int *fd) +{ + int uart_fd; + struct termios uart_term; + + uart_fd = open(device, O_RDWR | O_NOCTTY); + + if (uart_fd < 0) + return false; + + memset(&uart_term, 0, sizeof(uart_term)); + uart_term.c_cflag = baudrate | CS8 | CLOCAL | CREAD; + uart_term.c_iflag = IGNPAR; + uart_term.c_oflag = 0; + + /* set noncanonical mode */ + uart_term.c_lflag = 0; + uart_term.c_cc[VTIME] = 30; + uart_term.c_cc[VMIN] = 1; + tcflush(uart_fd, TCIFLUSH); + + if (tcsetattr(uart_fd, TCSANOW, &uart_term) != 0) { + close(uart_fd); + return false; + } + + *fd = uart_fd; + + return true; +} + +bool udp_send(const char *address, int port, const char *buf, int len) +{ + int sockfd; + struct sockaddr_in servaddr; + + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) + return false; + + memset(&servaddr, 0, sizeof(servaddr)); + + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(port); + servaddr.sin_addr.s_addr = INADDR_ANY; + + sendto(sockfd, buf, len, MSG_CONFIRM, (const struct sockaddr *) &servaddr, + sizeof(servaddr)); + + close(sockfd); + + return true; +} + +bool host_tool_send_data(int fd, const char *buf, unsigned int len) +{ + int cnt = 0; + ssize_t ret; + + if (fd == -1 || buf == NULL || len <= 0) { + return false; + } + + resend: ret = write(fd, buf, len); + + if (ret == -1) { + if (errno == ECONNRESET) { + close(fd); + } + + // repeat sending if the outbuffer is full + if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (++cnt > 10) { + close(fd); + return false; + } + sleep(1); + goto resend; + } + } + + return (ret == len); +} + +#define SET_RECV_PHASE(ctx, new_phase) {ctx->phase = new_phase; ctx->size_in_phase = 0;} + +/* + * input: 1 byte from remote + * output: parse result + * return: -1 invalid sync byte + * 1 byte added to buffer, waiting more for complete packet + * 0 completed packet + * 2 in receiving payload + */ +int on_imrt_link_byte_arrive(unsigned char ch, imrt_link_recv_context_t *ctx) +{ + if (ctx->phase == Phase_Non_Start) { + if (ctx->message.payload) { + free(ctx->message.payload); + ctx->message.payload = NULL; + ctx->message.payload_size = 0; + } + + if (leading[0] == ch) { + ctx->phase = Phase_Leading; + } else { + return -1; + } + } else if (ctx->phase == Phase_Leading) { + if (leading[1] == ch) { + SET_RECV_PHASE(ctx, Phase_Type); + } else { + ctx->phase = Phase_Non_Start; + return -1; + } + } else if (ctx->phase == Phase_Type) { + unsigned char *p = (unsigned char *) &ctx->message.message_type; + p[ctx->size_in_phase++] = ch; + + if (ctx->size_in_phase == sizeof(ctx->message.message_type)) { + ctx->message.message_type = ntohs(ctx->message.message_type); + SET_RECV_PHASE(ctx, Phase_Size); + } + } else if (ctx->phase == Phase_Size) { + unsigned char * p = (unsigned char *) &ctx->message.payload_size; + p[ctx->size_in_phase++] = ch; + + if (ctx->size_in_phase == sizeof(ctx->message.payload_size)) { + ctx->message.payload_size = ntohl(ctx->message.payload_size); + SET_RECV_PHASE(ctx, Phase_Payload); + + if (ctx->message.payload) { + free(ctx->message.payload); + ctx->message.payload = NULL; + } + + /* no payload */ + if (ctx->message.payload_size == 0) { + SET_RECV_PHASE(ctx, Phase_Non_Start); + return 0; + } + + if (ctx->message.payload_size > 1024 * 1024) { + SET_RECV_PHASE(ctx, Phase_Non_Start); + return -1; + } + + ctx->message.payload = (char *) malloc(ctx->message.payload_size); + SET_RECV_PHASE(ctx, Phase_Payload); + } + } else if (ctx->phase == Phase_Payload) { + ctx->message.payload[ctx->size_in_phase++] = ch; + + if (ctx->size_in_phase == ctx->message.payload_size) { + SET_RECV_PHASE(ctx, Phase_Non_Start); + return 0; + } + + return 2; + } + + return 1; +} diff --git a/wamr/test-tools/host-tool/src/transport.h b/wamr/test-tools/host-tool/src/transport.h new file mode 100644 index 0000000..2d98c12 --- /dev/null +++ b/wamr/test-tools/host-tool/src/transport.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef DEPS_APP_MGR_HOST_TOOL_SRC_TRANSPORT_H_ +#define DEPS_APP_MGR_HOST_TOOL_SRC_TRANSPORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* IMRT link message between host and WAMR */ +typedef struct { + unsigned short message_type; + unsigned long payload_size; + char *payload; +} imrt_link_message_t; + +/* The receive phase of IMRT link message */ +typedef enum { + Phase_Non_Start, Phase_Leading, Phase_Type, Phase_Size, Phase_Payload +} recv_phase_t; + +/* The receive context of IMRT link message */ +typedef struct { + recv_phase_t phase; + int size_in_phase; + imrt_link_message_t message; +} imrt_link_recv_context_t; + +/** + * @brief Send data to WAMR. + * + * @param fd the connection fd to WAMR + * @param buf the buffer that contains content to be sent + * @param len size of the buffer to be sent + * + * @return true if success, false if fail + */ +bool host_tool_send_data(int fd, const char *buf, unsigned int len); + +/** + * @brief Handle one byte of IMRT link message + * + * @param ch the one byte from WAMR to be handled + * @param ctx the receive context + * + * @return -1 invalid sync byte + * 1 byte added to buffer, waiting more for complete packet + * 0 completed packet + * 2 in receiving payload + */ +int on_imrt_link_byte_arrive(unsigned char ch, imrt_link_recv_context_t *ctx); + +/** + * @brief Initialize TCP connection with remote server. + * + * @param address the network address of peer + * @param port the network port of peer + * @param fd pointer of integer to save the socket fd once return success + * + * @return true if success, false if fail + */ +bool tcp_init(const char *address, uint16_t port, int *fd); + +/** + * @brief Initialize UART connection with remote. + * + * @param device name of the UART device + * @param baudrate baudrate of the device + * @param fd pointer of integer to save the uart fd once return success + * + * @return true if success, false if fail + */ +bool uart_init(const char *device, int baudrate, int *fd); + +/** + * @brief Parse UART baudrate from an integer + * + * @param the baudrate interger to be parsed + * + * @return true if success, false if fail + * + * @par + * @code + * int baudrate = parse_baudrate(9600); + * ... + * uart_term.c_cflag = baudrate; + * ... + * @endcode + */ +int parse_baudrate(int baud); + +/** + * @brief Send data over UDP. + * + * @param address network address of the remote + * @param port network port of the remote + * @param buf the buffer that contains content to be sent + * @param len size of the buffer to be sent + * + * @return true if success, false if fail + */ +bool udp_send(const char *address, int port, const char *buf, int len); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif /* DEPS_APP_MGR_HOST_TOOL_SRC_TRANSPORT_H_ */ diff --git a/wamr/wamr-compiler/CMakeLists.txt b/wamr/wamr-compiler/CMakeLists.txt new file mode 100644 index 0000000..7efe1c0 --- /dev/null +++ b/wamr/wamr-compiler/CMakeLists.txt @@ -0,0 +1,181 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project (aot-compiler) +else() + project (aot-compiler C ASM) + enable_language (ASM_MASM) +endif() + +if (NOT DEFINED WAMR_BUILD_PLATFORM) + set (WAMR_BUILD_PLATFORM "linux") +endif() + +# Reset default linker flags +set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +add_definitions(-DWASM_ENABLE_INTERP=1) +add_definitions(-DWASM_ENABLE_WAMR_COMPILER=1) +add_definitions(-DWASM_ENABLE_BULK_MEMORY=1) +add_definitions(-DWASM_DISABLE_HW_BOUND_CHECK=1) +add_definitions(-DWASM_ENABLE_SHARED_MEMORY=1) +add_definitions(-DWASM_ENABLE_THREAD_MGR=1) + +# Set WAMR_BUILD_TARGET, currently values supported: +# "X86_64", "AMD_64", "X86_32", "ARM_32", "MIPS_32", "XTENSA_32" +if (NOT WAMR_BUILD_TARGET) + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + # Build as X86_64 by default in 64-bit platform + set (WAMR_BUILD_TARGET "X86_64") + else () + # Build as X86_32 by default in 32-bit platform + set (WAMR_BUILD_TARGET "X86_32") + endif () + if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "Win32")) + set (WAMR_BUILD_TARGET "X86_32") + endif() + endif() +endif () + +string(TOUPPER ${WAMR_BUILD_TARGET} WAMR_BUILD_TARGET) + +# Add definitions for the build target +if (WAMR_BUILD_TARGET STREQUAL "X86_64") + add_definitions(-DBUILD_TARGET_X86_64) +elseif (WAMR_BUILD_TARGET STREQUAL "AMD_64") + add_definitions(-DBUILD_TARGET_AMD_64) +elseif (WAMR_BUILD_TARGET STREQUAL "X86_32") + add_definitions(-DBUILD_TARGET_X86_32) +elseif (WAMR_BUILD_TARGET MATCHES "AARCH64.*") + add_definitions(-DBUILD_TARGET_AARCH64) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") +elseif (WAMR_BUILD_TARGET MATCHES "ARM.*") + add_definitions(-DBUILD_TARGET_ARM) + add_definitions(-DBUILD_TARGET="${WAMR_BUILD_TARGET}") +else () + message (FATAL_ERROR "-- Build target isn't set") +endif () + +message ("-- Build as target ${WAMR_BUILD_TARGET}") + +if (CMAKE_SIZEOF_VOID_P EQUAL 8) + if (WAMR_BUILD_TARGET STREQUAL "X86_64" OR WAMR_BUILD_TARGET STREQUAL "AMD_64" OR WAMR_BUILD_TARGET MATCHES "AARCH64.*") + # Add -fPIC flag if build as 64-bit + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") + set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC") + else () + add_definitions (-m32) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32") + endif () +endif () + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif (NOT CMAKE_BUILD_TYPE) +message ("-- CMAKE_BUILD_TYPE = " ${CMAKE_BUILD_TYPE}) + +# Enable LLVM +set (LLVM_SRC_ROOT "${PROJECT_SOURCE_DIR}/../core/deps/llvm") +if (WAMR_BUILD_PLATFORM STREQUAL "windows") + if (NOT EXISTS "${LLVM_SRC_ROOT}/win32build") + message (FATAL_ERROR "Cannot find LLVM dir: ${LLVM_SRC_ROOT}/win32build") + endif () + set (CMAKE_PREFIX_PATH "${LLVM_SRC_ROOT}/win32build;${CMAKE_PREFIX_PATH}") +else() + if (NOT EXISTS "${LLVM_SRC_ROOT}/build") + message (FATAL_ERROR "Cannot find LLVM dir: ${LLVM_SRC_ROOT}/build") + endif () + set (CMAKE_PREFIX_PATH "${LLVM_SRC_ROOT}/build;${CMAKE_PREFIX_PATH}") +endif () + +find_package(LLVM REQUIRED CONFIG) +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + if(NOT MSVC) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + else() + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") + endif() +endif() + +if (NOT MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections \ + -Wall -Wno-unused-parameter -Wno-pedantic") +endif() + +set (SHARED_DIR ../core/shared) +set (IWASM_DIR ../core/iwasm) +set (APP_FRAMEWORK_DIR ../core/app-framework) + +include_directories (${SHARED_DIR}/include + ${IWASM_DIR}/include) + +enable_language (ASM) + +include (${SHARED_DIR}/platform/${WAMR_BUILD_PLATFORM}/shared_platform.cmake) +include (${SHARED_DIR}/mem-alloc/mem_alloc.cmake) +include (${SHARED_DIR}/utils/shared_utils.cmake) +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) +include (${IWASM_DIR}/libraries/thread-mgr/thread_mgr.cmake) +include (${IWASM_DIR}/libraries/libc-builtin/libc_builtin.cmake) +include (${IWASM_DIR}/common/iwasm_common.cmake) +include (${IWASM_DIR}/interpreter/iwasm_interp.cmake) +include (${IWASM_DIR}/aot/iwasm_aot.cmake) +include (${IWASM_DIR}/compilation/iwasm_compl.cmake) + +if (NOT MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") +endif() + +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion -Wsign-conversion") +if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang" OR MSVC)) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif() +endif () + +if (NOT MSVC) + 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") +endif() + +# We disable these flags by default to stay the same with wasm runtime +# set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch=thunk -mfunction-return=thunk") + +if (NOT MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pie -fPIE -ftrapv -D_FORTIFY_SOURCE=2") +endif() + +# message ("-- CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}") + +add_library (vmlib + ${PLATFORM_SHARED_SOURCE} + ${MEM_ALLOC_SHARED_SOURCE} + ${UTILS_SHARED_SOURCE} + ${UNCOMMON_SHARED_SOURCE} + ${THREAD_MGR_SOURCE} + ${LIBC_BUILTIN_SOURCE} + ${IWASM_COMMON_SOURCE} + ${IWASM_INTERP_SOURCE} + ${IWASM_AOT_SOURCE}) + +add_library (aotclib ${IWASM_COMPL_SOURCE}) + +add_executable (wamrc main.c) + +if (NOT MSVC) + target_link_libraries (wamrc aotclib vmlib LLVMDemangle ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread) +else() + target_link_libraries (wamrc aotclib vmlib ${LLVM_AVAILABLE_LIBS}) +endif() diff --git a/wamr/wamr-compiler/build_llvm.py b/wamr/wamr-compiler/build_llvm.py new file mode 100644 index 0000000..d9912af --- /dev/null +++ b/wamr/wamr-compiler/build_llvm.py @@ -0,0 +1,98 @@ +# +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# + +#!/usr/bin/env python3 +import os +import sys +from pathlib import Path + +def clone_llvm(): + llvm_dir = Path("llvm") + if(llvm_dir.exists() == False): + print("Clone llvm to core/deps/ ..") + for line in os.popen("git clone --branch release/10.x https://github.com/llvm/llvm-project.git llvm"): + print(line) + else: + print("llvm source codes already existed") + return llvm_dir + +""" def detect_VS_version(): + program_dirs = [os.environ['ProgramFiles(x86)'], os.environ['ProgramFiles']] + for dir in program_dirs: + vswhere = Path("{}\\Microsoft Visual Studio\\Installer\\vswhere.exe".format(dir)) + if (vswhere.exists()): + print('"{}" -version 14.0,16.0'.format(vswhere)) + for line in os.popen('"{}" -version 14.0,16.0'.format(vswhere)): + keyvalue = line.split(':', maxsplit=1) + if(keyvalue[0] == "installationPath"): + value = keyvalue[1].strip() + for line in os.popen('"{}\\VC\\Auxiliary\\Build\\vcvars32.bat"'.format(value)): + print(line) + break """ + + +def main(): + current_os = sys.platform + print("current OS is ", current_os) + + current_dir = Path.cwd() + deps_dir = current_dir.joinpath( "../core/deps") + + os.chdir(deps_dir) + llvm_dir = clone_llvm() + os.chdir(llvm_dir) + + if(current_os == "linux"): + build_dir_name = "build" + llvm_file = "bin/llvm-lto" + # generator = '"Unix Makefiles"' + elif(current_os == "win32"): + build_dir_name = "win32build" + llvm_file = "LLVM.sln" + # generator = '"Visual Studio 15 2017"' + else: + build_dir_name = "build" + # generator = '""' + + Path(build_dir_name).mkdir(exist_ok = True) + build_dir = Path(build_dir_name) + os.chdir(build_dir) + + if ( not Path(llvm_file).exists()): + core_number = os.cpu_count() + print("Build llvm with", core_number, " cores") + cmd = 'cmake ../llvm \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;ARM;AArch64;Mips" \ + -DLLVM_INCLUDE_GO_TESTS=OFF \ + -DLLVM_INCLUDE_TOOLS=OFF \ + -DLLVM_INCLUDE_UTILS=OFF \ + -DLLVM_ENABLE_TERMINFO=OFF \ + -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ + -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ + -DLLVM_ENABLE_ZLIB:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_APPEND_VC_REV:BOOL=OFF' + print(cmd) + for line in os.popen(cmd): + print(line) + else: + print("llvm has already been Cmaked") + + if(current_os == "linux"): + for line in os.popen("make -j {}".format(core_number)): + print(line) + elif(current_os == "win32"): + print("Please open LLVM.sln in {} to build *Release* version".format(build_dir.absolute())) + + os.chdir(current_dir) + + +if __name__ == "__main__": + main() diff --git a/wamr/wamr-compiler/build_llvm.sh b/wamr/wamr-compiler/build_llvm.sh new file mode 100755 index 0000000..98f7a67 --- /dev/null +++ b/wamr/wamr-compiler/build_llvm.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# Copyright (C) 2020 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +DEPS_DIR=${PWD}/../core/deps + +cd ${DEPS_DIR} +if [ ! -d "llvm" ]; then + echo "Clone llvm to core/deps/ .." + git clone --depth 1 --branch release/10.x https://github.com/llvm/llvm-project.git llvm +fi + +cd llvm +mkdir -p build +cd build + +if [ ! -f bin/llvm-lto ]; then + + CORE_NUM=$(nproc --all) + if [ -z "${CORE_NUM}" ]; then + CORE_NUM=1 + fi + + echo "Build llvm with" ${CORE_NUM} "cores" + + cmake ../llvm \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;ARM;AArch64;Mips" \ + -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ + -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ + -DLLVM_ENABLE_ZLIB:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_APPEND_VC_REV:BOOL=OFF + make -j ${CORE_NUM} + +else + echo "llvm has already been built" +fi + +cd ${PWD} + diff --git a/wamr/wamr-compiler/build_llvm_xtensa.sh b/wamr/wamr-compiler/build_llvm_xtensa.sh new file mode 100755 index 0000000..c8bb9b8 --- /dev/null +++ b/wamr/wamr-compiler/build_llvm_xtensa.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +DEPS_DIR=${PWD}/../core/deps + +cd ${DEPS_DIR} +if [ ! -d "llvm" ]; then + echo "Clone llvm Xtensa to core/deps/ .." + git clone --depth 1 --branch xtensa_release_10.0.1 https://github.com/espressif/llvm-project.git llvm +fi + +cd llvm +mkdir -p build +cd build + +if [ ! -f bin/llvm-lto ]; then + + CORE_NUM=$(nproc --all) + if [ -z "${CORE_NUM}" ]; then + CORE_NUM=1 + fi + + echo "Build llvm with" ${CORE_NUM} "cores" + + cmake ../llvm \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DCMAKE_BUILD_TYPE:STRING="Release" \ + -DLLVM_TARGETS_TO_BUILD:STRING="X86;ARM;AArch64;Mips" \ + -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Xtensa" \ + -DLLVM_BUILD_LLVM_DYLIB:BOOL=OFF \ + -DLLVM_OPTIMIZED_TABLEGEN:BOOL=ON \ + -DLLVM_ENABLE_ZLIB:BOOL=OFF \ + -DLLVM_INCLUDE_DOCS:BOOL=OFF \ + -DLLVM_INCLUDE_EXAMPLES:BOOL=OFF \ + -DLLVM_INCLUDE_TESTS:BOOL=OFF \ + -DLLVM_INCLUDE_BENCHMARKS:BOOL=OFF \ + -DLLVM_APPEND_VC_REV:BOOL=OFF + make -j ${CORE_NUM} + +else + echo "llvm has already been built" +fi + +cd ${PWD} + diff --git a/wamr/wamr-compiler/main.c b/wamr/wamr-compiler/main.c new file mode 100644 index 0000000..3c72dc3 --- /dev/null +++ b/wamr/wamr-compiler/main.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" +#include "aot_export.h" + +static int +print_help() +{ + printf("Usage: wamrc [options] -o output_file wasm_file\n"); + printf(" --target= Set the target arch, which has the general format: \n"); + printf(" = x86_64, i386, aarch64, arm, thumb, xtensa, mips.\n"); + printf(" Default is host arch, e.g. x86_64\n"); + printf(" = for ex. on arm or thumb: v5, v6m, v7a, v7m, etc.\n"); + printf(" Use --target=help to list supported targets\n"); + printf(" --target-abi= Set the target ABI, e.g. gnu, eabi, gnueabihf, etc. (default: gnu)\n"); + printf(" Use --target-abi=help to list all the ABI supported\n"); + printf(" --cpu= Set the target CPU (default: host CPU, e.g. skylake)\n"); + printf(" Use --cpu=help to list all the CPU supported\n"); + printf(" --cpu-features= Enable or disable the CPU features\n"); + printf(" Use +feature to enable a feature, or -feature to disable it\n"); + printf(" For example, --cpu-features=+feature1,-feature2\n"); + printf(" Use --cpu-features=+help to list all the features supported\n"); + printf(" --opt-level=n Set the optimization level (0 to 3, default is 3)\n"); + printf(" --size-level=n Set the code size level (0 to 3, default is 3)\n"); + printf(" -sgx Generate code for SGX platform (Intel Software Guard Extention)\n"); + printf(" --bounds-checks=1/0 Enable or disable the bounds checks for memory access:\n"); + printf(" by default it is disabled in all 64-bit platforms except SGX and\n"); + printf(" in these platforms runtime does bounds checks with hardware trap,\n"); + printf(" and by default it is enabled in all 32-bit platforms\n"); + printf(" --format= Specifies the format of the output file\n"); + printf(" The format supported:\n"); + printf(" aot (default) AoT file\n"); + printf(" object Native object file\n"); + printf(" llvmir-unopt Unoptimized LLVM IR\n"); + printf(" llvmir-opt Optimized LLVM IR\n"); + printf(" --enable-bulk-memory Enable the post-MVP bulk memory feature\n"); + printf(" --enable-multi-thread Enable multi-thread feature, the dependent features bulk-memory and\n"); + printf(" thread-mgr will be enabled automatically\n"); + printf(" -v=n Set log verbose level (0 to 5, default is 2), larger with more log\n"); + printf("Examples: wamrc -o test.aot test.wasm\n"); + printf(" wamrc --target=i386 -o test.aot test.wasm\n"); + printf(" wamrc --target=i386 --format=object -o test.o test.wasm\n"); + return 1; +} + +int +main(int argc, char *argv[]) +{ + char *wasm_file_name = NULL, *out_file_name = NULL; + uint8 *wasm_file = NULL; + uint32 wasm_file_size; + wasm_module_t wasm_module = NULL; + aot_comp_data_t comp_data = NULL; + aot_comp_context_t comp_ctx = NULL; + RuntimeInitArgs init_args; + AOTCompOption option = { 0 }; + char error_buf[128]; + int log_verbose_level = 2; + bool sgx_mode = false; + + option.opt_level = 3; + option.size_level = 3; + option.output_format = AOT_FORMAT_FILE; + /* default value, enable or disable depends on the platform */ + option.bounds_checks = 2; + + /* Process options. */ + for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) { + if (!strcmp(argv[0], "-o")) { + argc--, argv++; + if (argc < 2) + return print_help(); + out_file_name = argv[0]; + } + else if (!strncmp(argv[0], "--target=", 9)) { + if (argv[0][9] == '\0') + return print_help(); + option.target_arch = argv[0] + 9; + } + else if (!strncmp(argv[0], "--target-abi=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + option.target_abi = argv[0] + 13; + } + else if (!strncmp(argv[0], "--cpu=", 6)) { + if (argv[0][6] == '\0') + return print_help(); + option.target_cpu = argv[0] + 6; + } + else if (!strncmp(argv[0], "--cpu-features=", 15)) { + if (argv[0][15] == '\0') + return print_help(); + option.cpu_features = argv[0] + 15; + } + else if (!strncmp(argv[0], "--opt-level=", 12)) { + if (argv[0][12] == '\0') + return print_help(); + option.opt_level = (uint32)atoi(argv[0] + 12); + if (option.opt_level > 3) + option.opt_level = 3; + } + else if (!strncmp(argv[0], "--size-level=", 13)) { + if (argv[0][13] == '\0') + return print_help(); + option.size_level = (uint32)atoi(argv[0] + 13); + if (option.size_level > 3) + option.size_level = 3; + } + else if (!strcmp(argv[0], "-sgx")) { + sgx_mode = true; + } + else if (!strncmp(argv[0], "--bounds-checks=", 16)) { + option.bounds_checks = (atoi(argv[0] + 16) == 1) ? 1 : 0; + } + else if (!strncmp(argv[0], "--format=", 9)) { + if (argv[0][9] == '\0') + return print_help(); + if (!strcmp(argv[0] + 9, "aot")) + option.output_format = AOT_FORMAT_FILE; + else if (!strcmp(argv[0] + 9, "object")) + option.output_format = AOT_OBJECT_FILE; + else if (!strcmp(argv[0] + 9, "llvmir-unopt")) + option.output_format = AOT_LLVMIR_UNOPT_FILE; + else if (!strcmp(argv[0] + 9, "llvmir-opt")) + option.output_format = AOT_LLVMIR_OPT_FILE; + else { + printf("Invalid format %s.\n", argv[0] + 9); + return print_help(); + } + } + else if (!strncmp(argv[0], "-v=", 3)) { + log_verbose_level = atoi(argv[0] + 3); + if (log_verbose_level < 0 || log_verbose_level > 5) + return print_help(); + } + else if (!strcmp(argv[0], "--enable-bulk-memory")) { + option.enable_bulk_memory = true; + } + else if (!strcmp(argv[0], "--enable-multi-thread")) { + option.enable_bulk_memory = true; + option.enable_thread_mgr = true; + } + else + return print_help(); + } + + if (argc == 0) + return print_help(); + + if (sgx_mode) { + option.size_level = 1; + option.is_sgx_platform = true; + } + + wasm_file_name = argv[0]; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + + bh_log_set_verbose_level(log_verbose_level); + + bh_print_time("Begin to load wasm file"); + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file = (uint8*) + bh_read_file_to_buffer(wasm_file_name, &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + if (!(comp_data = aot_create_comp_data(wasm_module))) { + printf("%s\n", aot_get_last_error()); + goto fail3; + } + + bh_print_time("Begin to create compile context"); + + if (!(comp_ctx = aot_create_comp_context(comp_data, + &option))) { + printf("%s\n", aot_get_last_error()); + goto fail4; + } + + bh_print_time("Begin to compile"); + + if (!aot_compile_wasm(comp_ctx)) { + printf("%s\n", aot_get_last_error()); + goto fail5; + } + + switch (option.output_format) { + case AOT_LLVMIR_UNOPT_FILE: + case AOT_LLVMIR_OPT_FILE: + if (!aot_emit_llvm_file(comp_ctx, out_file_name)) { + printf("%s\n", aot_get_last_error()); + goto fail5; + } + break; + case AOT_OBJECT_FILE: + if (!aot_emit_object_file(comp_ctx, out_file_name)) { + printf("%s\n", aot_get_last_error()); + goto fail5; + } + break; + case AOT_FORMAT_FILE: + if (!aot_emit_aot_file(comp_ctx, comp_data, out_file_name)) { + printf("%s\n", aot_get_last_error()); + goto fail5; + } + break; + default: + break; + } + + bh_print_time("Compile end"); + + printf("Compile success, file %s was generated.\n", out_file_name); + +fail5: + /* Destroy compiler context */ + aot_destroy_comp_context(comp_ctx); + +fail4: + /* Destroy compile data */ + aot_destroy_comp_data(comp_data); + +fail3: + /* Unload WASM module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file); + +fail1: + /* Destroy runtime environment */ + wasm_runtime_destroy(); + + bh_print_time("wamrc return"); + return 0; +} + diff --git a/wamr/wamr-sdk/Kconfig b/wamr/wamr-sdk/Kconfig new file mode 100644 index 0000000..96c23a8 --- /dev/null +++ b/wamr/wamr-sdk/Kconfig @@ -0,0 +1,84 @@ +mainmenu "WebAssembly Micro Runtime Configuration" + +choice + prompt "select a build target" + + config TARGET_X86_64 + bool "X86_64" + + config TARGET_X86_32 + bool "X86_32" + +endchoice + +choice + prompt "select a target platform" + + config PLATFORM_LINUX + bool "Linux" + +endchoice + +menu "select execution mode" + comment "At least one execution mode must be selected" + config EXEC_AOT + bool "AOT" + depends on PLATFORM_LINUX + + config EXEC_JIT + bool "JIT" + depends on PLATFORM_LINUX + select BUILD_LLVM + + config BUILD_LLVM + bool "build llvm (this may take a long time)" + depends on EXEC_JIT + help + llvm library is required by JIT mode. + + config EXEC_INTERP + bool "INTERPRETER" + default y +endmenu + +choice + prompt "libc support" + + config LIBC_BUILTIN + bool "builtin libc" + help + use builtin libc, this is a minimal subset of libc. + + config LIBC_WASI + bool "WebAssembly System Interface [WASI]" + depends on PLATFORM_LINUX + help + enable WebAssembly System Interface + +endchoice + +choice + prompt "application framework" + config APP_FRAMEWORK_DISABLE + bool "Disable app framework" + help + Disable wamr app framework + + config APP_FRAMEWORK_DEFAULT + bool "Default components" + help + Default components + + config APP_FRAMEWORK_ALL + bool "All components" + + config APP_FRAMEWORK_CUSTOM + bool "customized module config" + + menu "modules:" + depends on APP_FRAMEWORK_CUSTOM + + source ".wamr_modules" + + endmenu +endchoice diff --git a/wamr/wamr-sdk/Makefile b/wamr/wamr-sdk/Makefile new file mode 100644 index 0000000..a0824ae --- /dev/null +++ b/wamr/wamr-sdk/Makefile @@ -0,0 +1,10 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# # This will generate SDK for both runtime and wasm application +config: + ./build_sdk.sh -i + +menuconfig: + ./menuconfig.sh + diff --git a/wamr/wamr-sdk/README.md b/wamr/wamr-sdk/README.md new file mode 100644 index 0000000..f9d143c --- /dev/null +++ b/wamr/wamr-sdk/README.md @@ -0,0 +1,126 @@ +# 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): + +``` +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) +``` + + + +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] +``` + +The output 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 +``` + + + +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. + + + +### 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). + +> diff --git a/wamr/wamr-sdk/app/CMakeLists.txt b/wamr/wamr-sdk/app/CMakeLists.txt new file mode 100644 index 0000000..3424b7a --- /dev/null +++ b/wamr/wamr-sdk/app/CMakeLists.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(app-framework) + +SET (CMAKE_C_FLAGS "-O3") + +if (NOT DEFINED WAMR_BUILD_SDK_PROFILE) + set (WAMR_BUILD_SDK_PROFILE "default") +endif () + +if (NOT DEFINED CONFIG_PATH) + set (CONFIG_PATH ${CMAKE_CURRENT_LIST_DIR}/../wamr_config_default.cmake) + message(STATUS, "CONFIG_PATH set to ${CONFIG_PATH} ") +endif () + +if (NOT EXISTS "${CONFIG_PATH}") + message (FATAL_ERROR "${CONFIG_PATH} not exist") +endif () + +include(${CONFIG_PATH}) + + +set (OUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../out/${WAMR_BUILD_SDK_PROFILE}") +set (APP_SDK_DIR "${OUT_DIR}/app-sdk") + +if (DEFINED EXTRA_SDK_INCLUDE_PATH) + message(STATUS, "EXTRA_SDK_INCLUDE_PATH = ${EXTRA_SDK_INCLUDE_PATH} ") + include_directories ( + ${EXTRA_SDK_INCLUDE_PATH} + ) +endif () + +if (WAMR_BUILD_LIBC_BUILTIN EQUAL 1) + set (SYSROOT_DIR "${APP_SDK_DIR}/libc-builtin-sysroot") + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${SYSROOT_DIR}/include) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${SYSROOT_DIR}/share) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/libc-builtin-sysroot/share/defined-symbols.txt ${SYSROOT_DIR}/share) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/wamr_toolchain.cmake ${APP_SDK_DIR}) + execute_process( + COMMAND ${CMAKE_COMMAND} + -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/libc-builtin-sysroot/include ${SYSROOT_DIR}/include + ) +else() + if (WAMR_BUILD_LIBC_WASI EQUAL 1) + set (SYSROOT_DIR "${APP_SDK_DIR}/wasi-sysroot") + message("sysroot: ${SYSROOT_DIR}") + execute_process(COMMAND ln -s ${WASI_SDK_DIR}/share/wasi-sysroot ${SYSROOT_DIR}) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/wasi_toolchain.cmake ${APP_SDK_DIR}/wamr_toolchain.cmake) + endif () +endif() + +if (WAMR_BUILD_APP_FRAMEWORK EQUAL 1) + message(WAMR_BUILD_APP_FRAMEWORK) + set (APP_FRAMEWORK_INCLUDE_TYPE "APP") + set (WAMR_APP_OUT_DIR "${APP_SDK_DIR}/wamr-app-framework") + + include(${CMAKE_CURRENT_LIST_DIR}/../../core/app-framework/app_framework.cmake) + + add_library(app_framework + ${WASM_APP_SOURCE_ALL} + ) + + add_custom_command( + TARGET app_framework POST_BUILD + + COMMAND ${CMAKE_COMMAND} -E make_directory ${WAMR_APP_OUT_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E make_directory ${WAMR_APP_OUT_DIR}/include/wa-inc + COMMAND ${CMAKE_COMMAND} -E make_directory ${WAMR_APP_OUT_DIR}/share + COMMAND ${CMAKE_COMMAND} -E copy_directory ${WASM_APP_BI_INC_DIR} ${WAMR_APP_OUT_DIR}/include/bi-inc + COMMAND ${CMAKE_COMMAND} -E copy ${WASM_APP_BASE_DIR}/bh_platform.h ${WAMR_APP_OUT_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy ${WASM_APP_BASE_DIR}/wasm_app.h ${WAMR_APP_OUT_DIR}/include + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/*.a ${WAMR_APP_OUT_DIR}/lib + + # bi-inc folder should also copy into runtime-sdk + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUT_DIR}/runtime-sdk/include + COMMAND ${CMAKE_COMMAND} -E copy_directory ${WASM_APP_BI_INC_DIR} ${OUT_DIR}/runtime-sdk/include/bi-inc + ) + + # If app-framework is enabled, add the undefined-symbol list to the toolchain file + if (WAMR_BUILD_LIBC_WASI EQUAL 1) + file (APPEND + ${APP_SDK_DIR}/wamr_toolchain.cmake + "SET (CMAKE_EXE_LINKER_FLAGS \"\${CMAKE_EXE_LINKER_FLAGS},--allow-undefined-file=\${CMAKE_CURRENT_LIST_DIR}/wamr-app-framework/share/defined-symbols.txt\" CACHE INTERNAL \"\")" + ) + endif () + + FOREACH (dir IN LISTS WASM_APP_WA_INC_DIR_LIST) + file (COPY ${dir} DESTINATION ${WAMR_APP_OUT_DIR}/include/) + ENDFOREACH (dir) + + if (DEFINED EXTRA_SDK_INCLUDE_PATH) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${EXTRA_SDK_INCLUDE_PATH} ${WAMR_APP_OUT_DIR}/include) + endif () + +endif() diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/assert.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/assert.h new file mode 100644 index 0000000..534a90d --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/assert.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_ASSERT_H +#define _WAMR_LIBC_ASSERT_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/ctype.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/ctype.h new file mode 100644 index 0000000..1a3cd4e --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/ctype.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_CTYPE_H +#define _WAMR_LIBC_CTYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int isupper(int c); +int isalpha(int c); +int isspace(int c); +int isgraph(int c); +int isprint(int c); +int isdigit(int c); +int isxdigit(int c); +int tolower(int c); +int toupper(int c); +int isalnum(int c); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/errno.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/errno.h new file mode 100644 index 0000000..9e9cca1 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/errno.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_ERRNO_H +#define _WAMR_LIBC_ERRNO_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/fcntl.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/fcntl.h new file mode 100644 index 0000000..a67c24a --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/fcntl.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_FCNTL_H +#define _WAMR_LIBC_FCNTL_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/inttypes.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/inttypes.h new file mode 100644 index 0000000..a04ec7b --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/inttypes.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_INTTYPES_H +#define _WAMR_LIBC_INTTYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/limits.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/limits.h new file mode 100644 index 0000000..d46fb4f --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/limits.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_LIMITS_H +#define _WAMR_LIBC_LIMITS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHAR_BIT 8 +#define SCHAR_MIN -128 +#define SCHAR_MAX 127 +#define UCHAR_MAX 255 +#define CHAR_MIN 0 +#define CHAR_MAX 127 +#define MB_LEN_MAX 1 +#define SHRT_MIN -32768 +#define SHRT_MAX +32767 +#define USHRT_MAX 65535 +#define INT_MIN -32768 +#define INT_MAX +32767 +#define UINT_MAX 65535 +#define LONG_MIN -2147483648 +#define LONG_MAX +2147483647 +#define ULONG_MAX 4294967295 + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h new file mode 100644 index 0000000..38be34e --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/pthread.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIB_PTHREAD_H +#define _WAMR_LIB_PTHREAD_H + +/* Data type define of pthread, mutex, cond and key */ +typedef unsigned int pthread_t; +typedef unsigned int pthread_mutex_t; +typedef unsigned int pthread_cond_t; +typedef unsigned int pthread_key_t; + +/* Thread APIs */ +int pthread_create(pthread_t *thread, const void *attr, + void *(*start_routine) (void *), void *arg); + +int pthread_join(pthread_t thread, void **retval); + +int pthread_detach(pthread_t thread); + +int pthread_cancel(pthread_t thread); + +pthread_t pthread_self(void); + +void pthread_exit(void *retval); + +/* Mutex APIs */ +int pthread_mutex_init(pthread_mutex_t *mutex, const void *attr); + +int pthread_mutex_lock(pthread_mutex_t *mutex); + +int pthread_mutex_unlock(pthread_mutex_t *mutex); + +int pthread_mutex_destroy(pthread_mutex_t *mutex); + +/* Cond APIs */ +int pthread_cond_init(pthread_cond_t *cond, const void *attr); + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, + unsigned int useconds); + +int pthread_cond_signal(pthread_cond_t *cond); + +int pthread_cond_destroy(pthread_cond_t *cond); + +/* Pthread key APIs */ +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); + +int pthread_setspecific(pthread_key_t key, const void *value); + +void *pthread_getspecific(pthread_key_t key); + +int pthread_key_delete(pthread_key_t key); + +#endif /* end of _WAMR_LIB_PTHREAD_H */ \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdbool.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdbool.h new file mode 100644 index 0000000..29e03f9 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdbool.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STDBOOL_H +#define _WAMR_LIBC_STDBOOL_H + +#define __bool_true_false_are_defined 1 + +#ifndef __cplusplus + +#define bool _Bool +#define false 0 +#define true 1 + +#endif /* __cplusplus */ + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h new file mode 100644 index 0000000..bff920b --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdint.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STDINT_H +#define _WAMR_LIBC_STDINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long int int64_t; + +/* Unsigned. */ +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; + +typedef __INTPTR_TYPE__ intptr_t; +typedef __UINTPTR_TYPE__ uintptr_t; + +/* Minimum of signed integral types. */ +# define INT8_MIN (-128) +# define INT16_MIN (-32767-1) +# define INT32_MIN (-2147483647-1) +# define INT64_MIN (-__INT64_C(9223372036854775807)-1) +/* Maximum of signed integral types. */ +# define INT8_MAX (127) +# define INT16_MAX (32767) +# define INT32_MAX (2147483647) +# define INT64_MAX (__INT64_C(9223372036854775807)) + +/* Maximum of unsigned integral types. */ +# define UINT8_MAX (255) +# define UINT16_MAX (65535) +# define UINT32_MAX (4294967295U) +# define UINT64_MAX (__UINT64_C(18446744073709551615)) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdio.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdio.h new file mode 100644 index 0000000..1d51e6d --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdio.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STDIO_H +#define _WAMR_LIBC_STDIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef NULL +# define NULL ((void*) 0) +#endif + +typedef unsigned long size_t; + +int printf(const char *format, ...); +int putchar(int c); +int snprintf(char *str, size_t size, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int puts(char *string); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdlib.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdlib.h new file mode 100644 index 0000000..7eb2cc4 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/stdlib.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STDLIB_H +#define _WAMR_LIBC_STDLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long size_t; + +int atoi(const char *s); +void exit(int status); +long strtol(const char *nptr, char **endptr, register int base); +unsigned long strtoul(const char *nptr, char **endptr, register int base); +void *malloc(size_t size); +void *calloc(size_t n, size_t size); +void free(void *ptr); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/string.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/string.h new file mode 100644 index 0000000..e71a168 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/string.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STRING_H +#define _WAMR_LIBC_STRING_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long size_t; + +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +void *memchr(const void *s, int c, size_t n); +int strncasecmp(const char *s1, const char *s2, size_t n); +size_t strspn(const char *s, const char *accept); +size_t strcspn(const char *s, const char *reject); +char *strstr(const char *s, const char *find); +char *strchr(const char *s, int c); +int strcmp(const char *s1, const char *s2); +char *strcpy(char *dest, const char *src); +size_t strlen(const char *s); +int strncmp(const char * str1, const char * str2, size_t n); +char *strncpy(char *dest, const char *src, unsigned long n); +char * strdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/include/strings.h b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/strings.h new file mode 100644 index 0000000..7f1bec6 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/include/strings.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _WAMR_LIBC_STRINGS_H +#define _WAMR_LIBC_STRINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/wamr/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt b/wamr/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt new file mode 100644 index 0000000..08a3784 --- /dev/null +++ b/wamr/wamr-sdk/app/libc-builtin-sysroot/share/defined-symbols.txt @@ -0,0 +1,83 @@ +wasm_open_connection +wasm_close_connection +wasm_send_on_connection +wasm_config_connection +wasm_sensor_open +wasm_sensor_config +wasm_sensor_config_with_attr_container +wasm_sensor_close +wasm_btn_native_call +wasm_obj_native_call +wasm_label_native_call +wasm_cont_native_call +wasm_page_native_call +wasm_list_native_call +wasm_ddlist_native_call +wasm_cb_native_call +wasm_register_resource +wasm_response_send +wasm_post_request +wasm_sub_event +wasm_create_timer +wasm_timer_destroy +wasm_timer_cancel +wasm_timer_restart +wasm_get_sys_tick_ms +printf +sprintf +snprintf +puts +putchar +memcmp +memcpy +memmove +memset +strchr +strcmp +strcpy +strlen +strncmp +strncpy +malloc +calloc +strdup +free +atoi +bsearch +exit +strtol +strtoul +memchr +strncasecmp +strspn +strcspn +strstr +isupper +isalpha +isspace +isgraph +isprint +isdigit +isxdigit +tolower +toupper +isalnum +pthread_create +pthread_join +pthread_detach +pthread_cancel +pthread_self +pthread_exit +pthread_mutex_init +pthread_mutex_lock +pthread_mutex_unlock +pthread_mutex_destroy +pthread_cond_init +pthread_cond_wait +pthread_cond_timedwait +pthread_cond_signal +pthread_cond_destroy +pthread_key_create +pthread_setspecific +pthread_getspecific +pthread_key_delete diff --git a/wamr/wamr-sdk/app/wamr_toolchain.cmake b/wamr/wamr-sdk/app/wamr_toolchain.cmake new file mode 100644 index 0000000..c2e0cdf --- /dev/null +++ b/wamr/wamr-sdk/app/wamr_toolchain.cmake @@ -0,0 +1,29 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_PROCESSOR wasm32) +SET (CMAKE_SYSROOT ${CMAKE_CURRENT_LIST_DIR}/libc-builtin-sysroot) + +if (NOT DEFINED WASI_SDK_DIR) + SET (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +SET (CMAKE_C_FLAGS "-nostdlib -z stack-size=4096" CACHE INTERNAL "") +SET (CMAKE_C_COMPILER_TARGET "wasm32") +SET (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") + +SET (CMAKE_CXX_FLAGS "-nostdlib -z stack-size=4096" CACHE INTERNAL "") +SET (CMAKE_CXX_COMPILER_TARGET "wasm32") +SET (CMAKE_CXX_COMPILER "${WASI_SDK_DIR}/bin/clang++") + +SET (CMAKE_EXE_LINKER_FLAGS + "-Wl,--initial-memory=65536,--no-entry,--no-threads,--strip-all" CACHE INTERNAL "") + +SET (CMAKE_LINKER "${WASI_SDK_DIR}/bin/wasm-ld" CACHE INTERNAL "") +SET (CMAKE_AR "${WASI_SDK_DIR}/bin/llvm-ar" CACHE INTERNAL "") +SET (CMAKE_NM "${WASI_SDK_DIR}/bin/llvm-nm" CACHE INTERNAL "") +SET (CMAKE_OBJDUMP "${WASI_SDK_DIR}/bin/llvm-dwarfdump" CACHE INTERNAL "") +SET (CMAKE_RANLIB "${WASI_SDK_DIR}/bin/llvm-ranlib" CACHE INTERNAL "") +SET (CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS},--allow-undefined-file=${CMAKE_SYSROOT}/share/defined-symbols.txt" CACHE INTERNAL "") diff --git a/wamr/wamr-sdk/app/wasi_toolchain.cmake b/wamr/wamr-sdk/app/wasi_toolchain.cmake new file mode 100644 index 0000000..f80c737 --- /dev/null +++ b/wamr/wamr-sdk/app/wasi_toolchain.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +SET(CMAKE_SYSTEM_NAME Linux) + +if (NOT DEFINED WASI_SDK_DIR) + SET (WASI_SDK_DIR "/opt/wasi-sdk") +endif () + +SET (CMAKE_C_COMPILER "${WASI_SDK_DIR}/bin/clang") +SET (CMAKE_CXX_COMPILER "${WASI_SDK_DIR}/bin/clang++") + +SET (CMAKE_LINKER "${WASI_SDK_DIR}/bin/wasm-ld" CACHE INTERNAL "") +SET (CMAKE_AR "${WASI_SDK_DIR}/bin/llvm-ar" CACHE INTERNAL "") +SET (CMAKE_NM "${WASI_SDK_DIR}/bin/llvm-nm" CACHE INTERNAL "") +SET (CMAKE_OBJDUMP "${WASI_SDK_DIR}/bin/llvm-dwarfdump" CACHE INTERNAL "") +SET (CMAKE_RANLIB "${WASI_SDK_DIR}/bin/llvm-ranlib" CACHE INTERNAL "") diff --git a/wamr/wamr-sdk/build_sdk.sh b/wamr/wamr-sdk/build_sdk.sh new file mode 100755 index 0000000..0b819c5 --- /dev/null +++ b/wamr/wamr-sdk/build_sdk.sh @@ -0,0 +1,246 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +sdk_root=$(cd "$(dirname "$0")/" && pwd) +wamr_root_dir=${sdk_root}/.. +out_dir=${sdk_root}/out +profile_path=${out_dir}/profile.cmake +wamr_config_cmake_file="" +# libc support, default builtin-libc +LIBC_SUPPORT="BUILTIN" +CM_DEXTRA_SDK_INCLUDE_PATH="" +CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Release" +CM_TOOLCHAIN="" + +# menuconfig will pass options to this script +MENUCONFIG="" + +usage () +{ + echo "build.sh [options]" + echo " -n [profile name]" + echo " -x [config file path name]" + echo " -t [cmake toolchain file]" + echo " -e [extra include path], files under this path will be copied into SDK package" + echo " -c, clean" + echo " -d, debug mode" + echo " -i, enter menu config settings" + exit 1 +} + + +while getopts "e:x:n:t:icd" opt +do + case $opt in + n) + PROFILE=$OPTARG + ;; + t) + CM_TOOLCHAIN="-DCMAKE_TOOLCHAIN_FILE=$OPTARG" + ;; + x) + wamr_config_cmake_file=$OPTARG + ;; + e) + CM_DEXTRA_SDK_INCLUDE_PATH="-DEXTRA_SDK_INCLUDE_PATH=${OPTARG}" + ;; + c) + CLEAN="TRUE" + ;; + d) + CM_BUILD_TYPE="-DCMAKE_BUILD_TYPE=Debug" + ;; + i) + MENUCONFIG="TRUE" + ;; + ?) + echo "Unknown arg: $arg" + usage + exit 1 + ;; + esac +done + + +if [ ! -f "/opt/wasi-sdk/bin/clang" ]; then + echo "Can't find wasi-sdk under /opt/wasi-sdk" + echo "You can download wasi-sdk from here:" + echo "" + echo "https://github.com/CraneStation/wasi-sdk/releases/tag/wasi-sdk-7" + echo "" + echo "please install it to the default path for your convenience" + echo "" + exit 1 +fi + + +echo "download dependent external repositories.." +${wamr_root_dir}/core/deps/download.sh +[ $? -eq 0 ] || exit $? + + + +if [ -z "$PROFILE" ]; then + PROFILE="default" + echo "PROFILE argument not set, using DEFAULT" + if [[ -z "$wamr_config_cmake_file" ]]; then + wamr_config_cmake_file=${sdk_root}/wamr_config_default.cmake + echo "use default config file: [$wamr_config_cmake_file]" + fi +fi + + +if [ ! -d "${out_dir}" ]; then + mkdir -p ${out_dir} +fi + +curr_profile_dir=${out_dir}/${PROFILE} +wamr_app_out_dir=${curr_profile_dir}/app-sdk/wamr-app-framework +sysroot_dir=${curr_profile_dir}/app-sdk/libc-builtin-sysroot + + +echo "CM_DEXTRA_SDK_INCLUDE_PATH=${CM_DEXTRA_SDK_INCLUDE_PATH}" + + +if [[ "$CLEAN" = "TRUE" ]]; then + rm -rf ${curr_profile_dir} +fi + + + +# cmake config file for wamr runtime: +# 1. use the users provided the config cmake file path. +# 2. if user set MENU CONFIG, enter menu config to generate +# menu_config.cmake in the profile output folder +# 3. If the menu_config.cmake is already in the profile folder, use it +# 4. Use the default config cmake file +# +if [[ -n "$wamr_config_cmake_file" ]]; then + if [[ ! -f $wamr_config_cmake_file ]]; then + echo "user given file not exist: ${wamr_config_cmake_file}" + exit 1 + fi + + echo "User config file: [${wamr_config_cmake_file}]" + +else + wamr_config_cmake_file=${out_dir}/wamr_config_${PROFILE}.cmake + # always rebuilt the sdk if user is not giving the config file + if [ -d ${curr_profile_dir} ]; then + rm -rf ${curr_profile_dir} + fi + + if [[ "$MENUCONFIG" = "TRUE" ]] || [[ ! -f $wamr_config_cmake_file ]]; then + echo "MENUCONFIG: [${wamr_config_cmake_file}]" + ./menuconfig.sh -x ${wamr_config_cmake_file} + [ $? -eq 0 ] || exit $? + else + echo "use existing config file: [$wamr_config_cmake_file]" + fi +fi + + +mkdir -p ${curr_profile_dir} +mkdir -p ${curr_profile_dir}/app-sdk +mkdir -p ${curr_profile_dir}/runtime-sdk + + +if [ "${BUILD_LLVM}" = "TRUE" ]; then + if [ ! -d "${wamr_root_dir}/core/deps/llvm" ]; then + echo -e "\n" + echo "###### build llvm (this will take a long time) #######" + echo "" + cd ${wamr_root_dir}/wamr-compiler + ./build_llvm.sh + fi +fi + +echo -e "\n\n" +echo "############## Start to build wasm app sdk ###############" + +# If wgl module is selected, check if the extra SDK include dir is passed by the args, prompt user to input if not. +app_all_selected=`cat ${wamr_config_cmake_file} | grep WAMR_APP_BUILD_ALL` +app_wgl_selected=`cat ${wamr_config_cmake_file} | grep WAMR_APP_BUILD_WGL` + +if [[ -n "${app_wgl_selected}" ]] || [[ -n "${app_all_selected}" ]]; then + if [ -z "${CM_DEXTRA_SDK_INCLUDE_PATH}" ]; then + echo -e "\033[31mWGL module require lvgl config files, please input the path to the lvgl SDK include path:\033[0m" + read -a extra_file_path + + if [[ -z "${extra_file_path}" ]] || [[ ! -d "${extra_file_path}" ]]; then + echo -e "\033[31mThe extra SDK path is empty\033[0m" + else + CM_DEXTRA_SDK_INCLUDE_PATH="-DEXTRA_SDK_INCLUDE_PATH=${extra_file_path}" + fi + fi + + cd ${wamr_root_dir}/core/app-framework/wgl/app + ./prepare_headers.sh +fi + +cd ${sdk_root}/app +rm -fr build && mkdir build +cd build + +out=`grep WAMR_BUILD_LIBC_WASI ${wamr_config_cmake_file} |grep 1` +if [ -n "$out" ]; then + LIBC_SUPPORT="WASI" +fi +if [ "${LIBC_SUPPORT}" = "WASI" ]; then + echo "using wasi toolchain" + cmake .. $CM_DEXTRA_SDK_INCLUDE_PATH -DWAMR_BUILD_SDK_PROFILE=${PROFILE} -DCONFIG_PATH=${wamr_config_cmake_file} -DCMAKE_TOOLCHAIN_FILE=../wasi_toolchain.cmake +else + echo "using builtin libc toolchain" + cmake .. $CM_DEXTRA_SDK_INCLUDE_PATH \ + -DWAMR_BUILD_SDK_PROFILE=${PROFILE} \ + -DCONFIG_PATH=${wamr_config_cmake_file} \ + -DCMAKE_TOOLCHAIN_FILE=../wamr_toolchain.cmake +fi +[ $? -eq 0 ] || exit $? + +make +if (( $? == 0 )); then + echo -e "\033[32mSuccessfully built app-sdk under ${curr_profile_dir}/app-sdk\033[0m" +else + echo -e "\033[31mFailed to build app-sdk for wasm application\033[0m" + exit 1 +fi + +cd .. +rm -fr build +echo -e "\n\n" + + + +echo "############## Start to build runtime sdk ###############" +cd ${sdk_root}/runtime +rm -fr build_runtime_sdk && mkdir build_runtime_sdk +cd build_runtime_sdk +cmake .. $CM_DEXTRA_SDK_INCLUDE_PATH \ + -DWAMR_BUILD_SDK_PROFILE=${PROFILE} \ + -DCONFIG_PATH=${wamr_config_cmake_file} \ + $CM_TOOLCHAIN $CM_BUILD_TYPE +[ $? -eq 0 ] || exit $? +make + +if (( $? == 0 )); then + echo -e "\033[32mSuccessfully built runtime library under ${curr_profile_dir}/runtime-sdk/lib\033[0m" +else + echo -e "\033[31mFailed to build runtime sdk\033[0m" + exit 1 +fi + +APP=`grep WAMR_BUILD_APP_FRAMEWORK ${wamr_config_cmake_file} |grep 1` +if [ -n "$APP" ]; then + # Generate defined-symbol list for app-sdk + cd ${wamr_app_out_dir}/share + cat ${curr_profile_dir}/runtime-sdk/include/*.inl | egrep "^ *EXPORT_WASM_API *[(] *[a-zA-Z_][a-zA-Z0-9_]* *?[)]" | cut -d '(' -f2 | cut -d ')' -f1 > defined-symbols.txt +fi + + +cd .. +rm -fr build_runtime_sdk + +exit 0 diff --git a/wamr/wamr-sdk/menuconfig.sh b/wamr/wamr-sdk/menuconfig.sh new file mode 100755 index 0000000..b2f6fa6 --- /dev/null +++ b/wamr/wamr-sdk/menuconfig.sh @@ -0,0 +1,223 @@ +#!/bin/bash + +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + +usage () +{ + echo "menuconfig.sh [options]" + echo " -x [config file path name]" + exit 1 +} + + +while getopts "x:" opt +do + case $opt in + x) + wamr_config_cmake_file=$OPTARG + ;; + ?) + echo "Unknown arg: $arg" + usage + exit 1 + ;; + esac +done + + +if [ -z $wamr_config_cmake_file ]; then + usage + exit +fi + + +function set_build_target () { + target=$1 + + if [[ "${target}" = "X86_64" ]]; then + echo -e "set (WAMR_BUILD_TARGET \"X86_64\")" >> ${wamr_config_cmake_file} + elif [[ "${target}" = "X86_32" ]]; then + echo -e "set (WAMR_BUILD_TARGET \"X86_32\")" >> ${wamr_config_cmake_file} + else + echo "unknown build target." + exit 1 + fi +} + +function set_build_platform () { + platform=$1 + + if [[ "${platform}" = "linux" ]]; then + echo -e "set (WAMR_BUILD_PLATFORM \"linux\")" >> ${wamr_config_cmake_file} + # TODO: add other platforms + else + echo "${platform} platform currently not supported" + exit 1 + fi +} + +# input: array of selected exec modes [aot jit interp] +function set_exec_mode () { + modes=($1) + + for mode in ${modes[@]} + do + if [[ "$mode" = "aot" ]]; then + echo "set (WAMR_BUILD_AOT 1)" >> ${wamr_config_cmake_file} + elif [[ "$mode" = "jit" ]]; then + echo "set (WAMR_BUILD_JIT 1)" >> ${wamr_config_cmake_file} + BUILD_LLVM="TRUE" + elif [[ "$mode" = "interp" ]]; then + echo "set (WAMR_BUILD_INTERP 1)" >> ${wamr_config_cmake_file} + else + echo "unknown execute mode." + exit 1 + fi + done +} + +function set_libc_support () { + libc=$1 + + if [ "$libc" = "WASI" ]; then + echo "set (WAMR_BUILD_LIBC_WASI 1)" >> ${wamr_config_cmake_file} + else + echo "set (WAMR_BUILD_LIBC_BUILTIN 1)" >> ${wamr_config_cmake_file} + fi +} + +function set_app_framework () { + app_support=$1 + + if [ "$app_support" = "TRUE" ]; then + echo "set (WAMR_BUILD_APP_FRAMEWORK 1)" >> ${wamr_config_cmake_file} + fi +} + +# input: array of selected app modules +function set_app_module () { + modules=($1) + + for module in ${modules[*]} + do + if [ "${module}" = "all" ]; then + cmake_app_list="WAMR_APP_BUILD_ALL" + break + fi + + cmake_app_list="${cmake_app_list} WAMR_APP_BUILD_${module^^}" + done + + # APP module list + if [ -n "${cmake_app_list}" ]; then + echo "set (WAMR_BUILD_APP_LIST ${cmake_app_list# })" >> ${wamr_config_cmake_file} + fi +} + + + + +sdk_root=$(cd "$(dirname "$0")/" && pwd) +wamr_root=${sdk_root}/.. + +if [ ! `command -v menuconfig` ]; then + echo "Can't find kconfiglib python lib on this computer" + echo "Downloading it through pip" + echo "If this fails, you can try `pip install kconfiglib` to install it manually" + echo "Or download the repo from https://github.com/ulfalizer/Kconfiglib" + + pip install kconfiglib +fi + +if [ -f ".wamr_modules" ]; then + rm -f .wamr_modules +fi + +# get all modules under core/app-framework +for module in `ls ${wamr_root}/core/app-framework -F | grep "/$" | grep -v "base" | grep -v "app-native-shared" | grep -v "template"` +do + module=${module%*/} + echo "config APP_BUILD_${module^^}" >> .wamr_modules + echo " bool \"enable ${module}\"" >> .wamr_modules +done + +menuconfig Kconfig +[ $? -eq 0 ] || exit $? + +if [ ! -e ".config" ]; then + exit 0 +fi + +# parse platform +platform=`cat .config | grep "^CONFIG_PLATFORM"` +platform=${platform%*=y} +platform=${platform,,} +platform=${platform#config_platform_} + +# parse target +target=`cat .config | grep "^CONFIG_TARGET"` +target=${target%*=y} +target=${target#CONFIG_TARGET_} + +# parse execution mode +modes=`cat .config | grep "^CONFIG_EXEC"` +mode_list="" +for mode in ${modes} +do + mode=${mode%*=y} + mode=${mode#CONFIG_EXEC_} + mode_list="${mode_list} ${mode,,}" +done +if [ -z "${mode_list}" ]; then + echo "execution mode are not selected" + exit 1 +fi + +# parse libc support +libc=`cat .config | grep "^CONFIG_LIBC"` +libc=${libc%*=y} +if [ "${libc}" = "CONFIG_LIBC_WASI" ]; then + libc_support="WASI" +else + libc_support="BUILTIN" +fi + +# parse application framework options +app_option=`cat .config | grep "^CONFIG_APP_FRAMEWORK"` +app_option=${app_option%*=y} +app_option=${app_option#CONFIG_APP_FRAMEWORK_} + +if [ "${app_option}" != "DISABLE" ]; then + app_enable="TRUE" + + # Default components + if [ "${app_option}" = "DEFAULT" ]; then + app_list="base connection sensor" + # All components + elif [ "${app_option}" = "ALL" ]; then + app_list="all" + # Customize + elif [ "${app_option}" = "CUSTOM" ]; then + app_option=`cat .config | grep "^CONFIG_APP_BUILD"` + app_list="base" + for app in ${app_option} + do + app=${app%*=y} + app=${app#CONFIG_APP_BUILD_} + app_list="${app_list} ${app,,}" + done + fi +fi + +if [[ -f $wamr_config_cmake_file ]]; then + rm $wamr_config_cmake_file +fi + +set_build_target ${target} +set_build_platform ${platform} +set_exec_mode "${mode_list[*]}" +set_libc_support ${libc_support} +set_app_module "${app_list[*]}" +set_app_framework ${app_enable} diff --git a/wamr/wamr-sdk/runtime/CMakeLists.txt b/wamr/wamr-sdk/runtime/CMakeLists.txt new file mode 100644 index 0000000..c3bd863 --- /dev/null +++ b/wamr/wamr-sdk/runtime/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required(VERSION 2.8) +project(runtime-sdk) + +SET (CMAKE_C_FLAGS "-O3") +set (CMAKE_BUILD_TYPE Release) + +add_definitions(-DBH_MALLOC=wasm_runtime_malloc) +add_definitions(-DBH_FREE=wasm_runtime_free) + +if (NOT DEFINED WAMR_BUILD_SDK_PROFILE) + set (WAMR_BUILD_SDK_PROFILE "default") +endif () + +if (NOT DEFINED CONFIG_PATH) + set (CONFIG_PATH ${CMAKE_CURRENT_LIST_DIR}/../wamr_config_default.cmake) +endif () + +if (NOT EXISTS "${CONFIG_PATH}") + message (FATAL_ERROR "${CONFIG_PATH} not exist") +endif () + +include(${CONFIG_PATH}) + +set (OUT_DIR "${CMAKE_CURRENT_LIST_DIR}/../out/${WAMR_BUILD_SDK_PROFILE}") +set (RUNTIME_SDK_DIR "${OUT_DIR}/runtime-sdk") + +include(${CMAKE_CURRENT_LIST_DIR}/../../build-scripts/runtime_lib.cmake) + +# build vmlib +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# copy vmlib.a to ${SDK_ROOT}/out/runtime-sdk/lib +add_custom_command( + TARGET vmlib POST_BUILD + + COMMAND ${CMAKE_COMMAND} -E make_directory ${RUNTIME_SDK_DIR}/lib + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/*.a ${RUNTIME_SDK_DIR}/lib +) + +# copy headers to ${SDK_ROOT}/out/runtime-sdk/include +FOREACH (header IN LISTS RUNTIME_LIB_HEADER_LIST) + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${RUNTIME_SDK_DIR}/include) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${header}" ${RUNTIME_SDK_DIR}/include) +ENDFOREACH (header) + + +if (DEFINED EXTRA_SDK_INCLUDE_PATH) + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${EXTRA_SDK_INCLUDE_PATH} ${RUNTIME_SDK_DIR}/include) +endif () + +# config.h is not needed when building a runtime product with pre-built library +# erase the file to avoid compile error +file (WRITE ${RUNTIME_SDK_DIR}/include/config.h "") diff --git a/wamr/wamr-sdk/wamr_config_default.cmake b/wamr/wamr-sdk/wamr_config_default.cmake new file mode 100644 index 0000000..98cc6e9 --- /dev/null +++ b/wamr/wamr-sdk/wamr_config_default.cmake @@ -0,0 +1,12 @@ +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) + +# +# set (EXTRA_SDK_INCLUDE_PATH "")

    +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