【问题标题】:Are ARM Cortex-M0 Stacking Registers Saved On $psp or $msp During Hardfault?ARM Cortex-M0 堆栈寄存器是否在硬故障期间保存在 $psp 或 $msp 上?
【发布时间】:2023-04-11 11:17:01
【问题描述】:

我的 Cortex-M0 出现硬故障,因此我正在尝试调试它。我正在尝试打印发生硬故障时推送到堆栈的 ARM 内核寄存器的内容。

这是我的基本汇编代码:

__attribute__((naked)) void HardFaultVector(void) {
    asm volatile(
        // check LR to see whether the process stack or the main stack was being used at time of exception.
        "mov r2, lr\n"
        "mov r3, #0x4\n"
        "tst r2, r3\n"
        "beq _MSP\n"

        //process stack was being used.
        "_PSP:\n"
        "mrs r0, psp\n"
        "b _END\n"

        //main stack was being used.
        "_MSP:\n"
        "mrs r0, msp\n"
        "b _END\n"

        "_END:\n"
        "b fault_handler\n"
    );  
}  

函数fault_handler 将打印被推送到进程堆栈或主堆栈的堆栈帧的内容。这是我的问题:

当我打印应该有已保存寄存器的堆栈帧的内容时,我看到的是:

Stack frame at 0x20000120:
 pc = 0xfffffffd; saved pc 0x55555554
 called by frame at 0x20000120, caller of frame at 0x20000100
 Arglist at unknown address.
 Locals at unknown address, Previous frame's sp is 0x20000120
 Saved registers:
  r0 at 0x20000100, r1 at 0x20000104, r2 at 0x20000108, r3 at 0x2000010c, r12 at 0x20000110, lr at 0x20000114, pc at 0x20000118, xPSR at 0x2000011c

您可以看到保存的寄存器,这些是发生硬故障时由 ARM 内核推送的寄存器。您还可以看到pc = 0xfffffffd; 行,它表明这是LREXC_RETURN 值。值0xfffffffd 向我表明在发生硬故障时正在使用进程堆栈。

如果我打印 $psp 值,我会得到以下信息:

gdb $ p/x $psp
$91 = 0x20000328

如果我打印 $msp 值,我会得到以下信息:

gdb $ p/x $msp
$92 = 0x20000100

您可以清楚地看到$msp 指向的堆栈顶部应该是保存的寄存器所在的位置。 这不是说主堆栈中保存了 ARM 内核压入堆栈的寄存器吗?

如果我打印内存内容,从$msp 地址开始,我会得到以下信息:

gdb $ x/8xw 0x20000100
0x20000100 <__process_stack_base__>:    0x55555555  0x55555555  0x55555555  0x55555555
0x20000110 <__process_stack_base__+16>: 0x55555555  0x55555555  0x55555555  0x55555555

它是空的......

现在,如果我打印内存内容,从$psp 地址开始,我会得到以下信息:

gdb $ x/8xw 0x20000328
0x20000328 <__process_stack_base__+552>:    0x20000860  0x00000054  0x00000054  0x20000408
0x20000338 <__process_stack_base__+568>:    0x20000828  0x08001615  0x1ad10800  0x20000000

这看起来更准确。 但我认为保存的寄存器应该指示它们在闪存中的位置?那么这有什么意义呢?

【问题讨论】:

  • 在指令流抢占时,硬件将上下文状态保存到一个 SP 寄存器指向的堆栈中,请参阅第 B1-186 页的 SP 寄存器。使用的堆栈取决于异常发生时处理器的模式。
  • 在异常进入和退出的伪代码序列中描述了用于异常进入和退出的堆栈指针,请参见第 B1-196 页的异常进入行为和第 B1-198 页的异常返回行为更多细节。 SP_main 在重置时被选中并初始化,请参阅第 B1-195 页的重置行为。
  • 为什么要和msp搞混?
  • 你应该从真正的汇编语言开始,这样你就不会让任何编译器弄乱你正在尝试做的事情。
  • 嘿@old_timer!再次感谢!我假设您在提及页码时指的是 ARMvxM 参考手册。我查看了那里的伪代码,并验证了在异常发生期间正在使用进程堆栈,并且进程堆栈是推送核心寄存器的位置。我不一定要弄乱 msp,我只是好奇为什么推送的寄存器具有与 msp 相关联的地址而不与 psp 相关联。你知道为什么会这样吗?

标签: arm cortex-m fault thumb stack-frame


【解决方案1】:

old_timer在你的问题下的cmets都是正确的。寄存器将在异常进入时被推送到活动堆栈,无论当时是 PSP 还是 MSP。默认情况下,所有代码都使用主堆栈 (MSP),但如果您使用的不是完整的裸机,则很可能您使用的任何内核都已将线程模式切换为使用进程堆栈 (PSP)。

您的大多数调查表明 PSP 正在使用中,您对 PSP 和 MSP 的记忆几乎是无可争辩的。您拥有 MSP 的唯一证据是 fault_handler 函数的结果,您尚未发布其源代码;所以我的第一个猜测是这个功能在某种程度上被破坏了。

请记住,进入 HardFault 处理程序的一个常见原因是另一个异常处理程序导致了异常。在内存损坏的情况下很容易发生这种情况。在这些情况下(假设线程模式使用 PSP)CPU 将首先进入处理程序模式以响应原始异常,将r0-r3,r12,lr,pc,psr 推送到进程堆栈。它将开始执行原始异常处理程序,然后再次出错,在进入 HardFault 处理程序时将r0-r3,r12,lr,pc,psr 推送到 main 堆栈。经常有一些事情要做。

old_timer 还提到使用真正的汇编语言,我也同意。即使((naked)) 属性应该删除序言和尾声(它们之间的大部分可能的“编译器”),如果它是用纯汇编语言编写的,您的代码将更具可读性。内联汇编语言有它的用途,例如,如果你想做一些在 C 中无法做到的非常低级的事情,但又想避免调用返回开销。但是如果你的整个函数都是用汇编语言编写的,就没有理由使用它了。

【讨论】:

    猜你喜欢
    • 2021-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多