我正在研究应用程序(使用C ++编写),它在运行时生成一些机器代码(现在是Linux,x86-64,但是我打算在ARM上迁移)。 接下来,它将生成的代码存储在内存中并通过跳转到内存位置来执行它。 很长一段时间,我在分配可执行内存时遇到了一个问题,但是我终于解决了这个问题:
uint8_t *memory = mmap (NULL, length, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
到目前为止它工作,但我不知道是否这是做这种事情的优雅方式。 我不知道如何可执行文件加载程序这样做
这基本上是可执行的装载机如何做的事情; 在他们的情况下,他们执行一个文件的mmap
,而不是一个匿名映射,但除此之外,它本质上是相同的。
请注意,不要同时具有写入和执行访问权限,因为这会使某些类型的安全漏洞更容易。 您可以使用mprotect
在初始映射之后调整保护标志。
你的解决方案大部分是应该做的:操作系统认为这些页面是可执行的。 但是,一些操作系统将强制执行所谓的W ^ X策略,其中页面可以是可写或可执行的,但不能同时使用。 对于这样的系统(即OpenBSD,但也有修改后的Linux版本),上面的mmap()
将会失败。 所以完整的解决方案需要首先使用mmap()
和PROT_READ | PROT_WRITE
分配一些页面 PROT_READ | PROT_WRITE
,然后使用mprotect()
将页面切换到PROT_READ | PROT_EXEC
代码生成时的PROT_READ | PROT_EXEC
。
即使操作系统不执行W ^ X,强烈建议调用mprotect()
因为缓存效应(数据访问和执行在CPU中彼此完全分离;您要确保CPU将使用您的新编写的操作码,而不是之前在RAM中的内容; mprotect()
包含必要的魔术)。