一、栈回溯
1.1 栈回溯的原理
调试程序时,经常发生这类错误:
1.读写某个地址,导致程序崩溃
2.调用某个空函数,导致程序崩溃
在异常处理函数中,可以打印出”发生错误瞬间”的所有寄存器。
我们调试时,可以根据这些寄存器,知道发生错误的位置。
但是,光知道发生错误的位置还不够! 比如,根据打印信息知道在C函数里发生错误,但是你无法确定是在哪个调用链上出错:
1.A > B > C时出错?
2.D > C时出错?
C语言函数的返回地址,保存在栈里:
栈内容示例:
1.2 修改异常处理函数打印栈内容
发生错误时,异常函数如何处理?
发生错误时,栈的使用情况:
修改HardFault_Handler:
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
打印寄存器和栈:
1.3 分析栈找出函数调用关系
要分析栈,需要得到程序的反汇编码:
fromelf --text -a -c --output=all.dis F103_Moduel\F103_Moduel.axf
根据PC值在反汇编文件中找到发生错误的位置:
发现函数C太简单,它根本没有使用栈,函数C执行完,直接返回到LR。
在打印的信息中,LR=0x08000383,去掉bit0,就是:0x08000382
根据这个值,在反汇编文件中找到函数C的调用者,是函数B:
在函数B的入口处,发现使用了8字节的栈,并且保存了LR:
分析函数B的栈:确定返回地址LR=0x08000355,bit0清零后就是0x08000354:
在反汇编中搜0x08000354 ,确定函数B的返回地址是函数A:
在函数A的入口处,发现使用了8字节的栈,并且保存了LR:
分析函数A的栈:确定返回地址LR=0x08001d99,bit0清零后就是0x08001d98。
这是TestDebug函数。
最终,调用链为:
二、修改bin文件实现断点
点击深入解析栈回溯技术:如何通过异常处理精准定位程序崩溃点查看全文