STM32 Hardfault异常调试-笔记
STM32 Hardfault异常调试-笔记
- 一、基础寄存器与栈结构
- 1.1 核心寄存器作用
- 1.2 压栈基础知识
- 1.3 PC指针是什么?
- 1.3 SP指针是什么?
- 1.4实际演练
在嵌入式开发中,Hardfault异常是常见的问题之一。当程序访问非法地址或执行非法指令时,系统会触发Hardfault中断。此时,如何通过栈地址快速定位问题根源是调试的关键,如何通过栈的地址找到我们的问题发生的地方。
一、基础寄存器与栈结构
1.1 核心寄存器作用
R0-R12:通用寄存器,用于存储临时数据和运算结果。
R13(SP):栈指针(Stack Pointer),指向当前栈顶。STM32有主栈(MSP)和进程栈(PSP)两种模式。
R14(LR):链接寄存器(Link Register),保存函数调用返回地址。(它保存的是什么呢?保存的是程序下一个要运行的地址,就是下一条要运行的指令。)
PC:程序计数器(Program Counter),指向当前执行的指令地址。
1.2 压栈基础知识
假如说我们的函数,我们的func_A如果调用了func_B。如果调用了func_B那么func_A在调用它之前,会把自己要运行的下一条这条return的这个地址放到R14里面。
它调用完func_B以后,R14指向func_A的return处,让它继续运行。这个就是我们的基础知识。
1.3 PC指针是什么?
PC指向的是我们当前程序所运行到的地址,PC指令装填我们当前现在即将要运行的这条指令。
1.3 SP指针是什么?
SP就是我们的栈顶指针,它指向哪里呢?
从上面的图中可以看出栈是从下依次往上,地址逐渐减小,栈指针就指向的就是这个栈顶的。
1.4实际演练
调试一步一步走这个时候它PC指针一直在动,这个栈指针没动。我们再走一步。到达调用func_A函数。
看他把这里的0X1234678装载到R4寄存器中,R4当中这个时候它func_A,主要func_A它即将要进行一个调用。
这个时候你看这里的这是他的指令汇编指令,他现在知道了这一条,这一条是什么?把0XA123装到R1里面
0XA123在这里,比如说他先去把他在调用这个函数的时候,他先去把这个函数的第二个参数,他先装进了自己的R1寄存器。
#0xA123装进了我们的R1寄存器,0x12345678这里的R4值就把我们这里的R4就放到了R0。
R4就放到了R0。
为什么把R4放?因为R4之前在"uint32_t var_main.0x12345678;“载入变量了,他再作为参数传进到"func_A”。
R0是什么呢?R0就是它的另一个参数,所以它在调用这个函数的时候,会预先把这里传入的两个参数压到R0和R1寄存器
调试下一步进入函数
跳转进来,可以看到汇编的进行了一个压栈,“PUSH”。
方便我运行完function a以后,我还能知道我退出function a以后,我是要运行这一段指令“”
现在看下R13(SP)寄存器 地址为“”。
往下走单步调试,R13(SP)寄存器变成 。它是不是一次前进了16个字节
打开一个Memory的窗口。输入地址0x200005A8
这里的地址从大依次变小,里面存了一个数“12345678”。这个地方好像就把之前存在这里R4的这个是不是压进来了。
把R4到R6都给压进来了,然后还把LR给压进来了。
那么LR它压到哪里了呢?LR是什么?
LR其实就指向的是我们调用function a的,下一句话
我们在往Memory的窗口看,他压栈的过程当中是先压这个LR,栈从右边往左写,地址逐渐减小。
所以LR就是0x08001653。
如果我想看这个地址是哪条指令,我们怎么办呢?
在汇编窗随便点击右键,选择“show disassembly at address”
跳转到0x08001653处,但是呢?显示不出来是为什么呢?
因为LR
如果你想跳到准确的下一个执行的地址,汇编不会完整的显示。但是可以推断出来
然后我们再往下调用,调试进行下一步,跳到function b函数处了
进行压栈 。R13的地址变成“0x20000598”。原来的是“0x200005A8”。对比正好又压进了16字节
最后就是它的LR寄存器 “0x08001601”。
跳转
上下滚动一下鼠标发现就在这里。那是不是这一句话就是我们function a调用完function b会回到这里
这样就实现了我们通过栈来追溯回收信息
以这个为例子产生have fault的过程。
“0xA0000000”在STM3F4当中,我们访问的这里这个地址,你看我们让它指向了一个错误的地址。
它是不存在的,即使存在也是系统保留。如果它指向这里,直接会造成那个地址非法访问。
假如当我们不知道在哪个位置发生错误的时候,程序全速运行。程序突然进入have fault
通过LR寄存器,我们来反追查到是哪个指令调用完以后就产生了have fault。
但我在这一看这个LR寄存器是0xFFFFFFE9,它不像是一个有意义的地址
通过地址0xFFFFFFE9 跳转到这里,也是have fault函数的位置处
遇到这种情况我们该怎么办?这就需要用到要使用栈指针
现在我们看R13(SP)地址为0x20000548,在Memory的窗口处该地址会压入四个字就是16个字节
这个地方好像存在了一个地址,地址都是80开头的。
所以在这种进入错误的时候,进入这种have fault的时候,它不会给你压成压完整的栈,它这个地方会给你强行有一个主栈切换到从栈
按照我这个方法找,就从你最顶的站,最顶上面的站指针,你一直往后找,找到这里有一个疑似你地址段
跳转
如果我能从have fault里面退出来,程序能从have fault里面退出来,应该是运下一条就要运行这个指令“while”。那我上一条指令是不是就这个指令往上找就行了,最终找到
对比下地址0X08001611与0x08001608 是不是很靠近,疑似你地址段的, 所以我们完全有理由怀疑到这个位置,就怀疑这里已经出错了