Xtensa架构的callstack实现方式

首先参考轮子库Linux的实现,Linux实现的主干逻辑如下:

Xtensa架构有其特殊性,最核心的一点是所谓的window ABI,通过window ABI减少了函数调用过程中压栈出栈的操作,获取了性能上的提升。不知道是不是由于 Window ABI引入的代价,他的的堆栈布局非常规整,有板有眼。这对stack hacking带了了很多便利,从上图中可以看出,Linux中针对xtensa架构的dump_stack实现还是非常的简洁的。

移植结果:

#include <stddef.h>
#include <stdio.h>

#define ALIGN(x, a)  (((x) + (a) - 1) & ~((a) - 1))
#define THREAD_SIZE  (8*1024)
#define SPILL_SLOT(sp, reg) (*(((unsigned long *)(sp)) - 4 + (reg))) 
#define MAKE_PC_FROM_RA(ra,sp)    (((ra) & 0x3fffffff) | ((sp) & 0xc0000000))
#define __always_inline inline __attribute__((__always_inline__))

struct stackframe {
    unsigned long pc;
    unsigned long sp;
};

static __always_inline unsigned long *stack_pointer(void)
{
    unsigned long *sp;

    __asm__ __volatile__ ("mov %0, a1\n" : "=a"(sp)); 

    return sp;
}

static inline void spill_registers(void)
{
#if XCHAL_NUM_AREGS > 16
    __asm__ __volatile__ (
        "call8	1f\n"
        "_j	2f\n"
        "retw\n"
        ".align	4\n"
        "1:\n"
#if XCHAL_NUM_AREGS == 32
        "_entry	a1, 32\n"
        "addi	a8, a0, 3\n"
        "_entry	a1, 16\n"
        "mov	a12, a12\n"
        "retw\n"
#else
        "_entry	a1, 48\n"
        "call12	1f\n"
        "retw\n"
        ".align	4\n"
        "1:\n"
        ".rept	(" __stringify(XCHAL_NUM_AREGS) " - 16) / 12\n"
        "_entry	a1, 48\n"
        "mov	a12, a0\n"
        ".endr\n"
        "_entry	a1, 16\n"
#if XCHAL_NUM_AREGS % 12 == 0
        "mov	a12, a12\n"
#elif XCHAL_NUM_AREGS % 12 == 4
        "mov	a4, a4\n"
#elif XCHAL_NUM_AREGS % 12 == 8
        "mov	a8, a8\n"
#endif
        "retw\n"
#endif
        "2:\n"
            : : : "a8", "a9", "memory");
#else
        __asm__ __volatile__ (
            "mov    a12, a12\n"
        : : : "memory");
#endif
}

static int recursive_counter = 0;
void walk_stackframe(unsigned long *sp, int (*fn)(struct stackframe *frame, void *data), void *data)
{
    recursive_counter ++;

    // check nest call scenarios for printf would get lock, which would call dump_stack again.
    if(recursive_counter > 20)
    {
	return;
    }

    unsigned long a0, a1;
    unsigned long sp_end;

    a1 = (unsigned long)sp;
    sp_end = ALIGN(a1, THREAD_SIZE);

    spill_registers();

    while (a1 < sp_end)
    {
        struct stackframe frame;

        sp = (unsigned long *)a1;
        a0 = SPILL_SLOT(a1, 0);
        a1 = SPILL_SLOT(a1, 1);

        if (a1 <= (unsigned long)sp)
        {
            break;
        }

        frame.pc = MAKE_PC_FROM_RA(a0, a1);
        frame.sp = a1;

        if (fn(&frame, data))
        {
            return;
        }
    }

    return;
}

static int show_trace_cb(struct stackframe *frame, void *data)
{
    printf("%s line %d, pc 0x%lx, sp 0x%lx, data=%p.\n", __func__, __LINE__, frame->pc, frame->sp, data);

    return 0;
}

void show_trace(unsigned long pc, unsigned long *sp)
{
    walk_stackframe(sp, show_trace_cb, NULL); 
}

void show_stack(unsigned long pc, unsigned long *sp)
{
    unsigned long *stack;

    stack = sp;
    show_trace(pc, stack);
}

static void __dump_stack(void)
{
    unsigned long *sp = stack_pointer();

    show_stack(0, sp);
}

void dump_stack(void)
{
    __dump_stack();
}

结束!

  • 7
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值