GNU C 内联汇编介绍
GNU C 内联汇编介绍
简介
1、很早之前就听说 C 语言能够直接内嵌汇编指令。但是之前始终没有去详细了解过。最近由于某种需求,看到了相关的 C 语言代码。也就自然去简单的学习了一下如何在 C 代码中内嵌汇编指令。
asm/__asm__ 关键字
1、总的来说在 C 代码中我们通过 asm/__asm__ 关键字来告诉编译器将指定的内容当汇编指令处理。废话不多说,先看个例子:
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 3, y = 4;
__asm__("addl %%ebx, %%eax"
: "=a" (y)
: "b" (x), "a" (y));
printf("x + y = %d\n", y);
return 0;
}
2、这个例子,求两数之和。将 x 的值加到 y 中,并输出 y 值。首先来看一下在 C 代码中插入汇编指令的框架代码:
__asm__("汇编指令1\n\t"
"汇编指令2\n\t"
"汇编指令3\n\t"
"汇编指令n"
: 输出变量列表
: 输入变量列表
: 被破坏的寄存器列表);
汇编指令
1、在 __asm__(); 的“”中,便是编写汇编指令的地方。利用 C 语言自动连接双引号的特性,我们可以像框架那样每一行只写一条指令,当然你也可以全部写在一行,那么需要用 ';' 将不同的指令分开。
2、\n 用于指令换行,\t使 GCC 编译的时候产生的汇编指令格式保持规范。
GCC 默认使用 AT&T 格式的汇编语法 它与 intel 的汇编语法之间稍有不同。简单说两点不同的地方:
- AT&T 汇编在操作寄存器时需要在前面加一个 '%' 符号,而 intel 的不用。由于在 C 代码中嵌入汇编时,写在字符串中,由于 '%' 在 C 语言中是特殊字符,所以为什么在第一个例子中寄存器前加了两个 '%'.
- AT&T 在操作立即数时,需要在立即数前面加 '$',而 intel 却是 '#'.
- AT&T 的源与目的与 intel 相反。例如: intel:
mov eax, #1AT&T:movl $1, %eax.
3、这里只是提到了本文中会见到的一部分差异,更多具体关于 AT&T 汇编的知识,这里就不再赘述。可参见相关描述 AT&T 汇编的书籍。
输出变量列表
1、输出变量列表是描述,在内嵌的汇编指令中将哪些值输出到 C 代码环境中的哪个变量中。比如第一个例子中我们指定在执行完了所写的汇编指令后将 eax 寄存器的值输出到变量 y 中。
其中 "=a" 指明使用 eax 寄存器为输出寄存器,输出到紧跟的变量 (y) 中。
- = 代表输出变量用作输出,原来的值会被新值替换。
- + 代表即可用作输入,也可用作输出。
2、输出变量列表可以写多个变量,每个之间使用逗号隔开。例如: : “=a” (x), "=b" (y), "=r" (z)。其中用到的 a, b 等代表相应的寄存器。如下是一部分对应关系。
| 代码 | 含义 |
|---|---|
| a | 使用寄存器 eax |
| b | 使用寄存器 ebx |
| c | 使用寄存器 ecx |
| d | 使用寄存器 edx |
| S | 使用 esi |
| D | 使用 edi |
| q | 使用动态分配字节可寻址寄存器 |
| r | 使用任意动态分配的寄存器 |
| A | 使用寄存器 eax 与 edx 联合 |
| m | 使用内存地址 |
| o | 使用内存地址并可以加偏移量 |
| I | 使用常数 0-31 |
| J | 使用常数 0-63 |
| K | 使用常数 0-255 |
| M | 使用常数 0-3 |
| N | 使用一字节常数 0-255 |
3、这里仅仅列出了一部分常用到的代码,更多详细请参考 GNU C 的 GCC 使用手册。
这里讲一下 "=r" 的用法,像 a, b 这些代码都是指定使用的寄存器。但是 r 是让编译器随机给一个,那么我怎么知道是那个呢?
不用担心,编译器为使用的随机寄存器遍了一个号。规则是:从输出列表开始,一直到输入列表结束,从左到右,从上到下一次为 %0, %1, %2....所以我们可以这样改写第一个代码例子:
#include <stdio.h>
int main(int argc, char *argv[])
{
int x = 3, y = 4;
__asm__("addl %1, %0"
: "=r" (y)
: "r" (x), "0" (y));
printf("x + y = %d\n", y);
return 0;
}
输入变量列表
1、和输出变量列表一样,使用的寄存器代码依然一样的含义。只是少了 '=' 而已。注意如果一个变量使用 'r' 代码时,既做输出,又做输入的话,在写输入变量对应的寄存器时,就写它在输出列表里对应的编号。如上一个例子中 y 既做输出又做输入,那么刚进入汇编指令时,%0的值便为 y 之前的值 4 ,指令结束后 %0 为 7 , 接着又把 %0 输出到了 y 。
破坏寄存器列表
1、这一行告诉 GCC 在内联的汇编代码中,哪些寄存器可能会被使用到(显式/隐式)。那么 GCC 就会在进入内联汇编之前将这些寄存器保存起来,最后再恢复。避免影响到其他的代码。
早期的 GCC 要求把输入、输出用到的寄存器写到破坏列表里面。但是现在的编译器能够自动保存、恢复在输出、输入列表里面用到的寄存器。因此上述的例子中由于没有影响到其他非输出、非输入的寄存器,所以可以省略破坏列表。
看个栗子:
#include <stdio.h>
char* strcpy(char *dst, const char *src)
{
__asm__("cld\n"
"1:\tlodsb\n\t"
"stosb\n\t"
"testb %%al, %%al\n\t"
"jne 1b"
:
:"S" (src), "D" (dst)
:"ax");
return dst;
}
int main(int argc, char *argv[])
{
char buf[512];
strcpy(buf,"Hello,AT&T!");
printf("%s\n", buf);
return 0;
}
// 代码中隐式的使用到了 ax 寄存器,因此我们特别的指明了 ax 为被破坏的寄存器。
GCC 的一些新特性
1、新的 GCC 允许我们为随机分配的寄存器命名,这样极大的方便我们编写内联汇编代码。看个例子:
#include <stdio.h>
int main ( int argc , char *argv[] )
{
int a = 1;
int b = 2;
__asm__("addl %[b], %[a]"
: [a] "=r"(a)
: [b] "r"(b), "[a]"(a));
printf ("a = %d\n" , a);
return 0;
}
2、其实一看代码,你就明白,只需要在指明 "=r" , "r" 的前面加上 [name] 之后,便可以在汇编指令里面直接通过 %[name] 的方式使用相应分配的寄存器了。
我在阅读 GCC 的使用手册时,发现了这个特性十分方便,因此在这里特别提出。当然还有很多新特性,感兴趣的读者可以自行阅读 GNU GCC 的开发者手册,并寻找有用的特性。记得回来分享哦。
好了,这次就到这里吧!
// 本文属于博主原创,欢迎使用任何形式的转载。
// 但是必须注明出处,否则必究相关责任。
GNU C 内联汇编介绍的更多相关文章
- GNU C内联汇编(AT&T语法)
转:http://www.linuxso.com/linuxbiancheng/40050.html 内联汇编提供了可以在C或C++代码中创建汇编语言代码,不必连接额外的库或程序.这种方法对最终程序在 ...
- 最牛X的GCC 内联汇编
导读 正如大家知道的,在C语言中插入汇编语言,其是Linux中使用的基本汇编程序语法.本文将讲解 GCC 提供的内联汇编特性的用途和用法.对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 ...
- GCC内联汇编入门
原文为GCC-Inline-Assembly-HOWTO,在google上可以找到原文,欢迎指出翻译错误. 中文版说明 由于译者水平有限,故译文出错之处,还请见谅.C语言的关键字不译,一些单词或词组( ...
- x86平台转x64平台关于内联汇编不再支持的解决
x86平台转x64平台关于内联汇编不再支持的解决 2011/08/25 把自己碰到的问题以及解决方法给记录下来,留着备用! 工具:VS2005 编译器:cl.exe(X86 C/C+ ...
- C内联汇编
用C写程序比直接用汇编写程序更简洁,可读性更好,但效率可能不如汇编程序,因为C程序毕竟要经由编译器生成汇编代码,尽管现代编译器的优化已经做得很好了,但还是不如手写的汇编代码.另外,有些平台相关的指令必 ...
- Linux 中 x86 的内联汇编
工程中需要用到内联汇编,找到一篇不错的文章,趁机学习下. 原文地址:http://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/ 如果 ...
- [翻译] GCC 内联汇编 HOWTO
目录 GCC 内联汇编 HOWTO 原文链接与说明 1. 简介 1.1 版权许可 1.2 反馈校正 1.3 致谢 2. 概览 3. GCC 汇编语法 4. 基本内联 5. 扩展汇编 5.1 汇编程序模 ...
- Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)【转】
转自:http://www.linuxidc.com/Linux/2013-06/85221p3.htm 阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入 ...
- 推荐一篇讲arm架构gcc内联汇编的文章
这是来自ethernut网站的一篇文章,原文链接: http://www.ethernut.de/en/documents/arm-inline-asm.html 另外,据说nut/os是个不错的开源 ...
随机推荐
- Java的整个字符串的结束索引在最后一个字符之外
/** * Created by xfyou on 2016/11/4. */ public class SubstringDemo { public static void main(String[ ...
- Dynamics.js - 创建逼真的物理动画的 JS 库
Dynamics.js 是一个用来创建物理动画 JavaScript 库.你只需要把dynamics.js引入你的页面,然后就可以激活任何 DOM 元素的 CSS 属性动画,也可以用户 SVG 属性. ...
- asp.net正则模板引擎代码
我们申明一个数组 ]; 接下来关键的正则表达式: RegexOptions options = RegexOptions.None; //嵌套模板标签(兼容) r[] = new Regex(@&qu ...
- DOM中的事件处理概览与原理的全面剖析
事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信,DOM支持大量的事件: 本文通过这几点向大家详细解析事件处理的基本原理:事件类型.事件目标.事件处理程序.事件对象.事件传播 最后再向 ...
- Office 365 - SharePoint 2013 Online 中使用Windows PowerShell
1.如果想要在SharePoint Online中使用Windows PowerShell,首先需要安装SharePoint Online Management Shell(下载地址附后),如下图: ...
- Sharepoint 2010 工作流启动时处理出错
在Sharepoint 2010 中使用Sharepoint 2010 designer做了一个工作流: 运行工作流时,当主办工程师是“张三”的时候,工作流一启动就报错. -------------- ...
- 使用imeOptions
Android的软键盘右下角有Action按钮,如下图的“上一步” 在EditText中有 android:imeOptions选项,它包括完成按钮“actionDone”,发送按钮“actionSe ...
- XMPP实现登陆注销功能
XMPP框架的下载与导入等问题请参照 —— XMPP框架的分析.导入及问题解决 DEMO ——XMPP即时通讯(已导入框架)密码:3a7n 这篇我们利用XMPP框架来实现一下登陆功能,先来介绍一下XM ...
- Kotlin从入门到掉坑里
为什么要用Kotlin,和Java完全兼容,相互之间引用完全不是问题,所有没有什么负担. 使用Kotlin已经近一个月,基本上看完了语法就上正式项目了,期间几次准备放弃,最终坚持下来了.关于Kotli ...
- 【代码笔记】iOS-点击一个button,出6个button
一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> //加入头文件 #import "DCPathB ...