我正在从头开始阅读Jonathan Bartlett的《编程》。作者讨论了存储器寻址模式,并指出存储器地址引用的一般形式是:
ADDRESS_OR_OFFSET (%BASE_OR_OFFSET, %INDEX, MULTIPLIER)
其中最终地址是这样计算的:
CCD_ 2。
还指出,如果遗漏了任何一个片段,则在方程中用零代替。ADDRESS_OR_OFFSET
和MULTIPLIER
需要是常数,而其他元素需要是寄存器。这些似乎是唯一规定的一般规则。
到目前为止,一切都很好。
然后讨论了间接寻址方式,并举例说明:
movl (%eax), %ebx
其将存储在CCD_ 6寄存器中的地址处的值移动到CCD_。
为了实现这一点,(%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(缩放/索引/基)和间接寻址模式中,disp
可以被排除在0字节位移之外。在SIB寻址模式中,额外的base
和index
可以被排除在0标度、0索引之外;规模实际上不能被忽略。请注意,当我说"忽略"时,只有值被忽略;逗号保留在中。例如,(,,1)
表示"没有位移、没有基数、没有索引和1个小数位数的SIB操作数。">
在64位模式中,还可使用rip
相对寻址模式:
disp(%rip) rip relative result = MEM[disp + rip]
这种寻址模式对于编写与位置无关的代码非常有用。
16位模式有不同的寻址模式,但它们并不重要,所以我不打算详细说明它们。
因此,对于您的示例:这很容易理解,因为它实际上是一种间接寻址模式,而不是以eax
为寄存器且没有位移的SIB地址模式。
我也在读这本书,并注意到代码示例与您可能在互联网上找到的其他代码示例略有不同。这是因为:
本书中使用的汇编语言的语法在at&T语法。它是GNU工具链支持的工具链,每个Linux发行版。但是,x86汇编语言的官方语法(称为"英特尔®;"语法)不同。
关于这个问题,我在这里找到了更多信息:
基、指数和位移分量可以任意组合使用,每个分量都可以省略;省略的组件被排除在上面的计算之外。如果索引寄存器丢失,那么也必须省略无意义的比例因子。