发新帖本帖赏金 55.00元(功能说明)我要提问
返回列表
打印
[APM32F4]

如何使用调试器在非仿真情况下对Cortext-M内核进行HardFault错误分析

[复制链接]
1372|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
luobeihai|  楼主 | 2024-11-23 19:45 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 luobeihai 于 2024-11-23 20:12 编辑

#申请原创# @21小跑堂

最近在使用Geehy的芯片进行开发过程中,遇到了无法使用在线调试仿真的情况下,去分析开发过程中遇到的HardFault异常。于是,在解决问题的过程中,解锁了不用在线仿真的情况下,也可以对HardFault进行错误定位分析。

1. 内核异常与xPSR

1.1 内核异常

Cortex-M 内核的MCU,异常架构支持多种内核异常和外部中断,编号 1-15 的为系统异常,16 及以上的则为外部中断。

下表是Cortext-M的内核异常,可以看到我们将要分析的Hardfault异常,其编号是3。


HardFault 异常是无需使能,它有固定的异常优先级-1。当用户没有去使能 MemManage/Bus/Usage这三种Fault时,那么如果发生了这三种错误的话,都会触发Hardfault异常中断。

如果用户要使能MemManage/Bus/Usage这三种Fault,可以在芯片上电启动时进行配置。

SCB->SHCSR |= 0x00007000;

不过在大多数情况下,用户不会对使能这些异常,也就是说发生这些错误基本都是响应Hardfault中断服务函数。

1.2 xPSR

我们如何知道当前执行的中断是Hardfault中断?这就需要了解xPSR,即程序状态寄存器了。

程序状态寄存器(Program Status Register)一共涉及到三个寄存器,分别为:Application PSR(APSR)、Execution PSR(EPSR)和Interrupt PSR(IPSR)。如下所示, 这三个寄存器可以组合为一个寄存器一起访问,标记为xPSR。


程序状态寄存器的位定义如下:
  • N: 负数标识位(Negative flag)
  • Z: 零标识位(Zero flag)
  • C: 进位或者无借位标识位(Carry or NOT borrow flag)
  • V: 溢出标识位(Overflow flag)
  • Q: Sticky saturation flag
  • GE: 大于等于标识位(Greater-Than or Equal flags for each byte lane)
  • ICT/IT: Interrupt-Continuable Instruction(ICT)bits, IF-THEN instruction status bit for conditional execution.
  • T: Thumb状态标识位,始终为1;尝试清除该位将触发一个错误异常。
  • Exception Number: 异常代号,标志着正在处理的异常。

所以,我们通过通过程序状态寄存器的bit[0:8]位,可以得知当前执行的中断服务函数是否位Hardfault(如何才能获取xPSR寄存器的值,下面会进行介绍)。

2. 栈帧与内核寄存器

在异常入口处被压人栈空间的数据块为栈帧。

对于Cortex-M内核的处理器,如果不具有浮点单元的话,那么栈帧都是8个字(1个字32bit)大小的;而如果具有浮点单元的话,那么栈帧可能是8或者26个字。
处理器在运行程序过程中,当被中断打断了当前执行的程序,跳转到执行中断服务程序之前,会将PC、LR寄存器,还有一些通用寄存器压入栈中(PUSH),这就是保存现场。当中断服务函数处理完之后,又会将原来压栈的数据,从栈中恢复到内核寄存器(POP),这就是恢复现场。

大部分情况下,都是保存8个字的栈帧,那么一般都是保存如下图内核寄存器的值:


也就是说,跳到中断服务函数之前,压栈的内核寄存器,从低地址到高地址依次有:R0/R1/R2/R3/R12/LR/PC/xPSR,共8个内核寄存器。
  • xPSR: 程序状态寄存器
  • 返回地址:这里其实保存的就是CPU正在执行当前的指令,而触发了中断。触发中断的那一刻,会把执行当前指令的地址(即PC值)进行压栈(之前我一直搞不懂这个返回地址保存的是啥值,下面我们经过案例分析就可以更明了的知道这个返回地址是什么数据了)。
  • LR:返回函数指针。这里其实保存的就是跳转到中断服务函数之前,保存的下一条要执行的指令的地址。
  • r12:通用寄存器R12
  • r0~r3:通用寄存器R0-R3

压栈的内核寄存器一共有8个,这里Cortex-M内核手册说会有返回地址压栈,这个返回地址保存的其实就是执行了这条指令后触发了中断,然后保存的这条指令的地址

