237 lines
8.4 KiB
Diff
237 lines
8.4 KiB
Diff
From 7b488818107fff9f92e9778749d0046f2024e6af Mon Sep 17 00:00:00 2001
|
|
From: Tao Liu <ltao@redhat.com>
|
|
Date: Wed, 25 Jun 2025 16:02:01 +1200
|
|
Subject: [PATCH 5/9] x86_64: Add gdb multi-stack unwind support
|
|
|
|
Whenever extra stack is found, a user_regs structure is allocated and
|
|
regs value copied there. Later the values will be retrived by
|
|
get_current_task_reg() by given the thread's tid, aka the index of
|
|
stack.
|
|
|
|
Co-developed-by: Alexey Makhalov <alexey.makhalov@broadcom.com>
|
|
Co-developed-by: Tao Liu <ltao@redhat.com>
|
|
Signed-off-by: Tao Liu <ltao@redhat.com>
|
|
---
|
|
x86_64.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
|
|
1 file changed, 125 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/x86_64.c b/x86_64.c
|
|
index ee23d8b..cfefe3f 100644
|
|
--- a/x86_64.c
|
|
+++ b/x86_64.c
|
|
@@ -3508,6 +3508,8 @@ x86_64_exception_RIP_message(struct bt_info *bt, ulong rip)
|
|
#define STACK_TRANSITION_ERRMSG_I_P \
|
|
"cannot transition from IRQ stack to current process stack:\n IRQ stack pointer: %lx\n process stack pointer: %lx\n current stack base: %lx\n"
|
|
|
|
+#define SET_REG_BITMAP(REGMAP, TYPE, MEMBER) \
|
|
+ SET_BIT(REGMAP, REG_SEQ(TYPE, MEMBER))
|
|
/*
|
|
* Low-budget back tracer -- dump text return addresses, following call chain
|
|
* when possible, along with any verifiable exception frames.
|
|
@@ -3528,6 +3530,7 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
|
|
ulong last_process_stack_eframe;
|
|
ulong user_mode_eframe;
|
|
char *rip_symbol;
|
|
+ char buf[BUFSIZE];
|
|
|
|
/*
|
|
* User may have made a run-time switch.
|
|
@@ -3551,6 +3554,7 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
|
|
irq_eframe = 0;
|
|
last_process_stack_eframe = 0;
|
|
bt->call_target = NULL;
|
|
+ extra_stacks_idx = 0;
|
|
rsp = bt->stkptr;
|
|
ms = machdep->machspec;
|
|
|
|
@@ -3632,6 +3636,90 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
|
|
level++;
|
|
}
|
|
|
|
+ if (is_task_active(bt->task) && bt->flags & BT_DUMPFILE_SEARCH) {
|
|
+ if (!extra_stacks_regs[extra_stacks_idx]) {
|
|
+ extra_stacks_regs[extra_stacks_idx] =
|
|
+ (struct user_regs_bitmap_struct *)
|
|
+ malloc(sizeof(struct user_regs_bitmap_struct));
|
|
+ }
|
|
+ memset(extra_stacks_regs[extra_stacks_idx], 0,
|
|
+ sizeof(struct user_regs_bitmap_struct));
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.ip = bt->instptr;
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.sp = bt->stkptr + 8;
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, ip);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, sp);
|
|
+
|
|
+ /* Sometimes bp is needed for stack unwinding, so we try to get
|
|
+ it. The bt->instptr usually points to a inst after a call
|
|
+ inst, let's check the previous call inst. Note the call inst
|
|
+ len is 5 */
|
|
+ open_tmpfile2();
|
|
+ sprintf(buf, "x/1i 0x%lx", bt->instptr - 5);
|
|
+ gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR);
|
|
+ rewind(pc->tmpfile2);
|
|
+ fgets(buf, BUFSIZE, pc->tmpfile2);
|
|
+ if (strstr(buf, "call")) {
|
|
+ if (strstr(buf, "<relocate_range>") ||
|
|
+ strstr(buf, "<relocate_kernel>")) {
|
|
+ /* OK, we are calling relocate_kernel(), which
|
|
+ * is written in assembly and hasn't changed for
|
|
+ * years, so we get some extra regs out of it. */
|
|
+ readmem(bt->stkptr - sizeof(ulong) * 6, KVADDR, buf,
|
|
+ sizeof(ulong) * 6, "relocate_kernel", FAULT_ON_ERROR);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.r15 =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 0);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.r14 =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 1);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.r13 =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 2);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.r12 =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 3);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.bp =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 4);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.bx =
|
|
+ *(ulong *)(buf + sizeof(ulong) * 5);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, r15);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, r14);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, r13);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, r12);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, bp);
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, bx);
|
|
+ } else {
|
|
+ /* This is a try-best effort. Usually the call
|
|
+ inst will result in a next-inst addr pushed
|
|
+ in and a rbp push of the calling function.
|
|
+ So we can get rbp here */
|
|
+ readmem(extra_stacks_regs[extra_stacks_idx]->ur.sp
|
|
+ - sizeof(ulong) * 2, KVADDR,
|
|
+ &extra_stacks_regs[extra_stacks_idx]->ur.bp,
|
|
+ sizeof(ulong), "extra_stacks_regs.bp", FAULT_ON_ERROR);
|
|
+ if (INSTACK(extra_stacks_regs[extra_stacks_idx]->ur.bp, bt)) {
|
|
+ SET_REG_BITMAP(extra_stacks_regs[extra_stacks_idx]->bitmap,
|
|
+ x86_64_user_regs_struct, bp);
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.ip -= 5;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ close_tmpfile2();
|
|
+ /*
|
|
+ * bt->machdep is handled at x86_64_get_stack_frame(), so skip it
|
|
+ */
|
|
+ if (!bt->machdep ||
|
|
+ (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
|
|
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.ip !=
|
|
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.ip)) {
|
|
+ gdb_add_substack(extra_stacks_idx++);
|
|
+ }
|
|
+ }
|
|
|
|
if ((estack = x86_64_in_exception_stack(bt, &estack_index))) {
|
|
in_exception_stack:
|
|
@@ -4159,6 +4247,7 @@ x86_64_dwarf_back_trace_cmd(struct bt_info *bt_in)
|
|
last_process_stack_eframe = 0;
|
|
bt->call_target = NULL;
|
|
bt->bptr = 0;
|
|
+ extra_stacks_idx = 0;
|
|
rsp = bt->stkptr;
|
|
if (!rsp) {
|
|
error(INFO, "cannot determine starting stack pointer\n");
|
|
@@ -4799,6 +4888,31 @@ x86_64_exception_frame(ulong flags, ulong kvaddr, char *local,
|
|
} else if (machdep->flags & ORC)
|
|
bt->bptr = rbp;
|
|
|
|
+ /*
|
|
+ * Preserve registers set for each additional in-kernel stack
|
|
+ */
|
|
+ if (!(cs & 3) && verified && flags & EFRAME_PRINT &&
|
|
+ extra_stacks_idx < MAX_EXCEPTION_STACKS &&
|
|
+ !(bt->flags & BT_EFRAME_SEARCH)) {
|
|
+ if (!extra_stacks_regs[extra_stacks_idx]) {
|
|
+ extra_stacks_regs[extra_stacks_idx] = (struct user_regs_bitmap_struct *)
|
|
+ malloc(sizeof(struct user_regs_bitmap_struct));
|
|
+ }
|
|
+ memset(extra_stacks_regs[extra_stacks_idx], 0,
|
|
+ sizeof(struct user_regs_bitmap_struct));
|
|
+ memcpy(&extra_stacks_regs[extra_stacks_idx]->ur,
|
|
+ pt_regs_buf, SIZE(pt_regs));
|
|
+ for (int i = 0; i < SIZE(pt_regs)/sizeof(long); i++)
|
|
+ SET_BIT(extra_stacks_regs[extra_stacks_idx]->bitmap, i);
|
|
+ if (!bt->machdep ||
|
|
+ (extra_stacks_regs[extra_stacks_idx]->ur.sp !=
|
|
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.sp &&
|
|
+ extra_stacks_regs[extra_stacks_idx]->ur.ip !=
|
|
+ ((struct user_regs_bitmap_struct *)(bt->machdep))->ur.ip)) {
|
|
+ gdb_add_substack(extra_stacks_idx++);
|
|
+ }
|
|
+ }
|
|
+
|
|
if (kvaddr)
|
|
FREEBUF(pt_regs_buf);
|
|
|
|
@@ -5002,9 +5116,6 @@ get_reg_from_inactive_task_frame(struct bt_info *bt, char *reg_name,
|
|
return reg_value;
|
|
}
|
|
|
|
-#define SET_REG_BITMAP(REGMAP, TYPE, MEMBER) \
|
|
- SET_BIT(REGMAP, REG_SEQ(TYPE, MEMBER))
|
|
-
|
|
/*
|
|
* Get a stack frame combination of pc and ra from the most relevent spot.
|
|
*/
|
|
@@ -9221,7 +9332,8 @@ x86_64_get_kvaddr_ranges(struct vaddr_range *vrp)
|
|
case R##_REGNUM: \
|
|
if (!NUM_IN_BITMAP(ur_bitmap->bitmap, \
|
|
REG_SEQ(x86_64_user_regs_struct, r))) { \
|
|
- FREEBUF(ur_bitmap); \
|
|
+ if (!sid) \
|
|
+ FREEBUF(ur_bitmap); \
|
|
return FALSE; \
|
|
} \
|
|
break;
|
|
@@ -9256,6 +9368,12 @@ x86_64_get_current_task_reg(int regno, const char *name,
|
|
if (!tc)
|
|
return FALSE;
|
|
|
|
+ /* Non zero stack ID, use extra stacks regs */
|
|
+ if (sid && sid <= extra_stacks_idx) {
|
|
+ ur_bitmap = extra_stacks_regs[sid - 1];
|
|
+ goto get_sub;
|
|
+ }
|
|
+
|
|
/*
|
|
* Task is active, grab CPU's registers
|
|
*/
|
|
@@ -9280,6 +9398,7 @@ x86_64_get_current_task_reg(int regno, const char *name,
|
|
}
|
|
|
|
/* Get subset registers from stack frame*/
|
|
+get_sub:
|
|
switch (regno) {
|
|
CHECK_REG_CASE(RAX, ax);
|
|
CHECK_REG_CASE(RBX, bx);
|
|
@@ -9341,7 +9460,7 @@ get_all:
|
|
COPY_REG_CASE(ORIG_RAX, orig_ax);
|
|
}
|
|
|
|
- if (bt_info.need_free) {
|
|
+ if (!sid && bt_info.need_free) {
|
|
FREEBUF(ur_bitmap);
|
|
bt_info.need_free = FALSE;
|
|
}
|
|
@@ -9805,4 +9924,4 @@ x86_64_swp_offset(ulong entry)
|
|
return SWP_OFFSET(entry);
|
|
}
|
|
|
|
-#endif /* X86_64 */
|
|
+#endif /* X86_64 */
|
|
--
|
|
2.47.0
|
|
|