当您需要调用大于 2^32 位的函数时会发生什么?

What happens when you need to call a function that is > 2^32-bit away?

我才发现我们平时的call指令其实是程序计数器相关的。然而x86指令使用32位宽的偏移量来表示一个相对数。

如果我想跳转 > 4GB 怎么办?

我想如果您将一些代码 JIT 到一个缓冲区中,该缓冲区分配的距离它需要调用的某些函数超过 2^32。简单的答案是:不要那样做。

在Linux上,例如,使用mmap(MAP_32BIT)在虚拟地址space的低2GiB分配内存,如果你想让JITed代码到call函数在主执行程序中table。 (假设位置相关的 executable)。

在 PIE executable 或共享库中(通常不会映射到虚拟地址的低 32 位 space),您可能会尝试在自己附近分配内存通过尝试 mmap 没有 MAP_FIXEDtrying different addresses in range if that doesn't work the first time 来编码。 mmap(hint_address, ...) / 检查它是否在代码 and/or 需要到达的数据的 +-2GiB 范围内 / munmap 并用不同的提示重试。


原因是唯一的解决方法是使用绝对地址间接调用。参见 Call an absolute pointer in x86 machine code. You'd need to load the target address into a register, or have the address stored in memory as a pointer, and jump to that. See Intel's insn ref manual, where all the available encodings of call are listed.

还有x86 tag wiki links to https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html


如果您不需要它非常高效,实际 JIT 绝对间接调用的一种方法是将 table 指针放在相对于 JIT 代码的已知位置,这样它可以使用间接 call [rel pointer_to_func1](RIP 相对寻址)。这就像 Unix 共享库使用的全局偏移量 table,以及如果使用 gcc -fno-plt.

编译,编译器生成的代码如何调用共享库函数

我恰好有这个需求:将对绝对 64 位地址的调用发送到一些动态创建的代码中,事实证明它一点也不难,尽管与直接绝对跳转相比有点笨拙我们有 32 位模式(您的确切 asm 语法可能会有所不同,具体取决于您碰巧使用的汇编程序)例如:

  call qword ptr [rel @1]
  jmp @2
@1:
  dq <64-bit address>
@2: