GCC内联汇编
1. 基本内联语法:
GCC用关键字asm表示一段用汇编语言写的源代码。形式如下:
asm ("assemble language");
说明:
- 汇编指令必须用双引号扩起来
- 如果有多条汇编指令,必须用换行符隔开
\n,每条语句之前最好加上\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标准,asm和volatile都要写成__asm__ __volatile__。
2. 扩展的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放在%edx,data2放在%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 0f中f表示forward,如果向后跳转使用b即backward。

本文介绍了GCC内联汇编的基本语法和扩展形式,包括如何插入汇编指令、使用全局变量以及防止编译器优化。重点讲解了扩展ASM的四部分构成:汇编代码、输出位置、输入操作符和改变的寄存器。此外,还详细阐述了操作符的使用,如输入输出变量的定义以及占位符的含义,如`%0`、`%1`等。通过实例展示了内联汇编在处理寄存器和内存单元时的规则和注意事项。
661

被折叠的 条评论
为什么被折叠?



