2012-04-12 7 views
7

Ho un problema riguardante FFI in Haskell e la modalità interattiva di GHC.GHCi non funziona con le dichiarazioni di esportazione FFI/librerie condivise

(sorgente è disponibile anche tramite un 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() 

ç.ç:

#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 

compilazione e collegamento funziona bene:

$ 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 

Tuttavia, se voglio usare GHCi, non riesce con questo messaggio:

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

Bene, proviamo dando GHCi il 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 ... 

Oh bene ... proviamo con 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 ... 

Ed è lì che mi sono bloccato.

ho provato con due ambienti diversi con lo stesso risultato:

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

risposta

10

È necessario specificare alcuni più oggetti per creare un collegamento quando si richiama GHCi, perché l'oggetto C c.o è particolarmente esigente quando si tratta di collegamento ordine.

Prima di tutto, è necessario aggiungere i file oggetto del pacchetto bytestring. Questo viene fatto aggiungendo -package bytestring alla riga di comando GHCi.

Quindi, è necessario aggiungere il file oggetto effettivo che definisce callMeFromC. Quando viene compilato FFISo.hs, non produce un file oggetto che esporta callMeFromC. Utilizza invece la convenzione di denominazione GHC ed esporta Main_zdfcallMeFromCzuak4_closure, che in realtà è una variabile globale statica che punta alla chiusura/"thunk" che contiene la definizione e l'ambiente della funzione effettiva. Questo è così che non si può scrivere qualcosa del genere:

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

... e hanno il crollo del funzionamento, non appena il programma inizia perché il "valore della funzione" di foo non può essere valutata. La definizione della funzione viene ispezionata solo quando la funzione viene effettivamente utilizzata.

GHC genera un file stub, che contiene il codice C per chiamare la funzione Haskell da C. Questo file è chiamato FFISo_stub.c ed è compilato per FFISo_stub.o. Questo file oggetto esporta la "versione C" di callMeFromC che può essere richiamata direttamente. Sentiti libero di ispezionare il codice generato, è piuttosto interessante.

Tutto sommato, è necessario utilizzare questa riga di comando quando si richiama GHCi:

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