I need to write a little library, to be loaded by setting LD_PRELOAD, and which will override some functions in the standard C library but also call those functions in the standard library. Specifically, I want my shiny new library to let users provide their own resolv.conf so they can, eg, specify their default domain, or set aliases in it for long hostnames. I'm going to need a good understanding of how the linker works if I'm to do this, so can anyone point me at some good documentation, that doesn't assume that I know it all already and just need a reference, but also doesn't assume that I'm a complete idiot?
It sounds like your interested in dynamic loading rather than linking in general. That's how ODBC finds and uses a particular driver at run time, like what you want to do with your resolver.
Rather than google you up a random link, I'll give you some keywords on dynamic linking:
Concepts: "dynamic loading" "Position Independent Code" soname
Tools: LD_DEBUG ldconfig ldd nm objdump
The C API for dynamic loading: dlfcn.h dlopen dlsym dlerror dlclose
I did find one good tutorial, which I remember being helpful when I was working with ODBC: http://www.ibm.com/developerworks/library/l-shobj/
However, it recommends calling ld directly, which is a no-no with gcc. Instead use the -shared option:
g++ -shared -fPIC foo.c -o libfoo.so.1.0 -Wl,soname,libfoo.so.1
Also: You should look for solutions that do not involve LD_PRELOAD or LD_LIBRARY_PATH, which are basically debug tools, and libraries that require them in production are flawed: Why LD_LIBRARY_PATH is bad LD_LIBRARY_PATH Is Not The Answer LD_LIBRARY_PATH - just say no
ODBC avoids it with an odbc.ini config file where you specify the full path to the .so to use.
If you are using the GNU linker, one thing you need to know about is RTLD_NEXT. This allows you to "find the next occurrence of a function in the search order after the current library".
This way, you can override a standard function and still be able to call the original version.
I used this function in a version of malloc that tracks allocation stats. I needed to override malloc to collect the stats but I still wanted the original version of malloc to do the allocation:
static void *(*next_malloc)(size_t) = NULL;
__attribute__ ((constructor))
static void
bootstrap()
{
next_malloc = dlsym(RTLD_NEXT, "malloc");
}
void *
malloc(size_t size)
{
void *ptr = next_malloc(size);
// collect some stats
return ptr;
}
Several good answers, but no one's mentioned the one resource that led me through this: Building library interposers for fun and profit.