48

I am trying to create wrapper functions for free and malloc in C to help notify me of memory leaks. Does anyone know how to declare these functions so when I call malloc() and free() it will call my custom functions and not the standards lib functions?

2
  • 2
    Side note, this is what tools like Valgrind do. If you'd rather use something out of the box on Unix or Linux, Valgrind is a good option. Commented Jul 21, 2017 at 19:15
  • 1
    Related: What is the LD_PRELOAD trick? Commented Jun 28, 2021 at 22:45

10 Answers 10

87

You have a few options:

  1. GLIBC-specific solution (Removed since glibc-2.34). If your compilation environment is glibc with gcc, you can use malloc hooks. Not only it lets you specify custom malloc and free, but will also identify the caller by the return address on the stack.

  2. POSIX-specific solution. Define malloc and free as wrappers to the original allocation routines in your executable, which will "override" the version from libc. Inside the wrapper you can call into the original malloc implementation, which you can look up using dlsym with RTLD_NEXT handle. Your application or library that defines wrapper functions needs to link with -ldl.

    #define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> void* malloc(size_t size) { void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc"); fprintf(stderr, "malloc(%zu)\n", size); return libc_malloc(size); } void free(void *ptr) { void (*libc_free)(void*) = dlsym(RTLD_NEXT, "free"); fprintf(stderr, "free(%p)\n", ptr); libc_free(ptr); } int main() { free(malloc(10)); return 0; } 
  3. Linux specific. You can override functions from dynamic libraries non-invasively by specifying them in the LD_PRELOAD environment variable.

     LD_PRELOAD=./mymalloc.so ./exe 
  4. Mac OSX specific.

Same as Linux, except you will be using DYLD_INSERT_LIBRARIES environment variable.

Sign up to request clarification or add additional context in comments.

11 Comments

Hi I am getting error, how to resolve? ERROR: ld.so: object '/home/tmp/libjmalloc.so' from LD_PRELOAD cannot be preloaded: ignored.
@Thangaraj, I can't tell, this is a very generic error. It can be that the file isn't found, or compiled for the wrong architecture (x86 vs x86_64), or the lib isn't owned by the user owning the executable, if it has SUID bit set and the lib isn't owned by the owner of the executable (otherwise you could run your lib's code as other user).
Option 2 works well, until the application is run by valgrind, and then all sorts of odd problems happen. Is this due to valgrind doing a similar override and causing some sort of conflict? Which option is best suited for malloc wrapping with valgrind?
You are using printf() inside the custom malloc but printf() itself uses malloc. I'm getting segfaults with for example LD_PRELOAD=./malloc.so ls. Isn't this creating an infinite recursion? How to tell the functions inside our custom malloc to use the libc-malloc?
If you override malloc() and free(), rather than simply wrapping them, you must take into account some special considerations, such as you may be creating infinite recursion if you call printf() from within malloc(), as printf() may call malloc(), which now calls printf()...forever until stack overflow. See my answer here on this: “Segmentation fault (core dumped)” for: “No such file or directory” for libioP.h, printf-parse.h, vfprintf-internal.c, etc
|
20

You can do wrapper and "overwrite" function with LD_PRELOAD - similarly to example shown earlier.

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

But I recommend to do this "slightly" smarter, I mean calling dlsym once.

#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; } 

example I've found here: http://www.jayconrod.com/cgi/view_post.py?23 post by Jay Conrod.

But what I've found really cool at this page is that: GNU linker provides a useful option, --wrap . When I check "man ld" there is following example:

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

I agree with them that's "trivial example" :). Even dlsym is not needed.

Let, me cite one more part of my "man ld" page:

--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. 

I hope, description is complete and shows how to use those things.

7 Comments

Hi I am getting error, how to resolve? ERROR: ld.so: object '/home/tmp/libjmalloc.so' from LD_PRELOAD cannot be preloaded: ignored.
Strange. In past it worked, currently I got the same error as you. As I google it, there are many many similar cases. Please, let us know, when you find solution. I tried and couldn't - 'file' shows that binaries are same arch, so it should work. It needs more attention.
I had problem in two systems, in one system I corrected by replacing relative path with absolute path and in other system still digging :). I have one doubt, this method will hold good for small programs. suppose if I have a large program, then how can I find out from where (which function) the malloc function is called.
Good you've wrote, that full path works in one of your two configurations. I've checked out. In my configuration it does not work, when path contains white-spaces or is too long. So simply, copy libjmalloc.so into /tmp , and run LD_PRELOAD=/tmp/libjmalloc.so ./a.out . It solves problem in my case. Does it help in yours?
Do you have any inputs for; I have one doubt, this method will hold good for small programs. suppose if I have a large program, then how can I find out from where (which function) the malloc function is called.
|
15

