AT&T汇编基础]

来源:互联网 发布:域名ns记录是什么 编辑:程序博客网 时间:2024/06/07 05:30

汇编在LINUX系统下的意义远远大于WINDOWS系统,LINUX内核部分代码就是汇编编写的。然后,绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多采用的还是 AT&T 格式,两者在语法格式上有着很大的不同,因此应对AT&T汇编应有一个基本的了解和熟悉。 
    我们在LINUX下用C编写一段最简单的helloworld程序,命令为hello.c

C代码  收藏代码
  1. #include <stdio.h>  
  2.  int main()  
  3. {   
  4. printf("hello,world\n");  
  5.  exit(0);   
  6. }   


然后,使用GCC编译,同时使用-s参数生成中间汇编代码,看看AT&T汇编的真实面目 
gcc -S  hello.c

Asm代码  收藏代码
  1. .file   "hello.c"  
  2.   
  3.     .section    .rodata  
  4.   
  5. .LC0:  
  6.   
  7.     .string "hello,world"  
  8.   
  9.     .text  
  10.   
  11. .globl main  
  12.   
  13.     .type   main, @function  
  14.   
  15. main:  
  16.   
  17.     pushl   %ebp  
  18.   
  19.     movl    %esp, %ebp  
  20.   
  21.     andl    $-16, %esp  
  22.   
  23.     subl    $16, %esp  
  24.   
  25.     movl    $.LC0, (%esp)  
  26.   
  27.     call    puts  
  28.   
  29.     movl    $0, (%esp)  
  30.   
  31.     call    exit  
  32.   
  33.     .size   main, .-main  
  34.   
  35.     .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"  
  36.   
  37.     .section    .note.GNU-stack,"",@progbits  

 
汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编工具,通常包含在 binutils 软件包中。 
AT&T汇编主要有以下特点: 
1、在 AT&T 汇编格式中,寄存器名要加上 '%' 作为前缀

如:

把eax寄存器的内容复制到ebx中

movl %eax,%ebx
2、用 '$' 前缀表示一个立即操作数

如:将1复制到eax中

movl $1, %eax
3、目标操作数在源操作数的右边

movl %eax,%ebx
eax是源操作数,ebx是目标操作数
4、在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为字节(byte,8 比特)、字(word,16 比特)和长字(long,32比特)

比如:

movl对32位进行操作,将eax寄存器32位的内容复制到ebx中

movl %eax, %ebx

movw对16位进行操作,将ax寄存器的内容复制到bx中

movw %ax, %bx

movb对8位进行操作,将al寄存器的内容复制到bl中

movb %al, %bl

 

我们再以入栈为例:

pushl %ecx  # 32位ecx的内容入栈

pushw %cx   # 16位ecx的内容入栈

pushl $180  # 80做为一个32位整数入栈

pushl data  # data变量内容入栈,长度为32位

pushl $data # 这一个操作很特别,在变量前面加上$表示取变量的地址,这是将data变量的地址入栈
5、在 AT&T 汇编格式中,绝对转移和调用指(jump/call)的操作数前要加上'*'作为前缀 
6、远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 ljump和lcall 
我们从生成的中间代码可以看出这几个特点。

我们再来看一段用AT&T汇编编写的helloworld程序

 

Asm代码  收藏代码
  1. .section .data#初始化的变量  
  2. output:  
  3.    .ascii "hello,world\n"  
  4.    #要打印的字符串,.data为初始化值的变量。output是标签,指示字符串开始的位置,ascii为数据类型  
  5. .section .bss#未初始化的变量,由0填充的缓冲区  
  6.    .lcomm num,20  
  7.    #lcomm为本地内存区域,即本地汇编外的不能进行访问。.comm是通用内存区域。  
  8. .section .text#汇编语言指令码  
  9.    .globl _start#启动入口  
  10.    _start:  
  11.    movl $4,%eax#调用的系统功能,4为write     
  12.    movl $output,%ecx#要打印的字符串  
  13.    movl $1,%ebx#文件描述符,屏幕为1     
  14.    movl $12,%edx#字符串长度  
  15.    int $0x80#显示字符串hello,world  
  16.      
  17.    movl $0,%eax  
  18.    movl $num,%edi  
  19.    movl $65,1(%edi)#A 的ascii  
  20.    movl $66,2(%edi)#B 的ascii   
  21.    movl $67,3(%edi)#C 的ascii   
  22.    movl $68,4(%edi)#D 的ascii  
  23.    movl $10,5(%edi)#\n的ascii   
  24.      
  25.    movl $4,%eax#调用的系统功能,4为write      
  26.    movl $num,%ecx#要打印的字符串    
  27.    movl $1,%ebx#文件描述符,屏幕为1     
  28.    movl $6,%edx#字符串长度  
  29.    int $0x80#显示字符串ABCD  
  30.            
  31.    movl $1,%eax#1为退出  
  32.    movl $0,%ebx#返回给shell的退出代码值  
  33.   
  34.    int $0x80#内核软中断,退出系统  
  35.      
  36.      
  37.      

 我们对上面这段汇编代码的结构和内容进行解说:

