catch 发表于 2013-11-30 16:36:10

有关嵌入汇编:

有时候需要在C语言里使用汇编语言,或者是提高性能,或者是因为某些功能不能由系统调用实现。而在内核里,C语言里嵌入汇编是非常普遍的。如何在C语言里嵌入汇编语言呢?
[*]int main()
[*]
[*]{
[*]          __asm__ __volatile__ (
[*]          "movl %eax,%ebx\n\taddl %eax %ebx\n"
[*]          );
[*]          return 0;
[*]}
使用__asm__宏就可以嵌入汇编,__volatile__指示不让gcc优化下面的汇编代码。
[*]   .file "gccasm.c"
[*]   .text
[*]     .globl main
[*]   .type main, @function
[*]main:
[*]   pushl %ebp
[*]   movl %esp, %ebp
[*]
[*]#APP
[*]# 3 "gccasm.c" 1
[*]   movl %eax,%ebx
[*]   addl %eax,%ebx
[*]# 0 "" 2
[*]#NO_APP
[*]
[*]   movl $0, %eax
[*]   popl %ebp
[*]ret
红色的部分就是我们嵌入的代码。上面的指令确实执行了我们嵌入的两条指令,可是这两条指令只是执行了。没给我们任何结果。如果我们想用汇编实现两个内存数的相加,怎么办?
[*]int main()
[*]
[*]{
[*]
[*]   int a,b,sum;
[*]
[*]   scanf("%d %d",&a,&b);
[*]
[*]   __asm__ __volatile__ (
[*]
[*]   ??????????????
[*]
[*]   );
[*]
[*]   printf("sum=%d\n",sum);
[*]
[*]   return 0;
[*]
[*]}
中间要怎么填呢?
[*]__asm__ (
[*]
[*]    "addl %2,%1\n\tmovl %1,%0\n"
[*]
[*]    :"=m"(sum)
[*]
[*]   :"r"(a),"r"(b)
[*]
[*]   );
上面的代码是什么意思呢?实际上,嵌入汇编的标准格式是下面这个样子:__asm__(      汇编语句模板      : 输出部分      : 输入部分      : 破坏描述部分);第一个冒号前边是汇编语句模版,%0,%1,%2被称为占位符,%0是下面出现的第一个变量,%1是第二个,依次类推,一共可以有10个,%0-%9。下面的三个变量出现顺序是sum,a,b,因此%0,%1,%2分表代表sum,a,b。那是不是这两句汇编就相当于addl b,a; movl a,sum?明显不是,因为一条指令是不能出现两个内存数的。接下来就是输出部分和输入部分了。这两部分主要就是说使用了哪些变量,一次列出,比如这里就列出了sum,a,b,分别用%0,%1,%2表示,但是每个变量前边还有个标志,是告诉编译器怎么使用这些变量。输出部分前边还要加个“=”。这些标志是什么意思呢?前边如果是r,那么这个变量要事先处理一下,先载入一个寄存器,然后再使用我们嵌入的汇编,因此前边要加一个指令,例如a和b,都是寄存器数,前边还需要添加movl a,%eax movl b, %ebx(r表示让编译器选寄存器,可以使用其他标志选特定的寄存器)。而sum是内存数,直接使用即可。因此这段代码实际上对应的汇编代码是:movl a, %eaxmovl b, %ebxaddl %ebx,%eaxmovl %eax, sum前边两条是编译器加上的。http://img1.51cto.com/attachment/201212/25/962631_1356418825SW4v.png输出部分就是说,在这段汇编执行完后,需要把结果保存到这个变量中。如果变量是内存数(=m),那就不用管了,但如果变量在汇编指令执行过程中使用了寄存器,那么需要将这个寄存器的值存入变量,例如使用了eax,那么这段汇编后需要加上一条指令 movl %eax, sum。如果我们将sum也使用寄存器,"=r",那么会多加两条语句movl sum,%ecxmovl a, %eaxmovl b, %ebxaddl %ebx,%eaxmovl %eax, %ecxmovl %ecx, sumhttp://img1.51cto.com/attachment/201212/25/962631_1356418826AcIP.png实际上只在最后加了一句movl %ebx, sum,前边那一句理论上是应该加的,但是由于并没有对它进行破坏,不需要加。最后的破坏部分是指该嵌入汇编代码段可能会影响哪些寄存器,也可以是内存,告诉编译器要注意保护这些地方。由逗号格开的字符串组成,每个字符串描述一种情况,一般是寄存器名;除寄存器外还有 “memory”。例如:“%eax”,“%ebx”,“memory” 等。全局变量如果使用全局变量就不用这么麻烦了。
[*]int a,b,sum;
[*]int main()
[*]{
[*]         scanf("%d,%d",&a,&b);
[*]         asm("movl a,%eax\n"\
[*]             "\taddl b,%eax\n"\
[*]             "\tmovl %eax,sum\n");
[*]         printf("sum=%d\n",sum);
[*]         return 0;
[*]}
注意:如果没有出现%0,%1这样的占位符,寄存器就用%eax,%ebx,如果出现了,就用%%eax,%%ebx。 限制字符限制字符有很多种,有些是与特定体系结构相关,此处仅列出常用的限定字符和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的两个操作数可以交换顺序(当然两个操作数都不能是立即数)
#部分注释,从该字符到其后的逗号之间所有字母被忽略
*表示如果选用寄存器,则其后的字母被忽略

页: [1]
查看完整版本: 有关嵌入汇编: