You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无C main函数时,如何生成并链接含Haskell导出函数的.o文件

从C调用Haskell函数并生成可被其他程序链接的库文件

需求

我知道如何从C调用Haskell函数,但不想生成可执行文件,而是要创建能和其他.o文件链接的库文件(可被其他C程序调用)。

代码示例

Safe.hs

{-# LANGUAGE ForeignFunctionInterface #-}

module Safe where

import Foreign.C.Types

fibonacci :: Int -> Int
fibonacci n = fibs !! n
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral

foreign export ccall fibonacci_hs :: CInt -> CInt

test.c

#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Safe_stub.h"
#endif

int foo()
{
    int i;
    // 定义合法的argc和argv,避免未定义错误
    int argc = 0;
    char *argv[] = {NULL};
    hs_init(&argc, &argv);

    i = fibonacci_hs(42);

    hs_exit();
    return i;
}

已尝试的操作及错误

  1. 编译Haskell代码:

    ghc -fPIE -c -O Safe.hs
    

    生成Safe_stub.h、Safe.o和Safe.hi,这一步没问题。

  2. 编译C代码:

    ghc --make -fPIE -no-hs-main -optc-O -c test.c -o test.o
    

    得到test.o。

  3. 尝试链接生成目标文件:

    ghc -no-hs-main -optc-nostartfiles -optc-static-pie test.o Safe.o -o result.o
    

    报错:

    /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crt1.o:function _start: error: undefined reference to 'main'
    collect2: error: ld returned 1 exit status
    `x86_64-linux-gnu-gcc' failed in phase `Linker'. (Exit code: 1)
    

补充说明:有人建议用ld -r链接,但该方法无法引入Haskell运行时,我需要生成可被其他C程序调用的库文件。

问题分析与解决方案

错误原因

你试图用GHC生成单个.o文件,但GHC默认会尝试生成可执行文件——即使指定-o result.o,它依然会链接启动代码(比如crt1.o),而启动代码依赖main函数,因此报错。另外,原test.c中argc和argv未定义,也是潜在问题。

正确操作步骤

步骤1:修复C代码中的未定义变量

按照上面的test.c示例,添加argc和argv的合法定义。

步骤2:重新编译C代码

直接用gcc编译,指定Haskell头文件路径:

gcc -c -O -fPIE test.c -o test.o -I$(ghc --print-libdir)/include

步骤3:生成动态库(推荐)

用GHC生成动态库,它会自动处理Haskell运行时的所有依赖:

ghc -shared -fPIE -no-hs-main -o libfoo.so test.o Safe.o

生成的libfoo.so可以被其他C程序链接调用,比如:

gcc -o main main.c -L. -lfoo -Wl,-rpath=.

(假设main.c中调用了foo()函数)

步骤4:生成静态库

如果需要静态库,先用ar打包目标文件,链接其他C程序时需手动指定所有Haskell依赖:

# 打包静态库
ar cr libfoo.a test.o Safe.o
# 链接其他程序时带上Haskell运行时依赖
gcc -o main main.c libfoo.a $(ghc --print-libdir)/libHSrts.a -L$(ghc --print-libdir) -lHSbase-4.16.4.0 -lHSinteger-gmp-1.0.3.0 -lgmp -lm

注意:静态库需要手动指定所有Haskell依赖,因此动态库更方便维护。


内容的提问来源于stack exchange,提问作者Abhiroop Sarkar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 17:02:25