要了解.s 后缀的含义,您需要了解x86 指令的编码方式。如果我们以adc 为例,操作数可以采取的主要形式有四种:
- 源操作数是立即数,目标操作数是累加器寄存器。
- 源操作数是立即数,目标操作数是寄存器或内存位置
- 源操作数是寄存器,目标操作数是寄存器或内存位置。
- 源操作数是寄存器或内存位置,目标操作数是寄存器。
当然还有针对不同操作数大小的变体:8 位、16 位、32 位等。
当一个操作数是寄存器而另一个是内存位置时,很明显汇编器应该使用形式 3 和 4 中的哪一个,但是当两个操作数都是寄存器时,任何一种形式都适用。 .s 前缀告诉汇编器使用哪种形式(或者在反汇编的情况下,显示已使用的形式)。
看adcb %bl,%dh的具体例子,可以编码的两种方式如下:
10 de adcb %bl,%dh
12 f3 adcb.s %bl,%dh
第一个字节决定了所用指令的形式,我稍后再谈。第二个字节是众所周知的 ModR/M 字节,它指定了寻址模式和使用的寄存器操作数。 ModR/M 字节可以分为三个字段:Mod(最高 2 位)、REG(下 3 位)和 R/M(最后 3 位)。
de: Mod=11, REG = 011, R/M = 110
f3: Mod=11, REG = 110, R/M = 011
如果操作数之一是内存位置,则 Mod 和 R/M 字段共同确定内存位置的有效地址,但当该操作数只是一个寄存器时,Mod 字段设置为 11,并且 R/M是寄存器的值。 REG 字段显然只是代表另一个寄存器。
所以在de字节中,R/M字段保存dh寄存器,REG字段保存bl寄存器。而在f3字节中,R/M字段保存bl寄存器,REG字段保存dh寄存器。 (8位寄存器编码为数字0到7,顺序为al,cl,dl,bl,ah,ch,dh,bh)
回到第一个字节,10 告诉我们使用形式 3 编码,其中源操作数始终是寄存器(即来自 REG 字段),目标操作数是内存位置或寄存器(即由 Mod 和 R/M 字段确定)。 12 告诉我们使用 form 4 编码,其中操作数是相反的——源操作数由 Mod 和 R/M 字段确定,目标操作数来自 REG 字段。
所以寄存器存储在ModR/M字节中的位置被交换,指令的第一个字节告诉我们哪个操作数存储在哪里。