在我的一次又一次的编译器项目中,我已经将闭包实现为具有可执行前缀的已分配内存。所以闭包是这样分配的:
c = make_closure(code_ptr, env_size, env_data);
c
是指向已分配内存块的指针,如下所示:
movl $closure_call, %eax
call *%eax
.align 4
; size of environment
; environment data
; pointer to closure code
closure_call是一个辅助函数,它查看最近放在堆栈上的地址,并使用它来查找闭包数据和代码指针。 Boehm GC用于一般内存管理,当不再引用闭包时,它可以被GC解除分配。
无论如何,这个分配的内存需要标记为可执行;事实上,它跨越的整个页面都被标记出来。随着闭包的创建和释放,进程中越来越多的堆内存将可执行。
出于防御性编程的原因,我宁愿最小化可执行堆的数量。我的计划是尝试将所有闭包保存在同一页面上,并根据需要分配和释放可执行页面;即为闭包实现自定义分配器。 (如果所有闭包大小相同,这会更容易;因此第一步是将环境数据移动到可以正常管理的单独的非可执行分配中。这也使得防御性编程有意义。)
但剩下的问题是GC。 Boehm已经做到了!我想要的是以某种方式告诉Boehm关于我的自定义分配,并让Boehm告诉我他们什么时候能够进行GC,但要让我自己解除分配。
所以我的问题是,Boehm中是否有钩子提供这样的自定义分配?
答案 0 :(得分:3)
你可以用finalizer做你想做的事情--Boehm GC仍会解除分配,但你有机会事先用断点操作(x86上的0xCC)来设置闭包并标记其页面如果可能,不可执行。
但是,终结器具有性能成本,因此不应轻易使用。 Boehm GC基于标记扫描算法,该算法首先识别应该不被释放的所有块(mark.c),然后立即释放所有其他块(reclaim.c)。在您的情况下,修改回收过程以使用断点操作填充可执行区域中的所有可用空间是有意义的,并将页面标记为非可执行文件,因为它们变为完全空白。这样可以避免终结器,但代价是牺牲了库(我找不到任何可扩展性机制)。
最后,请注意执行预防是一种纵深防御措施,不应该是您唯一的安全保护措施。 Return-oriented programming可用于使用不可修改的可执行区域执行任意代码。