我正在编写自己的JIT解释器。 如何执行生成的指令?

我打算编写自己的JIT解释器作为VM课程的一部分。 我对高级语言,编译器和解释器有很多了解,但很少或根本没有关于x86汇编(或C)的知识。

实际上我不知道JIT是如何工作的,但这是我对它的看法:用一些中间语言读入程序。 将其编译为x86指令。 确保最后一条指令返回到VM代码中的某个地方。 将指令存储在内存中的某些位置。 无条件跳转到第一条指令。 瞧!

所以,考虑到这一点,我有以下小C程序:

#include  #include  #include  int main() { int *m = malloc(sizeof(int)); *m = 0x90; // NOP instruction code asm("jmp *%0" : /* outputs: */ /* none */ : /* inputs: */ "d" (m) : /* clobbers: */ "eax"); return 42; 

}

好的,所以我的目的是让这个程序将NOP指令存储在内存中的某个位置,跳转到该位置然后可能崩溃(因为我没有设置任何方式让程序返回到main)。

问题:我是走在正确的道路上吗?

问题:你能告诉我一个经过修改的程序,它可以找到回到main中某个地方的方法吗?

问题:我应该注意的其他问题?

PS:我的目标是获得理解,而不是以正确的方式做所有事情。


感谢所有的反馈。 以下代码似乎是在我的Linux机器上启动和工作的地方:

 #include  #include  #include  #include  unsigned char *m; int main() { unsigned int pagesize = getpagesize(); printf("pagesize: %u\n", pagesize); m = malloc(1023+pagesize+1); if(m==NULL) return(1); printf("%p\n", m); m = (unsigned char *)(((long)m + pagesize-1) & ~(pagesize-1)); printf("%p\n", m); if(mprotect(m, 1024, PROT_READ|PROT_EXEC|PROT_WRITE)) { printf("mprotect fail...\n"); return 0; } m[0] = 0xc9; //leave m[1] = 0xc3; //ret m[2] = 0x90; //nop printf("%p\n", m); asm("jmp *%0" : /* outputs: */ /* none */ : /* inputs: */ "d" (m) : /* clobbers: */ "ebx"); return 21; } 

问题:我是走在正确的道路上吗?

我会说是的。

问题:你能告诉我一个经过修改的程序,它可以找到回到main中某个地方的方法吗?

我没有为您提供任何代码,但更好的方法是获取生成的代码并返回使用一对call / ret指令,因为它们将自动管理返回地址。

问题:我应该注意的其他问题?

是 – 作为一种安全措施,许多操作系统会阻止您在没有做出特殊安排的情况下在堆上执行代码。 这些特殊安排通常相当于您必须将相关的内存页面标记为可执行文件。

在Linux上,这是使用mprotect()完成的。

如果生成的代码遵循正确的调用约定 ,那么您可以声明指向函数的指针类型并以这种方式调用函数:

 typedef void (*generated_function)(void); void *func = malloc(1024); unsigned char *o = (unsigned char *)func; generated_function *func_exec = (generated_function *)func; *o++ = 0x90; // NOP *o++ = 0xcb; // RET func_exec();