目的

为了更好的分析hardfault问题、代码卡在某个地方但是又不方便仿真,需要理解m3、m4的栈是怎样的,更快的定位问题。

预备知识

内核有如下寄存器:
R0~R12,SP(R13),LR(R14),PC(R15),xPSR,在进入异常时内核会自动保存R0 ~ R3,R12,LR,PC,xPSR,对于m4,如果在进入异常前使用了浮点计算还会保存S0 ~ S15,FPSCR寄存器,栈帧如下

SP(R13):
cortex有2个栈指针,复位后默认使用msp主栈指针,中断中使用的是msp;psp线程栈,rtos中各个任务中就是使用psp。

LR(R14):
做分支时,用于保存返回地址;进入异常时用于区分当前使用的msp还是psp

如上LR的bit2=1则使用psp,bit2=0使用msp
并且对于m4,bit4=0则使用了fpu,bit4=1则没有使用fpu

PC(R15):将要执行指令的地址,PC的LSB总是0,但是向PC写值时必须保证LSB=1,用以表明这是在Thumb状态下执行,如果写了0,则企图转入ARM模式,将产生fault异常

栈例子

函数调用过程中的栈

如下例子是在freertos环境,代码如下

int f3(int a,int b)
{a += (a+b);b += (a-b);return a+b;
}int f2(int a,int b)
{int c = f3(a,b);c++;if(c>0)printf("%d\n",c);return c;
}
void f1()
{int c=0;c = f2(1,2);c++;if(c>0)printf("%d\n",c);
}
static  void  AppTaskStart (void *p_arg)
{//    fault_test_by_unalign();f1();while (1) {LED_RUN_ON;vTaskDelay(500/portTICK_RATE_MS);LED_RUN_OFF;vTaskDelay(500/portTICK_RATE_MS);}
}

在f3第一句打断点,可以看到

1.pc是将要执行的指令,此时还未执行

2.函数调用关系f1–>f2–>f3,所以返回的顺序是f3–>f2–>f1
从f3返回f2

f3返回f2用LR寄存器值写入PC,所以实际返回地址=LR的值-1
f2返回f1就要用之前保存再栈里的值

中断过程中的栈

现在向f3中加入如下函数,将产生hardfault

void fault_test_by_div0(void) {volatile int * SCB_CCR = (volatile int *) 0xE000ED14; // SCB->CCRint x, y, z;*SCB_CCR |= (1 << 4); /* bit4: DIV_0_TRP. */x = 10;y = 0;z = x / y;printf("z:%d\n", z);
}
int f3(int a,int b)
{a += (a+b);fault_test_by_div0();b += (a-b);return a+b;
}

LR的bit2=1,则使用的是psp栈,往前回溯得到pc=0x08001FE2,正是产生hardfault的代码,

pc旁边的LR=0x08001CF3则是要返回的上一及函数

再分析栈中数据,要是奇数且在代码地址范围内



如上一步一步倒推则把调用关系理清了

栈回溯代码

作者Armink就是把上述过程代码化,并增加一些寄存器的错误位解释,源码如下
https://github.com/armink/CmBacktrace

关键代码

作者为了判断PC的准确性还判断PC的上一条指令是不是BL或者BLX,关键代码如下

    /* copy called function address */for (; sp < stack_start_addr + stack_size; sp += sizeof(size_t)) {/* the *sp value may be LR, so need decrease a word to PC */pc = *((uint32_t *) sp) - sizeof(size_t);/* the Cortex-M using thumb instruction, so the pc must be an odd number */if (pc % 2 == 0) {continue;}/* fix the PC address in thumb mode */pc = *((uint32_t *) sp) - 1;if ((pc >= code_start_addr + sizeof(size_t)) && (pc <= code_start_addr + code_size) && (depth < CMB_CALL_STACK_MAX_DEPTH)/* check the the instruction before PC address is 'BL' or 'BLX' */&& disassembly_ins_is_bl_blx(pc - sizeof(size_t)) && (depth < size)) {/* the second depth function may be already saved, so need ignore repeat */if ((depth == 2) && regs_saved_lr_is_valid && (pc == buffer[1])) {continue;}buffer[depth++] = pc;}}
/* check the disassembly instruction is 'BL' or 'BLX' */
static bool disassembly_ins_is_bl_blx(uint32_t addr) {uint16_t ins1 = *((uint16_t *)addr);uint16_t ins2 = *((uint16_t *)(addr + 2));#define BL_INS_MASK         0xF800
#define BL_INS_HIGH         0xF800
#define BL_INS_LOW          0xF000
#define BLX_INX_MASK        0xFF00
#define BLX_INX             0x4700if ((ins2 & BL_INS_MASK) == BL_INS_HIGH && (ins1 & BL_INS_MASK) == BL_INS_LOW) {return true;} else if ((ins2 & BLX_INX_MASK) == BLX_INX) {return true;} else {return false;}
}

以0x0800035D为例子,减去1就是0x0800035C,再减去4就是0x08000358,而
0x08000358地址的内容是0xF001FC9E,符合第一个if条件,就认为这是一个有效的代码地址

fpu

还有对于有fpu的,如果LR的bit4=0则表明还把S0~S15、FPSCR也压入栈了,分析是要退出来

