如何dynamic生成和运行本机代码?

我想为我所写的玩具语言处理器(纯学术)编写一个非常小的概念certificateJIT编译器,但是在devise的中间高度,我遇到了一些麻烦。 从概念上讲,我熟悉JIT是如何工作的 – 将字节码编译成(机器或程序集)代码来运行。 然而,在坚果和螺栓层面,我并不十分在意你如何 去做件事

我的(非常“新鲜”)膝反应,因为我没有第一个线索从哪里开始,那就是尝试如下的方法:

  1. mmap()一块内存,设置对PROT_EXEC的访问
  2. 将本地代码写入块中
  3. 将当前的寄存器(堆栈指针等)存储在一个舒适的地方
  4. 修改当前的寄存器以指向映射区域中的本地代码块
  5. 本机代码现在将被机器执行
  6. 恢复以前的寄存器

那甚至接近正确的algorithm? 我已经试过了解不同的项目,我知道有JIT编译器来学习(比如V8 ),但是这些代码库因为它们的大小而变得很难使用,而且我不知道从哪里开始寻找。

Solutions Collecting From Web of "如何dynamic生成和运行本机代码?"

不知道关于Linux,但这在x86 / Windows上工作。
更新: http : //codepad.org/sQoF6kR8

 #include <stdio.h> #include <windows.h> typedef unsigned char byte; int arg1; int arg2; int res1; typedef void (*pfunc)(void); union funcptr { pfunc x; byte* y; }; int main( void ) { byte* buf = (byte*)VirtualAllocEx( GetCurrentProcess(), 0, 1<<16, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); if( buf==0 ) return 0; byte* p = buf; *p++ = 0x50; // push eax *p++ = 0x52; // push edx *p++ = 0xA1; // mov eax, [arg2] (int*&)p[0] = &arg2; p+=sizeof(int*); *p++ = 0x92; // xchg edx,eax *p++ = 0xA1; // mov eax, [arg1] (int*&)p[0] = &arg1; p+=sizeof(int*); *p++ = 0xF7; *p++ = 0xEA; // imul edx *p++ = 0xA3; // mov [res1],eax (int*&)p[0] = &res1; p+=sizeof(int*); *p++ = 0x5A; // pop edx *p++ = 0x58; // pop eax *p++ = 0xC3; // ret funcptr func; func.y = buf; arg1 = 123; arg2 = 321; res1 = 0; func.x(); // call generated code printf( "arg1=%i arg2=%i arg1*arg2=%i func(arg1,arg2)=%i\n", arg1,arg2,arg1*arg2,res1 ); } 

你可能想看看libjit ,它提供了你正在寻找的基础设施:

libjit库实现了即时编译功能。 与其他JIT不同的是,这个JIT被设计成独立于任何特定的虚拟机字节码格式或语言。

http://freshmeat.net/projects/libjit

Android Dalvik JIT编译器也值得一看。 它应该是相当小和精益(不知道这是否有助于理解或使事情更复杂)。 它也针对Linux。

如果事情变得越来越严重,看LLVM可能也是一个不错的选择。

耶利米建议的函数指针方法听起来不错。 无论如何,您可能都想使用调用者的堆栈,并且可能只剩下几个寄存器(在x86上),您需要保留或不接触这些寄存器。 在这种情况下,如果编译的代码(或条目存根)在继续之前将它们保存在堆栈中,可能是最简单的。 最后,归结起来就是编写一个汇编函数并从C中接口到它。

答案取决于你的编译器和你把代码放在哪里。 见http://encode.ru/threads/1273-Just-In-Time-Compilation-Improvement-For-ZPAQ?p=24902&posted=1#post24902

在32位Vista中进行测试时,无论代码是放在堆栈,堆还是静态内存中,Visual C ++都会提供DEP(数据执行阻止)错误。 g ++,Borland和Mars可以有时做出工作。 由JIT代码访问的数据需要声明为volatile。

如何JIT – 介绍是一篇新的文章(从今天开始!)解决了一些这些问题,并描述了更大的图景。

除了迄今为止所提出的技术之外,可能还需要考虑线程创建功能。 如果你创建了一个新的线程,起始地址设置为你生成的代码,那么你肯定知道没有旧的寄存器需要保存或者恢复,操作系统为你处理相关寄存器的设置。 即你消除你的列表中的步骤3,4和6。

您可能会对为什么选择幸运的Potion编程语言感兴趣。 这是一个小而不完整的语言,其特点是及时编译。 药水的小尺寸使其更易于理解。 存储库包括语言内部的描述(JIT内容从标题“ 〜jit〜 ”开始)。

由于它在Potion的虚拟机环境中运行,实现变得复杂了。 不过,不要让这个吓跑你。 不用多久就可以看到他在做什么。 基本上,使用一小部分VM操作码可以将某些操作建模为优化程序集 。