Anulando 'malloc' usando el mecanismo LD_PRELOAD

Resuelto Dany Zatuchna asked hace 13 años • 6 respuestas

Estoy intentando escribir una biblioteca compartida simple que registre las llamadas de malloc a stderr (una especie de 'mtrace', por así decirlo).

Sin embargo, esto no funciona. Esto es lo que hago:

/* mtrace.c */
#include <dlfcn.h>
#include <stdio.h>

static void* (*real_malloc)(size_t);

void *malloc(size_t size)
{
    void *p = NULL;
    fprintf(stderr, "malloc(%d) = ", size);
    p = real_malloc(size);
    fprintf(stderr, "%p\n", p);
    return p;
}

static void __mtrace_init(void) __attribute__((constructor));
static void __mtrace_init(void)
{
    void *handle = NULL;
    handle = dlopen("libc.so.6", RTLD_LAZY);
    if (NULL == handle) {
        fprintf(stderr, "Error in `dlopen`: %s\n", dlerror());
        return;
    }
    real_malloc = dlsym(handle, "malloc");
    if (NULL == real_malloc) {
        fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
        return;
    }
}

Compilo esto con:

gcc -shared -fPIC -o mtrace.so mtrace.c

Y luego, cuando intento ejecutar ls:

$ LD_PRELOAD=./mtrace.so ls
malloc(352) = Segmentation fault

Ahora, sospecho que dlopen necesita malloc y, mientras lo estoy redefiniendo dentro de la biblioteca compartida, usa esa versión con el archivo real_malloc.

La pregunta es... ¿cómo hago para que funcione?

PD: Perdón por la escasez de etiquetas, no pude encontrar etiquetas apropiadas y todavía no tengo suficiente reputación para crear otras nuevas.

Dany Zatuchna avatar May 21 '11 23:05 Dany Zatuchna
Aceptado

Siempre lo hago de esta manera:

#define _GNU_SOURCE

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

static void* (*real_malloc)(size_t)=NULL;

static void mtrace_init(void)
{
    real_malloc = dlsym(RTLD_NEXT, "malloc");
    if (NULL == real_malloc) {
        fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
    }
}

void *malloc(size_t size)
{
    if(real_malloc==NULL) {
        mtrace_init();
    }

    void *p = NULL;
    fprintf(stderr, "malloc(%d) = ", size);
    p = real_malloc(size);
    fprintf(stderr, "%p\n", p);
    return p;
}

No utilice constructores, simplemente inicialícelo en la primera llamada a malloc. Úselo RTLD_NEXTpara evitar dlopen. También puedes probar los ganchos malloc . Tenga en cuenta que todas esas son extensiones de GNU y probablemente no funcionarán en ningún otro lugar.

Piotr Praszmo avatar May 21 '2011 18:05 Piotr Praszmo

Si realmente desea utilizar LD_PRELOAD con malloc y descubre que el código en la respuesta aceptada todavía tiene errores segmentados, tengo una solución que parece funcionar.

El error de segmentación se debió a que dlsym llamó a calloc durante 32 bytes, lo que provocó una recursividad hasta el final de la pila.

Mi solución fue crear un asignador estático súper simple que se encargue de las asignaciones antes de que dlsym devuelva el puntero de función malloc.

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

char tmpbuff[1024];
unsigned long tmppos = 0;
unsigned long tmpallocs = 0;

void *memset(void*,int,size_t);
void *memmove(void *to, const void *from, size_t size);

/*=========================================================
 * interception points
 */

static void * (*myfn_calloc)(size_t nmemb, size_t size);
static void * (*myfn_malloc)(size_t size);
static void   (*myfn_free)(void *ptr);
static void * (*myfn_realloc)(void *ptr, size_t size);
static void * (*myfn_memalign)(size_t blocksize, size_t bytes);

static void init()
{
    myfn_malloc     = dlsym(RTLD_NEXT, "malloc");
    myfn_free       = dlsym(RTLD_NEXT, "free");
    myfn_calloc     = dlsym(RTLD_NEXT, "calloc");
    myfn_realloc    = dlsym(RTLD_NEXT, "realloc");
    myfn_memalign   = dlsym(RTLD_NEXT, "memalign");

    if (!myfn_malloc || !myfn_free || !myfn_calloc || !myfn_realloc || !myfn_memalign)
    {
        fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
        exit(1);
    }
}

void *malloc(size_t size)
{
    static int initializing = 0;
    if (myfn_malloc == NULL)
    {
        if (!initializing)
        {
            initializing = 1;
            init();
            initializing = 0;

            fprintf(stdout, "jcheck: allocated %lu bytes of temp memory in %lu chunks during initialization\n", tmppos, tmpallocs);
        }
        else
        {
            if (tmppos + size < sizeof(tmpbuff))
            {
                void *retptr = tmpbuff + tmppos;
                tmppos += size;
                ++tmpallocs;
                return retptr;
            }
            else
            {
                fprintf(stdout, "jcheck: too much memory requested during initialisation - increase tmpbuff size\n");
                exit(1);
            }
        }
    }

    void *ptr = myfn_malloc(size);
    return ptr;
}

void free(void *ptr)
{
    // something wrong if we call free before one of the allocators!
//  if (myfn_malloc == NULL)
//      init();

    if (ptr >= (void*) tmpbuff && ptr <= (void*)(tmpbuff + tmppos))
        fprintf(stdout, "freeing temp memory\n");
    else
        myfn_free(ptr);
}

void *realloc(void *ptr, size_t size)
{
    if (myfn_malloc == NULL)
    {
        void *nptr = malloc(size);
        if (nptr && ptr)
        {
            memmove(nptr, ptr, size);
            free(ptr);
        }
        return nptr;
    }

    void *nptr = myfn_realloc(ptr, size);
    return nptr;
}

void *calloc(size_t nmemb, size_t size)
{
    if (myfn_malloc == NULL)
    {
        void *ptr = malloc(nmemb*size);
        if (ptr)
            memset(ptr, 0, nmemb*size);
        return ptr;
    }

    void *ptr = myfn_calloc(nmemb, size);
    return ptr;
}

void *memalign(size_t blocksize, size_t bytes)
{
    void *ptr = myfn_memalign(blocksize, bytes);
    return ptr;
}

Espero que esto ayude a alguien.

FatalFlaw avatar Apr 04 '2012 09:04 FatalFlaw

Si está utilizando glibc, debe utilizar su mecanismo de enlace malloc integrado ; el ejemplo de esta página muestra cómo buscar el malloc original. Esto es particularmente importante si agrega información de seguimiento adicional a las asignaciones, para garantizar que las funciones de la biblioteca que devuelven buffers mallocados sean consistentes con su free()implementación.

bdonlan avatar May 22 '2011 04:05 bdonlan