
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 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... ] 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. Let us know how you get on... Cheers, Simon

On Mon, 2 Sep 2002 11:31:27 +0100
"Simon Marlow"
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 gives: /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. Duncan

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
Great stuff! I've just linked a copy of GHCi that doesn't need to load up the base package when it starts :-) However, the binary is ~15Mb compared to ~8Mb normally.
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.
I couldn't get it to work either - although I didn't get any duplicate symbols when linking, it seems the resulting binary didn't have all the symbol information from the library in it. It looks like -export-dynamic doesn't work properly with --whole-archive.
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
gives: /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
I'm not sure exactly what's going here. RtsAPIDeprec.o does refer to Addr_Azh_con_info, which is defined in the Addr module in the lang package. However, RtsAPIDeprec normally isn't linked in, because nothing refers to anything in it.
So generally quite sucessful. I should probably come up with some tutorial / documentation on doing plugins with ghc.
That's a good plan. We can probably make it easier too, perhaps by providing a flag to do the right magic at link-time. I need to think about this some more. Cheers, Simon

On Mon, 2 Sep 2002 15:16:37 +0100
"Simon Marlow"
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.
I couldn't get it to work either - although I didn't get any duplicate symbols when linking, it seems the resulting binary didn't have all the symbol information from the library in it. It looks like -export-dynamic doesn't work properly with --whole-archive.
I discovered that just giving -optl--whole-archive.didn't actually pass it on to the linker for some reason. If you use -optl-verbose it'll tell you exaclty what's going on, and --whole-archive was missing.
I'm not sure exactly what's going here. RtsAPIDeprec.o does refer to Addr_Azh_con_info, which is defined in the Addr module in the lang package. However, RtsAPIDeprec normally isn't linked in, because nothing refers to anything in it.
If anyone's interested, in dynamic linking in general or this poroblem in particular my code is here (including a makefile): http://charlesstreet22.force9.co.uk/~duncan/projects/dynlink/
That's a good plan. We can probably make it easier too, perhaps by providing a flag to do the right magic at link-time. I need to think about this some more.
Linking in HSbase.o directly is *very* slow. I don't know enough about linking to know why. Duncan

There are lots of recursive symbol interdependencies, and it just takes a while to resolve them all. On Monday 02 September 2002 09:13, Duncan Coutts wrote:
On Mon, 2 Sep 2002 15:16:37 +0100
"Simon Marlow"
wrote: 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.
I couldn't get it to work either - although I didn't get any duplicate symbols when linking, it seems the resulting binary didn't have all the symbol information from the library in it. It looks like -export-dynamic doesn't work properly with --whole-archive.
I discovered that just giving -optl--whole-archive.didn't actually pass it on to the linker for some reason. If you use -optl-verbose it'll tell you exaclty what's going on, and --whole-archive was missing.
I'm not sure exactly what's going here. RtsAPIDeprec.o does refer to Addr_Azh_con_info, which is defined in the Addr module in the lang package. However, RtsAPIDeprec normally isn't linked in, because nothing refers to anything in it.
If anyone's interested, in dynamic linking in general or this poroblem in particular my code is here (including a makefile): http://charlesstreet22.force9.co.uk/~duncan/projects/dynlink/
That's a good plan. We can probably make it easier too, perhaps by providing a flag to do the right magic at link-time. I need to think about this some more.
Linking in HSbase.o directly is *very* slow. I don't know enough about linking to know why.
Duncan _______________________________________________ Glasgow-haskell-users mailing list Glasgow-haskell-users@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
-- ----------------------------------- Seth Kurtzberg M. I. S. Corp. 1-480-661-1849

Duncan Coutts
Linking in HSbase.o directly is *very* slow. I don't know enough about linking to know why.
The usual way to slow a linker down is to put all the files concerned on an NFS-mounted filesystem (as opposed to a local disk). The usual way to speed one up is to find a memory disk and put all the files there. For example, FreeBSD often puts /tmp in a memory disk. If you have plenty of RAM, all accesses to /tmp are to memory. Only if you're low on RAM, do the fiel accesses actually hit a physical disk. I think Linux has memory disks too. (There's also RAM disks but those are different beasts. If I understand them correctly, they cannot be swapped out to disk.) -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/

