X86汇编语言风格比较: AT&T 和 Intel 风格
大学期间使用Windows平台的研究较多. 基本接触的程序是Win平台的. IDE用Visual C++ , 自然一般也只会接触到$MS的编译器. 而微软家使用的是Intel 风格的汇编语言. 而在开源界,特别是基于
GCC
的编译器是使用的是AT&T
风格的汇编 . 这里简单记录下两者的区别. 以备查. Linux小白. 如有纰漏, 大家请轻拍.
注: 这里是为了研究语言底层的实现机制,同时研究一些JVM的底层实现逻辑需要. 所以需要对栈帧的结构进行较细致的研究. 因此需要了解编译后的语言的反汇编.
差异对比
寄存器命名
在
AT&T
风格中,寄存器会加上%
作为前缀.而Intel
汇编中寄存器名是不需要加前缀的.可以直接使用.
AT&T风格 | Intel风格 | 说明 |
---|---|---|
push %eax | push eax | 这是一条入栈指令,把寄存器eax 中的值压入栈中 |
立即数格式
- 在
AT&T
汇编中 , 用$
前缀表示一个立即数.- 在
Intel
汇编中 , 立即数没有任何前缀. 直接用一个数字表示. (当然有不同的进制. 比如 0x01 , 10 等)
AT&T风格 | Intel风格 | 说明 |
---|---|---|
push $1 | push 1 | 把一个立即数压入栈中 |
操作数顺序
AT&T和Intel格式中的源操作数和目标操作数的位置正好相反
下面是给寄存器EAX
赋一个初值1.
AT&T
风格:操作符 源操作数 , 目的操作数
mov $1 , %eaxIntel 风格:
操作符 目的操作数 , 源操作数
mov eax , 1
AT&T风格 | Intel风格 | 说明 |
---|---|---|
push $1 | push 1 | 把一个立即数压入栈中 |
内存操作数的寻址方式
AT&T
寻址格式:section:disp(base, index, scale)
Intel 寻址格式
:section:[base + index*scale + disp]
无论形式如何,都是实现如下的地址计算:(其中base和index必须是寄存器,disp和scale可以是常数)
disp + base + index * scale
中文释义: 地址或偏移 (%基址或偏移量寄存器, %索引寄存器, 比例因子)
计算方法: 最终地址 = 地址或偏移 + %基址或偏移量寄存器 + %索引寄存器 * 比例因子
寻址参考: AT&T汇编语言及其寻址方式
AT&T格式 | Intel格式 |
---|---|
movl -4(%ebp), %eax | mov eax, [ebp - 4] |
movl array(, %eax, 4), %eax | mov eax, [eax*4 + array] |
movw array(%ebx, %eax, 4), %cx | mov cx, [ebx + 4*eax + array] |
movb $4, %fs:(%eax) | mov fs:eax, 4 |
注: 关于寻址中的段寻址知识参考:
目前在Linux环境上,不管是32位还是64位操作系统段寻址都被废弃. 直接使用的线性地址. 不过fs
和gs
在特殊情况下还有使用.
数据宽度表示
- 在AT&T汇编格式中,操作数的字长由操作符的最后一个字母决定,
后缀b
、w
、l
分别表示操作数为字节(byte,8比特)、字(word,16比特)和长字(long,32比特);- 而在Intel汇编格式中,操作数的字长是用
byte ptr
和word ptr
等前缀来表示的
AT&T格式 | Intel格式 |
---|---|
movb val, %al | mov al, byte ptr val |
反汇编示例
源代码如下:
int add(int x , int y)
{
int sum = 0;
sum = x + y;
return sum;
}
下面是一段C语言函数的反汇编表示.
注: 这些代码都是GCC编译的. 编译后的结果可以使用如下命令来分别输出不同风格的反汇编代码.
- 直接输出
AT&T
风格:objdump -S main.out
- 输出Intel风格:
objdump -S main.out
- 或者这样:
objdump -d -mi386:x86-64:intel main.out
具体可以参考:gcc和objdump能生成Intel汇编吗?AT&T格式汇编看着不太习惯
对于gdb调试也可以使用命令:(gdb) set disassembly-flavor intel进行汇编风格设定.
AT&T
风格
000000000040056a <add>:
40056a: 55 push %rbp
40056b: 48 89 e5 mov %rsp,%rbp
40056e: 89 7d ec mov %edi,-0x14(%rbp)
400571: 89 75 e8 mov %esi,-0x18(%rbp)
400574: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
40057b: 8b 55 ec mov -0x14(%rbp),%edx
40057e: 8b 45 e8 mov -0x18(%rbp),%eax
400581: 01 d0 add %edx,%eax
400583: 89 45 fc mov %eax,-0x4(%rbp)
400586: 8b 45 fc mov -0x4(%rbp),%eax
400589: 5d pop %rbp
40058a: c3 retq
40058b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
Intel
风格
000000000040056a <add>:
40056a: 55 push rbp
40056b: 48 89 e5 mov rbp,rsp
40056e: 89 7d ec mov DWORD PTR [rbp-0x14],edi
400571: 89 75 e8 mov DWORD PTR [rbp-0x18],esi
400574: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
40057b: 8b 55 ec mov edx,DWORD PTR [rbp-0x14]
40057e: 8b 45 e8 mov eax,DWORD PTR [rbp-0x18]
400581: 01 d0 add eax,edx
400583: 89 45 fc mov DWORD PTR [rbp-0x4],eax
400586: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
400589: 5d pop rbp
40058a: c3 ret
40058b: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
参考文档
- can-i-print-the-gdtr-and-gdt-descriptor-under-gdb
- GDB调试指南
- # linux下gdb常用的调试命令
- # Linux从头学11:理解了这三个概念,才能彻底理解任务管理和任务切换
- Intel 64/x86_64/IA-32/x86处理器段寄存器 - 32位段寄存器/64位段寄存器
- x86段寄存器和分段机制
- 汇编语言如何注释?at&t注释为#号
- ## objdump反汇编对于小白的一个坑
- # gdb,objdump显 intel语法
- Differences in Intel (NASM) vs AT&T (GAS) Syntax.
- # Linux assemblers: A comparison of GAS and NASM
- Red Hat Enterprise Linux 3: Using as, the Gnu Assembler - # AT&T Syntax versus Intel Syntax
- # Intel vs. AT&T syntax
- CSCI 223 Computer Organisation and Assembly Language
- https://stackoverflow.com/questions/972602/limitations-of-intel-assembly-syntax-compared-to-att
- [中英对照]INTEL与AT&T汇编语法对比
- # GCC汇编语法与Intel汇编语法的几个差异点
- Intel汇编指令格式解析
- # Linux下AT&T汇编语法格式与Intel汇编语法格式异同
- Linux下AT&T汇编语法格式与Intel汇编语法格式异同
- AT&T汇编语言及其寻址方式
- # gcc命令objdump用法----反汇编
- # AT&T与Intel汇编语言的比较
- 汇编格式 AT&T 与 Intel