一、Linux汇编语言的学习内容
为了学习并能深刻理解Linux内核原代码,必须学习Linux汇编语言,对于Linux汇编语言的学习我觉得应该包括如下三个方面:
0)机器独立的汇编,比如:x86汇编指令,也叫instruction set,这部分需要参考处理器编程手册,比如:intel手册
《Intel64 and IA-32 Architectures Software Developer Manual Volume 2A: Instruction Set Reference A-M》
《Intel64 and IA-32 Architectures Software Developer Manual Volume 2B: Instruction Set Reference N-Z》
1)GAS汇编器支持的"伪汇编"指令,也叫derective,这部分需要参考AS的manual
文档地址:
只需要阅读前7章节
二、AT&T和Intel汇编的差异
Linux汇编语言采用的是AT&T风格(GAS采用的汇编语言风格),而我们读书的时候接触的都是Intel风格的汇编,这两种汇编在语法组成上主要有如下的区别:
1)前缀的区别:AT&T中寄存器名称前需要加上"%",立即数前加上"$",16进制前加上0x,jump/call的操作数前要加上'*'作为前缀
2)操作数的方向区别:两中正好相反,AT&A有点类似copy操作
3)操作数的字长由操作码的最后一个字母决定,b字节,w字,l长字
4)内存单元操作数,基寄存器用()而不是[]
5)间接寻址方式不同:%segreg:disp(base,index,scale),除base之外都可选,等价于Intel: segreg:[base+index*scale+disp]
三、GAS directives:
以.打头+directive名(小写字母),比如:
.section .data
.ascii "hello,world!"
.global symbl
GAS支持的所有的directive,可以参考:Using as/The gnu Assembler 第7章:Assembler Directives,
GAS2.14包含有:90条,而GAS2.20共有1百20条directives
四、GCC汇编扩展:
格式:__asm__("asm statements" : outputs : inputs : Clobber/Modify);
说明:
0)其中outputs : inputs : registers-modified可选
1)如果需要忽略GCC -O的优化作用,则要加上__asm__ __volatile__ (””)
2)最基本的汇编语句只包括:"asm statements"部分;可以有多条,每条之间用\n\t,指令中可以包括:%1,%2...表示C语言变量作为操作数
3)outputs:__asm__ __volatile__("movl %%cr0, %0":"=a" (i)); //把寄存器CR0中的内容输出到变量i中,通过eax进行中转
指定当前内联汇编语句的输出,它是一个操作表达式,一个操作表达式由两部分组成"操作约束"和(C表达式),其中()中的内容为在outputs相当于
C/C++赋值操作的左值表达式;而“”中的内容也可以称作“操作约束”,操作约束有很多种类型,对于outputs来说“操作约束”包括两部份:
一是:"="或者“+”,“=”号说明左值表达式是一个Write-Only的,而"+"则说明左值表达式是Read-Write,包含了“=”/“+”的约束只能在outputs中。
操作数约束的主要作用是确定操作数的寻址方式,常用的有如下几种:
寄存器操作数约束:用在寄存器间接寻址中, "a/b/c/d//r/q/D/S/f/t/u"
内存操作数约束: "m":不通过寄存器直接修改一内存变量,比如:("sidt %0\n" : :"m"(loc));
立即数约束:用在inputs中,用()中的立即数作为输输入,"i" (100)
通用约束:表示可以使用通用寄存器,内存,立即数等任何一种处理方式。__asm__ ("movl %0, %%eax" : : "g"(foo))
在Output域中可以有多个输出操作表达式,多个操作表达式中间必须用逗号(,)分开
4)inputs:__asm__("movl %0, %%cr0" : : "a" (pStr->f1)); //把pStr->f1中的内容通过赋给cr0寄存器。
Input域的内容用来指定当前内联汇编语句的输入,有点抽象,不好理解。
Input也是操作表达式,而其中"操作约束"部分通常是一个寄存器约束,
(C表达式)部分通过前面的寄存器输入到当前内联汇编中,这个时候相当于C赋值操作的右值表达式
所以它可以是一个变量,一个数字,还可以是一个复杂的表达式(比如a+b/c*d)。
5)Clobber/Modify:__asm__ ("movl %0, %%ebx; popl %%ecx" : : "a"(__foo) : "bx", "cx" ); //ebx,ecx可能会被修改,通知GCC
当内联汇编语句可能对某些寄存器或者某些内存进行修改,而这些寄存器或者内存又没有在outputs/inputs操作表达式中指定,则需要在Clobber/Modify域声明这些寄存器或内存。
这样等于告诉了GCC在编译的时候进行临时保存这些寄存器或者内存的值,当内联汇编结束后再进行恢复,以免覆盖掉有用的信息。如果是申明内存可能被修改,一般用“memory”关键
字;如果内联汇编可能会影响eflags寄存器中的条件标志,需要在Clobber/Modify域中使用"cc"来声明这一点
另外想进一步学习这部分的内容,除上面提到的文档或者链接地址外,推荐阅读:
《Professional Assembly Language》--Richard Blum
阅读(1676) | 评论(0) | 转发(0) |