简介AT&T风格汇编
Posted on 二月 17th, 2010 in 翻译 | 14 Comments »
作者:vivek
翻译:ssword
原文:http://sig9.com/articles/att-syntax
本文粗谈一下gas(1)的汇编语法,即AT&T风格汇编。初次接触很可能会觉得它别扭,不过若有其他汇编语言的基础,稍事了解即可快速上手。我假设你熟悉INTEL风格汇编——也就是INTEL手册中的那种风格。方便起见,我就用NASM(Netwide Assembler)来做比对。
gas属于GNU Binary Utilities(binutils),也是GCC的一个后端。对编写较长的汇编程序而言它并非首选,不过对于类Unix系统的内核级hacking,它就无可替代了。选择AT&T风格使得gas饱受争议,人们总说它只是GCC的后端,而对开发者不友好。INTEL风格汇编的教众也认为,它在可读性及编译上几乎是令人窒息。尽管如此,有一点必须了解:很多操作系统都选择了gas作为底层代码的汇编器。
基本形式
AT&T汇编程序的结构与其他汇编大同小异,伪指令、标签、指令—即最多带三个操作数的助记符。要说AT&T汇编的不同,最显眼的地方就是它操作数的顺序。
例如,一个简单的数据移动指令在INTEL风格下边是这个样子:
mnemonic destination, source在AT&T风格下边则是这样:
mnemonic source, destination一部分人(包括我)觉得这种格式更贴切。接下来说说AT&T风格指令中的操作数。
寄存器
每个IA-32架构寄存器的名字必须以’%'作前缀,如%al,%bx,%ds,%cr0,等等。
mov %ax, %bx
如上的mov指令就是把一个16位寄存器ax中的值移动到另一个16位寄存器bx中。
字面量
每个字面量必须以’$'为前缀。 例如:
mov $100, %bx mov $A, %al
第一个指令是把值100移动到寄存器bx中,第二个指令是把一个字节A移动到AL寄存器中。下面这个指令就是错误的:
mov %bx, $100
怎么说呢,这条指令是要把寄存器bx的值移动给一个字面量,显然不靠谱。
内存寻址
在AT&T风格中,内存引用起来是这个格式:
segment-override:signed-offset(base,index,scale)
按你寻址的需求,其中的部分可以省略
%es:100(%eax,%ebx,2)
注意下,基地址及偏移中的数不带前缀’$'。拿几个例子和对应的NASM风格做个比较应该好些:
GAS memory operand NASM memory operand ------------------ ------------------- 100 [100] %es:100 [es:100] (%eax) [eax] (%eax,%ebx) [eax+ebx] (%ecx,%ebx,2) [ecx+ebx*2] (,%ebx,2) [ebx*2] -10(%eax) [eax-10] %ds:-10(%ebp) [ds:ebp-10]
实例:
mov %ax, 100 mov %eax, -100(%eax)
第一个指令是把寄存器AX中的值移动到数据段寄存器(默认)偏移100的内存位置,第二个指令是把寄存器eax中的值移动到[eax-100]。
操作数大小
有时指明操作数的大小是必须的,尤其是移动字面量到内存。例如这个指令:
mov $10, 100
这里只说了把值10移动到内存偏址100处,而没有说值的大小。在NASM中,这通过给操作数后面跟个byte/word/dword之类的关键词来指明。而在AT&T风格里,是通过指令中b/w/l之类的后缀指明。如:
movb $10, %es:(%eax)
把值为10的一个字节移动到内存地址[ex:eax],另如:
movl $10, %es:(%eax)
把值为10的一个长整数移动到同一位置。
再几个例子:
movl $100, %ebx pushl %eax popw %ax
控制流程
jmp,call,ret等指令可以转移程序的执行位置。在同一代码段中跳转,是近距跳转(near)。若是跳转到不同的代码段,就是远程跳转(far)。可用的跳转地址可以来自相对偏移(label)、寄存器、内存以及段偏移指针。相对偏移通过label指明,如下:
label1: . . jmp label1
使用寄存器或者内存的值做地址的操作数必须加个前缀’*'。若是远程跳转,必须加个’l'作前缀,如‘ljmp’,‘lcall’等等。例如:
GAS syntax NASM syntax ========== =========== jmp *100 jmp near [100] call *100 call near [100] jmp *%eax jmp near eax jmp *%ecx call near ecx jmp *(%eax) jmp near [eax] call *(%ebx) call near [ebx] ljmp *100 jmp far [100] lcall *100 call far [100] ljmp *(%eax) jmp far [eax] lcall *(%ebx) call far [ebx] ret retn lret retf lret $0x100 retf 0x100
段偏移指针按下面的格式指明:
jmp $segment, $offset
例如:
jmp $0x10, $0x100000
记住这些很快就能上手了。要了解gas的更多细节,不妨参阅这个文档。

14 Responses
我开始看的是王爽的8086汇编,后来曾用gcc开发,痛苦了我很长时间。
另外,我记得貌似IBM还是哪儿有过介绍,也很好。
找到了:
http://www.ibm.com/developerworks/cn/linux/l-gas-nasm.html
p.s请楼主注意一下时间的设定……
p.s2我的gravatar为什么不见了-_-#
@wangweinoo1 哈,多谢推荐。mark之 :)
@wangweinoo1 gavatar好像只和email相关?
ps: 哈哈,现在才发现没设置时区~
在测试下我的头像-_-#
有检查了遍,邮箱分明没问题的说……
p.s 6#某同学发生拼写错误,当然,在Opera,不论你怎么写,红线总是存在的。
出来了,心情振奋……
囧
这个用做C编译器的时候学习过…虽然编译器没做出来,AT&T语法倒学的查不多了。
做个友链啦,啦啦啦.
@davelv 欢迎D大~ :D
@杨漱玉青 嘿嘿~
已经把友情链接更新了、、