为什么这个MOVSS指令使用RIP相对寻址?

问题描述:

我在反汇编器(浮点逻辑C++)中找到了下面的汇编代码。为什么这个MOVSS指令使用RIP相对寻址?

842: movss 0x21a(%rip),%xmm0 

我明白,当进程rip将总是842和这个0x21a(%RIP)将是常量。使用这个寄存器似乎有点奇怪。

我想知道是否有使用rip相对地址的任何优势,而不是其他寻址。

RIP是指令指针寄存器,这意味着它包含紧跟当前指令的指令地址。

例如,考虑下面的代码:

mov rax, [rip] 
nop 

在代码的第一行有,RIP指向指令,使其指向在NOP。因此,该代码将NOP指令的地址加载到RAX寄存器中。

因此,它是而不是RIP只是一个常数的情况。你理解RIP在这个过程中“永远是842”是不正确的。取决于代码已加载到内存中的位置,RIP的值将会改变。 842只是你的调试符号中的行号,一旦代码被编译成二进制文件,它就不再有行号了。 :-)

在你的反汇编中,常量是偏移量(0x21A)。这是从RIP中的当前值的偏移量。另一种书写方式是:%rip + 0x21A

RIP -relative寻址是64位长模式引入的一种新的有效寻址形式。重点是它可以更容易地编写与位置无关的代码,因为您可以创建任何内存引用RIP -relative。实际上,RIP -relative寻址是64位应用程序中的默认寻址模式。实际上全部以64位模式寻址存储器的指令是RIP -relative。我会从Ken Johnson (aka Skywing)'s blog引用,因为我不能说任何更好的自己:

一个相对于86的较大(但常常被忽视)改变到x64的是,以前只能通过绝对引用的数据最说明寻址现在可以通过参考数据RIP相对地址寻址。

RIP相对寻址是一种模式,其中地址引用提供为来自当前指令指针的(带符号)32位位移。虽然这通常只在x86上用于控制传输指令(call,jmp和soforth),但x64扩展了指令指针相对寻址的用途,以涵盖更大的一组指令。

使用RIP相对寻址有什么优势?那么,主要好处是,生成位置独立代码或代码不会依赖于它在内存中加载的位置变得更容易。这在包含数据(全局变量)和与之相关的代码的(相对)自包含模块(如DLL或EXE)的当今世界尤其有用。如果在x86上使用平面寻址,则假定模块在其首选基地址处加载,则对全局变量的引用通常需要硬编码所讨论全局的绝对地址。如果在运行时无法将模块加载到首选基地址,加载器必须执行一组基址重定位,其基本上重写所有具有绝对地址操作数组件的指令以考虑新地址该模块。

[。 。 。 ]

但是,如果包含它的模块被重新定位,那么使用RIP相对寻址的指令通常在加载时不需要任何基址重定位(也称为“修正”)。这是因为只要模块的某些部分不在内部重新布置在内存中(PE格式不支持的部分),任何地址引用都是相对于当前指令指针的,并且指的是在当前图像将继续引用正确的位置,无论图像在加载时放置在何处。

因此,许多x64图像的修正数量大大减少,因为大多数操作都可以以RIP相对方式执行。

他是在Windows环境下讲话,但概念上类似的东西也适用于其他操作系统。

您的代码正在将一个常量值(存储在二进制映像的某处)加载到XMM0寄存器中,并且它使用RIP相对寻址进行操作,因为它具有许多优点。

+1

RIP相对寻址IMO的一个被忽略的原因是SIB字节没有64位移位,并且只有* moffs64 *作为立即数的唯一构造是'mov rax,moffs64'('A1')和'mov moffs64,rax'('A3')。我相信RIP相对寻址是为了避免使用64位立即数而不是追求PIC,这是一个副作用。毕竟,这也是RISC解决这个问题的方法。然而这些只是我的两分钱。 –

+1

@MargaretBloom:当AMD在设计AMD64时,i386的PIC开销非常出名(特别是对于那些将GOT指针绑定到寄存器的Linux),我认为在库中放入大量代码是已经开始发生在2000年。我相信PIC的好处是他们想到的,并且即使在低于2GB或4GB的虚拟地址空间之外加载代码+数据也是有效的。 –