如何实现Rust链接GHC-WASM外部库?
近期Rust新增支持Rust+C WASM编译的编译器标志,已有成熟的Rust+C WASM编译示例仓库。由于Haskell与Rust均支持C-FFI且可编译为WASM(当前Haskell仅支持WASI目标),我希望复刻该流程,将C二进制替换为Haskell库进行链接。最终目标是构建一个Rust crate,通过构建脚本调用GHC WASM编译并静态链接指定Haskell库(例如Rust生态缺失的HaTeX),供其他WASM目标的Rust crate依赖,确保最终产物包含Haskell编译生成的WASM代码。
已尝试方案
可执行文件方案
参考Haskell WASM GHC官方仓库,通过Nix-shell搭建基础项目,结构如下:
cbits\ wizerinit.c <-- 复刻GHC WASM仓库的Wizer预初始化代码 src\ Lib.hs <-- 导出`hs_dummy_init :: IO ()`和`hs_fib :: CInt -> CInt`的FFI模块 OtherModule.hs <-- 空模块用于验证多模块兼容性 latex-preprocessor.cabal
latex-preprocessor.cabal配置内容:
cabal-version: 3.12 name: latex-preprocessor version: 0 executable latex-preprocessor main-is: Lib.hs other-modules: OtherModule c-sources: cbits/wizerinit.c hs-source-dirs: src default-language: GHC2021 build-depends: , base ghc-options: -Wall -no-hs-main -optl-mexec-model=reactor "-optl-Wl,--export=hs_dummy_init,--export=hs_fib"
执行以下命令成功生成WASM文件:
wasm32-wasi-cabal build exe:latex-preprocessor LATEX_PREPROCESSOR_WASM="$(wasm32-wasi-cabal list-bin exe:latex-preprocessor)" wizer --allow-wasi --wasm-bulk-memory true \ "$LATEX_PREPROCESSOR_WASM" -o "dist-newstyle/haskell.wasm"
尝试用wasm-ld链接Rust与Haskell产物时,Rust侧使用nightly工具链并启用wasm_c_abi,lib.rs测试函数代码:
#[no_mangle] pub extern "C" fn add(left: usize, right: usize) -> usize { left + right }
wasm-ld报错:wasm-ld: error: ./build/haskell.wasm: not a relocatable wasm file,推测原因是Haskell产物为可执行文件而非可重定位库。
库方案
将Cabal配置改为library stanza:
library exposed-modules: Lib other-modules: OtherModule c-sources: cbits/wizerinit.c hs-source-dirs: src default-language: GHC2021 build-depends: , base ghc-options: -Wall -no-hs-main -optl-mexec-model=reactor "-optl-Wl,--export=hs_dummy_init,--export=hs_fib"
执行wasm32-wasi-cabal build lib:latex-preprocessor生成.a和.so库,对.a执行wizer时出现魔数匹配失败,遂省略该步骤。
修改后构建脚本无报错,但输出的.wat几乎为空;添加--whole-archive标志后,Rust的add函数可正常导出,但链接Haskell库时出现大量未定义符号错误:
wasm-ld: error: ./build/haskell.wasm(Lib.o): undefined symbol: __Sp ... wasm-ld: error: ./build/haskell.wasm(wizerinit.o): undefined symbol: malloc_inspect_all
这些符号来自Haskell RTS及补丁版libc,推测GHC生成的库未静态链接依赖项。
疑问与求助
- Cabal的
foreign-librarystanza、GHC的shared/static/-fPIC等标志,以及-optl-Wl,--relocatable链接标志如何适配WASM后端? - wizer是否仅适用于WASM可执行文件?是否需要将
cbits/wizerinit.c的逻辑暴露到Rust wrapper crate,待最终生成可执行文件后再执行wizer? - GHC的
wasm32-wasi与Rust的wasm32-unknown-unknown能否兼容链接?是否需要浏览器WASI polyfill? - WASM多内存模型的
linear_memory导入如何处理,才能避免内存布局冲突?
现询问该方案是否可行,以及是否有GHC+WASM+C-FFI相关的实现资源、示例或文档。
内容的提问来源于stack exchange,提问作者the thinker

