当前位置: 首页 > news >正文

77、【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:栈指针和帧指针(上)

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

背景

接之前 blog
【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例
【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:叶子函数
分析了示例中体现的 caller-saved 和 callee-saved 规则,以及里面的叶子函数概念,下面来看下栈空间操作的一些具体细节

栈空间操作

还是回到之前那个示例

// main.c
static int add_func(int a, int b) {return (a + b);
}int main(void) {int c = add_func(1, 2);return 0;
}

汇编代码如下

	.cpu cortex-m4.arch armv7e-m.fpu softvfp.eabi_attribute 20, 1.eabi_attribute 21, 1.eabi_attribute 23, 3.eabi_attribute 24, 1.eabi_attribute 25, 1.eabi_attribute 26, 1.eabi_attribute 30, 6.eabi_attribute 34, 1.eabi_attribute 18, 4.file	"main.c".text.align	1.syntax unified.thumb.thumb_func.type	add_func, %function
add_func:@ args = 0, pretend = 0, frame = 8@ frame_needed = 1, uses_anonymous_args = 0@ link register save eliminated.push	{r7}sub	sp, sp, #12add	r7, sp, #0str	r0, [r7, #4]str	r1, [r7]ldr	r2, [r7, #4]ldr	r3, [r7]add	r3, r3, r2mov	r0, r3adds	r7, r7, #12mov	sp, r7@ sp neededpop	{r7}bx	lr.size	add_func, .-add_func.align	1.global	main.syntax unified.thumb.thumb_func.type	main, %function
main:@ args = 0, pretend = 0, frame = 8@ frame_needed = 1, uses_anonymous_args = 0push	{r7, lr}sub	sp, sp, #8add	r7, sp, #0movs	r1, #2movs	r0, #1bl	add_funcstr	r0, [r7, #4]movs	r3, #0mov	r0, r3adds	r7, r7, #8mov	sp, r7@ sp neededpop	{r7, pc}.size	main, .-main.ident	"GCC: (15:13.2.rel1-2) 13.2.1 20231009"

下面来看下栈空间操作的几个细节,首先是 main 函数这里的栈空间行为
在这里插入图片描述

LR 寄存器压栈

这个之前 blog 【OS】【Nuttx】【启动】caller-saved 和 callee-saved 示例:叶子函数 应该说得很详细了,因为调用了子函数,执行了跳转命令

