Sobreescribiendo ‘malloc’ usando el mecanismo LD_PRELOAD

Intento escribir una biblioteca compartida simple que pueda registrar llamadas malloc a stderr (una especie de ‘mtrace’ si lo desea).

Sin embargo, esto no está funcionando. Esto es lo que hago:

/* mtrace.c */ #include  #include  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 bash ejecutar ls :

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

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

La pregunta es … ¿cómo hago que funcione?

PD: perdón por la escasez de tags, no pude encontrar las tags adecuadas y aún no tengo la reputación suficiente para crear nuevas.

Siempre lo hago de esta manera:

 #define _GNU_SOURCE #include  #include  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 use constructores, solo inicialice en la primera llamada a malloc . Use RTLD_NEXT para 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 otros lugares.

Si realmente quiere usar LD_PRELOAD con malloc y encontró que el código en la respuesta aceptada sigue segmentando, tengo una solución que parece funcionar.

La segfault fue causada por dlsym llamando a calloc por 32 bytes, causando una recursión al final de la stack.

Mi solución fue crear un asignador estático super simple que se encargara de las asignaciones antes de que dlsym devuelva el puntero de la función malloc.

 #define _GNU_SOURCE #include  #include  #include  #include  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.

Si está utilizando glibc, debe usar su mecanismo integrado de enganche malloc : el ejemplo de esta página tiene un ejemplo de cómo buscar el malloc original. Esto es particularmente importante si está agregando información de seguimiento adicional a las asignaciones, para garantizar que las funciones de la biblioteca que devuelven almacenamientos intermedios mal localizados sean consistentes con su implementación free() .