zne*_*eak 5 assembly x86-64 addressing-mode
我熟悉这种形式的内存引用:
XXX ptr [base + index * size + displacement]
Run Code Online (Sandbox Code Playgroud)
其中XXX是一些尺寸(字节/字/ DWORD /等),两者base和index是寄存器,size为二的小功率,并且displacement是带符号的值。
amd64 引入了 rip 相对寻址。据我了解,我应该可以rip用作基址寄存器。但是,当我使用 clang-900.0.39.2 尝试此操作时:
mov r8b, byte ptr [rip + rdi * 1 + Lsomething]
Run Code Online (Sandbox Code Playgroud)
我得到:
错误:无效的基数+索引表达式
Run Code Online (Sandbox Code Playgroud)mov r8b, byte ptr [rip + rdi * 1 + Lsomething]
rip用作基址寄存器时是否不能使用索引寄存器?我是否必须使用lea计算rip + Lsomething然后抵消它?
不,[RIP + rel32]是唯一涉及 RIP 的寻址模式。 另请参阅引用内存位置的内容。(x86 寻址模式)。
如果您希望索引静态数组的效率最高,则需要编写位置相关代码,以便disp32在正常寻址模式下将表地址用作 32 位绝对地址。这在 Linux 中与位置相关的可执行文件中是允许的,但不允许共享库(必须是 PIC)。看到x86-64 Linux 中不再允许使用 32 位绝对地址了吗?-fno-pie -no-piegcc 默认配置为制作 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],即 no-base + disp32:有或没有 SIB 字节,因为 SIB 具有无基数和无索引的编码。请参阅http://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing以获取漂亮的表格,或参阅 Intel 的手册。
无索引 SIB 编码使编码[esp]而不是成为可能[esp+esp],因为表示 base=RSP 的 ModR/M 编码是表示“存在 SIB”的转义码。他们本来可以设计它,以便您可以将其esp用作除 以外的基数的索引esp,但首先没有人想esp用作索引。有趣的事实:无基(with disp32)编码使用[ebp]没有位移的[ebp]编码,这就是为什么实际上编码为[ebp + disp8=0]. 在 x86-64 模式下,这也适用于 R13。
x86-64 将更短的(无 SIB)编码重新用于[disp32]into[RIP + disp32],也就是[RIP + rel32]。
32 位绝对地址 ( [disp32]) 仍然可以使用更长的 SIB 编码进行编码。(这是 NASM 默认情况下所做的,如果您不使用default rel。)甚至没有[RIP + disp8](例如用于加载附近的代码地址)。ModR/M 字节的 Mod 和 M 字段中恰好有一个位模式,用于编码 RIP 相对地址。