In my case I needed to wrap memalign/aligned_malloc under malloc. After trying other solutions I ended up implementing the one listed below. It seems to be working fine.

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); } 

Comments

5

In C, the method I used was similar to:

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

This allowed me to detect the line and file of where the memory was allocated without too much difficulty. It should be cross-platform, but will encounter problems if the macro is already defined (which should only be the case if you are using another memory leak detector.)

If you want to implement the same in C++, the procedure is a bit more complex but uses the same trick.

8 Comments

Best not to use leading underscores in names - they're mainly reserved to the implementation.
Right, he's using those values as defined in the implementation. gcc.gnu.org/onlinedocs/gcc-3.2.3/cpp/…
@JonathanLeffler, leading __ and names starting with _ and a capital letter are reserved by the standard. Names starting with a single underscore are not reserved, but they should be contained within the file, i.e. the linker shouldn't see them. So as long as _my_malloc and _my_free are static functions, it's ok. On the other hand, redefining library functions is undefined behavior.
@Shahbaz: ISO/IEC 9899:2011, §7.1.3 Reserved identifiers says: — All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces. I believe that _my_malloc() as a static function falls foul of the second restriction.
@JonathanLeffler, how so? static void *_my_malloc(size_t) is defined only in file scope in the ordinary name space (and it's not a struct/union/enum, so it doesn't even enter the tag name space).
|
5

Here's a set of wrapper functions I used for years (and still do when I dip into C) to detect unfree'd memory, memory free'd multiple times, references to free'd memory, buffer overflows/underflows, and freeing memory that was not allocated.

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

They've been around for 25 years and have proven themselves.

You could use the macro preprocessor to redefine malloc and free to use the mem package ones, but I recommend against it, because it won't redirect library calls to malloc like what strdup does.

1 Comment

Auth protected link
4

If your goal is to eliminate memory leaks, an easier, less intrusive way is to use a tool like Valgrind (free) or Purify (costly).

1 Comment

Agree. Valgrind is awesome. I like how it works with compiled binaries. You don't need to compile it special or anything although you'll get the best results if you compile with -O0 and -g flags.
2

If you define your own functions for malloc() and free() and explicitly link that with your applications, your functions should be used in preference to those in the library.

However, your function called 'malloc' cannot then call the library malloc function, because in 'c' there's no concept of separate namespaces. In other words, you'd have to implement the internals of malloc and free yourself.

Another approach would be to write functions my_malloc() and my_free(), which call the standard library ones. This would mean that any code calling malloc would have to be changed to call your my_xxx functions.

5 Comments

And you can #define malloc my_malloc to get your code to work without modification. But you'll need to be consistent about usage - don't use my_malloc if the memory will be freed in a library, or vice versa.
Paragraphs 2 and 3 are misleading.
@Matt Joiner - can you elaborate, please?
P2: If there were namespaces it still wouldn't change the situation. Also you can subsequently call the real malloc function. This has nothing to do with C. P3: Yes you could, and this wouldn't be hooking calls to malloc/free properly in code you didn't have control over. You can instruct the linker to redirect references to different names. Without modifying the C code. Summary: None of the limitations you claim really exist, and none of the workarounds you have given are necessary.
@Matt - Thanks: I wasn't aware of malloc hooks and --wrap, BUT they are highly toolchain and os specific. Only gcc supports them, AFAICT - and the OP didn't specify OS or tools. C++ namespaces could provide a similar hack to the #define approach, but I'd agree it's far from ideal and suffers the problem you mentioned. In general, I'm still happy with my answer.
1

If you are the only client of the custom malloc and free (i.e. you're not trying to monkey patch those methods for code in some other library), then you can use dependency injection.

#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); } } 

So you would pass around an Allocator * to whichever piece of code you write that wants to allocate stuff (beyond something like char buf[n] on the stack). You can use a MallocAllocator to just use the system malloc/free, or you could use a BufferedAllocator at the very top of your program. A BufferedAllocator is just an example of a really simple malloc/free. It works well in my use-case because I pretty much know how much memory my program will use in advance, and I don't delete any object until the entire program is done. Using this interface, you could write a more complicated algorithm like one of the ones described in this lecture. There are a lot of different strategies for preventing fragmentation and many trade-offs, so rolling your own malloc/free could be really useful.

1 Comment

like you solid approach, will implement using i_p_c naming convention
0

If you are using Linux, you can use malloc_hook() (with GNU glibc). This function allows you to have malloc call your function prior to calling the actual malloc. The man page has an example on how to use it.

Comments

0

If you are only talk about memory that you have under control, i.e. that you malloc and free on your own, you can take a look on rmdebug. Probably it is what you are going to write anyway, so you can save sometime. It has a very liberal licence, if that should be important for you.

I personally use it in a project, to look for memory leaks, the nice things is that it is much faster then valgrind, however it isn't that powerful so you don't get the full calling stack.

Comments