1.gcc编译程序支持的另一种编码形式是内联汇编(inline assembly)代码。内联汇编不需要调用单独编译的汇编程序。我们可以通过特定的结构告诉编译程序将代码组合到一起,而不是要编译该代码块。虽然这样做会生成体系结构相关的文件,但能大大提高C函数的可读性并提高其执行效率。
内联汇编程序的结构:
asm ( assembler instruction (s)
:output operands (optional)
:input operands(optioanl)
: clobbered registers (optional)
);
内联汇编程序最基本的形式是:
asm ("movl %eax,%ebx");
也可以写成:
asm ("movl %eax,%ebx" : : :);
输出操作数:是一个C表达式列表,其后的圆括号中是约束条件。约束条件通常用“=”来表示,表示“只写”;“&”表示这是一个已经被修改过的操作数
输入操作数:语法同上,只是不用“=”修饰
修改过的寄存器:列出修改过的寄存器和内存
2.参数的编号方式
所有参数从0开始统一编号
例如,若有一个输出参数和2个输入参数,则%0是输出参数,%1和%2都是输入参数
3.约束条件
a:寄存器eax
b:寄存器ebx
c:寄存器ecx
d: 寄存器edx
S:寄存器esi
D:寄存器edi
I:常数(0....31)
q:从eax,ebx,ecx,edx动态分配一个寄存器
r:与q+esi,edi一样
m:内存定位
A:与a+b的作用一样。同时分配eax和ebx,形成一个64位寄存器
。。。。。
_volatile_:不要优化
例1:
int foo(void)
{
int ee=0x4000,ce=0x8000,reg;
_asm_ _volatile_("movl %1,%%eax",//将ce的值传给eax
"movl %2,%%ebx";//将ee的值传给ebx
"call setbits";//在汇编程序中调用函数
"movl %%eax,%0"//将返回值存储到eax中,并将其复制给reg
: "=r"(reg)//输出参数列表,reg属性为只写
: "r"(ce),"r"(ee)//输入参数列表,ce和ee是寄存器变量
: "%eax","%ebx"//寄存器修改列表,
)
printf("reg=%x",reg);
}
例2:
#define switch_to(prev,next,last) do{
unsigned long esi,edi;//局部变量
asm volatile(
"pushfl /n/t"
"pushl %%ebp/n/t"
"movl %%esp,%0/n/t"//把esp值复制给prev->thread.esp
" movl %5,%%esp/n/t"//将next->thread.esp赋值给esp
" movl $1f,%1/n/t"//保存eip的值
"pushl %6/n/t"//重新给eip赋值
"jmp _switch_to /n"
"1:/t"//参数被用作返回地址
"popl %%ebp/n/t"
"popfl"
:"=m" (prev->thread.esp),"=m"(prev->thread.eip),"=a"(last),"=S"(esi),"=D"(edi)//输出参数列表
:"m"(next->thread.esp),"m"(next->thread.eip),"2"(prev),"d"(next)//输入参数列表
);
}while(0)
/n/t与汇编程序的接口有关。每条汇编指令都应该在各自的线路上运行
输出参数列表:
[%0]=(prev->thread.esp),只写内存
[%1]=(prev->thread.eip),只写内存
[%2]=(last),对寄存器eax只写
[%3]=(esi),对寄存器esi只写
[%4]=(edi),对寄存器edi只写
输入参数列表:
[%5]=(next->thread.esp),内存
[%6]=(next->thread.eip),内存
[%7]=(prev),重新使用2号参数(寄存器eax)作为输入
[%8]=(next),是赋给寄存器edx的一个输入
此处没有已修改寄存器列表