如何将Haskell库静态链接到Rust项目?链接报错问题排查
问题背景
目标为将Haskell库静态链接到Rust项目,动态链接已有可运行示例,但静态链接始终失败,最初报错提示ghc需要recompile with -fPIC,后续多轮调整后仍出现不同链接错误。
初始配置与首轮报错
初始项目配置
build.rs
fn main() { println!("cargo:rustc-link-search=native=deps"); println!("cargo:rustc-link-lib=static=tesths"); }
src/main.rs
extern "C" { pub fn addTwo(x: i32) -> i32; pub fn init(); pub fn fin(); } fn main() { println!("Hello, world!"); }
src/haskell/Lib.hs
module Lib where import Foreign.C.Types addTwo :: CInt -> CInt addTwo = (2 + ) foreign export ccall addTwo :: CInt -> CInt
cwrapper.c
#include <HsFFI.h> #ifdef __GLASGOW_HASKELL__ #include "Lib_stub.h" #endif #include <stdio.h> void init(void) { static char *argv[] = {"libhs.so", 0}, **argv_ = argv; static int argc = 1; hs_init(&argc, &argv_); } void fin(void) { hs_exit(); }
初始编译步骤与报错
使用ghc -c -static -lHSrts -lffi cwrapper.c编译C包装层得到cwrapper.o,使用ghc -c -static -fPIC -lffi Lib.hs编译Haskell代码得到对应目标文件,执行ar rcs libtesths.a Lib.o cwrapper.o将两个目标文件打包为静态库。
执行cargo build时报错如下:
note: /usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x29): undefined reference to `newCAF' /usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x39): undefined reference to `stg_bh_upd_frame_info' /usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x48): undefined reference to `base_ForeignziCziTypes_zdfNumCInt_closure' /usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x4f): undefined reference to `stg_ap_p_info' …
错误原因为GHC运行时(RTS)相关依赖未被正确打包进静态库,导致链接时缺失符号。
阶段1:GMP依赖缺失报错
调整GHC编译参数,在stack配置中添加ghc-options: -staticlib -stubdir .和c-sources: cwrapper.c完成Haskell侧构建后,执行cargo rustc — -C relocation-model=static构建Rust项目时出现新报错:
/usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_zdwplusBigNatWord_info' (.text.integerzmwiredzmin_GHCziIntegerziType_zdwplusBigNatWord_info+0x128): undefined reference to `__gmpn_add_1' /usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_zdwminusBigNatWord_info' (.text.integerzmwiredzmin_GHCziIntegerziType_zdwminusBigNatWord_info+0xdf): undefined reference to `__gmpn_sub_1' /usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_complementInteger_info' (.text.integerzmwiredzmin_GHCziIntegerziType_complementInteger_info+0x138): undefined reference to `__gmpn_sub_1' /usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_zdwtimesBigNatWord_info' (.text.integerzmwiredzmin_GHCziIntegerziType_zdwtimesBigNatWord_info+0x158): undefined reference to `__gmpn_mul_1'
错误原因为Haskell的Integer类型实现依赖GMP数学库,未将该库加入链接配置。在build.rs中新增libgmp的静态链接配置即可解决该问题。
阶段2:符号多重定义报错
解决GMP依赖问题后,出现符号重复定义报错:
note: /usr/bin/ld: deps/libhssource.a(Lib.o): in function `testFFI': (.text+0x88fd): multiple definition of `testFFI'; deps/libhssource.a(Lib.o):(.text+0x1b66): first defined here /usr/bin/ld: deps/libhssource.a(cwrapper.o): in function `init': cwrapper.c:(.text+0x0): multiple definition of `init'; deps/libhssource.a(cwrapper.o):cwrapper.c:(.text+0x0): first defined here /usr/bin/ld: deps/libhssource.a(cwrapper.o): in function `fin': cwrapper.c:(.text+0x20): multiple definition of `fin'; deps/libhssource.a(cwrapper.o):cwrapper.c:(.text+0x1a): first defined here collect2: error: ld returned 1 exit status
错误原因为静态库打包或编译阶段重复引入了相同目标文件,导致同一个符号被多次定义。
最终可行方案
最终可用的构建命令如下:
cargo rustc -- -C relocation-model=static -Clink-arg=-Wl,--allow-multiple-definition
注意事项:不要通过全局RUSTFLAGS配置上述参数,该配置会对项目所有依赖启用静态重定位模型,若项目依赖rustversion这类不支持该配置的crate,会再次抛出recompile with -fPIC错误。
内容的提问来源于stack exchange,提问作者SmokedTurkey

