GHCi无法与FFI导出声明/共享库一起使用。

7
我在Haskell的FFI和GHC交互模式中遇到了问题。
(源代码也可以通过gist获得):
FFISo.hs:
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module Main where

import qualified Data.ByteString.Char8 as B

foreign import ccall "callMeFromHaskell"
  callMeFromHaskell :: IO ()

foreign export ccall callMeFromC :: IO ()
callMeFromC :: IO ()
callMeFromC = B.putStrLn "callMeFromC"

main :: IO ()
main = do
  B.putStrLn "main"
  callMeFromHaskell
  return ()

c.c:

#include <stdio.h>

void callMeFromC(void);

void callMeFromHaskell(void)
{   
    printf("callMeFromHaskell\n");
    callMeFromC();
}

Makefile:

GHC_OPT := -Wall -O2 -fno-warn-unused-do-bind

all: ffiso

test: ffiso
    ./$<

ffiso: FFISo.hs c.c
    ghc --make $(GHC_OPT) $^ -o $@

clean:
    rm -rf *.hi *.o ffiso *_stub.*

ghci0: ffiso
    echo main | ghci FFISo.hs

ghci1: ffiso
    echo main | ghci FFISo.hs c.o

ghci2: ffiso
    echo main | ghci FFISo.hs c.o FFISo.o

编译和链接工作正常:

$ make test
ghc --make -Wall -O2 -fno-warn-unused-do-bind FFISo.hs c.c -o ffiso
[1 of 1] Compiling Main             ( FFISo.hs, FFISo.o )
Linking ffiso ...
./ffiso
main
callMeFromHaskell
callMeFromC

然而,如果我想使用GHCi,则会出现以下错误消息:
$ make ghci0
echo main | ghci FFISo.hs
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Ok, modules loaded: Main.
Prelude Main> Loading package bytestring-0.9.2.1 ... linking ... done.
<interactive>: FFISo.o: unknown symbol `callMeFromHaskell'

Prelude Main> Leaving GHCi.

好的,让我们尝试将c.o目标文件提供给GHCi。
$ make ghci1
echo main | ghci FFISo.hs c.o
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading object (static) c.o ... done
ghc: c.o: unknown symbol `callMeFromC'
linking extra libraries/objects failed
make: *** [ghci1] Error 1
final link ... 

好的,请让我试一下用FFISo.o:
$ make ghci2
echo main | ghci FFISo.hs c.o FFISo.o
GHCi, version 7.4.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading object (static) c.o ... done
Loading object (static) FFISo.o ... done
ghc: FFISo.o: unknown symbol `bytestringzm0zi9zi2zi1_DataziByteStringziInternal_PS_con_info'
linking extra libraries/objects failed
make: *** [ghci2] Error 1
final link ... 

那就是我遇到的瓶颈。

我在两个不同的环境下测试了它,结果相同:

$ # system 1
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.4.1
$ uname -a
Linux phenom 3.2.13-1-ARCH #1 SMP PREEMPT Sat Mar 24 09:10:39 CET 2012 x86_64 AMD Phenom(tm) II X6 1055T Processor AuthenticAMD GNU/Linux

$ # system 2
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 6.12.1
$ uname -a
Linux hermann 2.6.32-22-generic-pae #36-Ubuntu SMP Thu Jun 3 23:14:23 UTC 2010 i686 GNU/Linux
1个回答

10

在调用GHCi时,需要指定更多的对象来链接,因为C语言对象c.o对于链接顺序特别挑剔。

首先,您需要添加bytestring包的对象文件。这可以通过在GHCi命令行中添加-package bytestring来完成。

然后,您需要添加定义callMeFromC的实际对象文件。当编译FFISo.hs时,它不会生成一个导出callMeFromC的对象文件。相反,它使用了GHC命名约定并导出Main_zdfcallMeFromCzuak4_closure,这实际上是一个指向包含实际函数定义和环境的闭包/“thunk”的静态全局变量。这样您就不能编写如下代码:

foregin export ccall foo :: IO ()
foo = undefined

如果程序开始时无法计算foo的"函数值",那么运行时会立即崩溃。只有实际使用函数时才会检查函数定义。

GHC会生成一个存根文件,其中包含调用Haskell函数的C代码。该文件称为FFISo_stub.c,并为您编译成FFISo_stub.o。此对象文件导出可直接调用的callMeFromC的"C版本"。可以随意检查生成的代码,它非常有趣。

总之,在调用GHCi时,您需要使用此命令行:

ghci -package bytestring FFISo.o c.o FFISo_stub.o FFISo.hs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接