首先说明一下,arm中函数调用不同的编译器可能差别很大,即使都是arm-linux的交叉编译器,也有差别,有的编译器把r7寄存器作为栈帧寄存器(fp),有的把r11寄存器作为栈帧指针(fp),例如arm-linux-gnueabihf-gcc用的r7和arm-linux-gnueabi-gcc用的r11,另外在函数执行开头的处理也不一样
1. arm-linux-gnueabihf-gcc编译器先给函数中变量分配栈空间,然后将fp和sp指向栈顶
2. arm-linux-gnueabi-gcc编译器先让fp和sp指向栈顶(栈底),然后再给函数中的变量分配栈空间,之后sp指向新的栈顶,而fp指向的老的栈顶变成了函数的栈底。
个人感觉第二种比较好理解,但是总而言之,每次函数开始执行都需要保存fp指针到栈空间,这里的被保存fp指针是上一个函数的数据(一般是栈底数据),然后立即将本函数的栈底数据保存到fp指针, 函数调用结束,根据fp的数据恢复sp指针,从而释放结束函数的栈空间。
这么看来,函数调用需要入栈的数据只有前一个函数的fp指针,然后跳转的时候会将跳转指令bl的下一条指令地址保存到 lr寄存器,方便函数调用结束后返回。当然如果函数入参很多,寄存器不够用,这些参数同样需要入栈。
下面贴出两种编译器的C代码以及反汇编:
下面我们看C程序:
int m = 8;
int fun(int a,int b)
{
int c = 0;
c = a + b;
return c;
}
int main()
{
int i = 4;
int j = 5;
m = fun(i, j);
return 0;
}
使用的编译器是arm-linux-gnueabihf-gcc,针对的芯片是I.Mx6ull,对应的反汇编代码:
Disassembly of section .text:
00010094 <fun>:
10094: b480 push {r7}
10096: b085 sub

本文深入探讨了ARM架构下不同编译器(arm-linux-gnueabihf-gcc与arm-linux-gnueabi-gcc)在函数调用过程中的实现差异,包括栈帧寄存器的选择、栈空间分配策略及参数传递方式。通过对比两种编译器生成的反汇编代码,详细分析了函数调用前后栈空间的管理,以及如何利用fp和lr寄存器确保正确调用和返回。
最低0.47元/天 解锁文章
3383





