这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 行业应用 » 汽车电子 » 【CmbackTrace】CmbackTrace 函数调用栈解析原理

共2条 1/1 1 跳转至

【CmbackTrace】CmbackTrace 函数调用栈解析原理

高工
2026-05-25 23:18:03     打赏

【简介】

我们之前介绍过Cortex M异常解析神器(RA8 Freertos 使用 CmBacktrace 调试死机问题)的使用。CmBacktrace  的一大特色功能是可以根据任务栈中的内容回溯出触发异常时的函数调用栈,我们本次的主题时解析下这部分功能的实现原理。Cmbacktrace 解析函数调用栈的基本原理是检查任务栈中的数据元素是是否在代码段区间,如果在代码段对应的地址的上一条指令如果为BL/BLX 指令的话改该地址就认为是函数调用栈的一个返回地址。上述逻辑的核心一是解析栈里面的代码段的地址的上一条指令是否是BL 跳转指令,对应的代码如下:

/* 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             0x4700

    if ((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;
    }
}

以下是BL 指令的编码说明:

image.png

从上述描述中可知J1/J2 在Thumb-2 的指令集中为1,和代码的 

BL_INS_HIGH         0xF800
BL_INS_LOW          0xF000

   以下是本地实际代码中抓取的BL指令的机器码

image.png

image.png

从上述的BL 的伪代码说明中可知BL 指令做了两件事,1 更新PC指针 2 更新LR返回地址,跳转到对应的函数的时候把LR押入到栈中这也就是为啥在栈中可以找到LR返回地址的信息。

image.png

看完了BL指令我们继续看BLX指令,以下是BLX指令的编码说明。

image.png

代码中只是去了对应的指令的高八字节来比较,对应的数值定义如下:

#define BLX_INX             0x4700

以下的代码逻辑是查找任务栈的中的元素是否在代码段中,如果在则查找上一条指令是否为 BL/BLX 指令,如果是的话则认为此地址为任务栈中的一次函数调用路径。

size_t cm_backtrace_call_stack_any(uint32_t *buffer, size_t size, uint32_t sp, uint32_t stack_start_addr, uint32_t stack_size)
{
    uint32_t pc;
    size_t depth = 0;
    /* 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 */
            buffer[depth++] = pc;
        }
    }

    return depth;
}

上述跳转的指令只是检查了BL/BLX 指令没有检查B /BX 指令这个也和容易理解,函数调用的时候一定是通过BL/BLX 来执行的,以下是跳转指令的说明。

image.png

BX 指令通常配置LR寄存器来使用

image.png



院士
2026-05-31 08:57:26     打赏
2楼

楼主 您这涉猎面真广。

坦白讲,现在很少人,很少人在学习指令了。



共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]