【问题标题】:What happens when you need to call a function that is > 2^32-bit away?当您需要调用大于 2^32 位的函数时会发生什么?
【发布时间】:2015-08-04 20:47:07
【问题描述】:

我刚刚发现我们通常实际上是程序计数器相关的调用指令。然而 x86 指令使用 32 位宽的偏移量来表示相对数字。

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

【问题讨论】:

  • 根据this,您需要使用绝对 64 位地址或相对于保存 64 位偏移量的寄存器。
  • 实际上这通常不是问题。我认为没有人编写过程序的可执行部分大于 2GB 的程序。无论如何,通过指针访问可能大于 2GB 的共享库 (DLL)。
  • 谢谢你们俩。罗斯,你能解释一下你的意思吗?我是新手,所以对我来说,我不明白为什么无论如何都需要通过指针访问 dll。如果编译器放置一个 E8 操作码来调用这个 dll 方法怎么办?或者您是说编译器永远无法做到这一点,因为它不知道这个 dll 是否存在,因此它会通过间接调用进行调用?
  • @halivingston:我不是罗斯,但你说的完全正确:编译器不会直接调用外部模块——而是通过间接调用,导入表中的存根,它确实为目标地址使用了一个完整的 64 位指针。

标签: assembly linker x86 masm


【解决方案1】:

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

例如,在 Linux 上,使用 mmap(MAP_32BIT) 在虚拟地址空间的低 2GiB 中分配内存,如果您希望 JITed 代码到主可执行文件中的 call 函数。 (假设一个位置相关的可执行文件)。

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


原因是唯一的解决方法是使用绝对地址间接调用。见Call an absolute pointer in x86 machine code。您需要将目标地址加载到寄存器中,或者将地址作为指针存储在内存中,然后跳转到该地址。请参阅 Intel 的 insn 参考手册,其中all the available encodings of call are listed.

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


如果您不需要它非常高效,一种实际 JIT 绝对间接调用的方法是将指针表放在相对于 JITed 代码的已知位置,以便它可以使用间接 @987654334 @(RIP 相对寻址)。这就像 Unix 共享库使用的全局偏移表,以及如果使用 gcc -fno-plt 编译,编译器生成的代码如何调用共享库函数。

【讨论】:

  • 你已经知道我在做什么了 :) 我的代码位于内存中,几乎总是 2^32 远,我愿意将副本复制到靠近其他代码,但我需要了解如何做到这一点,所以这就是为什么这些问题:-)
  • 您的“解决方法”不是如何调用共享库中的方法吗?
  • 共享库在加载时被动态链接器重定位。 RIP 相对寻址意味着它们只需要对外部符号进行重定位(即调用 other 库中的函数),这是从 x86 到 x86-64 的一大改进。我忘记了细节;调用实际上调用了一个存根,该存根从全局偏移表或其他东西中加载一个值。尝试在 ASM 中单步执行对库函数的第一次调用(您会看到它实际上执行符号查找,这需要大量的 insns)。然后尝试单步执行对同一函数的后续调用,这样会快很多。
  • @halivingston:尝试分配缓冲区,使其与其他代码足够接近。我敢肯定您不是第一个使用 JIT 遇到这种情况的人。它可能需要一些更多特定于平台的调用,例如 mmap(..., MAP_FIXED|MAP_ANONYMOUS, ...) 在内存中的特定地址分配内存。避免踩到地址空间的已映射部分可能需要在 Linux 上解析 /proc/self/maps,或在其他平台上解析其他内容。也许已经有一个 API。
  • @PeterCordes 这不适合使用MAP_FIXED
【解决方案2】:

我恰好有这个非常需要:向一些动态创建的代码发出对绝对 64 位地址的调用,结果证明一点也不难,尽管与我们的直接绝对跳转相比有点笨拙在 32 位模式下(您的确切 asm 语法可能会因您碰巧使用的汇编程序而异)例如:

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

【讨论】:

  • mov rax, 64-bit address / call rax 相比效率低下。 Call an absolute pointer in x86 machine code。或者,如果您从内存中加载指针,请将地址数据放在其他地方,这样您就不必跳过它,就像函数中的 ret 之后一样。 (最好使用其他数据,这样您就不会在从代码页面加载数据时浪费 dTLB 条目和 L1d 缓存未命中。)
  • @Peter Cordes:注意。不过,在我的场景中,rax(以及所有其他寄存器)已经在使用中。
  • 好的,那么[rel @1] 很好,但是把数据放在你不需要jmp 的地方。没有一个独立的机器代码片段可能稍微不方便,但最坏的情况是你只是把它放在整个功能块之前/之后。顺便说一句,32 位 x86 没有直接绝对 jmpcallrel32 可以到达任何 32 位地址,因此您始终可以在位置相关代码中使用 call rel32
猜你喜欢
  • 1970-01-01
  • 2017-07-12
  • 2010-12-07
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 2014-05-08
  • 2016-11-29
  • 1970-01-01
相关资源
最近更新 更多