我一直在想如何V8 JavaScript引擎和任何其他JIT编译器执行生成的代码。如何将x64机器代码写入虚拟内存并在C++中为Windows执行它
以下是我在尝试编写小型演示时阅读的文章。
- http://eli.thegreenplace.net/2013/11/05/how-to-jit-an-introduction
- http://nullprogram.com/blog/2015/03/19/
我只知道很少装配,所以我最初使用http://gcc.godbolt.org/编写一个函数,并得到分解输出,但是代码不工作在Windows上。
然后我写了一个小的C++代码,用-g -Og
进行编译,然后用gdb获得disassmbled输出。
#include <stdio.h>
int square(int num) {
return num * num;
}
int main() {
printf("%d\n", square(10));
return 0;
}
输出:
Dump of assembler code for function square(int):
=> 0x00000000004015b0 <+0>: imul %ecx,%ecx
0x00000000004015b3 <+3>: mov %ecx,%eax
0x00000000004015b5 <+5>: retq
我复制粘贴的输出(删除 '%'),以online x86 assembler并获得{ 0x0F, 0xAF, 0xC9, 0x89, 0xC1, 0xC3 }
。
这是我的最终代码。如果我用gcc编译它,我总是得到1.如果我用VC++编译它,我会得到随机数。到底是怎么回事?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
typedef unsigned char byte;
typedef int (*int0_int)(int);
const byte square_code[] = {
0x0f, 0xaf, 0xc9,
0x89, 0xc1,
0xc3
};
int main() {
byte* buf = reinterpret_cast<byte*>(VirtualAlloc(0, 1 << 8, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
if (buf == nullptr) return 0;
memcpy(buf, square_code, sizeof(square_code));
{
DWORD old;
VirtualProtect(buf, 1 << 8, PAGE_EXECUTE_READ, &old);
}
int0_int square = reinterpret_cast<int0_int>(buf);
int ans = square(100);
printf("%d\n", ans);
VirtualFree(buf, 0, MEM_RELEASE);
return 0;
}
注
我努力学习如何JIT的作品,所以请不要建议我使用LLVM或任何库。我保证我会在真实项目中使用适当的JIT库,而不是从头开始编写。
好一点,编辑建议的标题。我希望我的问题对其他读者有所帮助,因为大多数在线JIT文章都是针对POSIX的。 –
请注意,这不是“堆内存”,你在任何堆外分配一个页面(这是最好的,这样你的'VirtualProtect'调用不会影响任何其他对象) –
显示为int生成的程序集ans = square(100);'调用函数指针的地方。 –