Due to some developing I needed a "lighttpd":http://www.lighttpd.net with modmagnet enabled. modmagnet is a module which allows inserting of lua code into the request processing stream. This is a cool feature, and I was pleased to see that
- lighttpd is on the standard Solaris install DVD
- mod_magnet is provided
Of course there is this small problem:
2009-12-29 23:29:31: (plugin.c.165) dlopen() failed for: /usr/lighttpd/1.4/lib/mod_magnet.soi
ld.so.1: lighttpd: fatal: relocation error: file /usr/lighttpd/1.4/lib/mod_magnet.so:
symbol luaL_checklstring: referenced symbol not found
What this means is that there are unresolved symbols remaining in the code after the dymanic loader has done it's work, which should not happen. Let's look a the dynamic deps of the module.
$ ldd /usr/lighttpd/1.4/lib/mod_magnet.so
libsendfile.so.1 => /lib/libsendfile.so.1
libm.so.2 => /lib/libm.so.2
libresolv.so.2 => /lib/libresolv.so.2
libnsl.so.1 => /lib/libnsl.so.1
libsocket.so.1 => /lib/libsocket.so.1
libc.so.1 => /lib/libc.so.1
libmd.so.1 => /lib/libmd.so.1
libmp.so.2 => /lib/libmp.so.2
libscf.so.1 => /lib/libscf.so.1
libuutil.so.1 => /lib/libuutil.so.1
libgen.so.1 => /lib/libgen.so.1
libsmbios.so.1 => /usr/lib/libsmbios.so.1
Judging from the name of the missing symbol luaL_checklstring
it ought to come from some kind of lua library. But the listing above does not show any missing libraries, lest of all a lua one.
So what happened?
Somehow (and I have no idea how) Sun managed to build a mod_magnet without linking it to the lua libraries at the end. Simply speaking, this is broken.
Fortunately there is a way to fix this. Sun provides a utility called elfedit(1)
which allows the editing of ELF file headers (like shared libraries). The lua library which provides the missing symbols is called liblua.so
(no version information). The type of record in an ELF header which denotes the dynamic libraries needed is called DT_NEEDED. elfedit(1)
takes two parameters: the file to edit, and the file into which to write the modified version.
First show the existing DT_NEEDED records.
$ elfedit mod_magnet.so mod_magnet2.so
> dyn:value DT_NEEDED
index tag value
[0] NEEDED 0x5f9 libsendfile.so.1
[1] NEEDED 0x60a libm.so.2
[2] NEEDED 0x614 libresolv.so.2
[3] NEEDED 0x623 libnsl.so.1
[4] NEEDED 0x62f libsocket.so.1
[5] NEEDED 0x5d3 libc.so.1
This is basically the same list as above, with liblua.so notably lacking. Now add a new entry:
> dyn:value -add -s DT_NEEDED liblua.so
index tag value
[34] NEEDED 0x63e liblua.so
Now look at the new table, and save it.
> dyn:value DT_NEEDED
index tag value
[0] NEEDED 0x5f9 libsendfile.so.1
[1] NEEDED 0x60a libm.so.2
[2] NEEDED 0x614 libresolv.so.2
[3] NEEDED 0x623 libnsl.so.1
[4] NEEDED 0x62f libsocket.so.1
[5] NEEDED 0x5d3 libc.so.1
[34] NEEDED 0x63e liblua.so
> :write
> :quit
Looking at the ldd(1)
output, just to be sure.
$ ldd ./mod_magnet2.so
libsendfile.so.1 => /lib/libsendfile.so.1
libm.so.2 => /lib/libm.so.2
libresolv.so.2 => /lib/libresolv.so.2
libnsl.so.1 => /lib/libnsl.so.1
libsocket.so.1 => /lib/libsocket.so.1
libc.so.1 => /lib/libc.so.1
liblua.so => /usr/lib/liblua.so
libmd.so.1 => /lib/libmd.so.1
libmp.so.2 => /lib/libmp.so.2
libscf.so.1 => /lib/libscf.so.1
libdl.so.1 => /lib/libdl.so.1
libuutil.so.1 => /lib/libuutil.so.1
libgen.so.1 => /lib/libgen.so.1
libsmbios.so.1 => /usr/lib/libsmbios.so.1
Now the linker picks up the lua libraries. If the modified modmagnet.so is now put back into /usr/lighttpd/1.4/lib
, lighttpd will start and modmagnet will work.
Now, this wasn't so hard, was it?