0%

C语言通过重写malloc等函数实现对内存泄漏的检测

重写带参数的宏实现对内存泄漏的检测

#define malloc(size) _malloc(size, __FILE__, __LINE__)
#define free(ptr) _free(ptr, __FILE__, __LINE__)
  • 使用C语言的__FILE__宏定位文件,使用__LINE__定位行数
  • 进一步定义函数
    void* _malloc(size_t size, const char* filename, int line)
    {
    void* p = malloc(size);
    printf("[+]%p, %s, %d\n", p, filename, line);
    return p;
    }

    void _free(void* ptr, const char *filename, int line)
    {
    free(ptr);
    printf("[-]%p, %s, %d\n", ptr, filename, line);
    }
  • 注意上述的宏定义需要在定义指定的函数_malloc_free之后执行
  • 线程安全的完整例子
  • #define _GNU_SOURCE 是一个预处理指令,它用于启用GNU C库(glibc)中的各种扩展功能。这些扩展功能包含了许多标准C库中没有的额外功能和特性,包括额外的函数、常量和类型等
  • 定义 _GNU_SOURCE 可以启用GNU C库中的许多扩展功能,使程序能够使用标准C库和POSIX标准之外的额外功能
    • 如果你要使用 dlsym 函数,并希望它能够使用 RTLD_NEXT 这个特殊的标识符,你可能需要定义 _GNU_SOURCE。这是因为 RTLD_NEXT 是GNU扩展的一部分,而不是标准POSIX的一部分
      #define _GNU_SOURCE

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      #include <pthread.h>
      #include <dlfcn.h>


      // 数据结构定义
      typedef struct MemBlock {
      void *ptr;
      size_t size;
      const char *file;
      int line;
      struct MemBlock *next;
      } MemBlock;

      static MemBlock *mem_list = NULL;
      static pthread_mutex_t mem_mutex = PTHREAD_MUTEX_INITIALIZER;

      // 原始的 malloc 和 free 函数指针
      static void *(*real_malloc)(size_t) = NULL;
      static void (*real_free)(void *) = NULL;

      // 添加内存块到链表
      void add_mem_block(void *ptr, size_t size, const char *file, int line) {
      if (!real_malloc) {
      real_malloc = malloc;
      }

      pthread_mutex_lock(&mem_mutex);
      MemBlock *new_block = (MemBlock *)real_malloc(sizeof(MemBlock));
      new_block->ptr = ptr;
      new_block->size = size;
      new_block->file = file;
      new_block->line = line;
      new_block->next = mem_list;
      mem_list = new_block;
      pthread_mutex_unlock(&mem_mutex);
      }

      // 从链表中移除内存块
      void remove_mem_block(void *ptr) {
      pthread_mutex_lock(&mem_mutex);
      MemBlock *current = mem_list;
      MemBlock *previous = NULL;
      while (current) {
      if (current->ptr == ptr) {
      if (previous) {
      previous->next = current->next;
      } else {
      mem_list = current->next;
      }
      real_free(current);
      pthread_mutex_unlock(&mem_mutex);
      return;
      }
      previous = current;
      current = current->next;
      }
      pthread_mutex_unlock(&mem_mutex);
      }

      // malloc 钩子函数
      void *malloc_hook(size_t size, const char *file, int line) {
      void *ptr = real_malloc(size);
      if (ptr) {
      add_mem_block(ptr, size, file, line);
      }
      return ptr;
      }

      // free 钩子函数
      void free_hook(void *ptr, const char *file, int line) {
      if (ptr) {
      remove_mem_block(ptr);
      real_free(ptr);
      }
      }

      // 检查内存泄漏
      void check_memory_leaks() {
      pthread_mutex_lock(&mem_mutex);
      MemBlock *current = mem_list;
      while (current) {
      fprintf(stderr, "Memory leak detected: %p (%zu bytes) allocated at %s:%d\n",
      current->ptr, current->size, current->file, current->line);
      current = current->next;
      }
      pthread_mutex_unlock(&mem_mutex);
      }

      // 宏定义替换 malloc 和 free
      #define malloc(size) malloc_hook(size, __FILE__, __LINE__)
      #define free(ptr) free_hook(ptr, __FILE__, __LINE__)

      int main() {
      // 使用 dlsym 获取原始的 malloc 和 free 函数指针
      real_malloc = (void *(*)(size_t))dlsym(RTLD_NEXT, "malloc");
      real_free = (void (*)(void *))dlsym(RTLD_NEXT, "free");

      // 示例用法
      char *leak = (char *)malloc(10); // 这个分配未释放,应该被检测到
      char *no_leak = (char *)malloc(20);
      free(no_leak);

      check_memory_leaks(); // 检测并打印内存泄漏

      return 0;
      }