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. Typecast 免费了!献给设计师们的礼物

    TypeCast 让你可以从 Fonts.com.TypeKit.FontDeck 和 Google 这些字体供应和商选择字体,而且能非常方便的比较这些字体使用效果.如果你想获得用户对这些字体效果的反 ...

  2. Web前端开发工程师基本要求

    一位好的Web前端开发工程师在知识体系上既要有广度,又要有深度,所以很多大公司即使出高薪也很难招聘到理想的前端开发工程师.现在说的重点不在于讲解技术,而是更侧重于对技巧的讲解.技术非黑即白,只有对和错 ...

  3. 【经验之谈】前端面试知识点总结03(JavaScript相关)——附答案

    目录 三.JavaScript部分 1.谈谈你对Ajax的理解?(概念.特点.作用) 2.说说你对延迟对象deferred的理解? 3.什么是跨域,如何实现跨域访问? 4.为什么要使用模板引擎? 5. ...

  4. MySQL索引类型 btree索引和hash索引的区别

    来源一 Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 ...

  5. Windows 安装 GTK+ 图文说明

    首先去官方下载: //官方网站 http://www.gtk.org/download/index.php //下载链接 http://win32builder.gnome.org/gtk+-bund ...

  6. 【Bootstrap】1.初识Bootstrap

    作为Web前端开发框架,Bootstrap为大多数标准的UI设计常见提供了用户友好.扩浏览器的解决方案. 1.下载Bootstrap 打开官方网址 http://getbootstrap.com/ 进 ...

  7. 学习笔记——关于HTML(含HTML5)的块级元素和行级(内联)元素总结

    1.首先我们要知道什么是块级元素和行级(内联)元素? 块级(block)元素的特点: ①总是在新行上开始: ②高度,行高以及外边距和内边距都可控制: ③宽度缺省是它的容器的100%,除非设定一个宽度: ...

  8. URL 路径长度限制(错误:指定的文件或文件夹名称太长)

    本节讨论 URL 的构成.SharePoint 2010 构建 URL 的方式.URL 的编码和加长以及作为其他 URL 中的参数传递的方式. SharePoint URL 的构成 SharePoin ...

  9. SharePoint 2013 Error - File names can't contain the following characters: & " ? < > # {} % ~ / \.

    错误截图: 错误信息: --------------------------- Message from webpage --------------------------- File names ...

  10. UILabel用法

    // UILabel -> UIView // UILabel用来显示文字内容 //创建一个Label,一般都直接通过initWithFrame确定位置跟大小 UILabel *lb = [[U ...