阅读Android或Linux的源码时,有时会遇到使用内嵌汇编的代码。阅读内嵌汇编代码不是一件特别容易的事,如果只了解普通汇编语言,没学习过内嵌汇编,从语言上大概能明白内嵌汇编代码的作用,但是要精确的了解每行代码,每个寄存器的含义就不太可能了。内嵌汇编其实并不复杂,只不过gcc的内嵌汇编必须是AT&T格式,而且有自己一套独特的标记,加上资料很少,有限的资料写的又很晦涩,所以学习起来比较困难。本文争取用简短的篇幅介绍明白内嵌汇编的规则和作用。如果对AT&T汇编格式还不了解,请先参考前一篇博文:AT&T汇编格式介绍。
一、简单内嵌汇编格式
内嵌汇编是用来和C语言混合编程的,所以要有方法把它的语句和C语句隔开,下面看一个简单的例子:
__asm__ __volatile__ ("movl $5, % eax");
内嵌汇编语句必须放在__asm__开头,并且加上括号和引号。__volatile__修饰符不是必须的,它的作用是告诉编译器不要调整我们写的汇编语句(通常编译器会因为优化的原因这么做)。
也可以把多条汇编语句放在一个__asm__中,例如:
__asm__ __volatile__ ("movl $5, % eax; movl $5, % ebx ");
汇编语句之间要使用分号‘;’隔开。除了用分号‘;’作为语句间的分割符,还可以用"\n"来作为分割符。本人也试过“\n\r”,或者"\n\t"也能编译通过。如果看到有些内嵌汇编语句使用它们作为分隔符,不要奇怪。
其实,最简单的内嵌汇编就这点内容,把汇编语句放到__asm__块中就完了。这种简单的内嵌汇编用处并不是很大(当然我们可以用它来调用一些特殊的CPU指令),主要是因为它不能和C代码交流,只是孤零零的汇编代码用处有限。
二、复杂的内嵌汇编格式
如何才能让汇编代码要和C语句交流呢?答案很简单,就是让汇编中能使用和改变C语言中的变量。因此内嵌汇编需要用一种方式来表示这种意图。首先,我们需要告诉编译器我们要在哪个地方使用变量,因此内嵌汇编用“%0”到“%9”这10个符号来代替汇编语句中的操作数,同时把c的表达式放在汇编语句后面的括号中,例如:
int kk = 0;
__asm__ __volatile__ ("movl %0, %% eax":"=m"(kk));
上面汇编语句中的“%0”又被称为占位符,0到9的数值表示后面变量的位置。例如下面的例子中有两个变量:
int kk = 0,mm;
__asm__ __volatile__ ("movl %0, %% eax; movl %% eax, %1":"=m"(kk),"=m"(mm));
上面的例子中%0代表变量kk,%1代表变量