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_OFFSET
和MULTIPLIER
要求是常量,其他元素要求是寄存器。这些似乎是唯一指定的一般规则。
到目前为止,还不错。
作者接着讨论了间接寻址方式并给出了例子:
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寻址方式下,另外可以省略base
和index
,表示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.
我正在通读 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_OFFSET
和MULTIPLIER
要求是常量,其他元素要求是寄存器。这些似乎是唯一指定的一般规则。
到目前为止,还不错。
作者接着讨论了间接寻址方式并给出了例子:
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寻址方式下,另外可以省略base
和index
,表示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.