虚拟机如何动态生成本机机器代码并执行它?

假设您可以弄清楚想要发出的本机机器操作码是什么,那么如何实际运行它呢?

它是否像将助记符指令映射到二进制代码,将其填充到 char* 指针中并将其转换为函数并执行一样hacky?

或者您会生成一个临时共享库(.dll 或 .so 或其他)并使用标准函数将其加载到内存中,例如 LoadLibrary ?

有帮助吗?

解决方案

你可以只做 程序计数器 指向您要执行的代码。请记住,数据可以是数据或代码。在 x86 上,程序计数器是 EIP 寄存器。EIP的IP部分代表指令指针。调用JMP指令跳转到某个地址。跳转后的EIP就会包含这个地址。

它是否像将助记符指令映射到二进制代码,将其填充到 char* 指针中并将其转换为函数并执行一样hacky?

是的。这是一种方法。生成的代码将被转换为 指向函数的指针 在C.

其他提示

它是否像将助记符指令映射到二进制代码,将其填充到 char* 指针中并将其转换为函数并执行一样hacky?

是的,如果您使用 C 或 C++(或类似的语言)执行此操作,那么您确实会这样做。

它看起来很老套,但这实际上是语言设计的产物。请记住,您要使用的实际算法非常简单:确定要使用的指令,将它们加载到内存中的缓冲区中,然后跳转到该缓冲区的开头。

不过,如果您确实尝试这样做,请确保返回 C 程序时调用约定正确。我想如果我想生成代码,我会寻找一个库来为我处理这方面的事情。Nanojit 最近成为新闻焦点;你可以看看那个。

是的。您只需构建一个 char* 并执行它。但是,您需要注意一些细节。char* 必须位于内存的可执行部分,并且必须具有正确的对齐方式。

除了 nanojit 之外,您还可以查看 LLVM,它是另一个库,能够将各种程序表示形式编译为函数指针。它的界面很干净,生成的代码也很高效。

据我所知,它会编译内存中的所有内容,因为它必须运行一些启发式方法来优化代码(即:随着时间的推移内联)但你可以看看 共享源代码公共语言基础设施 2.0 转子释放。除了抖动和 GC 之外,整个代码库与 .NET 相同。

除了 Rotor 2.0 - 您还可以看看 热点虚拟机 在 OpenJDK 中。

关于生成DLL:为此需要额外的 I/O,加上链接,再加上生成 DLL 格式的复杂性,将使事情变得更加复杂,最重要的是,它们会降低性能;另外,最后你仍然调用一个指向加载代码的函数指针,所以......此外,JIT 编译一次可以发生一种方法,如果您想这样做,您将生成许多小 DLL。

关于“可执行部分”的要求,在 POSIX 系统上调用 mprotect() 可以修复权限(Win32 上有类似的 API)。您需要对大内存段执行此操作,而不是每个方法执行一次,否则会太慢。

在普通的 x86 上你不会注意到这个问题,在带有 PAE 的 x86 或 64 位 AMD64/Intel 64 位机器上你会遇到段错误。

它是否像将mnemonic指令映射到二进制代码一样,将其塞入char*指针中并将其施放为函数和执行?

是的,这有效。

要在 Windows 中执行此操作,您必须将 PAGE_EXECUTE_READWRITE 设置为分配的块:

void (*MyFunc)() = (void (*)()) VirtualAlloc(NULL, sizeofblock,  MEM_COMMIT, PAGE_EXECUTE_READWRITE);

//Now fill up the block with executable code and issue-

MyFunc();
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top