前言&吐槽
汇编代码有两种格式,Intel 格式和 AT&T 格式。这两种只是语法格式上的区别,内容是一样的。
之前想偷懒用 IDA 把 Bomb Lab 写了就算了(见上一篇 Bomb Lab 题解),但 IDA 是 Intel 格式的汇编代码,然后昨天听说考试会给出 AT&T 格式的汇编代码要求瞪眼法去读……哎,早知道当时就把 IDA 设置成 AT&T 格式了,还能熟悉一下语法。
然后还要吐槽一下,如果你自己看过 objdump 和 gdb 产生的线性内容,再对比一下我上一篇的题解,应该能知道我想说什么。尽管 IDA 只是加了几个箭头,原本的汇编代码只是差了几个箭头……但这差距也太大了吧!!!地址跳转这种计算机最擅长人类最不会的操作为什么要我自己瞪眼法啊!!!!!
为什么考试不是现场断网发放新炸弹现场拆弹啊,你管我用什么工具和方法呢😭
作为对比,放phase_2在这作为对比。
下面是 objdump 生成的汇编代码。
0000000000400efc <phase_2>:
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 call 40145c <read_six_numbers>
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30 <phase_2+0x34>
400f10: e8 25 05 00 00 call 40143a <explode_bomb>
400f15: eb 19 jmp 400f30 <phase_2+0x34>
400f17: 8b 43 fc mov -0x4(%rbx),%eax
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25 <phase_2+0x29>
400f20: e8 15 05 00 00 call 40143a <explode_bomb>
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17 <phase_2+0x1b>
400f2e: eb 0c jmp 400f3c <phase_2+0x40>
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17 <phase_2+0x1b>
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 ret
这是什么呢?不知道?那我们来看看 IDA 的。

这不就是一个简单的循环嘛!一目了然。
好了吐槽环节结束,下面来看看 Intel 和 AT&T 的语法区别吧。当然,是 AI 生成的内容,我只是做了简单的修改。
正文
Intel 与 AT&T 汇编语法对比详解
这两种语法描述的是同一套 x86/x86-64 指令集,只是书写风格不同。Intel 语法常见于 Windows、NASM、MASM;AT&T 语法常见于 Linux、GCC、GDB。
一、核心差异总览
| 特性 | Intel | AT&T |
|---|---|---|
| 操作数顺序 | op 目的, 源 |
op 源, 目的 |
| 寄存器前缀 | 无,如 eax |
加 %,如 %eax |
| 立即数前缀 | 无,如 10 |
加 $,如 $10 |
| 十六进制 | 0AH 或 0x0A |
$0xA |
| 内存寻址 | [ebx+esi*4+8] |
8(%ebx,%esi,4) |
| 指令大小后缀 | 无(靠操作数推断) | 有:b/w/l/q |
| 间接跳转/调用 | jmp eax |
jmp *%eax |
| 注释 | ; 或 ; |
# 或 /* */ |
二、操作数顺序(最容易出错!)
Intel:目的在前,源在后 AT&T:源在前,目的在后(与英语"从 A 到 B"一致)
Intel: mov eax, ebx ; 把 ebx 的值 → eax
AT&T: movl %ebx, %eax # 把 ebx 的值 → eax
三、前缀符号
AT&T 要求显式标明操作数类型:
%寄存器:%eax、%rbx、%r10$立即数:$100、$0xFF- 直接内存地址:不加前缀,如
movl foo, %eax
Intel: mov eax, 5
AT&T: movl $5, %eax
四、指令大小后缀(AT&T 独有)
AT&T 在助记符后加一个字母表示操作数宽度:
| 后缀 | 含义 | 位数 |
|---|---|---|
b |
byte | 8 |
w |
word | 16 |
l |
long | 32 |
q |
quad | 64 |
Intel: mov al, 1 AT&T: movb $1, %al
Intel: mov ax, 1 AT&T: movw $1, %ax
Intel: mov eax, 1 AT&T: movl $1, %eax
Intel: mov rax, 1 AT&T: movq $1, %rax
Intel 则通过寄存器名称或 byte ptr、word ptr、dword ptr、qword ptr 来说明。
五、内存寻址(差别最大)
Intel 的通用形式:
[base + index*scale + displacement]
AT&T 的通用形式:
displacement(base, index, scale)
它们的对应关系为:
示例对照
| Intel | AT&T |
|---|---|
[ebx] |
(%ebx) |
[ebx+8] |
8(%ebx) |
[ebx+esi] |
(%ebx,%esi) |
[ebx+esi*4] |
(%ebx,%esi,4) |
[ebx+esi*4+8] |
8(%ebx,%esi,4) |
[esi*4+100] |
100(,%esi,4) |
[0x1234] |
0x1234 |
📌 注意:scale 只能是 1、2、4、8。
六、具体指令对比
6.1 MOV 系列
Intel: AT&T:
mov eax, 10 movl $10, %eax
mov eax, ebx movl %ebx, %eax
mov eax, [ebx] movl (%ebx), %eax
mov [ebx+4], eax movl %eax, 4(%ebx)
mov byte ptr [ebx], 1 movb $1, (%ebx)
mov dword ptr [ebx+ecx*4], 0 movl $0, (%ebx,%ecx,4)
6.2 算术指令
Intel: AT&T:
add eax, 1 addl $1, %eax
sub esp, 16 subl $16, %esp
imul eax, ebx, 3 imull $3, %ebx, %eax # 注意顺序反过来
lea eax, [ebx+ecx*2+4] leal 4(%ebx,%ecx,2), %eax
6.3 跳转与调用
直接跳转/调用写法相同(除了标签语法):
Intel: jmp label AT&T: jmp label
Intel: call func AT&T: call func
间接跳转/调用时,AT&T 需要加 *:
Intel: jmp eax AT&T: jmp *%eax
Intel: jmp [eax] AT&T: jmp *(%eax)
Intel: call [ebx+4] AT&T: call *4(%ebx)
6.4 Push / Pop
Intel: push eax AT&T: pushl %eax
Intel: push 1 AT&T: pushl $1
Intel: pop ebx AT&T: popl %ebx
七、一个完整的例子
下面是一个简单函数:计算 a + b*2。
Intel 语法
add_func:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; a
mov ecx, [ebp+12] ; b
shl ecx, 1 ; b*2
add eax, ecx
pop ebp
ret
AT&T 语法
add_func:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax # a
movl 12(%ebp), %ecx # b
shll $1, %ecx # b*2
addl %ecx, %eax
popl %ebp
ret
八、记忆口诀
-
AT&T = 三多一反
- 多
%(寄存器) - 多
$(立即数) - 多指令后缀
b/w/l/q - 操作数顺序相反
- 多
-
方向一致性:AT&T 的
movl %a, %b读作 "move a to b",和箭头方向一致。 -
内存寻址转换口诀: 把 Intel 的
[base + index*scale + disp]中的disp拎出来放前面,其余用圆括号包起来,逗号分隔,就得到 AT&T:disp(base, index, scale)。
九、考试时的快速检查清单
- 源/目的顺序是否反过来了?
- 寄存器是否加了
%? - 立即数是否加了
$? - 指令是否加了
b/w/l/q后缀? - 内存访问是否改成了
disp(base, index, scale)? - 间接跳转/调用是否加了
*?
掌握这六条,几乎所有 Intel→AT&T 的转换都能搞定。祝考试顺利!💪