我最近在学习JIT编译器。据我所知,JIT是一种技术,可以在执行之前将某些脚本语言代码实时编译成本地代码。当我想象这样的编译器内部时,我发现必须有一个动态分配缓冲区段,用于存放生成的本地代码。但是我们需要一种方法来从缓冲区内运行代码,因为缓冲区持有数据。我的意思是,您不能只是将一些代码放入char[]
中,然后跳转到执行,因为这会带来安全问题,操作系统必须阻止您这样做。必须有一些方式将缓冲区标记为可执行。考虑以下幼稚的方法:
#include <stdlib.h>
void *jit_some_native_code(void) {
void *code_segment = malloc(1024);
/*
* bla bla bla...
* Generate code into this code_segment.
*/
return code_segment;
}
int main(void) {
void *code = jit_some_native_code();
/*
* How can I start executing instruction in code?
*/
typedef void (*func_ptr_t)(void);
/*
* This won't work. OS bans you doing so.
*/
((func_ptr_t)code)();
}
在Ubuntu上,代码将会运行但会以状态码26退出。考虑到C的不安全类型特性,代码可以编译通过,但在C++中,编译器将直接停止您。这是否意味着JIT必须绕过编译器,并设置可执行标志?
编辑:除了mprotect之外,如果您使用mmap,您还可以指定要映射的页面权限。
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
因此,该页面将具有可执行权限。
code
指针的地址,而不是函数本身.... 应该是((func_t)code)()
。对于C++,编译器会直接停止你。只需将void *code = (void*)&some_jit_func
进行强制转换,然后就可以使用C++进行编译了。 - KamilCukvoid*
强制转换为函数指针? - cgsdfcstatic_cast
禁止这种转换,而 C 风格的转换允许它! - cgsdfccode = &some_jit_func
声明为它所代表的函数指针呢?jit-me-this-bit-of-script 函数应该返回一个函数指针而不是 void*。 - Goswin von Brederlow