2012-04-12 11 views
7

Tengo un problema con respecto a FFI en Haskell y el modo interactivo de GHC.GHCi no funciona con las declaraciones de exportación FFI/bibliotecas compartidas

(Fuente también está disponible a través de 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() 

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 

compilación y el enlace funciona bien:

$ 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 

Sin embargo, si quiero usar GHCi, se produce un error con este mensaje:

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

Bueno, vamos a tratar de dar GHCi la objectFile c.o.

$ 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 bien ... vamos a tratar 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 ... 

Y ahí es donde estoy atascado.

he comprobado con dos ambientes diferentes con el mismo resultado:

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

Respuesta

10

Es necesario especificar algunos más objetos de vincular al invocar GHCi, porque el objeto C c.o es particularmente exigente cuando se trata de la vinculación orden.

Primero, necesita agregar los archivos de objetos del paquete bytestring. Esto se hace agregando -package bytestring a la línea de comando de GHCi.

Luego, necesita agregar el archivo de objeto real que define callMeFromC. Cuando se compila FFISo.hs, no produce un archivo de objeto que exporte callMeFromC. En su lugar, utiliza la convención de nomenclatura GHC y exporta Main_zdfcallMeFromCzuak4_closure, que en realidad es una variable global estática que apunta al cierre/"thunk" que contiene la definición y el entorno de la función real. Esto es por lo que no se puede escribir algo como esto:

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

... y tienen el choque de ejecución tan pronto como se inicia el programa debido a que el "valor de la función" de foo no puede ser evaluada. La definición de función solo se inspecciona una vez que la función se utiliza realmente.

GHC genera un archivo de resguardo, que contiene el código C para llamar a la función Haskell desde C. Este archivo se llama FFISo_stub.c, y se compila en FFISo_stub.o para usted. Este archivo objeto exporta la "versión C" de callMeFromC que se puede invocar directamente. Siéntase libre de inspeccionar el código generado, es bastante interesante.

Con todo, es necesario utilizar esta línea de comandos cuando se invoca GHCi:

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