On Mon, Sep 02, 2002 at 03:16:37 +0100, Simon Marlow wrote:
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
gives: /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
I'm not sure exactly what's going here. RtsAPIDeprec.o does refer to Addr_Azh_con_info, which is defined in the Addr module in the lang package. However, RtsAPIDeprec normally isn't linked in, because nothing refers to anything in it.
RtsAPIDeprec.o is in libHSrts.a (as the error message says).
Therefore using libHSrts.a means that you'll get all of
RtsAPIDeprec.o along with it, and therefore need to resolve all
its unresolved symbols.
I think I also understand why using -package doesn't work. I'll
paste in two (cropped) gcc lines below; the first line is when
you use the "-lHSrts -lHSlang" options (which works), and the
second is when you use "-package rts -package -lang" (which
doesn't work):
... -L/usr/lib/ghc-5.04 -L/usr/lib/gcc-lib/i386-linux/3.2 -L/usr/lib/gcc-lib/i386-linux/3.2/../../.. Main.o -ldl -lHSlang -lHSlang_cbits -lHShaskell98 -lHSbase -lHSbase_cbits -lHSrts -lgmp -lm -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-linux/3.2/crtend.o /usr/lib/gcc-lib/i386-linux/3.2/../../../crtn.o
... -L/usr/lib/ghc-5.04/ -L/usr/lib/ghc-5.04 -L/usr/lib/gcc-lib/i386-linux/3.2 -L/usr/lib/gcc-lib/i386-linux/3.2/../../.. /usr/lib/ghc-5.04/HSbase.o Main.o -ldl -lHSrts -lHSlang -lHShaskell98 -lHSbase -lHSbase_cbits -lHSrts -lgmp -lm -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i386-linux/3.2/crtend.o /usr/lib/gcc-lib/i386-linux/3.2/../../../crtn.o
.. so the only real difference between the line which works and
the line which doesn't work is:
* line which works has /usr/lib/ghc-5.04/HSbase.o
* line which works has an extra -lHSrts flag
It turns out that the pedant linker needs to see the -lHSrts flag
before -lHSlang. This is a bit odd, since the HSlang library is
the one which has the required "Addr_Azh_con_info" symbol. Of
course, ld(1) says:
-( archives -)
--start-group archives --end-group
The archives should be a list of archive files. They may be
either explicit file names, or -l options.
The specified archives are searched repeatedly until no new
undefined references are created. Normally, an archive is
searched only once in the order that it is specified on the
command line. If a symbol in that archive is needed to
resolve an undefined symbol referred to by an object in an
archive that appears later on the command line, the linker
would not be able to resolve that reference. By grouping
the archives, they all be searched repeatedly until all
possible references are resolved.
Using this option has a significant performance cost. It
is best to use it only when there are unavoidable circular
references between two or more archives.
I'd classify this as an ld problem, but we still have to work
around it.
Aaaaanyway, using either "-package lang -package rts" or
"-package rts -package lang" doesn't affect the order of the -l
options, so we probably have to whack it in there manually.
--
#ozone/algorithm

On Mon, Sep 02, 2002 at 03:16:37PM +0100, Simon Marlow wrote:
Great stuff! I've just linked a copy of GHCi that doesn't need to load up the base package when it starts :-) However, the binary is ~15Mb compared to ~8Mb normally.
Does it work on Windows as well? (I'm presuming that you tried
it under Linux.) It would rock if it did!
--
#ozone/algorithm

On Mon, Sep 02, 2002 at 03:16:37PM +0100, Simon Marlow wrote:
Great stuff! I've just linked a copy of GHCi that doesn't need to load up the base package when it starts :-) However, the binary is ~15Mb compared to ~8Mb normally.
BTW, I just ran a strip --strip-unneeded on Duncan's TestDynLink
program, and it reduced the file size a fair bit:
-rwxrwx--- 1 andrep andrep 5267252 2002-09-04 16:07 TestDynLink*
-rwxrwx--- 1 andrep andrep 7186984 2002-09-04 16:07 TestDynLink-nostrip*
.. and the quick test still seems to work:
16:07 ~/projects/haskell_dynamic_linking/dynlink % ./TestDynLink
GHC dynamic loading test program
c_loadObj pluginObj returned 1
c_resolveObjs returned 1
lookup symbol:
TestObj_zdgfromMyBool_info
symbol is present
(Sorry for the flood of posts, but I'm pretty excited that we've
all managed to get this working!)
--
#ozone/algorithm
participants (5)
-
Alastair Reid
-
Andre Pang
-
Duncan Coutts
-
Seth Kurtzberg
-
Simon Marlow