Linux 上 x86 的内存寻址模式解释

Memory addressing mode interpretation for x86 on Linux

我正在通读 Programming from the ground up by Jonathan Bartlett。作者讨论了内存寻址方式,指出内存地址引用的一般形式是这样的:

ADDRESS_OR_OFFSET (%BASE_OR_OFFSET, %INDEX, MULTIPLIER)

其中最终地址是这样计算的:

FINAL_ADDRESS = ADDRESS_OR_OFFSET + %BASE_OR_OFFSET + MULTIPLIER * %INDEX.

还指出,如果任何部分被遗漏,它只是在等式中用零代替。 ADDRESS_OR_OFFSETMULTIPLIER要求是常量,其他元素要求是寄存器。这些似乎是唯一指定的一般规则。

到目前为止,还不错。

作者接着讨论了间接寻址方式并给出了例子:

movl (%eax), %ebx

将存储在 eax 寄存器中的地址值移动到 ebx 寄存器。

要使其生效,(%eax) 应解释为 0(%eax,0,0) 而不是 0(0,%eax,0)。是否有执行此解释的附加规则?

书中的解释并非100%正确。 x86架构有以下32位寻址方式:

$imm                         immediate     result = imm
%reg                         register      result = reg
disp(%reg)                   indirect      result = MEM[disp + reg]
disp                         direct        result = MEM[disp]
disp(%base, %index, %scale)  SIB           result = MEM[disp + base + index * scale]

在 SIB (scale/index/base) 和间接寻址模式中,disp 可以省略 0 字节位移。在SIB寻址方式下,另外可以省略baseindex,表示0标度,0索引;实际上不能遗漏比例。请注意,当我说“省略”时,只有值被省略;逗号被留在。例如,(,,1) 表示“SIB 操作数没有位移,没有基数,没有索引,和 1 个比例。”

在 64 位模式下,还可以使用 rip 相对寻址模式:

disp(%rip)                   rip relative  result = MEM[disp + rip]

这种寻址模式对于编写与位置无关的代码很有用。

16 位模式有不同的寻址模式,但它们并不重要,所以我不打算详细说明它们。

因此对于您的示例:这很容易理解,因为它实际上是 间接 寻址模式,而不是 SIB 寻址模式 eax作为寄存器,没有位移。

我也在读这本书,注意到其中的代码示例与您在 Internet 上找到的其他代码示例略有不同。这是因为:

The syntax for assembly language used in this book is known at the AT&T syntax. It is the one supported by the GNU tool chain that comes standard with every Linux distribution. However, the official syntax for x86 assembly language (known as the Intel® syntax) is different.

关于这个问题,我查到了更多资料here:

The base, index and displacement components can be used in any combination, and every component can be omitted; omitted components are excluded from the calculation above. If index register is missing, the pointless scale factor must be omitted as well.