一、栈回溯
引入:调试程序时,经常会发生这类错误:
读写某个地址,程序报错;调用某个空函数,导致程序报错等等。
解决方法是,可以利用异常处理函数去打印出“发生错误瞬间”的所有寄存器地址
根据这些打印出来的地址信息,回溯发生错误的位置,并且需要知道位于调用链路。
1.栈回溯的原理
本质还是看汇编,C语言函数的返回地址,保存在栈里、
2.修改异常处理函数打印栈内容
位于硬件错误异常中 HardFault_Handler 都是死循环,并不能提供更多的信息,例如RT thread做的就很好,会有对应的寄存器信息 以及相关反馈。
整个流程大致为:发生错误
中断运行->硬件 原本LR先入栈 LR等于异常返回地址 保存程序状态(软件也要保存硬件保存剩余寄存器) 返回地址PC值->执行异常向量函数 打印栈
STMFD r0!, {r4 - r11} ; push r4 - r11 register STMFD r0!, {lr} ; push exec_return register
来自RTthread的函数
3.分析stack,找出函数调用关系
得到反汇编,例如fromelf --text -a -c --output=all.dis test\test.axf
通过分析反汇编以及地址
二、修改bin文件实现断点
引入:什么情况下需要打断点,在没有调试器,不方便使用调试器,想查看代码任意位置的状态
如何触发?修改原来的代码 改为未定义指令 并且修改异常处理函数打印寄存器和栈内容
需要得到.dis以及.bin 比如MDK是利用
fromelf --text -a -c --output=all.dis test\test.axf fromelf --bin --output=test.bin test\test.axf
本质是通过软断点的方式实现(把正确的指令改错),软断点的关键是利用特定的指令替换被调试程序中的某些指令,迫使程序在执行到该位置时产生异常。
硬断点就例如MDK中打的断点了有限制次数,处理器硬件支持的调试机制,例如位于flash中打断点,ARM架构的功能watchpower,并且有次数限制。
重新烧写