Dynamic linking again

Duncan Coutts duncan@coutts.uklinux.net
Mon, 2 Sep 2002 14:13:21 +0100

On Mon, 2 Sep 2002 11:31:27 +0100
"Simon Marlow" <simonmar@microsoft.com> wrote:

> > So here's another idea. ghc's linker provides (via the ffi) a command
> > addDLL to load dynamic libraries. It uses dlopen on unix and 
> > LoadLibrary on win32. 
> > 
> > Now, with dlopen if you pass it NULL rather than the filename of a
> > library it will give you back a handle to itself - the program that
> > called it. If you've linked your program with the right incantations
> > this gives you access to all the symbols in the main program.
> Yes, we already use dlopen() with NULL to get symbols from other shared
> libraries, so you can refer to things like printf() from libc.

I noticed this just after posting, so you don't even need to do call addDLL,
just doing initLinker is sufficient.

> I didn't know about -export-dynamic: yes, it looks like that would do
> exactly the right thing.  The linker might not even need any
> modification for this to work, because we already fall back to using
> dlsym() for a symbol that the linker can't find in its own symbol table.

> [... I've just tried a simple example using -export-dynamic, and it does
> indeed work as advertised... ]

In fact it all works quite nicely, I've got a test program with a little
plugin module that refers back to the main program. When the main
program is linked with -optl-export-dynamic the plugin can be loaded up
and all symbols resolve correctly. I still had to load copies of the
base packages since the plugin .o still refers to symbols not in the
main executalbe. (More on that in a moment)

And yes indeed, it works with a vanilla ghc-5.04

> To like the whole of the base package into the binary, you can either
> use --whole-archive with libHSbase.a as suggested by Alastair, or link
> in HSbase.o.  Similarly for the haskell98 package.

I got lots of linking errors about duplicate symbols doing using
--whole-archive for the standard packages. Perhaps someone who knows a
bit more about linking could advise me.

It works linking in HSbase.o however. Here's the output of the test
program. It loads the .o file of a plugin module that imports things
from the main program:

[duncan@dunky dlink]$ ./TestDLink 
GHC dynamic loading test program
c_loadObj pluginObj returned 1
c_resolveObjs returned 1
lookup symbol:
> TestObj_foo_closure
  symbol is present

where "TestObj_foo_closure" is a symbol corresponding to my function
"foo" defined to be a function imported from the main program. Note here
that I am not loading a copy of the base package at runtime, it is
resolving against the copy already in the main executable immage. Of
course the main program's binary is now much larger ~5mb stripped vs
350k. Note that the program can be striped because the symbls are in a
special dynamic export ELF section so do not get stripped.

I'm having a problem using just -package flags. I get unresloved
symbols. I'm currently doing this:

ghc -optl-export-dynamic  -ldl -L/usr/lib/ghc-5.04/ -lHSrts -lHSlang  \
  TestDLink.o -o TestDLink

since doing

ghc -optl-export-dynamic  -ldl -package rst -package lang  \
  TestDLink.o -o TestDLink

/usr/lib/ghc-5.04/libHSrts.a(RtsAPIDeprec.o): In function `rts_mkAddr':
RtsAPIDeprec.o(.text+0x14): undefined reference to `Addr_Azh_con_info'
/usr/lib/ghc-5.04/libHSrts.a(RtsAPIDeprec.o): In function `rts_getAddr':
RtsAPIDeprec.o(.text+0x2d): undefined reference to `Addr_Azh_con_info'
RtsAPIDeprec.o(.text+0x35): undefined reference to `Addr_Azh_static_info'
collect2: ld returned 1 exit status

the flag -ldl is required to aviod link errors about dlopen() and friends.

So generally quite sucessful. I should probably come up with some
tutorial / documentation on doing plugins with ghc.