...
bl add_func
str	r0, [r7, #4]
...
  • 这个指令会把返回地址(即 bl 下一条指令的地址,也就是 str r0, [r7, #4] 命令)写入 lr
  • 如果不保存原来的 lr 值,那么之前在 lr 中保存的内容就会被覆盖。
  • 为了能正确地从 main 返回到它的调用者(比如启动代码 _start 或操作系统),必须先把 lr 压栈保存

栈指针和帧指针

栈指针(Stack Pointer)和帧指针FP(Frame Pointer)是栈空间操作中两个非常重要的概念,它们各自承担不同的角色,

栈指针 SP

栈好理解,就是一个后进先出的数据结构,栈空间可以用来存储函数参数、返回地址、局部变量以及临时数据等,下面来看下官方文档 AAPCS 中,对栈的描述

在这里插入图片描述
这里有比较多的细节:

  • 栈是一块连续的内存区域,可以存储函数中的局部变量
  • 当调用函数所需的参数数量超过可用寄存器个数时,多出来的参数通过栈来传递(之前讲过的)
  • full-descending:full 意味着 SP 指向最后一个有效的栈元素,即高地址栈顶,descending 表示栈向低地址方向增长,SP 会变小
  • 栈在内存中有起始地址(高地址)和终止地址(低地址),但用户程序不需要知道这些地址是多少,这些地址信息一般由操作系统维护(比如 nuttx)
  • 栈可以是固定的,也可以动态扩展
  • 固定大小栈常见于嵌入式实时系统(比如 nuttx),预先分配好一定大小的空间
  • 动态扩展栈常见于复杂操作系统(比如 linux,windows),当栈快用完时,系统可以自动扩大栈空间

帧指针 FP

帧指针是函数调用时分配的栈空间基址,为了方便访问局部变量和参数,编译器通常会将 sp 的当前值保存到一个通用寄存器中,比如 r7,然后使用 r7 作为栈空间基址来访问栈上的数据,下面来看下官方文档描述
在这里插入图片描述
这里也有比较多的细节:

  • 调试器或操作系统在进行进行 backtrace(堆栈回溯)或 异常处理 时,需要知道每个函数调用是如何嵌套的,这就需要构建一条栈帧链,通俗点也叫调用链
  • 每个函数调用生成的栈帧中,都会在栈上保留一个帧记录,这个记录有两个字段,第一个字段(低地址)指向前一个函数调用的帧指针,第二个字段(高地址)保存当前函数被调用时 LR 寄存器的原始值(即返回地址),其实就是之前 blog 看到的这个操作
    在这里插入图片描述
  • 当某个帧记录的前一个帧指针地址为 0,说明这已经是最外层的函数,后面没有调用者了,比如 main 或启动代码
  • 编译器可以自由决定把帧记录放在栈帧的哪个位置(一般进入调用函数后先保存下帧记录)
  • 调用函数时,在新的帧记录完全构造完成之前,不能更新帧指针 FP,确保在中断或异常发生时,堆栈状态是完整的,不会出现部分构造的帧记录,导致回溯失败

先分析到这里,下篇再分析


文章转载自:
http://atomistics.aaladrg.cn
http://bioenvironmental.aaladrg.cn
http://bruges.aaladrg.cn
http://azinphosmethyl.aaladrg.cn
http://ceriferous.aaladrg.cn
http://calycoid.aaladrg.cn
http://bicoastal.aaladrg.cn
http://calumniate.aaladrg.cn
http://antiworld.aaladrg.cn
http://acrophobe.aaladrg.cn
http://beak.aaladrg.cn
http://assure.aaladrg.cn
http://barbeque.aaladrg.cn
http://astrological.aaladrg.cn
http://bray.aaladrg.cn
http://assertory.aaladrg.cn
http://anastomose.aaladrg.cn
http://aureole.aaladrg.cn
http://atraumatic.aaladrg.cn
http://canid.aaladrg.cn
http://alveolus.aaladrg.cn
http://bedpan.aaladrg.cn
http://chattel.aaladrg.cn
http://cerebral.aaladrg.cn
http://aestivation.aaladrg.cn
http://bayadere.aaladrg.cn
http://berley.aaladrg.cn
http://adnoun.aaladrg.cn
http://bahada.aaladrg.cn
http://absorptive.aaladrg.cn
http://www.dtcms.com/a/281691.html

相关文章:

  • Qt图形视图框架5-状态机框架
  • Springboot儿童认知图文辅助系统6yhkv(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 再见吧,Windows自带记事本,这个轻量级文本编辑器太香了
  • 基于mybatis的基础操作的思路
  • C++-linux系统编程 8.进程(二)exec函数族详解
  • 终端安全管理系统为什么需要使用,企业需要的桌面管理软件
  • X 射线探伤证考试核心:辐射安全基础知识点梳理
  • golang二级缓存示例
  • HC165并转串
  • js分支语句和循环语句
  • 如何写一份有效的技术简历?
  • vscode输出中文乱码问题的解决
  • QTableView鼠标双击先触发单击信号
  • Vue 常用的 ESLint 规则集
  • resources为什么是类的根目录
  • Linux 基本操作与服务器部署
  • 【高等数学】第三章 微分中值定理与导数的应用——第一节 不定积分的概念与性质
  • Android 图片压缩
  • 21.映射字典的值
  • 【强化学习】Reinforcement Learning基础概述
  • 如何进行 Docker 数据目录迁移
  • 三轴云台之深度学习算法篇
  • vscode配置运行完整C代码项目
  • QGIS新手教程9:字段计算器进阶用法与批量处理技巧
  • onecode 3.0 微内核引擎 基础注解驱动的速查手册(服务治理及通讯)
  • Altium Designer(AD)25软件下载及安装教程(7.9)
  • Axios方法完成图书管理页面完整版
  • Redis Desktop Manager(RDM)下载与安装使用教程
  • JavaScript中关于环境对象的拓展
  • 【Qt】 设计模式