分类: LINUX
2012-04-10 20:14:28
GNU as汇编:
内核中其余所有汇编程序(包括C语言产生的汇编程序)均使用gas编译。
由于操作系统许多关键代码要求有很高的执行速度和效率,因此在一个操作系统源代码中通常就会包含大约10%左右的起关键作用的汇编语言程序量。
在编译C语言程序时,GNU gcc编译器会首先输出一个作为中间结果的as汇编语言文件,然后gcc会调用as汇编器把这个临时汇编语言程序编译成目标文件。
编译as汇编语言程序:
命令行格式:
as [选项] [-o objfile] [srcfile.s ...]
如果没有指定objfile,那么as会编译输出a.out这个默认目标文件。
srcfile.s文件名的放置次序与编译结果密切相关。程序的源代码是所有文件按次序组合的结果,每次运行as编译器,它只编译一个源程序。
如果没有给出srcfile.s,那么as会从标准输入中读取输入文件内容,按Ctrl-D结束输入。
as汇编语法:
为了与gcc输出汇编程序的兼容性,as编译器使用AT&T系统V的汇编语法。这种语法与Intel汇编程序的语法很不一样:
1、AT&T语法中立即操作数前面要加上字符"$";寄存器操作数名前要加字符"%";绝对跳转/调用操作数前加"*"。而Intel汇编语法没有这些限制。
2、AT&T语法与Intel语法使用的源和目的操作数次序正好相反。AT&T的源和目的操作数是从左到右"源,目的"。例如Intel语句"add eax, 4"对应AT&T的"addl $4, %eax"。
3、AT&T语法中内存操作数的长度(宽度)由操作码最后一个字符来确定,操作码后缀"b"、"w"、"l"分别指示内存引用宽度为8位字节(byte)、16位字(word)、32位长字(long)。Intel语法则通过在内存操作数前使用前缀"byte ptr"、"word ptr"、"dword ptr"来达到同样的目的。因此Intel语句中的"mov al, byte ptr foo"对应于AT&T语句"movb $foo, %al"。
4、AT&T语法中立即形式的远跳转和远调用为"ljmp/lcall $section, $offset",而Intel的是"jmp/call far section:offset"。同样AT&T语法中远返回指令"lret $stack-adjust"对应Intel的"ret far stack-adjust"。
5、AT&T汇编器不提供对多代码段程序的支持。UNIX类操作系统要求所有代码在一个段中。
汇编程序预处理:
1、调整并删除多余的空格字符和制表符。
2、删除所有注释语句并且使用单个空格或一些换行符替换它们。
3、把字符常数转换为对应的数值。
该功能不会对宏定义进行处理,也没有处理文件包含的功能。如果需要,可以将文件名改为大写后缀".S",让as使用gcc的CPP预处理功能。
由于as除了使用C语言的"/*...*/"注释语句以外,还使用"#"作为单行注释开始字符,因此若在汇编之前不对程序执行预处理,那么程序中包含的所有以"#"开头的指示符或命令均会被当做注释部分。
符号、语句和常数:
符号:由字符组成,包括大小写字符、数字、"_.$"。不允许以数字开头,大小写敏感。
语句:以换行符或者行分割字符";"作为结束。"\"加上换行符,可以让一个语句多行。
标号:符号后面跟一个":"。
关键符号:以"."开始,那么是一个汇编命令(伪指令);以字母开始,是一个汇编指令语句。
一条语句通用格式:
常数:一个数字,分为字符常数(字符串和单个字符)和数字常数(整数、大数和浮点数)。
字符串:用双引号括住,可以用\表示转义字符。反斜杠后是其他字符,那么反斜杠不起作用,as发出警告。
整数数字常数:
1、以"0b"或"0B"开头的二进制数。
2、以"0"开头的八进制数。
3、以非0数字开始的十进制数。
4、以"0x"或"0X"开始的十六进制数。
负数前面添加"-"。
大数(Bignum):位数超过32位二进制位的数。
浮点数:与C语言基本一样,内核很少使用浮点数。
指令语句、操作数与寻址:
指令:CPU执行的操作,通常指令称为操作码(Opcode)。
操作数(Operand):指令的操作对象。
地址:指定数据在内存中的位置。
指令语句:程序运行时刻执行的一条语句,通常包括4个部分:
1、标号(可选)。
2、操作码(指令助记符)。
3、操作数(由具体指令指定)。
4、注释。
一条指令语句可以含有0到3个用逗号分开的操作数。对于具有两个操作数的指令语句,第1个是源操作数,第2个是目的操作数。
操作数可以是立即数、寄存器、内存。一个间接操作数含有实际操作数值的地址值。AT&T语法通过在操作数前加"*"来指定一个间接操作数。只有调转/调用指令才能使用间接操作数。跳转指令说明:
1、立即数前加"$"。
2、寄存器名前加"%"。
3、内存操作数由变量名或者含有变量地址的一个寄存器指定。
指令操作码的命名:
AT&T语法中,指令操作码名称最后一个字符用来指明操作数的宽度。
字符"b"、"w"、"l"分别指定byte、word、long类型的操作数。
如果指令名称没有带这样的字符后缀,并且指令语句中不含操作数,那么as就会根据目的寄存器操作数来尝试确定操作数宽度。例如"mov %ax, %bx"等价于"movw %ax, %bx"。
AT&T与Intel语法中几乎所有指令操作码的名称都相同。
例外:符号扩展和零扩展指令需要两个宽度"movs..."和"movz...",从byte到long的符号扩展为"movsbl ..."。