C块扩展libBlocksRuntime-为Block_copy使用自定义内存分配器Boehm GC

问题描述

我正在编写一个使用Apple的Blocks扩展来提供词法闭包的C程序。我也在使用Boehm垃圾收集器。我想要的是让Block_copy()在堆上分配块时使用GC_MALLOC,以便对它们进行垃圾收集。

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

我使用-DREDIRECT_MALLOC=GC_MALLOC -DREDIRECT_REALLOC=GC_REALLOC -DIGnorE_FREE从源代码编译了libBlocksRuntime(https://github.com/mackyle/blocksruntime),以使Boehm重写malloc()和realloc()调用

然后我用-fblocks -lBlocksRuntime -l:libgc.so -fsanitize=address编译了上面的c程序,但是它表明内存泄漏,因此Block_copy()未使用Boehm的分配器。

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

如何强制libBlocksRuntime使用Boehm的内存分配器?

编辑: 我试图通过使用malloc钩子,然后与LD_PRELOAD来解决此问题,但是这些钩子似乎都不能与libBlocksRuntime(或通常用于该问题的块)配合使用。

解决方法

好的,我终于解决了。可能有更好的方法来做到这一点,但我能找到的唯一文档是源代码。首先,您需要#include Block_private.h 标头以及Block.h。这允许访问 _Block_use_GC() 函数。 Debian 存储库中的 libBlocksRuntime 版本不适合于此,因为 _Block_use_GC() 未编译到其 libBlocksRuntime.so 中。然后你需要定义这 5 个函数:

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

这些是 libBlocksRuntime 将用来访问垃圾收集器的函数。在这种情况下,重要的是 blk_alloc(),它只是调用 GC_MALLOC()。然而,其他垃圾收集器可能会使用其他功能,您可能可以利用它们来获得更好的性能。现在我们必须告诉 libBlocksRuntime 像这样使用这些函数:

  _Block_use_GC(blk_alloc,blk_setHasRefcount,blk_gc_assign_strong,blk_gc_assign_weak,blk_gc_memmove);

这会通知 libBlocksRuntime 我们正在使用垃圾收集器,这就是如何与之交互。