最近在研究一些JIT编译器。据我所知,JIT 是一种将一些脚本语言代码即时(在执行之前)编译为 native 代码的技术。当我想象这样一个编译器的内部时,我发现生成的 native 代码所在的位置必须有一段动态分配的缓冲区。但是我们需要一种方法来从保存数据的缓冲区中开始运行代码。我的意思是,您不能只是将一些代码放入 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.
因此该页面将具有可执行权限。
如果你想在堆中创建一个可执行区域,你可以使用mprotect .
int main() {
typedef void (*func_t)(void);
void *code = &some_jit_func;
int pagesize = getpagesize();
mprotect(code, pagesize,PROT_EXEC);
((func_t)code)();
}
您还可以将标志与 PROT_READ/PROT_WRITE 或
我是一名优秀的程序员,十分优秀!