dynamic linking test failures
duncan at well-typed.com
Fri May 1 04:33:42 EDT 2009
Currently I'm getting some test failures for WAY=dyn. I'm not posting
the full list at the moment as I'm not convinced it's quite accurate.
I'll post an update later.
I've investigated ffi002. This test ffi exports a Haskell foo function
and calls it from a C main function. Two things go wrong when building
this with -dynamic.
1. We do not compile the _stub.c with -fPIC.
Let's first be clear what is happening:
compile ffi002.hs -> ffi002.o with -dynamic
compile ffi002_c.c -> ffi002_c.o without any special options
compile ffi002_stub.c -> ffi002_stub.o without any special options
link ffi002.o ffi002_c.o ffi002_stub.o with libHSrts.so, etc to make a
binary. The symbol "main" lives in ffi002_c.c.
When compiling the .hs to a .o we have to use -dynamic otherwise the
final binary does not link.
When ghc calls gcc to compile ffi002_stub.c it does not use -fPIC. When
we do this then in the final link we end up with a bad reference to a
symbol from the base lib:
dynamic variable `base_GHCziTopHandler_runIO_closure' is zero size
ffi002_stub.o(.text+0x44): unresolvable R_X86_64_32 relocation against
I'm not sure why this happens. However if we compile ffi002_stub.c using
-fPIC or even -fPIE (or -fpic or -fpie) then it all links fine.
On the other hand it doesn't seem to matter at all that we compile
ffi002_c.c without -fPIC/-fpie even though it also references symbols
from the rts shared lib. The only difference is that
base_GHCziTopHandler_runIO_closure is a symbol to data rather than to a
So, it'd be good if we understood what is going on, but the solution
looks fairly simple, we just have ghc compile the _stub.c file using
-fPIC when ghc itself is invoked with -dynamic (it already does in the
case of -fPIC).
2. The main symbol lives in the libHSrts.so which fails if we're using a
C main function
The C main() function lives in the rts. It initialises the rts and calls
the Haskell Main.main function (that is, the rts embeds a reference to a
symbol that will be found in code we compile and link later).
Now ghc also supports -no-hs-main. That lets us link using ghc but have
main() in a C file that we wrote. Normally with static libs everything
works out ok. That's because the linker picks the main symbol from our
local .o file and ignores the main from the rts. In fact it doesn't even
include the main from the rts into the final binary. That works
perfectly because if it did, then there would be undefined symbols
because remember that the main in the rts refers to the Haskell
Main.main which doesn't necessarily exist.
But, with shared libs we can't just not include the main from the rts
into the final binary. The libHSrts.so was linked already and we us the
whole thing rather than copying bits out of it as we do with .a static
libs. So now we have the problem that the rts.so refers to the Haskell
Main.main which really does not exist. And so we cannot link our program
(if we try and cheat the static linker then we just get caught by the
dynamic linker, which is fair enough, after all the dynamic linker
doesn't know that nobody is ever going to call the main in the rts.).
So we need another approach for where to put main. Ian suggests that we
put it in the object file for the Main module when compiling it. Or we
could do what gcc does which is to keep a special .o file around which
contains just the initialisation code (the real OS entry point that goes
on to call main) and link that in unless using -no-hs-main.
This not just a problem for the rare programs where we want a C main. It
also means we cannot have Haskell shared libs that we load up as plugins
in other program (unless we make a dummy Main.main). So this is
something I'll be trying to solve.
More information about the Cvs-ghc