假设你是一家快递公司的仓管员,公司有两个分店。A 分店的快递单把“寄件人”写在第一栏、“收件人”写在第二栏,寄件人名字前要挂“%“工牌,直接塞现金时要在金额前贴”$"标签;B 分店的快递单把"收件人"写在前面,寄件人名字也不挂工牌。两边的快递员送的是同一批货、走的是同一条路,可两张单子的格式完全不同。CPU 和快递公司一样,只认装货、发车、卸货这些动作,而人类为了描述这些动作发明了两种"填单风格"——AT&T 语法和 Intel 语法。这篇文章就用"双快递单"的比喻,带你逐项拆解:指令方向的顺序、"%"和"$“的前缀、括号里的寻址公式、指令尾巴上的规格章,以及用 objdump 这位”翻译官”一键切换的实战技巧。

岗位关联

掌握 AT&T 与 Intel 双语法,是阅读 Linux 内核汇编、驱动代码与反汇编输出的必备技能,也是 AI 代码生成与二进制分析工具开发的基本功。

目录
  1. 可视化演示
  2. 指令方向顺序
  3. 寄存器前缀标记
  4. 立即数书写
  5. 内存寻址格式
  6. 助记符与后缀
  7. 工具链与转换
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示AT&T与Intel语法操作数顺序差异、寄存器立即数前缀规则、指令后缀与多指令示例对比三阶段交互探索。

本节配有交互动效(进入视口后自动加载,约 3KB)。

操作数顺序对比 AT&T源操作数在前目标在后 Intel目标操作数在前源在后 movl %eax,%ebx vs mov ebx,eax ATT: src→dst | INT: dst←src 操作数顺序是最显著语法差异

指令方向顺序

三年前我在快递公司实习,站长丢给我一叠快递单让我填。A 分店的单子第一栏写“寄件人”,第二栏写“收件人”;B 分店的单子恰好反过来。同一批货,A 店写“从上海发往北京”,B 店写“北京接收来自上海”。这就是 AT&T 与 Intel 语法在指令方向上的本质区别:AT&T 把源操作数放在前面,Intel 把目标操作数放在前面。

x86 汇编里的每条指令,本质是一张“数据快递单”。以 mov 指令为例,它负责把一个值从一个地方搬到另一个地方。AT&T 语法写 movl %eax, %ebx,读作“把 eax 的值发送给 ebx”;Intel 语法写 mov ebx, eax,读作“ebx 接收来自 eax 的值”。注意两种写法里寄存器名字的顺序完全相反,但执行的机器码一模一样——就像同一张快递单,只是栏目顺序不同。

为什么会有这种差异?历史原因:AT&T 语法脱胎于贝尔实验室的 UNIX 汇编传统,强调“动作+源+目标”的自然阅读顺序;Intel 语法则跟随 Intel 官方手册,习惯把“目的地”放在显眼位置。GNU 汇编器 GAS 默认使用 AT&T,而 MASM、NASM 默认使用 Intel。理解方向是掌握两种语法的第一步,方向写反,程序就会把数据搬错仓库。

回到快递公司,只要记住“AT&T 是寄件人在前”这一条,再看任何 AT&T 指令都不会晕方向;看到 Intel 指令就默念“收件人签收单”。方向是两种语法的分水岭,第一关过了,后面都是记号问题。

AT&T: movl %src,%dst⟺Intel: mov dst,src⟺dst←src\text{AT\&T: } \texttt{movl } \%src, \%dst \quad \Longleftrightarrow \quad \texttt{Intel: } \texttt{mov } dst, src \quad \Longleftrightarrow \quad dst \leftarrow src
这条公式说明:无论哪种语法,指令的最终语义都是把源(src)的数据写入目标(dst)。
  1. 源操作数 src数据的出发点,相当于快递单上的“寄件人”。
    srcsrc
  2. 目标操作数 dst数据的目的地,相当于“收件人”。
    dstdst
  3. AT&T 写法源在前、目标在后,按“寄件人 → 收件人”的顺序读。
    movl %src,%dst\texttt{movl } \%src, \%dst
  4. Intel 写法目标在前、源在后,按“收件人 ← 寄件人”的顺序读。
    mov dst,src\texttt{mov } dst, src
  5. 最终效果两种写法都让 dst 被 src 的值覆盖。
    dst←srcdst \leftarrow src

