如何在C中将内存段标记为可执行文件?

cgs*_*dfc 1 c executable jit

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

#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)();

}
Run Code Online (Sandbox Code Playgroud)

在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.
Run Code Online (Sandbox Code Playgroud)

这样,页面将具有可执行权限。

Nin*_*ina 8

如果要使堆中的区域成为可执行文件,则可以使用mprotect

int main() {
  typedef void (*func_t)(void);
  void *code = &some_jit_func;
  int pagesize = getpagesize();
  mprotect(code, pagesize,PROT_EXEC);
  ((func_t)code)();
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用PROT_READ / PROT_WRITE对标志进行OR

  • @ryyker号。正如其[手册](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html)中明确提到的那样,`mprotect` _changes_一页内存的访问权限。除此之外,大多数操作系统将拒绝执行可执行和可写的页面。 (2认同)