GCC内嵌汇编

gcc基本的内联汇编

基本的内联汇编格式是__asm__ __volatile__("Instruction List");

_asm_
 __asm__是gcc关键字asm的宏定义,用来申明一个内联汇编表达式

_volatile_
如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

Instruction List
 Instruction List是汇编指令序列,他可以是空的,比如__asm__ __volatile__("");
当Instruction List中有多条指令的时候,
a. 可以在一对引号中列出全部指令;

__asm__("
movl $1,%eax
xor %ebx,%ebx
int $0x80
");

b. 可以将一条或几条指令放在一对引号中,所有指令放在多对引号中。
 当所有的指令放在一对引号中的时候,可以将每一条指令放在一行,

__asm__("movl %eax, %ebx"
"sti"
"popl %edi"
"subl %ecx, %ebx");

如果要将多条指令放在一行,必须用分号;或换行符\n(大多数情况下后还要跟一个\t)将这些指令分开。

__asm__("movl %eax, %ebx; sti\n\t"
"popl %edi; subl %ecx, %ebx");

带有c/c++表达式的内联汇编

带有C/C++表达式的内联汇编格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
它和基本内联汇编的不同之处在于:它多了3个部分(Output,Input,Clobber/Modify)。在括号中的4个部分通过冒号(:)分开。另外基本内联汇编要求寄存器前只能使用一个百分号(%),而带有C/C++表达式格式则要求寄存器前必须使用两个百分号(%%)。

Output
 Output用来指定当前内联汇编语句的输出。
 比如__asm__("movl %%cr0, %0": "=a" (cr0));
“=a”中字母a是寄存器EAX/AX/AL的缩写,=表示作为当前内联汇编的输出
(cr0),括号内的部分是一个c/c++表达式,用来保存内联汇编的输出值
 所以这里最终寄存器EAX/AX/AL的值会输出到cr0中。

Input
 Input用来指定当前内联汇编语句的输入。
 比如__asm__("movl %0, %%db7" : : "a" (cpu->db7));
“a“,这里没有=表示a是作为输入的,内联汇编执行结束后表达式cpu->db7的值会写到字母a代表的寄存器中。

Operation Constraint
 Operation Constraint有很多,寄存器约束,内存约束,立即数约束,通用约束等

Clobber/Modify
有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这一点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在”Instruction List”,但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用”r”约束时由GCC 为其选择的,同时此寄存器被”Instruction List”中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。
 例如
__asm__ ("mov R0, #0x34" : : : "R0");
寄存器R0出现在”Instruction List中”,并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定”R0”,以让GCC知道这一点。
 因为你在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作表达式使用”r”约束,让GCC为你选择一个寄存器时,GCC对这些寄存器是非常清楚的——它知道这些寄存器是被修改的,你根本不需要在Clobber/Modify域再声明它们。但除此之外, GCC对剩下的寄存器中哪些会被当前的内联汇编修改一无所知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在Clobber/Modify 中声明它们,让GCC针对这些寄存器做相应的处理(因为这些寄存器在此内联汇编中只是临时使用,声明他们,gcc在执行内联汇编之前就会会保存这些寄存器的值,当内联汇编执行完了之后再恢复这些寄存器的值)。否则有可能会造成寄存器的不一致,从而造成程序执行错误。

如果一个内联汇编语句的Clobber/Modify域存在”memory”,声明内存有变化,那么GCC会保证在此内联汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。
 这只是使用”memory”时,GCC会保证做到的一点,但这并不是全部。因为使用”memory”是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。

猜你喜欢

转载自www.linuxidc.com/Linux/2016-12/137888.htm