寄存器前缀标记

快递公司的 A 分店有个规定:所有快递员必须佩戴工牌才能进仓库取货,工牌上印着“%“符号加编号,比如”%”这队的 eax 仓管员。所以在 AT&T 的单子上,寄存器永远以 %eax、%ebx 的形式出现,% 就是那块工牌。B 分店不要求工牌,直接写 eax、ebx,因为他们的系统里寄存器名字是保留字,不会和普通地名混淆。

技术上说,AT&T 语法给寄存器加 % 前缀,是为了把寄存器与符号名、标签区分开。比如程序里可能有个变量也叫 eax,但加了 % 之后,%eax 就只可能指 CPU 的寄存器。Intel 语法没有这个前缀,它通过上下文(比如 mov 指令的操作数槽位)来判断哪个是寄存器名,因此也是严格的——只是不用显式标记。

x86 的通用寄存器家族在两种语法下都能写,只是差一个 %:累加器 %eax/eax、基址 %ebx/ebx、计数 %ecx/ecx、数据 %edx/edx、栈指针 %esp/esp、帧指针 %ebp/ebp 等;64 位模式下则是 %rax/rax 这组名字。GAS 里如果写 movl eax, %ebx(两边都是寄存器却忘了 %),汇编器会报错或把 eax 当成符号引用,这是新手最常见的坑。

所以遇到 AT&T 代码,把 % 想象成快递员的工牌就好:看到工牌就知道是自己人(寄存器),不会跟地名(变量符号)混在一起。Intel 不戴工牌,但系统里存了员工名册,看名字就能认出来。

AT&T: %eax,%ebx,%ecx,%edx,%esp,%ebpIntel: eax,ebx,ecx,edx,esp,ebp\text{AT\&T: } \%eax, \%ebx, \%ecx, \%edx, \%esp, \%ebp \qquad \text{Intel: } eax, ebx, ecx, edx, esp, ebp
这一组对照说明:两种语法唯一的区别是“工牌” %,去掉它之后寄存器名完全相同。
  1. 百分号 %AT&T 的寄存器“工牌”,告知汇编器后面是寄存器而非符号。
    %\%
  2. 寄存器名 eaxx86 32 位通用寄存器,去掉前缀后与 Intel 完全同名。
    eaxeax
  3. 等价关系%eax 与 eax 指向同一个物理寄存器。
    %eax≡eax\%eax \equiv eax
  4. 64 位扩展r 前缀表示 8 字节版本,同样只差一个 %。
    %rax≡rax\%rax \equiv rax

立即数书写

快递公司除了搬仓库之间的货,还会直接给现金。A 分店的快递单上用 $ 标记现金,比如 $42 就是“直接塞 42 块钱”;没有 $ 的数字则被当成一个门牌号(内存地址)。B 分店的单子上裸写 42 就是现金,如果写地址必须用方括号包起来(如 [42])。这就是立即数(immediate)在两种语法中的写法差异。

立即数是“写在指令里的常量”,不需要去内存取,直接作为数据本身参与运算。AT&T 规定立即数必须以 $ 开头:movl $42, %ecx 表示把常量 42 写入 ecx;如果写成 movl 42, %ecx,GAS 则会把 42 当作绝对地址,从内存地址 42 处读取数据——差一个 $,语义天差地别。Intel 语法中,裸数字就是立即数,地址才需要特殊记号。

十六进制写法也值得注意:AT&T 里 $0x1F 是立即数 31,而 0x1F 不加 $ 就是地址;Intel 里一般写作 mov ecx, 1Fh(NASM 风格)或 mov ecx, 0x1F(MASM 某些模式也支持),但都表示立即数。在写内联汇编时,如果混用两种语法,最容易出错的就是忘写 $ 导致“把常量当地址”。

一句话记住:AT&T 的 $ 就是快递单上的“现金”标签,看到 $ 就知道是直接塞钱;没有 $ 的数字是门牌号(地址),要上门取货。Intel 反过来——裸数字是现金,方括号才是门牌号。

