The LD_PRELOAD trick

If you ever wanted to replace some symbol in a library, in order to inspect what happens in a binary you don’t have the source code or even to inject some code, this can be done using the LD_PRELOAD trick.

To use it, invoke your binary as such: $ LD_PRELOAD=somelib.so executable.

Let’s look at a simple example. Imagine we want to keep track of the calls to the libc’ open function. Its prototype is int open(const char *pathname, int flags). So we start by writing a similar function:

int open(const char *pathname, int flags)
{
    (void)pathname;
    (void)flags;

    printf("Using open function\n");
}

This will however break the program since we do not recall the real open function. To do so, we need to acquire a function pointer to the original symbol. We do so, by using the dlsym function, with RTLD as a first argument.

The function dlsym() takes a “handle” of a dynamic library returned by dlopen() and the null-terminated symbol name, returning the address where that symbol is loaded into memory. If the symbol is not found, in the specified library or any of the libraries that were automatically loaded by dlopen() when that library was loaded, dlsym() returns NULL. (The search performed by dlsym() is breadth first through the dependency tree of these libraries.) Since the value of the symbol could actually be NULL (so that a NULL return from dlsym() need not indicate an error), the correct way to test for an error is to call dlerror() to clear any old error conditions, then call dlsym(), and then call dlerror() again, saving its return value into a variable, and check whether this saved value is not NULL.

There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find the first occurrence of the desired symbol using the default library search order. The latter will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library.

#include <dlfcn.h>
 
typedef int (*orig_open_f_type)(const char *pathname, int flags);
 
int open(const char *pathname, int flags)
{
    printf("Using open function\n");
 
    orig_open_f_type orig_open;
    orig_open = (orig_open_f_type)dlsym(RTLD_NEXT,"open");
    return orig_open(pathname,flags);
}

This trick is used by the quadifier project in order to turn mono double buffer OpenGL code, into stereo quad buffer OpenGL code, for use in our immersive space.

The MacOSX equivalent of LD_PRELOAD is DYLD_INSERT_LIBRARIES.

Leave a Reply

Your email address will not be published.