18

I want to build a dynamic library containing haskell functions. I work on linux and want to call this dynamic library from C++ code.

I used the example at http://wiki.python.org/moin/PythonVsHaskell and have the following files:

Test.hs:

{-# LANGUAGE ForeignFunctionInterface #-}
module Test where

import Foreign.C.Types

hsfun :: CInt -> IO CInt
hsfun x = do
    putStrLn "Hello World"
    return (42 + x)

foreign export ccall
    hsfun :: CInt -> IO CInt

module_init.c:

#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a

#include <HsFFI.h>

extern void CAT (__stginit_, MODULE) (void);

static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
  /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
  static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
  static int argc = 1;

  hs_init (&argc, &argv_);
  hs_add_root (CAT (__stginit_, MODULE));
}

static void library_exit (void) __attribute__ ((destructor));
static void
library_exit (void)
{
  hs_exit ();
}

Now I compile this files to a dynamic library:

$ ghc -dynamic -shared -fPIC -optc '-DMODULE=Test' Test.hs module_init.c -o libTest.so
[1 of 1] Compiling Test             ( Test.hs, Test.o )
Linking libTest.so ...

This creates among other things the file Test_stub.h:

#include "HsFFI.h"
#ifdef __cplusplus
extern "C" {
#endif
extern HsInt32 hsfun(HsInt32 a1);
#ifdef __cplusplus
}
#endif

and Test_stub.c:

#define IN_STG_CODE 0
#include "Rts.h"
#include "Stg.h"
#ifdef __cplusplus
extern "C" {
#endif

extern StgClosure Test_zdfhsfunzua165_closure;
HsInt32 hsfun(HsInt32 a1)
{
Capability *cap;
HaskellObj ret;
HsInt32 cret;
cap = rts_lock();
cap=rts_evalIO(cap,rts_apply(cap,(HaskellObj)runIO_closure,rts_apply(cap,&Test_zdfhsfunzua165_closure,rts_mkInt32(cap,a1))) ,&ret);
rts_checkSchedStatus("hsfun",cap);
cret=rts_getInt32(ret);
rts_unlock(cap);
return cret;
}
static void stginit_export_Test_zdfhsfunzua165() __attribute__((constructor));
static void stginit_export_Test_zdfhsfunzua165()
{getStablePtr((StgPtr) &Test_zdfhsfunzua165_closure);}
#ifdef __cplusplus
}
#endif

Then I create a cpp file main.cpp:

#include "Test_stub.h"

#include <iostream>

using namespace std;

int main()
{
    cout << hsfun(5);
}

and want to compile and link it. But when I call g++, it says:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp
/tmp/ccFP2AuB.o: In function `main':
main.cpp:(.text+0xa): undefined reference to `hsfun'
collect2: ld gab 1 als Ende-Status zurück

So I added the Test_stub.o file to the command line (although I think the hsfun function should already be defined in libTest.so which is added via the -lTest parameter. I don't think, I should link the Test_stub.o file into the executable because I want to use dynamic linking), but this also doesn't work:

$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp Test_stub.o
Test_stub.o: In function `hsfun':
Test_stub.c:(.text+0x9): undefined reference to `rts_lock'
Test_stub.c:(.text+0x16): undefined reference to `rts_mkInt32'
Test_stub.c:(.text+0x1d): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text+0x28): undefined reference to `rts_apply'
Test_stub.c:(.text+0x2f): undefined reference to `base_GHCziTopHandler_runIO_closure'
Test_stub.c:(.text+0x3a): undefined reference to `rts_apply'
Test_stub.c:(.text+0x4a): undefined reference to `rts_evalIO'
Test_stub.c:(.text+0x5c): undefined reference to `rts_checkSchedStatus'
Test_stub.c:(.text+0x66): undefined reference to `rts_getInt32'
Test_stub.c:(.text+0x70): undefined reference to `rts_unlock'
Test_stub.o: In function `stginit_export_Test_zdfhsfunzua165':
Test_stub.c:(.text.startup+0x3): undefined reference to `Test_zdfhsfunzua165_closure'
Test_stub.c:(.text.startup+0x8): undefined reference to `getStablePtr'
collect2: ld gab 1 als Ende-Status zurück

Do I have to link the Test_stub.o? If yes, why? And which arguments should I pass to the linker?

4
  • I don't know the details; but you certainly need to link the Haskell runtime too (in particular its garbage collector). Commented Dec 31, 2011 at 19:27
  • 1
    "$ g++ -I/usr/lib/ghc-7.0.3/include -L. -lTest main.cpp" perhaps it works if you put the linker flags at the end of the command line? Commented Dec 31, 2011 at 19:58
  • @Daniel: That's the second time I see someone suggest to put linker flags at the end, and the first time it seemed to solve the problem too. Why's that? I thought the positioning of the flags doesn't matter? Commented Dec 31, 2011 at 20:28
  • @Xeo I don't know if the flag order is important for g++ (except later -O options overriding earlier and such), but it's for gcc, so I thought it's worth a try. As far as I can tell, it didn't solve the problem, what worked was using ghc to do the job, that knows how to call the linker. Commented Dec 31, 2011 at 20:35

1 Answer 1

9

Probably easier than wrestling with g++ is letting ghc do the work,

ghc main.cpp -o hithere -L. -lTest -lstdc++

did the job for me after creating the shared lib the way you did. I have tested it with 7.2.2 and 7.0.2, both worked here.

Sign up to request clarification or add additional context in comments.

3 Comments

The haskell interface is one module of a larger project and I don't want to compile the whole C++ project with ghc.
That makes sense. You could try capturing the necessary linker flags by having ghc compile the example with high enough verbosity and redirecting stderr to a file. It will give you a pretty long list of to-be-linked items, though.
I got it working using your solution. When I pass the parameter -v to ghc, it prints the used linker parameters on the command line.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.