GCC内联汇编

来源:互联网 发布:淘宝客返利在哪里查看 编辑:程序博客网 时间:2024/06/05 11:07

GCC内联汇编


基本内联语法:

GCC用关键字asm表示一段用汇编语言写的源代码。形式如下:
asm ("assemble language");
说明:
1. 汇编指令必须用双引号扩起来
2. 如果有多条汇编指令,必须用换行符隔开(\n),每条语句之前最好加上tab(\t),为了区别汇编语言中的标号(label)。
编译器实际上把asm括号中双引号引起来的语句逐字的插入到编译后产生的汇编文件中(.s),并进行了相应的替换操作。
可以这样:
asm ("movl $1,%eax\n\tmovl $0,%ebx\n\tint $0x80");
看起来麻烦,可以分行,分开成多个双引号:

asm ("movl $1,%eax\n\t"    "movl $0,%ebx\n\t"    "int $0x80");

可以使用C程序中的全局变量:

#include <stdio.h>int a = 10;int b = 20;int result;int main(){    asm("pusha\n\t"        "movl a, %eax\n\t"        "movl b, %ebx\n\t"        "imull %ebx, %eax\n\t"        "movl %eax, result\n\t"        "popa");    printf("the answer is %d\n",result);    return 0;}

有时候编译器会对变易产生的汇编代码进行优化,可能导致产生的效果与我们预想的不一样。可以在asm之后使用volatile关键字进行说明,让编译器不进行优化。
Note:如果使用ANSI C标准,asmvolatile都要写成__asm__ __volatile__

扩展的ASM

基本形式有很多限制:输入输出只能使用C语言中的全局变量,而且不能随意地改变寄存器的值。(前面的例子中开头和结尾使用pusha和popa来保存寄存器)
扩展汇编形式:
asm ("assembly code" : output locations : input operands : changed registers);
这种形式有四个部分,用冒号隔开:
汇编代码(assembly code):内联汇编代码,语法同基本asm格式。
输出位置(output locations):汇编代码中输出值所在的寄存器和内存单元列表。(执行汇编代码之后)
输入操作符(input operands):汇编代码中输入值所在的寄存器和内存单元的列表。(执行汇编代码前处理)
改变的寄存器(changed registers):内联汇编代码中改变的寄存器(用于提醒编译器对这些寄存器进行保护操作)。

在基本asm格式中,汇编语言中输入输出值可以直接使用C语言中的全局变量,但是在扩展asm格式中,格式不太一样:
“constraint”(variable)
variable是一个C语言变量(全局变量或局部变量都可以),constraint定义了从哪里取这个变量(对于输入变量来说)或往哪里存放(对输出变量)。constraint可能的取值如下表:

constraint description a use the %eax,%ax or %al regiters b use the %ebx,%bx or %bl registers c use the %ecx,%cx or %cl registers d use the %edx,%dx or %dl registers S use the %esi or %si registers D use the %edi or %di registers r use any available general-purpose register q use either the %eax,%ebx,%ecx or %edx register A use the %eax and the %edx regiters for a 64-bit value f use a floating-point register t use the first(top) floating-point register u use the second floating-point register m use the variable’s memory location o use an offset memory location V use a direct memory location i use an immediate integer value n use an immediate integer value with a known value g use any register or memory location available

除了这些限制,输出值还包含一个限制修饰符,它指明编译器如何处理输出值。

output modifer description + The operand can be both read from and written to = The operand can only be written to % The operand can be switched with the next operand if necessary & The operand can be deleted and reused before the inline functions c

举例:
asm ("assembly code" : "=a"(result) : "d"(data1),"c"(data2));
这个例子中C变量data1放在%edxdata2放在%ecx。输出结果会被放到%eax,然后送到result变量。

#include <stdio.h>int main(){    int data1 = 10;    int data2 = 20;    int result;    asm ("imull %%edx, %%ecx\n\t"        "movl %%ecx, %%eax"        : "=a"(result)        : "d"(data1), "c"(data2)        );    printf("The result is %d\n",result);    return 0;}
asm volatile ("cld\n\t"        "rep movsb"        :        :"S"(input),"D"(output),"c"(length));

由于没有指定输出规则,编译器会认为这段汇编是不必要的,因此要加上volatile

汇编语言部分(assembly language)可以使用占位符(placeholder)用来引用输出输入变量。格式为%0 %1 ...用来表示输出输入列表中的变量。

asm ("imull %1, %2\n\t"    "movl %2, %0\n\t"    :"=r"(result)    :"r"(data1),"r"(data2)    );

%0代表存放result的寄存器;%1代表存放data1的寄存器;%2代表存放data2的寄存器。

asm ("imull %1,%0"    : "=r"(data2)    : "r"(data1),"0"(data2));

“0”表示让编译器使用第一个命名的寄存器。

int dividend = 20 ;int divisor = 5;int result ;asm ("divb %2\n\t"    "movl %%eax, %0"    :  "=m"(result)    : "a"(dividend),"m"(divisor));

内联汇编可以使用标签,用来进行跳转。数字可以作为局部标签。

asm ("cmp %1, %2\n\t"    "jge 0f\n\t"    "movl %1, %0\n\t"    "jmp 1f\n"    "0:\n\t"    "movl %2, %0\n"    "1:"    :"=r"(result)    :"r:(a),"r"(b));

0: 1:都是label,jmp 0ff表示forward,如果向后跳转使用bbackward。