Commit b4d5ec9b authored by Nathan Huckleberry's avatar Nathan Huckleberry Committed by Russell King

ARM: 8992/1: Fix unwind_frame for clang-built kernels

Since clang does not push pc and sp in function prologues, the current
implementation of unwind_frame does not work. By using the previous
frame's lr/fp instead of saved pc/sp we get valid unwinds on clang-built
kernels.

The bounds check on next frame pointer must be changed as well since
there are 8 less bytes between frames.

This fixes /proc/<pid>/stack.

Link: https://github.com/ClangBuiltLinux/linux/issues/912Reported-by: default avatarMiles Chen <miles.chen@mediatek.com>
Tested-by: default avatarMiles Chen <miles.chen@mediatek.com>
Cc: stable@vger.kernel.org
Reviewed-by: default avatarNick Desaulniers <ndesaulniers@google.com>
Signed-off-by: default avatarNathan Huckleberry <nhuck@google.com>
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 2cbd1cc3
...@@ -22,6 +22,19 @@ ...@@ -22,6 +22,19 @@
* A simple function epilogue looks like this: * A simple function epilogue looks like this:
* ldm sp, {fp, sp, pc} * ldm sp, {fp, sp, pc}
* *
* When compiled with clang, pc and sp are not pushed. A simple function
* prologue looks like this when built with clang:
*
* stmdb {..., fp, lr}
* add fp, sp, #x
* sub sp, sp, #y
*
* A simple function epilogue looks like this when built with clang:
*
* sub sp, fp, #x
* ldm {..., fp, pc}
*
*
* Note that with framepointer enabled, even the leaf functions have the same * Note that with framepointer enabled, even the leaf functions have the same
* prologue and epilogue, therefore we can ignore the LR value in this case. * prologue and epilogue, therefore we can ignore the LR value in this case.
*/ */
...@@ -34,6 +47,16 @@ int notrace unwind_frame(struct stackframe *frame) ...@@ -34,6 +47,16 @@ int notrace unwind_frame(struct stackframe *frame)
low = frame->sp; low = frame->sp;
high = ALIGN(low, THREAD_SIZE); high = ALIGN(low, THREAD_SIZE);
#ifdef CONFIG_CC_IS_CLANG
/* check current frame pointer is within bounds */
if (fp < low + 4 || fp > high - 4)
return -EINVAL;
frame->sp = frame->fp;
frame->fp = *(unsigned long *)(fp);
frame->pc = frame->lr;
frame->lr = *(unsigned long *)(fp + 4);
#else
/* check current frame pointer is within bounds */ /* check current frame pointer is within bounds */
if (fp < low + 12 || fp > high - 4) if (fp < low + 12 || fp > high - 4)
return -EINVAL; return -EINVAL;
...@@ -42,6 +65,7 @@ int notrace unwind_frame(struct stackframe *frame) ...@@ -42,6 +65,7 @@ int notrace unwind_frame(struct stackframe *frame)
frame->fp = *(unsigned long *)(fp - 12); frame->fp = *(unsigned long *)(fp - 12);
frame->sp = *(unsigned long *)(fp - 8); frame->sp = *(unsigned long *)(fp - 8);
frame->pc = *(unsigned long *)(fp - 4); frame->pc = *(unsigned long *)(fp - 4);
#endif
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment