[RFC PATCH v2 14/15] unwind_user/backchain: Introduce back chain user space unwinding

Jens Remus posted 15 patches 1 week, 6 days ago
There is a newer version of this series
[RFC PATCH v2 14/15] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Jens Remus 1 week, 6 days ago
Add support for unwinding of user space using back chain to the
unwind user interface.  Use it as secondary fallback for unwinding
using SFrame, if that fails and the primary fallback using frame
pointer is not available.

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

Notes (jremus):
    Changes in RFC v2:
    - Adjusted to latest unwind user enhancements.  Does hopefully no longer
      appear grafted on.

 arch/Kconfig                          |  6 ++++++
 include/linux/unwind_user_backchain.h | 20 ++++++++++++++++++++
 include/linux/unwind_user_types.h     |  2 ++
 kernel/unwind/user.c                  | 12 ++++++++++++
 4 files changed, 40 insertions(+)
 create mode 100644 include/linux/unwind_user_backchain.h

diff --git a/arch/Kconfig b/arch/Kconfig
index 7fa89d70b244..37fb78a5e876 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -488,6 +488,12 @@ config AS_SFRAME
 config UNWIND_USER
 	bool
 
+config HAVE_UNWIND_USER_BACKCHAIN
+	bool
+	select UNWIND_USER
+	help
+	  The arch supports unwinding of user space using back chain.
+
 config HAVE_UNWIND_USER_FP
 	bool
 	select UNWIND_USER
diff --git a/include/linux/unwind_user_backchain.h b/include/linux/unwind_user_backchain.h
new file mode 100644
index 000000000000..e7a8e584b13f
--- /dev/null
+++ b/include/linux/unwind_user_backchain.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_UNWIND_USER_BACKCHAIN_H
+#define _LINUX_UNWIND_USER_BACKCHAIN_H
+
+struct unwind_user_state;
+
+#ifdef CONFIG_HAVE_UNWIND_USER_BACKCHAIN
+
+extern int arch_unwind_user_next_backchain(struct unwind_user_state *state);
+
+#else /* !CONFIG_HAVE_UNWIND_USER_BACKCHAIN */
+
+static inline int arch_unwind_user_next_backchain(struct unwind_user_state *state)
+{
+	return -EINVAL;
+}
+
+#endif /* !CONFIG_HAVE_UNWIND_USER_BACKCHAIN */
+
+#endif /* _LINUX_UNWIND_USER_BACKCHAIN_H */
diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h
index 6efc12b6e831..b44502e90b7f 100644
--- a/include/linux/unwind_user_types.h
+++ b/include/linux/unwind_user_types.h
@@ -11,6 +11,7 @@
 enum unwind_user_type_bits {
 	UNWIND_USER_TYPE_SFRAME_BIT =		0,
 	UNWIND_USER_TYPE_FP_BIT =		1,
+	UNWIND_USER_TYPE_BACKCHAIN_BIT =	2,
 
 	NR_UNWIND_USER_TYPE_BITS,
 };
@@ -20,6 +21,7 @@ enum unwind_user_type {
 	UNWIND_USER_TYPE_NONE =			0,
 	UNWIND_USER_TYPE_SFRAME =		BIT(UNWIND_USER_TYPE_SFRAME_BIT),
 	UNWIND_USER_TYPE_FP =			BIT(UNWIND_USER_TYPE_FP_BIT),
+	UNWIND_USER_TYPE_BACKCHAIN =		BIT(UNWIND_USER_TYPE_BACKCHAIN_BIT),
 };
 
 struct unwind_stacktrace {
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index 122045cb411f..5b4649bc91ba 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -6,6 +6,7 @@
 #include <linux/sched.h>
 #include <linux/sched/task_stack.h>
 #include <linux/unwind_user.h>
+#include <linux/unwind_user_backchain.h>
 #include <linux/uaccess.h>
 #include <linux/sframe.h>
 
@@ -105,6 +106,11 @@ static int unwind_user_next_common(struct unwind_user_state *state,
 	return 0;
 }
 
+static int unwind_user_next_backchain(struct unwind_user_state *state)
+{
+	return arch_unwind_user_next_backchain(state);
+}
+
 static int unwind_user_next_fp(struct unwind_user_state *state)
 {
 	struct pt_regs *regs = task_pt_regs(current);
@@ -159,6 +165,10 @@ static int unwind_user_next(struct unwind_user_state *state)
 			if (!unwind_user_next_fp(state))
 				return 0;
 			continue;
+		case UNWIND_USER_TYPE_BACKCHAIN:
+			if (!unwind_user_next_backchain(state))
+				return 0;
+			continue;		/* Try next method. */
 		default:
 			WARN_ONCE(1, "Undefined unwind bit %d", bit);
 			break;
@@ -187,6 +197,8 @@ static int unwind_user_start(struct unwind_user_state *state)
 		state->available_types |= UNWIND_USER_TYPE_SFRAME;
 	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
 		state->available_types |= UNWIND_USER_TYPE_FP;
+	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN))
+		state->available_types |= UNWIND_USER_TYPE_BACKCHAIN;
 
 	state->ip = instruction_pointer(regs);
 	state->sp = user_stack_pointer(regs);
