[RFC PATCH v3 16/17] unwind_user/fp: Use arch-specific helper to initialize FP frame

Jens Remus
2025-12-08T17:16:47Z

This enables more sophisticated initialization of the FP frame, for
instance to implement support for unwinding of user space using back
chain on s390 with a subsequent commit.

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---

Notes (jremus):
Changes in RFC v3:
- New patch. Prerequirement to implement unwind user fp using back
chain on s390.

arch/x86/include/asm/unwind_user.h | 20 +++++++++++++++++---
include/linux/unwind_user.h | 20 +++++++-------------
kernel/unwind/user.c | 16 ++++------------
3 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/arch/x86/include/asm/unwind_user.h b/arch/x86/include/asm/unwind_user.h
index 2480d86a405e..ca581edecb9d 100644
--- a/arch/x86/include/asm/unwind_user.h
+++ b/arch/x86/include/asm/unwind_user.h
@@ -46,11 +46,25 @@ static inline int unwind_user_word_size(struct pt_regs *regs)
.use_fp = false, \
.outermost = false,

-static inline bool unwind_user_at_function_start(struct pt_regs *regs)
+static inline int unwind_user_fp_get_frame(struct unwind_user_state *state,
+ struct unwind_user_frame *frame)
{
- return is_uprobe_at_func_entry(regs);
+ struct pt_regs *regs = task_pt_regs(current);
+
+ if (state->topmost && is_uprobe_at_func_entry(regs)) {
+ const struct unwind_user_frame fp_entry_frame = {
+ ARCH_INIT_USER_FP_ENTRY_FRAME(state->ws)
+ };
+ *frame = fp_entry_frame;
+ } else {
+ const struct unwind_user_frame fp_frame = {
+ ARCH_INIT_USER_FP_FRAME(state->ws)
+ };
+ *frame = fp_frame;
+ }
+ return 0;
}
-#define unwind_user_at_function_start unwind_user_at_function_start
+#define unwind_user_fp_get_frame unwind_user_fp_get_frame

#endif /* CONFIG_HAVE_UNWIND_USER_FP */

diff --git a/include/linux/unwind_user.h b/include/linux/unwind_user.h
index 61fd5c05d0f0..4adab1a612a6 100644
--- a/include/linux/unwind_user.h
+++ b/include/linux/unwind_user.h
@@ -7,21 +7,15 @@

#ifndef CONFIG_HAVE_UNWIND_USER_FP

-#define ARCH_INIT_USER_FP_FRAME(ws)
-
-#endif
-
-#ifndef ARCH_INIT_USER_FP_ENTRY_FRAME
-#define ARCH_INIT_USER_FP_ENTRY_FRAME(ws)
-#endif
-
-#ifndef unwind_user_at_function_start
-static inline bool unwind_user_at_function_start(struct pt_regs *regs)
+static inline int unwind_user_fp_get_frame(struct unwind_user_state *state,
+ struct unwind_user_frame *frame)
{
- return false;
+ WARN_ON_ONCE(1);
+ return -EINVAL;
}
-#define unwind_user_at_function_start unwind_user_at_function_start
-#endif
+#define unwind_user_fp_get_frame unwind_user_fp_get_frame
+
+#endif /* CONFIG_HAVE_UNWIND_USER_FP */

#ifndef unwind_user_get_ra_reg
static inline int unwind_user_get_ra_reg(unsigned long *val)
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index 7d06bdbc7f0d..6877242ceae3 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -111,19 +111,11 @@ static int unwind_user_next_common(struct unwind_user_state *state,

static int unwind_user_next_fp(struct unwind_user_state *state)
{
- struct pt_regs *regs = task_pt_regs(current);
-
- if (state->topmost && unwind_user_at_function_start(regs)) {
- const struct unwind_user_frame fp_entry_frame = {
- ARCH_INIT_USER_FP_ENTRY_FRAME(state->ws)
- };
- return unwind_user_next_common(state, &fp_entry_frame);
- }
+ struct unwind_user_frame frame;

- const struct unwind_user_frame fp_frame = {
- ARCH_INIT_USER_FP_FRAME(state->ws)
- };
- return unwind_user_next_common(state, &fp_frame);
+ if (unwind_user_fp_get_frame(state, &frame))
+ return -ENOENT;
+ return unwind_user_next_common(state, &frame);
}

static int unwind_user_next_sframe(struct unwind_user_state *state)
--
2.51.0