Linux C中内联汇编的语法格式及使用方法
在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入汇编在CS术语上叫做inline assembly。本文的笔记试图说明Inline Assembly的基本语法规则和用法(建议英文阅读能力较强的同学直接阅读本文参考资料中推荐的技术文章 ^_^)。
注意:由于gcc采用AT&T风格的汇编语法(与Intel Syntax相对应,二者的区别参见这里),因此,本文涉及到的汇编代码均以AT&T Syntax为准。
1. 基本语法规则
内联汇编(或称嵌入汇编)的基本语法模板比较简单,如下所示(为使结构更清晰,这里特意做了换行,其实完全可以全部写到单行中):
asm [ volatile ] (
assembler template
[ : output operands ] /* optional */
[ : input operands ] /* optional */
[ : list of clobbered registers ] /* optional */
);
备注:本文遵从linux系统的统一风格,以[ ]来表示其对应的内容为可选项。
由代码模板可以看到,基本语法规则由5部分组成,下面分别进行说明。
1)关键字asm和volatile
asm为gcc关键字,表示接下来要嵌入汇编代码。为避免keyword asm与程序中其它部分产生命名冲突,gcc还支持__asm__关键字,与asm的作用等价。
volatile为可选关键字,表示不需要gcc对下面的汇编代码做任何优化。同样出于避免命名冲突的原因,__volatile__也是gcc支持的与volatile等效的关键字。
BTW: C语言中也经常用到volatile关键字来修饰变量(不熟悉的同学,请参考这里)
2)assembler template
这部分即我们要嵌入的汇编命令,由于我们是在C语言中内联汇编代码,故需用双引号""将命令括起来,以便gcc以字符串形式将这些命令传给汇编器AS。例如可以写成这样:"movl %eax, %ebx"
有时候,汇编命令可能有多个,则通常分多行写,每行的命令都用双引号括起来,命令后紧跟"\n\t"之类的分隔符(当然,也可以只用1对双引号将多行命令括起来,从语法来说,两种写法均有效,我们可自行决定用哪种格式来写)。示例代码如下所示:
__asm__ __volatile__ ( "movl %eax, %ebx\n\t"
"movl %ecx, 2(%edx, %ebx, $8)\n\t"
"movb %ah, (%ebx)"
);
还有时候,根据程序上下文,嵌入的汇编代码中可能会出现一些类似于魔数(Magic Number )的操作数,比如下面的代码:
int a=10, b;
asm ("movl %1, %%eax; /* NOTICE: 下面会说明此处用%%eax引用寄存器eax的原因
movl %%eax, %0;"
:"=r"(b) /* output 该字段的语法后面会详细说明,此处可无视,下同 */
:"r"(a) /* input */
:"%eax" /* clobbered register */
);
我们看到,movl指令的操作数(operand)中,出现了%1、%0,这往往让新手摸不着头脑。其实只要知道下面的规则就不会产生疑惑了:
在内联汇编中,操作数通常用数字来引用,具体的编号规则为:若命令共涉及n个操作数,则第1个输出操作数(the first output operand)被编号为0,第2个output operand编号为1,依次类推,最后1个输入操作数(the last input operand)则被编号为n-1。
具体到上面的示例代码中,根据上下文,涉及到2个操作数变量a、b,这段汇编代码的作用是将a的值赋给b,可见,a是input operand,而b是output operand,那么根据操作数的引用规则,不难推出,a应该用%1来引用,b应该用%0来引用。
还需要说明的是:当命令中同时出现寄存器和以%num来引用的操作数时,会以%%reg来引用寄存器(如上例中的%%eax),以便帮助gcc来区分寄存器和由C语言提供的操作数。
3)output operands
该字段为可选项,用以指明输出操作数,典型的格式为:
: "=a" (out_var)
其中,"=a"指定output operand的应遵守的约束(constraint),out_var为存放指令结果的变量,通常是个C语言变量。本例中,“=”是output operand字段特有的约束,表示该操作数是只写的(write-only);“a”表示先将命令执行结果输出至%eax,然后再由寄存器%eax更新位于内存中的out_var。关于常用的约束规则,本文后面会给出说明。
若输出有多个,则典型格式示例如下:
asm ( "cpuid"
: "=a" (out_var1), "=b" (out_var2), "=c" (out_var3)
: "a" (op)
);
可见,我们可以为每个output operand指定其约束。
4)input operands
该字段为可选项,用以指明输入操作数,其典型格式为:
: "constraints" (in_var)
其中,constraints可以是gcc支持的各种约束方式,in_var通常为C语言提供的输入变量。
与output operands类似,当有多个input时,典型格式为:
: "constraints1" (in_var1), "constraints2" (in_var2), "constraints3" (in_var3), ...
当然,input operands + output operands的总数通常是有限制的,考虑到每种指令集体系结构对其涉及到的指令支持的最多操作数通常也有限制,此处的操作数限制也不难理解。此处具体的上限为max(10, max_in_instruction),其中max_in_instruction为ISA中拥有最多操作数的那条指令包含的操作数数目。
需要明确的是,在指明input operands的情况下,即使指令不会产生output operands,其:也需要给出。例如asm ("sidt %0\n" : :"m"(loc)); 该指令即使没有具体的output operands也要将:写全,因为有后面跟着: input operands字段。
5)list of clobbered registers
该字段为可选项,用于列出指令中涉及到的且没出现在output operands字段及input operands字段的那些寄存器。若寄存器被列入clobber-list,则等于是告诉gcc,这些寄存器可能会被内联汇编命令改写。因此,执行内联汇编的过程中,这些寄存器就不会被gcc分配给其它进程或命令使用。
Linux C中内联汇编的语法格式及使用方法相关推荐
- C语言中内嵌汇编asm语法
这篇文章写得炒鸡详细而且很全面,易于理解,建议新手查看 C语言中内嵌汇编asm语法 下面这两篇文章作为补充来看 C语言内嵌汇编:asm volatile C语言ASM汇编内嵌语法
- linux arm gcc 内联汇编参考手册
关于本文档 GNU C 编译器为 ARM 精简指令系统处理器提供了在 C 代码中内嵌汇编的功能.这种非常酷的特性提供了一些 C 代码没有的功能,比如手工优化软件关键代码.使用相关的处理器指令. 本文假 ...
- 一步步编写操作系统 79 在c代码中内联汇编
基本内联汇编是最简单的内联形式,其格式为: asm [volatile] ("assembly code") 各关键字之间可以用空格或制表符分隔也可以紧凑挨在一起不分隔,各部分意义 ...
- gcc 内联汇编用法介绍
前言 大部分内容翻译提取自某国外HOW-TO文档,原地址: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html AT& ...
- 在 VC++ 中使用 内联汇编
From:https://blog.csdn.net/root19881111/article/details/8450266 VC++内联汇编(MSDN相关内容完整翻译):http://www.cp ...
- GCC Inline ASM GCC内联汇编
GCC 支持在C/C++代码中嵌入汇编代码,这些汇编代码被称作GCC Inline ASM--GCC内联汇编.这是一个非常有用的功能,有利于我们将一些C/C++语法无法表达的指令直接潜入C/C++代码 ...
- C语言内联汇编使用方法
GCC内联汇编 一.基本语法 asm volatile ( assembler template: output operands /* optional */: input operands /* ...
- 内联汇编 - 从头开始
FROM: http://www.ibm.com/developerworks/cn/aix/library/au-inline_assembly/ 对于 C/C++ 程序员来说,内联汇编并不是一个新 ...
- mupdf不支持x64_x86平台转x64平台关于内联汇编不再支持的解决
工具:VS2005 编译器:cl.exe(X86 C/C++) ml64.exe(X64 ASM64) 前提:X86下内联汇编是嵌在函数当中实现的 在X86平台下,可以轻松的在C/C++代码中嵌入 ...
最新文章
- css知多少(1)——我来问你来答(转)
- JDK、JRE、JVM三者间的关系
- C#使用Tesseract OCR 解析验证码
- 679. 24 Game 24 点游戏
- Oracle中压缩数据节省空间和提高速度
- vue项目使用eslint
- c语言如何用循环语句一个字一个字的输出,怎样用c语言的for嵌套循环,用·画出泳字,求解,主要是怎样用循环语句打出,在某一行中既有空格又有·...
- python解包的概念_Python学习第176课——tar解包和压缩
- mysql c2_Mysql具有C2级安全性
- 第三届“空间信息网络”学术论坛诚邀您的参加
- Linux 重启oracle数据库
- CuteFtp通用注册码
- 什么是LSI关键词?LSI关键词怎么用?2019
- html - 空格符号 - 字符实体 - 预留字
- 举头皮皮虾机器人_一种仿生水下皮皮虾机器人通信系统的制作方法
- 艾美捷-临床数量排名前20的药物靶点分析-磷酸二酯酶(PDEs)抗体
- python之禅中文原文_Python之禅及释义
- python外国人也用吗_再也不怕和老外交流了!我用python实现一个微信聊天翻译助手!...
- 野人与传教士——宽度优先搜索(完整报告,含全部代码)
- 【视频异常检测-论文阅读】Anomaly Detection in Video via Self-Supervised and Multi-Task Learning