我的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
这看起来更准确。 但是我认为保存的寄存器应该表明它们在闪存中的位置?那么,这有什么意义呢?
答案 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语言无法完成的,但又希望避免调用返回开销。但是,当您用汇编语言编写整个函数时,就没有理由使用它。