9

在我的 on-again-off-again 编译器项目中,我将闭包实现为具有可执行前缀的分配内存。所以一个闭包是这样分配的:

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 中是否有提供这样的自定义分配的钩子?

4

1 回答 1

3

您可能可以使用终结器执行您想要的操作- Boehm GC 仍会释放它,但您将有机会事先使用断点操作(x86 上的 0xCC)memset 闭包,并在可能的情况下将其页面标记为不可执行。

但是,终结器有性能成本,因此不应轻易使用。Boehm GC 基于标记扫描算法,它首先识别所有不应释放的块( mark.c),然后一次性释放所有其他块 (reclaim.c)。在您的情况下,修改回收过程以使用断点操作填充可执行区域中的所有可用空间并在页面完全为空时将它们标记为不可执行是有意义的。这避免了终结器,代价是分叉库(我找不到任何可扩展性机制)。

最后,请注意执行预防是一种纵深防御措施,不应成为您唯一的安全保护。面向返回的编程可用于使用不可修改的可执行区域执行任意代码。

于 2012-02-06T00:45:14.927 回答