ARM Coretex STM32的HardFault_Handler在发生崩溃时只能获取几个寄存器的值,r0、r1、r2、r3、lr、pc和xPSR。但是堆栈中没有FP和SP。因此,我无法解开堆栈。
有没有解决方案?非常感谢。
[更新] 按照网上的指导,让ARMGCC(Keil uvision IDE)通过添加编译选项"--use_frame_pointer"生成FP,但是我在堆栈中找不到FP。我是一个真正的新手。以下是我的演示代码:
我这里有两个问题:
1. 我不确定FP(r11)在堆栈中的索引位置,或者它是否被推入堆栈。我假设它在r12之前,因为我比较了添加选项"--use_frame_pointer"之前和之后的汇编源代码。我还比较了从Hard_Fault_Handler读取的值,似乎r11不在堆栈中。因为我读取的r11地址指向的地方不是我的代码。 [更新] 我已确认FP被推入堆栈。第二个问题仍需回答。
使用选项“--use_frame_pointer”
/* 故障处理程序的实现调用一个名为Hard_Fault_Handler()的函数。 */
[更新4] 终于,我成功解决了。我的解决方案是:
注意:要访问r11,我们必须使用嵌入式汇编器,请参考这里,这花费了我很多时间来弄清楚。
[更新] 按照网上的指导,让ARMGCC(Keil uvision IDE)通过添加编译选项"--use_frame_pointer"生成FP,但是我在堆栈中找不到FP。我是一个真正的新手。以下是我的演示代码:
int test2(int i, int j)
{
return i/j;
}
int main()
{
SCB->CCR |= 0x10;
int a = 10;
int b = 0;
int c;
c = test2(a,b);
}
enum { r0 = 0, r1, r2, r3, r11, r12, lr, pc, psr};
void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
uint32_t r0_val = faultStackAddress[r0];
uint32_t r1_val = faultStackAddress[r1];
uint32_t r2_val = faultStackAddress[r2];
uint32_t r3_val = faultStackAddress[r3];
uint32_t r12_val = faultStackAddress[r12];
uint32_t r11_val = faultStackAddress[r11];
uint32_t lr_val = faultStackAddress[lr];
uint32_t pc_val = faultStackAddress[pc];
uint32_t psr_val = faultStackAddress[psr];
}
我这里有两个问题:
1. 我不确定FP(r11)在堆栈中的索引位置,或者它是否被推入堆栈。我假设它在r12之前,因为我比较了添加选项"--use_frame_pointer"之前和之后的汇编源代码。我还比较了从Hard_Fault_Handler读取的值,似乎r11不在堆栈中。因为我读取的r11地址指向的地方不是我的代码。 [更新] 我已确认FP被推入堆栈。第二个问题仍需回答。
请参考下面的代码片段:
没有使用选项"--use_frame_pointer"
test2 PROC
MOVS r0,#3
BX lr
ENDP
main PROC
PUSH {lr}
MOVS r0,#0
BL test2
MOVS r0,#0
POP {pc}
ENDP
使用选项“--use_frame_pointer”
test2 PROC
PUSH {r11,lr}
ADD r11,sp,#4
MOVS r0,#3
MOV sp,r11
SUB sp,sp,#4
POP {r11,pc}
ENDP
main PROC
PUSH {r11,lr}
ADD r11,sp,#4
MOVS r0,#0
BL test2
MOVS r0,#0
MOV sp,r11
SUB sp,sp,#4
POP {r11,pc}
ENDP
2. 似乎FP不在Hard_Fault_Handler()的输入参数faultStackAddress中,我应该从哪里获取调用者的FP以解开堆栈?
[再次更新]
现在我明白了最后的FP(r11)没有存储在堆栈中。我只需要读取r11寄存器的值,然后就可以解开整个堆栈。
所以现在我的最终问题是如何使用C内联汇编来读取它。 我尝试了下面的代码,但未能根据 http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0472f/Cihfhjhg.html 的参考读取正确的值。
volatile int top_fp;
__asm
{
mov top_fp, r11
}
r11的值为0x20009DCC top_fp的值为0x00000004
[更新3] 以下是我的整个代码。
int test5(int i, int j, int k)
{
char a[128] = {0} ;
a[0] = 'a';
return i/j;
}
int test2(int i, int j)
{
char a[18] = {0} ;
a[0] = 'a';
return test5(i, j, 0);
}
int main()
{
SCB->CCR |= 0x10;
int a = 10;
int b = 0;
int c;
c = test2(a,b); //create a divide by zero crash
}
/* 故障处理程序的实现调用一个名为Hard_Fault_Handler()的函数。 */
#if defined(__CC_ARM)
__asm void HardFault_Handler(void)
{
TST lr, #4
ITE EQ
MRSEQ r0, MSP
MRSNE r0, PSP
B __cpp(Hard_Fault_Handler)
}
#else
void HardFault_Handler(void)
{
__asm("TST lr, #4");
__asm("ITE EQ");
__asm("MRSEQ r0, MSP");
__asm("MRSNE r0, PSP");
__asm("B Hard_Fault_Handler");
}
#endif
void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
volatile int top_fp;
__asm
{
mov top_fp, r11
}
//TODO: use top_fp to unwind the whole stack.
}
[更新4] 终于,我成功解决了。我的解决方案是:
注意:要访问r11,我们必须使用嵌入式汇编器,请参考这里,这花费了我很多时间来弄清楚。
//we have to use embedded assembler.
__asm int getRegisterR11()
{
mov r0,r11
BX LR
}
//call it from Hard_Fault_Handler function.
/*
Function call stack frame:
FP1(r11) -> | lr |(High Address)
| FP2|(prev FP)
| ...|
Current FP(r11) ->| lr |
| FP1|(prev FP)
| ...|(Low Address)
With FP, we can access lr(link register) which is the address to return when the current functions returns(where you were).
Then (current FP - 1) points to prev FP.
Thus we can unwind the stack.
*/
void unwindBacktrace(uint32_t topFp, uint16_t* backtrace)
{
uint32_t nextFp = topFp;
int j = 0;
//#define BACK_TRACE_DEPTH 5
//loop backtrace using FP(r11), save lr into an uint16_t array.
for(int i = 0; i < BACK_TRACE_DEPTH; i++)
{
uint32_t lr = *((uint32_t*)nextFp);
if ((lr >= 0x08000000) && (lr <= 0x08FFFFFF))
{
backtrace[j*2] = LOW_16_BITS(lr);
backtrace[j*2 + 1] = HIGH_16_BITS(lr);
j += 1;
}
nextFp = *((uint32_t*)nextFp - 1);
if (nextFp == 0)
{
break;
}
}
}
#if defined(__CC_ARM)
__asm void HardFault_Handler(void)
{
TST lr, #4
ITE EQ
MRSEQ r0, MSP
MRSNE r0, PSP
B __cpp(Hard_Fault_Handler)
}
#else
void HardFault_Handler(void)
{
__asm("TST lr, #4");
__asm("ITE EQ");
__asm("MRSEQ r0, MSP");
__asm("MRSNE r0, PSP");
__asm("B Hard_Fault_Handler");
}
#endif
void Hard_Fault_Handler(uint32_t *faultStackAddress)
{
//get back trace
int topFp = getRegisterR11();
unwindBacktrace(topFp, persistentData.faultStack.back_trace);
}