Linux汇编语言GNUGAS汇编开发指南bk.docx
《Linux汇编语言GNUGAS汇编开发指南bk.docx》由会员分享,可在线阅读,更多相关《Linux汇编语言GNUGAS汇编开发指南bk.docx(13页珍藏版)》请在冰豆网上搜索。
Linux汇编语言GNUGAS汇编开发指南bk
Linux汇编语言(GNUGAS汇编)开发指南
编语言的优点是速度快,可以直接对硬件进行操作,这对诸如图形处理等关键应用是非常重要的。
Linux是一个用C语言开发的操作系统,这使得很多程序员开始忘记在Linux中还可以直接使用汇编这一底层语言来优化程序的性能。
本文为那些在Linux平台上编写汇编代码的程序员提供指南,介绍Linux汇编语言的语法格式和开发工具,并辅以具体的例子讲述如何开发实用的Linux汇编程序。
一、简介
作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。
就拿Linux内核来讲,虽然绝大部分代码是用C语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码,其中主要是在Linux的启动部分。
由于这部分代码与硬件的关系非常密切,即使是C语言也会有些力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。
大多数情况下Linux程序员不需要使用汇编语言,因为即便是硬件驱动这样的底层程序在Linux操作系统中也可以用完全用C语言来实现,再加上GCC这一优秀的编译器目前已经能够对最终生成的代码进行很好的优化,的确有足够的理由让我们可以暂时将汇编语言抛在一边了。
但实现情况是Linux程序员有时还是需要使用汇编,或者不得不使用汇编,理由很简单:
精简、高效和libc无关性。
假设要移植Linux到某一特定的嵌入式硬件环境下,首先必然面临如何减少系统大小、提高执行效率等问题,此时或许只有汇编语言能帮上忙了。
汇编语言直接同计算机的底层软件甚至硬件进行交互,它具有如下一些优点:
能够直接访问与硬件相关的存储器或I/O端口;
能够不受编译器的限制,对生成的二进制代码进行完全的控制;
能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;
能够根据特定的应用对代码做最佳的优化,提高运行速度;
能够最大限度地发挥硬件的功能。
同时还应该认识到,汇编语言是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码,因此不可避免地存在一些缺点:
编写的代码非常难懂,不好维护;
很容易产生bug,难于调试;
只能针对特定的体系结构和处理器进行优化;
开发效率很低,时间长且单调。
Linux下用汇编语言编写的代码具有两种不同的形式。
第一种是完全的汇编代码,指的是整个程序全部用汇编语言编写。
尽管是完全的汇编代码,Linux平台下的汇编工具也吸收了C语言的长处,使得程序员可以使用#include、#ifdef等预处理指令,并能够通过宏定义来简化代码。
第二种是内嵌的汇编代码,指的是可以嵌入到C语言程序中的汇编代码片段。
虽然ANSI的C语言标准中没有关于内嵌汇编代码的相应规定,但各种实际使用的C编译器都做了这方面的扩充,这其中当然就包括Linux平台下的GCC。
二、Linux汇编语法格式
绝大多数Linux程序员以前只接触过DOS/Windows下的汇编语言,这些汇编代码都是Intel风格的。
但在Unix和Linux系统中,更多采用的还是AT&T格式,两者在语法格式上有着很大的不同:
1.在AT&T汇编格式中,寄存器名要加上'%'作为前缀;而在Intel汇编格式中,寄存器名不需要加前缀。
例如:
AT&T格式
Intel格式
pushl%eax
pusheax
2.在AT&T汇编格式中,用'$'前缀表示一个立即操作数;而在Intel汇编格式中,立即数的表示不用带任何前缀。
例如:
AT&T格式
Intel格式
pushl$1
push1
3.AT&T和Intel格式中的源操作数和目标操作数的位置正好相反。
在Intel汇编格式中,目标操作数在源操作数的左边;而在AT&T汇编格式中,目标操作数在源操作数的右边。
例如:
AT&T格式
Intel格式
addl$1,%eax
addeax,1
4.在AT&T汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀'b'、'w'、'l'分别表示操作数为字节(byte,8比特)、字(word,16比特)和长字(long,32比特);而在Intel汇编格式中,操作数的字长是用"byteptr"和"wordptr"等前缀来表示的。
例如:
AT&T格式
Intel格式
movbval,%al
moval,byteptrval
5.在AT&T汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上'*'作为前缀,而在Intel格式中则不需要。
6.远程转移指令和远程子调用指令的操作码,在AT&T汇编格式中为"ljump"和"lcall",而在Intel汇编格式中则为"jmpfar"和"callfar",即:
AT&T格式
Intel格式
ljump$section,$offset
jmpfarsection:
offset
lcall$section,$offset
callfarsection:
offset
与之相应的远程返回指令则为:
AT&T格式
Intel格式
lret$stack_adjust
retfarstack_adjust
7.在AT&T汇编格式中,内存操作数的寻址方式是
section:
disp(base,index,scale)
而在Intel汇编格式中,内存操作数的寻址方式为:
section:
[base+index*scale+disp]
由于Linux工作在保护模式下,用的是32位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:
disp+base+index*scale
下面是一些内存操作数的例子:
AT&T格式
Intel格式
movl-4(%ebp),%eax
moveax,[ebp-4]
movlarray(,%eax,4),%eax
moveax,[eax*4+array]
movwarray(%ebx,%eax,4),%cx
movcx,[ebx+4*eax+array]
movb$4,%fs:
(%eax)
movfs:
eax,4
三、HelloWorld!
真不知道打破这个传统会带来什么样的后果,但既然所有程序设计语言的第一个例子都是在屏幕上打印一个字符串"HelloWorld!
",那我们也以这种方式来开始介绍Linux下的汇编语言程序设计。
在Linux操作系统中,你有很多办法可以实现在屏幕上显示一个字符串,但最简洁的方式是使用Linux内核提供的系统调用。
使用这种方法最大的好处是可以直接和操作系统的内核进行通讯,不需要链接诸如libc这样的函数库,也不需要使用ELF解释器,因而代码尺寸小且执行速度快。
Linux是一个运行在保护模式下的32位操作系统,采用flatmemory模式,目前最常用到的是ELF格式的二进制代码。
一个ELF格式的可执行程序通常划分为如下几个部分:
.text、.data和.bss,其中.text是只读的代码区,.data是可读可写的数据区,而.bss则是可读可写且没有初始化的数据区。
代码区和数据区在ELF中统称为section,根据实际需要你可以使用其它标准的section,也可以添加自定义section,但一个ELF可执行程序至少应该有一个.text部分。
下面给出我们的第一个汇编程序,用的是AT&T汇编语言格式:
例1.AT&T格式
#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 #调用内核功能
初次接触到AT&T格式的汇编代码时,很多程序员都认为太晦涩难懂了,没有关系,在Linux平台上你同样可以使用Intel格式来编写汇编程序:
例2.Intel格式
;hello.asm
section.data ;数据段声明
msgdb"Hello,world!
",0xA ;要输出的字符串
lenequ$-msg ;字串长度
section.text ;代码段声明
global_start ;指定入口函数
_start:
;在屏幕上显示一个字符串
movedx,len ;参数三:
字符串长度
movecx,msg ;参数二:
要显示的字符串
movebx,1 ;参数一:
文件描述符(stdout)
moveax,4 ;系统调用号(sys_write)
int0x80 ;调用内核功能
;退出程序
movebx,0 ;参数一:
退出代码
moveax,1 ;系统调用号(sys_exit)
int0x80 ;调用内核功能
上面两个汇编程序采用的语法虽然完全不同,但功能却都是调用Linux内核提供的sys_write来显示一个字符串,然后再调用sys_exit退出程序。
在Linux内核源文件include/asm-i386/unistd.h中,可以找到所有系统调用的定义。
四、Linux汇编工具
Linux平台下的汇编工具虽然种类很多,但同DOS/Windows一样,最基本的仍然是汇编器、连接器和调试器。
1.汇编器
汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标代码。
Linux平台的标准汇编器是GAS,它是GCC所依赖的后台汇编工具,通常包含在binutils软件包中。
GAS使用标准的AT&T汇编语法,可以用来汇编用AT&T格式编写的程序:
[xiaowp@garycode]$as-ohello.ohello.s
Linux平台上另一个经常用到的