| From 00e4268f474b94e34fae1c9880baa3ce5e6b7037 Mon Sep 17 00:00:00 2001 |
| From: Nathan Huckleberry <nhuck@google.com> |
| Date: Fri, 10 Jul 2020 20:23:37 +0100 |
| Subject: [PATCH] ARM: 8992/1: Fix unwind_frame for clang-built kernels |
| |
| commit b4d5ec9b39f8b31d98f65bc5577b5d15d93795d7 upstream. |
| |
| 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/912 |
| |
| Reported-by: Miles Chen <miles.chen@mediatek.com> |
| Tested-by: Miles Chen <miles.chen@mediatek.com> |
| Cc: stable@vger.kernel.org |
| Reviewed-by: Nick Desaulniers <ndesaulniers@google.com> |
| Signed-off-by: Nathan Huckleberry <nhuck@google.com> |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c |
| index 71778bb0475b..a082f6e4f0f4 100644 |
| --- a/arch/arm/kernel/stacktrace.c |
| +++ b/arch/arm/kernel/stacktrace.c |
| @@ -22,6 +22,19 @@ |
| * A simple function epilogue looks like this: |
| * 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 |
| * prologue and epilogue, therefore we can ignore the LR value in this case. |
| */ |
| @@ -34,6 +47,16 @@ int notrace unwind_frame(struct stackframe *frame) |
| low = frame->sp; |
| 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 */ |
| if (fp < low + 12 || fp > high - 4) |
| return -EINVAL; |
| @@ -42,6 +65,7 @@ int notrace unwind_frame(struct stackframe *frame) |
| frame->fp = *(unsigned long *)(fp - 12); |
| frame->sp = *(unsigned long *)(fp - 8); |
| frame->pc = *(unsigned long *)(fp - 4); |
| +#endif |
| |
| return 0; |
| } |
| -- |
| 2.27.0 |
| |