Wasm3 的 WAPM 打包与自托管运行:在 WebAssembly 上运行 WebAssembly 解释器(wasm3 / wasm3-strace 实战指南)
解释器嵌入式语言运行时【免费下载链接】wasm3 A fast WebAssembly interpreter and the most universal WASM runtime项目地址https://gitcode.com/gh_mirrors/wa/wasm3点击查看免费下载本篇技术指南围绕仓库 extra/wapm-package/ 下的 WAPMWebAssembly Package Manager包展开讲解如何把 wasm3 解释器自身编译成 WASI 格式的 WebAssembly 模块并打包分发使其可以在 WebAssembly.sh 等宿主环境中运行任意 WASM 文件甚至实现“wasm3 内嵌 wasm3”的自托管运行。同时文章将深入剖析随包发布的wasm3-strace工具它能在不要求目标 WASM 做任何特殊配合的前提下输出结构化的函数调用跟踪与 trap 时的 wasm 回溯backtrace。读完本文你将掌握--repl交互模式、--func定向调用、WASI 程序运行、栈空间调优以及一套从命令实操到源码级注入原理的完整分析方法。Wasm3 与 WAPM 包为什么要把解释器编译成 WASMWasm3 是一个用 C 实现的快速 WebAssembly 解释器。它的独特之处在于解释器本身可以被编译为一个 WASI 格式的 WebAssembly 模块并以 WAPM 包的形式发布——这意味着你可以在一个 WASM 运行时里再运行一个 WASM 运行时即“在 WebAssembly 上运行 WebAssembly”。包的定义位于 extra/wapm-package/wapm.toml其完整内容如下[package] name vshymanskyy/wasm3 version 0.5.0 description A fast WebAssembly interpreter. You can finally run WebAssembly on WebAssembly readme README.md repository https://github.com/wasm3/wasm3 homepage https://github.com/wasm3/wasm3 [[module]] name wasm3 source build/wasm3-wasi.wasm abi wasi [[module]] name wasm3-strace source build/wasm3-strace.wasm abi wasi [[command]] name wasm3 module wasm3 [[command]] name wasm3-strace module wasm3-strace这个清单揭示了包的三个关键设计两个 WASI 模块wasm3对应build/wasm3-wasi.wasm是标准解释器wasm3-strace对应build/wasm3-strace.wasm是开启跟踪插桩的跟踪版。两者都声明abi wasi即按 WASI 命令行应用command的方式导出_start、依赖 WASI 的fd_*、args_*等系统调用接口。两个命令快捷方式[[command]]把命令名wasm3/wasm3-strace分别映射到对应模块这样用户在 WAPM 兼容环境中可以直接敲wasm3 ...而不是手动指定模块。分发即产物source指向构建出的build/*.wasm说明这些是 CI/发布流程编译生成的二进制产物而非仓库内源码。对应源码侧的 WASI 支持由 source/m3_api_wasi.c 与 source/m3_api_tracer.c 提供后者仅在d_m3HasTracer编译开关下生效。前置准备获取要运行的 WASM 文件进入支持 WASI 的宿主环境例如 WebAssembly.sh后先用curl拉取一个测试用的 wasm 文件。原文档使用fib32.wasm作为示例$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/lang/fib32.wasm -o /tmp/fib32.wasm $ ls -l /tmp/fib32.wasm ---------- 1 somebody somegroup 62 1970-01-19 05:45 /tmp/fib32.wasm该文件仅 62 字节是一个计算斐波那契数的极简模块。除了远程获取这份测试资产也直接内置于当前仓库test/lang/fib32.wasm其可读源码形式见 test/lang/fib32.wat另有 64 位变体 test/lang/fib64.wasm。在本地非沙箱环境中完全可以直接以仓库相对路径代替 curl 下载$ wasm3 --repl test/lang/fib32.wasm交互模式wasm3 --replwasm3支持交互式解释器REPL模式加载模块后逐行输入函数调用$ wasm3 --repl /tmp/fib32.wasm wasm3 fib 20 Result: 6765 wasm3 fib 30 Result: 832040 wasm3 ^C $REPL 的提示符为wasm3输入fib 20即调用模块导出的fib函数并传入参数20运行后以Result: 6765打印返回值。从源码结构看这一交互循环实现在 platforms/app/main.c程序反复读取一行命令、按空白切分参数然后分发执行。REPL 内部还内置了一批冒号开头的管理命令见 platforms/app/main.c了解它们有助于深度使用命令作用:init重新初始化运行时可指定栈大小:load file加载一个 wasm 模块:load-hex size从标准输入按十六进制字节加载模块:invoke func [args...]调用模块导出函数供 spec 测试使用浮点按位模式处理:invoke-in module func [args...]调用指定模块内的导出函数:get-global/:set-global读写模块全局变量:register name/:name module为模块注册/命名供后续模块 import:compile强制编译默认按需惰性编译:dump将最近加载模块的内存导出为wasm3_dump.bin:version/:exit打印版本 / 退出会话函数调用的核心路径是repl_callplatforms/app/main.c它先用m3_FindFunction在运行时中查找函数再用m3_GetArgCount校验实参个数少了报not enough arguments多了报too many arguments最后通过m3_CallArgv执行并借助m3_GetResults读取返回值。定向调用wasm3 --func如果只想运行模块中的某个具体函数可以跳过 REPL用--func直接指定$ wasm3 --func fib /tmp/fib32.wasm 30 Result: 832040这里--func fib指定调用导出的fib函数30是紧随其后的实参。默认入口函数是_startplatforms/app/main.c 的print_usage中写明default: _start所以对普通模块直接wasm3 file.wasm就会运行其_start。--func亦可用-f简写见参数解析分支 platforms/app/main.c。在 REPL 中输入不带冒号的命令也会走同一条repl_call路径。运行 WASI 程序wasm3编译时若开启 WASI 支持就能直接运行依赖系统接口的 WASI 应用$ curl https://raw.githubusercontent.com/wasm3/wasm3/main/test/wasi/simple/test.wasm -o /tmp/test.wasm $ wasm3 /tmp/test.wasm仓库中的对应测试程序是 test/wasi/simple/test.c编译命令见 test/wasi/simple/README.md其产物即 test/wasi/simple/test.wasm。它的_start通过fd_write向 stdout 输出并由一个会主动触发 trap 的test_trap函数用于跟踪演示见后文。从底层看WASI 支持是在模块加载完成后通过link_all链接进来的platforms/app/main.cm3_LinkLibC链接 libc 扩展m3_LinkWASI链接 WASI 接口当运行_start时repl_call会先把 WASI 上下文中的argc/argv设置为命令行参数并将 wasm 文件名作为argv[0]再执行调用platforms/app/main.c。程序若以proc_exit结束运行时返回m3Err_trapWasiExitCLI 会读取wasi_ctx-exit_code作为进程退出码。自托管在 wasm3 里运行 wasm3由于 wasm3 本身也被编译成了 WASI 模块因此可以“套娃”——用外层 wasm3 解释器运行内层的 wasm3-wasi.wasm再由它运行第三个 WASM 文件$ curl https://registry-cdn.wapm.io/contents/vshymanskyy/wasm3/0.5.0/build/wasm3-wasi.wasm -o /tmp/wasm3.wasm $ wasm3 --stack-size 100000 /tmp/wasm3.wasm /tmp/test.wasm这里的关键是--stack-size 100000外层解释器默认栈大小为 512KB见 platforms/app/main.c 的unsigned argStackSize 512 * 1024;但嵌套运行一个解释器其内部又要为第三个模块维护另一套栈会明显加深调用链因此需要显式调大栈空间。理解这一点的意义在于--stack-size并非模块内存大小而是解释器为执行 wasm 调用所预留的宿主侧栈空间遇到深度递归或嵌套运行时若出现栈相关错误首先应当尝试增大该参数。该 CLI 支持的其余选项均可在运行wasm3 --help时看到源码见 platforms/app/main.c包括--compile关闭惰性编译、--validate-only/--no-validate仅校验 / 跳过校验、--spec-repl面向 spec 测试的 REPL、--dump-on-traptrap 时保存转储、--gas-meter/--gas-limit燃料计量与上限、--snapshot/--resume/--interrupt执行快照的保存、恢复与暂停点。wasm3-strace结构化函数调用跟踪随包发布的wasm3-strace可以在不需要目标 WASM 文件做任何特殊配合的前提下输出任意 WASM 文件执行的结构化跟踪。例如跟踪纯函数调用$ wasm3-strace --repl /tmp/fib32.wasm wasm3 fib 3 fib (i32: 3) { fib (i32: 1) { } 1 fib (i32: 2) { fib (i32: 0) { } 0 fib (i32: 1) { } 1 } 1 } 2 Result: 2输出以缩进括号树呈现每个调用的入参如i32: 3与返回值如} 2可以直观观察fib 3的两次递归分支。仓库中保存了更多参考输出例如 test/strace/fib32.txt 展示了fib 6的完整调用树test/strace/host-call.txt 则展示了宿主原生函数调用被标记为native的情形。WASI 应用的跟踪同样支持且 trap 发生时还会附带完整的 wasm 回溯$ wasm3-strace /tmp/test.wasm trap _start () { __wasm_call_ctors () { __wasilibc_populate_preopens () { wasi_snapshot_preview1!fd_prestat_get(3, 65528) { native } 0 malloc (i32: 2) { dlmalloc (i32: 2) { sbrk (i32: 0) { } 131072 } 70016 } 70016 ... strcmp (i32: 70127, i32: 32) { } 0 test_trap () { a () { b () { c () { } !trap [trap] unreachable ... wasm backtrace: 0: 0x000c59 - .unnamed!c 1: 0x000c5e - .unnamed!b 2: 0x000c68 - .unnamed!a 3: 0x000c72 - .unnamed!test_trap 4: 0x000d2c - .unnamed!main 5: 0x0037c9 - .unnamed!__main_void 6: 0x000edb - .unnamed!__original_main 7: 0x0002f3 - .unnamed!_start Error: [trap] unreachable这段输出信息量很大跟踪树把 WASI 启动序列_start→__wasm_call_ctors→__wasilibc_populate_preopens与宿主调用wasi_snapshot_preview1!fd_prestat_get ... { native } 0完整记录了下来WASI 系统调用使用模块名!函数名前缀区分当c内触发unreachable指令时以!trap [trap] unreachable标记陷阱发生位置及原因 wasm backtrace:之后按调用顺序列出 8 层帧每帧给出 wasm 模块内偏移如0x000c59与函数名。此处函数名显示为.unnamed!c是因为该构建未携带 name section若模块带名称段如 test/wasi/simple/test.wasm 用--strip-debug保留名称段的方式编译回溯会显示真实函数名——参考 test/strace/trap.txt 中的table64-bounds.wasm!call_max。跟踪的实现原理编译期插桩与钩子注入“不需要目标模块特殊配合”并非魔法而是源自跟踪版运行时在编译阶段向每个函数体插入的追踪钩子。其核心实现在 source/m3_api_tracer.c整体受d_m3HasTracer编译开关控制钩子集合m3_LinkTracersource/m3_api_tracer.c把一组env模块下的追踪函数链接进被加载的模块包括函数进入/退出/循环的log_exec_enter/log_exec_exit/log_exec_loop、内存读写钩子load_ptr/store_ptr/load_val_i32…store_val_f64以及局部变量读写钩子get_i32…set_f64原始事件流这些钩子把事件写成id;func、id;val这类分号分隔行输出到wasm3_trace.csv首次找到任一追踪函数时打开见 source/m3_api_tracer.c容错设计SuppressLookupFailure会吞掉“函数查找失败”m3Err_functionLookupFailed即模块没有引用这些钩子时静默跳过这正是“无需目标模块特殊支持”的机制——只有模块被插桩后才会消费这些导入跟踪构建wasm3-strace.wasm对应以-Dd_m3EnableStrace2 -Dd_m3RecordBacktraces1编译的版本。测试脚本 test/run-strace-test.py 明确说明该构建“以独立 release 二进制形式发布”且因需要保留帧信息会关闭尾调用优化回溯来源trap 时m3_GetBacktrace从运行时取出调用帧链表CLI 逐帧打印模块偏移与函数名platforms/app/main.c最终错误信息经m3_GetErrorInfo补充[trap] unreachable等具体原因。跟踪输出的自动化验证跟踪功能并非仅供演示仓库自带一套回归测试 test/run-strace-test.py把上述命令行的 stderr 输出与 test/strace/ 下的参考文件逐行比对测试用例模块参考文件嵌套调用、参数与返回值test/lang/fib32.wasm--func fib 6test/strace/fib32.txt未命名函数的调用test/regression/github-477.wattest/strace/unnamed-func.txt宿主原生函数调用test/regression/wasi-memory-export-index0.wattest/strace/host-call.txttrap 及其发生帧test/regression/table64-bounds.wattest/strace/trap.txt深调用链上的 trap 回溯test/wasi/simple/test.wasmtraptest/strace/trap-nested.txt测试可用--exec指定被测试的解释器例如直接指定wasm3-strace.wasm或让外层宿主运行它用--update重新录制参考输出。这说明wasm3-strace的输出格式是稳定契约可作为调试与回归的基准。小结通过 extra/wapm-package/ 中的包定义wasm3 完成了一次有趣的“自举”作为 WASI 模块发布的解释器既可以在 WebAssembly.sh 等环境中直接运行任意 WASM--repl交互、--func定向调用、WASI 程序、--stack-size调栈又能被嵌套进另一个解释器实现自托管而wasm3-strace借助编译期插桩与env钩子注入为任意模块提供函数级调用树跟踪和 trap 回溯并有 test/strace/ 下的参考输出与 test/run-strace-test.py 提供可复现的验证手段。这套“命令实操 源码原理 回归测试”的组合既适合作为 WASM 运行时的上手实验也是理解解释器插桩与 WASI 应用调试的良好范例。赞分享解释器嵌入式语言运行时【免费下载链接】wasm3 A fast WebAssembly interpreter and the most universal WASM runtime项目地址https://gitcode.com/gh_mirrors/wa/wasm3点击查看免费下载相关推荐Wasm3一款高效的WebAssembly解释器和通用运行时Wasm3一款高效的WebAssembly解释器和通用运行时 WebAssemblyWasm作为一种新兴的技术正逐渐成为开发者的热门选择。今天我要为大解释器嵌入式语言运行时群晖硬盘兼容性终极破解指南6步解锁第三方硬盘完全教程群晖硬盘兼容性终极破解指南6步解锁第三方硬盘完全教程 您是否曾为群晖NAS拒绝识别第三方硬盘而烦恼是否在存储管理器看到不兼容硬盘的警告而束手无策Syn运维存储CLIwasm3 在 ESP32 上的构建与运行指南基于 ESP-IDF v5.4 的嵌入式 WebAssembly 解释器实践wasm3 在 ESP32 上的构建与运行指南基于 ESP IDF v5.4 的嵌入式 WebAssembly 解释器实践 导读 本文以 wasm3 官方仓库解释器嵌入式语言运行时上一篇Vuelidate与TypeScript集成指南构建类型安全的表单验证系统下一篇5分钟上手dat.gui如何在网页中快速构建交互式参数调节器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考