使用LD_PRELOAD机制重写'malloc'
我试图写一个简单的共享库,可以将malloc调用logging到stderr(如果愿意的话,可以使用一种“mtrace”)。
但是,这是行不通的。 这就是我所做的:
/* 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; } }
我编译这个:
gcc -shared -fPIC -o mtrace.so mtrace.c
然后当我尝试执行ls
:
$ LD_PRELOAD=./mtrace.so ls malloc(352) = Segmentation fault
现在,我怀疑dlopen需要malloc,并且正在重新定义它在共享库中,它使用该版本还有未分配的real_malloc
。
问题是…我如何使它工作?
PS抱歉在标签缺乏,我找不到适当的标签,我仍然没有足够的声誉来创造新的。
我总是这样做:
#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; }
不要使用构造函数,只需在第一次调用malloc
初始化。 使用RTLD_NEXT
来避免dlopen
。 你也可以试试malloc钩子 。 要知道,所有这些都是GNU扩展,可能不会在其他地方工作。
如果你真的想与malloc一起使用LD_PRELOAD,并发现接受的答案中的代码仍然是段错误,我有一个似乎工作的解决scheme。
segfault是由dlsym调用32个字节的calloc造成的,导致recursion到堆栈的末尾。
我的解决scheme是创build一个超级简单的静态分配器,在dlsym返回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; }
希望这有助于某人。
如果您使用的是glibc,则应该使用其内置的malloc挂钩机制 – 本页中的示例提供了如何查找原始malloc的示例。 如果您要为分配添加额外的跟踪信息,这尤其重要,以确保返回malloc'd缓冲区的库函数与您的free()
实现一致。