2012-04-12 9 views
7

Mam problem dotyczący FFI w Haskell i trybie interaktywnym GHC.GHCi nie działa z deklaracjami eksportowymi FFI/bibliotekami współdzielonymi

(źródło jest również poprzez 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 [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 

Kompilacja i łączenie działa dobrze:

$ 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 

Jednakże, jeśli chcę użyć GHCi, nie jest on z tej wiadomości:

$ 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. 

Dobrze, spróbujmy dając GHCi się 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 ... 

Och dobrze ... spróbujmy z 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 ... 

I w tym miejscu utknąłem.

Testowałem go z dwóch różnych środowisk z takim samym skutkiem:

$ # 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 

Odpowiedz

10

Musisz podać trochę więcej obiektów połączyć podczas wywoływania GHCi, ponieważ obiekt C c.o jest szczególnie wybredni, jeśli chodzi o powiązanie zamówienie.

Najpierw należy dodać pliki obiektów pakietu bytestring. Odbywa się to przez dodanie -package bytestring do linii poleceń GHCi.

Następnie należy dodać rzeczywisty plik obiektu, który definiuje callMeFromC. Po kompilacji FFISo.hs nie tworzy pliku obiektowego, który eksportuje callMeFromC. Zamiast tego używa konwencji nazewnictwa GHC i eksportuje Main_zdfcallMeFromCzuak4_closure, która w rzeczywistości jest statyczną zmienną globalną wskazującą zamknięcie/"thunk", które zawiera rzeczywistą definicję funkcji i środowisko. To jest tak, że nie można napisać coś takiego:

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

... i mają awarię wykonania, jak tylko zaczyna program, ponieważ „wartość funkcji” od foo nie można ocenić. Definicja funkcji jest sprawdzana tylko wtedy, gdy funkcja jest rzeczywiście używana.

GHC generuje plik pośredniczący, który zawiera kod C do wywoływania funkcji Haskella z C. Ten plik nazywa się FFISo_stub.c i jest skompilowany dla Ciebie w FFISo_stub.o. Ten plik obiektowy eksportuje wersję "C" o numerze callMeFromC, którą można wywołać bezpośrednio. Zapraszam do sprawdzenia wygenerowanego kodu, jest całkiem interesujący.

W sumie, trzeba użyć tego wiersza poleceń podczas wywoływania GHCi:

ghci -package bytestring FFISo.o c.o FFISo_stub.o FFISo.hs 
Powiązane problemy