gcc中的内嵌汇编语言_第1页
gcc中的内嵌汇编语言_第2页
gcc中的内嵌汇编语言_第3页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、gcc中的内嵌汇编语言作者:欧阳光初次接触到AT&T格式的汇编代码,看着那一堆莫名其妙的怪符号,真是有点痛不欲生的感觉,只好慢慢地去啃gcc文档,在似懂非懂的状态下过了一段时间。后来又在网上找到了灵溪写的gcc中的内嵌汇编语言一文,读后自感大有裨益。几个月下来,接触的源代码多了以后,慢慢有了一些经验。为了使初次接触AT&T格式的汇编代码的同志不至于遭受我这样的痛苦,就整理出该文来和大家共享.如有错误之处,欢迎大家指正,共同提高.本文主要以举例的方式对gcc中的内嵌汇编语言进行进一步的解释。一、gcc对内嵌汇编语言的处理方式gcc在编译内嵌汇编语言时,采取的步骤如下1. 变量输入:根据限定符的内

2、容将输入操作数放入合适的寄存器,如果限定符指定为立即数(I)或内存变量(m),则该步被省略,如果限定符没有具体指定输入操作数的类型(如常用的g),gcc会视需要决定是否将该操作数输入到某个寄存器.这样每个占位符都与某个寄存器,内存变量,或立即数形成了一一对应的关系.这就是对第二个冒号后内容的解释.如:a(foo),T(100),m(bar)表示%0对应eax寄存器,%1对应100,%2对应内存变量bar.2. 生成代码:然后根据这种一一对应的关系(还应包括输出操作符),用这些寄存器,内存变量,或立即数来取代汇编代码中的占位符(则有点像宏操作),注意,则一步骤并不检查由这种取代操作所生成的汇编代

