基本内联汇编是最简单的内联形式,其格式为:
asm [volatile] (“assembly code”)
各关键字之间可以用空格或制表符分隔也可以紧凑挨在一起不分隔,各部分意义如下:
关键字asm用于声明内联汇编表达式,这是内联汇编固定的部分,不可少。
asm和__asm__是一样的,是由gcc定义的宏:#define __asm__ asm。
因为gcc有个优化选项-O,可以指定优化级别。当用-O来编译时,gcc按照自己的意图优化代码,说不定就会把自己所写的代码修改了。关键字volatile是可选项,它告诉gcc:“不要修改我写的汇编代码,请原样保留”。volatile和__volatile__是一样的,是由gcc定义的宏:#define __volatile__ volatile。
“assembly code”是咱们所写的汇编代码,它必须位于圆括号中,而且必须用双引号引起来。这是格式要求,只要满足了这个格式asm [volatile] (“”),assembly code甚至可以为空。
下面说下assembly code的规则:
- 1.指令必须用双引号引起来,无论双引号中是一条指令或多条指令。
- 2.一对双引号不能跨行,如果跨行需要在结尾用反斜杠’\’转义。
- 3.指令之间用分号’;’或换行符’\n’或换行符加制表符’\n’’\t’分隔。
提醒一下,即使是指令分布在多个双引号中,gcc最终也要把它们合并到一起来处理,合并之后,指令间必须要有分隔符。所以,当指令在多个双引号中时,除最后一个双引号外,其余双引号中的代码最后一定要有分隔符,这和其它编程语言中表示代码结束的分隔符是一样的,如:
asm(“movl $9,%eax;””pushl %eax”)正确
asm(“movl $9,%eax””pushl %eax”)错误
大家注意,在内联汇编中,咱们要注意操作数的顺序啦,现在是和intel反着的。
给大家举个例子,见文件inlineASM.c
1 char* str="hello,world\n";2 int count = 0;3 void main(){4 asm("pusha; \5 movl $4,%eax; \6 movl $1,%ebx; \7 movl str,%ecx; \8 movl $12,%edx; \9 int $0x80; \ 10 mov %eax,count; \ 11 popa \ 12 "); 13 }
代码inlineASM.c是演示用汇编代码直接调用“系统调用”write来打印字符患,该系统调用执行后会返回打印的字符数。
第1~2行定义了两个全局变量,待打印的字符串是str,count是用来存储返回值
第4~12行是内联汇编,这是咱们之前说过的c语言中跨过运行库直接调用系统调用的实例。这完全是AT&T风格的汇编语句:寄存器前面加前缀%,立即数前面加前缀$,操作数由左到右的顺序。似乎看上去很简单。
第4行将8个通用寄存器压栈,AT&T中的汇编指令是pusha(intel中的是pushad)。
第5行是传入第4号系统调用,这就是write的调用号。
第6~8行是为write系统调用传入参数,前面说系统调用的时候有讲过参数传递所用到的寄存器,不再赘述。
第9行用int 0x80执行系统调用,在AT&T中立即数的地位比较低,要加$前缀才表示数字为立即数(常数)。
第10行是获取write的返回值,返回值都是存储在eax寄存器中,所以将其复制到变量count中。
好啦,编译运行看结果,如图
大家注意到没有,inlineASM.c中的变量count和str是定义为全局变量。对的,在基本内联汇编中,若要引用c变量,只能将它定义为全局变量。如果定义为局部变量,链接时会找不到这两个符号,这就是基本内联汇编的局限性,简单的东西往往功能不够强大,所以咱们还得学下扩展内联汇编形式,下一节走起。