我正在尝试在C中为free
和malloc
创建包装函数,以帮助通知我内存泄漏。有谁知道如何声明这些函数,所以当我调用malloc()
和free()
时,它会调用我的自定义函数而不是标准的lib函数?
答案 0 :(得分:71)
您有几个选择:
特定于GLIBC的解决方案(主要是Linux)。如果您的编译环境为glibc
且gcc
,则首选方法是使用malloc hooks 。它不仅可以指定自定义malloc
和free
,还可以通过堆栈上的返回地址识别调用者。
特定于POSIX的解决方案。将malloc
和free
定义为可执行文件中原始分配例程的包装,它将“覆盖”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;
}
Linux特定。您可以通过在LD_PRELOAD
环境变量中指定动态库来非侵入地覆盖动态库中的函数。
LD_PRELOAD=mymalloc.so ./exe
特定于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)
答案 5 :(得分:4)
很抱歉重新开了7年的帖子。
在我的情况下,我需要在malloc下包装memalign / aligned_malloc。尝试其他解决方案后,我最终实现了下面列出的解决方案。它似乎工作正常。
/*
* 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)
如果您是自定义malloc
和free
的唯一客户端(即,您不想在其他库中尝试对那些方法进行补丁),则可以使用依赖注入。
#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
,也可以在程序的顶部使用BufferedAllocator
。 BufferedAllocator
只是一个非常简单的malloc / free的示例。它在我的用例中很好用,因为我非常了解我的程序会预先使用多少内存,并且在整个程序完成之前我不会删除任何对象。使用该接口,您可以编写更复杂的算法,例如this lecture中描述的算法之一。有很多不同的策略可以防止碎片和权衡取舍,因此滚动自己的malloc / free可能真的很有用。
答案 8 :(得分:0)
如果您使用的是Linux,则可以使用malloc_hook()(使用GNU glibc)。此函数允许您在调用实际的malloc之前让malloc调用您的函数。手册页有一个如何使用它的例子。
答案 9 :(得分:0)
如果你只是谈论你已经掌控的记忆,即你自己的malloc和free,你可以看看rmdebug。也许这就是你要写的东西,所以你可以节省一些时间。它有一个非常自由的许可证,如果这对你很重要。
我个人在一个项目中使用它来寻找内存泄漏,好的事情是它比valgrind快得多,但它不是那么强大,所以你没有得到完整的调用堆栈。