static uint32_t statck_del_fpu_regs(uint32_t fault_handler_lr, uint32_t sp) {statck_has_fpu_regs = (fault_handler_lr & (1UL << 4)) == 0 ? true : false;/* the stack has S0~S15 and FPSCR registers when statck_has_fpu_regs is true, double word align */return statck_has_fpu_regs == true ? sp + sizeof(size_t) * 18 : sp;
}

cortex-m3/m4栈回溯相关推荐

  1. cortex m3/m4处理器的复位设计

    cortex m3/m4处理器在复位层面总体上可以划分为core和debug logic两部分.core部分包括处理器内核(core)以及NVIC,BUS Matrix,MPU的非debug部分.de ...

  2. ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 有啥区别

    ARM架构.ARM7.ARM9.STM32.Cortex M3 M4.51.AVR之间有什么区别和联系? ARM架构:由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~AR ...

  3. ARM 架构 ARM7 ARM9 STM32 Cortex M3 M4 51 AVR 有啥区别

    ARM架构.ARM7.ARM9.STM32.Cortex M3 M4.51.AVR之间有什么区别和联系? ARM架构:由英国ARM公司设计的一系列32位的RISC微处理器架构总称,现有ARMv1~AR ...

  4. ARM 架构、ARM7、ARM9、STM32、Cortex M3 M4 、51、AVR 之间有什么区别和联系?

    本文转自嵌入式资讯精选公众号,特别鸣谢, 编者按:初学习ARM单片机的同学们可能会对ARM的架构定义并不是很明确,形形色色的名词背后到底代表什么含义呢?请听听这位嵌入式工程师的经验总结. ARM架构: ...

  5. Cortex M3/M4 学习摘要(二)

    ########################################## 处理器类型 使用RICS 3级流水选,且拥有哈佛架构 4GB寻址空间 加载存储架构 ############### ...

  6. Cortex M3/M4 学习摘要(一)

    #####################################################  微控制器和处理器: 在一个基本的微处理器中,处理器只占了很小一部分,一般被内存.时钟发生器 ...

  7. Cortex、ARMv8、arm架构、ARM指令集、soc?Cortex A8、A9都是ARMv7a 架构;Cortex M3、M4是ARMv7m架构;前者是处理器(内核)后者是指令集的架构(架构)

    架构组成元素的指令集状态或者语法thumb指令集与arm指令集的区别例如thumb指令集是什么_thumb指令集与arm指令集的区别以及thumb-2的关系在下一文中介绍,本文暂时不讨论 有粉丝问我到 ...

  8. Cortex‐M3的Faults异常究竟是什么?

    关注+星标公众号,不错过精彩内容 作者 | strongerHuang 微信公众号 | strongerHuang 有许多朋友在学习,或者开发STM32时都遇到过HardFault_Handler的情 ...

  9. 以SIGSEGV为例详解信号处理(与栈回溯)

    以SIGSEGV为例详解信号处理(与栈回溯) 信号是内核提供的向用户态进程发送信息的机制, 常见的有使用SIGUSR1唤醒用户进程执行子程序或发生段错误时使用SIGSEGV保存用户错误现场. 本文以S ...

  10. Linux内核出错的栈打印详解,linux内核中打印栈回溯信息 - dump_stack()函数分析

    简介 当内核出现比较严重的错误时,例如发生Oops错误或者内核认为系统运行状态异常,内核就会打印出当前进程的栈回溯信息,其中包含当前执行代码的位置以及相邻的指令.产生错误的原因.关键寄存器的值以及函数 ...

最新文章

  1. python连接ssh执行模块_python ssh之paramiko模块使用
  2. 计算机一级b类试题及答案,计算机一级b类试题及答案
  3. 阿里云Redis开发规范
  4. yield %%% generator
  5. Java语言基础(数组)
  6. [转载]宇宙文明等级的划分标准
  7. 首个比较成型的切图作品
  8. 源码大招:不服来战!撸这些完整项目,你不牛逼都难!
  9. 并发编程学习(2)----volatile与synchronized
  10. Eclipse汉化方法202012月版
  11. FPGA信号处理系列文章——卷积编码与维特比译码
  12. 误删除数据恢复 plsql
  13. 关于通过图注意神经网络处理多元时间序列的错误预测论文的讨论
  14. python(八) python 面向对象中的类(class)
  15. Delphi TreeView树型框节点拖拽停靠、按条件开始拖拽
  16. 1-100能被7整除的数c语言,列印1-100的 范围数字包含7,或是能被7整除的所有数字(java程式码 用for语句 线上等 )...
  17. SQL中的DML、DDL、DCL分别是什么意思
  18. 高中计算机基础知识课件,5.1.2 信息资源管理的方式方法
  19. 负荷分配问题的动态规划算法递归实现
  20. CS、BS架构定义(笔记)

热门文章

  1. mac原生壁纸,拿走不谢!
  2. 小程序实现保存图片到手机
  3. 产品读书《谷歌和亚马逊如何做产品》
  4. PHP-swoole 聊天室
  5. 对接支付宝APP支付后端实现最详细教程
  6. android友盟埋点,友盟埋点详解
  7. 计算机网络>速率、带宽、吞吐量
  8. 最燃黑客情报官薛锋:端起AK伏特加,代表人民把坏人抓
  9. mysql 查看校对集_MySQL教程之校对集问题
  10. 岌岌可危?大数据营销与数据泄露相伴而行