2012-04-12 61 views
7

我有關於Haskell中的FFI和GHC的交互模式的問題。GHCi不適用於FFI導出聲明/共享庫

(源,也可通過一個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(); 
} 

生成文件:

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

all: ffiso 

test: ffiso 
    ./$< 

ffiso: FFISo.hs c.c 
    ghc --make $(GHC_OPT) $^ -o [email protected] 

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. 

好吧,讓我們試着給GHCI的c.o objectfile。

$ 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 

回答

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生成一個存根文件,其中包含用於從C調用Haskell函數的C代碼。此文件被稱爲FFISo_stub.c,並且已編譯爲FFISo_stub.o。該目標文件導出可直接調用的「C版本」callMeFromC。隨意檢查生成的代碼,這非常有趣。

一切的一切,你需要使用此命令行調用GHCI時:

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