JIT代码生成技术

时间:2020-03-05 18:48:05  来源:igfitidea点击:

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

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

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

还是我们会生成一个临时共享库(.dll或者.so或者其他),然后使用诸如LoadLibrary之类的标准函数将其加载到内存中?

解决方案

回答

据我所知,它会编译内存中的所有内容,因为它必须运行一些试探法来优化代码(即:随着时间的推移内联),但是我们可以看看Shared Source Common Language Infrastructure 2.0转子版本。整个代码库与.NET相同,除了抖动和GC。

回答

我们可以使程序计数器指向我们要执行的代码。请记住,数据可以是数据或者代码。在x86上,程序计数器是EIP寄存器。 EIP的IP部分代表指令指针。调用JMP指令以跳转到一个地址。跳转后,EIP将包含此地址。

Is it something as hacky as mapping the mnemonic instructions to binary codes, stuffing it into an char* pointer and casting it as a function and executing?

是的。这是一种方法。结果代码将转换为C语言中的函数指针。

回答

除了Rotor 2.0之外,我们还可以查看OpenJDK中的HotSpot虚拟机。

回答

Is it something as hacky as mapping the mnemonic instructions to binary codes, stuffing it into an char* pointer and casting it as a function and executing?

是的,如果我们使用C或者C ++(或者类似的东西)来做,那正是我们要做的。

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

但是,如果我们确实尝试执行此操作,请确保在返回C程序时正确使用了调用约定。我想如果我想生成代码,我会寻找一个图书馆来照顾我。 Nanojit最近成为新闻。你可以看一下。

回答

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

除了nanojit之外,我们还可以签出LLVM,这是另一个库,它能够将各种程序表示形式编译为功能指针。它的界面整洁,生成的代码趋向于高效。

回答

关于生成DLL:为此需要额外的I / O,再加上链接,再加上生成DLL格式的复杂性,将使处理变得更加复杂,并且最重要的是,它们会破坏性能。此外,最后我们仍然调用指向已加载代码的函数指针,因此...
而且,JIT编译一次只能发生一种方法,如果要这样做,则会生成许多小的DLL。

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

在普通的x86上,我们不会注意到此问题,而在带有PAE或者64位AMD64 / Intel 64位计算机的x86上,我们会遇到段错误。

回答

Is it something as hacky as mapping
  the mnemonic instructions to binary
  codes, stuffing it into an char*
  pointer and casting it as a function
  and executing?

是的,那行得通。

要在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();