Question

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_GHCziInt_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'
Was it helpful?

Solution

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top