views:

62

answers:

3

Hi ;-)

I have build a little patch to append to a certain application and trace the invocations of some functions. Among them, malloc() and open(). I am using dlsym to store the pointer to the original symbol and replace the function name with my own. It compiles -and works- perfectly under linux. Here's the code:

#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <dlfcn.h>

/**
 * Interponemos nuestra funcion open
 * 
 * @param   char*    filename
 * @param   int      flags
 **/

int open(char * filename, int flags)
{
    static int (*real_open)(char*, int) = NULL;
    if (!real_open)
        real_open = dlsym(RTLD_NEXT, "open");

    // Entero
    int p = real_open(filename, flags);
    fprintf(stderr, "Abrimos %s) = %i\n", filename, flags);

    // Devolvemos
    return p;
}

//--------------------------------------------------------

/**
 * Interponemos nuestra funcion malloc
 * 
 * @param   size_t    size
 */

void* malloc(size_t size)
{
    static void* (*real_malloc)(size_t) = NULL;
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");

    void *p = real_malloc(size);

    // Memoria reservada
    fprintf(stderr, "Reserva de memoria (%d) = %p\n", size, p);

    // Devolvemos
    return p;
}

Then, I compile it with the following instruction, creating a pi.so.

gcc -Wall -O2 -fPIC -shared -ldl -o pi.so pi.c 

And then, I use the LD_PRELOAD directive to inject it into any app.

LD_PRELOAD=/home/.../injection/pi.so <binary>

And it worjks marvellous under Linux! But when I get back home and try to compile it using GCC under Mac, it fails to compile and the LD_PRELOAd directive does not work. What should I change? Thank you very much.

+1  A: 

The Mac OS X (10.6.4) does not contain any references to LD_PRELOAD - so it is ignored, and hence your attempt to interpose your code fails. All the world is not the same as Linux.

Looking through the man pages, I found:

$ man dyld

...

DYLD_INSERT_LIBRARIES This is a colon separated list of dynamic libraries to load before the ones specified in the program. This lets you test new modules of existing dynamic shared libraries that are used in flat-namespace images by loading a temporary dynamic shared library with just the new modules. Note that this has no effect on images built a two-level namespace images using a dynamic shared library unless DYLD_FORCE_FLAT_NAMESPACE is also used.

Jonathan Leffler
+1  A: 

For OSX you'd have to use the DYLD_INSERT_LIBRARIES env. variable instead of LD_PRELOAD.

However I'm fairly certain you won't get the overriding to work. The closest thing I can find is you'll have to do some magic assembly tricks, which someone has neatly packaged up in the code under mach_override here: http://extendamac.svn.sourceforge.net/viewvc/extendamac/trunk/code/

nos
Hi :-) Really? Is that complicated under mac os x? I have downloaded mach_override and I am studying the source code but I don't understand how to use it yet. The package does not contain text documentation :P Thank you anyway. Any examples of use are welcomed :P
flaab
http://github.com/rentzsch/mach_star seems to be easier
flaab
+2  A: 

On mach, you have to use DYLD_INSERT_LIBRARIES macro instead of LD_PRELOAD to specify a list of shared libraries to pre-load (1...*).

By default, standard functions are not getting replaced with functions from the shared objects just because they have the same names. You have to explicitly state what function overrides what using DYLD_INTERPOSE macro. It allows you to declare a replacement function with any name (for example, override open with my_open) and be able to call original function you are overriding. In addition to that, you don't have to resolve addresses of real functions manually.

Alternatively, to achieve Linux-like behavior you have to define DYLD_FORCE_FLAT_NAMESPACE macro.

There is a lot more going on, see dyld manual for details.

So your code should look something like this:

#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <mach-o/dyld-interposing.h>

/**
 * Interponemos nuestra funcion open
 * 
 * @param   char*    filename
 * @param   int      flags
 **/

int my_open(char * filename, int flags)
{
    // Entero
    int p = open(filename, flags);
    fprintf(stderr, "Abrimos %s) = %i\n", filename, flags);

    // Devolvemos
    return p;
}
DYLD_INTERPOSE(my_open, open)

//--------------------------------------------------------

/**
 * Interponemos nuestra funcion malloc
 * 
 * @param   size_t    size
 */

void* my_malloc(size_t size)
{
    void *p = malloc(size);

    // Memoria reservada
    fprintf(stderr, "Reserva de memoria (%d) = %p\n", size, p);

    // Devolvemos
    return p;
}
DYLD_INTERPOSE(my_malloc, malloc)
Vlad Lazarenko
I got it working :-) Thank you very much!! Dyld is also useful for class interposing? :)
flaab
@flaab You are welcome. This probably won't be useful for classes due to name mangling (depending on class name, namespace etcetera). Technically it is possible to override a class's method but that would be near impossible to support as name mangling is not standardized.
Vlad Lazarenko
Hello Vlad :) I have managed to replace functions no problem. what I really need now is overriding a specific function of a class.
flaab
The especific function is QString::append(..., ..., ...). And the constructor would be ideal to override as well: all those would give me exactly the information i need :P
flaab
@flaab: In short, you better don't do it. Otherwise see http://en.wikipedia.org/wiki/Name_mangling and approach that method as a regular function. Maybe you will get some results, but I doubt that will work.
Vlad Lazarenko