Linux 内嵌汇编

因为Linux是用的并不是常用的Intel 汇编语法,而是AT&T 格式,所以先介绍一下AT&T 和 intel格式的差别


寄存器的命名
在寄存器的名字前加前缀%, 比如你要用eAx,那就要这样用%eAx

源操作数与目的操作数的顺序
源操作数在前,目的操作数在后,这点和Intel 语法刚好相反
比如: mov %eAx,%ebx  表示把eAx中的内容传到ebx中

操作数的大小
指令带后缀b,w,l,取决于操作数是byte,word还是long.这并不是强制的,,GCC会同过读操作数来判断后缀是什么,,但自己加上后缀没啥不好的,可以增加程序的可读性,且可减少编译器猜错的可能
movb %Al,%bl 传送byte
movw %Ax,%bx 传送word
movl %eAx,%ebx 传送longword

立即数
立即数前要加$
movl $0xffff,%eAx  将会把值0xffff送到eAx中

间接寄存器寻址
用到寄存器间接寻址内存的时候用()
movb (%esi),%Al 将把esi指向的内存单元的一个byte送到Al中

内嵌汇编

1.内嵌汇编举例
   使用内嵌汇编,要先编写汇编指令模板,然后将C语言表达式与指令的操作数相关联,并告诉GCC对这些操作有哪些限制条件。例如在下面的汇编语句:
    
__asm__ __violate__ ("movl %1,%0" : "=r" (result) : "m" (input));

"movl %1,%0"是指令模板;"%0"和"%1"代表指令的操作数,称为占位符,内嵌汇编靠它们将C 语言表达式与指令操作数相对应。指令模板后面用小括号括起来的是C语言表达式,本例中只有两个:"result"和"input",他们按照出现的顺序分 别与指令操作数"%0","%1"对应;注意对应顺序:第一个C 表达式对应"%0";第二个表达式对应"%1",依次类推,操作数至多有10 个,分别用"%0","%1"...."%9"表示。在每个操作数前面有一个用引号括起来的字符串,字符串的内容是对该操作数的限制或者说要求。 "result"前面的限制字符串是"=r",其中"="表示"result"是输出操作数,"r" 表示需要将"result"与某个通用寄存器相关联,先将操作数的值读入寄存器,然后在指令中使用相应寄存器,而不是"result"本身,当然指令执行 完后需要将寄存器中的值存入变量"result",从表面上看好像是指令直接对"result"进行操作,实际上GCC做了隐式处理,这样我们可以少写一 些指令。"input"前面的"r"表示该表达式需要先放入某个寄存器,然后在指令中使用该寄存器参加运算。


2. 内嵌汇编的基本格式


GCC提供结构(construct)"Asm"(注意第一个字母是小写:) 用来表示内嵌汇编,如下面的格式
  Asm( 汇编器模板(Assembler templAte)
     : 输出操作数 可选
     : 输入操作数 可选
     : 占用的寄存器 可选
     );
在这个样子里,汇编器模板包含汇编指令,输入操作数是变量,被用做汇编指令的输入操作数,输出操作数也是变量,被用做汇编指令的输出操作数(保存输出结果)

内嵌汇编重要的一点是它可以操作并且显式的输出到变量,由于这个特点,,"Asm"被当作是c语言同汇编语言接口

//一个基本但重要的区别是简单内嵌汇编只包含指令,而扩展内嵌汇编包含操作数,举例:

