为什么此MOVSS指令使用相对RIP寻址? [英] Why does this MOVSS instruction use RIP-relative addressing?

查看:811
本文介绍了为什么此MOVSS指令使用相对RIP寻址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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



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

解决方案

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



例如,考虑以下代码:

  mov rax,[rip] 
nop

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



因此, RIP 仅仅是一个常量,这不是不是。您对 RIP 在此过程中将始终为842的理解是不正确的。 RIP 的值将更改,具体取决于将代码加载到内存中的位置。 842只是从调试符号中提取的行号;将代码编译成二进制文件后,就不再有行号了。 :-)



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



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


相对于x86,对x64进行的较大(但经常被忽略)更改是,大多数指令以前只是通过绝对寻址的引用数据现在可以通过 RIP相对寻址来引用数据。



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



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

[。 。 。 ]



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



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


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



您的代码正在将存储在二进制映像中某个位置的常量值加载到 XMM0 寄存器,由于它具有许多优点,因此它使用RIP相对寻址。


I found the following assembly code in disassembler (floating point logic c++).

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

I understand that when process rip will allways be 842 and this 0x21a(%rip) will be const. It seems a little odd to use this register.

I want to know is there any advantage of using rip relative address, instead other addressing.

解决方案

RIP is the instruction pointer register, which means that it contains the address of the instruction immediately following the current instruction.

For example, consider the following code:

mov  rax, [rip]
nop

In the first line of code there, RIP points to the next instruction, so it points at the NOP. Thus, this code loads the address of the NOP instruction into the RAX register.

As such, it is not the case that RIP is simply a constant. Your understanding that RIP in this process "will always be 842" is not correct. The value of RIP will change, depending on where the code has been loaded into memory. 842 is just the line number, pulled from your debugging symbols; once code is compiled into a binary, it doesn't have line numbers anymore. :-)

In your disassembly, the constant is the offset (0x21A). That's the offset from the current value in RIP. Another way of writing this is: %rip + 0x21A.

RIP-relative addressing is a new form of effective addressing introduced with 64-bit long mode. The point is that it makes it easier to write position-independent code because you can make any memory reference RIP-relative. In fact, RIP-relative addressing is the default addressing mode in 64-bit applications. Virtually all instructions that address memory in 64-bit mode are RIP-relative. I'll quote from Ken Johnson (aka Skywing)'s blog because I couldn't say it any better myself:

One of the larger (but often overlooked) changes to x64 with respect to x86 is that most instructions that previously only referenced data via absolute addressing can now reference data via RIP-relative addressing.

RIP-relative addressing is a mode where an address reference is provided as a (signed) 32-bit displacement from the current instruction pointer. While this was typically only used on x86 for control transfer instructions (call, jmp, and soforth), x64 expands the use of instruction pointer relative addressing to cover a much larger set of instructions.

What’s the advantage of using RIP-relative addressing? Well, the main benefit is that it becomes much easier to generate position independent code, or code that does not depend on where it is loaded in memory. This is especially useful in today’s world of (relatively) self-contained modules (such as DLLs or EXEs) that contain both data (global variables) and the code that goes along with it. If one used flat addressing on x86, references to global variables typically required hardcoding the absolute address of the global in question, assuming the module loads at its preferred base address. If the module then could not be loaded at the preferred base address at runtime, the loader had to perform a set of base relocations that essentially rewrite all instructions that had an absolute address operand component to refer to take into account the new address of the module.

[ . . . ]

An instruction that uses RIP relative addressing, however, typically does not require any base relocations (otherwise known as "fixups") at load time if the module containing it is relocated, however. This is because as long as portions of the module are not internally re-arranged in memory (something not supported by the PE format), any addresses reference that is both relative to the current instruction pointer and refers to a location within the confines of the current image will continue to refer to the correct location, no matter where the image is placed at load time.

As a result, many x64 images have a greatly reduced number of fixups, due to the fact that most operations can be performed in an RIP-relative fashion.

He's speaking in the context of Windows, but something conceptually similar applies on other operating systems as well.

The code you have is loading a constant value, stored somewhere in the binary image, into the XMM0 register, and it's doing so using RIP-relative addressing because of its many advantages.

这篇关于为什么此MOVSS指令使用相对RIP寻址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