C语言内联汇编使用方法
GCC内联汇编
一、基本语法
asm volatile ( assembler template: output operands /* optional */: input operands /* optional */: list of clobbered registers /* optional */);
(1) 关键字asm和volatile
asm为GCC关键字,表示接下来要嵌入汇编代码,如果asm与其他程序中的命名冲突,可以使用__asm__
。
volatile为可选关键字,表示不需要对汇编代码做任何优化,类似的,也支持__volatile__
(2) 汇编指令
C语言中内联汇编代码必须用双引号将命令括起来,如果是多行汇编指令,则每条指令占用一行,每行指令用双引号括起来,以后缀\t\n结尾,\n表示newline的缩写,\t表示tab的缩写。
如示例代码:
__asm__ __volatile__ ("movl %eax, %ebx \n\t""movl $100, %esi \n\t""movl %ecx, $label(%edx, %ebx, $4) \n\t""movb %ah, (%ebx) \n\t");
当使用拓展模式(包含output,input,clobbered部分),汇编指令中需要使用两个%来引用寄存器,比如%%rax;使用一个%来引用输入、输出参数,比如%1。
(3) 输出操作数
内联汇编由零个或者多个输出操作数,用来指示内联汇编指令修改了C代码中的变量,则需要对每个输出参数进行分隔,格式为:
[[asmSymbollicName]] constraint (cvariablename)
输出操作数指定名字asmSymbollicName,汇编指令可以使用这个名字引用输出操作数。
除了使用名字引用操作数外,还可以使用序号引用操作数,比如操作数有两个,%0引用第一个操作数,%1引用第二个操作数,依次类推。
输出操作数必须用”=“或者”+“作为前缀,=表示只写,+表示读写。前缀之后就可以是各种约束,比如”=a"表示将结果输出至rax/eax寄存器,然后再由rax/eax寄存器更新相应的输出。
cvariablename为代码中C变量的名字,需要使用括号括起来。
(4) 输入操作数
内联函数可以由零个或者多个输入操作数,操作数来自C代码中的变量或者是表达式,作为汇编指令的输入,每个输入操作数的格式如下:
[[asmSymbollicName]] constraint (cvariablename)
参数可参照输出操作数,但是序号操作数为最后一个输出操作数加1开始,依次类推。
(5) clobber list
某些汇编指令执行之后有一些副作用,可能会隐性地影响某些寄存器或者是内存的值,如果被影响的寄存器或者是内存并没有在输入、输出操作数中列出来,那么需要将这些寄存器或者内存列入clobber list中。
执行一个加法运算将val + 400的计算结果放入sum,sum = val + 400:
int val = 100, sum = 0;asm ("movl %1, %%rax; \n\t""movl %c[addend], %%rbx; \n\t""addl %%rbx, %%rax; \n\t""movl %%rax, %0": "=" (sum): (c)(val), [addend] " i " (400): "rbx");
第三行:因为存在寄存器引用和通过需要引用的操作数,所有使用两个”%“来引用寄存器,%1引用的是第一个输入操作数val,其中c表示使用rcx的寄存器保存val,然后将rcx的值赋值给rax。
第四行:引用addend是第二个输入操作数符号的名字,因为这是一个立即数,所以这个变量前面使用了c修饰符,这个是GCC语法,表示后面是一个立即数。
第五行:将rbx 和 rax寄存器的值相加,结果存放在rax中。
第六行:%0引用的是第一个输出操作数sum,将rax的值赋值给sum,因为sum只是输出操作数,所以只用"="修饰。
这段代码可以看到,在汇编代码中使用了rbx寄存器,但是rbx并没有出现在输入和输出操作数中,所以内联汇编需要把rbx寄存器放入clobber list中。
二、使用实例
(1) 简单求和
#include <stdio.h>
#include <stdlib.h>int main(void)
{/* basic command demo */__asm__("movl %eax, %ecx");/* set b = 10 */int a = 10, b = 0;__asm__("movl %1, %%eax;""movl %%eax, %0;":"=r" (b) /* output */:"r" (a) /* input */:"%eax" /* clobbered register */);printf("%s: b = %d\n", __func__, b);return 0;
}
以上就是基本Linux c语言内联汇编的基本语法,可以参考下,下面对b赋值的汇编代码进行解释:
- “b”是输出操作数,由 %0 引用,“a”是输入操作数,由 %1 引用。
- “r”是对操作数的约束。"r”告诉 GCC 使用任何寄存器来存储操作数。输出操作数约束应该有一个约束修饰符“=”。这个修饰符表示它是输出操作数并且是只写的。
- 寄存器名称前有两个 % 前缀。这有助于 GCC 区分操作数和寄存器。操作数有一个 % 作为前缀。
- 第三个冒号后被破坏的寄存器 %eax 告诉 GCC %eax 的值将在“asm”中修改,因此 GCC 不会使用这个寄存器来存储任何其他值。
当“asm”的执行完成时,“b”将反映更新后的值,因为它被指定为输出操作数。换句话说,在“asm”内部对“b”所做的更改应该反映在“asm”之外。
(2) 内核中idt desc_ptr获取实例
/** Override for the debug_idt. Same as the default, but with interrupt* stack set to DEFAULT_STACK (0). Required for NMI trap handling.*/
struct desc_ptr {unsigned short size;unsigned long address;
} __attribute__((packed)) ;const struct desc_ptr debug_idt_descr = { .size = IDT_ENTRIES * 16 - 1,.address = (unsigned long) debug_idt_table,
};static inline void store_idt(struct desc_ptr *dtr)
{asm volatile("sidt %0":"=m" (*dtr));
}
#include <stdio.h>
#include <stdlib.h>struct desc_ptr {unsigned short size;unsigned long address;
} __attribute__((packed)) ;int main(void)
{/* ring3 get idt table address */struct desc_ptr idt_ptr = {0};__asm__("sidt %0":"=m" (idt_ptr));printf("%s: the desc_ptr address is 0x%lx\n", __func__, idt_ptr.address);return 0;
}
这里也就是通过汇编指令sidt获取struct desc_ptr的地址,然后再访问idt表的地址。
(3) SGDT/SIDT-存储全局/中断描述附表寄存器
操作码 | 指令 | 说明
操作码 | 指令 | 说明 |
---|---|---|
OF 01 /0 | SGDT m | 将GDTR存储到m |
OF 01 /1 | SITD m | 将IDTR存储到m |
将全局描述符表格寄存器 (GDTR) 或中断描述符表格寄存器 (IDTR) 中的内容存储到目标操作数。目标操作数是指定 6 字节内存位置。如果操作数大小属性为 32 位,则寄存器的 16 位限制字段存储到内存位置的 2 个低位字节,32 位基址存储到 4 个高位字节。如果操作数大小属性为 16 位,则限制字段存储在 2 个低位字节,24 位基址存储在第三、四及五字节,第六字节使用 0 填充。
SGDT 与 SIDT 指令仅在操作系统软件中有用;不过它们也可以在应用程序中使用,而不会导致生成异常。
(4) 内联汇编实现getpid系统调用
getpid系统调用的作用是获取当前进程的进程号。系统调用采用eax传递系统调用号。getpid的系统调用号是20,也就是要将调用号20存入寄存器eax。然后执行系统调用是通过执行int $0x80。
#include <stdio.h>
#include <stdlib.h>int main(void)
{unsigned int pid = getpid();printf("%s: pid is %d\n", __func__, pid);return 0;
}
#include <stdio.h>
#include <time.h>
int main()
{unsigned int pid;asm volatile("mov $0,%%ebx\n\t""mov $20,%%eax\n\t""int $0x80\n\t""mov %%eax,%0\n\t":"=m" (pid));printf("pid is %u\nget pid by asm\n",pid);return 0;
}
(5) x86_64内核cr0寄存器读写操作
static inline unsigned long read_cr0(void)
{ unsigned long cr0;asm volatile("movq %%cr0,%0" : "=r" (cr0));return cr0;
} static inline void write_cr0(unsigned long val)
{ asm volatile("movq %0,%%cr0" :: "r" (val));
}
C语言内联汇编使用方法相关推荐
- C 语言内联汇编介绍
文章目录 为什么要用内联汇编 内联汇编的基本要素 语法 汇编语句模板 操作数 输出部分和输入部分 操作数约束 常用约束 寄存器操作数约束 内存操作数约束 (m) 匹配(数字)约束 为什么要用内联汇编 ...
- Linux C中内联汇编的语法格式及使用方法
在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入汇编在CS术语上叫做inline assembly.本文的笔记试图说明Inline Assembly的基 ...
- C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质...
C语言中递归什么时候能够省略return引发的思考:通过内联汇编解读C语言函数return的本质 事情的经过是这种,博主在用C写一个简单的业务时使用递归,因为粗心而忘了写return.结果发现返回的结 ...
- C语言-ATT拓展内联汇编(ATT/GCC)
你能看到这篇文章说明你肯定是知道AT&T语法是个什么东西,但是要注意,下文所示示例都是拓展内联汇编,而不是基本内联汇编,代码示例在Ubuntu环境下演示 ATT完整语法如下,asm volat ...
- 【ATT 与 Intel】汇编与C语言相互调用及内联汇编
目录 一.ATT 与 Intel 二.函数调用的约定 三.C语言调用汇编程序 四.汇编程序调用C语言 五.内联汇编 5.1.基本asm格式 5.2.扩展asm格式 5.3.使用占位符来替代寄存器名称 ...
- C语言进阶——内联汇编
内联函数 在 C 语言中,我们可以指定编译器将一个函数代码直接复制到调用其代码的地方执行.这种函数调用方式和默认压栈调用方式不同,我们称这种函数为内联函数.有点像宏. 优点:内联函数降低了函数的调用开 ...
- 最牛X的GCC 内联汇编
导读 正如大家知道的,在C语言中插入汇编语言,其是Linux中使用的基本汇编程序语法.本文将讲解 GCC 提供的内联汇编特性的用途和用法.对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 ...
- linux arm gcc 内联汇编参考手册
关于本文档 GNU C 编译器为 ARM 精简指令系统处理器提供了在 C 代码中内嵌汇编的功能.这种非常酷的特性提供了一些 C 代码没有的功能,比如手工优化软件关键代码.使用相关的处理器指令. 本文假 ...
- 一步步编写操作系统 77 内联汇编与ATT语法简介
内联汇编 之前和大家介绍过了一种汇编方法,就是C代码和汇编代码分别编译,最后通过链接的方式结合在一起形成可执行文件. 另一种方式就是在C代码中直接嵌入汇编语言,强大的GCC无所不能,咱们本节要学习的就 ...
最新文章
- [学习笔记]CDQ分治
- dataframe按行按列选择方法
- 关于2020年第十五届全国大学生智能汽车竞赛山东赛区技术答辩的相关规则
- pyinstaller 打包成exe
- 金融行业信息系统信息安全等级保护实施指引_中国人民银行发布金融行业网络安全等级保护实施指引...
- javascript添加HTML事件处理程序的两种方式学习
- 一文读懂分布式架构知识体系(内含超全核心知识大图)
- java中的lastIndexOf( )函数是什么意思
- 互联网开源贡献是什么意思_为什么我们为开源软件做出贡献?
- AndroidStudio安卓原生开发_UI控件_TextView_Button_EditText---Android原生开发工作笔记97
- toast弹窗_弹窗实用素材模板|UI设计中的弹窗设计技巧,快get
- Educational Codeforces Round 61
- vSphere Client 连接ESXi 或者是vCenter 时虚拟机提示VMRC异常的解决办法
- 一个离线的简单的 JSON 格式化编辑器
- python 求解二次规划(quadprog)
- Bitdefender 发布GandCrab V5.2勒索病毒解密工具
- ubuntu独立显卡驱动
- 函数的连续性以及间断点
- Python selenium模块对网页进行截屏保存图片 easyocr模块识别提取图片文字
- 如何在Joomla中创建一个漂亮的单页网站