没有合适的资源?快使用搜索试试~ 我知道了~
首页c语言asm汇编内嵌语法.pdf
c语言asm汇编内嵌语法.pdf
5星 · 超过95%的资源 需积分: 39 104 下载量 112 浏览量
更新于2023-03-16
评论 5
收藏 185KB PDF 举报
GCC 支持在C/C++代码中嵌入汇编代码,这些汇编代码被称作GCC Inline ASM——GCC内联汇编。这是一个非常有用的功能,有利于我们将一些C/C++语法无法表达的指令直接潜入C/C++代码中,另外也允许我们直接写 C/C++代码中使用汇编编写简洁高效的代码。
资源详情
资源评论
资源推荐
.3 GCC Inline ASM
GCC 支 持 在 C/C++ 代 码 中 嵌 入 汇 编 代 码 , 这 些 汇 编 代 码 被 称 作 GCC Inline
ASM——GCC 内联汇编。这是一个非常有用的功能,有利于我们将一些 C/C++语法无法
表达的指令直接潜入 C/C++代码中,另外也允许我们直接写 C/C++代码中使用汇编编写
简洁高效的代码。
1.基本内联汇编
GCC 中基本的内联汇编非常易懂,我们先来看两个简单的例子:
__asm__("movl %esp,%eax"); // 看起来很熟悉吧!
或者是
__asm__("
movl $1,%eax // SYS_exit
xor %ebx,%ebx
int $0x80
");
或
__asm__(
"movl $1,%eax " \
"xor %ebx,%ebx " \
"int $0x80" \
);
基本内联汇编的格式是
__asm__ __volatile__("Instruction List");
1、__asm__
__asm__是 GCC 关键字 asm 的宏定义:
#define __asm__ asm
__asm__或 asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它
开头的,是必不可少的。
2、Instruction List
Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或
__asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并
非所 有 Instruction List 为空 的 内 联 汇 编 表 达式都 是 没 有 意 义 的 ,比如 : __asm__
("":::"memory"); 就非常有意义,它向 GCC 声明:“我对内存作了改动”,GCC 在编译
的时候,会将此因素考虑进去。
我们看一看下面这个例子:
$ cat example1.c
int main(int __argc, char* __argv[])
{
int* __p = (int*)__argc;
(*__p) = 9999;
//__asm__("":::"memory");
if((*__p) == 9999)
return 5;
return (*__p);
}
在 这段代码中,那条内联汇编是被注释掉的。在这条内联汇编之前,内存指针__p 所指向
的内存被赋值为 9999,随即在内联汇编之后,一条 if 语句判断__p 所指向的内存与 9999
是否相等。很明显,它们是相等的。GCC 在优化编译的时候能够很聪明的发现这一点。我
们使用下面的命令行对其进行编译:
$ gcc -O -S example1.c
选项-O 表示优化编译,我们还可以指定优化等级,比如-O2 表示优化等级为 2;选项-S 表
示将 C/C++源文件编译为汇编文件,文件名和 C/C++文件一样,只不过扩展名由.c 变
为.s。
我们来查看一下被放在 example1.s 中的编译结果,我们这里仅仅列出了使用 gcc 2.96
在 redhat 7.3 上编译后的相关函数部分汇编代码。为了保持清晰性,无关的其它代码未被
列出。
$ cat example1.s
main:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax # int* __p = (int*)__argc
movl $9999, (%eax) # (*__p) = 9999
movl $5, %eax # return 5
popl %ebp
ret
参 照一下 C 源码和编译出的汇编代码,我们会发现汇编代码中,没有 if 语句相关的代码,
而是在赋值语句(*__p)=9999 后直接 return 5;这是因为 GCC 认为在(*__p)被赋值之
后,在 if 语句之前没有任何改变(*__p)内容的操作,所以那条 if 语句的判断条件(*__p) ==
9999 肯定是为 true 的,所以 GCC 就不再生成相关代码,而是直接根据为 true 的条件生
成 return 5 的汇编代码(GCC 使用 eax 作为保存返回值的寄存器)。
我们现在将 example1.c 中内联汇编的注释去掉,重新编译,然后看一下相关的编译结果。
$ gcc -O -S example1.c
$ cat example1.s
main:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax # int* __p = (int*)__argc
movl $9999, (%eax) # (*__p) = 9999
#APP
# __asm__("":::"memory")
#NO_APP
cmpl $9999, (%eax) # (*__p) == 9999 ?
jne .L3 # false
movl $5, %eax # true, return 5
jmp .L2
.p2align 2
.L3:
movl (%eax), %eax
.L2:
popl %ebp
ret
由于内联汇编语句__asm__("":::"memory")向 GCC 声明,在此内联汇编语句出现的位
置内存内容可能了改变,所以 GCC 在编译时就不能像刚才那样处理。这次,GCC 老老实
实的将 if 语句生成了汇编代码。
可能有人会质疑:为什么要使用__asm__("":::"memory")向 GCC 声明内存发生了变
化?明明“Instruction List”是空的,没有任何对内存的操作,这样做只会增加 GCC 生成
汇编代码的数量。
确 实,那条内联汇编语句没有对内存作任何操作,事实上它确实什么都没有做。但影响内
存内容的不仅仅是你当前正在运行的程序。比如,如果你现在正在操作的内存 是一块内存
映射,映射的内容是外围 I/O 设备寄存器。那么操作这块内存的就不仅仅是当前的程序,
I/O 设备也会去操作这块内存。既然两者都会去操作同一块 内存,那么任何一方在任何时
候都不能对这块内存的内容想当然。所以当你使用高级语言 C/C++写这类程序的时候,你
必须让编译器也能够明白这一点,毕竟高 级语言最终要被编译为汇编代码。
你可能已经注意到了,这次输出的汇编结果中,有两个符号:#APP 和#NO_APP,GCC
将内联汇编语 句中"Instruction List"所列出的指令放在#APP 和#NO_APP 之间,由于
__asm__("":::"memory")中“Instruction List”为空,所以#APP 和#NO_APP 中间也
没有任何内容。但我们以后的例子会更加清楚的表现这一点。
关于为什么内联汇编__asm__("":::"memory")是一条声明内存改变的语句,我们后面会
详细讨论。
刚才我们花了大量的内容来讨论"Instruction List"为空是的情况,但在实际的编程中,
"Instruction List"绝大多数情况下都不是空的。它可以有 1 条或任意多条汇编指令。
当 在"Instruction List"中有多条指令的时候,你可以在一对引号中列出全部指令,也可以
将一条或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,你可以将 每
一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(\n,大多
数情况下\n 后还要跟一个\t,其中\n 是为了换行,\t 是为了 空出一个 tab 宽度的空格)
将它们分开。比如:
__asm__("movl %eax, %ebx
sti
popl %edi
subl %ecx, %ebx");
__asm__("movl %eax, %ebx; sti
popl %edi; subl %ecx, %ebx");
__asm__("movl %eax, %ebx; sti\n\t popl %edi
subl %ecx, %ebx");
都是合法的写法。如果你将指令放在多对引号中,则除了最后一对引号之外,前面的所有引
号里的最后一条指令之后都要有一个分号(;)或(\n)或(\n\t)。比如:
__asm__("movl %eax, %ebx
sti\n"
"popl %edi;"
"subl %ecx, %ebx");
__asm__("movl %eax, %ebx; sti\n\t"
"popl %edi; subl %ecx, %ebx");
__asm__("movl %eax, %ebx; sti\n\t popl %edi\n"
"subl %ecx, %ebx");
__asm__("movl %eax, %ebx; sti\n\t popl %edi;" "subl %ecx, %ebx");
都是合法的。
上述原则可以归结为:
任意两个指令间要么被分号(;)分开,要么被放在两行;
放在两行的方法既可以从通过\n 的方法来实现,也可以真正的放在两行;
可以使用 1 对或多对引号,每 1 对引号里可以放任一多条指令,所有的指令都要被放到引
号中。
在基本内联汇编中,“Instruction List”的书写的格式和你直接在汇编文件中写非内联汇编
没有什么不同,你可以在其中定义 Label,定义对齐(.align n ),定义段(.section name )。
例如:
__asm__(".align 2\n\t"
"movl %eax, %ebx\n\t"
"test %ebx, %ecx\n\t"
"jne error\n\t"
"sti\n\t"
"error: popl %edi\n\t"
"subl %ecx, %ebx");
上面例子的格式是 Linux 内联代码常用的格式,非常整齐。也建议大家都使用这种格式来
写内联汇编代码。
3、__volatile__
__volatile__是 GCC 关键字 volatile 的宏定义:
#define __volatile__ volatile
__volatile__ 或 volatile 是可选的,你可以用它也可以不用它。如果你用了它,则是向
GCC 声明“不要动我所写的 Instruction List,我需要原封不动的保留每一条指令”,否则
当你使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编
表达式中的指 令优化掉。
剩余25页未读,继续阅读
ddssff139
- 粉丝: 1
- 资源: 3
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- zigbee-cluster-library-specification
- JSBSim Reference Manual
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论2