LR 在函数中的用法 return

LR usage in function return

我正在 STM32F427 上开发嵌入式应用程序。此 MCU 使用 ARM Cortex M4F 内核。

我目前正在开发一个调试工具,它在崩溃期间保存 CPU 状态的快照以供以后分析。具体来说,我对调用错误处理程序之前执行的最后一条指令感兴趣。

最初我虽然可以只记录 LR 的值,因为在处理程序 returns 之后这将成为新的 PC。但我意识到情况并非总是如此。

我发现在某些情况下我的处理程序确实有效。 编译器调用函数:

bl      0x60238

然后,函数 returns 具有:

bx      lr

正如我所料。

但是,在其他一些情况下,编译器调用函数时使用:

bl      0x60250

然后函数执行:

push    {r7, lr}

然后 returns 与:

pop     {r7, pc}

在后一种情况下,由于 LR 可能用于另一个嵌套函数调用,因此内容被覆盖,我丢失了所需的信息。

为什么 GCC 会生成这样的代码,为什么为函数调用生成的代码并不总是相同的? 综上所述,请问还有其他方法可以达到我想要的效果吗?


编辑 在我的例子中,"system crash" 可能有很多原因。这就是为什么我试图读取实际的寄存器内容,为我的问题提供一个通用的解决方案。相同的情况可能是:

编译器可以进行多种优化以节省代码大小/提高速度。这些优化中的 class 围绕函数是否 leaf functions. You could go through the list of compiler flag optimizations and try to disable some of the optimizations that will impact how branches are generated (such -fno-optimize-sibling-calls) 展开,但这样会影响性能并不必要地增加代码大小。

正如其他人所指出的,"crash" 的意思也很重要。如果您的代码中有断言并且希望在它们被命中时保存状态,则提供了一个解决方案 here 可以捕获 pc 和 lr。

Cortex-M4F 也可以体验硬件 "fault" 许多不同的场景(错误的总线访问、未对齐的内存访问、除以 0 等)。发生这种情况时,将调用中断处理程序,您 cannot 只需阅读 $lr$pc 即可找出导致崩溃的原因。 section in this article 介绍了如何检测代码以恢复最初触发错误的指令。

请注意,即使您有 $pc$lr,仍然很难查明究竟是什么调用链导致了崩溃。在这些情况下,您希望从活动堆栈中保存几百个字节以及寄存器状态。使用 ELF 中发出的调试信息(只要您使用 -g 进行编译),您可以恢复具有导致崩溃的多个帧的堆栈跟踪。