为malloc创建一个包装函数,在C中创建free

时间:2008-11-04 16:50:09

标签: c memory-management memory-leaks malloc

我正在尝试在C中为freemalloc创建包装函数,以帮助通知我内存泄漏。有谁知道如何声明这些函数,所以当我调用malloc()free()时,它会调用我的自定义函数而不是标准的lib函数?

10 个答案:

答案 0 :(得分:71)

您有几个选择:

  1. 特定于GLIBC的解决方案(主要是Linux)。如果您的编译环境为glibcgcc,则首选方法是使用malloc hooks 。它不仅可以指定自定义mallocfree,还可以通过堆栈上的返回地址识别调用者。

  2. 特定于POSIX的解决方案。mallocfree定义为可执行文件中原始分配例程的包装,它将“覆盖”libc中的版本。在包装器中,您可以调用原始的malloc实现,您可以使用带有RTLD_NEXT句柄的dlsym进行查找。定义包装函数的应用程序或库需要与-ldl链接。

    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    
    void* malloc(size_t sz)
    {
        void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
        printf("malloc\n");
        return libc_malloc(sz);
    }
    
    void free(void *p)
    {
        void (*libc_free)(void*) = dlsym(RTLD_NEXT, "free");
        printf("free\n");
        libc_free(p);
    }
    
    int main()
    {
        free(malloc(10));
        return 0;
    }
    
  3. Linux特定。您可以通过在LD_PRELOAD环境变量中指定动态库来非侵入地覆盖动态库中的函数。

    LD_PRELOAD=mymalloc.so ./exe
    
  4. 特定于Mac OSX。

    与Linux相同,但您将使用DYLD_INSERT_LIBRARIES环境变量。

答案 1 :(得分:16)

您可以使用LD_PRELOAD执行包装和“覆盖”功能 - 与前面显示的示例类似。

LD_PRELOAD=/path.../lib_fake_malloc.so ./app

但我建议这个“稍微”更聪明,我的意思是一次调用dlsym

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>

void* malloc(size_t size)
{
    static void* (*real_malloc)(size_t) = NULL;
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");

    void *p = real_malloc(size);
    fprintf(stderr, "malloc(%d) = %p\n", size, p);
    return p;
}

例子我在这里找到了:http://www.jayconrod.com/cgi/view_post.py?23由Jay Conrod发布。

但是我在这个页面上发现非常酷的是: GNU链接器提供了一个有用的选项, - wrap 。当我检查“man ld”时,有以下示例:

void *
__wrap_malloc (size_t c)
{
    printf ("malloc called with %zu\n", c);
    return __real_malloc (c);
}

我同意他们的“琐碎的例子”:)。甚至不需要dlsym。

让我,我引用我的“男人”页面的另一部分:

--wrap=symbol
       Use a wrapper function for symbol.
       Any undefined reference to symbol will be resolved to "__wrap_symbol".
       Any undefined reference to "__real_symbol" will be resolved to symbol.

我希望,描述已经完成并展示了如何使用这些东西。

答案 2 :(得分:5)

在C中,我使用的方法类似于:

#define malloc(x) _my_malloc(x, __FILE__, __LINE__)
#define free(x) _my_free(x)

这使我能够毫不费力地检测分配内存的行和文件。它应该是跨平台的,但如果已经定义了宏,则会遇到问题(如果您使用另一个内存泄漏检测器,则应该是这种情况。)

如果你想在C ++中实现相同的程序,那么程序会更加complex,但使用相同的技巧。

答案 3 :(得分:5)

这是我多年来使用的一组包装函数(当我浸入C时仍然会这样做)来检测不自由的内存,多次释放内存,引用freed内存,缓冲区溢出/下溢,以及释放未分配的内存。

ftp://ftp.digitalmars.com/ctools.zip

他们已经存在了25年并证明了自己。

您可以使用宏预处理器重新定义malloc并自由使用mem包,但我建议不要使用它,因为它不会像对strdup那样将库调用重定向到malloc。

答案 4 :(得分:4)

如果你的目标是消除内存泄漏,那么使用Valgrind(免费)或Purify(代价高昂)等工具就会更简单,更少侵入。

答案 5 :(得分:4)

很抱歉重新开了7年的帖子。

在我的情况下,我需要在malloc下包装memalign / aligned_malloc。尝试其他解决方案后,我最终实现了下面列出的解决方案。它似乎工作正常。

mymalloc.c

/* 
 * Link-time interposition of malloc and free using the static
 * linker's (ld) "--wrap symbol" flag.
 * 
 * Compile the executable using "-Wl,--wrap,malloc -Wl,--wrap,free".
 * This tells the linker to resolve references to malloc as
 * __wrap_malloc, free as __wrap_free, __real_malloc as malloc, and
 * __real_free as free.
 */
#include <stdio.h>

void *__real_malloc(size_t size);
void __real_free(void *ptr);


/* 
 * __wrap_malloc - malloc wrapper function 
 */
void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size);
    printf("malloc(%d) = %p\n", size, ptr);
    return ptr;
}

/* 
 * __wrap_free - free wrapper function 
 */
void __wrap_free(void *ptr)
{
    __real_free(ptr);
    printf("free(%p)\n", ptr);
}

答案 6 :(得分:2)

