Back at MacHack 2003 Jonathan Rentzsch talked about how to override functions and inject code in Mac OS X using [several neat tricks](http://rentzsch.com/papers/overridingMacOSX). He also released a framework called [mach_star](http://rentzsch.com/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 <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 <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!