cmbacktrace分析

分析

以瑞萨RZN2L举例,在发生错误时,由于默认情况下RZN2L运行在SVC模式,所以要先切换回SVC模式,栈才对应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Default_Handler (void)
{
/** A error has occurred. The user will need to investigate the cause. Common problems are stack corruption
* or use of an invalid pointer. Use the Fault Status window in e2 studio or manually check the fault status
* registers for more information.
*/

__asm volatile (
"mrs r0, cpsr \n"
"bic r0, r0, %[bsp_mode_mask] \n"
"orr r0, r0, %[bsp_svc_mode] \n"
"msr cpsr, r0 \n"
"push {lr} \n"
"mov r0, lr \n"
"mov r1, sp \n"
"bl backtrace_fault \n"
::[bsp_mode_mask] "i" (BSP_MODE_MASK), [bsp_svc_mode] "i" (BSP_SVC_MODE) : "memory"
);
while (1);
BSP_CFG_HANDLE_UNRECOVERABLE_ERROR(0);
}

这里栈的地址也要用SVC

1
2
3
4
5
6
7
8
9
10
11
void backtrace_fault(uint32_t fault_handler_lr, uint32_t fault_handler_sp) {
uint32_t stack_pointer = fault_handler_sp;
uint32_t saved_regs_addr = stack_pointer;
HS_UNUSED(saved_regs_addr);
stack_start_addr = (uint32_t)((uint32_t *)&__SvcStackBase);
stack_size = (uint32_t)((uint32_t *)&__SvcStackLimit) - stack_start_addr;
code_start_addr = (uint32_t)((uint32_t *)&_text_start);
code_size = (uint32_t)((uint32_t *)&_text_end) - code_start_addr;
hs_log_error(TAG, "A error has occurred.");
print_backtrace(stack_pointer);
}

分析一下这幅图,这是栈的存储结构,其中混合存储了参数和LR,所以我们要将它们区分出来。

我们可以通过判断指令码里是否有BL或者BLX来判断这是不是函数,pc % 2 ==0 过滤掉不是thumb指令的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void print_backtrace(uint32_t sp) {
int deep = 0;
uint32_t pc, lr;
uint32_t addr[MAX_DEEP];

for (; sp < stack_start_addr + stack_size; sp += sizeof(uint32_t)) {
lr = *((uint32_t *)sp);
pc = lr - sizeof(uint32_t);
if (pc % 2 == 0) {
continue;
}
pc -= 1;
if (pc >= code_start_addr && pc <= code_start_addr + code_size) {
if (disassembly_ins_is_bl_blx(pc)) {
if (deep >= MAX_DEEP)
break;
addr[deep] = lr;
deep++;
}
}
}

while (deep > 0) {
deep--;
hs_log_error(TAG, "addr: %x", addr[deep]);
}
}