语法格式

C语言内嵌汇编语法如下:

__asm__("汇编语句"
       	: 输出部分
       	: 输入部分
       	: 破坏描述部分)

这个模板中共四部分,其中汇编语句是必须,其他三部分是可选的

  • 汇编语句,这是必须的一部分,多条汇编语句之间用 ;\n\n\t 分隔开,指令中的操作数可以使用占位符引用后面三部分的变量,操作数占位符最多10个,名称如下:%0,%1,…,%9。

  • 输出部分, 也叫输出寄存器, 即将该汇编指令执行完成后的某个寄存器值输出到C变量中,多个对应的关系用 逗号, 隔开。在描述寄存的字符前必须用包含 = 表示是一个输出操作数

    // __volatile__ 表示不优化
    __asm__ __volatile__("pushfl; popl %0; cli": "=g"(x))
    
  • 输入部分 也叫输入寄存器 ,在执行汇编前,将C变量的值赋给某个寄存器

  • 破坏描述部分,也叫会被修改的寄存器 表示你已对其中列出的寄存器中的值进行了改动,gcc编译器不能依赖于它原先对这些寄存器加载的值。

举例说明

上面的一些定义还是比较抽象的,我们以下面一段示例代码解释各个部分

#define get_seg_byte(seg,addr) ({ \   	// 1
register char __res; \				 	// 2				
__asm__("push %%fs;\					// 3
		 mov %%ax,%%fs; \				// 4
		 movb %%fs:%2,%%al; \			// 5
		 pop %%fs" \					// 6
		 :"=a" (__res) \				// 7
		 :"0" (seg),"m" (*(addr))); \	// 8
__res;})						// 9

这段代码是一个宏,\ 字符是续行符,因为宏是不允许换行的,但是全部写在一行又不便阅读,所以使用了续行符

#define get_seg_byte(seg,addr)  ({...})

代码行2,register char __res; 这是一个寄存器变量__res 定义。

接着是汇编语言,这段嵌入汇编只有三部分,没有最后的破坏描述部分,如果是后面的部分没有,那么 : 可以不写,如果是前面输入,输出部分没有,但是又第四部分那么 前面的: 需要写上,内容留空。

__asm__ ("cli":::"memory")  // 只有汇编语句和破坏描述符

汇编语句部分 (3-6)

这里使用的是 AT&T 汇编格式,寄存器是用 %寄存器 表示, 为了使gcc编译器生成这种格式就必须在 % 再加上一个 % ,可以理解位转义字符。这一部分就是我们使用的汇编语言的语法,多条汇编语句用 分号; 分隔。其中代码5 movb %%fs:%2,%%al; 中的 %2 是一个占位描述符,关于占位描述符在后面的部分祥解。

输出部分(7)

输出部分是由多个操作描述符构成,描述符之间用逗号隔开,每个描述符是由一个限定字符串和C语言变量组成。输出部分表示汇编代码处理完后要输出结果的C表达式。如果有多个输出或输入表达式,需要用逗号(“,”)这里是只有一个操作描述符,操作描述符格式, 作为输出操作数,只需要在限定符前加上=就可以了.

"[modifier]constraint"(C expression)
"=a" (__res) 

constraint 限定符

限定符 描述 限定符 描述
a 使用寄存器eax m 使用内存地址
b 使用寄存器ebx o 使用内存地址并可以加偏移
c 使用寄存器ecx I 使用常数 0-31
d 使用寄存器edx J 使用常数0-63
S 使用 esi K 使用常数0-255
D 使用edi L 使用常数0-65535
q 使用动态分配字节可寻址寄存器
(eax、ebx、ecx 或edx)
M 使用常数0-3
r 使用任意动态分配的寄存器 N 使用1字节常数(0-255)
g 使用通用有效的地址即可
(eax、ebx、ecx、edx、或内存变量)
O 使用常数0-31
A 使用eax与edx联合(64位)

modifier 修改符

修改符 描述
= 输出操作数。输出值将替换前值
+ 可读且可写操作数,使用时必须要列在输出操作数中
& 寄存器只能用于输出

输入部分(8)

输入部分列表和输出部分列表类似。不同的地方是不用修改符,这样默认这个操作数只是只读的。

占位符

每个操作描述按顺序编号从 %0,%1,… %9, (老版本gcc只支持10个,最新的可能支持更多)

上面示例对应的编号
%0-----"=a" (__res)
%1-----"0" (seg)
%2-----"m" (*(addr))) 	

所以在代码 movb %%fs:%2,%%al;%2 就是 "m" (*(addr))) 即addr表示的地址,除了对每个描述符编号还有对限定字符串编号, "0" (seg) 中的 0 表示的就是 =a

关于C语言嵌入汇编相关的知识在写这篇文章之前还没有接触过,这是第一次了解接触。这篇博文的内容大部分是参考内容拼接而成的。

参考