cortex-m3/m4栈回溯

news/2024/12/15 9:05:47/

目的

为了更好的分析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;
}

http://www.ppmy.cn/news/748367.html

相关文章

国密算法m1-m4概述

密码学中应用最为广泛的的三类算法&#xff1a; 1、对称算法&#xff08;分组密码算法&#xff09;代表分组密码算法(DES和SM4)&#xff1b; 2、非对称算法&#xff08;公钥密码算法&#xff09;代表公钥密码算法(RSA和SM2)&#xff1b; 3、杂凑算法&#xff08;摘要算法&#…

Arm Cortex -M4内存对齐问题

一、字节对齐的含义 4字节对齐的含义就是变量地址对4求余数为0&#xff1b;8字节对齐就是地址对8求余等于0&#xff0c;依次类推&#xff0c;比如&#xff1a; 如果让p去访问0x20000001&#xff0c; 0x20000002&#xff0c;0x20000003这都是不对齐访问。 二、背景知识 对于M…

零拷贝小结

零拷贝&#xff08;Zero-copy&#xff09;是一种优化技术&#xff0c;用于减少数据传输过程中的拷贝操作&#xff0c;从而提高系统性能和效率。在传统的数据传输中&#xff0c;涉及多个缓冲区之间的数据拷贝操作&#xff08;例如从磁盘到内存的拷贝、内存到网络缓冲区的拷贝等&…

config.m4

config.m4的 作用是为了配合phpize工具生成configure文件&#xff0c;configure文件是用于环境检测的&#xff0c;检测扩展编译运行的环境是否满足需求 转载于:https://www.cnblogs.com/hanshuai0921/p/7530547.html

ThunderSoft Apple Music Converter Mac(drm限制解除工具)

ThunderSoft Apple Music Converter是一款帮助大家解除音频文件drm版权保护的破解工具。大家可以将失去drm版权保护的音频文件直接从iTunes或音乐文件导入音乐&#xff0c;快速转换为MP3&#xff0c;AAC&#xff0c;M4B&#xff0c;M4A&#xff0c;WAV&#xff0c;FLAC或其他流…

详解m4文件

最近在分析speex代码&#xff0c;发现编译过程中需要的一个speex.m4文件不知道是何方神圣&#xff0c;怀着对未知知识的渴望&#xff0c;跑到某哥和某基问了一下&#xff0c;算是认识了&#xff0c;为了方便以后经常见面&#xff0c;这里就做个记录吧。 M4实际上是一种编程语言…

9.使用M4sh编程

本系列文章均翻译自Autoconf官方文档&#xff1a;Autoconf Manual&#xff0c;github同步项目&#xff1a;question M4sh&#xff0c;称作mash&#xff0c;目的是产生便携式可移植 Bourne shell 脚本。因为不同的shell的语法不兼容&#xff0c;故创造此种宏来解决。 其宏以AS…

如何修复破损的录音文件(m4a)

http://sysfrontier.com/en/2014/12/31/hello-world/ 你的录音文件损坏了吗&#xff1f;你可以自己修复&#xff01;这篇文章里&#xff0c;我将告诉你所需的步骤。 录音文件的拓展名为“m4a”. 这种音频数据是用AAC格式编码的&#xff0c;然后封装在了MPEG4文件中。 在安卓系统…