- 通过宏定义来包装
malloc
和free
函数,以便在每次内存分配和释放时记录相关信息,如文件名和行号。这使得你能够跟踪哪个函数在哪里分配和释放内存。
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <link.h> // 方式一:宏定义 void *_malloc(size_t size, char *filename,int line) { void *ptr = malloc(size); char file[128] = {0}; sprintf(file ,"./mem/%p.mem" ,ptr); FILE *fp = fopen(file ,"w");//w小写 fprintf(fp, "[+]addr: %p filename: %s line: %d\n",ptr,filename,line); fflush(fp); fclose(fp); return ptr; } void _free(void *ptr, char *filename,int line) { char file[128] = {0}; sprintf(file,"./mem/%p.mem",ptr); if(unlink(file) < 0){//unlink用于在文件系统中删除指定的文件 printf("double free %p\n",ptr); return; } return free(ptr); } // __FILE__ 获取文件名 // __LINE__ 获取函数执行的行号 #define malloc(size) _malloc(size, __FILE__,__LINE__) #define free(ptr) _free(ptr, __FILE__,__LINE__) int main(void) { init_hook(); void *p1 = malloc(8); void *p2 = malloc(16); void *p3 = malloc(32); free(p1); free(p2); return 0; }
编译执行:
表示在memleak.c 文件中, 第149行出现内存泄漏问题。
2.通过使用hook方法来重定向 malloc
和 free
函数, 与此同时通过__builtin_return_address()函数的返回值结合(*caller) 命令: addr2line -f -e ./程序名 -a 返回值(caller) 查看内存泄露的程序及具体行数。
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <link.h> //方式二:hook // gcc -o memleak memleak.c -g -ldl // addr2line -f -e ./memleak -a 0x400b38(返回值) typedef void *(*malloc_t)(size_t size); malloc_t malloc_f = NULL; typedef void (*free_t)(void *ptr); free_t free_f = NULL; int enable_malloc_hook = 1; int enable_free_hook = 1; void *COnvertToELF(void *addr) { Dl_info info; struct link_map *link; dladdr1(addr, &info, (void**)&link,RTLD_DL_LINKMAP); return (void*)((size_t)addr - link->l_addr); } void *malloc(size_t size) { void *ptr = NULL; if (enable_malloc_hook) { enable_malloc_hook = 0; ptr = malloc_f(size); // main --> f1() --> f2() --> f3() { __builtin_return_address(0) } void *caller = __builtin_return_address(0); char filename[128] = {0}; sprintf(filename, "./mem/%p.mem", ptr); FILE *fp = fopen(filename, "w"); fprintf(fp, "[+] caller: %p, addr: %p, size: %ld\n", COnvertToELF(caller), ptr, size); fflush(fp); enable_malloc_hook = 1; } else { ptr = malloc_f(size); } return ptr; } void free(void *ptr) { if (enable_free_hook) { enable_free_hook = 0; char file[128] = {0}; sprintf(file, "./mem/%p.mem", ptr); if (unlink(file) < 0) { // filename no exist; printf("double free: %p\n", ptr); return ; } free_f(ptr); enable_free_hook = 1; } else { free_f(ptr); } } void init_hook(void) { if (!malloc_f) { malloc_f = dlsym(RTLD_NEXT, "malloc"); } if (!free_f) { free_f = dlsym(RTLD_NEXT, "free"); } } int main(void) { init_hook(); void *p1 = malloc(8); void *p2 = malloc(16); void *p3 = malloc(32); free(p1); free(p2); return 0; }
编译执行:
表示在memleak.c 文件的 main 函数中, 第149行出现内存泄漏问题。
源文件 memleak.c
#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <link.h> // 方式一:宏定义 #if 0 void *_malloc(size_t size, char *filename,int line) { void *ptr = malloc(size); char file[128] = {0}; sprintf(file ,"./mem/%p.mem" ,ptr); FILE *fp = fopen(file ,"w");//w小写 fprintf(fp, "[+]addr: %p filename: %s line: %d\n",ptr,filename,line); fflush(fp); fclose(fp); return ptr; } void _free(void *ptr, char *filename,int line) { char file[128] = {0}; sprintf(file,"./mem/%p.mem",ptr); if(unlink(file) < 0){//unlink用于在文件系统中删除指定的文件 printf("double free %p\n",ptr); return; } return free(ptr); } // __FILE__ 获取文件名 // __LINE__ 获取函数执行的行号 #define malloc(size) _malloc(size, __FILE__,__LINE__) #define free(ptr) _free(ptr, __FILE__,__LINE__) //方式二:hook #elif 1 // gcc -o memleak memleak.c -g -ldl // addr2line -f -e ./memleak -a 0x400b38 typedef void *(*malloc_t)(size_t size); malloc_t malloc_f = NULL; typedef void (*free_t)(void *ptr); free_t free_f = NULL; int enable_malloc_hook = 1; int enable_free_hook = 1; void *COnvertToELF(void *addr) { Dl_info info; struct link_map *link; dladdr1(addr, &info, (void**)&link,RTLD_DL_LINKMAP); return (void*)((size_t)addr - link->l_addr); } void *malloc(size_t size) { void *ptr = NULL; if (enable_malloc_hook) { enable_malloc_hook = 0; ptr = malloc_f(size); // main --> f1() --> f2() --> f3() { __builtin_return_address(0) } void *caller = __builtin_return_address(0); char filename[128] = {0}; sprintf(filename, "./mem/%p.mem", ptr); FILE *fp = fopen(filename, "w"); fprintf(fp, "[+] caller: %p, addr: %p, size: %ld\n", COnvertToELF(caller), ptr, size); fflush(fp); enable_malloc_hook = 1; } else { ptr = malloc_f(size); } return ptr; } void free(void *ptr) { if (enable_free_hook) { enable_free_hook = 0; char file[128] = {0}; sprintf(file, "./mem/%p.mem", ptr); if (unlink(file) < 0) { // filename no exist; printf("double free: %p\n", ptr); return ; } free_f(ptr); enable_free_hook = 1; } else { free_f(ptr); } } void init_hook(void) { if (!malloc_f) { malloc_f = dlsym(RTLD_NEXT, "malloc"); } if (!free_f) { free_f = dlsym(RTLD_NEXT, "free"); } } #endif #if 1 int main(void) { init_hook(); void *p1 = malloc(8); void *p2 = malloc(16); void *p3 = malloc(32); free(p1); free(p2); return 0; } #endif
注意:提前在程序目录下创建mem文件夹,编译时添加 -g -ldl
具体使用时只需将该文件的main函数注释掉,与需要检测的程序源文件一起编译执行即可。