From 968debd0d5979dd9ddca3af0766bad714dbd51e3 Mon Sep 17 00:00:00 2001 From: Tao Liu Date: Wed, 4 Sep 2024 19:49:40 +1200 Subject: [PATCH] arm64: Add gdb stack unwind support Signed-off-by: Tao Liu --- arm64.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- defs.h | 36 +++++++++++++++++ 2 files changed, 148 insertions(+), 8 deletions(-) diff --git a/arm64.c b/arm64.c index 19a3e4b..608b19d 100644 --- a/arm64.c +++ b/arm64.c @@ -121,6 +121,11 @@ static void arm64_get_struct_page_size(struct machine_specific *ms); #define mte_tag_reset(addr) (((ulong)addr & ~mte_tag_shifted(KASAN_TAG_KERNEL)) | \ mte_tag_shifted(KASAN_TAG_KERNEL)) +struct user_regs_bitmap_struct { + struct arm64_pt_regs ur; + ulong bitmap[32]; +}; + static inline bool is_mte_kvaddr(ulong addr) { /* check for ARM64_MTE enabled */ @@ -197,6 +202,94 @@ out: machdep->is_page_ptr = arm64_vmemmap_is_page_ptr; } +static int +arm64_get_current_task_reg(int regno, const char *name, + int size, void *value) +{ + struct bt_info bt_info, bt_setup; + struct task_context *tc; + struct user_regs_bitmap_struct *ur_bitmap; + ulong ip, sp; + bool ret = FALSE; + + switch (regno) { + case X0_REGNUM ... PC_REGNUM: + break; + default: + return FALSE; + } + + tc = CURRENT_CONTEXT(); + if (!tc) + return FALSE; + BZERO(&bt_setup, sizeof(struct bt_info)); + clone_bt_info(&bt_setup, &bt_info, tc); + fill_stackbuf(&bt_info); + + get_dumpfile_regs(&bt_info, &sp, &ip); + if (bt_info.stackbuf) + FREEBUF(bt_info.stackbuf); + ur_bitmap = (struct user_regs_bitmap_struct *)bt_info.machdep; + if (!ur_bitmap) + return FALSE; + + if (!bt_info.need_free) { + goto get_all; + } + + switch (regno) { + case X0_REGNUM ... X30_REGNUM: + if (!NUM_IN_BITMAP(ur_bitmap->bitmap, + REG_SEQ(arm64_pt_regs, regs[0]) + regno - X0_REGNUM)) { + FREEBUF(ur_bitmap); + return FALSE; + } + break; + case SP_REGNUM: + if (!NUM_IN_BITMAP(ur_bitmap->bitmap, + REG_SEQ(arm64_pt_regs, sp))) { + FREEBUF(ur_bitmap); + return FALSE; + } + break; + case PC_REGNUM: + if (!NUM_IN_BITMAP(ur_bitmap->bitmap, + REG_SEQ(arm64_pt_regs, pc))) { + FREEBUF(ur_bitmap); + return FALSE; + } + break; + } + +get_all: + switch (regno) { + case X0_REGNUM ... X30_REGNUM: + if (size != sizeof(ur_bitmap->ur.regs[regno])) + break; + memcpy(value, &ur_bitmap->ur.regs[regno], size); + ret = TRUE; + break; + case SP_REGNUM: + if (size != sizeof(ur_bitmap->ur.sp)) + break; + memcpy(value, &ur_bitmap->ur.sp, size); + ret = TRUE; + break; + case PC_REGNUM: + if (size != sizeof(ur_bitmap->ur.pc)) + break; + memcpy(value, &ur_bitmap->ur.pc, size); + ret = TRUE; + break; + } + + if (bt_info.need_free) { + FREEBUF(ur_bitmap); + bt_info.need_free = FALSE; + } + return ret; +} + /* * Do all necessary machine-specific setup here. This is called several times * during initialization. @@ -549,6 +642,7 @@ arm64_init(int when) machdep->dumpfile_init = NULL; machdep->verify_line_number = NULL; machdep->init_kernel_pgd = arm64_init_kernel_pgd; + machdep->get_current_task_reg = arm64_get_current_task_reg; /* use machdep parameters */ arm64_calc_phys_offset(); @@ -683,6 +777,7 @@ arm64_init(int when) */ if (!LIVE()) arm64_get_crash_notes(); + gdb_change_thread_context(); break; case LOG_ONLY: @@ -4134,6 +4229,7 @@ arm64_get_dumpfile_stackframe(struct bt_info *bt, struct arm64_stackframe *frame try_kernel: frame->sp = ptregs->sp; frame->fp = ptregs->regs[29]; + bt->machdep = ptregs; } if (arm64_in_kdump_text(bt, frame) || @@ -4162,22 +4258,30 @@ arm64_get_stackframe(struct bt_info *bt, struct arm64_stackframe *frame) static void arm64_get_stack_frame(struct bt_info *bt, ulong *pcp, ulong *spp) { - int ret; + struct user_regs_bitmap_struct *ur_bitmap; struct arm64_stackframe stackframe = { 0 }; if (DUMPFILE() && is_task_active(bt->task)) { - ret = arm64_get_dumpfile_stackframe(bt, &stackframe); + arm64_get_dumpfile_stackframe(bt, &stackframe); + bt->need_free = FALSE; } else { if (bt->flags & BT_SKIP_IDLE) bt->flags &= ~BT_SKIP_IDLE; - ret = arm64_get_stackframe(bt, &stackframe); - } + arm64_get_stackframe(bt, &stackframe); - if (!ret) - error(WARNING, - "cannot determine starting stack frame for task %lx\n", - bt->task); + ur_bitmap = (struct user_regs_bitmap_struct *)GETBUF(sizeof(*ur_bitmap)); + memset(ur_bitmap, 0, sizeof(*ur_bitmap)); + ur_bitmap->ur.pc = stackframe.pc; + ur_bitmap->ur.sp = stackframe.sp; + ur_bitmap->ur.regs[29] = stackframe.fp; + SET_BIT(ur_bitmap->bitmap, REG_SEQ(arm64_pt_regs, pc)); + SET_BIT(ur_bitmap->bitmap, REG_SEQ(arm64_pt_regs, sp)); + SET_BIT(ur_bitmap->bitmap, REG_SEQ(arm64_pt_regs, regs[0]) + + X29_REGNUM - X0_REGNUM); + bt->machdep = ur_bitmap; + bt->need_free = TRUE; + } bt->frameptr = stackframe.fp; if (pcp) diff --git a/defs.h b/defs.h index 14b9a91..b93a7a6 100644 --- a/defs.h +++ b/defs.h @@ -8143,6 +8143,42 @@ enum x86_64_regnum { LAST_REGNUM }; +enum arm64_regnum { + X0_REGNUM, + X1_REGNUM, + X2_REGNUM, + X3_REGNUM, + X4_REGNUM, + X5_REGNUM, + X6_REGNUM, + X7_REGNUM, + X8_REGNUM, + X9_REGNUM, + X10_REGNUM, + X11_REGNUM, + X12_REGNUM, + X13_REGNUM, + X14_REGNUM, + X15_REGNUM, + X16_REGNUM, + X17_REGNUM, + X18_REGNUM, + X19_REGNUM, + X20_REGNUM, + X21_REGNUM, + X22_REGNUM, + X23_REGNUM, + X24_REGNUM, + X25_REGNUM, + X26_REGNUM, + X27_REGNUM, + X28_REGNUM, + X29_REGNUM, + X30_REGNUM, + SP_REGNUM, + PC_REGNUM, +}; + /* * Register numbers to make crash_target->fetch_registers() * ---> machdep->get_current_task_reg() work properly.