Today I learned something from a nice stranger I met on the internet in the #stackoverflow channel on irc.freenode.net named at cky944.
For a personal project I’m modifying the Hoard memory allocator — a shared object library that application programs can link with to replace memory allocation code (e.g., malloc, free, etc.). Other users of Hoard can attest to the fact that one never explicit uses it, but rather implicit uses it through calls to system provided memory management functions.
Having compiled the application on several flavors of Linux, I proceeded to compile a test application that used my modified version of libhoard.so. I used the following incomplete command to compile the hoard_test application:
$ g++ -lrt -L. -lhoard -o hoard_test hoard_test.cpp
The output test application did not behave as expected, so I used ldd to inspect what shared libraries were linked with the application binary.
$ ldd hoard_test
linux-gate.so.1 => (0x00aa3000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0x008b5000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x0048b000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x0056d000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000)
/lib/ld-linux.so.2 (0x003aa000)
My new friend cky944 had the insight to try explicitly using code that is contained in libhoard.so. He therefore inspected the library with nm -D libhoard.so to see what symbols the shared library exported. Using this information, he identified two functions (hoardstrdup and hoardfree) which he explicitly used in a test case.
#include <stdio.h>
char* hoardstrdup(char* s);
void hoardfree(void* p);
int main(int argc, char** argv) {
char* thing = hoardstrdup(argc < 2 ? "world" : argv[1]);
printf("Hello, %s!\n", thing);
hoardfree(thing);
return 0;
}
Viola! This code worked as expected. But why? Apparently, the latest version of Ubuntu’s linker includes a nifty feature that optimizes unused (not explicitly used in the application’s code) shared libraries out of application binaries (the Ubuntu linker includes the --as-needed option by default). Being that I was using libhoard.so implicitly — I had a problem. This default parameter has the undesirable effect of removing the libhoard.so library from the application binary at link time!
To get around this nifty feature one can notify the linker to include all shared libraries by using the gcc flag -Wl,--no-as-needed. Additionally, including a run-time path for the linker to search for libhoard.so was also necessary. This is documented at GCC’s website and can be achieved by exporting the path to libhoard.so in LD_LIBRARY_PATH or by using -Wl,-R flags.
The final compilation command used to build the test application was as follows. Note, /path/to/dir is the path to the directory where libhoard.so exists (and could have been replaced by exporting the same path into LD_LIBRARY_PATH).
$ g++ -O2 -Wall -o hoard_test hoard_test.cpp \
-Wl,--no-as-needed \
-Wl,-R,/path/to/dir \
-L. -lhoard
Sure enough, this yields a test binary that includes all the references to all of the shared libraries the programmer decided to include regardless of their explicit usage within the application.
$ ldd hoard_test
linux-gate.so.1 => (0x00c63000)
libhoard.so => /export/libhoard.so (0x00709000)
libstdc++.so.6 => /usr/lib/i386-linux-gnu/libstdc++.so.6 (0x00918000)
libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x00a4b000)
libgcc_s.so.1 => /lib/i386-linux-gnu/libgcc_s.so.1 (0x00e16000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000)
libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0x0066a000)
libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0x00c3d000)
/lib/ld-linux.so.2 (0x00f58000)
Again, a very big thank you to cky944 for working tirelessly with me to resolve this mysterious issue (upvote his efforts at www.stackoverflow.com)! =)