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, #1 AT&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 内联汇编介绍的更多相关文章

  1. GNU C内联汇编(AT&amp;T语法)

    转:http://www.linuxso.com/linuxbiancheng/40050.html 内联汇编提供了可以在C或C++代码中创建汇编语言代码,不必连接额外的库或程序.这种方法对最终程序在 ...

  2. 最牛X的GCC 内联汇编

    导读 正如大家知道的,在C语言中插入汇编语言,其是Linux中使用的基本汇编程序语法.本文将讲解 GCC 提供的内联汇编特性的用途和用法.对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 ...

  3. GCC内联汇编入门

    原文为GCC-Inline-Assembly-HOWTO,在google上可以找到原文,欢迎指出翻译错误. 中文版说明 由于译者水平有限,故译文出错之处,还请见谅.C语言的关键字不译,一些单词或词组( ...

  4. x86平台转x64平台关于内联汇编不再支持的解决

    x86平台转x64平台关于内联汇编不再支持的解决     2011/08/25   把自己碰到的问题以及解决方法给记录下来,留着备用!   工具:VS2005  编译器:cl.exe(X86 C/C+ ...

  5. C内联汇编

    用C写程序比直接用汇编写程序更简洁,可读性更好,但效率可能不如汇编程序,因为C程序毕竟要经由编译器生成汇编代码,尽管现代编译器的优化已经做得很好了,但还是不如手写的汇编代码.另外,有些平台相关的指令必 ...

  6. Linux 中 x86 的内联汇编

    工程中需要用到内联汇编,找到一篇不错的文章,趁机学习下. 原文地址:http://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/ 如果 ...

  7. [翻译] GCC 内联汇编 HOWTO

    目录 GCC 内联汇编 HOWTO 原文链接与说明 1. 简介 1.1 版权许可 1.2 反馈校正 1.3 致谢 2. 概览 3. GCC 汇编语法 4. 基本内联 5. 扩展汇编 5.1 汇编程序模 ...

  8. Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)【转】

    转自:http://www.linuxidc.com/Linux/2013-06/85221p3.htm 阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入 ...

  9. 推荐一篇讲arm架构gcc内联汇编的文章

    这是来自ethernut网站的一篇文章,原文链接: http://www.ethernut.de/en/documents/arm-inline-asm.html 另外,据说nut/os是个不错的开源 ...

随机推荐

  1. java内存模型-volatile

    volatile 的特性 当我们声明共享变量为 volatile 后,对这个变量的读/写将会很特别.理解 volatile 特性的一个好方法是:把对 volatile 变量的单个读/写,看成是使用同一 ...

  2. 实战手记:让百万级数据瞬间导入SQL Server

    想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...

  3. css知多少(4)——解读浏览器默认样式

    上一节<css知多少(3)——样式来源与层叠规则>介绍了样式的五种来源,咱们再通过一张图回顾一下. 对于上面的三层,咱们大概都比较熟悉了.下面的两层中,用户自定义样式一般也就是改一改字号大 ...

  4. Slick – 这是你需要的最后一款 jQuery 传送带插件

    slick 是一款完全响应式的 jQuery 传送带插件,能够根据容器自动适应宽度.在现代浏览器中会使用 CSS3 来实现特殊效果,可以使用扩展方法对项目进行添加.删除和过滤.这是你需要的最后一款 j ...

  5. 12款免费的 WordPress 响应式主题下载

    最流行的内容管理系统(WordPress)和最流行的网页设计技术(响应式设计)结合会是什么样的呢?下面这个列表收集了12款响应式的 Wordpress 主题,能够带给你不一样的网站体验. 您可能感兴趣 ...

  6. CSS级联和继承

    2016-11-06 <CSS入门经典>第七章 1.在HTML中使用CSS样式表的三种方式: (1)内联的样式表. eg:<em style="background-whi ...

  7. ArcGis 001270 : 合并数据失败

    描述 工具无法将服务所需的数据和资源打包. 如果用于发布 GIS 资源的路径或要向服务器复制的数据的路径大小超出了操作系统的限制,则当您向 ArcGIS 服务器复制数据时会发生此错误. 此路径包括过渡 ...

  8. 转:与Microsoft Visual Studio集成良好的第三方工具

    我们都知道,Microsoft Visual Studio(简称VS)是微软开发的一个非常高效的集成开发环境,但即使已经发展到VS2012,仍然有许多不尽人意的地方.一般来讲,一个项目中需要有控件.代 ...

  9. SP2013 SP1(kb28805502)补丁安装测试初体验

    安装完SP1(kb28805502)第一印象是整体页面加载浏览速度非常快了,在笔记本建立的虚拟机能达到肉眼感觉不到卡顿真的是非常快了. 1.新添加了页面个性化设置功能菜单 3.默认访问网站的页面显示, ...

  10. [Android]使用Kotlin+Anko开发Android(一)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4800656.html  Kotlin是由JetBrains开发 ...