-
通过宏定义来包装
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函数注释掉,与需要检测的程序源文件一起编译执行即可。