MT7682 打印堆栈

前期信息

  1. 编译生成.elf文件以后,反汇编
arm-eabi-objdump -D ***.elf > ***.dis

可以生成汇编文件
2. 查看.dis汇编文件,例如
在这里插入图片描述
可以看到最前面一列为程序地址
使用

$ arm-eabi-addr2line -Cfe ***.elf 080400b0
dump_r7_callback
/media/data/MTK/MT7682/mt7682/sdk/project/mt7682_hdk/apps/lock/GCC/../../../../../project/mt7682_hdk/apps/lock/../common/src/arm_backtrace.c:102

就可以得到对应的代码位置.

所以如果要回溯堆栈,那么就要能得到类似080400b0这样的地址堆栈
然后使用addr2line来回溯到sourcecode的堆栈

编译命令

编译cortex-m4的thumb代码
arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -c -o main.o main.c
编译arm代码
arm-none-eabi-gcc -c -o main.o main.c

疑问

arm模式编译的代码:

arm-none-eabi-gcc  -fno-omit-frame-pointer -c -o main.o main.c
arm-none-eabi-objdump -d main.o 

0000001c <func1>:
  1c:   e92d4800        push    {fp, lr}        压栈 fp/lr, sp会自动减8
  20:   e28db004        add     fp, sp, #4   将fp回退4,指向堆栈里面的fp
  24:   e24dd008        sub     sp, sp, #8
  28:   e50b0008        str     r0, [fp, #-8]
  2c:   e50b100c        str     r1, [fp, #-12]
  30:   ebfffffe        bl      0 <func2>
  34:   e1a03000        mov     r3, r0
  38:   e1a00003        mov     r0, r3
  3c:   e24bd004        sub     sp, fp, #4
  40:   e8bd8800        pop     {fp, pc}

thumb模式编译的代码:

00000010 <func1>:
  10:   b580            push    {r7, lr}    得出r7作为frame pointer的结论
  12:   b082            sub     sp, #8
  14:   af00            add     r7, sp, #0 ???? 为什么会把修改过的sp赋值给r7,指向栈底?
  16:   6078            str     r0, [r7, #4]
  18:   6039            str     r1, [r7, #0]
  1a:   f7ff fffe       bl      0 <func2>
  1e:   4603            mov     r3, r0
  20:   4618            mov     r0, r3
  22:   f107 0708       add.w   r7, r7, #8
  26:   46bd            mov     sp, r7
  28:   bd80            pop     {r7, pc}
  2a:   bf00            nop

以上对比,
arm模式下 r11(fp)作为frame pointer
thumb模式下 r7 作为frame pointer

但是区别是arm的fp指向当前栈帧的栈底的fp,thumb的指向栈顶,这个怎么回溯堆栈?

测试

1 栈类型

    uint32_t*sp;
    __asm   volatile   ( " MOV   r0,   #12\n " );
    __asm   volatile   ( " MOV   r1,   #13\n " );
    __asm   volatile   ( " MOV   r2,   #14\n " );
    __asm   volatile   ( " PUSH   {r0,   r1,   r2}\n " );
    asm volatile ("mov %0, sp" : "=r" (sp));

    printf( " 0   %08x\r\n " ,   sp[ 0 ]);
    printf( " 4   %08x\r\n " ,   sp[ 1 ]);
    printf( " 8   %08x\r\n " ,   sp[ 2 ]);
    以上代码可以验证堆栈类型,输出
  0   0000000c
  4   0000000d
  8   0000000e
    得出的结论:
    1 递减堆栈,否则sp[0] [1] [2] 不会为压栈的数据
    2 满减堆栈,参考下图,即SP移动以后,是指向最后一个压栈的有效数据
    3 push {r0, r1, r2}的入栈顺序为:r2->r1->r0,因为r2在最高地址

在这里插入图片描述

可以使用下列代码来打印感兴趣的寄存器信息
void dump_r7_callback()
{
    uint32_t* fp, *fp2, *pc;
    asm volatile ("mov %0, r7" : "=r" (fp));
    asm volatile ("mov %0, r11" : "=r" (fp2));
    asm volatile ("mov %0, pc" : "=r" (pc));
    printf("dump_r7_callback fp 0x%x addr %p fp2 0x%x %p pc 0x%x %p\n", 
            fp, &fp, fp2, &fp2, pc, &pc);
}

实现方法

在这里插入图片描述
参考上图,既然r7指向栈尾,并且子函数开始的时候,会将lr/r7先后入栈
那么我们可以从当前的sp地址开始回溯,直到当前task栈的最顶部
如果有一个变量的内容比存放他的地址大8,则此地址存放的是r7,往上4个字节为lr,例如:

//向下生长的堆栈
int dump_backtrack_by_scan_stack(uint32_t *stackEnd, uint32_t *stackStart)
{
    //回溯整个堆栈
    printf("dump_backtrack_by_scan_stack stack %p - %p\n", stackStart, stackEnd);

    int depth = 0;
    for(uint32_t* p = stackEnd; p <= stackStart; p ++)
    {
        uint32_t sp_data = *p;
        if((sp_data - (uint32_t)p) == 8) {
            uint32_t lr = *(p + 1) - 4;
            if(lr >= code_start && lr <= code_end) {
                printf("      #%d: %8lx\n", depth++, *(p + 1) - 4);
                // printf("dump_current_thread: r7 addr %p data 0x%lx, lr 0x%lx\n", 
                            // p, sp_data, *(p + 1) - 4);
            }
        }
    }
    return 0;
}

这样我们就可以回溯出来当前task对应的调用栈。

backtrace其他堆栈

当前task的堆栈与其他task不同的地方在于,无法直接获取到sp,也就是栈底的位置。

注意事项

  • ARM Cortex-A用的是r11做frame pointer,thumb系列的用可能是r7,这个可以查看加上-no-omit-frame-pointer选项以后,编译出来的汇编文件压栈的是哪个寄存器

  • 如果反汇编出来的寄存器与你希望的不同,比如我们希望r7来做fp寄存器,但是汇编代码中r7总是被修改,那么要考虑编译的优化选项当中,是否有Ox的优化选项,可以去掉。
    注意一定要看所有的编译选项,是否有Os,因为编译选项可能很长,会漏掉一些地方

  • 注意编译完,多反汇编几次,查看你需要的寄存器有没有被意外使用
    例如,当我们想打印r7的时候,r7之前已经被重新赋值了,所以就不会是我们需要的fp寄存器了。
    在这里插入图片描述

reference:

下图可以作为参考,但是不同的CPU的栈帧结构不完全一样,而且编译选项不同,栈帧结构也会 不同
在这里插入图片描述

https://github.com/JnuSimba/AndroidSecNotes/blob/master/Android%E9%80%86%E5%90%91%E5%9F%BA%E7%A1%80/ARM%20%E5%AF%84%E5%AD%98%E5%99%A8%E7%AE%80%E4%BB%8B.md

https://developer.arm.com/documentation/100067/0607/armclang-Command-line-Options/-fomit-frame-pointer—fno-omit-frame-pointer

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值