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

Vla*_*iev 2 x86 assembly x86-64 disassembly addressing-mode

我在反汇编程序(浮点逻辑c ++)中找到了以下汇编代码。

  842: movss  0x21a(%rip),%xmm0 
Run Code Online (Sandbox Code Playgroud)

我知道,当进程rip将一直为842且此0x21a(%rip)将为const。使用该寄存器似乎有些奇怪。

我想知道使用rip相对地址代替其他寻址有什么好处。

Cod*_*ray 6

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

例如,考虑以下代码:

mov  rax, [rip]
nop
Run Code Online (Sandbox Code Playgroud)

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

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

在您的反汇编中,常数为偏移量(0x21A)。那是与中的当前值的偏移量RIP。另一种写法是:%rip + 0x21A

RIP相对寻址是64位长模式引入的一种有效寻址的新形式。关键是它使编写位置无关的代码变得更加容易,因为您可以使任何与内存相关的引用RIP都是相对的。实际上,RIP-relative寻址是64位应用程序中的默认寻址模式。几乎所有以64位模式寻址存储器的指令都是RIP相对的。我引用肯·约翰逊(又名Skywing)的博客,因为我自己无法说得更好:

与x86相比,x64的较大更改(但经常被忽略)之一是,以前仅通过绝对寻址引用数据的大多数指令现在可以通过RIP相对寻址引用数据。

RIP相对寻址是一种模式,其中提供的地址参考是距当前指令指针的32位(有符号)位移。虽然通常只在x86上将其用于控制​​传输指令(调用,jmp和forsoth),但x64扩展了指令指针相对寻址的使用,以覆盖更多的指令集。

使用RIP相对寻址的优势是什么?好吧,主要好处是生成位置无关的代码或不依赖于其在内存中加载位置的代码变得容易得多。在当今(相对)自包含的模块(例如DLL或EXE)中,该模块既包含数据(全局变量),也包含与之相关的代码,这尤其有用。如果在x86上使用了平面寻址,则假设模块在其首选基地址处加载,则通常需要对全局变量进行引用来对所涉及全局的绝对地址进行硬编码。如果随后无法在运行时将模块加载到首选基址,则加载器必须执行一组基址重定位 实质上重写了具有绝对地址操作数组件以供参考的所有指令,以考虑到模块的新地址。

[。。。]

但是,如果使用包含RIP相对寻址的指令进行了重定位,则在加载时通​​常不需要任何基址重定位(也称为“固定”)。这是因为,只要模块的各部分没有在内存中进行内部重新排列(PE格式不支持该内容),任何地址引用都相对于当前指令指针,并且引用该地址范围内的某个位置无论在加载时将图像放置在哪里,当前图像都将继续引用正确的位置。

结果,由于大多数操作可以以相对RIP的方式执行,因此许多x64图像的固定数量大大减少。

他是在Windows环境下发言,但是在概念上类似的内容也适用于其他操作系统。

您拥有的代码将一个常量值(存储在二进制映像中的某个位置)加载到XMM0寄存器中,并且由于其许多优点,因此使用RIP相对寻址来实现。

  • @MargaretBloom:当 AMD 设计 AMD64 时,i386 的 PIC 开销是众所周知的(特别是对于 Linux,它绑定了一个指向 GOT 的指针的寄存器),而且我认为在库中放入大量代码已经是早在 2000 年就开始发生了。我确信 PIC 的好处是他们想到的,并且即使在低 2 或 4GB 的虚拟地址空间之外加载时也允许代码+数据高效。 (3认同)
  • RIP 相对寻址 IMO 的一个被忽视的原因是 SIB 字节没有 64 位位移,唯一将 *moffs64* 作为立即数的指令是`mov rax、moffs64`(`A1`)和`mov moffs64,rax`(`A3`)。我相信引入 RIP 相对寻址是为了避免 64 位立即数而不是追求 PIC,这是一个副作用。毕竟,这也是 RISC 中解决这个问题的方式。然而,这些只是我的两分钱。 (2认同)