在Hardfault期间,ARM Cortex-M0堆栈寄存器是否保存在$ psp或$ msp上?

时间:2018-08-29 22:55:03

标签: arm cortex-m fault thumb stackframe

我的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;行,表明这是LR的{​​{1}}值。值EXC_RETURN对我来说表示在发生硬故障时正在使用过程堆栈。

如果打印0xfffffffd值,则会得到以下信息:

$psp

如果打印gdb $ p/x $psp $91 = 0x20000328 值,则会得到以下信息:

$msp

您可以清楚地看到gdb $ p/x $msp $92 = 0x20000100 指向应该保存的寄存器位于的堆栈顶部。 这不是说主堆栈具有ARM内核推送到堆栈的已保存寄存器吗?

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

$msp

它是空的...

现在,如果我从gdb $ x/8xw 0x20000100 0x20000100 <__process_stack_base__>: 0x55555555 0x55555555 0x55555555 0x55555555 0x20000110 <__process_stack_base__+16>: 0x55555555 0x55555555 0x55555555 0x55555555 地址开始打印内存内容,则会得到以下信息:

$psp

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

1 个答案:

答案 0 :(得分:3)

old_timer在您的问题下的评论都是正确的。寄存器将在异常条目(无论当时是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语言无法完成的,但又希望避免调用返回开销。但是,当您用汇编语言编写整个函数时,就没有理由使用它。