From 70ed844838173b97a966ed2bb033c290dc9014b0 Mon Sep 17 00:00:00 2001 From: Cloud_Yun Date: Fri, 2 Oct 2026 09:10:13 +0900 Subject: [PATCH] compat.wamr: no-hw-bound-check, instruction-metering and thread-mgr features Three runtime build options as features, with a test member that stops an endless guest loop by instruction budget and by wasm_runtime_terminate() from another thread. thread-mgr also needs its directory in the base include_dirs, and on openkal-musl a target_cfg define, because openkal has no sigaction for the wakeup handler. --- mcpp.toml | 1 + pkgs/c/compat.wamr.lua | 26 ++++ .../examples/wamr-runtime-features/mcpp.toml | 9 ++ .../tests/runtime_features.cpp | 116 ++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 tests/examples/wamr-runtime-features/mcpp.toml create mode 100644 tests/examples/wamr-runtime-features/tests/runtime_features.cpp diff --git a/mcpp.toml b/mcpp.toml index ad27a9d9..9c63f694 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -182,6 +182,7 @@ members = [ "tests/examples/vulkan-memory-allocator", "tests/examples/wamr", "tests/examples/wamr-features", + "tests/examples/wamr-runtime-features", "tests/examples/quickjs-ng", ] diff --git a/pkgs/c/compat.wamr.lua b/pkgs/c/compat.wamr.lua index c75e0b3e..7e335abb 100644 --- a/pkgs/c/compat.wamr.lua +++ b/pkgs/c/compat.wamr.lua @@ -81,6 +81,10 @@ -- compat.recastnavigation's DT_POLYREF64 out of the feature table does not -- apply here. -- +-- `no-hw-bound-check`, `instruction-metering` and `thread-mgr` select runtime +-- build options rather than a guest library. They are defines of the same kind: +-- neither wasm_export.h nor wasm_c_api.h branches on any of them. +-- -- ───────────────────────────────────────────────────────────────────────── -- CN MIRROR -- @@ -140,6 +144,8 @@ package = { "*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/include", "*/core/iwasm/libraries/libc-wasi/sandboxed-system-primitives/src", "*/core/shared/platform/common/libc-util", + -- wasm_c_api.c includes thread_manager.h by bare name under thread-mgr + "*/core/iwasm/libraries/thread-mgr", "mcpp_generated/include", }, @@ -165,6 +171,14 @@ package = { ldflags = { "-lpthread", "-lm" }, }, + target_cfg = { + -- openkal has no sigaction: with thread-mgr, wasm_runtime_init cannot + -- install its wakeup handler and returns false + ["cfg(all(kernel-abi = \"openkal\", c-abi = \"musl\"))"] = { + cflags = { "-DWASM_DISABLE_WAKEUP_BLOCKING_OP=1" }, + }, + }, + generated_files = { ["mcpp_generated/include/mcpp_wamr_config.h"] = [==[ @@ -239,6 +253,18 @@ package = { targets = { ["wamr"] = { kind = "lib" } }, features = { + -- software bound checks only: no guard pages, sigaltstack or SIGSEGV handler + ["no-hw-bound-check"] = { + defines = { "WASM_DISABLE_HW_BOUND_CHECK=1" }, + }, + ["instruction-metering"] = { + defines = { "WASM_ENABLE_INSTRUCTION_METERING=1" }, + }, + -- wasm_runtime_terminate() from another thread stops a running guest + ["thread-mgr"] = { + sources = { "*/core/iwasm/libraries/thread-mgr/*.c" }, + defines = { "WASM_ENABLE_THREAD_MGR=1" }, + }, -- Built-in libc wrappers exported to the guest under `env`. ["libc-builtin"] = { sources = { "*/core/iwasm/libraries/libc-builtin/*.c" }, diff --git a/tests/examples/wamr-runtime-features/mcpp.toml b/tests/examples/wamr-runtime-features/mcpp.toml new file mode 100644 index 00000000..5ffa37a8 --- /dev/null +++ b/tests/examples/wamr-runtime-features/mcpp.toml @@ -0,0 +1,9 @@ +# WAMR execution-control features: software bound checks, instruction +# metering and the thread manager. The base member (tests/examples/wamr) does +# not request them. +[package] +name = "wamr-runtime-features-tests" +version = "0.1.0" + +[target.'cfg(linux)'.dependencies.compat] +wamr = { version = "2.4.5", features = ["no-hw-bound-check", "instruction-metering", "thread-mgr"] } diff --git a/tests/examples/wamr-runtime-features/tests/runtime_features.cpp b/tests/examples/wamr-runtime-features/tests/runtime_features.cpp new file mode 100644 index 00000000..a7d16dc9 --- /dev/null +++ b/tests/examples/wamr-runtime-features/tests/runtime_features.cpp @@ -0,0 +1,116 @@ +// compat.wamr with `no-hw-bound-check`, `instruction-metering` and +// `thread-mgr`: an infinite loop in the guest is stopped once by the +// instruction budget and once by wasm_runtime_terminate() from another thread. +#ifdef __linux__ + +#include + +#include +#include + +#include +#include +#include +#include + +namespace { + +// (module +// (func (export "spin") (loop (br 0))) +// (func (export "add") (param i32 i32) (result i32) +// local.get 0 local.get 1 i32.add)) +const unsigned char kModule[] = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x0a, 0x02, 0x60, 0x00, 0x00, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, + 0x03, 0x03, 0x02, 0x00, 0x01, + 0x07, 0x0e, 0x02, 0x04, 's', 'p', 'i', 'n', 0x00, 0x00, + 0x03, 'a', 'd', 'd', 0x00, 0x01, + 0x0a, 0x11, 0x02, 0x07, 0x00, 0x03, 0x40, 0x0c, 0x00, 0x0b, 0x0b, + 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b, +}; + +wasm_module_inst_t g_inst; +wasm_exec_env_t g_env; +std::atomic g_returned{ false }; + +bool call(const char *name, uint32_t *argv, uint32_t argc) +{ + return wasm_runtime_call_wasm(g_env, wasm_runtime_lookup_function(g_inst, name), argc, argv); +} + +bool exception_has(const char *text) +{ + const char *ex = wasm_runtime_get_exception(g_inst); + std::printf(" exception: %s\n", ex ? ex : "(none)"); + return ex && std::strstr(ex, text); +} + +bool usable_again() +{ + wasm_runtime_clear_exception(g_inst); + uint32_t argv[2] = { 2, 3 }; + return call("add", argv, 2) && argv[0] == 5; +} + +// Repeats until the call has returned, so a terminate that lands before the +// guest started running is not lost. +void *terminate_until_returned(void *) +{ + while (!g_returned) { + usleep(100 * 1000); + wasm_runtime_terminate(g_inst); + } + return nullptr; +} + +} // namespace + +int main() +{ + alarm(60); + + RuntimeInitArgs init; + std::memset(&init, 0, sizeof init); + init.mem_alloc_type = Alloc_With_System_Allocator; + if (!wasm_runtime_full_init(&init)) { + std::puts("wasm_runtime_full_init failed"); + return 1; + } + + char err[128] = { 0 }; + unsigned char *image = static_cast(std::malloc(sizeof kModule)); + std::memcpy(image, kModule, sizeof kModule); + wasm_module_t mod = wasm_runtime_load(image, sizeof kModule, err, sizeof err); + g_inst = mod ? wasm_runtime_instantiate(mod, 16 * 1024, 0, err, sizeof err) : nullptr; + g_env = g_inst ? wasm_runtime_create_exec_env(g_inst, 16 * 1024) : nullptr; + if (!g_env) { + std::printf("module setup failed: %s\n", err); + return 1; + } + + uint32_t argv[2] = { 0, 0 }; + bool ok = true; + + wasm_runtime_set_instruction_count_limit(g_env, 1000000); + bool stopped = !call("spin", argv, 0) && exception_has("instruction limit"); + std::printf("budget stops the loop %s\n", stopped ? "ok" : "FAIL"); + ok = ok && stopped && usable_again(); + + wasm_runtime_set_instruction_count_limit(g_env, -1); + pthread_t th; + pthread_create(&th, nullptr, terminate_until_returned, nullptr); + bool terminated = !call("spin", argv, 0) && exception_has("terminated by user"); + g_returned = true; + pthread_join(th, nullptr); + std::printf("terminate stops the loop %s\n", terminated ? "ok" : "FAIL"); + ok = ok && terminated && usable_again(); + + std::printf("instance usable afterwards %s\n", ok ? "ok" : "FAIL"); + return ok ? 0 : 1; +} + +#else + +int main() { return 0; } + +#endif