Intel手册中对此有描述:
3.7.5 指定偏移量
内存地址的偏移部分可以直接指定为静态值(称为位移)或通过
由以下一个或多个组件组成的地址计算:
-
移位— 8 位、16 位或 32 位值。
-
Base— 通用寄存器中的值。
-
Index— 通用寄存器中的值。 [不能是 ESP/RSP]
-
比例因子— 值 2、4 或 8 乘以索引值。
添加这些组件所产生的偏移量称为有效地址。
比例因子被编码为 2 位移位计数 (0,1,2,3),比例因子为 1、2、4 或 8。是的,*1
(shift count = 0) 是默认值,如果你写(%edi, %edx)
;这相当于(%edi, %edx, 1)
在 AT&T 语法中,它是disp(base, index, scale)
- 常量位于括号之外。一些 Intel 语法汇编器也允许类似的语法1234[ebx]
,其他人则没有。但 AT&T 语法很严格;寻址模式的每个组件都可以only去它适当的地方。例如:
movzwl foo-0x10(,%edx,2), %eax
从地址将零扩展 16 位(“字”)加载到 EAX 中foo-0x10 + edx*2
。 EDX 是索引寄存器,比例因子为 2。没有基址寄存器。foo
and -0x10
都是位移的一部分,都是链接时间常数。foo
是一个符号地址,链接器将填充该地址并从中减去 0x10(因为-0x10
组装时间偏移)。
如果可以选择,请仅使用基数而不是小数位数为 1 的索引。索引需要 SIB 字节进行编码,从而使指令更长。这就是为什么编译器选择这样的寻址模式8(%ebp)
访问堆栈内存,而不是8(,%ebp)
.
也可以看看引用内存位置的内容。 (x86 寻址模式) https://stackoverflow.com/questions/34058101/referencing-the-contents-of-a-memory-location-x86-addressing-modes有关何时可以使用基数和/或索引和/或位移的更多信息。
16 位位移只能在 16 位寻址模式下进行编码,该模式使用不同的格式 https://stackoverflow.com/questions/55657904/why-dont-x86-16-bit-addressing-modes-have-a-scale-factor-while-the-32-bit-vers不能包含比例因子,并且有一个非常寄存器的选择有限 https://stackoverflow.com/questions/12474010/nasm-x86-16-bit-addressing-modes可以是基数或索引。
所以像这样的模式1234(%edx)
必须将 1234 编码为 32 位disp32
在 32 位机器代码中。
-128 .. +127 的字节偏移可以使用短格式 8 位编码。您的汇编器将为您处理这个问题,使用最短的有效编码进行位移。
所有这些在 64 位寻址模式下的 64 位模式中都是相同的,disp32 也像 disp8 一样被符号扩展为 64 位。
64位模式确实添加了新的不同寻址模式,symbol(%rip)
它不适用于任何通用寄存器,仅适用于 RIP 的 32 位偏移量。看x86-64 GAS Intel 语法中的 RIP 相对变量引用(如“[RIP + _a]”)如何工作? https://stackoverflow.com/questions/54745872/how-do-rip-relative-variable-references-like-rip-a-in-x86-64-gas-intel-sy其中还涵盖了 AT&T 语法。