RIP相对寻址可以与另一个寄存器一起使用吗?

5

我熟悉这种形式的内存引用:

XXX ptr [base + index * size + displacement]

其中XXX代表某个大小(字节/字/双字等),baseindex都是寄存器,size是2的幂次方,displacement是有符号值。

amd64引入了rip相对寻址。据我所知,我应该能够使用rip作为基本寄存器。然而,当我尝试在clang-900.0.39.2中这样做时:

mov r8b, byte ptr [rip + rdi * 1 + Lsomething]

I get:

error: invalid base+index expression

mov r8b, byte ptr [rip + rdi * 1 + Lsomething]

当使用rip作为基址寄存器时,是否无法使用索引寄存器?我需要使用lea指令来计算rip + Lsomething,然后进行偏移吗?


2
RIP + Displacement是唯一允许使用RIP相对寻址的寻址方式。因此,您可以使用lea指令获取[rip + Lsomething]并将其放置在通用寄存器中。该寄存器可用于完成其余的地址计算。没有真正的方法可以避开这个限制。 - Michael Petch
1
你可以在Intel® 64和IA-32体系结构软件开发人员手册的第2.2.1.6节“RIP相对寻址”中找到它的文档。 - Michael Petch
请注意,您可能想要使用movzx r8d,byte ptr [...],除非您明确想要将其合并到R8的现有内容中,而不是为该寄存器启动新的依赖链。 - Peter Cordes
@PeterCordes,感谢您的建议。我正在编写一个玩具级别的Meltdown PoC,这是探测负载,因此大小或目的地并不重要。 - zneak
1个回答

7
不,[RIP + rel32]是唯一涉及RIP的寻址模式。有关详细信息,请参见引用内存位置的内容。(x86寻址模式)
如果你想要最大效率地索引静态数组,需要制作依赖位置的代码,这样你就可以将表地址作为32位绝对disp32在普通寻址模式中使用。这在Linux中允许在依赖位置的可执行文件中,但不允许在共享库中(必须是PIC)。请参见32位绝对地址在x86-64 Linux中不再允许?,了解如何在gcc默认配置下使用-fno-pie -no-pie来制作非PIE二进制文件。
否则,对于独立于位置的数组索引,使用lea rsi, [rip + Lsomething]并使用指针增量或[rsi + rdi*1 + constant]寻址模式,或任何其他适用的替代方法。 (非索引寻址模式有时可以在Intel CPU上节省一个uop,所以指针增量可能很好,特别是如果你展开了循环,那么每个指针的add操作可以超过自身的成本,与使用相同的索引来处理多个数组相比。)
在任意寻址模式中,RIP不是“基础寄存器”;机器码编码中没有足够的空间。 x86-64有16个可能的基础寄存器,用ModR/M或SIB字节的3位+可选REX前缀的1位进行编码。将RIP用作任意寻址模式的基础需要挤出其他一些寄存器,并在32位和64位模式之间创建大量的有效地址解码差异。
x86-32有2种冗余方式来编码[0x123456],即无基础+disp32:带有或不带有SIB字节,因为SIB具有无基础和无索引的编码。请参见http://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing以获取漂亮的表格,或查看英特尔的手册。
no-index SIB编码使得可以对[esp+esp]进行编码,而不是[esp],因为ModR/M编码意味着基址(RSP)等于转义码"SIB"。他们本来可以设计成可以使用esp作为除了esp之外的其他基址的索引,但是没有人想在第一位使用esp作为索引。有趣的事实是,没有基址(带disp32)的编码使用本来会是没有位移的[ebp],这就是为什么[ebp]实际上编码为[ebp + disp8 = 0]的原因。在x86-64模式下,这也适用于R13。

x86-64将较短的(无SIB)编码重新定位为[disp32][RIP + disp32],也就是[RIP + rel32]

32位绝对地址([disp32])仍可通过更长的SIB编码进行编码。(这是NASM默认使用的方式,如果你不使用default rel)。甚至没有一个[RIP + disp8] (例如,用于加载附近的代码地址)。在Mod和M字段中,恰好有一个位模式编码了RIP相对地址。


感谢您提供详细的答案!让我找出了问题所在 我需要修复SIB disp32-only编码。公平地说,当我将index=4特殊情况泛化以支持除SIB=24h(S=0 I=4 B=4)之外的其他情况时,我引入了这个错误,但没有意识到mod=0 index=4 base=5实际上是两种特殊情况的结合体。这提醒我,您的文本并不完全准确:由于S(比例)字段被忽略,因此SIB disp32-only编码实际上可以用4种不同的方式进行编码,其中S等于0、1、2或3。 - ecm
1
@ecm:有趣的观点!如果有用的话,可能还有一些新的寻址模式可以使用。但是没有干净的方法来涉及寄存器编号,所以我可以理解为什么它不适合作为一个好的编译器目标。挤入几个有限的寄存器选择只会让解码变得痛苦,而且我看不到只有32位常数和RIP或非RIP值得做的事情。通过添加或减去(或缩放)32位数字来获得更多范围并不需要足够的价值。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接