[PATCH 3/3] arm64: perf: Add a config option saying 32-bit thumb code uses R11 for FP

From: Douglas Anderson
Date: Fri May 07 2021 - 16:55:49 EST


The frame pointer story for 32-bit ARM/Thumb code is a bit of a
nightmare. See the patch ("arm64: perf: perf_callchain_user() compat
support clang/non-APCS-gcc-arm") (including the inline comments) for
some details.

Apparently, all hope is not lost for some resolution to this story.
Recently it's been agreed upon that the frame pointer should be R11
across both ARM and Thumb. This should, at least, allow us to crawl
through mixed code.

Unfortunately I can't think of any automagic way to figure out if code
is using R7 or R11 for the frame pointer. We'll force the person
compiling the kernel to choose one or the other.

NOTE: apparently as-of right now (2021Q1) there are no compilers out
there that actually support this. Thus this patch is untested.
However, it's so simple that it feels right to land it now while
everyone is thinking about it. I have, at least, confirmed that
tracing Thumb code produced with the old compiler _breaks_ when I set
this option. ;-)

Signed-off-by: Douglas Anderson <dianders@xxxxxxxxxxxx>
---

arch/arm64/Kconfig | 12 ++++++++++++
arch/arm64/kernel/perf_callchain.c | 12 ++++++++----
2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 9f1d8566bbf9..f123736ac535 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1062,6 +1062,18 @@ config ARCH_SPARSEMEM_ENABLE
select SPARSEMEM_VMEMMAP_ENABLE
select SPARSEMEM_VMEMMAP

+config PERF_COMPAT_THUMB_FP_R11
+ bool "Assume userspace 32-bit Thumb code uses R11 for Frame pointer"
+ help
+ Historically R11 was the frame pointer (FP) for 32-bit ARM code
+ and R7 was the frame pointer for 32-bit Thumb code. This resulted in
+ the inability to use the FP for stack crawling with mixed code.
+ The 2019Q4 Issue of AAPCS revised the frame pointer to be R11 for
+ BOTH ARM and Thumb. If your userspace was built with this new
+ standard then say "yes" here.
+ depends on PERF_EVENTS
+ depends on COMPAT
+
config HW_PERF_EVENTS
def_bool y
depends on ARM_PMU
diff --git a/arch/arm64/kernel/perf_callchain.c b/arch/arm64/kernel/perf_callchain.c
index b3cd9f371469..c8187acdbf3f 100644
--- a/arch/arm64/kernel/perf_callchain.c
+++ b/arch/arm64/kernel/perf_callchain.c
@@ -311,12 +311,16 @@ static void compat_perf_callchain_user(struct perf_callchain_entry_ctx *entry,

/*
* Assuming userspace is compiled with frame pointers then it's in
- * R11 for ARM code and R7 for thumb code. If it's thumb mode we'll
- * also set the low bit of the PC to match how the PC indicates thumb
- * mode when crawling down the stack.
+ * R11 for ARM code and R7 for thumb code (unless you've got a really
+ * new compiler). If it's thumb mode we'll also set the low bit of
+ * the PC to match how the PC indicates thumb mode when crawling
+ * down the stack.
*/
if (compat_thumb_mode(regs)) {
- fp = regs->regs[7];
+ if (IS_ENABLED(CONFIG_PERF_COMPAT_THUMB_FP_R11))
+ fp = regs->regs[11];
+ else
+ fp = regs->regs[7];
pc |= BIT(0);
} else {
fp = regs->compat_fp;
--
2.31.1.607.g51e8a6a459-goog