作者:小安
博客地址:我的博客
今天,我们来梳理一下这常见的x86/x64平台下汇编语言的格式。
一个高级语言写的程序会编译为二进制文件,但是其中有一个步骤是反汇编,这也是人们所能可以理解最底层的语言了,继而汇编会被变成一堆二进制数据。
人们发明汇编的原因就是因为指令集非常不便于理解和阅读,但是说到指令集,人们有发明了两种指令集:复杂指令集和精简指令集,两者代表分别是如今PC端的霸主X86架构与移动端的霸主ARM架构处理器。
是不是有点晕乎了呢?是的,那请你看下方的流程发展图:
学一个东西就是这样,像极了“不识庐山真面目,只缘身在此山中”,从知识的海洋跳出来,站在山顶也许会有破局的妙计!
这样看表已经明了很多了,网上好多人长篇大论都说不清,下面我们每个举个例子就明白了。
前缀不同:在Intel的语法中,寄存器和和立即数都没有前缀。但是在AT&T中,寄存器前需要加上“%”,而立即数前需要加上“$”。
Intel语法 | AT&T语法 |
---|---|
mov eax,8 | movl $8,%eax |
前缀不同: 在Intel的语法中,十六进制和二进制立即数后缀分别是“h”和“b”,而在AT&T中,十六进制立即数前需要加上“0x”。
Intel语法 | AT&T语法 |
---|---|
int 80h | int $0x80 |
操作数方向不同:Intel与AT&T操作数的方向正好相反。在Intel语法中,第一个操作数是目的操作数,第二个操作数源操作数。而在AT&T中,第一个数是源操作数,第二个数是目的操作数。
Intel语法 | AT&T语法 |
---|---|
mov eax,[ecx] | movl (%ecx),%eax |
内存单元操作数不同:在Intel的语法中,基寄存器用“[]”括起来,而在AT&T中,用“()”括起来。
Intel语法 | AT&T语法 |
---|---|
mov eax,[ebx+5] | movl 5(%ebx),%eax |
后缀不同:在AT&T的操作码后面有一个后缀,其含义就是指出操作码的大小。"l"表示长整数(32位),"w"表示字(16位),"b"表示字节(8位)。而在Intel的语法中,则要在内存单元操作数的前面加上byte ptr, word ptr 和 dword ptr。
Intel语法 | AT&T语法 |
---|---|
mov al,bl | movb %bl,%al |
mov ax,bx | movw %bx,%ax |
mov eax,ebx | movl %ebx,%eax |
mov eax, dword ptr [ebx] | movl (%ebx),%eax |
寻址方式不同:Intel的指令格式是segreg:[base+index*scale+disp],而AT&T的格式是%segreg:disp(base,index,scale)。其中index/scale/disp/segreg全部是可选的,完全可以简化掉。如果没有指定scale而指定了index,则scale的缺省值为1。当立即数用在scale/disp中时,不应当在其前冠以“$”前缀。
Intel语法 | AT&T语法 |
---|---|
mov eax,[ebx+20h] | movl 0x20(%ebx),%eax |
add eax,[ebx+ecx*2h | addl (%ebx,%ecx,0x2),%eax |
lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax |
sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax |
我们实战一下,用两种不同的汇编风格写一下HelloWorld。
#hello.s
.data # 数据段声明
msg : .string "Hello, world!\\n" # 要输出的字符串
len = . - msg # 字串长度
.text # 代码段声明
.global _start # 指定入口函数
_start: # 在屏幕上显示一个字符串
movl $len, %edx # 参数三:字符串长度
movl $msg, %ecx # 参数二:要显示的字符串
movl $1, %ebx # 参数一:文件描述符(stdout)
movl $4, %eax # 系统调用号(sys_write)
int $0x80 # 调用内核功能
# 退出程序
movl $0,%ebx # 参数一:退出代码
movl $1,%eax # 系统调用号(sys_exit)
int $0x80 # 调用内核功能
; hello.asm
section .data ; 数据段声明
msg db "Hello, world!", 0xA ; 要输出的字符串
len equ $ - msg ; 字串长度
section .text ; 代码段声明
global _start ; 指定入口函数
_start: ; 在屏幕上显示一个字符串
mov edx, len ; 参数三:字符串长度
mov ecx, msg ; 参数二:要显示的字符串
mov ebx, 1 ; 参数一:文件描述符(stdout)
mov eax, 4 ; 系统调用号(sys_write)
int 0x80 ; 调用内核功能
; 退出程序
mov ebx, 0 ; 参数一:退出代码
mov eax, 1 ; 系统调用号(sys_exit)
int 0x80 ; 调用内核功能
说了这么多,那这两种格式都在哪里应用呢??
因为AT&T风格有更好的可移植性,所以在Linux的平台下有很好的支持,根据不同情况选择不同的风格进行工作学习,这也是人类的智慧。
参考AT&T汇编语言 和汇编语言编写的Hello World