-- 
2.51.0
Re: [RFC PATCH v2 14/15] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Josh Poimboeuf 1 week, 4 days ago
On Fri, Dec 05, 2025 at 06:14:45PM +0100, Jens Remus wrote:
> @@ -159,6 +165,10 @@ static int unwind_user_next(struct unwind_user_state *state)
>  			if (!unwind_user_next_fp(state))
>  				return 0;
>  			continue;
> +		case UNWIND_USER_TYPE_BACKCHAIN:
> +			if (!unwind_user_next_backchain(state))
> +				return 0;
> +			continue;		/* Try next method. */
>  		default:
>  			WARN_ONCE(1, "Undefined unwind bit %d", bit);
>  			break;
> @@ -187,6 +197,8 @@ static int unwind_user_start(struct unwind_user_state *state)
>  		state->available_types |= UNWIND_USER_TYPE_SFRAME;
>  	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
>  		state->available_types |= UNWIND_USER_TYPE_FP;
> +	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN))
> +		state->available_types |= UNWIND_USER_TYPE_BACKCHAIN;

Any reason not to just use the existing CONFIG_HAVE_UNWIND_USER_FP hook
here rather than create the new BACKCHAIN one?

-- 
Josh
Re: [RFC PATCH v2 14/15] unwind_user/backchain: Introduce back chain user space unwinding
Posted by Jens Remus 1 week, 3 days ago
Hello Josh,

thank you for your feedback!

On 12/7/2025 4:10 PM, Josh Poimboeuf wrote:
> On Fri, Dec 05, 2025 at 06:14:45PM +0100, Jens Remus wrote:
>> @@ -159,6 +165,10 @@ static int unwind_user_next(struct unwind_user_state *state)
>>  			if (!unwind_user_next_fp(state))
>>  				return 0;
>>  			continue;
>> +		case UNWIND_USER_TYPE_BACKCHAIN:
>> +			if (!unwind_user_next_backchain(state))
>> +				return 0;
>> +			continue;		/* Try next method. */
>>  		default:
>>  			WARN_ONCE(1, "Undefined unwind bit %d", bit);
>>  			break;
>> @@ -187,6 +197,8 @@ static int unwind_user_start(struct unwind_user_state *state)
>>  		state->available_types |= UNWIND_USER_TYPE_SFRAME;
>>  	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_FP))
>>  		state->available_types |= UNWIND_USER_TYPE_FP;
>> +	if (IS_ENABLED(CONFIG_HAVE_UNWIND_USER_BACKCHAIN))
>> +		state->available_types |= UNWIND_USER_TYPE_BACKCHAIN;
> 
> Any reason not to just use the existing CONFIG_HAVE_UNWIND_USER_FP hook
> here rather than create the new BACKCHAIN one?

At first I thought this would not be a good idea, as my unwind user
backchain implementation relies on being standalone without using
unwind_user_next_common().  Mainly because s390 back chain unwinding
does not have fixed CFA, FP, and RA offsets/locations.  But then I gave
it a try and it does not look that bad actually.

I'll send a RFC v3 soon.

Regards,
Jens
-- 
Jens Remus
Linux on Z Development (D3303)
+49-7031-16-1128 Office
jremus@de.ibm.com

IBM

IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Böblingen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/