Intel vs AT&T 语法

前言&吐槽

汇编代码有两种格式,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
十六进制 0AH0x0A $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 ptrword ptrdword ptrqword 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

八、记忆口诀

  1. AT&T = 三多一反

    • %(寄存器)
    • $(立即数)
    • 多指令后缀 b/w/l/q
    • 操作数顺序​​相反
  2. 方向一致性​​:AT&T 的 movl %a, %b 读作 "move a to b",和箭头方向一致。

  3. 内存寻址转换口诀​​: 把 Intel 的 [base + index*scale + disp] 中的 disp 拎出来放前面,其余用圆括号包起来,逗号分隔,就得到 AT&T:disp(base, index, scale)


九、考试时的快速检查清单

  • 源/目的顺序是否反过来了?
  • 寄存器是否加了 %
  • 立即数是否加了 $
  • 指令是否加了 b/w/l/q 后缀?
  • 内存访问是否改成了 disp(base, index, scale)
  • 间接跳转/调用是否加了 *

掌握这六条,几乎所有 Intel→AT&T 的转换都能搞定。祝考试顺利!💪

AI 助手
AI
你好!我可以根据当前文章内容回答你的问题。