马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
有时候需要在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, %eax movl b, %ebx addl %ebx,%eax movl %eax, sum 前边两条是编译器加上的。 输出部分就是说,在这段汇编执行完后,需要把结果保存到这个变量中。如果变量是内存数(=m),那就不用管了,但如果变量在汇编指令执行过程中使用了寄存器,那么需要将这个寄存器的值存入变量,例如使用了eax,那么这段汇编后需要加上一条指令 movl %eax, sum。 如果我们将sum也使用寄存器,"=r",那么会多加两条语句 movl sum,%ecx movl a, %eax movl b, %ebx addl %ebx,%eax movl %eax, %ecx movl %ecx, sum 实际上只在最后加了一句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的两个操作数可以交换顺序(当然两个操作数都不能是立即数) | | # | 部分注释,从该字符到其后的逗号之间所有字母被忽略 | | * | 表示如果选用寄存器,则其后的字母被忽略 |
|