如何正确链接Haskell目标文件?解决C++调用Haskell的链接脆弱问题
解决C++调用Haskell时移除函数调用后的链接错误问题
这个问题的核心原因是链接器的“按需链接”(as-needed)行为:当你的代码里没有调用foo时,链接器会认为foo.so里的符号都没被用到,因此不会自动加载它依赖的Haskell核心库(比如base、ghc-prim),但libHSrts本身依赖这些库的符号,于是就出现了未定义引用的错误。而当你保留foo调用时,链接器会跟着foo的符号引用,自动拉取所有依赖的库,填补了libHSrts的缺失符号。
下面提供两种可靠的解决方案:
方案1:显式链接所有必要的Haskell核心库
从你提供的ldd输出可以看到,foo.so依赖这些核心库:
libHSbase-4.10.0.0-ghc8.2.1.solibHSinteger-gmp-1.0.1.0-ghc8.2.1.solibHSghc-prim-0.5.1.0-ghc8.2.1.so
你需要在链接命令中显式添加这些库的路径和链接参数。修改构建脚本中的链接步骤:
g++ test.o "$hs_obj" \ -L "$ghc_libdir/rts" "-lHSrts-ghc$ghc_version" \ -L "$ghc_libdir/base-4.10.0.0" -lHSbase-4.10.0.0-ghc8.2.1 \ -L "$ghc_libdir/integer-gmp-1.0.1.0" -lHSinteger-gmp-1.0.1.0-ghc8.2.1 \ -L "$ghc_libdir/ghc-prim-0.5.1.0" -lHSghc-prim-0.5.1.0-ghc8.2.1 \ -o test
这种方法的好处是依赖关系清晰,但需要你手动匹配GHC版本对应的库路径和名称,版本变更时需要调整。
方案2:强制链接器保留foo.so的依赖
使用GCC的-Wl,--no-as-needed选项,告诉链接器即使foo.so的符号没有被直接引用,也要处理它的所有依赖。修改链接命令如下:
g++ test.o -Wl,--no-as-needed "$hs_obj" -Wl,--as-needed \ -L "$ghc_libdir/rts" "-lHSrts-ghc$ghc_version" \ -o test
-Wl,--no-as-needed会作用于紧跟在它后面的库(这里是foo.so),之后用-Wl,--as-needed恢复默认的按需链接行为,避免不必要的库被链接进来。这种方法更简洁,不需要手动管理每个核心库,适合大多数场景。
验证效果
修改构建脚本后,即使移除std::cout << foo(37, 19) << "\n";这行代码,链接阶段也能正常完成,因为必要的Haskell核心库已经被正确引入了。
内容的提问来源于stack exchange,提问作者leftaroundabout
相关产品推荐
相关产品推荐