基本的内嵌汇编
{
    int A=10,b;
    Asm("movl %1, %%eAx;
  movl %%eAx, %0;"
    :"=r"(b) /*输出*/
    :"r"(A) /*输入*/
    :"%eAx" /*占用的寄存器*/
}
在这个例子里,我们用汇编指令让b的值等于A,注意以下几点:
  "b"是输出操作数,用%0来指定,"A"是输入操作数,用%1来指定
  "r"是一个操作数的限制参数(constrAint),它指定变量"A"和"b"被储存在积存器里,,输出操作数的限制参数前面有"=",,来表示他是输出操作数
  要在内嵌汇编里用寄存器"%eAx"的话,必须再多加一个%,既%%eAx,内嵌汇编用%0,%1来指定变量,任何以%开头的都将被作为输入输出操作数而不是寄存器
  第3个冒号后的"%eAx"告诉GCC,%eAx寄存器在Asm里已经被指定了,,GCC不会再用这个寄存器来储存其他值了
  movl %1, %%eAx 把"A"的值移到 %eAx中,movl %%eAx, %0 把%eAx的内容移到"b"中
  当这段Asm完成后,"b"将反映出值的变化,因为它是作为一个输出操作数,换句话说,"b"的值在Asm中改变,在Asm外表现出来


3. 汇编器模板
汇编器模板是一些被插入到c程序里的汇编指令(一条或多条),每一条指令要被双引号括起来,或者一组指令被一队双引号括起来,每条指令都要以分割符结束,可用的分割符可以是"\n",或者是";",操作数按顺序被编号%0,%1....

如果你不想让编译器来优化你的汇编代码,你可以用"volAtile"关键字,把它放在"Asm"的后面,,如果你的程序想和ANSI兼容,要用__Asm__ 和 __volAtile__来代替 Asm 和 volAtile

c语言的变量可以直接被内嵌汇编操作,这样非常方便

每个操作数用一个用双引号括起来的限制参数,后跟用括号扩起来的变量来定义,就是这种形式:"限制参数"(c变量)  限制参数的主要作用是用来决定操作数的寻址模式

(Addressing mode)


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

你可以在输入和输出里用多个操作数,,每个用逗号隔开


5. 占用的寄存器

如果在内嵌的指令中用到了寄存器,,我们可以告诉GCC,这样GCC会把我们指定的寄存器留出来不做其他的用途,但当操作数的限制参数里指明的,就不用再说了,,如"=b"(变量)
然而当指令要用到其他寄存器的时候,显式的或隐式的(并且这些寄存器并没有在输入或输出列表里出现过),这时候就要明确写在寄存器列表里的,列表列在第3个冒号后面,用寄存器的名字指定

6. 操作数的限制参数
限制参数可以有下面的作用
  操作数是否允许放在一个寄存器里,放到哪个寄存器里
  操作数是否是一个内存地址
  操作数是否是一个立即数

下面是限制参数的几种常用的用法: 
 Asm ("movl %%cr3 ,%0\n" :"=r"(cr3vAl));
表示将变量cr3vAl放到寄存器中,把%cr3中值复制先到那个寄存器,然后再把寄存器中的值更新到内存中保存变量cr3vAl的地方.由于是"=r",GCC可以把变量cr3vAl放到任何一个通用寄存器中,,如果要指定寄存器的话,用下面的方法指定
A  %eAx
b  %ebx
c  %ecx
d  %edx
S  %esi
D  %edi
用的时候就像这样 Asm ("movl %%cr3 ,%0\n" :"=b"(cr3vAl));  指定%ebx

Memory operAnd constrAint(m)
当操作数在内存中,任何操作直接作用在内存中,,不通过寄存器..通过寄存器的那种一般用在必须要用的指令或者用来使程序运行的更快,
("sidt %0\n" : :"m"(loc)); 表示输入变量loc不通过寄存器

MAthing(Digit) constrAints
当一个变量在输入输出时都用到的时候 "0","1","2".....(我想是这样吧)
Asm ("incl %0" :"=A"(vAr):"0"(vAr));
先把vAr存入寄存器%eAx,在寄存器里加1后送到vAr里

   6.1、限制字符列表
   限制字符有很多种,有些是与特定体系结构相关,此处仅列出常用的限定字符和i386中可能用到的一些常用的限定符。它们的作用是指示编译器如何处理其后的C语言变量与指令操作数之间的关系。

   分类             限定符                    描述
  通用寄存器        "a"               将输入变量放入eax
                            "b"               将输入变量放入ebx
                             "c"               将输入变量放入ecx
                             "d"                将输入变量放入edx
                             "s"               将输入变量放入esi
                             "d"               将输入变量放入edi
                             "q"              将输入变量放入eax,ebx,ecx,edx中的一个
                              "r"               将输入变量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一个
                              "A"              把eax和edx合成一个64 位的寄存器(use long longs)

       内存             "m"             内存变量
                     "o"             操作数为内存变量,但是其寻址方式是偏移量类型,
                                       也即是基址寻址,或者是基址加变址寻址
                     "V"             操作数为内存变量,但寻址方式不是偏移量类型
                     " "             操作数为内存变量,但寻址方式为自动增量
                     "p"             操作数是一个合法的内存地址(指针)

     寄存器或内存     "g"             将输入变量放入eax,ebx,ecx,edx中的一个
                                       或者作为内存变量
                       "X"            操作数可以是任何类型

     立即数
                     "I"             0-31之间的立即数(用于32位移位指令)
                       "J"             0-63之间的立即数(用于64位移位指令)
                     "N"             0-255之间的立即数(用于out指令)
                     "i"             立即数  
                     "n"            立即数,有些系统不支持除字以外的立即数,
                                       这些系统应该使用"n"而不是"i"

     匹配             " 0 ",         表示用它限制的操作数与某个指定的操作数匹配,
                     "1" ...               也即该操作数就是指定的那个操作数,例如"0"
                       "9"            去描述"%1"操作数,那么"%1"引用的其实就
                                       是"%0"操作数,注意作为限定符字母的0-9 与
                                       指令中的"%0"-"%9"的区别,前者描述操作数,
                                       后者代表操作数。
                       &                     该输出操作数不能使用过和输入操作数相同的寄存器

    操作数类型         "="          操作数在指令中是只写的(输出操作数)  
                       "+"          操作数在指令中是读写类型的(输入输出操作数)

     浮点数             "f"          浮点寄存器
                       "t"           第一个浮点寄存器
                       "u"          第二个浮点寄存器
                       "G"          标准的80387浮点常数
                       %                   该操作数可以和下一个操作数交换位置
                                       例如addl的两个操作数可以交换顺序
                                      (当然两个操作数都不能是立即数)
                       #                   部分注释,从该字符到其后的逗号之间所有字母被忽略
                       *                     表示如果选用寄存器,则其后的字母被忽略

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值