2 回答
TA贡献1936条经验 获得超6个赞
这里的所有内容也适用于jmp绝对地址,并且用于指定目标的语法相同。该问题询问有关JITing的问题,但我还添加了NASM和AT&T语法以扩大范围。
另请参阅在JIT中处理对遥远的内在函数的调用,以获取分配“附近”内存的方法,以便您可以用来rel32从JITed代码中调用提前编译的函数。
x86没有对指令中的普通(近)call或jmp绝对地址进行编码的编码。 没有绝对的直接调用/ jmp编码,除非jmp far您不需要。请参阅英特尔的insn set ref手册条目call。(有关文档和指南的其他链接,另请参见x86标签wiki。)大多数计算机体系结构都使用相对编码来进行正常跳转,例如x86,BTW。
最好的选择(如果可以使位置依赖的代码知道其自身的地址)是使用normalcall rel32,E8 rel32直接近距离调用编码,该rel32字段为target - end_of_call_insn(2的补码二进制整数)。
请参阅$在NASM中如何工作?以手动编码call指令为例;在JITing期间执行此操作应该同样容易。
在AT&T语法中: call 0x1234567
在NASM语法中:call 0x1234567
也适用于具有绝对地址的命名符号(例如使用equ或创建.set)。MASM没有等效功能,它显然只接受标签作为目的地,因此人们有时会使用低效的解决方法来解决工具链(和/或目标文件格式重定位类型)的限制。
这些汇编和链接恰好在位置相关的代码中(而不是共享的lib或PIE可执行文件)。但不是在x86-64 OS X中,该文本段映射在4GiB上方,因此无法通过到达低地址rel32。
在要调用的绝对地址范围内分配JIT缓冲区。 例如,mmap(MAP_32BIT)在Linux上,可以在2GB的低内存中分配内存,其中+ -2GB可以到达该区域中的任何其他地址,或者在跳转目标所在的位置附近提供非NULL的提示地址。(MAP_FIXED不过,不要使用;如果您的提示与任何现有映射重叠,则最好让内核选择一个不同的地址。)
(Linux非PIE可执行文件在2GB的虚拟地址空间中进行了映射,因此它们可以使用[disp32 + reg]带有符号扩展的32位绝对地址的数组索引,或将静态地址放入具有mov eax, imm32零扩展的绝对地址的寄存器中。因此,2GB的低地址是,不低于4GB, 但PIE可执行文件正在成为常态,因此,除非您确保与之建立+链接,否则请不要假设主可执行文件中的静态地址位于32位以下-no-pie -fno-pie。并且其他操作系统(如OS X)始终将可执行文件的容量设置为4GB以上)
如果您无法call rel32使用
但是,如果您需要制作不知道其绝对地址的与位置无关的代码,或者您需要调用的地址与调用者之间的距离大于+ -2GiB(可能为64位,但是最好放置)代码足够接近),则应使用间接注册call
; use any register you like as a scratch
mov eax, 0xdeadbeef ; 5 byte mov r32, imm32
; or mov rax, 0x7fffdeadbeef ; for addresses that don't fit in 32 bits
call rax ; 2 byte FF D0
或AT&T语法
mov $0xdeadbeef, %eax
# movabs $0x7fffdeadbeef, %rax # mov r64, imm64
call *%rax
很明显,你可以使用任何寄存器,比如r10或r11这是呼叫重挫,但不用于ARG-传递的x86-64系统V. AL = XMM参数的个数数的可变参数函数,所以你需要在AL = 0之前的固定值x86-64 System V调用约定中对可变参数函数的调用。
如果确实需要避免修改任何寄存器,则可以将绝对地址保持为内存中的常数,并使用call具有RIP相对寻址模式的间接内存,例如
NASM call [rel function_pointer] ; 如果您无法破坏
AT&T的任何法规call *function_pointer(%rip)
请注意,间接调用/跳转会使您的代码容易受到Spectre攻击,尤其是在同一流程中将JIT作为不信任代码的沙箱的一部分时。(在那种情况下,仅内核补丁将无法保护您)。
您可能希望使用“ retpoline”而不是普通的间接分支来减轻Spectre的性能。
间接跳转的分支错误预测惩罚也比直接(call rel32)稍差。普通直接callinsn 的目的地一经解码就被知道,一旦它检测到根本没有分支,就在管道中更早地知道。
间接分支通常可以在现代x86硬件上很好地预测,并且通常用于对动态库/ DLL的调用。这并不可怕,但是call rel32绝对更好。
但是,即使直接也call需要一些分支预测来完全避免管道气泡。(在解码之前需要进行预测,例如,假设我们刚刚获取了该块,则提取阶段接下来应获取该块。jmp next_instruction 当用完分支预测器条目时,速度会变慢)。 即使具有完美的分支预测,mov间接+ call reg也更糟糕,因为它具有更大的代码大小和更多的微指令,但是效果很小。如果有其他mov问题,如果可能的话,内联代码而不是调用它是一个好主意。
有趣的事实:call 0xdeadbeef它将在Linux上汇编但不会链接到64位静态可执行文件中,除非您使用链接程序脚本将.textsection / text segment 放在靠近该地址的位置。该.text部分通常从0x400080静态可执行文件(或非PIE动态可执行文件)开始,即从虚拟地址空间的低2GiB开始,所有静态代码/数据都驻留在默认代码模型中。但是0xdeadbeef在低32位的高半部分(即在低4G而不是低2G中),因此可以将其表示为零扩展的32位整数,而不是符号扩展的32位。并且0x00000000deadbeef - 0x0000000000400080不适合将正确扩展为64位的有符号32位整数。(负数可以到达的地址空间部分rel32从低位地址回绕的是64位地址空间的顶部2GiB;通常,地址空间的前一半保留给内核使用。)
它确实可以与组装yasm -felf64 -gdwarf2 foo.asm,并objdump -drwC -Mintel显示:
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <.text>:
0: e8 00 00 00 00 call 0x5 1: R_X86_64_PC32 *ABS*+0xdeadbeeb
但是,当ld尝试真正在那里的.text开始于它链接到一个静态可执行文件0000000000400080,ld -o foo foo.o说foo.o:/tmp//foo.asm:1:(.text+0x1): relocation truncated to fit: R_X86_64_PC32 against '*ABS*'。
在32位代码中,call 0xdeadbeef汇编和链接很好,因为a rel32可以从任何地方到达任何地方。相对位移不必将符号扩展为64位,而只需32位二进制加法即可。
直接远call编码(慢,不使用)
您可能会在的手册条目中注意到,call并且jmp其中的编码带有绝对目标地址,直接编码在指令中。但那些只存在于“远” call/ jmp也设置CS一个新的代码段选择,这是缓慢的(见昂纳雾指南)。
CALL ptr16:32(“在操作数中给出的远,绝对地址调用”)具有6个字节的段:将偏移量直接编码到指令中,而不是将其作为数据从普通寻址模式下的位置加载。因此,这是对绝对地址的直接调用。
Far call还将Push CS:EIP作为返回地址,而不仅仅是EIP,因此它甚至与call仅推送EIP的普通(附近)兼容。这不是问题jmp ptr16:32,只是缓慢和弄清楚段部分的内容。
更改CS通常仅对从32位模式更改为64位模式有效,反之亦然。通常,只有内核才能执行此操作,尽管您可以在大多数普通的OS(在GDT中保留32位和64位段描述符)下的用户空间中执行此操作。但是,那将是更多愚蠢的计算机技巧,而不是有用的东西。(带有iret或带有的64位内核将返回到32位用户空间sysexit。大多数操作系统在引导过程中仅使用远jmp一次即可切换到内核模式下的64位代码段。)
主流操作系统使用的平面内存模型不需要更改cs,并且cs对于用户空间进程将使用什么值还没有标准化。即使您想使用far jmp,也必须找出要在细分选择器部分中输入的值。(易而JIT编译:刚读当前cs有mov eax, cs,但很难提前-的即时编译可移植的。)
call ptr16:64不存在,远距离直接编码仅适用于16位和32位代码。在64位模式下,您只能call使用10字节的m16:64内存操作数,例如call far [rdi]。或将segment:offset推入堆栈并使用retf。
TA贡献1863条经验 获得超2个赞
您仅凭一条指令就无法做到。一个不错的方法是使用MOV + CALL:
0000000002347490: 48b83412000000000000 mov rax, 0x1234
000000000234749a: 48ffd0 call rax
如果要调用的过程的地址发生更改,请更改从偏移2开始的八个字节。如果调用0x1234的代码的地址发生更改,则无需执行任何操作,因为该寻址是绝对的。
- 2 回答
- 0 关注
- 814 浏览
添加回答
举报