想要用汇编实现的代码有哪几种方式?
1. 直接从头开始用汇编写。
2. 先写好C程序,然后通过-S选项来修改。
3. 直接在C中代码嵌入汇编的代码,也就是所谓的内联汇编
如何实现内联汇编代码?
asm(“”);
你在” ”之间所写的内容就是你加入的汇编代码。
要求如下:
1. 指令必须包含在引号里面
2. 如果包含的指令超过一条,那么必须使用新行字符分隔汇编语言代码的每一行
就像下面一样
asm(“movl $1,%eax\n\t”
“mov $0,%ebx\n\t”
“int $0x80”);
就像这样。注意”” 和””不在一排的时候C语言会自动将其合并成一个字符串。
\t并不是必须的。但是你想看你程序最后嵌入代码的时候,\t会方便你查看代码。
在书中。将原C代码汇编出来的时候会加上#APP和#NO_APP。这就看的很清楚了。
3. 汇编代码中对于变量只能使用全局变量。直接用在C中的名字就行了。
4. 有时候编译器会直接优化掉你的汇编代码,所以最好用asm volatile(” ”);
加上volatile来优化。
注意:asm在ANSI C中是有其他用途的,所以可能你在你的代码中写asm不能用。
那么就改成__asm__这个名字就行了
当然这种内联汇编的限制也是很大的,毕竟不能用局部变量,所以就引出了下面的扩展汇编。
格式如下
asm(“汇编代码”:output locations: input operands : changed registers);
这里感觉output和input写反了。。反正用的时候记得,第一个冒号后接'='+变量。最后一个changed一般可以省略,可能个别情况要写“memory”表示写到内存。
前面是汇编代码,注意这里汇编引用寄存器必须使用两个%号,就像这样%%eax。
示例如下:
asm(“imull %%edx,%%ecx\n\t”
“movl %%ecx,%%eax”
:”=a(result)”
:”d”(data1),”c”(data2));
这样ecx中就是存储的data2的值。Edx中存的就是data1的值。
对应的关系如下
a | 使用eax |
b | 使用ebx |
c | 使用ecx |
d | 使用edx |
S | 使用esi |
D | 使用edi |
r | 使用任何可用的通用寄存器 |
q | Eax,ebx,ecx,edx其中之一 |
A | 64位的值使用eax和edx |
f | 使用浮点寄存器 |
t | 使用第一个浮点寄存器 |
u | 使用第二个浮点寄存器 |
m | 使用变量的内存位置 |
o | 使用偏移内存位置 |
V | 只使用直接内存的位置 |
i | 使用立即整数值 |
n | 使用值已知的立即整数值 |
g | 使用任何可用的寄存器或者内存位置 |
+ | 可以读取和写入操作数 |
= | 只能写入操作数 |
% | 如果必要,操作数可以和下一个操作数切换 |
& | 在内联函数完成之前,可以删除或者重新使用操作数 |
还有更方便的方法,就是使用占位符,直接用数字代替变量名字。
注:
一般asm后面会跟一个volatile防止编译器优化。
对于__asm__与__volatile__和asm与volatile一样。
内联汇编还有一个非常有用的使用方式,那就是在宏中使用内联汇编代码。
因为在宏中可以使用任一变量,直接通过在各处插入宏,使得通用性更强。
我想可变参数列表var_list就是通过内联汇编来实现的吧。有时间去找代码看看。
还有一种在C++中声明C代码的方式。因为汇编是以C的形式供程序员使用的。
又因为C和C++的命名和调用有一定程度的不同(因为C++重载等特性)。
所以需要声明一下。
extern “C”
{
C函数声明;
}
这样就可以在C++中使用内联汇编了。
最后再补充一个关于内联宏中写了memory的情况。
Linux内核中有个宏,是关于内存屏障的。
#define barrier() __asm__ __volatile__("": : :"memory);
这个宏看似什么指令都没有执行。但是它会告诉gcc强制刷新内存和寄存器。避免产生内存屏障。我会在另一篇博客有介绍内存屏障。