语法格式
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语言嵌入汇编相关的知识在写这篇文章之前还没有接触过,这是第一次了解接触。这篇博文的内容大部分是参考内容拼接而成的。