函数和调用机制的JIT代码生成

时间:2018-01-27 09:00:28

标签: assembly compilation compiler-construction jit

我在macOS 10.13上实现了一个简单的X86-64 JIT编译器,用于简单的编程语言我的问题是关于在JIT中调用函数的最佳方法。

所以我会写一下我如何克服这个问题:

和大多数编译器一样,我有一个AST来帮助代码生成 当代码生成器找到函数节点 (假设函数名称为func时,它将存储指向映射的可执行内存页面中当前位置的指针然后它将生成其余的指令,当其他函数调用此函数func时,后端将计算Call指令操作数。

我将更具体地说明我如何计算操作数:

让我们假设下面的代码是由JIT生成的

0000000100000f90 <_func>:
   100000f90:   55                      push   %rbp
   100000f91:   48 89 e5                mov    %rsp,%rbp
   100000f94:   5d                      pop    %rbp
   100000f95:   c3                      retq   
   100000f96:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   100000f9d:   00 00 00 

0000000100000fa0 <_main>:
   100000fa0:   55                      push   %rbp
   100000fa1:   48 89 e5                mov    %rsp,%rbp
   100000fa4:   e8 e7 ff ff ff          callq  100000f90 <_func>
   100000fa9:   31 c0                   xor    %eax,%eax
   100000fab:   5d                      pop    %rbp
   100000fac:   c3                      retq   

因为我已经存储了func (0x100000f90)的地址 我可以使用公式计算call指令的操作数 调用操作数=函数地址(0x100000f90) - 当前指令地址(0x100000fa4) - sizeof(Call(5) = 0xffffffe7因为它是英特尔CPU,所以它是小端(0xe7ffffff)。

并且对于将被调用的每个函数都将使用相同的步骤

最后是算法的简单C ++代码

#include <iostream>
#include <vector>
#include <sys/mman.h> 

 std::vector<u_int8_t> func{
    0x55,
    0x48,0x89,0xe5,
    0xb8,0x5a,0x00,0x00,0x00,
    0x5d,
    0xc3
};

std::vector<u_int8_t> Main{
    0x55,
    0x48,0x89,0xe5,
    0x48,0x83,0xec,0x10,
    0xc7,0x45,0xfc,0x00,0x00,0x00,0x00,
    0xe8,0x00,0x00,0x00,0x00,
    0x48,0x83,0xc4,0x10,
    0x5d,
    0xc3
};

long calculateCallOperand(u_int8_t*funcAddress,u_int8_t*currentAddress){
  return (funcAddress - currentAddress)-5;
}

int main(){
   auto ptr = (u_int8_t*)mmap(nullptr,4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_ANONYMOUS|MAP_PRIVATE|MAP_JIT,-1,0);
   u_int8_t*funcAddress = &ptr[0];

   for(int i=0;i<11;++i)
      ptr[i] = func[i];

  auto operand = calculateCallOperand(funcAddress,&ptr[26]);


  Main[16] = (u_int8_t)operand;
  Main[17] = *((u_int8_t*)&operand+1);
  Main[18] = *((u_int8_t*)&operand+2);
  Main[19] = *((u_int8_t*)&operand+3);


  for(int i=0,j=11;i<Main.size();++i,++j)
       ptr[j]=Main[i];


  auto offset = ptr+11;

  auto call = (void(*)())offset;
  call();
  munmap(ptr,4096);

}

(我运行代码并且它正在运行(我不知道是否存在任何未定义的行为)。)

这是正确的方法,或者它不是

如果是,那么有更好的方法 如果不是这样做的正确方法

我很抱歉这个长期的问题但是我很努力地清楚地表明我的问题,提前谢谢。

0 个答案:

没有答案