我最近正在研究一些JIT编译器。据我所知,JIT是一种将一些脚本语言代码即时编译为本地代码的技术(在执行之前)。正如我想象的那样,这种编译器的内部结构使我发现,必须在生成的本机代码所在的位置上放置一段动态分配的缓冲区。但是然后我们需要一种方法来从保存数据的缓冲区内开始运行代码。我的意思是,char[]
由于安全隐患,操作系统不能阻止您这样做,因此您不能只是将一些代码放入a 然后跳入执行。必须有某种方法将缓冲区标记为可执行文件。考虑以下天真的方法:
#includevoid *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对标志进行OR