movl $42,%ecx⟺mov ecx, 42⟺ecx←42\texttt{movl } \$42, \%ecx \quad\Longleftrightarrow\quad \texttt{mov ecx, 42} \quad\Longleftrightarrow\quad ecx \leftarrow 42
这条公式表示:AT&T 的 $42 与 Intel 的 42 是同一个立即数,执行后 ecx 寄存器里都是常量 42。
  1. $ 前缀AT&T 中唯一的“现金”标记,表明后面是立即数。
    $42\$42
  2. Intel 裸数字Intel 语法中数字直接表示立即数,无需任何前缀。
    4242
  3. 不加 $ 的歧义AT&T 中裸数字 42 会被当成内存地址,去内存取值。
    movl 42,%ecx⇒ecx←M[42]\texttt{movl } 42, \%ecx \Rightarrow ecx \leftarrow M[42]
  4. 写入结果立即数版本执行后,ecx 里是常量本身。
    ecx=42ecx = 42

内存寻址格式

快递公司发代收货款时,经常要送到“大卖场的储物柜”。储物柜地址由四部分组成:柜区(base)、排号(index)、每排格数倍率(scale),以及固定偏移(disp)。A 分店的写法是“偏移(区,排,格距)”如 4(%esp,%eax,4);B 分店写成数学算式 [esp+eax*4+4]。两种写法都在描述“从 esp 出发,跳 eax 排,每排 4 格,再往前走 4 格”的位置。

这对应 x86 最强大的内存寻址能力。AT&T 格式写作 disp(base, index, scale),其中 base 和 index 可以是寄存器,scale 只能取 1、2、4、8 之一,disp 是立即数偏移。Intel 格式写作 [base + index*scale + disp],本质是同一个算式的两种排版。省略部分时也有对照:(%eax) 等价于 [eax],8(%ebp) 等价于 [ebp+8]。

实战中最常见的例子是访问数组或栈帧:C 语言中 arr[i] 在 32 位下通常编译为 movl (%ebx,%ecx,4), %eax(AT&T),其中 %ebx 是数组首地址、%ecx 是下标、4 是每个元素大小;Intel 写法则是 mov eax, [ebx+ecx*4]。反过来,函数栈上的局部变量多通过 %ebp 加偏移访问,如 -4(%ebp) 对应 [ebp-4]。

回到储物柜比喻:A 店的“偏移(区,排,格距)”和 B 店的“算式”描述的是同一个柜子。学会了换算公式,两种单子都能秒懂;写错了顺序,货物就会送错柜门。

addr(base,index,scale,disp)=base+index×scale+dispaddr(base, index, scale, disp) = base + index \times scale + disp
AT&T: disp(base, index, scale)⟺Intel: [base+index×scale+disp]\text{AT\&T: } \texttt{disp(base, index, scale)} \quad\Longleftrightarrow\quad \text{Intel: } [base + index \times scale + disp]
两条公式是同一件事的两种写法:AT&T 把偏移写在括号外,Intel 把偏移写在方括号内的算式末尾。
  1. base 基址地址计算的起点,通常是一个寄存器,相当于“哪一排柜子”。
    basebase
  2. index 变址在第几个元素上,通常也是寄存器,相当于“第几格”。
    indexindex
  3. scale 比例每个元素占几个字节,只能是 1、2、4、8,相当于“柜门间隔”。
    scale∈{1,2,4,8}scale \in \{1, 2, 4, 8\}
  4. disp 偏移在算完前面之后再加的固定位移,相当于“再往前挪几厘米”。
    dispdisp
  5. 最终地址四者相加得到线性地址,两种语法计算结果完全一致。
    address=base+index×scale+dispaddress = base + index \times scale + disp

助记符与后缀

快递单上还得写“包裹规格”:小信封(1 字节)、纸箱(2 字节)、大箱子(4 字节)、超大箱(8 字节)。A 分店强制在指令后面盖章标明规格:movb、movw、movl、movq 分别对应 1、2、4、8 字节。B 分店不盖章,但收件员看一眼“寄件人”用的是什么样的箱子(寄存器位数)就知道规格——Intel 由操作数大小自动推断。

AT&T 的后缀字符来自 GAS:b = byte(8 位)、w = word(16 位)、l = long(32 位;注意这里的 long 是 32 位,与 64 位 Linux 下 C 语言的 long 不同)、q = quad word(64 位)。所以 movb、movw、movl、movq 本质都是 mov,只是显式声明了数据宽度。Intel 语法在同一寄存器宽度下不写后缀:mov ebx, eax 默认 32 位,mov bx, ax 是 16 位,mov bl, al 是 8 位——操作数本身的位数暴露了规格。

