在ARM体系下C和汇编语言混合编程的学习过程中,了解到关于函数形参在x86平台与ARM平台所调用的寄存器有一定的区别。
64位程序根据参数的个数而不同, 当参数1~6个,使用寄存器传递;参数大于6个,多出来的参数使用栈传递。
对于ARM平台,结论是:参数值传递按顺序存放在寄存器r0,r1,r2,r3里,超过4个参数值传递则放栈里。
于是我就在MDK环境下用main
函数调用子函数,仿真来观察其具体过程,让我们一起来看看在ARM体系下关于调用函数的一些细节。
新建工程,创建一个main.c文件
编写main函数和一个子函数sub
#include
void sub(int a1,int a2,int a3,int a4,int a5,int a6);
int main()
{
sub(0x11,0x22,0x33,0x44,0x55,0x66);//16进制是为了方便调试时便于观察,在仿真时数据是以16进制显示的
return 0;
}
void sub(int a1,int a2,int a3,int a4,int a5,int a6)
{
a3=a1+a2; //此函数体可以为空
a5=(a3+a4);
}
此时注意仔细看寄存器状态栏中R0
,R1
,R2
,R3
的值,发现分别为0x66
,0x55
,0x33
,0x44
,是函数sub传入的参数
个人认为,MOV指令可以相当于c语言中的赋值,执行以上语句分别就是
将R0
赋值为0x66
,R1
赋值为0x55
,R3
赋值为0x44
,R2
赋值为0x33
我们再来看这条语句
STRD r1,r0,[sp,#0]
STRD的作用: 将数据加载到指定内存地址
所以这句话的意思应该是将r1,r0寄存器中的数据写到(sp指针+0)的内存地址中去。
经过以上步骤,我发现当函数参数多于4个时,其余参数是使用内存中的栈传递的。以本题为例,a1,a2,a3,a4分别用R0
,R1
,R2
,R3
寄存器传递,a5,a6 则被写入内存,调用栈来进行传递。
调用子函数间的参数依次通过R0~R3
这4个寄存器传递。父函数在调用子函数前先将参数存入到R0~R3
中,若只有一个参数则使用R0传递,2个则使用R0和R1传递,依次类推,当超过4个参数时,其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。
这里为什么在会先执行写入数据到R3,再执行写入数据到R2呢,难不成这指令还流行插队的?别急,马上我们就知道答案了。
R0~R3
4个寄存器,那么当发生嵌套调用时,原有的函数参数值是被丢弃了吗?参考这篇文章,我找到了答案
ARM 编程:C语言与汇编间互相调用,参数与返回值的传递方式详解
原来发生函数调用时,即使是父函数有参数需要传递,子函数也可以任意更改R0~R3寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。AAPCS规定,发生函数调用前,由父函数将R0~R3
中有用的数据压栈,然后才能调用子函数,以防止父函数R0~R3
中的有用数据被子函数破坏。
调用完子函数后,将原有数据弹栈,数据即可恢复。
这篇博客是我学习关于ARM体系下调用函数参数是寄存器的状态心得,在分析函数参数调用过程中时,遇到了很多困难,不过,发现问题并解决问题的过程总是很快乐的,也让我对ARM处理器中的寄存器有了更多了解,如果有机会,我会写写关于其余寄存器的一些心得,也算是一个收尾吧。
不过因为没有系统的学过汇编语言,编译原理等,所以上述分析过程可能存在许多问题。若有问题,希望大家能不吝指教,一起学习进步。