抛个砖:汇编中如何调用C函数?
引个玉:相互调用有四种组合:
1.C函数之间的相互调用
2.汇编例程的相互调用
3.C函数调用汇编例程
4.汇编例程调用C函数
但这里,主讲1和4。因为2与4类似,1与3类似
1.C函数调用机制
exch.c源文件:
void swap(int *a, int *b){
int c;
c = *a, *a = *b; *b = c;
}
int main(){
int a, b;
a = 16, b = 32;
swap(&a, &b);
return (a - b);
}
exch.c的汇编代码(AT&T风格)
小结:这里说说编译器的聪明之处,传递地址时使用 lea 指令计算的地址值并没有保存在调用者的栈中,而是两个寄存器。减少了内存访问次数。此外,使用mov将参数地址压入被调用者栈而不是用push压入调用者栈中,减少了所需系统周期数(push 肯定比 mov 所需周期数多)
基于exch.c 再改造一下,如果传递结构体,如何利用栈实现呢?
exch_struct.c
typedef struct{
int *a;
int *b;
}ts_struct;
void exch_struct(ts_struct ts){
int c;
c = *ts.a;
*ts.a = *ts.b;
*ts.b = c;
}
int main(void){
ts_struct ts;
int a = 10, b = 20;
ts.a = &a;
ts.b = &b;
exch_struct(ts);
return 0;
}
exch_struct.c对应的汇编代码:
C函数调用机制总结:
即使参数是结构体(或类)这类大的对象,也能通过栈机制(+寄存器)完成数据交换(调用者通过参数,被调用者通过返回值或指针交换)。
2.汇编中调用C函数
将调用函数的参数逆序(从右至左)压入调用者的栈中,再执行call指令,即如下图
小结:汇编中调用C函数比较自由,原因有二
1.只要在栈中适当位置的内容都可以作为参数供C函数使用
2.根本不用CALL指令,而采用JMP指令来达到调用函数的目的(前提需把下一条要执行的指令地址人工压栈中)
提前透露一点:
Linux内核利用了在被调用者中,传递的参数在栈中是连续分布的。由此可以只传入参数列表的几个,其它参数按连续性分布特点依次向上取得(以后的文章会有讲解)。
参考资料:
《Linux内核完全剖析——基于0.12内核》第3章