3、码是否合法,例如,如果有这样一条指令asm(movl%0,%1:m(foo),m(bar);如果你用gcc-c-S选项编译该源文件,那么在生成的汇编文件中,你将会看到生成了movlfoo,bar这样一条指令,这显然是错误的.这个错误在稍后的编译检查中会被发现.3. 变量输出:按照输出限定符的指定将寄存器的内容输出到某个内存变量中,如果输出操作数的限定符指定为内存变量(m),则该步骤被省略.这就是对第一个冒号后内容的解释,如:asm(mov%0,%1:=m(foo),=a(bar):);编译后为#APPmovlfoo,eax#NO_APPmovleax,bar该语句虽然有点怪怪的,但它很好的体现

4、了gcc的运作方式.再以arch/i386/kernel/apm.c中的一段代码为例,我们来比较一下它们编译前后的情况源程序编译后的汇编代码_asm_(pushl%edintpushl%ebpntsaisa%|aoiupaxpa%|aoiu8lX08%|AOIUqaxqa%|aoiueaxea%iaoiuddV_ON#!P9%|doddqa%|dodaqa|ppe|e%ogs:so%|eo|dqa%|qsnd!P9%lUsndddV#X08%,UI-X08|AOIUxq8%,ui-xq8|aoiuxe8%,ui-xe8iaoiu(urxo8)11o11,(urxq8)11q11,(uFxe8)

5、11e11:(se)11S=H,(P9).1P=.1,(O9).P=.1,(q8)11q=11,(e8)11e=11:.Ju!pe%|dodu.judq8%|dodu.Ju2%lL%ippeu.Ju|e%ops“.ju:so%|eo|u第三个冒号后面内容主要针对gcc优化处理,它告诉gcc在本段汇编代码中对寄存器和内存的使用情况,以免gcc在优化处理时产生错误.1.它可以是eax,ebx,ecx等寄存器名,表示本段汇编代码对该寄存器进行了显式操作,如asm(mov%eax,%0,:=r(foo):eax);这样gcc在优化时会避免使用eax作临时变量,或者避免cache到eax的内存变量通过该

6、段汇编码.下面的代码均用gcc的-02级优化,它显示了嵌入汇编中第三个冒号后eax的作用源程序编译后的汇编代码正常情况下intmain()intbar=1;bar=fun();bar+;returnbar;pushl%ebpmovl%esp,%ebpcallfunincl%eax#显然,bar缺省使用eax寄存器leaveret加了汇编后intmain()intbar=1;bar=fun();asmvolatile(:eax);bar+;returnbar;pushl%ebpmovl%esp,%ebp#建立堆栈框架callfun#fun的返回值放入bar中,此时由于嵌入汇编#指明改变了eax的

7、值,为了避免冲突,#bar改为使用edx寄存器movl%eax,%edx#APP#NO_APPincl%edxmovl%edx,%eax#放入main()的返回值leaveret2.merory是一个常用的限定,它表示汇编代码以不可预知的方式改变了内存,这样gcc在优化时就不会让cache到寄存器的内存变量使用该寄存器通过汇编代码,否则可能会发生同步出错.有了上面的例子,这个问题就很好理解了三.对&限定符的解释这是一个较常见用于输出的限定符.它告诉gcc输出操作数使用的寄存器不可再让输入操作数使用.对于g,r等限定符,为了有效利用为数不多的几个通用寄存器,gcc一般会让输入操作数和输出操作数选

8、用同一个寄存器.但如果代码没编好,会引起一些意想不到的错误:如asm(callfun;movebx,%1:=a(foo):r(bar);gcc编译的结果是foo和bar同时使用eax寄存器:movlbar,eax#APPcallfunmovlebx,eax#NO_APPmovleax,foo本来这段代码的意图是将fun()函数的返回值放入foo变量,但半路杀出个程咬金,用ebx的值冲掉了返回值,所以这是一段错误的代码,解决的方法是加上一个给输出操作数加上一个&限定符:asm(callfun;movebx,%1:=&a(foo):r(bar);这样gcc就会让输入操作数另寻高就,不再使用eax寄

9、存器了GCC内嵌汇编简介发表:2004-4-30:16:23出处:你的博客网()在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可,GCC会自动插入代码完成必要的操作。1、简单的内嵌汇编例:_asmvolatile_(hit);“表示后面”勺代码为内嵌汇编,“asn是“_asm别名。“_volatile_示编译器不要优化代码,后面的指令保留原样,“volatile是它的别名。括号里面是汇编指令。2、内嵌汇编举例使用内嵌汇编,

10、要先编写汇编指令模板,然后将C语言表达式与指令的操作数相关联,并告诉GCC对这些操作有哪些限制条件。例如在下面的汇编语句:_asmviolate_(movl%1,%0:=r(result):m(input);“movl%1,%0是指令模板;“(和“1代表指令的操作数,称为占位符,内嵌汇编靠它们将C语言表达式与指令操作数相对应。指令模板后面用小括号括起来的是C语言表达式,本例中只有两个:“resul和”“input,”他们按照出现的顺序分别与指令操作数“0,“1对应;注意对应顺序:第一个C表达式对应“0;第二个表达式对应“”依次类推,操作数至多有10个,分别用“0,“1.表%9示。在每个操作数前

11、面有一个用引号括起来的字符串,字符串的内容是对该操作数的限制或者说要求。“resul前面的限制字符串是“=r,”其中“二表示“resul是输出操作数,“表示需要将“resul与某个通用寄存器相关联,先将操作数的值读入寄存器,然后在指令中使用相应寄存器,而不是“resul本身,当然指令执行完后需要将寄存器中的值存入变量“result,从表面上看好像是指令直接对“resul进行操作,实际上GCC做了隐式处理,这样我们可以少写一些指令。“input前面的“r表示该表达式需要先放入某个寄存器,然后在指令中使用该寄存器参加运算。C表达式或者变量与寄存器的关系由GCC自动处理,我们只需使用限制字符串指导G

12、CC如何处理即可。限制字符必须与指令对操作数的要求相匹配,否则产生的汇编代码将会有错,读者可以将上例中的两个“,都改为“m(r表示操作数放在内存,而不是寄存器中),编译后得到的结果是:movlinput,result很明显这是一条非法指令,因此限制字符串必须与指令对操作数的要求匹配。例如指令movl允许寄存器到寄存器,立即数到寄存器等,但是不允许内存到内存的操作,因此两个操作数不能同时使用“m乍为限定字符GCC内嵌汇编2出处:内嵌汇编语法如下:_asm_(汇编语句模板:输出部分:输入部分:破坏描述部分)共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格开,汇编语句模

13、板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需要用“:”格开,相应部分内容为空。例如:_asm_volatile_(cli:memory)、汇编语句模板汇编语句模板由汇编语句序列组成,语句之间使用“;”、n”或“t”分开。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,%9。指令中使用占位符表示的操作数,总被视为long型(4个字节),但对其施加的操作根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,“b”代表低字节,h”

14、代表高字节,例如:%h1。1 、输出部分输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和C语言变量组成。每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数。例:_asm_volatile_(pushfl;popl%0;cli:=g(x)描述符字符串表示对该变量的限制条件,这样GCC就可以根据这些条件决定如何分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。2 、输入部分输入部分描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由限定字符串和C语言表达式或者C语言变量组成。例1:_asm_volatil

15、e_(lidt%0:m(real_mode_idt);例二(bitops.h):Static_inline_void_set_bit(intnr,volatilevoid*addr)_asm_(btsl%1,%0:=m(ADDR):Ir(nr);后例功能是将(*addr)的第nr位设为1。第一个占位符%0与C语言变量ADDR对应,第二个占位符%1与C语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:btslnr,ADDR,该指令的两个操作数不能全是内存变量,因此将nr的限定字符串指定为“lr,将nr与立即数或者寄存器相关联,这样两个操作数中只有ADDR为内存变量。4、限制字符4.1、

16、限制字符列表限制字符有很多种,有些是与特定体系结构相关,此处仅列出常用的限定字符和i386中可能用到的一些常用的限定符。它们的作用是指示编译器如何处理其后的C语言变量与指令操作数之间的关系。分类限定符描述通用寄存器a”将输入变量放入eax这里有一个问题:假设eax已经被使用,那怎么办?其实很简单:因为GCC知道eax已经被使用,它在这段汇编代码的起始处插入一条语句pushl%eax,将eax内容保存到堆栈,然后在这段代码结束处再增加一条语句popl%eax,恢复eax的内容b”将输入变量放入C将输入变量放入ebxecxd将输入变量放入edxS将输入变量放入d将输入变量放入esiedi“q将输入

17、变量放入eax,ebx,ecx,edx中的一个f将输入变量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一个A”把eax和edx合成一个64位的寄存器(uselongIongs)内存m”内存变量o”操作数为内存变量,但是其寻址方式是偏移量类型,也即是基址寻址,或者是基址加变址寻址“V操作数为内存变量,但寻址方式不是偏移量类型“”操作数为内存变量,但寻址方式为自动增量p”操作数是一个合法的内存地址(指针)寄存器或内存g”将输入变量放入eax,ebx,ecx,edx中的一个或者作为内存变量X”操作数可以是任何类型立即数“I”0-31之间的立即数(用于32位移位指令)“J”

18、0-63之间的立即数(用于64位移位指令)“N”0-255之间的立即数(用于out指令)“”立即数n”立即数,有些系统不支持除字以外的立即数,这些系统应该使用“n”而不是i”匹配“0”,表示用它限制的操作数与某个指定的操作数匹配,“1”.也即该操作数就是指定的那个操作数,例如“0”“9”去描述“1”操作数,那么“%1”引用的其实就是“%0”操作数,注意作为限定符字母的09与指令中的“0”“9”的区别,前者描述操作数,后者代表操作数。&该输出操作数不能使用过和输入操作数相同的寄存器操作数类型“=”操作数在指令中是只写的(输出操作数)“+”操作数在指令中是读写类型的(输入输出操作数)浮点数浮点寄存

19、器“t”第一个浮点寄存器u”第二个浮点寄存器G”标准的80387浮点常数%该操作数可以和下一个操作数交换位置例如addl的两个操作数可以交换顺序(当然两个操作数都不能是立即数)# 部分注释,从该字符到其后的逗号之间所有字母被忽略表示如果选用寄存器,则其后的字母被忽略5、破坏描述部分破坏描述符用于通知编译器我们使用了哪些寄存器或内存,由逗号格开的字符串组成,每个字符串描述一种情况,一般是寄存器名;除寄存器外还有“memory。例如:eax”,ebx“memory”等。AT&T与INTEL的汇编语言语法的区别岀处:1、大小写INTEL格式的指令使用大写字母,而AT&T格式的使用小写字母。例:INT

20、ELAT&TMOVEAX,EBXmovl%ebx,%eax2、操作数赋值方向在INTEL语法中,第一个表示目的操作数,第二个表示源操作数,赋值方向从右向左。AT&T语法第一个为源操作数,第二个为目的操作数,方向从左到右,合乎自然。例:INTELAT&TMOVEAX,EBXmovl%ebx,%eax3、前缀在INTEL语法中寄存器和立即数不需要前缀;AT&T中寄存器需要加前缀“%”;立即数需要加前缀“$”。例:INTELAT&TMOVEAX,1movl$1,%eax符号常数直接引用,不需要加前缀,如:movlvalue,%ebx,value为一常数;在符号前加前缀$表示引用符号地址,如movl$

21、value,%ebx,是将value的地址放到ebx中。总线锁定前缀lock:总线锁定操作。“lock前缀在Linux核心代码中使用很多,特别是SMP代码中。当总线锁定后其它CPU不能存取锁定地址处的内存单元。远程跳转指令和子过程调用指令的操作码使用前缀l,分别为ljmp,Icall,与之相应的返回指令伪lret。例:INTELAT&TCALLFARSECTION:OFFSETlcall$secion:$offsetJMPFARSECTION:OFFSETljmp$secion:$offsetRETFARSATCK_ADJUSTlret$stack_adjust3 、间接寻址语法INTEL中基

22、地址使用“”、“”,而在AT&T中使用“(”、)“”;另外处理复杂操作数的语法也不同,INTEL为Segreg:base+index*scale+disp,而在AT&T中为%segreg:disp(base,index,sale),其中segreg,index,scale,disp都是可选的,在指定index而没有显式指定Scale的情况下使用默认值1。Scale和disp不需要加前缀“&”。INTELAT&TInstrinstrfoo,segreg:base+index*scale+disp%segreg:disp(base,index,scale),foo、后缀AT&T语法中大部分指令操作

23、码的最后一个字母表示操作数大小,“b”表示byte(个字节);“w”表示word(2个字节);“丨”表示long(4个字节)。INTEL中处理内存操作数时也有类似的语法如:BYTEPTR、WORDPTR、DWORDPTR。例:INTELAT&Tmoval,blmovb%bl,%almovax,bxmovw%bx,%axmoveax,dwordptrebxmovl(%ebx),%eax在AT&T汇编指令中,操作数扩展指令有两个后缀,一个指定源操作数的字长,另一个指定目标操作数的字长。AT&T的符号扩展指令的为“movs”,零扩展指令为“movz”(相应的Intel指令为movsx”和movzx”

24、)。因此,movsbl%al,%edx”表示对寄存器al中的字节数据进行字节到长字的符号扩展,计算结果存放在寄存器edx中。下面是一些允许的操作数扩展后缀:bl:字节-长字bw:字节-字wl:字-长字跳转指令标号后的后缀表示跳转方向,“f表示向前(forward),b表示向后(back)。例:jmp1f1:jmp1f1:6、指令INTEL汇编与AT&T汇编指令基本相同,差别仅在语法上。关于每条指令的语法可以参考I386Manual。gcc中的内嵌汇编语言出处:gcc采用的是AT&T的汇编格式,MS采用Intel的格式一基本语法语法上主要有以下几个不同.寄存器命名原则AT&T:%eaxIntel

25、:eax源/目的操作数顺序AT&T:movl%eax,%ebxIntel:movebx,eax常数/立即数的格式AT&T:movl$_value,%ebxIntel:moveax,_value把_value的地址放入eax寄存器AT&T:movl$0xd00d,%ebxIntel:movebx,0xd00d操作数长度标识AT&T:movw%ax,%bxIntel:movbx,ax寻址方式AT&T:immed32(basepointer,indexpointer,indexscale)Intel:basepointer+indexpointer*indexscale+imm32)Linux工作于

26、保护模式下,用的是32位线性地址,所以在计算地址时不用考虑segmentffset的问题上式中的地址应为:imm32+basepointer+indexpointer*indexscale下面是一些例子:直接寻址AT&T:_booga;_booga是一个全局的C变量注意加上$是表示地址引用,不加是表示值引用注:对于局部变量,可以通过堆栈指针引用Intel:_booga寄存器间接寻址AT&T:(%eax)Intel:eax变址寻址AT&T:_variable(%eax)Intel:eax+_variableAT&T:_array(,%eax,4)Intel:eax*4+_arrayAT&T:_a

27、rray(%ebx,%eax,8)Intel:ebx+eax*8+_array基本的行内汇编基本的行内汇编很简单,一般是按照下面的格式asm(statements);例如:asm(nop);asm(cli);asm和_asm_是完全一样的如果有多行汇编,则每一行都要加上nt例如:asm(pushl%eaxntmovl$0,%eaxntpopl%eax);实际上gcc在处理汇编时,是要把asm(.)的内容打印到汇编文件中,所以格式控制字符是必要的再例如:asm(movl%eax,%ebx);asm(xorl%ebx,%edx);asm(movl$0,_booga);在上面的例子中,由于我们在行内

28、汇编中改变了edx和ebx的值,但是由于gcc的特殊的处理方法,即先形成汇编文件,再交给GAS去汇编,所以GAS并不知道我们已经改变了edx和ebx的值,如果程序的上下文需要edx或ebx作暂存,这样就会引起严重的后果对于变量_booga存在一样的问题为了解决这个问题,就要用到扩展的行内汇编语法三扩展的行内汇编扩展的行内汇编类似于Watcom.基本的格式是:asm(statements:output_regs:input_regs:clobbered_regs);clobbered_regs指的是被改变的寄存器下面是一个例子(为方便起见,我使用全局变量):intcount=1;intvalue

29、=1;intbuf10;voidmain()asm(cldntrepntstosl:c(count),a(value),D(buf0):%ecx,%edi);得到的主要汇编代码为:movlcount,%ecxmovlvalue,%eaxmovlbuf,%edi#APPcldrepstosl#NO_APPcld,rep,stos就不用多解释了这几条语句的功能是向buf中写上count个value值冒号后的语句指明输入,输出和被改变的寄存器通过冒号以后的语句,编译器就知道你的指令需要和改变哪些寄存器,从而可以优化寄存器的分配其中符号c(count)指示要把count的值放入ecx寄存器类似的还有:

30、aeaxbebxcecxdedxSesiDediI常数值,(0-31)q,r动态分配的寄存器geax,ebx,ecx,edx或内存变量A把eax和edx合成一个64位的寄存器(uselonglongs)我们也可以让gcc自己选择合适的寄存器如下面的例子:asm(leal(%1,%1,4),%0:=r(x):0(x);这段代码实现5*x的快速乘法得到的主要汇编代码为:movlx,%eax#APPleal(%eax,%eax,4),%eax#NO_APPmovl%eax,x几点说明:1. 使用q指示编译器从eax,ebx,ecx,edx分配寄存器使用r指示编译器从eax,ebx,ecx,edx,e

31、si,edi分配寄存器我们不必把编译器分配的寄存器放入改变的寄存器列表,因为寄存器已经记住了它们.2. =是标示输出寄存器,必须这样用.3. 数字n的用法:数字表示的寄存器是按照岀现和从左到右的顺序映射到用r或q请求的寄存器如果我们要重用r或q请求的寄存器的话,就可以使用它们.4. 如果强制使用固定的寄存器的话,如不用%1,而用ebx,贝9asm(leal(%ebx,%ebx,4),%0:=r(x):0(x);注意要使用两个%,因为一个%的语法已经被n用掉了GCC内嵌汇编之语法详解发表:2004-4-30:54:05出处:你的博客网()内嵌汇编语法如下:_asm_(汇编

32、语句模板:输出部分:输入部分:破坏描述部分)共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:格开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需要用“:格开,相应部分内容为空。例如:_asmvolatile_(cli:memory)1、汇编语句模板汇编语句模板由汇编语句序列组成,语句之间使用“;n”或“t分开。指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,%9。指令中使用占位符表示的操作数,总被视为long型(4个字节),但对其施加的操作根据指令可以是字或者字节,当把操作数当作字或者字节使用

33、时,默认为低字或者低字节。对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,“b代表低字节,“h代表高字节,例如:h1。2、输出部分输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和C语言变量组成。每个输出操作数的限定字符串必须包含“=表示他是一个输出操作数。例:_asmvolatile_(pushfl;popl%0;cli:=g(x)描述符字符串表示对该变量的限制条件,这样GCC就可以根据这些条件决定如何分配寄存器,如何产生必要的代码处理指令操作数与C表达式或C变量之间的联系。3、输入部分输入部分描述输入操作数,不同的操作数描

34、述符之间使用逗号格开,每个操作数描述符由限定字符串和C语言表达式或者C语言变量组成。例1:_asmvolatile_(lidt%0:m(real_mode_idt);例二(bitops.h):Static_inline_void_set_bit(intnr,volatilevoid*addr)_asm_(btsl%1,%0:=m(ADDR):(nr);后例功能是将(*addr)的第nr位设为1。第一个占位符%0与C语言变量ADDR对应,第二个占位符%1与C语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:btslnr,ADDR,该指令的两个操作数不能全是内存变量,因此将nr的限定字符串指定为“lr,”将nr与立即数或者寄存器相关联,这样两个操作数中只有ADDR为内存变量。4、限制字符4.1、限制字符列表i386中可能用

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论