学内嵌汇编首先知道编译器的编译流程,内嵌汇编就是嵌套在高级程序语言中的汇编语言。在cpp 文件转成 .s 汇编文件时,内嵌汇编保持不动,只有高级程序语言会编译成汇编合成在.s文件中。下面的链接将了C的源码是怎么变成汇编码:
《Linux C:汇编码的生成 》https://blog.youkuaiyun.com/superSmart_Dong/article/details/115920429
目录
一、基本汇编
基本汇编就是纯汇编语言,而扩展汇编在基本汇编上加了些功能,例如可用占位符实现从C程序中向内嵌汇编中传递/输出变量值,而不用去分析当前代码的堆栈结构。也可以不用关心具体用哪些寄存器合适。先来看看基本的内嵌汇编
int main(){
int aaa = 1 ,bbb=2;
__asm__ __volatile__ (
"movl $88,-12(%ebp) \n\t"
"movl $66,-16(%ebp) \n\t"
);
cout<<aaa<<" "<<bbb; //输出 88 66
return 0;
}
内嵌汇编用 asm() 关键字来表明括号内写的是汇编码,每段汇编码当用字符串引号来引起来,在编译时直接套在编译后的汇编文件中。由于计算机的原因,大多数系统用换行符作为汇编语言一条语句的结束符,而有些系统用分号';'表示,而也有些系统的分号是作为注释符号。所以每个系统下的汇编语言语法可能并不统一。
在上述代码的汇编代码块中,%ebp 表示 ebp寄存器中对应的内存地址,而-12(%ebp)表示ebp寄存器中对应的内存地址再往低地址偏移12个字节。在我的系统中该地址对应的变量是aaa的地址值。上段代码在我的WINDOWS上可以正常执行而在我的Linux上执行会出现段错误,代码要改成如下方式才有同样的效果。之所以无法兼容,是因为每个系统编译出来的堆栈情况可能会不一样,用的寄存器也不一样。
#include "iostream"
using namespace std;
int main(){
int aaa = 1 ,bbb=2;
__asm__ __volatile__ (
"movl $88,-8(%rbp) \n\t"
"movl $66,-4(%rbp) \n\t"
);
cout<<aaa<<" "<<bbb;
return 0;
}
对应的汇编码
.....
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $1, -8(%rbp)
movl $2, -4(%rbp)
#APP
# 9 "main.cpp" 1
movl $88,-8(%rbp)
movl $66,-4(%rbp)
# 0 "" 2
#NO_APP
movl -8(%rbp), %eax
movl %eax, %esi
.....
可以看出不同系统下的寄存器可能不同,例如fp,bp,ebp,rbp都是不同系统下功能类似的寄存器。也可以看出堆栈情况的表达方式也和WINDOWS下的不一样了。基本内嵌汇编可以写在函数体外部,但在不同系统的寄存器,汇编语法,堆栈分配情况不一样,用相同代码的兼容性问题就要考虑非常多,并且操作起来也十分的不方便。扩展内嵌汇编有些许的改善,虽然它必须写在C的函数体中。
二、扩展汇编
asm asm-qualifiers ( AssemblerTemplate
: OutputOperands
: InputOperands
: Clobbers
: GotoLabels
)
asm-qualifiers : __volatile__修饰词就不需要多讲了,不加volatile 编译器会帮你优化一些操作,删除一些没用的代码。 goto修饰词,用来配合内容中的GotoLabel
在小括号内的文本如果发现冒号‘:’则该语句块视为扩