news 2026/7/4 1:25:19

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

作者头像

张小明

前端开发工程师

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

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

0 参考资料&工具

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

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

使用C语言调用子函数是如何保护/恢复现场的呢?本文以Cortex-M3为例,逐行汇编代码分析C语言调用子函数时入/出栈(保护/恢复现场)全过程。
准备工作:
(1)使用keil5新建一个基于stm32f103的工程
(2)将栈底值设置为0x20000400(Cortex-M3栈从上往下生长),也就是将栈大小设置为0x400
在分析之前我们需要对寄存器的功能有所了解:

ATPCS即ARM-THUMB procedure call standard(ARM-Thumb过程调用标准)的简称。ATPCS规定了应用程序的函数可以如何分开地写,分开地编译,最后将它们连接在一起,所以它实际上定义了一套有关过程(函数)调用者与被调用者之间的协议。
规则如下:
(1)子程序通过寄存器R0~R3来传递参数.
(2)在子程序中,使用R4~R11来保存局部变量
(3)寄存器R12用作子程序间scratch寄存器,记作ip
(4)寄存器R13用作数据栈指针,记做SP
(5)寄存器R14用作连接寄存器,记作lr ; 它用于保存子程序的返回地址
(6)寄存器R15是程序计数器,记作PC

1.1 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)使用BL指令跳转到子函数,自动将子函数下一条指令地址保存到LR
(1.3)保护现场,至少要将LR(程序链接寄存器,保存有子函数返回地址)入栈
(2)恢复现场
至少要将LR(程序链接寄存器,保存有子函数返回地址)出栈,将LR的值写入到PC,跳转到子函数下一条指令位置继续执行

https://download.csdn.net/blog/column/12617739/141761244

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/29 0:28:37

Java面试-10-微服务SpringCloudAlibaba

Spring Cloud Alibaba 组件问答整理 目录 1. Nacos 1.1 服务注册与发现1.2 核心功能1.3 分布式配置管理1.4 多环境配置1.5 动态刷新1.6 配置中心优势1.7 灰度发布1.8 动态路由1.9 动态权重调整 2. OpenFeign 2.1 什么是 OpenFeign2.2 OpenFeign 工作原理2.3 OpenFeign 与 Fei…

作者头像 李华
网站建设 2026/6/29 0:31:45

Java毕设项目:基于 Java+SpringBoot 的地域旅游资源推荐系统的设计与实现 基于 SpringBoot 的智能旅游咨询推荐管理系统的设计与实现 (源码+文档,讲解、调试运行,定制等)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

作者头像 李华
网站建设 2026/6/29 10:44:55

计算机毕业设计之jsp基于性别网上学习特征及可视化

随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代&#xf…

作者头像 李华
网站建设 2026/6/29 0:28:43

在建项目去哪查?锁定目标工程的实用指南

做施工的都知道,项目从图纸到完工,中间会经过好几个阶段,每个阶段都会留下公开的信息痕迹。问题在于这些信息分散在不同的地方,查一个项目往往要折腾好几个渠道。下面按项目推进的顺序,把几个实用的查询路径整理一下。…

作者头像 李华
网站建设 2026/6/29 0:28:44

py learning - day 1(列表、解包)

1.List的操作 这里需明白sort()和sorted()的区别 前者返回None,对原列表进行排序;后者返回排序后列表,不改变原列表2.解包 场景 1:基础赋值解包(数量一一对应) 规则:左边有几个变量,右边就必须有…

作者头像 李华