1、.section .data段存放着初始化的变量, .section .bss段存放着未初始化的变量

2、变量的定义采用以下格式:

变量名:

  变量类型 变量值

上面代码中的output变量就是这么定义的

output:
   .ascii "hello,world\n"

下面例子定义了多个变量

.section .data
msg:
.ascii “This is a text”
x:
.double 109.45, 2.33, 19.16

y:
.int 89

z:
.int 21, 85, 27

 

.equ  a 8

 

其中,msg为字符符,x为双精度符点数,y和z为整数,a是一个特别的定义,它的是一个静态变量的定义,使用.equ 变量名 变量值来实现

3、.section .bss段中变量访问区域的定义规则为:
lcomm为本地内存区域,即本地汇编外的不能进行访问,而.comm是通用内存区域
比如上面的定义
 .lcomm num,20  
num为本地内存区域。
4、section .text段为汇编语言指令码,使用.globl _start指示_start标记后的代码为程序启动入口。
5、#表示注释,上面代码的其它部分均有注释,有汇编基础的程序员应很容易理解
 

变量的类型有以下几种:

.ascii 文本字符串

.asciz 以NULL结束的文本字符串

.byte  字节值

.double 双精度符点数

.float 单精度符点数

.int 32位整数

.long 32位整数

.octa 16位整数

.quad 8位整数

.short 16位整数

.single 单精度符点数

此外,AT&T汇编经常会涉及字节顺序反转,比较加载,交换,压入弹出所有寄存器等操作,以下例子涉及了这些操作,

每行代码都有详细的注释。 

Asm代码  收藏代码
  1. .section .text  
  2.   data:  
  3.    .byte 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01#8字节  
  4. .globl main  
  5. main:  
  6.   movl $0x12345678,%edx  
  7.   bswap %edx#反转字符顺序 ,结果为0x87654321   
  8.   movl %edx,%eax  
  9.   movl $1,%ebx  
  10.   xadd %ebx,%eax#交换%eax和%ebx的值,并相加,结果放在目标位置%eax  
  11.   cmpxchg %edx,%ebx#如果目标操作数%ebx等于%eax、16位是ax,8位是al,则把源操作数%edx的值加载到%eax中、16位  
  12.   #是ax,8位是al,如果不等,则把目标操作数%ebx加到源操作数%edx加载到%eax中、16位是ax,8位是al  
  13.     
  14.     
  15.   movl $2,%edx#高位  
  16.   movl $3,%eax#低位  
  17.   cmpxchg8b data#如果%edx:%eax组成的8字节值与目标值data匹配,则把%edx:%eax组成的8字节值传送到目标内存位置  
  18.   #,否则将目标值data加载到%edx:%eax  
  19. pushad   
  20. popad  
  21. #pusha/popa 压入弹出所有16位通用寄存器  
  22. #pushad/popad 压入弹出所有32位通用寄存器   
  23. #pushf/popf 压入弹出EFLAGS寄存器的低16位  
  24. #pushf/popf 压入弹出EFLAGS寄存器的全部32位  
  25.     

 

AT&T汇编关于地址和取地址操作的示例代码如下:

Asm代码  收藏代码
  1. .section .data  
  2.    mynum:  
  3.    .int 8  
  4.    mygs:  
  5.    .asciz "%x----%x----%x\n"  
  6. .section .text  
  7.    .globl main  
  8.    main:  
  9.       leal mynum,%eax #将mynum地址复制到%eax    
  10.       movl (%eax),%ebx#将%eax内地址所指内容复制到%ebx  
  11.       movl mynum,%ecx#将mynum内容复制到%ecx中  
  12.         
  13.       push %ecx  
  14.       push %ebx  
  15.       push %eax  
  16.       push $mygs  
  17.       call printf  
  18.         
  19.       push $0  
  20.       call exit  
  21.         

 

乘法和除法操作如下:

Asm代码  收藏代码
  1. .section .data  
  2.   
  3. .section .text  
  4.   
  5.   .globl main  
  6.   
  7.   main:  
  8.   
  9.     movl $2,%eax  
  10.   
  11.     movl $5,%ebx  
  12.   
  13.     mul  %ebx#%eax*%ebx->%eax,无符号乘法  
  14.   
  15.     movl $-2,%eax  
  16.   
  17.     movl  $5,%ebx  
  18.   
  19.     imul  %ebx#%eax*%ebx->%eax,有符号乘法   
  20.   
  21.  #除法使用dev和idev(有符号)  

 

引用来源: 
1、http://www.ibm.com/developerworks/cn/linux/l-assembly/ 
扩展阅读: 
《Professional Assembly Language》  作者: Richard Blum 

0 0