ARM开发中几个常见的寄存器详解

💡 原文中文,约5700字,阅读约需14分钟。
📝

内容提要

本文介绍了ARM的几个特殊寄存器:FP、SP和LR,它们分别用于栈回溯、指向当前栈位置和保存函数返回地址。文章还讨论了与栈帧相关的编译选项和不同处理器架构下SP的作用。

🔎

延伸解读

FP 与编译选项:调试与性能的权衡

文章指出,gcc 的 -fomit-frame-pointer 选项会忽略 FP 寄存器,从而节省保存、设置和恢复栈帧的指令,并腾出一个通用寄存器。但这也使得在某些机器上调试变得不可能。ARM CC5 编译器同样默认不生成 FP,可通过 --use_frame_pointer 开启。是否保留 FP,需在调试便利性与指令效率之间权衡。

不同架构下 SP 的差异与隔离设计

SP 的实现因处理器架构而异。Cortex-M3/4 区分主栈指针 MSP 和线程栈指针 PSP,通过 CONTROL 寄存器切换,使 OS 与应用程序栈隔离,应用出错不影响 OS。Cortex-R5 多种工作模式各有独立栈。Cortex-A53 在 AArch64 下则有 SP_EL0 至 SP_EL3,通过 SPSel 选择,且用作基址时低四位必须为 0,否则触发非对齐异常。

LR 的保存机制与跳转指令的返回语义

LR 保存函数返回地址,但调用嵌套时会被覆盖,因此子程序需在开头将 LR 压栈。文章以 A→B→C 为例:B 调用 C 前保存 LR,C 返回后 B 从栈恢复 LR 再返回 A。跳转指令中,B 和 BX 不保存返回地址,BL 和 BLX 会更新 LR,BR 不链接,BLR 链接。理解这些差异有助于分析调用关系和异常处理。

❓

Q&A

ARM架构中FP、SP、LR寄存器各自的作用是什么?

FP(Frame Pointer)指向栈帧顶部,记录函数调用时栈的起始位置,主要用于栈回溯;SP(Stack Pointer)指向当前栈位置,用于栈的存取操作;LR(Link Register)保存函数返回地址,支持程序调用返回。

gcc编译器的-fomit-frame-pointer选项有什么影响?

该选项让编译器在不需要栈帧的函数中不保留帧指针,从而节省保存、设置和恢复帧指针的指令,并腾出一个通用寄存器;但在某些机器上会导致无法调试。高版本gcc(约4.8起)默认不启用帧指针。

FP寄存器是如何实现栈回溯(backtrace)的?

函数调用时,FP、LR和参数等被压栈,每个FP指向上一级栈帧的顶部。通过当前FP找到保存的LR,再根据LR(通常减4得到调用指令地址)确定上一级函数,逐级回溯即可还原调用关系。

不同ARM处理器架构中SP指针有哪些差异?

Cortex-M3/4有主栈指针MSP和线程栈指针PSP,通过CONTROL寄存器选择;Cortex-R5有多种工作模式,每种模式有独立栈指针;Cortex-A53(ARMv8-A)在EL1~EL3有各自的SP_EL0~SP_EL3,通过SPSel选择,且SP用作基址时低4位必须为0,否则产生对齐异常。

ARM程序调用时LR如何保存和恢复返回地址?

当函数A调用B时,LR保存A中的返回地址;若B还要调用C,LR会被覆盖,因此B在开始时将LR压栈,调用C后LR保存B中的返回地址,C返回后B从栈中恢复LR,从而正确返回A。

ARM跳转指令B、BL、BX、BLX、BR、BLR有什么区别?

B:直接跳转,不改变LR,不返回;BL:跳转并将返回地址保存到LR;BX:跳转到寄存器或标签指定地址,可能切换指令集,不保存返回地址;BLX:跳转并保存返回地址,可能切换指令集;BR:跳转到寄存器地址,不保存返回地址;BLR:跳转到寄存器地址并保存返回地址。

🏷️

标签

➡️

继续阅读