C语言调用子函数时入/出栈(保护/恢复现场)全过程分析:以Cortex-M3为例

0 参考资料&工具

c 复制代码
Cortex M3权威指南(中文).pdf
keil5(用于仿真查看寄存器、栈变化)

1 C语言调用子函数时出入/出栈(保护/恢复现场)全过程分析

使用C语言调用子函数是如何保护/恢复现场的呢?本文以Cortex-M3为例,逐行汇编代码分析C语言调用子函数时入/出栈(保护/恢复现场)全过程。

准备工作:

(1)使用keil5新建一个基于stm32f103的工程

(2)将栈底值设置为0x20000400(Cortex-M3栈从上往下生长),也就是将栈大小设置为0x400

1.1 C代码

本文用来调试分析的C代码如下:

c 复制代码
typedef unsigned int u32;


u32 fun2(u32 p1, u32 p2, u32 p3, u32 p4)
{
    return p1 + p2 + p3 + p4;
}

/**
 *   主函数
 */
int main(void)
{
	u32 x;
    fun2(1, 2, 3, 4);
	x = 0x778899AA;
}


void SystemInit(void)
{
}

说明:

使用主函数调用fun2子函数,查看Cortex-M3的保护、恢复现场过程。

1.2 C代码编译生成的汇编指令

以上C代码使用0级优化(不优化)生成的汇编指令如下,我们重点关注执行fun2函数前后的操作。

(1)执行fun2函数前传递变量

执行fun2函数前首先将第4、3、2、1个参数依次传递给r3、r2、r1、r0寄存器。

(2)跳转到fun2函数

参数传递完之后跳转到fun2函数。之所以不先保护现场而是先跳转到fun2函数,是因为使用BL指令会自动将跳转指令的下一条指令地址保存到LR。

BL function1 ;

使用"分支并连接"指令呼叫 function1 ; PC= function1,并且 LR=main的下一条指令地址

也就是说,在跳转到子函数前LR寄存器会被设置为子函数下一条指令地址

这里保存到LR的并非是0x0800021A,也就是fun2函数的下一条指令,反而保存的是0x0800021B。

这样操作的原因可以参考Cortex M3权威指南(中文).pdf:

也就是说,每次使用跳转指令跳转到子函数时,LR保存的实际上是子函数下一条指令地址+1 ,避免产生fault 异常。地址+1的值写入PC,PC会自动将最低位设置为0。

(3)执行fun2函数前保护现场

在执行fun2函数前,CPU会执行一条入栈指令PUSH,至少会将lr寄存器(程序链接寄存器,保存了子函数返回地址)入栈。使用keil的单步仿真功能,我们观察执行这条指令前后栈的变化:

(3.1)执行到fun2函数保护现场前栈内从顶到底依次保存了局部变量x(此时还未赋值,栈内值为0x08000234)、mian函数返回地址0x080001D9。此时栈指针值为0x200003F8。

(3.2)PUSH指令依次将lr、r4的值压入栈内。也就是依次向栈顶写入值0x0800021B、0x08000234。

此时栈指针值为0x200003F0。

此时,寄存器组内容如下:

(4)执行完fun2函数后恢复现场

执行POP指令出栈,从栈顶开始依次将栈值写入r4、PC。执行完该语句后栈的内容如下:

可以看到出入栈的部分已经被绿色标记出来,至此就算完成了C语言调用子函数保护/恢复现场操作。

此时寄存器组内容如下:

2 总结

C语言在调用函数时保护/恢复现场操作如下:

(1)保护现场

(1.1)传递函数参数

(1.2)跳转到子函数,自动将子函数下一条指令地址保存到LR

(1.3)保护现场,至少要将LR(程序链接寄存器,保存有子函数返回地址)入栈

(2)恢复现场

至少要将LR(程序链接寄存器,保存有子函数返回地址)出栈,将LR的值写入到PC,跳转到子函数下一条指令位置继续执行

相关推荐
シ風箏15 小时前
Neo4j【环境部署 02】图形数据库Neo4j在Linux系统ARM架构下的安装使用
linux·数据库·arm·neo4j
徐某人..4 天前
ARM嵌入式学习--第八天(PWM)
arm开发·学习·arm
徐某人..4 天前
ARM嵌入式学习--第七天(GPT)
学习·arm
whik11948 天前
Keil-MDK开发环境编译后axf自动转换bin格式文件
stm32·arm·keil·axi·bin·mdk
编程零零七9 天前
【Python】tensorflow中的argmax()函数
开发语言·python·信息可视化·数据分析·arm·python学习·python数据可视化
嵌入式小能手11 天前
shell编程入门之提取字符并设置rtc时间
linux·单片机·嵌入式硬件·arm
爱喝纯牛奶的柠檬15 天前
基于stm23的智慧宿舍系统 (DAY10)_小程序
stm32·嵌入式硬件·物联网·阿里云·小程序·arm
嵌入式小能手16 天前
自动化点亮LED灯之shell点灯
linux·单片机·嵌入式硬件·arm
白书宇22 天前
28.100ASK_T113-PRO Linux+QT 显示一张照片
linux·开发语言·arm开发·嵌入式硬件·qt·arm·硬件工程
白书宇1 个月前
19.QT程序简单的运行脚本
linux·arm开发·嵌入式硬件·物联网·arm