AT&T汇编针对x86指令集格式

时间:2022-01-14 03:19:55

目前很多开源基于x86处理器环境的C/C++,Objective-C/C++编译器所带的汇编器使用AT&T格式。AT&T汇编针对x86指令集与其它处理器(比如ARM、Blackfin等)有所不同,它与Intel自定义的汇编格式有比较大的偏差。GCC的汇编器能支持Intel语法特性,可以参考我前面的博文来获悉如何使用。不过对于最新的LLVM2.0,又开始弃用Intel语法特性了。因此不管怎么说了解AT&T汇编语法还是有些好处的,呵呵。

 

首先,AT&T汇编在对于存储器访问上,如果要指定访问字节的宽度,是通过加后缀来实现的,而Intel则是在存储器操作数之前加访问宽度限定词,比如:

(Intel)mov    dword ptr [edx], eax                  (AT&T)movl    %eax, (%edx)

大部分指令对于AT&T使用以下后缀:

b    字节(8位),对应于Intel的byte ptr

w   字(16位),对应于Intel的word ptr

l     双字(实际是表示long,32位),对应于Intel的dword ptr

q    四字(64位),对应于Intel的qword ptr

 

然而,如果是x87浮点指令的话用以下后缀:

s    单精度浮点(short,32位),对应于Intel的dword ptr,如果x87关于存储访问的指令后缀缺省,则默认为后缀s。

l     双精度浮点(long,64位),对应于Intel的qword ptr。

t    扩展双精度浮点(twelve-byte,96位(由于四字节对齐需要,实际精度是80位)),对应于Intel的tbyte ptr。

然后,像FIADD这指令,在AT&A汇编格式中,fiadds表示操作数是short访存数据类型;而fiaddl表示操作数是int访存数据类型。由于操作数是整数类型的存储器寻址,所以后缀s表示short,而l则表示long(不管是32位模式还是64位模式都是32位长)。

 

 

其次,AT&T格式中,指令后面的操作数顺序与Intel格式是相反的。AT&T是源操作数在前,目的操作数在后:

(Intel)mov    rax, rbx                (AT&T)mov    %rbx, %rax

(Intel)mov    eax, cs:var           (AT&T)movl    %cs:var, %eax

(Intel)pinsrw     xmm8, [rdi], 1   (AT&T)pinsrw    $1, (%rdi), %xmm8

(Intel)pshuflw   xmm8, xmm9, 0a1H         (AT&T)pshuflw    $0xa1, %xmm9, %xmm8

 

 

再次,AT&T与Intel语法之间的寻址表示也不同:

(Intel)sub eax, [ebx + ecx * 4h - 20h]          (AT&T)subl    -0x20(%ebx, %ecx, 0x4), %eax

上面表示缩放因子以及偏移量的立即数前不需要加$符号。

 这个可以总结为:Intel的寻址方式若表示为[<base register> + <index register> * <scale> + <offset>],则AT&T表示为:<offset>(<base register>, <index register>, <scale>)

 

最后,AT&T与Intel之间有些指令的表示也不同:

在Intel指令表示中,如果是带符号扩展的MOV指令是用movsx表示,而在AT&T中用movs;而Intel中零扩展MOV指令是movzx,而在AT&T中是用movz表示,比如:

(Intel)movzx    rax, byte ptr [rsi + 3]            (AT&T)movzb   3(%rsi), %rax

(Intel)movsx    rax, word ptr [rsi + 4]           (AT&T)movsw   4(%rsi), %rax

而MOVS指令在Intel指令集中是被废弃的字符串操作指令。

而在AT&T中也能通过REP指令来重复操作存储器拷贝工作,比如在Apple LLVM汇编器中,可以这么写:

// 第一个参数在%rdi,第二个参数在%rsi,第三个参数在%rdx
_MyMemCpy:

mov %rdx, %rcx
rep movsb
ret