aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Hibbits <jhibbits@FreeBSD.org>2023-03-11 16:30:00 +0000
committerJustin Hibbits <jhibbits@FreeBSD.org>2023-03-11 16:35:27 +0000
commit3e1155ade1baab51458374efd0295bdf6db455fc (patch)
tree637fb6fb9438679d23ae34c8049e7f2ec79ef0d9
parent635ecbf47015c259226cb47a3de8fc00c9ed5924 (diff)
downloadsrc-3e1155ade1baab51458374efd0295bdf6db455fc.tar.gz
src-3e1155ade1baab51458374efd0295bdf6db455fc.zip
dtrace/powerpc: "Fix" stack traces across trap frames
In function boundary tracing the link register is not yet saved to the save stack location, so the save point contains whatever the previous 'lr' save was, or even garbage, at the time the trap is taken. Address this by explicitly loading the link register from the trap frame instead of the stack, and propagate that out.
-rw-r--r--sys/cddl/dev/dtrace/powerpc/dtrace_isa.c21
1 files changed, 15 insertions, 6 deletions
diff --git a/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c b/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
index cce1c907b5d8..2139be1ccec7 100644
--- a/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
+++ b/sys/cddl/dev/dtrace/powerpc/dtrace_isa.c
@@ -97,15 +97,18 @@ dtrace_sp_inkernel(uintptr_t sp)
}
static __inline void
-dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc)
+dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc, uintptr_t *lr)
{
vm_offset_t callpc;
struct trapframe *frame;
+ if (lr != 0 && *lr != 0)
+ callpc = *lr;
+ else
#ifdef __powerpc64__
- callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET64);
#else
- callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
+ callpc = *(vm_offset_t *)(sp + RETURN_OFFSET);
#endif
/*
@@ -121,6 +124,8 @@ dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc)
*nsp = frame->fixreg[1];
if (pc != NULL)
*pc = frame->srr0;
+ if (lr != NULL)
+ *lr = frame->lr;
return;
}
@@ -128,6 +133,9 @@ dtrace_next_sp_pc(uintptr_t sp, uintptr_t *nsp, uintptr_t *pc)
*nsp = *(uintptr_t *)sp;
if (pc != NULL)
*pc = callpc;
+ /* lr is only valid for trap frames */
+ if (lr != NULL)
+ *lr = 0;
}
void
@@ -135,7 +143,7 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
uint32_t *intrpc)
{
int depth = 0;
- uintptr_t osp, sp;
+ uintptr_t osp, sp, lr = 0;
vm_offset_t callpc;
pc_t caller = (pc_t) solaris_cpu[curcpu].cpu_dtrace_caller;
@@ -154,7 +162,8 @@ dtrace_getpcstack(pc_t *pcstack, int pcstack_limit, int aframes,
if (!dtrace_sp_inkernel(sp))
break;
osp = sp;
- dtrace_next_sp_pc(osp, &sp, &callpc);
+ dtrace_next_sp_pc(osp, &sp, &callpc, &lr);
+ //printf("sp: %#lx, pc: %#lx, lr: %#lx\n", sp, callpc, lr);
if (aframes > 0) {
aframes--;
@@ -513,7 +522,7 @@ dtrace_getstackdepth(int aframes)
depth++;
osp = sp;
- dtrace_next_sp_pc(sp, &sp, NULL);
+ dtrace_next_sp_pc(sp, &sp, NULL, NULL);
}
if (depth < aframes)
return (0);