C指针原理教程之AT&T汇编
汇编在LINUX系统下的意义远远大于WINDOWS系统,LINUX内核部分代码就是汇编编写的。然后,绝大多数Linux程序员以前只接触过DOS/Windows下的汇编语言,这些汇编代码都是Intel风格的。但在Unix和Linux系统中,更多采用的还是AT&T格式,两者在语法格式上有着很大的不同,因此应对AT&T汇编应有一个基本的了解和熟悉。
我们在LINUX下用C编写一段最简单的helloworld程序,命令为hello.c
#includeintmain() { printf("hello,world\n"); exit(0); }
然后,使用GCC编译,同时使用-s参数生成中间汇编代码,看看AT&T汇编的真实面目
.section.data#初始化的变量
output:
.ascii"hello,world\n"
#要打印的字符串,.data为初始化值的变量。output是标签,指示字符串开始的位置,ascii为数据类型
.section.bss#未初始化的变量,由0填充的缓冲区
.lcommnum,20
#lcomm为本地内存区域,即本地汇编外的不能进行访问。.comm是通用内存区域。
.section.text#汇编语言指令码
.globl_start#启动入口
_start:
movl$4,%eax#调用的系统功能,4为write
movl$output,%ecx#要打印的字符串
movl$1,%ebx#文件描述符,屏幕为1
movl$12,%edx#字符串长度
int$0x80#显示字符串hello,world
movl$0,%eax
movl$num,%edi
movl$65,1(%edi)#A的ascii
movl$66,2(%edi)#B的ascii
movl$67,3(%edi)#C的ascii
movl$68,4(%edi)#D的ascii
movl$10,5(%edi)#\n的ascii
movl$4,%eax#调用的系统功能,4为write
movl$num,%ecx#要打印的字符串
movl$1,%ebx#文件描述符,屏幕为1
movl$6,%edx#字符串长度
int$0x80#显示字符串ABCD
movl$1,%eax#1为退出
movl$0,%ebx#返回给shell的退出代码值
int$0x80#内核软中断,退出系统
gcc-Shello.c .file"hello.c" .section.rodata .LC0: .string"hello,world" .text .globlmain .typemain,@function main: pushl%ebp movl%esp,%ebp andl$-16,%esp subl$16,%esp movl$.LC0,(%esp) callputs movl$0,(%esp) callexit .sizemain,.-main .ident"GCC:(Ubuntu4.4.3-4ubuntu5)4.4.3" .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位整数入栈
pushldata #data变量内容入栈,长度为32位
pushl$data#这一个操作很特别,在变量前面加上$表示取变量的地址,这是将data变量的地址入栈
5、在AT&T汇编格式中,绝对转移和调用指(jump/call)的操作数前要加上'*'作为前缀
6、远程转移指令和远程子调用指令的操作码,在AT&T汇编格式中为ljump和lcall
我们从生成的中间代码可以看出这几个特点。
我们再来看一段用AT&T汇编编写的helloworld程序。
.section.data#初始化的变量 output: .ascii"hello,world\n" #要打印的字符串,.data为初始化值的变量。output是标签,指示字符串开始的位置,ascii为数据类型 .section.bss#未初始化的变量,由0填充的缓冲区 .lcommnum,20 #lcomm为本地内存区域,即本地汇编外的不能进行访问。.comm是通用内存区域。 .section.text#汇编语言指令码 .globl_start#启动入口 _start: movl$4,%eax#调用的系统功能,4为write movl$output,%ecx#要打印的字符串 movl$1,%ebx#文件描述符,屏幕为1 movl$12,%edx#字符串长度 int$0x80#显示字符串hello,world movl$0,%eax movl$num,%edi movl$65,1(%edi)#A的ascii movl$66,2(%edi)#B的ascii movl$67,3(%edi)#C的ascii movl$68,4(%edi)#D的ascii movl$10,5(%edi)#\n的ascii movl$4,%eax#调用的系统功能,4为write movl$num,%ecx#要打印的字符串 movl$1,%ebx#文件描述符,屏幕为1 movl$6,%edx#字符串长度 int$0x80#显示字符串ABCD movl$1,%eax#1为退出 movl$0,%ebx#返回给shell的退出代码值 int$0x80#内核软中断,退出系统
我们对上面这段汇编代码的结构和内容进行解说:
1、.section.data段存放着初始化的变量,.section.bss段存放着未初始化的变量
2、变量的定义采用以下格式:
变量名:
变量类型变量值
上面代码中的output变量就是这么定义的
output:
.ascii"hello,world\n"
下面例子定义了多个变量
.section.data
msg:
.ascii“Thisisatext”
x:
.double109.45,2.33,19.16
y:
.int89
z:
.int21,85,27
.equ a8
其中,msg为字符符,x为双精度符点数,y和z为整数,a是一个特别的定义,它的是一个静态变量的定义,使用.equ变量名变量值来实现
3、.section.bss段中变量访问区域的定义规则为:
lcomm为本地内存区域,即本地汇编外的不能进行访问,而.comm是通用内存区域
比如上面的定义
.lcommnum,20
num为本地内存区域。
4、section.text段为汇编语言指令码,使用.globl_start指示_start标记后的代码为程序启动入口。
5、#表示注释,上面代码的其它部分均有注释,有汇编基础的程序员应很容易理解
变量的类型有以下几种:
.ascii文本字符串
.asciz以NULL结束的文本字符串
.byte 字节值
.double双精度符点数
.float单精度符点数
.int32位整数
.long32位整数
.octa16位整数
.quad8位整数
.short16位整数
.single单精度符点数
此外,AT&T汇编经常会涉及字节顺序反转,比较加载,交换,压入弹出所有寄存器等操作,以下例子涉及了这些操作,
每行代码都有详细的注释。
.bss段定义的数据元素为未初始化的变量,在运行时对其进行初始化。
可分为数据通用内存区域和本地通用内存区域
本地通用内存区域不能从本地汇编代码之外进行访问。
.text段存放代码
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。