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

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/912



Reported-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
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -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;
}