C函数调用
一、基本概念
在函数调用中,r0-r4被用来传递参数或返回值。在子函数调用中,被用来存放临时数据。
r4-r8,r10,r11被用来存放函数局部变量。
子程序必须保留寄存器r4-r8、r10、r11和SP的内容。
在所有程序中r12-r15是特点寄存器,分别是IP(The Intra-Procedure-call scratch register),SP(The Stack Pointer),LR(The Link Register)和PC(The Program Counter)。
CPSR是全局寄存器。
二、函数调用
2.1 函数调用
返回地址存放在LR寄存器中;将目标地址存放到PC寄存器实现跳转。LR寄存器的0位值是1表示Thumb,0表示Arm。
2.2 参数传递
使用r0-r4及栈传递参数。参数较多少才使用栈传递参数。
2.3 栈帧创建
2.3.1 保存旧FP
- 在调用函数之前,如果需要,会保存当前帧指针(FP,通常是R11)到栈上,确保调用者栈帧的完整性。
2.3.2 更新FP和SP
- 通过将栈指针(SP)的值赋给帧指针(FP),然后调整SP以为局部变量和可能的额外参数分配空间。这形成了一个新的栈帧。
2.3.3 保存调用者状态
- 在某些情况下,可能会保存LR(链接寄存器,用于保存返回地址)和其他可能受影响的寄存器到栈上,确保函数调用后的恢复。
三、函数执行
3.1 局部变量分配
函数内部的局部变量空间在栈上分配,栈指针SP会相应调整。
3.2 执行代码
函数执行其指定的逻辑,可能包括对其他函数的调用。
四、返回过程
4.1 返回值
返回时使用r0-r4传递返回值。对于多返回值或复杂类型,需要其他机制。
4.2 恢复栈帧
4.2.1 恢复FP
如果改变了FP,函数在返回前会将FP指向的地址(即上一层函数的栈帧底部)赋给FP,恢复调用者的栈帧指针。
4.2.2 恢复SP
调整SP以释放当前函数的栈帧空间,回到调用者函数的栈状态。
4.3 返回地址
通过LR寄存器中的值来实现返回,LR在函数调用时被自动设置为下一条指令的地址(通过BL指令自动完成,或者在手动管理栈时显式保存)。执行BX LR或BLX LR指令,根据Thumb/ARM状态切换,跳转到LR寄存器保存的地址,从而返回到调用者。
五、继续执行
控制权回到调用函数,从调用点的下一条指令开始执行。
六、参考
Procedure Call Standard for the Arm® Architecture