3. 如何使用调试器定位错误代码

下面介绍不使用在线仿真的情况下,只使用调试器如何进行Hardfault分析。

这里以Jlink调试器为例,如果大家对Jlink调试器的一些命令不熟悉的话,可以参考下网上的资源。或者这篇博文:https://blog.csdn.net/qq_30095921/article/details/128311887

对于Jlink命令行的使用这篇文章介绍的已经非常的详细了。

3.1 获取内核寄存器

1、打开Jlink Commander,输入下面命令连接到芯片。


2、输入h命令获取内核寄存器


3.2 如何判断当前是否触发Hardfault

我们前面就已经介绍过,xPSR寄存器的低9bit的值,存放这当前中断的编号。所以上面通过Jlink获取到内核寄存器的xPSR的值之后,即可判断到其是否触发了Hardfault异常(中断编号3)。


3.3 分析定位错误代码的原理

前面我们已经知道,压栈的内核寄存器有8个。而且上面获取到了内核寄存器的值之后,有一个SP的值。我们通过读取SP所执行的栈空间,就可以获取到产生Hardfault错误之前,8个内核寄存器的状态。


根据上图通过mem32命令,读取SP指针指向的地址0x20000420,8个字的栈数据,其内核寄存器值和对应的栈地址如下表:

其中最重要的就是LR和PC的值,我们通过LR可以知道跳转到Hardfault中断服务函数之前,下一条指令的地址;而通过PC值,可以知道执行了那一条指令地址导致的Hardfault异常。然后我们再结合编译生成的.map文件,或者反汇编文件知道各个函数的地址,我们就可以明确的确定具体是哪条指令导致的Hardfault异常了。

4. 案例分析

下面通过几个案例进行分析,可以理解的比较深刻。

4.1 非法地址访问

1、在main函数中故意调用下面这个函数访问0x12345678的非法地址。

void illegal_address_access(void)
{
    uint32_t temp = 0;
   
    temp = *(uint32_t *)0x12345678;
}

2、执行代码触发Hardfault错误之后,我们通过Jlink获取内核寄存器值。


其中最重要的LR值是0x08000D4B,PC值是0x08000D00 。

3、通过生成的反汇编代码(或者.map文件),我们去搜索这两个地址值,或者搜索到与之相近的地址值,然后就可以确定具体出错的是那个函数,哪条语句。


所以可以确定导致Hardfault错误的函数是:illegal_address_access 。

4.2 非法函数调用

1、故意调用下面的非法函数。

void illegal_function_call(void)
{
    void (*pFunc)(void);
   
    pFunc = (void (*)(void))0x08FFAAAA;
     
    pFunc();  // 通过函数指针调用非法函数
}

2、通过Jlink获取内核寄存器值。


3、搜索反汇编代码的0x08000D03(LR)和0x08FFAAAA(PC)地址值,确定代码出错的函数和位置。


通过分析可以确定是 illegal_function_call 这个函数导致的 Hardfault 错误。是因为 BLX r4 这条指令,跳转到了一个非法地址 0x08FFAAAA 。

这里这条指令的地址是0x08000D00,但是为什么栈帧里面的压栈数据,PC值不是这条指令的地址呢?这是因为BLX跳转指令,会把跳转的地址0x08FFAAAA赋值给PC,所以栈帧里面记录的不是跳转指令的地址值,而是要跳转的地址值,即0x08FFAAAA(这一点对于我最近做的项目很有用,终于搞清楚了是什么原因导致的Hardfault问题)。

为了方便大家分析,下面附件是这两个案例的工程代码。
APM32F4xx_SDK_V1.4_BlogDemo.zip (971.38 KB)



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 55.00 元 2024-11-26
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论
21小跑堂 2024-11-26 17:54 回复TA
不进行在线仿真,通过调试器的命令进行Hardfault问题的定位和分析。 
沙发
micoccd| | 2024-11-24 18:27 | 只看该作者
这种调试器连接到芯片会造成芯片复位吗

使用特权

评论回复
评论
luobeihai 2024-11-25 12:20 回复TA
不会的。除非你通过Jlink发出复位信号的命令 
板凳
zhe_jiang| | 2024-12-3 08:34 | 只看该作者
学习了。 感谢。

使用特权

评论回复
发新帖 本帖赏金 55.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

75

帖子

2

粉丝