为 JIT 跳转 (x86_64)

Jumps for a JIT (x86_64)

我正在为 x86_64 linux.

用 C 编写 JIT 编译器

目前的想法是在可执行内存的缓冲区中生成一些字节码(例如,通过 mmap 调用获得)并使用函数指针跳转到它。

我希望能够 link 多个可执行内存块在一起,这样它们就可以仅使用本机指令在彼此之间跳转。

理想情况下,指向可执行块的 C 级指针可以作为绝对跳转地址写入另一个块,如下所示:

unsigned char *code_1 = { 0xAB, 0xCD, ... };
void *exec_block_1 = mmap(code1, ... );
write_bytecode(code_1, code_block_1);
...
unsigned char *code_2 = { 0xAB, 0xCD, ... , exec_block_1, ... };
void *exec_block_2 = mmap(code2, ... );
write_bytecode(code_2, exec_block_2); // bytecode contains code_block_1 as a jump
                                      // address so that the code in the second block
                                      // can jump to the code in the first block

然而,我发现 x86_64 的局限性是一个很大的障碍。无法跳转到 x86_64 中的绝对 64 位地址,因为所有可用的 64 位跳转操作都是相对于指令指针的。这意味着我不能使用 C 指针作为生成代码的跳转目标。

是否有解决此问题的方法,可以让我按照我描述的方式 link 将块放在一起?也许是我不知道的 x86_64 指令?

嗯,我不确定我是否清楚地理解了你的问题,以及这是否是一个正确的答案。这是一个相当复杂的实现方式:

    ;instr              ; opcodes [op size] (comment)
    call next           ; e8 00 00 00 00 [4] (call to get current location)
next:
    pop rax             ; 58 [1]  (next label address in rax)
    add rax, 12h        ; 48 83 c0 12 [4] (adjust rax to fall on landing label)
    push rax            ; 50 [1]  (push adjusted value)
    mov rax, code_block ; 48 b8 XX XX XX XX XX XX XX XX [10] (load target address)
    push rax            ; 50 [1] (push to ret to code_block)
    ret                 ; c3 [1] (go to code_block)
landing:    
    nop
    nop

e8 00 00 00 00 只是为了获取堆栈顶部的当前指针。然后代码调整 rax 以稍后落在着陆标签上。您需要用 code block 的虚拟地址替换 XX(在 mov rax, code_block 中)。 ret 指令用作调用。当调用者returns时,code应该落在landing.

这就是你想要实现的目标吗?

如果你在发出跳转指令时知道块的地址,你可以只检查从跳转指令地址到目标块地址的字节距离是否合适在 jXX 指令族的 32 位有符号偏移量内。

即使您 mmap 每个块分开,您也很有可能不会得到两个相距超过 ±2GiB 的相邻(在控制流意义上)块。话虽这么说,有几个很好的理由 像那样分别映射每个块。首先,mmap的最小分配单位是(几乎按照定义)一页,大概至少是4KiB。这意味着每个块的代码之后未使用的 space 被浪费了。其次,将基本块打包得更紧密可以提高指令缓存的利用率和更短的跳转编码有效的机会。

Perhaps an x86_64 instruction that I'm not aware of?

顺便说一句,有一个指令可以将 64 位立即数加载到 rax。 GNU 工具链将其称为 movabs:

0000000000000000 <.text>:
   0:   49 b8 ff ff ff ff ff    movabs rax,0x7fffffffffffffff
   7:   ff ff 7f

所以如果你真的想,你可以简单地将指针加载到rax并使用跳转来注册。