Calling dynamically linked Haskell code from Rust
You mention two different final link commands,
ghc -o libfibonacci.so -shared -dynamic -fPIC Fibonacci.o wrapper.o -lHSrts
and
ghc -o libfibonacci.so -shared -static haskell/Fibonacci.o haskell/wrapper.o -lHSrts
It might be worth explicitly describing what some of these flags mean.
-shared
tells ghc to produce a shared object (rather than an executable).-dynamic
tells ghc to link the output against the dynamic library versions of its Haskell dependencies (base, ghc-prim, etc.)-static
is the opposite of-dynamic
, it tells ghc to link against the static library versions of Haskell dependencies.-lHSrts
means to link against the (static or shared) library libHSrts. But in GHC, only the static library actually has the basename libHSrts (so that the library file name islibHSrts.a
). The shared library version has file namelibHSrts-ghc7.8.4.so
(adjust for your GHC version). So,-lHSrts
really means to link against the static library version of the RTS.
So the second command is linking against the static versions of all Haskell dependencies, including the RTS. This may work on OS X where all code must be generated as PIC, but it won't work on a normal Linux binary distribution of GHC because a shared library must be PIC code, but the static Haskell libraries shipped with GHC are built as non-PIC (they are intended to be linked into non-relocatable executables). I don't totally understand why GHC is not smart enough to add -lffi
itself here, possibly it doesn't really expect this combination of options since it won't work on a normal Linux setup.
The first command is odd, because you are linking statically against the RTS, but dynamically against all other Haskell dependencies. If you change the library name in the -l
option to -lHSrts-ghc7.8.4
, then things will Just Work on Linux, and probably everywhere else (other than Windows).
When you compile your shared library, it looks like you need to link against libffi
as well:
ghc -o libfibonacci.dylib -shared -dynamic -fPIC \
Fibonacci.hs wrapper.c -lHSrts -lffi
I deduced this by going into my GHC library directory (/usr/local/lib/ghc-7.10.1/rts
) and then grepping for the symbol ffi_call
:
$ grep -lRa ffi_call .
./include/ffi.h
./rts/libHSrts-ghc7.10.1.dylib
...
I then used nm
to find which exact library had it:
for i in *dylib; do
if nm $i | grep -q 'T.*ffi_call'; then
echo "== $i";
fi;
done
I was then able to run with:
DYLD_LIBRARY_PATH='.' ./main
Unfortunately, it appears your code isn't quite right, as I just get a bunch of empty tuples. You forgot to have a return type on the function, and then you run into a problem that the 46th or so Fibbonacci is too big for a u32
.
Additionally, you should be using the types from the libc
package, and it may be safest to use a u64
here.
I have installed GHC 7.10.1 using Homebrew, but hopefully the same pattern would work elsewhere.