Overriding library functions in Mac OS X, the easy way: DYLD_INSERT_LIBRARIES

Back at MacHack 2003 Jonathan Rentzsch talked about how to override functions and inject code in Mac OS X using several neat tricks. He also released a framework called mach_star which has two components: mach_override and mach_inject. These are great, but overkill for some simple cases.

A much easier way of doing library function overrides is using the DYLD_INSERT_LIBRARIES environment variable (analogous to LD_PRELOAD on Linux). The concept is simple: at load time the dynamic linker (dyld) will load any dynamic libraries specified in DYLD_INSERT_LIBRARIES before any libraries the executable wants loaded. By naming a function the same as one in a library function it will override any calls to the original.

The original function is also loaded, and can be retrieved using the dlsym(RTLD_NEXT, “function_name”); function. This allows a simple method of wrapping existing library functions.

Here’s a simple example which prints out the path of every file opened using the “fopen” function (lib_overrides.c):

#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>

// for caching the original fopen implementation
FILE * (*original_fopen) (const char *, const char *) = NULL;

// our fopen override implmentation
FILE * fopen(const char * filename, const char * mode)
{
    // if we haven’t already, retrieve the original fopen implementation
    if (!original_fopen)
        original_fopen = dlsym(RTLD_NEXT, "fopen");

    // do our own processing; in this case just print the parameters
    printf("== fopen: {%s,%s} ==\n", filename, mode);
    
    // call the original fopen with the same arugments
    FILE* f = original_fopen(filename, mode);
    
    // return the result
    return f;
}

And a simple test program (overrides_test.c):

#include <stdio.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    char hello[] = "hello world";
    
    FILE *fp = fopen("hello.txt", "w");
   
    if (fp) {
        fwrite(hello, 1, strlen(hello), fp);
        fclose(fp);
    }

    return 0;
}

Compiled and tested:

tlrobinson$ gcc -Wall -o lib_overrides.dylib -dynamiclib lib_overrides.c
tlrobinson$ gcc -Wall -o overrides_test overrides_test.c
tlrobinson$ DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=lib_overrides.dylib overrides_test
== fopen: {hello.txt,w} ==
tlrobinson$

There are certainly scenarios far more interesting than this, though!

blog comments powered by Disqus


Warning: include(/home/tlrobinson/tlrobinson.net/_footer-analytics.php) [function.include]: failed to open stream: No such file or directory in /home/tlrobinson/tlrobinson.net/blog/wp-content/themes/clean-look-150/footer.php on line 13

Warning: include() [function.include]: Failed opening '/home/tlrobinson/tlrobinson.net/_footer-analytics.php' for inclusion (include_path='.:/usr/local/lib/php:/usr/local/php5/lib/pear') in /home/tlrobinson/tlrobinson.net/blog/wp-content/themes/clean-look-150/footer.php on line 13