I am writing a C program that uses Apple's Blocks extension to provide lexical closures. I am also using the Boehm garbage collector. What I would like is for Block_copy() to use GC_MALLOC when allocating blocks on the heap, so that they are garbage collected.
#include <stdio.h>
#include <Block.h>
#include <GC.h>
int main()
{
int i = 42;
void(^test)(void) = Block_copy(^{printf("Hello %d\n", i);});
test();
}
I compiled libBlocksRuntime (https://github.com/mackyle/blocksruntime) from source using -DREDIRECT_MALLOC=GC_MALLOC -DREDIRECT_REALLOC=GC_REALLOC -DIGNORE_FREE
in order to have Boehm override malloc() and realloc() calls.
I then compiled the above c program with -fblocks -lBlocksRuntime -l:libgc.so -fsanitize=address
but it showed that memory was leaked and thus Boehm's allocator was unused by Block_copy().
Hello 42
==5885==WARNING: invalid path to external symbolizer!
==5885==WARNING: Failed to use and restart external symbolizer!
=================================================================
==5885==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 36 byte(s) in 1 object(s) allocated from:
#0 0x4961ed (/home/finn/test+0x4961ed)
#1 0x7ff40c7e0c04 (/lib/x86_64-linux-gnu/libBlocksRuntime.so.0+0xc04)
#2 0x7ff40c436cc9 (/lib/x86_64-linux-gnu/libc.so.6+0x26cc9)
SUMMARY: AddressSanitizer: 36 byte(s) leaked in 1 allocation(s).
How can I force libBlocksRuntime to use Boehm's memory allocator?
EDIT: I've tried to solve this by using malloc hooks, and then with LD_PRELOAD, but neither of these seem to cooperate with libBlocksRuntime (or blocks in general for that matter).
Ok, I've finally worked it out. There's probably a better way of doing this, but the only documentation for this I could find was the source code. Firstly, you need to #include
the Block_private.h
header as well as Block.h
. This allows access to the _Block_use_GC()
function. The version of libBlocksRuntime in the Debian repositories is unsuitable for this, since _Block_use_GC()
was not compiled into its libBlocksRuntime.so
. You then need to define these 5 functions:
BLOCK_EXPORT void* blk_alloc(const unsigned long size, __attribute__((unused)) const _Bool _, __attribute__((unused)) const _Bool __) { return GC_MALLOC(size); }
BLOCK_EXPORT void blk_setHasRefcount(__attribute__((unused)) const void* _, __attribute__((unused)) const _Bool __) {}
BLOCK_EXPORT void blk_gc_assign_strong(void* src, void** dst) { *dst = src; }
BLOCK_EXPORT void blk_gc_assign_weak(const void* src, void* dst) { *(void**)dst = (void*)src; }
BLOCK_EXPORT void blk_gc_memmove(void* dst, void* src, unsigned long size) { memmove(dst, src, size); }
These are the functions that libBlocksRuntime is going to use to access the garbage collector. The important one in this case is blk_alloc()
, which just calls GC_MALLOC()
. Other garbage collectors may use the other functions however, and you can probably leverage them for better performance. Now we have to tell libBlocksRuntime to use these functions like so:
_Block_use_GC(blk_alloc, blk_setHasRefcount, blk_gc_assign_strong, blk_gc_assign_weak, blk_gc_memmove);
This informs libBlocksRuntime that we are using a garbage collector, and this is how to interface with it.