Hard_Fault时进行栈回溯
基于M3、M4内核,手动分析栈。
原理:
调试程序时,经常发生这类错误:读写某个地址,导致程序崩溃;调用某个空函数,导致程序崩溃。在异常处理函数中,可以打印出”发生错误瞬间”的所有寄存器。我们调试时,可以根据这些寄存器,知道发生错误的位置。
修改HardFault_Handler
-
首先根据汇编代码得到当前使用的栈指针(MSP/PSP),在代码发生异常后,硬件会自动把一些寄存器的值压入栈中(如下图),然后给LR = EXC_RETURN,其值的bit2位用来判断是MSP栈指针,还是PSP栈指针。
-
然后跳转到异常向量,根据栈指针打印出栈中的数据。
;HardFault_Handler
; PROC
; EXPORT HardFault_Handler [WEAK]
; B .
; ENDP
IMPORT rt_hw_hard_fault_exception
EXPORT HardFault_Handler
HardFault_Handler PROC
; get current context
TST lr, #0x04 ; if(!EXC_RETURN[2])
ITE EQ
MRSEQ r0, msp ; [2]=0 ==> Z=1, get fault context from handler.
MRSNE r0, psp ; [2]=1 ==> Z=0, get fault context from thread.
STMFD r0!, {r4 - r11} ; push r4 - r11 register
STMFD r0!, {lr} ; push exec_return register
TST lr, #0x04 ; if(!EXC_RETURN[2])
ITE EQ
MSREQ msp, r0 ; [2]=0 ==> Z=1, update stack pointer to MSP.
MSRNE psp, r0 ; [2]=1 ==> Z=0, update stack pointer to PSP.
PUSH {lr}
BL rt_hw_hard_fault_exception
POP {lr}
ORR lr, lr, #0x04
BX lr
ENDP
tst:逻辑处理指令,用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。当前运算结果为1,则Z=0;当前运算结果为0,则Z=1
tst r0 , #0x2
//进行and运算,如果bit_2为1,zero== 0,如果bit_2为0,则zero==1,即该指令测试bit_2是否为0
打印寄存器和栈
#define rt_uint32_t unsigned int
struct exception_info
{
rt_uint32_t exc_return;
rt_uint32_t r4;
rt_uint32_t r5;
rt_uint32_t r6;
rt_uint32_t r7;
rt_uint32_t r8;
rt_uint32_t r9;
rt_uint32_t r10;
rt_uint32_t r11;
rt_uint32_t r0;
rt_uint32_t r1;
rt_uint32_t r2;
rt_uint32_t r3;
rt_uint32_t r12;
rt_uint32_t lr;
rt_uint32_t pc;
rt_uint32_t psr;
};
/*
* fault exception handler
*/
void rt_hw_hard_fault_exception(struct exception_info * exception_info)
{
unsigned int *app_sp;
int i;
app_sp = (unsigned int *)(exception_info + 1); /* context + 16*4 */
printf("psr: 0x%08x\r\n", exception_info->psr);
printf("r00: 0x%08x\r\n", exception_info->r0);
printf("r01: 0x%08x\r\n", exception_info->r1);
printf("r02: 0x%08x\r\n", exception_info->r2);
printf("r03: 0x%08x\r\n", exception_info->r3);
printf("r04: 0x%08x\r\n", exception_info->r4);
printf("r05: 0x%08x\r\n", exception_info->r5);
printf("r06: 0x%08x\r\n", exception_info->r6);
printf("r07: 0x%08x\r\n", exception_info->r7);
printf("r08: 0x%08x\r\n", exception_info->r8);
printf("r09: 0x%08x\r\n", exception_info->r9);
printf("r10: 0x%08x\r\n", exception_info->r10);
printf("r11: 0x%08x\r\n", exception_info->r11);
printf("r12: 0x%08x\r\n", exception_info->r12);
printf(" lr: 0x%08x\r\n", exception_info->lr);
printf(" pc: 0x%08x\r\n", exception_info->pc);
printf("stacks: \r\n");
i = 0;
for (i = 0; i < 1024; )
{
printf("%08x ", *app_sp);
app_sp++;
i++;
if (i % 16 == 0)
printf("\r\n");
}
printf("\r\n");
while (1);
}
分析栈,找出函数调用关系
要分析栈,需要得到程序的反汇编码
fromelf --text -a -c --output=all.dis F103_Moduel\F103_Moduel.axf
在分析栈的时候注意:确定返回地址LR后,需要将bit0清零,才能在反汇编文件中搜到指令。因为bit0是当前使用的是何种指令集。