C语言ASM汇编内嵌语法详解

2020-01-18 19:38:27于丽

我们现在将example1.c中内联汇编的注释去掉,重新编译,然后看一下相关的编译结果。


$ gcc -O -S example1.c

$ cat example1.s

main: 
pushl %ebp 
movl %esp, %ebp 
movl 8(%ebp), %eax # int* __p = (int*)__argc
movl $9999, (%eax) # (*__p) = 9999
#APP 

# __asm__("":::"memory")
#NO_APP
cmpl $9999, (%eax) # (*__p) == 9999 ?
jne .L3 # false 
movl $5, %eax # true, return 5 
jmp .L2 
.p2align 2 
.L3: 
movl (%eax), %eax 
.L2: 
popl %ebp 
ret

由于内联汇编语句__asm__("":::"memory")向GCC声明,在此内联汇编语句出现的位置内存内容可能了改变,所以GCC在编译时就不能像刚才那样处理。这次,GCC老老实实的将if语句生成了汇编代码。

可能有人会质疑:为什么要使用__asm__("":::"memory")向GCC声明内存发生了变化?明明“Instruction List”是空的,没有任何对内存的操作,这样做只会增加GCC生成汇编代码的数量。

确 实,那条内联汇编语句没有对内存作任何操作,事实上它确实什么都没有做。但影响内存内容的不仅仅是你当前正在运行的程序。比如,如果你现在正在操作的内存 是一块内存映射,映射的内容是外围I/O设备寄存器。那么操作这块内存的就不仅仅是当前的程序,I/O设备也会去操作这块内存。既然两者都会去操作同一块 内存,那么任何一方在任何时候都不能对这块内存的内容想当然。所以当你使用高级语言C/C++写这类程序的时候,你必须让编译器也能够明白这一点,毕竟高 级语言最终要被编译为汇编代码。

你可能已经注意到了,这次输出的汇编结果中,有两个符号:#APP和#NO_APP,GCC将内联汇编语 句中"Instruction List"所列出的指令放在#APP和#NO_APP之间,由于__asm__("":::"memory")中“Instruction List”为空,所以#APP和#NO_APP中间也没有任何内容。但我们以后的例子会更加清楚的表现这一点。

关于为什么内联汇编__asm__("":::"memory")是一条声明内存改变的语句,我们后面会详细讨论。

刚才我们花了大量的内容来讨论"Instruction List"为空是的情况,但在实际的编程中,"Instruction List"绝大多数情况下都不是空的。它可以有1条或任意多条汇编指令。

当 在"Instruction List"中有多条指令的时候,你可以在一对引号中列出全部指令,也可以将一条或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,你可以将 每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(n,大多数情况下n后还要跟一个t,其中n是为了换行,t是为了 空出一个tab宽度的空格)将它们分开。比如:


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

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

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