这个差异在两边混写时是个大坑。比如把 AT&T 的 movl %eax, %ebx 翻译成 Intel 时,写 mov ebx, eax 是对的;但若把后缀当成指令名的一部分直接搬到 Intel 里写 movl ebx, eax,在 NASM/MASM 中大概率报错(它俩不认识 movl)。反过来,把 Intel 的 mov 直接写进 GAS 也可能因为无法确定操作数宽度而报错,需要用 .intel_syntax noprefix 指令声明。

所以看到 AT&T 里带尾巴的 movl,别慌:它只是把 mov 的规格章盖得更显眼;Intel 不盖章,是因为箱子(寄存器/操作数)自己会说话。规格一致,货物等价,翻译快递单时保留大小、只换版式即可。

movb (1B),movw (2B),movl (4B),movq (8B)\texttt{movb}\ (1\text{B}),\quad \texttt{movw}\ (2\text{B}),\quad \texttt{movl}\ (4\text{B}),\quad \texttt{movq}\ (8\text{B})
这条公式列出了 AT&T 四种规格章,分别对应 8 位、16 位、32 位、64 位的数据搬运。
  1. b 后缀byte,一次搬运 8 位,相当于“小信封”。
    movb\texttt{movb}
  2. w 后缀word,一次搬运 16 位,相当于“纸箱”。
    movw\texttt{movw}
  3. l 后缀long,一次搬运 32 位,相当于“大箱子”。
    movl\texttt{movl}
  4. q 后缀quad word,一次搬运 64 位,相当于“超大箱”。
    movq\texttt{movq}

工具链与转换

前面聊的都是“单子长什么样”,最后聊聊“谁在写单子”。GNU 工具链这家物流公司(GCC 编译器、GAS 汇编器、objdump 反汇编器)默认开的是 A 分店——AT&T 语法;而 Windows 生态(MASM、NASM)和 Intel 手册用的是 B 分店——Intel 语法。幸运的是,物流公司配了翻译官:objdump 一行命令就能把同一个二进制的反汇编在两种方言间切换。

技术上,objdump -d 默认输出 AT&T 语法,加 -M intel 参数后输出 Intel 语法。例如同样反汇编一个函数,objdump -d 显示 movl %eax, %ebx,objdump -d -M intel 显示 mov ebx, eax,二者对应的目标机器码完全一致。GAS 汇编器也有语法切换指令:源文件里用 .intel_syntax 改用 Intel 写法,配合 noprefix 去掉 % 和 $ 前缀。这些工具让“双语法”不再是负担,而是可随时换装的技能。

工程中的典型工作流是:用 gcc -S 生成 AT&T 汇编用于内核调试(Linux 内核全部使用 AT&T 风格内联汇编);用 objdump -s -d 检查编译产物,加 -M intel 让熟悉 Intel 的同事也能快速审阅;在 Windows 驱动开发中则直接用 NASM/MASM 写 Intel 语法。理解两种语法后,无论是分析崩溃栈、读反汇编输出,还是给 AI 助手描述汇编问题,都能自由切换表达。

把 objdump 想象成一位随身翻译官:默认说 A 店方言(AT&T),你只要对它说“请说 Intel 话”(-M intel),它立刻切换。有了这位翻译官,加上你对规格章和寻址公式的掌握,两种快递单都不再是阅读障碍。

