导出函数未在Wasm中显示:Zig编译WASI模块调用问题
问题原因
当使用zig build-exe编译WASM32-WASI目标时,默认生成的是WASI可执行文件,这类文件的设计目标是通过_start函数(对应Zig的main函数)启动执行,链接器只会将_start导出到Wasm模块的导出表中。即使你用pub export标记了add函数,链接器也会将其视为内部符号,不会暴露到模块外部——哪怕该函数被main调用,也不会改变这个行为。
解决方案
有两种方式可以解决这个问题,根据你的需求选择:
方案1:编译为Wasm库(适合仅暴露函数供外部调用)
使用zig build-lib代替zig build-exe,编译为动态库(Wasm模块的库需要动态导出符号):
zig build-lib -O ReleaseSmall -target wasm32-wasi -dynamic main.zig
编译后生成的main.wasm会将add函数导出到模块的导出表中。
验证WasmEdge调用
现在可以用reactor模式正常调用add:
wasmedge --reactor main.wasm add 2 2
验证Node.js调用
修改Node.js代码,无需执行wasi.start()(因为库没有main函数),直接调用add:
import { readFile } from 'node:fs/promises'; import { WASI } from 'wasi'; const wasi = new WASI({ version: 'preview1', preopens: { '/sandbox': './', }, }); const wasm = await WebAssembly.compile( await readFile(new URL('./main.wasm', import.meta.url)), ); const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject()); console.log(instance.exports.add(3, 4)); // 输出7
方案2:编译为可执行文件并强制导出add(同时保留main函数)
如果需要同时保留main函数的可执行能力,又要暴露add供外部调用,可以通过传递链接器参数,强制导出add函数:
zig build-exe -O ReleaseSmall -target wasm32-wasi -Wl,--export=add main.zig
这里-Wl,--export=add是将链接器参数--export=add传递给LLVM链接器,强制将add符号加入Wasm模块的导出表。
验证Node.js调用
此时模块会同时导出_start和add,你可以选择先执行main再调用add,或者直接调用add:
import { readFile } from 'node:fs/promises'; import { WASI } from 'wasi'; import { argv, env } from 'node:process'; const wasi = new WASI({ version: 'preview1', args: argv, env, preopens: { '/sandbox': './', }, }); const wasm = await WebAssembly.compile( await readFile(new URL('./main.wasm', import.meta.url)), ); const instance = await WebAssembly.instantiate(wasm, wasi.getImportObject()); // 可选:执行main函数 wasi.start(instance); // 调用add函数 const result = instance.exports.add(3, 4); console.log(result); // 输出7
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

