73、【OS】【Nuttx】【启动】深入理解 caller-saved 和 callee-saved(上)
【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
接上篇 blog
【OS】【Nuttx】【启动】栈溢出保护:caller-saved 和 callee-saved
简单分析了下 r10 为什么能保存栈底地址,下面继续来分析下 caller-saved 和 callee-saved
caller-saved 和 callee-saved
首先是 cortex-m 的寄存器结构
用户手册《Arm Cortex-M4 Processor Technical Reference Manual》里虽然只提到了 R13(SP),R14(LR),R15(PC)作为特殊用途寄存器,R0-R12 没有特殊用途,作为通用寄存器使用,但实际上,在使用过程中,R0-R12 之间还是有显著差别的,这种差别来源于比如函数调用等过程中,R0-R12 寄存器之间的一些使用约定
之前 blog 【OS】【Nuttx】【启动】栈溢出保护:caller-saved 和 callee-saved 提到
- r0–r3:作为临时寄存器,约定用来将参数值传递给被调用函数,或者从被调用函数中返回结果值
- r4-r12:约定用来保存函数内的局部变量或者长期使用的中间值
首先从这种约定的使用方式,又衍生出两个概念:易失性寄存器(volatile registers)和非易失性寄存器(non-volatile registers),下面来看下这两个概念
(非)易失性存储器?
在讲易失性寄存器(volatile registers)和非易失性寄存器(non-volatile registers)之前,先要区分开易失性存储器(volatile memory)和非易失性存储器(non-volatile memory)
通常讨论易失性存储器(volatile memory)和非易失性存储器(non-volatile memory)时,主要是讲掉电是否丢失,这取决于存储器的介质
- 非易失性存储器(non-volatile memory):比如 flash,rom,掉电后数据不会丢失
- 易失性存储器(volatile memory):比如 ram,掉电后数据会丢失
对于存储器来说,这是硬件介质上导致的差别,而对于寄存器里的易失性概念,更多的是寄存器上的使用约定,比如在 arm 的 AAPCS(ARM Architecture Procedure Call Standard) 或 x86 的调用规范中,所以两个词的意思和语境完全不同,需要理解这一点
volatile register
【易失性寄存器】:这些寄存器的内容在函数调用过程中可能被破坏,有几个点是反复强调过的
- 如果在一个函数里用了这些寄存器,并保存了某些值,然后调用了另一个子函数(比如 bl sub_function),那么等子函数执行完回来时,这些易失性寄存器的值可能就没了
- 如果想保留这些易失性寄存器的值,就需要把它们保存到内存的栈上
- 可以把这些寄存器看作是临时草稿纸,随时可能会被别人擦掉内容
- 在 arm 上,常见的 volatile 寄存器,比如 r0–r3,可以用来传入参数和传出返回值等临时数据
示意图如下
non-volatile register
【非易失性寄存器】:这些寄存器的内容在函数调用过程中必须保持不变,这里需要正确理解保持不变的含义
- 保持不变不是说在函数调用时,子程序不能使用这些寄存器,而是说如果子程序使用了这些寄存器并修改了它们的值,那子程序就需要负责在函数返回前,把这些寄存器恢复成原来的值
- 可以把这些寄存器看作是私人笔记本,别人借走后,不能随便改里面的内容(如果改了,最后还笔记本的时候,要恢复原状,让人看不出用过的痕迹)
- 在 arm 上,常见的 non-volatile 寄存器,比如 r4–r8,r10-r11,可以用来保存函数内的局部变量或者长期使用的中间值
具体过程看下面这个示意图
先分析到这,后面继续