ARM体系下函数形参调用寄存器详解

文章目录

  • 前言
  • 一、编写代码
  • 二、调试分析
  • 三.流程归纳
  • 总结


前言

在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); 
}
  • 设置断点

ARM体系下函数形参调用寄存器详解_第1张图片

  • 开始仿真

ARM体系下函数形参调用寄存器详解_第2张图片

  • 打开反汇编窗口

ARM体系下函数形参调用寄存器详解_第3张图片

  • 运行程序至断点

ARM体系下函数形参调用寄存器详解_第4张图片

二、调试分析

  • 单步执行

ARM体系下函数形参调用寄存器详解_第5张图片
每点击这个按钮,程序只运行1步,单步执行观察程序执行结果

  • 开始调试
    ARM体系下函数形参调用寄存器详解_第6张图片

  • 查看界面左侧寄存器状态栏的值

ARM体系下函数形参调用寄存器详解_第7张图片
可以看到寄存器r0的值变为0x66

  • 继续单步执行

ARM体系下函数形参调用寄存器详解_第8张图片
当反汇编窗口中的黄色指针指向上图情况,停下来稍微分析一下

此时注意仔细看寄存器状态栏中R0,R1,R2,R3的值,发现分别为0x660x55,0x33,0x44,是函数sub传入的参数

个人认为,MOV指令可以相当于c语言中的赋值,执行以上语句分别就是
R0赋值为0x66R1赋值为0x55,R3赋值为0x44,R2赋值为0x33

我们再来看这条语句

STRD  r1,r0,[sp,#0]

STRD的作用: 将数据加载到指定内存地址

所以这句话的意思应该是将r1,r0寄存器中的数据写到(sp指针+0)的内存地址中去。

  • 继续执行
    ARM体系下函数形参调用寄存器详解_第9张图片
    当程序执行到上图所示时,我们可以发现原来R0的值为0x66R1的值为0x55,最后R00x11覆盖,R10x22覆盖。

  • 小结

经过以上步骤,我发现当函数参数多于4个时,其余参数是使用内存中的栈传递的。以本题为例,a1,a2,a3,a4分别用R0,R1,R2,R3寄存器传递,a5,a6 则被写入内存,调用栈来进行传递。

调用子函数间的参数依次通过R0~R3这4个寄存器传递。父函数在调用子函数前先将参数存入到R0~R3中,若只有一个参数则使用R0传递,2个则使用R0和R1传递,依次类推,当超过4个参数时,其它参数通过栈传递。当子函数运行时,根据自身参数个数自动从R0~R3或者栈中读取参数。

三.流程归纳

  • 从最后一个参数开始写入,写入四个参数

ARM体系下函数形参调用寄存器详解_第10张图片

这里为什么在会先执行写入数据到R3,再执行写入数据到R2呢,难不成这指令还流行插队的?别急,马上我们就知道答案了。

  • 将多余参数加载到内存中
    ARM体系下函数形参调用寄存器详解_第11张图片
    仔细观察寄存器中的值,会发现从R0,R1,R2,R3中的值顺序刚好为函数传入参数中的第一个到第四个,原来上一步先执行将数据加载到R3寄存器中,再执行加载数据到R2寄存器的指令,是“早有预谋”,是不是很神奇呢,哈哈。

  • 关于嵌套调用子函数
    如果只有R0~R34个寄存器,那么当发生嵌套调用时,原有的函数参数值是被丢弃了吗?

参考这篇文章,我找到了答案
ARM 编程:C语言与汇编间互相调用,参数与返回值的传递方式详解

原来发生函数调用时,即使是父函数有参数需要传递,子函数也可以任意更改R0~R3寄存器,无需考虑会破坏它们在父函数中保存的数值,返回父函数前无需恢复其值。AAPCS规定,发生函数调用前,由父函数将R0~R3中有用的数据压栈,然后才能调用子函数,以防止父函数R0~R3中的有用数据被子函数破坏。
调用完子函数后,将原有数据弹栈,数据即可恢复。

总结

这篇博客是我学习关于ARM体系下调用函数参数是寄存器的状态心得,在分析函数参数调用过程中时,遇到了很多困难,不过,发现问题并解决问题的过程总是很快乐的,也让我对ARM处理器中的寄存器有了更多了解,如果有机会,我会写写关于其余寄存器的一些心得,也算是一个收尾吧。
不过因为没有系统的学过汇编语言,编译原理等,所以上述分析过程可能存在许多问题。若有问题,希望大家能不吝指教,一起学习进步。

你可能感兴趣的:(arm,stm32,c语言,keil,mdk)