如果为malloc()和free()定义自己的函数并将其与应用程序显式链接,则应优先使用函数,而不是库中的函数。

然而,你的函数'malloc'不能调用库malloc函数,因为在'c'中没有单独的命名空间的概念。换句话说,你必须实现malloc的内部并释放自己。

另一种方法是编写函数my_malloc()和my_free(),它们调用标准的库函数。这意味着任何调用malloc的代码都必须更改为调用my_xxx函数。

答案 7 :(得分:1)

如果您是自定义mallocfree的唯一客户端(即,您不想在其他库中尝试对那些方法进行补丁),则可以使用依赖注入。

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <stddef.h>

struct Allocator;

typedef struct {
    void *(*allocate)(struct Allocator *allocator, size_t size);

    void (*free)(struct Allocator *allocator, void *object);
} AllocatorVTable;

typedef struct Allocator {
    const AllocatorVTable *vptr;
} Allocator;

typedef struct {
    Allocator super;
    char *buffer;
    size_t offset;
    size_t capacity;
} BufferedAllocator;

void BufferedAllocator_init(BufferedAllocator *allocator, char *buffer, size_t capacity);

typedef Allocator MallocAllocator;

void MallocAllocator_init(MallocAllocator *allocator);

void *Allocator_allocate(Allocator *allocator, size_t size);

void Allocator_free(Allocator *allocator, void *object);

#endif
#include "allocator.h"
#include "malloc.h"

void *Allocator_allocate(Allocator *allocator, size_t size) {
    return allocator->vptr->allocate(allocator, size);
}

void Allocator_free(Allocator *allocator, void *object) {
    allocator->vptr->free(allocator, object);
}

void *BufferedAllocator_allocate(Allocator *allocator, size_t size) {
    BufferedAllocator *bufferedAllocator = (BufferedAllocator *) allocator;
    if (bufferedAllocator->offset + size > bufferedAllocator->capacity) {
        fprintf(stderr, "buffer overflow: %ld + %ld > %ld\n",
                bufferedAllocator->offset, size, bufferedAllocator->capacity);
        return NULL;
    }
    bufferedAllocator->offset += size;
    return bufferedAllocator->buffer + bufferedAllocator->offset - size;
}

void BufferedAllocator_free(Allocator *allocator, void *object) {

}

const AllocatorVTable bufferedAllocatorVTable = {
        .allocate = BufferedAllocator_allocate,
        .free = BufferedAllocator_free,
};

void BufferedAllocator_init(BufferedAllocator *allocator, char *buffer,
                            size_t capacity) {
    allocator->super.vptr = &bufferedAllocatorVTable;
    allocator->buffer = buffer;
    allocator->offset = 0;
    allocator->capacity = capacity;
}

void *MallocAllocator_allocate(Allocator *allocator, size_t size) {
    return malloc(size);
}

void MallocAllocator_free(Allocator *allocator, void *object) {
    free(object);
}

const AllocatorVTable mallocAllocatorVTable = {
        .allocate = MallocAllocator_allocate,
        .free = MallocAllocator_free,
};

void MallocAllocator_init(MallocAllocator *allocator) {
    allocator->vptr = &mallocAllocatorVTable;
}
#include <assert.h>
#include "allocator_test.h"
#include "allocator.h"

void testAllocator() {
    {
        BufferedAllocator bufferedAllocator;
        char buffer[4];
        size_t capacity = sizeof(buffer);
        BufferedAllocator_init(&bufferedAllocator, buffer, capacity);
        Allocator *allocator = &bufferedAllocator.super;

        void *chill = Allocator_allocate(allocator, capacity);
        assert(chill == buffer);
        void *oops = Allocator_allocate(allocator, 1);
        assert(oops == NULL);
    }

    {
        MallocAllocator allocator;
        MallocAllocator_init(&allocator);

        void *chill = Allocator_allocate(&allocator, 100);
        assert(chill != NULL);
        void *alsoChill = Allocator_allocate(&allocator, 100);
        assert(alsoChill != NULL);
    }
}

因此,您将在编写的任何要分配内容的代码中传递Allocator *(超出栈上char buf[n]之类的东西)。您可以使用MallocAllocator仅使用系统malloc / free,也可以在程序的顶部使用BufferedAllocatorBufferedAllocator只是一个非常简单的malloc / free的示例。它在我的用例中很好用,因为我非常了解我的程序会预先使用多少内存,并且在整个程序完成之前我不会删除任何对象。使用该接口,您可以编写更复杂的算法,例如this lecture中描述的算法之一。有很多不同的策略可以防止碎片和权衡取舍,因此滚动自己的malloc / free可能真的很有用。

答案 8 :(得分:0)

如果您使用的是Linux,则可以使用malloc_hook()(使用GNU glibc)。此函数允许您在调用实际的malloc之前让malloc调用您的函数。手册页有一个如何使用它的例子。

答案 9 :(得分:0)

如果你只是谈论你已经掌控的记忆,即你自己的malloc和free,你可以看看rmdebug。也许这就是你要写的东西,所以你可以节省一些时间。它有一个非常自由的许可证,如果这对你很重要。

我个人在一个项目中使用它来寻找内存泄漏,好的事情是它比valgrind快得多,但它不是那么强大,所以你没有得到完整的调用堆栈。