Interfacing haskell with c
I've been thinking that it would be nice to create midori plugins with haskell, but it seems to be nigh impossible. The problem lies with exporting haskell functions through ffi, as the ghc compiler makes use of a massive amount of -u switches.
tHas anyone seen haskell been used in a similar context, without having to replace gcc for ghc? If so, how did it work out and what hoops did they go through?
Edit: Some examples were requested, so here:
export.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Export where
import Foreign.C
import Foreign.C.Types
foo :: IO Int
foo = return 2
foreign export ccall foo :: IO Int
test.c (ifdefs snipped)
#include <stdio.h>
#include "HsFFI.h"
#include "export_stub.h"
extern void __stginit_Export(void);
int main(int argc, char **argv)
{
int i;
hs_init(&argc, &argv);
hs_add_root(__stginit_Export);
i = foo();
printf("%d\n", i);
hs_exit();
return 0;
}
Compiling with ghc --make -no-hs-main export.hs test.c
creates an a.out
executable which works. The ghc uses the following command for linking:
collect2 --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o a.out -z relro -u ghczmprim_GHCziTypes_Izh_static_info -u ghczmprim_GHCziTypes_Czh_static_info -u ghczmprim_GHCziTypes_Fzh_static_info -u ghczmprim_GHCziTypes_Dzh_static_info -u base_GHCziPtr_Ptr_static_info -u base_GHCziWord_Wzh_static_info -u base_GHCziIn开发者_开发知识库t_I8zh_static_info -u base_GHCziInt_I16zh_static_info -u base_GHCziInt_I32zh_static_info -u base_GHCziInt_I64zh_static_info -u base_GHCziWord_W8zh_static_info -u base_GHCziWord_W16zh_static_info -u base_GHCziWord_W32zh_static_info -u base_GHCziWord_W64zh_static_info -u base_GHCziStable_StablePtr_static_info -u ghczmprim_GHCziTypes_Izh_con_info -u ghczmprim_GHCziTypes_Czh_con_info -u ghczmprim_GHCziTypes_Fzh_con_info -u ghczmprim_GHCziTypes_Dzh_con_info -u base_GHCziPtr_Ptr_con_info -u base_GHCziPtr_FunPtr_con_info -u base_GHCziStable_StablePtr_con_info -u ghczmprim_GHCziBool_False_closure -u ghczmprim_GHCziBool_True_closure -u base_GHCziPack_unpackCString_closure -u base_GHCziIOziException_stackOverflow_closure -u base_GHCziIOziException_heapOverflow_closure -u base_ControlziExceptionziBase_nonTermination_closure -u base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -u base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -u base_ControlziExceptionziBase_nestedAtomically_closure -u base_GHCziWeak_runFinalizzerBatch_closure -u base_GHCziTopHandler_runIO_closure -u base_GHCziTopHandler_runNonIO_closure -u base_GHCziConc_ensureIOManagerIsRunning_closure -u base_GHCziConc_runSparks_closure -u base_GHCziConc_runHandlers_closure /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.4.3/crtbegin.o -L/usr/lib/ghc-6.12.1/base-4.2.0.0 -L/usr/lib/ghc-6.12.1/integer-gmp-0.2.0.0 -L/usr/lib/ghc-6.12.1/ghc-prim-0.2.0.0 -L/usr/lib/ghc-6.12.1 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../.. -L/usr/lib/i486-linux-gnu export.o export_stub.o test.o -lHSbase-4.2.0.0 -lHSinteger-gmp-0.2.0.0 -lgmp -lHSghc-prim-0.2.0.0 -lHSrts -lm -lffi -lrt -ldl -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crtn.o
Removing the -u
switches (leaving just -l, -L and some extra flags) from
the previous command doesn't compile and returns (and some 50 or so more
lines)
/usr/lib/ghc-6.12.1/libHSrts.a(RtsAPI.o): In function `rts_mkFunPtr':
(.text+0x5a9): undefined reference to `base_GHCziPtr_FunPtr_con_info'
/usr/lib/ghc-6.12.1/libHSrts.a(RtsAPI.o): In function `rts_mkString':
(.text+0x60f): undefined reference to `base_GHCziPack_unpackCString_closure'
I was able to solve the problem.
I'm using the following files:
main.c
#include <stdio.h>
#include "lib_stub.h"
int main(int argc, char **argv)
{
puts("foo");
printf("%d\n", hsfun(5));
return 0;
}
lib.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Test where
import Foreign.C.Types
hsfun :: CInt -> IO CInt
hsfun x = do
putStrLn "Hello from haskell"
return (42 * x)
foreign export ccall hsfun :: CInt -> IO CInt
module_init.c
#include <HsFFI.h>
extern void __stginit_Test(void);
static void library_init(void) __attribute__((constructor));
static void library_init(void)
{
static char *argv[] = { "libtest.so", 0 }, **argv_ = argv;
static int argc = 1;
hs_init(&argc, &argv_);
hs_add_root(__stginit_Test);
}
static void library_exit(void) __attribute__((destructor));
static void library_exit(void)
{
hs_exit();
}
I'm compiling the library with ghc --make -shared -dynamic -fPIC -o
libtest.so lib.hs module_init.c -lHSrts-ghc6.12.1
-optl-Wl,-rpath,/usr/lib/ghc-6.12.1/ -L/usr/lib/ghc-6.12.1
and the
executable with gcc -c main.c -I/usr/lib/ghc-6.12.1/include -L. -ltest
-Wl,-rpath=$PWD
The important part is making the library shared and having the rts library linked also which doesn't come default. The rpath makes it so that it can be ran without LD_LIBRARY_PATH.
http://weblog.haskell.cz/pivnik/building-a-shared-library-in-haskell/
http://www.well-typed.com/blog/30
This is probably the related part of the manual:
8.2. Using the FFI with GHC
See 8.2.1.2 in particular. You can make a library written in Haskell and callable from C code. Then you have only to write some glue code in C to turn in into a plugin or whatever. But I didn't do it myself, please wait for more experienced users of foreign export
to answer.
精彩评论