内联汇编
文章目录
本文总结内联汇编的基本用法和应用举例。
1 内联汇编语法格式
|
|
1.1 内联汇编参数说明
|
|
代码说明:该代码为GCC内联汇编,汇编格式为AT&T。
- 内联汇编代码说明
- 引号包含的内容为汇编语言。此处第7,8行为汇编代码。其中%0, %1, %2为占位符,占位符将在后续说明。
- 内联汇编中,若有多行代码,一般使用
\n\t分隔(也可以写在同一行,使用分好分隔); - 此处%0表示的是第一个输出操作数。%1为第一个输入操作数,%2为第二个输入操作数。
- 输出操作数列表
- 汇编代码结束后,紧挨着的一个冒号(:)后就是输出操作数列表;
- 输出操作数可以为空,但是即使输出操作数列表为空,冒号却不能省略;
- 若有多个输出操作数,多个输出操作数之间使用逗号(,)分隔。
- 输入操作数列表
- 输出操作数列表后紧挨着的一个冒号(:)后就是输入操作数列表;
- 输入操作数可以为空,但是即使输出操作数列表为空,冒号却不能省略;
- 若有多个输入操作数,多个操作数之间使用逗号(,)分隔。
- 被破坏的寄存器列表
- 若存在被破坏的寄存器列表,放在输入操作列表后。
- 若被破坏的寄存器列表为空,则冒号可以省略;
1.2 内联汇编中符号的功能
1.2.1 占位符
在内联汇编中,%0、%1、%2 等是占位符(placeholders),代表操作数列表中各个操作数的引用。它们按照操作数在列表中的顺序进行编号。
编号规则:
- 从0开始编号
- 先从输出操作数开始,依次编号。
- 编号顺序为:第一个输出操作数为%0,第二个输出操作数为%1,然后依次编号输入操作数。输出操作数编号完毕后,紧接着编号输入操作数。
1.2.2 修饰符
修饰符用于控制输入操作数列表,输出操作数列表和被破坏寄存器列表,可以多个修饰符共同使用。
| 修饰符 | 功能说明 | 示例 |
|---|---|---|
| = | 表示当前变量为只写变量,一般用用于修饰输出操作数 | “=r”(res) |
| + | 表示当前变量为可读写变量 | “+r”(a) |
| & | 表示修饰的变量不能与其他变量公用寄存器(或内存) | “=&r”(result1) |
| r | 表示当前操作数使用通用寄存器存放 | “r”(sum) |
| m | 表示当前操作数使用内存存放 | “m”(res) |
| 0-9 | 使用指定操作数保存变量 | “0”(var) |
2 内联汇编实例
2.1 x86内联汇编实例
|
|
代码含义说明:
- %0表示输出参数t,%1表示输出参数*p,%2表示输入参数t,%3表示输入参数s。
-
%0和%2都关联到同一个 C 变量t,但含义不同:%2是输入,提供期望值;%0是输出,将操作后的实际值写回t。"=a"(t)表示将通用寄存器EAX的值写入到输出参数t中。“a”(t)表示将输入参数t的保存到通用寄存器EAX中。 -
%1被声明为=m(输出),同时又被隐含地作为 CMPXCHG 的目的操作数,会被修改。虽然未在输入列表中显式出现,但"memory"破坏描述符同样确保了编译器不会在此前后缓存该内存的值。 -
*(void *volatile *)p这个写法:先将p强制转换为void *volatile *类型(即指向volatile void*的指针),再解引用,从而操作一个指针大小的内存单元,并告知编译器该内存是易变的。 -
函数功能说明:原子的比较
*p(即地址p处存放的指针值)是否等于t,如果相等则将其替换为s;*无论成功与否,最终都返回 p 在操作前的实际值。 -
返回值的行为是标准 CAS 的一种形式:函数返回旧值,调用方通过判断
返回值 == t来得知是否交换成功。