27

Hey, I'm learning Haskell and I'm interested in using it to make static libraries for using in Python and probably C. After some googling I found out how to get GHC to output a shared object, but it dynamically depends on GHC`s libraries. The resulting ELF from compiling in GHC is dynamically dependand only on C libs and it's a bit under a MB in size - it has been statically linked with GHC`s libs. How and if can this be achieved for shared objects?

Example of current state:

$ ghc --make -dynamic -shared -fPIC foo.hs -o libfoo.so
$ ldd libfoo.so
    linux-vdso.so.1 =>  (0x00007fff125ff000)
    libHSbase-4.2.0.2-ghc6.12.3.so => /usr/lib/ghc-6.12.3/base-4.2.0.2/libHSbase-4.2.0.2-ghc6.12.3.so (0x00007f7d5fcbe000)
    libHSinteger-gmp-0.2.0.1-ghc6.12.3.so => /usr/lib/ghc-6.12.3/integer-gmp-0.2.0.1/libHSinteger-gmp-0.2.0.1-ghc6.12.3.so (0x00007f7d5faac000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f7d5f816000)
    libHSghc-prim-0.2.0.0-ghc6.12.3.so => /usr/lib/ghc-6.12.3/ghc-prim-0.2.0.0/libHSghc-prim-0.2.0.0-ghc6.12.3.so (0x00007f7d5f591000)
    libHSffi-ghc6.12.3.so => /usr/lib/ghc-6.12.3/libHSffi-ghc6.12.3.so (0x00007f7d5f383000)
    libc.so.6 => /lib/libc.so.6 (0x00007f7d5f022000)
    /lib/ld-linux-x86-64.so.2 (0x00007f7d60661000)

$ ghc foo.hs
$ ldd foo
    linux-vdso.so.1 =>  (0x00007fff2d3ff000)
    libgmp.so.10 => /usr/lib/libgmp.so.10 (0x00007f50014ec000)
    libm.so.6 => /lib/libm.so.6 (0x00007f5001269000)
    librt.so.1 => /lib/librt.so.1 (0x00007f5001061000)
    libdl.so.2 => /lib/libdl.so.2 (0x00007f5000e5d000)
    libc.so.6 => /lib/libc.so.6 (0x00007f5000afc000)
    libpthread.so.0 => /lib/libpthread.so.0 (0x00007f50008df000)
    /lib/ld-linux-x86-64.so.2 (0x00007f5001759000)

If I try to compile it with(without '-dynamic'):

$ ghc --make -shared -fPIC foo.hs -o libfoo.so
Linking libfoo.so ...
/usr/bin/ld: foo.o: relocation R_X86_64_32S against `stg_CAF_BLACKHOLE_info' can not be used when making a shared object; recompile with -fPIC
foo.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

When googling I found something about this whole issue - that it may come from the fact that GHC is compiled in a specific way(dynamic/static?) and so static linking is not possible. If this is true how is it possible that the ELF binary is statically linked?

Anyway, I am hoping someone can shed some light on this since a huge amount of googling left me with more questions than I started with.

Huge thanks.

kuratkull
  • 327
  • 3
  • 9
  • What system do you use? It appears that it's x86_64 Linux. GHC version is also important, since it might actually be a bug that has been fixed since. Dynamic linking has had some bugs in the past and it might be one of them. – Tener Mar 01 '11 at 22:45
  • @Tener | Glasgow Haskell Compiler, Version 6.12.3, for Haskell 98, stage 2 booted by GHC version 6.12.1 | Maybe I should try GHC 7 to see if it has this problem covered. – kuratkull Mar 02 '11 at 23:09
  • @Tener. I got around to installing GHC7, and it's still not working, although it's giving me a bit different error. =>>> `--> ghc --make -shared -fPIC bwt.hs -o libbwt.so [1 of 1] Compiling Main ( bwt.hs, bwt.o ) Linking libbwt.so ... /usr/bin/ld: /usr/lib/ghc-7.0.2/base-4.3.1.0/libHSbase-4.3.1.0.a(Base__90.o): relocation R_X86_64_32S against "stg_upd_frame_info" can not be used when making a shared object; recompile with -fPIC /usr/lib/ghc-7.0.2/base-4.3.1.0/libHSbase-4.3.1.0.a: could not read symbols: Bad value collect2: ld returned 1 exit status` – kuratkull Mar 15 '11 at 19:46
  • @kuratkull No news about this problem? I have the same wish. – Stéphane Laurent Mar 20 '17 at 17:40

2 Answers2

5

The canonical way of is this:

  1. Export the functions (via FFI) to initialise RTS (runtime system) by the foreign program
  2. Export actual functions you would like to implement in Haskell

The following sections of manual describe this: [1] [2]

On the other way, you can try technique described in this blog post (which mine, by the way):

http://mostlycode.wordpress.com/2010/01/03/shared-haskell-so-library-with-ghc-6-10-4-and-cabal/

It boils down to creating a small C file which is called automatically right after a library is loaded. It should be linked together into the library.

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

#include

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 ();
}

Edit: Original blog post describing this technique is this: http://weblog.haskell.cz/pivnik/building-a-shared-library-in-haskell/

Tener
  • 5,280
  • 4
  • 25
  • 44
  • 3
    Thanks, but neither of these methods work(same errors) and it's probably due to this(found in one of the links you provided): "On most platforms however that would require all the static libraries to have been built with -fPIC so that the code is suitable to include into a shared library and we do not do that at the moment." I can assume that I would have to recompile GHC with -fPIC manually to solve my problem - otherwise it's just not possible. I will mark you answer as a correct answer, since all this _should_ work after recompiling GHC. Thanks a lot :) – kuratkull Mar 20 '11 at 16:08
1

This makes ghc compile statically (note that the pthread is before optl-static): ghc --make -static -optl-pthread -optl-static test.hs

Edit: But the static compilation seems to be a bit risky. Most of the times there are some errors. And on my x64 fedora it doesn't work at all. The resulting binary is also quite large, 1.5M for main = putStrLn "hello world"

Masse
  • 4,334
  • 3
  • 30
  • 41
  • 5
    But this makes the executables static, I want to have static libraries(*.so/*.a). I tried to use the '-shared' flag in there but I got: /usr/bin/ld: /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/crtbeginT.o: relocation R_X86_64_32 against `__DTOR_END__' can not be used when making a shared object; recompile with -fPIC /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/crtbeginT.o: could not read symbols: Bad value. Seems like making static libraries may not be so easy after all. Or am I doing something wrong? – kuratkull Feb 28 '11 at 11:52