assembly 如何使用64位绝对地址执行调用指令?

tpxzln5u  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(145)

我正试图从机器码调用一个函数--它在编译和链接时应该有一个绝对地址。我正在创建一个指向所需函数的函数指针,并试图将其传递给调用指令,但我注意到调用指令最多使用16或32位地址。有办法调用绝对64位地址吗?
我正在为x86-64架构进行部署,并使用NASM生成机器码。
如果我能保证可执行文件肯定会Map到底部4GB内存,我可以使用32位地址,但我不确定在哪里可以找到这些信息。

编辑:我不能使用callf指令,因为这需要关闭64位模式。
二次编辑:我也不想将地址存储在寄存器中并调用寄存器,因为这对性能至关重要,而且我不能有间接函数调用的开销和性能影响。
最终编辑:通过确保我的机器码Map到第一个2GB内存,我能够使用rel 32调用指令。这是通过带有MAP_32BIT标志的mmap实现的(我使用的是linux):

MAP_32BIT(自Linux 2.4.20,2.6起)将Map放入进程地址空间的前2 GB。此标志仅在x86-64上支持,适用于64位程序。它的加入是为了允许线程堆栈被分配到前2GB内存中的某个地方,以便在一些早期的64位处理器上提高上下文切换性能。现代x86-64处理器不再有这个性能问题,因此在这些系统上不需要使用此标志。当MAP_FIXED被设置时,MAP_32BIT标志被忽略。

gorkyyrv

gorkyyrv1#

相关:Handling calls to (potentially) far away ahead-of-time compiled functions from JITed code有更多关于JIT的内容,特别是在它想要调用的代码附近分配JIT缓冲区,因此您可以使用高效的call rel32。或者如果没有该怎么办。
Call an absolute pointer in x86 machine code也是一个很好的关于calljmp到绝对地址的规范Q&A。
TL:DR:要通过名称调用函数,只需像普通人一样使用call func,并让汇编器+链接器来处理它。既然你说你在使用NASM,我猜你实际上是在用汇编程序生成机器码。这听起来像是一个更复杂的问题,但我认为你只是想问正常的方式是否安全。
Indirect call r/m64 ( FF /2 )在64位模式下采用64位寄存器或内存操作数。
所以你可以

func equ  0x123456789ab
; or if func is a regular label

mov   rax, func          ; mov r64, imm64,  or mov r32, imm32 if it fits
call  rax

字符串
通常你会把一个标签地址放入一个带有lea rax, [rel func]的寄存器中,但是如果它是可编码的,那么你就可以使用call rel32
或者,如果你知道你的机器码将被存储在哪个地址,你可以使用正常的直接call rel32编码,在你计算从目标到call指令结束的地址差异之后。
如果不想使用间接调用,那么rel32编码是唯一的选择。确保机器码进入低2GiB,以便它可以到达低4GiB中的任何地址。
如果我能保证可执行文件肯定会Map到底部4GB的内存
是的,这是Linux、Windows和OS X的默认代码模型。AMD 64调用/跳转指令和RIP相对寻址仅使用rel32编码,因此所有系统都默认为“小”代码模型,其中代码和静态数据都在低2GiB中,因此可以保证链接器只需填写rel 32即可达到2G正向或2G反向。
x86-64 System V ABI确实讨论了大型/巨型代码模型,但如果有人使用过的话,那就是IDK,因为寻址数据和进行调用的效率低下。
re:效率:是的,mov/call rax效率较低。我认为,如果分支预测失败,并且无法从BTB提供目标预测,则会明显变慢。然而,即使call rel32jmp rel32仍然需要BTB来获得完整的性能。参见Slow jmp-instruction,当一个巨大的循环中有太多的时候,相对jmp next_insn会变慢。
使用热分支预测器,间接版本只是额外的代码大小和额外的uop(mov)。它可能会消耗更多的预测资源,但甚至可能不会。
参见What branch misprediction does the Branch Target Buffer detect?

hc8w905p

hc8w905p2#

在新的APXextension中,英特尔添加了一条新的JMPABS指令,该指令接收64位立即数作为绝对跳转目标
不幸的是,没有CALLABS,因此您需要像这样解决它。

nearby_trampoline:
    jmpabs target64
...
call nearby_trampoline

字符串
我不知道它是否比传统的mov reg, target64; call reg序列更快。然而,APX还增加了16个寄存器和3个操作数的整数指令(即非破坏性目的地),因此寄存器和I/O压力可能不再存在,您可以只为绝对地址留出一个寄存器并直接使用call reg

相关问题