C语言调用子函数时入/出栈(保护/恢复现场)全过程分析:以Cortex-M3为例
0 参考资料&工具
Cortex M3权威指南(中文).pdf
keil5(用于仿真查看寄存器、栈变化)
1 C语言调用子函数时出入/出栈(保护/恢复现场)全过程分析
使用C语言调用子函数是如何保护/恢复现场的呢?本文以Cortex-M3为例,逐行汇编代码分析C语言调用子函数时入/出栈(保护/恢复现场)全过程。
准备工作:
(1)使用keil5新建一个基于stm32f103的工程
(2)将栈底值设置为0x20000400(Cortex-M3栈从上往下生长),也就是将栈大小设置为0x400
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)跳转到子函数,自动将子函数下一条指令地址保存到LR
(1.3)保护现场,至少要将LR(程序链接寄存器,保存有子函数返回地址)入栈
(2)恢复现场
至少要将LR(程序链接寄存器,保存有子函数返回地址)出栈,将LR的值写入到PC,跳转到子函数下一条指令位置继续执行
相关文章:
C语言调用子函数时入/出栈(保护/恢复现场)全过程分析:以Cortex-M3为例
0 参考资料&工具 Cortex M3权威指南(中文).pdf keil5(用于仿真查看寄存器、栈变化)1 C语言调用子函数时出入/出栈(保护/恢复现场)全过程分析 使用C语言调用子函数是如何保护/恢复现场的呢?本文以Cortex-M3为例&a…...
理解Sigmoid激活函数原理和实现
Sigmoid 激活函数是一种广泛应用于机器学习和深度学习中的非线性函数,特别是在二分类问题中。它的作用是将一个实数值映射到(0, 1)区间,使得输出可以被解释为概率值,这在处理二分类问题时非常有用。 Sigmoid 函数的定义 Sigmoid 函数的数学…...
探秘DevSecOps黄金管道,安全与效率的完美融合
软件应用的安全性已成为企业和用户关注的焦点,DevSecOps作为一种将安全融入开发和运维全过程的理念和实践,旨在消除传统开发模式中安全被后置处理的弊端。DevSecOps黄金管道(Golden Pipeline)是实现这一理念的核心框架,…...
Redis的内存淘汰策略- volatile-lru
volatile-lru 策略简介 在 volatile-lru 策略下,当 Redis 的内存使用达到配置的上限(maxmemory)时,它会优先删除那些设置了过期时间的键,并且选择最近最少使用的键进行删除。LRU 算法的核心思想是,优先删除…...
HTTP和HTTPS的区别?哪一个更适合你的网站?
什么是 HTTP? HTTP(超文本传输协议)(Hypertext Transfer Protocol)它是一组允许网络浏览器与网络服务器(托管网站的计算机)进行通信的规则。 HTTP 使用请求-响应模型。 例如,当你…...
OpenAI SORA团队负责人 通往智能的方式 报告笔记
OpenAI SORA团队负责人 通往智能的方式 报告笔记 这个报告其实是2024年智源大会的主旨报告,OpenAI SORA和DALL-E团队负责人Aditya Ramesh给出的一段有关多模态大模型的报告。我去听了现场,感觉倍受启发,但是感觉很多并不能当场理解ÿ…...
006-Sleuth(Micrometer)+ZipKin分布式链路追踪
这里写目录标题 1 分布式链路追踪概述1.1 为什么会出现这个技术?需要解决哪些问题?1.2 在分布式与微服务场景下需要解决的问题 2 新一代Spring Cloud Sleuth:Micrometer2.1 官网重要提示2.1.1 新一代Sleuth2.1.2 官网2.1.3 说明2.1.3.1 老项目…...
AI模型:追求全能还是专精?-- 之6 语言复杂度类别(Category 0~3 类)和语言功能性类型(Type 0~Ⅲ 型)之2
Q17、我前面说过,语言复杂度的0~3级(Category 0~3)表示了语言的的上下文相关性 : 完全不相关, 单相关的 单词上下文, 双相关的句子上下文 全相关的文章上下文 。我准备翻译为 Context - irrelative /relati…...
20240907 每日AI必读资讯
大疆发布 DJI Neo 掌上 Vlog 无人机! - DJI Neo 是 DJI 迄今最轻、最小的无人机,无需遥控器,掌上起降即可轻松拍出主角大片… |135 克轻巧便携 丨零门槛掌上起降 丨AI 智能跟拍 ,一键成片 丨多种操控,丰富…...
深度学习基础--卷积基础模块
本节主要关注卷积神经网络发展过程中具有里程碑意义的基础模块,了解它们的原理和设计细节 1. 批归一化 在机器学习中,一般会假设模型的输入数据的分布是稳定的。如果这个假设不成立,即模型输入数据的分布发生变化,则称为协变量偏…...
视频智能分析打手机检测算法安防监控打手机检测算法应用场景、算法源码、算法模型介绍
随着智能手机的普及,手机已成为人们生活中不可或缺的一部分。然而,在某些场合,如驾驶、会议、学校课堂等,不当使用手机可能会导致安全隐患或干扰他人。因此,开发出一种能够准确识别并阻止不当使用手机的行为检测算法显…...
6.2图的存储及基本操作
6.2.1顺序存储 邻接矩阵法,用一个一维数组存储图中顶点信息,二维数组存储图中边的信息 无向图 1.无向图的邻接矩阵关于对角线对称,可采用压缩存储 2.边数为e,则邻接矩阵中1为2e; 3.第i行or 第i列非零元素之和恰好为顶点i的度数 4.判断是否有边用0,1 5. 有向图 1.关于对…...
Java语法全解析:掌握基本规则,打造稳固编程基础!
Java基本语法是编写Java程序的核心,它包括了数据类型、运算符、控制结构、类与对象等基本组成部分。这些语法要素共同构成了Java程序的基础框架,掌握它们是进行Java编程的前提。以下是Java基本语法的详细介绍: 数据类型 基本数据类型&#x…...
同时播放多个视频
介绍一款小众的视频播放器,之前有小伙伴找那种可以同时播放多个视频的软件,“恒硕加播放”可以做到这一点,功能不是太多,但是日常播放是足够了。 同时播放多个视频控制多个视频跳到指定进度同时暂停/播放/停止/静音/倍速浏览系统…...
伴奏提取消除人声如何操作?轻松几步玩转音乐世界
你是否梦想着独自演绎一曲,或是进行个性化的混音创作,却又希望摆脱原唱声音的干扰?那么,学会免费伴奏提取就显得尤为关键。 在这篇文章中,我将为你展示四种简单易学的方法,让你能够轻松地从歌曲中提取出伴…...
uniapp二维码生成
uniapp二维码生成 参考文档依赖引入代码html部分生成代码(vue3 hook)使用 参考文档 【博主:ChoneyLove】uniapp中生成二维码及解决微信小程序端问题总结 依赖引入 npm i uqrcodejs代码 html部分 <canvas type"2d" id"…...
Android UID 和 userID 以及 appID
我们知道Android 操作系统是基于Linux内核的,所以Android 的UID 是基于 Linux UID的。 Linux UID Linux 本身就是一个多用户操作系统,每一个用户都会有一个UID,不同UID 之间的资源访问是受限的。 其中,Linux的DAC权限模型&#…...
Kafka的三高设计原理
1.生产者缓存机制--高性能 生产者缓存机制的主要目的是将消息打包,减少网络IO频率 kafka生产者端存在消息累加器RecordAccumulator,它会对每个Partition维护一个双端队列,队列中消息到达一定数量后 或者 到达一定时间后,通过sen…...
生信圆桌x生信宝库:生物信息学资源与工具的终极指南
介绍 生物信息学作为现代生物科学的重要分支,涉及到大量的数据处理、分析和存储工作。随着领域的不断发展,各类生物信息学资源与工具也如雨后春笋般涌现。这些资源涵盖了从基因组数据、蛋白质结构到代谢路径的方方面面,极大地丰富了科研人员的…...
centos7 install rocketmq 宿主机快速搭建RocketMQ单机开发环境_centos7 单机部署rocketmq命令
2214 Jps 2071 BrokerStartup 1947 NamesrvStartup ### 第四步:发送消息测试消费着启动export NAMESRV_ADDRlocalhost:9876 ./tools.sh org.apache.rocketmq.example.quickstart.Consumer 发送测试消息export NAMESRV_ADDRlocalhost:9876 ./tools.sh org.apache.roc…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
