第2章 跳转 入栈出栈 pc lr的总结

跳转和返回总流程

从第一个函数a开始
当他需要进子函数b时,首先需要跳转
使用bl 汇编跳转

bl用于子程序跳转,要返回地址,返回地址存于LR中。当发生bl跳转前,会在寄存器 R14 (即LR)中保存当前PC-4,即bl跳转指令的下一条指令的地址。所以在返回时只要 MOV pc,lr 。
在这里插入图片描述

接下来进子函数b
入栈 push {r3-r5,lr}

等从子函数b出来时
出栈 pop {r3-r5,pc}

压lr出pc

为什么进子函数压lr,出子函数出栈到pc呢
因为上面提到。进子函数时执行Bl跳转,此时会在寄存器 R14 (即LR)中保存当前PC-4,即bl跳转指令的下一条指令的地址。所以在返回时只要 MOV pc,lr 。
就是说bl指令一起做了lr值更新的动作。
在这里插入图片描述

Lr的值的变化

在主函数a时,lr的值是a的返回地址。且lr的值在进a时已入栈。
此时要进子函数b,执行bl,此时,lr的值被更新为pc-4,变为了子函数b的返回地址.
此时函数a的返回地址,得靠出栈才能找到了。
接着进子函数b,一开始就入栈 push {r3-r5,lr}
此时在子函数b,lr的值是子函数b的返回地址,栈里也有子函数b的返回地址lr
等执行完子函数b要返回a时,
出栈 pop {r3-r5,pc}
将进函数时的lr给到pc,一执行pc,就等于跳转回函数a,接着刚才执行a的位置继续往下执行了。

Pc-4和不-4

函数调用

bl 跳转时,会在寄存器 LR(R14)中保存 pc-4 值,即 bl 跳转指令的下一条指令地址,所以返回时只要 MOV pc,lr。
这里pc-4是因为3级流水线。
也可以是入栈 push lr 出栈 pop pc。

就是说,首先3级流水线
在这里插入图片描述

从图中可以看出,一条汇编指令的运行有三个步骤,取指、译码、执行,当第一条汇编指令取指完成后,紧接着就是第二条指令的取指,然后第三条…如此嵌套
所以第一条指令开始执行时,译码是+4,PC值取码已经加了8
所以必须记住这个前提,在arm中,每次该指令执行时,其实这时的PC值是PC=PC+8
举例:
当执行的指令是1,译码是指令2,取码是当前pc是指令3。
当发生函数跳转bl时,最终从子函数返回时,我们希望继续执行的是指令2.
那指令2和pc指令3是什么关系?
Code是从小地址向大地址执行的,所以如果指令1的地址是0000,指令2地址是0004,指令3地址是0008.
所以pc-4=指令2.
所以bl设计为进子函数前执行Bl跳转,此时会在寄存器 R14 (即LR)中保存当前PC-4,此时已经-4了。
所有从子函数返回时,直接用lr的值给pc就可以。

进出中断

函数的跳转,是可预期的,在跳转前,把pc-4给lr就是子函数的返回地址。
中断和异常,都是不可预期的,在跳转前,没有pc-4就给了lr,所以中断和异常要在从中断返回函数时,lr-4给pc,跳到pc的地址
中断会返回时硬件lr-4,异常需要用户自己lr-4.
在这里插入图片描述

Thumb 和 arm指令及奇偶

后续提到cmbacktrace时,会出现通过% 2 != 0 来筛选栈里的各个pc
原因提到:
//又由于Cortex-M使用 thumb 指令,因此pc必须是奇数
这是怎么解释呢?

CM3 中的指令至少是半字(2字节)对齐的,所以 PC 的 LSB 读回 的总是0
无论是直接写 PC 的值还是使用分支指令,都必须保证加载到 PC 的数值是奇数(即 LSB=1),用以表明这是在 Thumb 状态下执行。倘若写了 0,则视为企图转入 ARM 模式,CM3 将产生一个 fault 异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值