GCC内联汇编

基本内联汇编

基本内联汇编的语法很简单,实现的功能也很简单,就是直接将汇编语句插入到编译后的C代码中。基本形式如下。

asm("assembly code");

例如。

asm("nop"); asm("cli");

可以通过加入换行\n\t写多行内联汇编。

asm( "pushl %eax\n\t"
     "movl $0,%eax\n\t"
     "popl %eax"
);

但基本内联汇编有很大的问题,那就是编译器其实是不知道你写的内联汇编代码语义是什么,如果你在基本内联汇编里面对寄存器,或者内存做了什么修改它是不知道的,还是照常编译C代码,这就会出现冲突。

并且还有一个缺点就是没法和C代码中的变量进行交互,只能自己玩自己的,这样你就是想改变C代码中的一个变量也做不到。

扩展内联汇编

扩展内联汇编使用更复杂的语法,来指示编译器自己将对哪些寄存器或者内存做修改,并且还可以使用C代码中的变量。其基本语法如下。

asm ( assembler template 
      : output operands                  /* optional */
      : input operands                   /* optional */
      : clobbered list                   /* optional */
);
  • assembler template汇编模板:就是要执行的汇编代码,并且其中可以使用占位符来指代后面的输出和输入变量,占位符从%0开始,依次是%1%2等等。
  • output/input operands输出和输入操作数,则可以将C代码中的变量和寄存器或者内存绑定起来,让编译器知道汇编模板中的代码会影响哪些寄存器和内存。
  • clobbered list改动列表,可以用来告诉编译器在输出和输入操作数中没用提及的,但是发生了改动的寄存器和内存。

例子如下。

int a=10, b;
asm ("movl %1, %%eax; 
     movl %%eax, %0;"
     :"=r"(b)        /* output */
     :"r"(a)         /* input */
     :"%eax"         /* clobbered register */
); 

这里输入输出操作数和r绑定起来,这里r的意思就是让编译器自己决定用哪个寄存器来缓存,对输出操作数还加了=表示赋值的意思。而在汇编模板中的%0%1占位符,用来指代输出输入操作数,从左到右从0开始编号。

最后注意到clobbered list还贴心地写上了%eax,那是因为在输出输入操作数中没有提及该寄存器,但实际的汇编代码中又会改动这个寄存器,所以需要手动告诉编译器自己的代码也会影响到%eax寄存器。

常用限制符

限制符就是在输出输入操作数中约束C变量与寄存器或者内存的关系的符号。

字母 含义
r 任何通用寄存器
q 寄存器eax, ebx, ecx或edx
a, b, c, d 对应的寄存器eax,ebx,ecx,edx
S 寄存器esi
D 寄存器edi
A 把eax和edx合成一个64 位的寄存器
m 内存变量
o 内存变量,但是其寻址方式是偏移量类型
V 内存变量,但寻址方式不是偏移量类型
I 0-31之间的立即数(用于32位移位指令)
J 0-63之间的立即数(用于64位移位指令)
N 0-255之间的立即数(用于out指令)
i 立即数
n 立即数
f 浮点寄存器
t 第一个浮点寄存器
u 第二个浮点寄存器
G 标准的80387浮点常数

常用clobber list

关键字 含义
memory 指令以一种不可预知的方式修改了内存,用memory告知编译器
cc 标志寄存器发生了改动

参考资料

posted @ 2022-12-11 18:38  HachikoT  阅读(113)  评论(0编辑  收藏  举报