【发布时间】:2011-06-22 04:06:12
【问题描述】:
我想为我编写的玩具语言处理器编写一个非常小的概念验证 JIT 编译器(纯粹是学术性的),但我在设计的中等高度遇到了一些麻烦。从概念上讲,我熟悉 JIT 的工作原理——您将字节码编译成(机器或程序集?)代码以运行。然而,在具体细节层面,我不太了解如何你实际上是如何做的。
我的(非常“新手”)下意识的反应,因为我没有第一个线索从哪里开始,会尝试如下:
- mmap() 一块内存,设置对 PROT_EXEC 的访问
- 将本机代码写入块中
- 在舒适的地方存储当前寄存器(堆栈指针等)
- 修改当前寄存器以指向映射区域中的本机代码块
- 本机代码现在将由机器执行
- 恢复以前的寄存器
这是否甚至 接近 一个/正确的算法?我尝试仔细阅读我知道有 JIT 编译器来研究的不同项目(例如 V8),但这些代码库由于它们的大小而难以使用,我不知道从哪里开始寻找。
【问题讨论】:
-
您可能可以进一步简化事情:您通常可以在
mmap'ed 块中获取代码的起始地址并将其转换为函数指针。在这种情况下,代码需要保存和恢复自己的寄存器等。您需要查看平台 ABI(应用程序二进制接口)中的调用约定,以了解您需要保存的确切内容(以及如何从 C 代码获取参数、调用 C 函数等)。 -
@Jeremiah Willcock:在我看来,这大致就是下面@Shelwien 展示的技术,我说的对吗?
-
mmap 到 PROT_EXEC 可能不起作用。我不相信当前版本的 Linux 允许任何内存同时可写和可执行。您需要将其映射为可写、写入,然后将其映射为可执行。至少我相信。
-
实际上到目前为止,在我的实验中它似乎工作得很好。我正在使用最新版本的内核 (2.6.35) 并将我的
mmap()访问权限设置为PROT_READ | PROT_WRITE | PROT_EXEC -
并不是说您的目标是非 x86,但要注意自我修改代码(或即时生成的代码)需要在其他平台上显式缓存同步。它几乎只是透明地执行它的 x86(这意味着大量的硅)。只需在完成写入之后和执行之前在缓冲区上调用 msync()。
标签: c++ linux compiler-construction x86 jit