关于 [base + index*scale + disp] 和 AT&T disp(base, index, scale) 的几个问题

A couple of questions about [base + index*scale + disp] and AT&T disp(base, index, scale)

Intel 和 AT&T 语法中内存寻址的一般形式如下:

[base + index*scale + disp]      # Intel, including GAS .intel_syntax noprefix
disp(base, index, scale)         # AT&T

我的问题如下:

这在英特尔的手册中有描述:

3.7.5 Specifying an Offset
The offset part of a memory address can be specified directly as a static value (called a displacement) or through an address computation made up of one or more of the following components:

  • Displacement — An 8-, 16-, or 32-bit value.
  • Base — The value in a general-purpose register.
  • Index — The value in a general-purpose register. [can't be ESP/RSP]
  • Scale factor — A value of 2, 4, or 8 that is multiplied by the index value.

添加这些组件产生的偏移称为有效地址。

比例因子被编码为 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 语法 assemblers 也允许像 1234[ebx] 这样的语法,其他的则不允许。但是 AT&T 的语法是死板的;寻址模式的每一个组成部分都只能进入其适当的位置。例如:

movzwl  foo-0x10(,%edx,2), %eax

从地址 foo-0x10 + edx*2 将零扩展 16 位(“字”)加载到 EAX 中。 EDX 是索引寄存器,比例因子为 2。没有基址寄存器。 foo-0x10都是位移的一部分,都是link-时间常数。 foo 是一个符号地址,linker 将填写并从中减去 0x10(因为 -0x10 assemble-时间偏移)。

如果可以选择,请仅使用基数而不是标度为 1 的索引。索引需要 SIB 字节进行编码,从而使指令更长。这就是为什么编译器选择像 8(%ebp) 这样的寻址模式来访问堆栈内存,而不是 8(,%ebp).

另请参阅 以了解有关何时可以使用基数、and/or 索引、and/or 位移的更多信息。


16 位位移只能在 16 位寻址模式下编码,它使用 different format that can't include a scale factor, and has a very limited selection of which registers 可以是基数或索引。

所以像 1234(%edx) 这样的模式必须在 32 位机器代码中将 1234 编码为 32 位 disp32

从 -128 到 +127 的字节偏移可以使用短格式的 8 位编码。您的 assembler 会为您解决这个问题,使用最短的有效位移编码。


对于 64 位寻址模式,所有这些在 64 位模式下都是相同的,disp32 也像 disp8 一样被符号扩展为 64 位。