objdump -d  ⇒  AT&T 输出\texttt{objdump -d} \;\Rightarrow\; \text{AT\&T 输出}
objdump -d -M intel  ⇒  Intel 输出\texttt{objdump -d -M intel} \;\Rightarrow\; \text{Intel 输出}
这两条公式说明:同一个可执行文件,用 objdump 加不加 -M intel,会得到不同方言的“快递单”。
  1. objdump 反汇编把机器码翻译回汇编指令,相当于把货物清单翻译成快递单。
    objdump -d\texttt{objdump -d}
  2. 默认方言不加参数时默认输出 AT&T 风格。
    ⇒AT&T\Rightarrow \text{AT\&T}
  3. -M intel指定反汇编方言为 Intel。
    −M intel-M\ \text{intel}
  4. 输出等价同一段机器码,两种排版只是“单子格式”不同。
    machine code⇒{movl %eax,%ebxmov ebx, eax\text{machine code} \Rightarrow \begin{cases} \text{movl } \%eax, \%ebx \\ \text{mov ebx, eax} \end{cases}

AI 应用场景

AI 反汇编结果语法归一化

阶段:数据预处理与特征工程

解决的问题:安全分析平台抓取到的反汇编结果来自不同工具链——Linux 上 objdump 默认 AT&T,IDA/Ghidra 默认 Intel。传统做法靠工程师肉眼切换,容易看错操作数方向;AI 模型若能自动归一化语法,下游的指令相似度比对与恶意家族分类准确率会明显提升。

具体案例:某恶意软件分析平台用大模型解析混合来源的汇编样本,在数据管道里先把 AT&T 指令统一转成 Intel(用类似后文的转换脚本),再输入模型做时序分类,误判率下降了约三成。

AI 代码生成器编写内联汇编

阶段:模型推理与提示工程

解决的问题:让大模型在内核模块(Linux 要求 AT&T)和 Windows 驱动(要求 Intel)中生成内联汇编时,若不指明方言,模型容易生成方向写反或加了错误前缀的代码。在提示词中加入“使用 AT&T 语法,源操作数在前、目标在后”这类约束,能显著减少编译失败。

具体案例:一个 Linux 驱动开发助手内置语法检查器:模型生成 asm volatile("movl %0, %1" ...) 后,工具自动验证操作数顺序并给出修正建议,代码评审通过率从 60% 提升到 85%。

AI 驱动的汇编教学辅导

阶段:模型微调与语料构建

解决的问题:教学场景里学生同时接触 GAS(AT&T)与 NASM(Intel)容易混淆。AI 辅导系统用双语法对齐语料训练,可自动把学生写的 Intel 指令翻译成 AT&T 对照展示,降低学习曲线。

具体案例:某在线判题系统内置转换服务,学生提交 mov eax, [ebx+4],评测报告里同时给出 AT&T 版 movl 4(%ebx), %eax,帮助学生双向掌握两种“快递单”写法。

Python 演示

python48 行
import re  def operand_to_intel(op):    """把一个 AT&T 操作数转换为 Intel 风格写法"""    if op.startswith('%'):        return op[1:]    if op.startswith('$'):        return op[1:]    m = re.fullmatch(r'([^()]*)\(([^)]*)\)', op)    if m:        disp = m.group(1) or '0'        parts = [p[1:] if p.startswith('%') else p                 for p in m.group(2).split(',')]        terms = []        if len(parts) == 3:            terms.append(parts[0])            terms.append(parts[1] + '*' + parts[2])        else:            terms = parts        expr = ' + '.join(terms)        if disp != '0':            expr = expr + ' + ' + disp        return '[' + expr + ']'    return op  def att_to_intel(line):    """把一行 AT&T 汇编指令(如 movl %eax, %ebx)转为 Intel 写法"""    parts = line.replace(',', ' , ').split()    if len(parts) < 3 or parts[2] != ',':        return line    mnemonic = re.sub(r'[bwlq]$', '', parts[0])    src = operand_to_intel(parts[1])    dst = operand_to_intel(parts[3])    return mnemonic + ' ' + dst + ', ' + src  if __name__ == '__main__':    tests = [        'movl %eax, %ebx',        'movl $42, %ecx',        'movl 8(%ebp), %eax',        'addl 4(%esp,%eax,4), %edx',        'movq %rax, %rbx',    ]    for line in tests:        print('%-26s =>  %s' % (line, att_to_intel(line)))
目的:这是一个迷你“快递单翻译官”,把 AT&T 语法的汇编指令逐行翻译成 Intel 语法,覆盖寄存器、立即数和括号内存寻址三种操作数。
  1. if op.startswith('%')发现操作数带“工牌” %,直接摘掉还原成 Intel 裸名。
  2. if op.startswith('$')发现是“现金”立即数,去掉 $ 标记。
  3. m = re.fullmatch(r'([^()]*)\(([^)]*)\)', op)匹配 AT&T 的括号地址格式 disp(base,index,scale)。
  4. parts = [p[1:] if p.startswith('%') else p ...]把括号里各个分量上的 % 全部摘掉。
  5. if len(parts) == 3括号里有三个分量,按“基址 + 变址 × 比例”组装。
  6. line.replace(',', ' , ').split()把逗号当作独立标记切分,便于取源和目标。
  7. mnemonic = re.sub(r'[bwlq]$', '', parts[0])剥掉 movl 尾巴上的规格章,得到 Intel 认识的 mov。
  8. return mnemonic + ' ' + dst + ', ' + src按“目标在前、源在后”的顺序输出,完成方向反转。

常见误区

误区`movl` 和 `mov` 看起来名字不一样,应该是两种不同的指令,操作码也应该不同。

正解实际上 movl 就是 mov 再加上后缀 l,后缀只是 AT&T 用来声明操作数宽度的“规格章”。同一个 mov 在 AT&T 里可以写成 movb、movw、movl、movq,对应 1、2、4、8 字节;它们生成的机器码与 Intel 同语义指令完全一致。把后缀当成指令名的一部分,是混用两种语法时最常见的误解。

为何常见:助记符多了字母后缀,乍一看很像另一条指令,尤其是背指令表时容易把四种后缀各记成一条。

误区C 语言里 `a = b` 是把 b 赋给 a,所以 `movl %eax, %ebx` 应该也是把 ebx 赋给 eax,左边是目标。

正解汇编指令不是等号赋值,它像快递单一样需要先写“寄件人”再写“收件人”。AT&T movl %eax, %ebx 的源是 %eax,目标是 %ebx,效果是把 eax 的值复制到 ebx。高级语言的赋值直觉在这里完全不适用,必须按“快递单”阅读。

为何常见:人脑习惯“等号左边是目标”的数学直觉,遇到方向相反的汇编写法时自然产生迁移错误。

误区在 AT&T 里写 `movl 42, %ecx` 和 `movl $42, %ecx` 是一样的,都是把 42 放进 ecx。

正解AT&T 中 $42 才是立即数,裸数字 42 会被 GAS 解析为内存绝对地址,执行的是“从地址 42 处读值”。差一个 $,语义从“寄现金”变成“上门取货”。Intel 恰好相反:裸数字就是立即数,地址才要加方括号。两种语法对“裸数字”的解释截然不同。

为何常见:立即数在 Intel 里不需要前缀,初学者把这种习惯直接搬进 AT&T,就会漏掉 $。

误区只要用了 `objdump -M intel`,GAS、NASM、MASM 全都会按 Intel 语法工作。

正解objdump 是反汇编器,-M intel 只改变它对二进制文件的“翻译输出”;GAS 汇编器默认仍按 AT&T 解析源文件,NASM/MASM 默认仍按 Intel 解析。汇编器与反汇编器是两个角色,切语法要看各自工具的参数(如 GAS 的 .intel_syntax 指令)。

为何常见:把“反汇编显示”和“汇编解析”混为一谈,以为一个参数能同时控制所有工具链。

误区`8(%ebp)` 大概是 8 乘以 ebp 的意思,`4(%esp,%eax,4)` 就更复杂了。

正解括号表示内存寻址,不是乘号。8(%ebp) 是“ebp 的值加 8 之后的内存地址”;4(%esp,%eax,4) 是 esp + eax×4 + 4 这个地址。乘法只出现在三参数格式的第三个位置(scale,只能是 1/2/4/8)。把它想成储物柜的“柜区 + 排号 × 格距 + 偏移”就好理解了。

为何常见:数学里括号紧跟数字容易让人联想到乘法,而汇编的括号语义完全不同。

面试题

Q1. 在AT&T语法中,指令 movl %eax, %ebx 的语义是什么?

单选题 · easy

  • A.将EBX的值复制到EAX
  • B.将EAX的值复制到EBX← 正确答案
  • C.交换EAX与EBX的值
  • D.将EAX的值清零归零

答案:B。AT&T 语法源在前、目标在后,%eax 是“寄件人”(源),%ebx 是“收件人”(目标),所以是把 EAX 的值复制到 EBX。方向读反是初学最常见的错误。

Q2. Intel语法 mov ebx, eax 等价于AT&T的哪条指令?

单选题 · easy

  • A.movl eax, ebx
  • B.movl %ebx, %eax
  • C.movl %eax, %ebx← 正确答案
  • D.movb %eax, %ebx

答案:C。Intel mov ebx, eax 的目标是 ebx、源是 eax。换成 AT&T 要“源在前、目标在后”并给寄存器加 %,得到 movl %eax, %ebx。D 选项把 32 位搬运写成了 8 位的 movb,规格不对。

Q3. NASM汇编器默认采用哪种语法风格?

单选题 · easy

  • A.默认使用AT&T语法
  • B.默认使用Intel语法← 正确答案
  • C.两种语法完全等价
  • D.语法由机器字长决定

答案:B。NASM 与 MASM 都走 Intel 路线:裸寄存器名、目标在前、方括号寻址。GAS 才是 AT&T 风格的典型代表。

Q4. AT&T语法中,寄存器名前必须加上什么符号?

单选题 · mid

  • A.寄存器名加美元符号
  • B.寄存器名加井号标记
  • C.寄存器名加百分号← 正确答案
  • D.寄存器名不需要前缀

答案:C。AT&T 给所有寄存器加 % 前缀作为“工牌”,$ 是立即数前缀,# 是注释符。%eax 与 eax 指向同一个物理寄存器。

Q5. AT&T指令 movl $8, %ecx 中的 $8 表示什么?

单选题 · mid

  • A.内存地址8处的数
  • B.立即数常量8← 正确答案
  • C.第八号通用寄存器
  • D.字节偏移量8

答案:B。$ 是“现金”标签,$8 表示立即数常量 8,直接把 8 写入 ecx。如果去掉 $,裸数字 8 会被当作内存绝对地址,语义完全不同。

Q6. GNU工具链中,gcc -S 生成汇编默认使用什么语法?

单选题 · mid

  • A.默认采用Intel语法
  • B.采用NASM厂商语法
  • C.默认采用AT&T语法← 正确答案
  • D.需要手动指定输出

答案:C。GCC/GAS 沿袭 UNIX 传统,gcc -S 默认输出 AT&T 语法。想换 Intel 需加 -masm=intel,反汇编查看则用 objdump -M intel。

Q7. AT&T指令 addl 4(%esp,%eax,4), %edx 完成了什么操作?

单选题 · hard

  • A.EDX加内存值再写回← 正确答案
  • B.内存值写进EDX寄存器
  • C.ESP与EAX相乘再加四
  • D.将EDX存入该内存地址

答案:A。源是 4(%esp,%eax,4),即地址 esp+eax×4+4 处的内存值;目标是 %edx。addl 表示加法,所以是“EDX + 该内存值 → EDX”。C 只算了地址没算加法,D 把方向写反了。

Q8. 关于AT&T与Intel语法,下列说法正确的有?

多选题 · mid

  • A.AT&T源操作数在前← 正确答案
  • B.Intel目标操作数在前← 正确答案
  • C.AT&T寄存器加%前缀← 正确答案
  • D.Intel立即数加$前缀

答案:ABC。AT&T 是“寄件人在前”,寄存器带 %;Intel 是“收件人在前”,寄存器裸名。Intel 的立即数不加 $,裸数字就是立即数,所以 D 错误。

Q9. 关于 objdump 反汇编,下列说法正确的有?

多选题 · mid

  • A.默认输出AT&T语法← 正确答案
  • B.加-M intel可切换← 正确答案
  • C.支持两种语法显示← 正确答案
  • D.无法查看机器码字节

答案:ABC。objdump -d 默认 AT&T,-M intel 可切换到 Intel 输出,同一二进制可以两种“方言”显示。objdump 搭配 -s 还能显示机器码字节,所以 D 错误。

Q10. AT&T中 movl 的 l 后缀,说法正确的有?

多选题 · hard

  • A.表示操作数为32位← 正确答案
  • B.对应32位C的long← 正确答案
  • C.比movq的操作数小← 正确答案
  • D.表示锁定总线操作

答案:ABC。l = long = 4 字节 = 32 位,对应 32 位环境下 C 语言的 long 类型;q = quad = 8 字节,所以 l 比 q 小。总线锁是 lock 前缀的职责,与 l 后缀无关,D 错误。