英特尔和AT&T语法中内存寻址的一般形式如下:
[base + index*scale + disp] # Intel, including GAS .intel_syntax noprefix
disp(base, index, scale) # AT&T
我的问题如下:
base
和index
可以是任何寄存器吗?scale
可以取什么值,是1、2、4和8吗(默认值为1)?index
和disp
是否可以互换(唯一的区别是index
是寄存器,而disp
是立即数)?
1条答案
按热度按时间dpiehjr41#
英特尔手册中对此进行了说明:
3.7.5指定偏移量
内存地址的偏移量部分可以直接指定为静态值(称为位移),也可以通过由以下一个或多个组件组成的地址计算来指定:
*位移-8、16或32位值。
*Base-通用寄存器中的值。
*索引-通用寄存器中的值。[不能是ESP/RSP]
*比例因子-2、4或8的值乘以索引值。
将这些分量相加得到的偏移量称为有效地址。
对于比例因子1、2、4或8,比例因子编码为2位移位计数(0、1、2、3)。是的,如果您写入
(%edi, %edx)
,则*1
(移位计数= 0)是默认值;相当于(%edi, %edx, 1)
在AT&T的语法中,
disp(base, index, scale)
-常量在括号外。一些英特尔语法汇编器也允许1234[ebx]
这样的语法,其他的则不允许。但是AT&T的语法是严格的;寻址模式的每一个组成部分都 * 只能 * 在其适当的位置。例如:执行零扩展16位(“字”)从地址
foo-0x10 + edx*2
加载到EAX。EDX是变址寄存器,比例因子为2。没有基址寄存器。foo
和-0x10
都是位移的一部分,两个链接时间常数。foo
是链接器将填入并从中减去0x 10的符号地址(因为-0x10
汇编时间偏移量)。如果可以选择的话,只使用基址,而不使用小数位数为1的索引。索引需要一个SIB字节来编码,这会使指令更长。这就是为什么编译器选择
8(%ebp)
这样的寻址模式来访问堆栈内存,而不是8(,%ebp)
。另请参阅Referencing the contents of a memory location. (x86 addressing modes),以取得有关何时可以使用基底、和/或索引、和/或置换的详细信息。
16位位移量只能在16位寻址模式下进行编码,该模式使用不能包含比例因子的different format,并且limited selection of which registers可以是基址或索引。
因此,像
1234(%edx)
这样的模式必须将1234编码为32位机器码中的32位disp32
。从-128到+127的字节偏移量可以使用短格式的8位编码。您的汇编程序将为您处理这一问题,对偏移量使用最短的有效编码。
对于64位寻址模式,所有这些在64位模式中是相同的,disp 32也像disp 8一样被符号扩展到64位。
64-位模式确实增加了一种新的不同寻址模式,
symbol(%rip)
,它不适用于任何通用寄存器,仅与RIP有32位偏移。参见 * How do RIP-relative variable references like "[RIP + _a]" in x86-64 GAS Intel-syntax work? *,其中也介绍了AT&T语法。