我在反汇编程序(浮点逻辑 C++)中找到了以下汇编代码。
842: movss 0x21a(%rip),%xmm0
我知道当进程 rip 总是 842 并且这个 0x21a(%rip) 将是 const 时。使用这个寄存器似乎有点奇怪。
我想知道使用 rip 相对地址而不是其他寻址有什么好处。
我在反汇编程序(浮点逻辑 C++)中找到了以下汇编代码。
842: movss 0x21a(%rip),%xmm0
我知道当进程 rip 总是 842 并且这个 0x21a(%rip) 将是 const 时。使用这个寄存器似乎有点奇怪。
我想知道使用 rip 相对地址而不是其他寻址有什么好处。
RIP
是指令指针寄存器,这意味着它包含紧跟当前指令的指令的地址。
例如,考虑以下代码:
mov rax, [rip]
nop
在第一行代码中,RIP
指向下一条指令,所以它指向NOP
. 因此,此代码将NOP
指令的地址加载到RAX
寄存器中。
因此,这不仅仅是一个RIP
常数。您对RIP
在此过程中“将始终是 842”的理解是不正确的。的值RIP
会根据代码加载到内存的位置而改变。842 只是行号,从您的调试符号中提取;一旦代码被编译成二进制文件,它就不再有行号了。:-)
在您的反汇编中,常数是偏移量 ( 0x21A
)。这是与当前值的偏移量RIP
。另一种写法是:%rip + 0x21A
.
RIP
-相对寻址是64位长模式引入的一种新的有效寻址形式。关键是它使编写与位置无关的代码更容易,因为您可以使任何内存引用RIP
相对。事实上,RIP
-relative 寻址是 64 位应用程序中的默认寻址模式。几乎所有在 64 位模式下寻址内存的指令都是RIP
相对的。我将引用Ken Johnson(又名 Skywing)的博客,因为我自己说得再好不过了:
x64 相对于 x86 的较大(但经常被忽视)的变化之一是,以前仅通过绝对寻址引用数据的大多数指令现在可以通过RIP 相对寻址引用数据。
RIP 相对寻址是一种将地址引用作为当前指令指针的(带符号的)32 位位移提供的模式。虽然这通常仅在 x86 上用于控制传输指令(call、jmp 等),但 x64 扩展了指令指针相对寻址的使用以涵盖更大的指令集。
使用 RIP 相对寻址有什么优势?好吧,主要的好处是生成位置无关代码或不依赖于它在内存中加载位置的代码变得更加容易。这在当今包含数据(全局变量)和随之而来的代码的(相对)独立模块(例如 DLL 或 EXE)的世界中特别有用。如果在 x86 上使用平面寻址,则对全局变量的引用通常需要硬编码所讨论的全局的绝对地址,假设模块在其首选基地址加载。如果模块在运行时无法加载到首选基地址,则加载程序必须执行一组基址重定位这实际上重写了所有具有绝对地址操作数组件的指令,以将模块的新地址考虑在内。
[ . . . ]
然而,如果包含它的模块被重定位,则使用 RIP 相对寻址的指令在加载时通常不需要任何基址重定位(也称为“修复”)。这是因为只要模块的某些部分没有在内存中进行内部重新排列(PE 格式不支持),任何地址引用都与当前指令指针相关,并且引用了范围内的位置当前图像将继续引用正确的位置,无论图像在加载时放置在何处。
因此,许多 x64 图像的修正数量大大减少,因为大多数操作都可以以相对于 RIP 的方式执行。
他是在 Windows 的背景下讲话的,但概念上类似的东西也适用于其他操作系统。
您拥有的代码正在将存储在二进制图像中某处的常量值加载到XMM0
寄存器中,并且由于其许多优点,它使用 RIP 相对寻址这样做。