public inbox for linux-trace-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/12] s390: SFrame user space unwinding
@ 2026-01-27 15:19 Jens Remus
  2026-01-27 15:19 ` [PATCH v4 01/12] s390: asm/dwarf.h should only be included in assembly files Jens Remus
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

This series adds s390 support for unwinding of user space using SFrame V3.
It is based on Josh's, Steven's, and my work (see prerequisites below).
The generic unwind user (sframe) frameworks are extended to enable
support for a few s390-particularities (see patches 5 and 6), including
unwinding of user space using back chain (see patches 10-12).


Changes in v4:
- Rebase on my user unwind sframe series v13, which provides SFrame V3
  support.

Changes in RFC v3:
- Rebase on and include my unwind user cleanup series v4, which includes
  a simplification of unwind_user_word_size() on x86. (Linus)
- Implement unwinding of user space using s390 back chain using unwind
  user fp instead of introducing a new unwind user backchain. (Josh)

Changes in RFC v2:
- Rebased on latest "unwind user" enhancements from Peter Zijlstra and
  my latest "unwind user sframe" series v12.
- Incorporated RFC v1 review feedback.
- No new config options (except for unwind user backchain).


Motivation:

On s390 unwinding using frame pointer (FP) is unsupported, because of
lack of proper s390 64-bit (s390x) ABI specification and compiler
support.  The ABI does only specify a "preferred" FP register.  Both GCC
and Clang, regardless of compiler option -fno-omit-frame-pointer, setup
the preferred FP register as late as possible, which usually is after
static stack allocation, so that the CFA cannot be deduced from the FP
without any further data, such as provided by DWARF CFI or SFrame.

In theory there is a s390-specific alternative of unwinding using
back chain (compiler option -mbackchain), but this has its own
limitations.  Ubuntu is currently the only distribution that that
builds user space with back chain.

As a consequence the Kernel stack tracer cannot unwind user space
(except if it is built with back chain).  Recording call graphs of user
space using perf is limited to stack dump sampling (i.e. perf record
--call-graph dwarf), which generates a fairly large amount of data and
has limitations.

Initial testing of recording call graphs using perf using the s390
support for SFrame provided by RFC v1 of this series shows that
data size notably improves:

perf record data size is greatly reduced (smaller perf.data):

  SFrame (--call-graph fp):
  # perf record -F 9999 --call-graph fp objdump -wdWF objdump
  [ perf record: Woken up 9 times to write data ]
  [ perf record: Captured and wrote 2.498 MB perf.data (10891 samples) ]

  Stack sampling (--call-graph dwarf) with a default stack size of 8192:
  # perf record -F 9999 --call-graph dwarf objdump -wdWF objdump
  [ perf record: Woken up 270 times to write data ]
  [ perf record: Captured and wrote 67.467 MB perf.data (8241 samples) ]


Prerequirements:

This series applies on top of the latest unwind user sframe series
"[PATCH v13 00/18] unwind_deferred: Implement sframe handling":
https://lore.kernel.org/all/20260127150554.2760964-1-jremus@linux.ibm.com/

Like above series it depends on the upcoming binutils 2.46 release to
be used to build executables and libraries (e.g. vDSO) with SFrame V3
on s390 (using the assembler option --gsframe-3).

The unwind user sframe series depends on a Glibc patch from Josh, that
adds support for the prctls introduced in the Kernel:
https://lore.kernel.org/all/20250122023517.lmztuocecdjqzfhc@jpoimboe/
Note that Josh's Glibc patch needs to be adjusted for the updated prctl
numbers from "[PATCH v13 18/18] unwind_user/sframe: Add prctl() interface
for registering .sframe sections":
https://lore.kernel.org/all/20260127150554.2760964-19-jremus@linux.ibm.com/


Overview:

Patch 1 aligns asm/dwarf.h to x86 asm/dwarf2.h. [*]

Patch 2 replicates Josh's x86 patch "x86/asm: Avoid emitting DWARF
CFI for non-VDSO" for s390.  [*]

Patch 3 changes the build of the vDSO on s390 to keep the function
symbols for stack tracing purposes. [*]

Patch 4 replicates Josh's patch "x86/vdso: Enable sframe generation
in VDSO" for s390.  It enables generation of SFrame V3 stack trace
information (.sframe section) for the vDSO if the assembler supports it.

Patches 5 and 6 enable the generic unwind user (sframe) frameworks to
support the following s390 particularities:

- Patch 5 adds support for architectures that define their CFA as SP at
  callsite + offset.

- Patch 6 adds support for architectures that store the CFA offset
  from CFA base register (e.g. SP or FP) in SFrame encoded.  For
  instance on s390 the CFA offset is stored adjusted by -160 and
  then scaled down by 8 to enable and improve the use of signed 8-bit
  SFrame offsets (i.e. CFA, RA, and FP offset).

Patch 7 converts several s390 ptrace function macros to inline
functions. [*]

Patch 8 introduces frame_pointer() in ptrace on s390, which is a
prerequisite for enabling unwind user.

Patch 9 adds support for unwinding of user space using SFrame on
s390.  It leverages the extensions of the generic unwind user (sframe)
frameworks from patches 5 and 6, as well as the unwind user sframe
series support for SFrame V3 flexible FDEs.

Patches 10 and 11 enable unwind user (fp) to support the following s390
back chain particularities:

- Patch 10 introduces FP/RA restore rule ZERO, which enables s390
  back chain unwinding, which cannot unwind FP.

- Patch 11 enables sophisticated architecture-specific initialization
  of the FP frame, which enables s390 back chain unwinding to provide
  dynamic information.

Patch 12 adds support for unwinding of user space using back chain on
s390.  Main reasons to support back chain on s390 are:
- With Ubuntu there is a major distribution that builds user space with
  back chain.
- Java JREs, such as OpenJDK, do maintain the back chain in jitted code.


Limitations:

Unwinding of user space using back chain cannot - by design - restore
the FP.  Therefore unwinding of subsequent frames using e.g. SFrame may
fail, if the FP is the CFA base register.

Thanks and regards,
Jens

Jens Remus (12):
  s390: asm/dwarf.h should only be included in assembly files
  s390/vdso: Avoid emitting DWARF CFI for non-vDSO
  s390/vdso: Keep function symbols in vDSO
  s390/vdso: Enable SFrame V3 generation in vDSO
  unwind_user: Enable archs that define CFA = SP_callsite + offset
  unwind_user/sframe: Enable archs with encoded SFrame CFA offsets
  s390/ptrace: Convert function macros to inline functions
  s390/ptrace: Provide frame_pointer()
  s390/unwind_user/sframe: Enable sframe unwinding on s390
  unwind_user: Introduce FP/RA recovery rule unknown
  unwind_user/fp: Use arch-specific helper to initialize FP frame
  s390/unwind_user/fp: Enable back chain unwinding of user space

 arch/Kconfig                               |   7 ++
 arch/s390/Kconfig                          |   2 +
 arch/s390/include/asm/dwarf.h              |  53 ++++++---
 arch/s390/include/asm/ptrace.h             |  43 +++++--
 arch/s390/include/asm/unwind_user.h        | 132 +++++++++++++++++++++
 arch/s390/include/asm/unwind_user_sframe.h |  21 ++++
 arch/s390/kernel/vdso/Makefile             |   9 +-
 arch/s390/kernel/vdso/vdso.lds.S           |   9 ++
 arch/x86/include/asm/unwind_user.h         |  21 +++-
 arch/x86/include/asm/unwind_user_sframe.h  |   2 +
 include/asm-generic/Kbuild                 |   1 +
 include/asm-generic/unwind_user_sframe.h   |  20 ++++
 include/linux/unwind_user.h                |  19 +--
 include/linux/unwind_user_types.h          |   2 +
 kernel/unwind/sframe.c                     |   5 +-
 kernel/unwind/sframe.h                     |  11 ++
 kernel/unwind/user.c                       |  31 +++--
 17 files changed, 323 insertions(+), 65 deletions(-)
 create mode 100644 arch/s390/include/asm/unwind_user.h
 create mode 100644 arch/s390/include/asm/unwind_user_sframe.h
 create mode 100644 include/asm-generic/unwind_user_sframe.h

-- 
2.51.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v4 01/12] s390: asm/dwarf.h should only be included in assembly files
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 02/12] s390/vdso: Avoid emitting DWARF CFI for non-vDSO Jens Remus
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Align to x86 and add a compile-time check that asm/dwarf.h is only
included in pure assembly files.

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

Notes (jremus):
    Changes in RFC v2:
    - Adjust to upstream change of __ASSEMBLY__ to __ASSEMBLER__.

 arch/s390/include/asm/dwarf.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/s390/include/asm/dwarf.h b/arch/s390/include/asm/dwarf.h
index e3ad6798d0cd..df9f467910f7 100644
--- a/arch/s390/include/asm/dwarf.h
+++ b/arch/s390/include/asm/dwarf.h
@@ -2,7 +2,9 @@
 #ifndef _ASM_S390_DWARF_H
 #define _ASM_S390_DWARF_H
 
-#ifdef __ASSEMBLER__
+#ifndef __ASSEMBLER__
+#warning "asm/dwarf.h should be only included in pure assembly files"
+#endif
 
 #define CFI_STARTPROC		.cfi_startproc
 #define CFI_ENDPROC		.cfi_endproc
@@ -33,6 +35,4 @@
 	.cfi_sections .eh_frame, .debug_frame
 #endif
 
-#endif	/* __ASSEMBLER__ */
-
 #endif	/* _ASM_S390_DWARF_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 02/12] s390/vdso: Avoid emitting DWARF CFI for non-vDSO
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
  2026-01-27 15:19 ` [PATCH v4 01/12] s390: asm/dwarf.h should only be included in assembly files Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 03/12] s390/vdso: Keep function symbols in vDSO Jens Remus
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

This replicates Josh's x86 commit TODO ("x86/asm: Avoid emitting DWARF
CFI for non-VDSO") for s390.  It also aligns asm/dwarf.h to x86
asm/dwarf2.h.

It was decided years ago that .cfi_* annotations aren't maintainable in
the kernel.  For the kernel proper, ensure the CFI_* macros don't do
anything.

On the other hand the vDSO library *does* use them, so user space can
unwind through it.

Make sure these macros only work for vDSO.  They aren't actually being
used outside of vDSO anyway, so there's no functional change.

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

Notes (jremus):
    Link to latest x86 patch:
    https://lore.kernel.org/all/20250425024022.477374378@goodmis.org/

 arch/s390/include/asm/dwarf.h | 45 ++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 16 deletions(-)

diff --git a/arch/s390/include/asm/dwarf.h b/arch/s390/include/asm/dwarf.h
index df9f467910f7..6bcf37256feb 100644
--- a/arch/s390/include/asm/dwarf.h
+++ b/arch/s390/include/asm/dwarf.h
@@ -6,6 +6,18 @@
 #warning "asm/dwarf.h should be only included in pure assembly files"
 #endif
 
+.macro nocfi args:vararg
+.endm
+
+#ifdef BUILD_VDSO
+
+	/*
+	 * For the vDSO, emit both runtime unwind information and debug
+	 * symbols for the .dbg file.
+	 */
+
+	.cfi_sections .eh_frame, .debug_frame
+
 #define CFI_STARTPROC		.cfi_startproc
 #define CFI_ENDPROC		.cfi_endproc
 #define CFI_DEF_CFA_OFFSET	.cfi_def_cfa_offset
@@ -16,23 +28,24 @@
 #ifdef CONFIG_AS_CFI_VAL_OFFSET
 #define CFI_VAL_OFFSET		.cfi_val_offset
 #else
-#define CFI_VAL_OFFSET		#
+#define CFI_VAL_OFFSET		nocfi
 #endif
 
-#ifndef BUILD_VDSO
-	/*
-	 * Emit CFI data in .debug_frame sections and not in .eh_frame
-	 * sections.  The .eh_frame CFI is used for runtime unwind
-	 * information that is not being used.  Hence, vmlinux.lds.S
-	 * can discard the .eh_frame sections.
-	 */
-	.cfi_sections .debug_frame
-#else
-	/*
-	 * For vDSO, emit CFI data in both, .eh_frame and .debug_frame
-	 * sections.
-	 */
-	.cfi_sections .eh_frame, .debug_frame
-#endif
+#else /* !BUILD_VDSO */
+
+/*
+ * On s390, these macros aren't used outside vDSO.  As well they shouldn't be:
+ * they're fragile and very difficult to maintain.
+ */
+
+#define CFI_STARTPROC		nocfi
+#define CFI_ENDPROC		nocfi
+#define CFI_DEF_CFA_OFFSET	nocfi
+#define CFI_ADJUST_CFA_OFFSET	nocfi
+#define CFI_RESTORE		nocfi
+#define CFI_REL_OFFSET		nocfi
+#define CFI_VAL_OFFSET		nocfi
+
+#endif /* !BUILD_VDSO */
 
 #endif	/* _ASM_S390_DWARF_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 03/12] s390/vdso: Keep function symbols in vDSO
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
  2026-01-27 15:19 ` [PATCH v4 01/12] s390: asm/dwarf.h should only be included in assembly files Jens Remus
  2026-01-27 15:19 ` [PATCH v4 02/12] s390/vdso: Avoid emitting DWARF CFI for non-vDSO Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 04/12] s390/vdso: Enable SFrame V3 generation " Jens Remus
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Keep all function symbols in the vDSO .symtab for stack trace purposes.
This enables a stack tracer, such as perf, to lookup these function
symbols in addition to those already exported in vDSO .dynsym.

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

Notes (jremus):
    Changes in RFC v2:
    - Use objcopy flag "-g" instead of "-S" with the cumbersome filter
      "-w -K "__arch_*" -K "__cvdso_*" -K "__s390_vdso_*" to keep the
      function symbols, as Josh did in "x86/vdso: Enable sframe
      generation in VDSO":
      https://lore.kernel.org/all/20250425024023.173709192@goodmis.org/
    - Reword commit message.
    
    Note that unlike Josh I did not squash this into the subsequent patch
    "s390/vdso: Enable SFrame generation in vDSO", as this change is
    unrelated to enabling the use of SFrame.  perf report/script do also
    benefit from this change when using perf record --call-graph dwarf.
    
    Note that this change does not cause the vDSO build-id to change.
    perf record may therefore not dump an updated copy of the vDSO to
    ~/.debug/[vdso]/<build-id>/vdso, so that perf report/script may
    use a stale copy without .symtab.  Resolve by deleting ~/.debug/.

 arch/s390/kernel/vdso/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index 2fa12d4ac106..3a8bde5716f4 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -50,7 +50,7 @@ $(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) $(obj-cvdso) FORCE
 	$(call if_changed,vdso_and_check)
 
 # strip rule for the .so file
-$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: OBJCOPYFLAGS := -g
 $(obj)/%.so: $(obj)/%.so.dbg FORCE
 	$(call if_changed,objcopy)
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 04/12] s390/vdso: Enable SFrame V3 generation in vDSO
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (2 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 03/12] s390/vdso: Keep function symbols in vDSO Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 05/12] unwind_user: Enable archs that define CFA = SP_callsite + offset Jens Remus
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

This replicates Josh's x86 patch "x86/vdso: Enable sframe generation
in VDSO" [1] for s390.

Test whether the assembler supports generating SFrame V3 stack trace
information.  Note that it is insufficient to test whether the assembler
supports option --gsframe-3, as GNU assembler supports that regardless
of whether it is actually capable of generating SFrame V3 stack trace
information for the architecture.

If so enable SFrame V3 stack trace information generation in the vDSO
library so kernel and user space can unwind through it.

[1]: x86/vdso: Enable sframe generation in VDSO,
     https://lore.kernel.org/all/20250425024023.173709192@goodmis.org/

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

Notes (jremus):
    Changes in v4:
    - Test for and generate SFrame V3 using assembler option --gsframe-3.
    
    Changes in RFC v2:
    - Introduce config option AS_SFRAME instead of requiring Josh's x86
      patch as pre-requisite.
    - Reword commit message.
    
    Link to Josh's latest x86 patch:
    https://lore.kernel.org/all/20250425024023.173709192@goodmis.org/

 arch/Kconfig                     | 7 +++++++
 arch/s390/include/asm/dwarf.h    | 4 ++++
 arch/s390/kernel/vdso/Makefile   | 7 ++++++-
 arch/s390/kernel/vdso/vdso.lds.S | 9 +++++++++
 4 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index dcb553136e0c..b5b3a83072f7 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -479,6 +479,13 @@ config HAVE_HARDLOCKUP_DETECTOR_ARCH
 	  It uses the same command line parameters, and sysctl interface,
 	  as the generic hardlockup detectors.
 
+config AS_SFRAME
+	bool
+
+config AS_SFRAME3
+	def_bool $(as-instr,.cfi_startproc\n.cfi_endproc,-Wa$(comma)--gsframe-3)
+	select AS_SFRAME
+
 config UNWIND_USER
 	bool
 
diff --git a/arch/s390/include/asm/dwarf.h b/arch/s390/include/asm/dwarf.h
index 6bcf37256feb..2f148b15fd7d 100644
--- a/arch/s390/include/asm/dwarf.h
+++ b/arch/s390/include/asm/dwarf.h
@@ -16,7 +16,11 @@
 	 * symbols for the .dbg file.
 	 */
 
+#ifdef CONFIG_AS_SFRAME
+	.cfi_sections .eh_frame, .debug_frame, .sframe
+#else
 	.cfi_sections .eh_frame, .debug_frame
+#endif
 
 #define CFI_STARTPROC		.cfi_startproc
 #define CFI_ENDPROC		.cfi_endproc
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index 3a8bde5716f4..d604839756d0 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -20,7 +20,11 @@ targets := $(obj-vdso) $(obj-cvdso) vdso.so vdso.so.dbg
 obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
 obj-cvdso := $(addprefix $(obj)/, $(obj-cvdso))
 
-KBUILD_AFLAGS_VDSO := $(KBUILD_AFLAGS) -DBUILD_VDSO
+ifeq ($(CONFIG_AS_SFRAME3),y)
+	SFRAME_CFLAGS := -Wa,--gsframe-3
+endif
+
+KBUILD_AFLAGS_VDSO := $(KBUILD_AFLAGS) -DBUILD_VDSO $(SFRAME_CFLAGS)
 
 KBUILD_CFLAGS_VDSO := $(KBUILD_CFLAGS) -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
 KBUILD_CFLAGS_VDSO := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS_VDSO))
@@ -29,6 +33,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO))
 KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO))
 KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
 KBUILD_CFLAGS_VDSO += -fno-stack-protector
+KBUILD_CFLAGS_VDSO += $(SFRAME_CFLAGS)
 ldflags-y := -shared -soname=linux-vdso.so.1 \
 	     --hash-style=both --build-id=sha1 -T
 
diff --git a/arch/s390/kernel/vdso/vdso.lds.S b/arch/s390/kernel/vdso/vdso.lds.S
index 7bec4de0e8e0..13d20fcede2f 100644
--- a/arch/s390/kernel/vdso/vdso.lds.S
+++ b/arch/s390/kernel/vdso/vdso.lds.S
@@ -51,6 +51,11 @@ SECTIONS
 
 	.eh_frame_hdr	: { *(.eh_frame_hdr) }		:text	:eh_frame_hdr
 	.eh_frame	: { KEEP (*(.eh_frame)) }	:text
+
+#ifdef CONFIG_AS_SFRAME
+	.sframe		: { *(.sframe) }		:text	:sframe
+#endif
+
 	.gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
 
 	.rela.dyn ALIGN(8) : { *(.rela.dyn) }
@@ -77,6 +82,7 @@ SECTIONS
  * Very old versions of ld do not recognize this name token; use the constant.
  */
 #define PT_GNU_EH_FRAME	0x6474e550
+#define PT_GNU_SFRAME	0x6474e554
 
 /*
  * We must supply the ELF program headers explicitly to get just one
@@ -88,6 +94,9 @@ PHDRS
 	dynamic		PT_DYNAMIC FLAGS(4);		/* PF_R */
 	note		PT_NOTE FLAGS(4);		/* PF_R */
 	eh_frame_hdr	PT_GNU_EH_FRAME;
+#ifdef CONFIG_AS_SFRAME
+	sframe		PT_GNU_SFRAME;
+#endif
 }
 
 /*
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 05/12] unwind_user: Enable archs that define CFA = SP_callsite + offset
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (3 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 04/12] s390/vdso: Enable SFrame V3 generation " Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 06/12] unwind_user/sframe: Enable archs with encoded SFrame CFA offsets Jens Remus
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Most architectures define their CFA as the value of the stack pointer
(SP) at the call site in the previous frame, as suggested by the DWARF
standard.  Therefore the SP at call site can be unwound using an
implicitly assumed value offset from CFA rule with an offset of zero:

  .cfi_val_offset <SP>, 0

As a result the SP at call site computes as follows:

  SP = CFA

Enable unwinding of user space for architectures, such as s390, which
define their CFA as the value of the SP at the call site in the previous
frame with an offset.  Do so by enabling architectures to override the
default SP value offset from CFA of zero with an architecture-specific
one:

  .cfi_val_offset <SP>, offset

So that the SP at call site computes as follows:

  SP = CFA + offset

Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
 arch/x86/include/asm/unwind_user.h        |  2 ++
 arch/x86/include/asm/unwind_user_sframe.h |  2 ++
 include/asm-generic/Kbuild                |  1 +
 include/asm-generic/unwind_user_sframe.h  | 12 ++++++++++++
 include/linux/unwind_user_types.h         |  1 +
 kernel/unwind/sframe.c                    |  1 +
 kernel/unwind/user.c                      | 11 ++++++-----
 7 files changed, 25 insertions(+), 5 deletions(-)
 create mode 100644 include/asm-generic/unwind_user_sframe.h

diff --git a/arch/x86/include/asm/unwind_user.h b/arch/x86/include/asm/unwind_user.h
index ae46906c3b39..0650bcfae461 100644
--- a/arch/x86/include/asm/unwind_user.h
+++ b/arch/x86/include/asm/unwind_user.h
@@ -66,6 +66,7 @@ static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
 		.rule		= UNWIND_USER_RULE_CFA_OFFSET_DEREF,\
 		.offset		= -2*(ws),		\
 			},				\
+	.sp_off		= 0,				\
 	.outermost	= false,
 
 #define ARCH_INIT_USER_FP_ENTRY_FRAME(ws)		\
@@ -80,6 +81,7 @@ static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
 	.fp		= {				\
 		.rule		= UNWIND_USER_RULE_RETAIN,\
 			},				\
+	.sp_off		= 0,				\
 	.outermost	= false,
 
 static inline bool unwind_user_at_function_start(struct pt_regs *regs)
diff --git a/arch/x86/include/asm/unwind_user_sframe.h b/arch/x86/include/asm/unwind_user_sframe.h
index d828ae1a4aac..40b03b482d1a 100644
--- a/arch/x86/include/asm/unwind_user_sframe.h
+++ b/arch/x86/include/asm/unwind_user_sframe.h
@@ -9,4 +9,6 @@
 
 #endif
 
+#include <asm-generic/unwind_user_sframe.h>
+
 #endif /* _ASM_X86_UNWIND_USER_SFRAME_H */
diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild
index 9aff61e7b8f2..91f86dd84677 100644
--- a/include/asm-generic/Kbuild
+++ b/include/asm-generic/Kbuild
@@ -61,6 +61,7 @@ mandatory-y += topology.h
 mandatory-y += trace_clock.h
 mandatory-y += uaccess.h
 mandatory-y += unwind_user.h
+mandatory-y += unwind_user_sframe.h
 mandatory-y += vermagic.h
 mandatory-y += vga.h
 mandatory-y += video.h
diff --git a/include/asm-generic/unwind_user_sframe.h b/include/asm-generic/unwind_user_sframe.h
new file mode 100644
index 000000000000..8c9ac47bc8bd
--- /dev/null
+++ b/include/asm-generic/unwind_user_sframe.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_UNWIND_USER_SFRAME_H
+#define _ASM_GENERIC_UNWIND_USER_SFRAME_H
+
+#include <linux/types.h>
+
+#ifndef SFRAME_SP_OFFSET
+/* Most archs/ABIs define CFA as SP at call site, so that SP = CFA + 0. */
+#define SFRAME_SP_OFFSET 0
+#endif
+
+#endif /* _ASM_GENERIC_UNWIND_USER_SFRAME_H */
diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h
index 059e5c76f2f3..89c71a4553b2 100644
--- a/include/linux/unwind_user_types.h
+++ b/include/linux/unwind_user_types.h
@@ -65,6 +65,7 @@ struct unwind_user_frame {
 	struct unwind_user_cfa_rule_data cfa;
 	struct unwind_user_rule_data ra;
 	struct unwind_user_rule_data fp;
+	s32 sp_off;
 	bool outermost;
 };
 
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index f24997e84e05..6a6221ce6d12 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -496,6 +496,7 @@ static __always_inline int __find_fre(struct sframe_section *sec,
 		return -EINVAL;
 	sframe_init_rule_data(&frame->ra, fre->ra_ctl, fre->ra_off);
 	sframe_init_rule_data(&frame->fp, fre->fp_ctl, fre->fp_off);
+	frame->sp_off  = SFRAME_SP_OFFSET;
 	frame->outermost = SFRAME_V3_FRE_RA_UNDEFINED_P(fre->info);
 
 	return 0;
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index eb7d9489f671..e21d088f7543 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -30,7 +30,7 @@ get_user_word(unsigned long *word, unsigned long base, int off, unsigned int ws)
 static int unwind_user_next_common(struct unwind_user_state *state,
 				   const struct unwind_user_frame *frame)
 {
-	unsigned long cfa, fp, ra;
+	unsigned long cfa, sp, fp, ra;
 
 	/* Stop unwinding when reaching an outermost frame. */
 	if (frame->outermost) {
@@ -62,16 +62,17 @@ static int unwind_user_next_common(struct unwind_user_state *state,
 	    get_user_word(&cfa, cfa, 0, state->ws))
 		return -EINVAL;
 
+	/* Get the Stack Pointer (SP) */
+	sp = cfa + frame->sp_off;
 	/*
 	 * Make sure that stack is not going in wrong direction.  Allow SP
 	 * to be unchanged for the topmost frame, by subtracting topmost,
 	 * which is either 0 or 1.
 	 */
-	if (cfa <= state->sp - state->topmost)
+	if (sp <= state->sp - state->topmost)
 		return -EINVAL;
-
 	/* Make sure that the address is word aligned */
-	if (cfa & (state->ws - 1))
+	if (sp & (state->ws - 1))
 		return -EINVAL;
 
 	/* Get the Return Address (RA) */
@@ -122,7 +123,7 @@ static int unwind_user_next_common(struct unwind_user_state *state,
 		return -EINVAL;
 
 	state->ip = ra;
-	state->sp = cfa;
+	state->sp = sp;
 	state->fp = fp;
 	state->topmost = false;
 	return 0;
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 06/12] unwind_user/sframe: Enable archs with encoded SFrame CFA offsets
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (4 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 05/12] unwind_user: Enable archs that define CFA = SP_callsite + offset Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 07/12] s390/ptrace: Convert function macros to inline functions Jens Remus
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Enable architectures, such as s390, which store SFrame CFA offset values
encoded, to e.g. make (better) use of unsigned 8-bit SFrame offsets.

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

Notes (jremus):
    Changes in RFC v2:
    - Rename generic_sframe_cfa_offset_decode() to
      sframe_cfa_offset_decode(). (Josh)

 include/asm-generic/unwind_user_sframe.h | 8 ++++++++
 kernel/unwind/sframe.c                   | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/include/asm-generic/unwind_user_sframe.h b/include/asm-generic/unwind_user_sframe.h
index 8c9ac47bc8bd..117b05e1dcab 100644
--- a/include/asm-generic/unwind_user_sframe.h
+++ b/include/asm-generic/unwind_user_sframe.h
@@ -9,4 +9,12 @@
 #define SFRAME_SP_OFFSET 0
 #endif
 
+#ifndef sframe_cfa_offset_decode
+static inline s32 sframe_cfa_offset_decode(s32 offset)
+{
+	return offset;
+}
+#define sframe_cfa_offset_decode sframe_cfa_offset_decode
+#endif
+
 #endif /* _ASM_GENERIC_UNWIND_USER_SFRAME_H */
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index 6a6221ce6d12..5ac502f16bad 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -209,6 +209,7 @@ __read_regular_fre_datawords(struct sframe_section *sec,
 
 	UNSAFE_GET_USER_INC(cfa_off, cur, dataword_size, Efault);
 	dataword_count--;
+	cfa_off = sframe_cfa_offset_decode(cfa_off);
 
 	ra_off = sec->ra_off;
 	if (!ra_off && dataword_count) {
@@ -258,6 +259,7 @@ __read_flex_fde_fre_datawords(struct sframe_section *sec,
 	UNSAFE_GET_USER_INC(cfa_ctl, cur, dataword_size, Efault);
 	UNSAFE_GET_USER_INC(cfa_off, cur, dataword_size, Efault);
 	dataword_count -= 2;
+	cfa_off = sframe_cfa_offset_decode(cfa_off);
 
 	ra_off = sec->ra_off;
 	ra_ctl = ra_off ? 2 : 0; /* regnum=0, deref_p=(ra_off != 0), reg_p=0 */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 07/12] s390/ptrace: Convert function macros to inline functions
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (5 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 06/12] unwind_user/sframe: Enable archs with encoded SFrame CFA offsets Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 08/12] s390/ptrace: Provide frame_pointer() Jens Remus
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Convert the function macros user_mode(), instruction_pointer(), and
user_stack_pointer() to inline functions, to align their definition
with x86 and arm64.

Use const qualifier on struct pt_regs pointer parameters to prevent
compiler warnings:

arch/s390/kernel/stacktrace.c: In function ‘arch_stack_walk_user_common’:
arch/s390/kernel/stacktrace.c:114:34: warning: passing argument 1 of
‘instruction_pointer’ discards ‘const’ qualifier from pointer target
type [-Wdiscarded-qualifiers]
...
arch/s390/kernel/stacktrace.c:117:48: warning: passing argument 1 of
‘user_stack_pointer’ discards ‘const’ qualifier from pointer target
type [-Wdiscarded-qualifiers]
...

While at it add const qualifier to all struct pt_regs pointer parameters
that are accessed read-only and use __always_inline instead of inline to
harmonize the helper functions.

[hca@linux.ibm.com: Use psw_bits() helper]

Reviewed-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
---

Notes (jremus):
    Changes in v4:
    - New patch.  Split out "while at it" changes from patch "s390/ptrace:
      Provide frame_pointer()" to go upstream separately. (Heiko)

 arch/s390/include/asm/ptrace.h | 37 ++++++++++++++++++++++++----------
 1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index 962cf042c66d..e6ec0ccf3d73 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -214,16 +214,23 @@ void update_cr_regs(struct task_struct *task);
 #define arch_has_single_step()	(1)
 #define arch_has_block_step()	(1)
 
-#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
-#define instruction_pointer(regs) ((regs)->psw.addr)
-#define user_stack_pointer(regs)((regs)->gprs[15])
 #define profile_pc(regs) instruction_pointer(regs)
 
-static inline long regs_return_value(struct pt_regs *regs)
+static __always_inline bool user_mode(const struct pt_regs *regs)
+{
+	return psw_bits(regs->psw).pstate;
+}
+
+static inline long regs_return_value(const struct pt_regs *regs)
 {
 	return regs->gprs[2];
 }
 
+static __always_inline unsigned long instruction_pointer(const struct pt_regs *regs)
+{
+	return regs->psw.addr;
+}
+
 static inline void instruction_pointer_set(struct pt_regs *regs,
 					   unsigned long val)
 {
@@ -233,19 +240,26 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
 int regs_query_register_offset(const char *name);
 const char *regs_query_register_name(unsigned int offset);
 
-static __always_inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+static __always_inline unsigned long kernel_stack_pointer(const struct pt_regs *regs)
+{
+	return regs->gprs[15];
+}
+
+static __always_inline unsigned long user_stack_pointer(const struct pt_regs *regs)
 {
 	return regs->gprs[15];
 }
 
-static __always_inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset)
+static __always_inline unsigned long regs_get_register(const struct pt_regs *regs,
+						       unsigned int offset)
 {
 	if (offset >= NUM_GPRS)
 		return 0;
 	return regs->gprs[offset];
 }
 
-static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+static __always_inline int regs_within_kernel_stack(const struct pt_regs *regs,
+						    unsigned long addr)
 {
 	unsigned long ksp = kernel_stack_pointer(regs);
 
@@ -261,7 +275,8 @@ static __always_inline int regs_within_kernel_stack(struct pt_regs *regs, unsign
  * is specifined by @regs. If the @n th entry is NOT in the kernel stack,
  * this returns 0.
  */
-static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+static __always_inline unsigned long regs_get_kernel_stack_nth(const struct pt_regs *regs,
+							       unsigned int n)
 {
 	unsigned long addr;
 
@@ -278,8 +293,8 @@ static __always_inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *r
  *
  * regs_get_kernel_argument() returns @n th argument of the function call.
  */
-static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
-						     unsigned int n)
+static __always_inline unsigned long regs_get_kernel_argument(const struct pt_regs *regs,
+							      unsigned int n)
 {
 	unsigned int argoffset = STACK_FRAME_OVERHEAD / sizeof(long);
 
@@ -290,7 +305,7 @@ static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs,
 	return regs_get_kernel_stack_nth(regs, argoffset + n);
 }
 
-static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
+static __always_inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
 {
 	regs->gprs[2] = rc;
 }
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 08/12] s390/ptrace: Provide frame_pointer()
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (6 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 07/12] s390/ptrace: Convert function macros to inline functions Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 09/12] s390/unwind_user/sframe: Enable sframe unwinding on s390 Jens Remus
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

On s390 64-bit the s390x ELF ABI [1] designates register 11 as the
"preferred" frame pointer (FP) register in user space.

[1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases

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

Notes (jremus):
    Changes in v4:
    - Split out "while at it" changes into separate commit that can go
      upstream independently. (Heiko)
    
    Changes in RFC v2:
    - Separate provide frame_pointer() into this new commit.

 arch/s390/include/asm/ptrace.h | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index e6ec0ccf3d73..8f18d00b5fdb 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -250,6 +250,12 @@ static __always_inline unsigned long user_stack_pointer(const struct pt_regs *re
 	return regs->gprs[15];
 }
 
+static __always_inline unsigned long frame_pointer(const struct pt_regs *regs)
+{
+	/* Return ABI-designated "preferred" frame-pointer register value. */
+	return regs->gprs[11];
+}
+
 static __always_inline unsigned long regs_get_register(const struct pt_regs *regs,
 						       unsigned int offset)
 {
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 09/12] s390/unwind_user/sframe: Enable sframe unwinding on s390
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (7 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 08/12] s390/ptrace: Provide frame_pointer() Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 10/12] unwind_user: Introduce FP/RA recovery rule unknown Jens Remus
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Add s390 support for unwinding of user space using SFrame.  This
leverages the previous commits to address the following s390
particularities:

- The CFA is defined as the value of the stack pointer (SP) at call
  site in the previous frame plus 160.  Therefore the SP unwinds as
  SP = CFA - 160.  Therefore use a SP value offset from CFA of -160.

- The return address (RA) is not saved on the stack at function entry.
  It is also not saved in the function prologue, when in leaf functions.
  Therefore the RA does not necessarily need to be unwound in the
  topmost frame.

- The frame pointer (FP) and/or return address (RA) may be saved in
  other registers when in leaf functions.  GCC effectively uses
  floating-point registers (FPR) for this purpose.  In SFrame V3 this
  is represented using flexible FDEs, where the CFA, FP, and RA recovery
  rules may specify a DWARF register number.

- To make use of the signed 8-bit SFrame data word size and effectively
  reduce the .sframe section size the SFrame CFA offset values are
  encoded as (CFA - 160) / 8.  This is because the lowest CFA offset
  value on s390 is by definition +160 (= value at function entry),
  which does not fit into a signed 8-bit SFrame data word / offset.
  Therefore the CFA offset values are stored adjusted by -160.
  Additionally they are scaled by the s390-specific DWARF data scaling
  factor of 8.  The s390x ELF ABI [1] guarantees that the CFA offset
  values are always aligned on an 8-byte boundary.

Add s390-specific SFrame format definitions.
Include <asm/unwind_user_sframe.h> after "sframe.h" to make those
s390-specific definitions available to architecture-specific unwind
user sframe code, particularly the s390-specific one.

[1]: s390x ELF ABI, https://github.com/IBM/s390x-abi/releases

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

Notes (jremus):
    Changes in v4:
    - Fix __s390_get_dwarf_fpr() access of user FPR. (Heiko)
    - Use __s390_dwarf_fpr_to_fpr() helper instead of large switch/case
      block. (Heiko)
    - Use unsigned int for DWARF register numbers, as DWARF defines them
      as unsigned LEB128.
    - SFrame V3 support:
      - Remove now unused arch_sframe_init_reginfo().
      - Remove now unused SFRAME_V2_S390X_OFFSET_IS_REGNUM() and
        SFRAME_V2_S390X_OFFSET_DECODE_REGNUM() macros.
      - Rename SFRAME_V2_*() macros to SFRAME_V3_*().
      - Add architecture-specific defines SFRAME_REG_SP and SFRAME_REG_FP.
    
    Changes in RFC v3:
    - Adjust to rename of UNWIND_USER_LOC_NONE to UNWIND_USER_LOC_RETAIN.
    - Adjust s390-specific unwind_user_word_size() to changes in the
      x86-specific (see patch 4).
    
    Changes in RFC v2:
    - Provide unwind_user_word_size() to satisfy new unwind user need.  Note
      that support for COMPAT has not been implemented as s390 support for
      COMPAT is expected to be removed with v6.19:
      https://lore.kernel.org/all/20251201102713.22472A5b-hca@linux.ibm.com/
    - Adjust to changes in preceding patches in this series that enable
      support in unwind user (sframe) for s390 particularities.
    
    Alternatively the s390-specific definitions could also be added to the
    s390-specific unwind user sframe header.  The current implementation
    follows Binutils approach to have all SFrame format definitions in one
    central header file.

 arch/s390/Kconfig                          |  1 +
 arch/s390/include/asm/unwind_user.h        | 70 ++++++++++++++++++++++
 arch/s390/include/asm/unwind_user_sframe.h | 21 +++++++
 kernel/unwind/sframe.c                     |  2 +-
 kernel/unwind/sframe.h                     | 11 ++++
 5 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 arch/s390/include/asm/unwind_user.h
 create mode 100644 arch/s390/include/asm/unwind_user_sframe.h

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0e5fad5f06ca..063f0c857600 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -250,6 +250,7 @@ config S390
 	select HAVE_SOFTIRQ_ON_OWN_STACK
 	select HAVE_STACKPROTECTOR if CC_HAS_STACKPROTECTOR_GLOBAL
 	select HAVE_SYSCALL_TRACEPOINTS
+	select HAVE_UNWIND_USER_SFRAME
 	select HAVE_VIRT_CPU_ACCOUNTING
 	select HAVE_VIRT_CPU_ACCOUNTING_IDLE
 	select HOTPLUG_SMT
diff --git a/arch/s390/include/asm/unwind_user.h b/arch/s390/include/asm/unwind_user.h
new file mode 100644
index 000000000000..941aa3f0f70f
--- /dev/null
+++ b/arch/s390/include/asm/unwind_user.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_UNWIND_USER_H
+#define _ASM_S390_UNWIND_USER_H
+
+#include <linux/sched/task_stack.h>
+#include <linux/types.h>
+#include <asm/fpu.h>
+
+#ifdef CONFIG_UNWIND_USER
+
+static inline int unwind_user_word_size(struct pt_regs *regs)
+{
+	return 8;
+}
+
+static inline int arch_unwind_user_get_ra_reg(unsigned long *val)
+{
+	struct pt_regs *regs = task_pt_regs(current);
+	*val = regs->gprs[14];
+	return 0;
+}
+#define unwind_user_get_ra_reg arch_unwind_user_get_ra_reg
+
+static inline unsigned long __s390_dwarf_fpr_to_fpr(unsigned int regnum)
+{
+	unsigned int fpr;
+
+	/*
+	 * Convert from s390 DWARF floating-point register number (16..31)
+	 * to floating-point register number (0..15): left rotate the least
+	 * significant three bits and then return the least significant four
+	 * bits.
+	 */
+	fpr  = (regnum & 3) << 1;
+	fpr |= (regnum & 4) >> 2;
+	fpr |= (regnum & 8);
+	return fpr;
+}
+
+static inline unsigned long __s390_get_dwarf_fpr(unsigned int regnum)
+{
+	struct fpu *fpu = &current->thread.ufpu;
+
+	save_user_fpu_regs();
+	return fpu->vxrs[__s390_dwarf_fpr_to_fpr(regnum)].high;
+}
+
+static inline int arch_unwind_user_get_reg(unsigned long *val,
+					   unsigned int regnum)
+{
+	if (regnum <= 15) {
+		/* DWARF register numbers 0..15 */
+		struct pt_regs *regs = task_pt_regs(current);
+		*val = regs->gprs[regnum];
+		return 0;
+	} else if (regnum <= 31) {
+		/* DWARF register numbers 16..31 */
+		*val = __s390_get_dwarf_fpr(regnum);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+#define unwind_user_get_reg arch_unwind_user_get_reg
+
+#endif /* CONFIG_UNWIND_USER */
+
+#include <asm-generic/unwind_user.h>
+
+#endif /* _ASM_S390_UNWIND_USER_H */
diff --git a/arch/s390/include/asm/unwind_user_sframe.h b/arch/s390/include/asm/unwind_user_sframe.h
new file mode 100644
index 000000000000..91eea28c8a48
--- /dev/null
+++ b/arch/s390/include/asm/unwind_user_sframe.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_UNWIND_USER_SFRAME_H
+#define _ASM_S390_UNWIND_USER_SFRAME_H
+
+#include <linux/unwind_user.h>
+#include <linux/types.h>
+
+#define SFRAME_SP_OFFSET SFRAME_S390X_SP_VAL_OFFSET
+
+#define SFRAME_REG_SP	15
+#define SFRAME_REG_FP	11	/* "preferred" FP register */
+
+static inline s32 arch_sframe_cfa_offset_decode(s32 offset)
+{
+	return SFRAME_V3_S390X_CFA_OFFSET_DECODE(offset);
+}
+#define sframe_cfa_offset_decode arch_sframe_cfa_offset_decode
+
+#include <asm-generic/unwind_user_sframe.h>
+
+#endif /* _ASM_S390_UNWIND_USER_SFRAME_H */
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index 5ac502f16bad..21283e3bda42 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -12,11 +12,11 @@
 #include <linux/mm.h>
 #include <linux/string_helpers.h>
 #include <linux/sframe.h>
-#include <asm/unwind_user_sframe.h>
 #include <linux/unwind_user_types.h>
 
 #include "sframe.h"
 #include "sframe_debug.h"
+#include <asm/unwind_user_sframe.h>
 
 struct sframe_fde_internal {
 	unsigned long	func_addr;
diff --git a/kernel/unwind/sframe.h b/kernel/unwind/sframe.h
index 5b6112945b6c..8a5322e95403 100644
--- a/kernel/unwind/sframe.h
+++ b/kernel/unwind/sframe.h
@@ -19,6 +19,7 @@
 #define SFRAME_ABI_AARCH64_ENDIAN_BIG		1
 #define SFRAME_ABI_AARCH64_ENDIAN_LITTLE	2
 #define SFRAME_ABI_AMD64_ENDIAN_LITTLE		3
+#define SFRAME_ABI_S390X_ENDIAN_BIG		4	/* s390 64-bit (s390x) */
 
 struct sframe_preamble {
 	u16	magic;
@@ -84,4 +85,14 @@ struct sframe_fda_v3 {
 #define SFRAME_V3_FLEX_FDE_CTLWORD_DEREF_P(data)	(((data) >> 1) & 0x1)
 #define SFRAME_V3_FLEX_FDE_CTLWORD_REG_P(data)		((data) & 0x1)
 
+/* s390 64-bit (s390x) */
+
+#define SFRAME_S390X_SP_VAL_OFFSET			(-160)
+
+#define SFRAME_S390X_CFA_OFFSET_ADJUSTMENT		SFRAME_S390X_SP_VAL_OFFSET
+#define SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR	8
+#define SFRAME_V3_S390X_CFA_OFFSET_DECODE(offset) \
+	(((offset) * SFRAME_S390X_CFA_OFFSET_ALIGNMENT_FACTOR) \
+	- SFRAME_S390X_CFA_OFFSET_ADJUSTMENT)
+
 #endif /* _SFRAME_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 10/12] unwind_user: Introduce FP/RA recovery rule unknown
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (8 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 09/12] s390/unwind_user/sframe: Enable sframe unwinding on s390 Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 11/12] unwind_user/fp: Use arch-specific helper to initialize FP frame Jens Remus
  2026-01-27 15:19 ` [PATCH v4 12/12] s390/unwind_user/fp: Enable back chain unwinding of user space Jens Remus
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Add support for the unwind user FP/RA recovery rule "unknown".  For the
frame pointer (FP) set the FP value to zero, so that subsequent unwind
next frame that rely on FP fail.  For the return address (RA) treat as
error.

This enables to implement support for unwinding of user space using back
chain on s390 with a subsequent commit, which can only unwind SP and RA,
but not FP.

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.

 include/linux/unwind_user_types.h | 1 +
 kernel/unwind/user.c              | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h
index 89c71a4553b2..fac8f470b597 100644
--- a/include/linux/unwind_user_types.h
+++ b/include/linux/unwind_user_types.h
@@ -45,6 +45,7 @@ struct unwind_user_cfa_rule_data {
 };
 
 enum unwind_user_rule {
+	UNWIND_USER_RULE_ZERO,			/* entity = 0 */
 	UNWIND_USER_RULE_RETAIN,		/* entity = entity */
 	UNWIND_USER_RULE_CFA_OFFSET,		/* entity = CFA + offset */
 	UNWIND_USER_RULE_REG_OFFSET,		/* entity = register + offset */
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index e21d088f7543..036328cb9ee8 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -114,6 +114,10 @@ static int unwind_user_next_common(struct unwind_user_state *state,
 			return -EINVAL;
 		fp += frame->fp.offset;
 		break;
+	case UNWIND_USER_RULE_ZERO:
+		/* FP cannot be unwound. Not an error. Set to zero. */
+		fp = 0;
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		return -EINVAL;
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 11/12] unwind_user/fp: Use arch-specific helper to initialize FP frame
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (9 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 10/12] unwind_user: Introduce FP/RA recovery rule unknown Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  2026-01-27 15:19 ` [PATCH v4 12/12] s390/unwind_user/fp: Enable back chain unwinding of user space Jens Remus
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

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 v4:
    - Remove superfluous unwind_user_fp_get_frame defines.
    
    Changes in RFC v3:
    - New patch.  Prerequirement to implement unwind user fp using back
      chain on s390.

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

diff --git a/arch/x86/include/asm/unwind_user.h b/arch/x86/include/asm/unwind_user.h
index 0650bcfae461..f5e9fbcdae28 100644
--- a/arch/x86/include/asm/unwind_user.h
+++ b/arch/x86/include/asm/unwind_user.h
@@ -84,11 +84,24 @@ static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
 	.sp_off		= 0,				\
 	.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
 
 #endif /* CONFIG_HAVE_UNWIND_USER_FP */
 
diff --git a/include/linux/unwind_user.h b/include/linux/unwind_user.h
index 92cdf38c8ade..f65b0573b3a5 100644
--- a/include/linux/unwind_user.h
+++ b/include/linux/unwind_user.h
@@ -7,21 +7,14 @@
 
 #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
+
+#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 036328cb9ee8..a64ceb4a2bf6 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -135,19 +135,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


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v4 12/12] s390/unwind_user/fp: Enable back chain unwinding of user space
  2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
                   ` (10 preceding siblings ...)
  2026-01-27 15:19 ` [PATCH v4 11/12] unwind_user/fp: Use arch-specific helper to initialize FP frame Jens Remus
@ 2026-01-27 15:19 ` Jens Remus
  11 siblings, 0 replies; 13+ messages in thread
From: Jens Remus @ 2026-01-27 15:19 UTC (permalink / raw)
  To: linux-kernel, linux-trace-kernel, linux-s390, bpf, x86,
	Steven Rostedt
  Cc: Jens Remus, Heiko Carstens, Vasily Gorbik, Ilya Leoshkevich,
	Josh Poimboeuf, Masami Hiramatsu, Mathieu Desnoyers,
	Peter Zijlstra, Ingo Molnar, Jiri Olsa, Arnaldo Carvalho de Melo,
	Namhyung Kim, Thomas Gleixner, Andrii Nakryiko, Indu Bhagat,
	Jose E. Marchesi, Beau Belgrave, Linus Torvalds, Andrew Morton,
	Florian Weimer, Kees Cook, Carlos O'Donell, Sam James,
	Dylan Hatch

Unwinding of user space using frame pointer (FP) is virtually impossible
on s390 for the following reasons:  The s390 64-bit (s390x) ELF ABI [1]
does only designate a "preferred" FP register and does not mandate fixed
FP and return address (RA) stack save slots.  Therefore neither the FP
register nor the FP/RA stack save slot offsets from CFA are known.
Compilers, such as GCC and Clang, do not necessarily setup a FP register
early in the function prologue, even not with compiler option
-fno-omit-frame-pointer.  Therefore the CFA offset from FP register is
not known.

This could be resolved by having compiler option -no-omit-frame-pointer
enforce all of the following:  Use the preferred FP register 11 as frame
pointer, use fixed FP/RA stack slot offsets from CFA (e.g. -72 for FP
and -48 for RA), and setup the FP register immediately after saving the
call saved registers.

Fortunately s390 provides an alternative to frame pointer:  back chain,
which can be enabled using s390-specific compiler option -mbackchain.
The back chain is very similar to a frame pointer on the stack.

Leverage the unwind user fp infrastructure to enable unwinding of user
space using back chain.  Enable HAVE_UNWIND_USER_FP and provide a s390-
specific implementation of unwind_user_fp_get_frame(), which uses the
back chain.

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

Notes (jremus):
    Changes in v4:
    - Remove flawed heuristic to detect if topmost IP in early prologue.
      While it may resolve the caller getting skipped it may erroneously
      inject a callee as caller.
    - Fix outermost frame indication.
    - Adjust to flexible CFA and FP/RA rules.
    - Remove superfluous unwind_user_fp_get_frame define.
    
    Changes in RFC v3:
    - New patch.  Implement unwind user fp using back chain on s390. Reuses
      logic from RFC v2 patch "unwind_user/backchain: Introduce back chain
      user space unwinding". (Josh)

 arch/s390/Kconfig                   |  1 +
 arch/s390/include/asm/unwind_user.h | 62 +++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 063f0c857600..5f7e83ba54b2 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -250,6 +250,7 @@ config S390
 	select HAVE_SOFTIRQ_ON_OWN_STACK
 	select HAVE_STACKPROTECTOR if CC_HAS_STACKPROTECTOR_GLOBAL
 	select HAVE_SYSCALL_TRACEPOINTS
+	select HAVE_UNWIND_USER_FP
 	select HAVE_UNWIND_USER_SFRAME
 	select HAVE_VIRT_CPU_ACCOUNTING
 	select HAVE_VIRT_CPU_ACCOUNTING_IDLE
diff --git a/arch/s390/include/asm/unwind_user.h b/arch/s390/include/asm/unwind_user.h
index 941aa3f0f70f..a7b97ea01c26 100644
--- a/arch/s390/include/asm/unwind_user.h
+++ b/arch/s390/include/asm/unwind_user.h
@@ -3,8 +3,12 @@
 #define _ASM_S390_UNWIND_USER_H
 
 #include <linux/sched/task_stack.h>
+#include <linux/security.h>
 #include <linux/types.h>
+#include <asm/asm-offsets.h>
 #include <asm/fpu.h>
+#include <asm/stacktrace.h>
+#include <linux/unwind_user_types.h>
 
 #ifdef CONFIG_UNWIND_USER
 
@@ -65,6 +69,64 @@ static inline int arch_unwind_user_get_reg(unsigned long *val,
 
 #endif /* CONFIG_UNWIND_USER */
 
+#ifdef CONFIG_HAVE_UNWIND_USER_FP
+
+static inline bool ip_within_vdso(unsigned long ip)
+{
+	return in_range(ip, current->mm->context.vdso_base, vdso_text_size());
+}
+
+static inline int unwind_user_fp_get_frame(struct unwind_user_state *state,
+					   struct unwind_user_frame *frame)
+{
+	struct stack_frame_user __user *sf;
+	unsigned long __user *ra_addr;
+	unsigned long sp;
+
+	sf = (void __user *)state->sp;
+	if (__get_user(sp, (unsigned long __user *)&sf->back_chain))
+		return -EINVAL;
+	if (!sp && ip_within_vdso(state->ip)) {
+		/*
+		 * Assume non-standard vDSO user wrapper stack frame.
+		 * See vDSO user wrapper code for details.
+		 */
+		struct stack_frame_vdso_wrapper *sf_vdso = (void __user *)sf;
+
+		ra_addr = (unsigned long __user *)&sf_vdso->return_address;
+		sf = (void __user *)((unsigned long)sf + STACK_FRAME_VDSO_OVERHEAD);
+		if (__get_user(sp, (unsigned long __user *)&sf->back_chain))
+			return -EINVAL;
+	} else if (!sp) {
+		/*
+		 * Assume outermost frame reached. unwind_user_next_common()
+		 * disregards all other fields in outermost frame.
+		 */
+		frame->outermost = true;
+		return 0;
+	} else {
+		/*
+		 * Assume IP past prologue and new stack frame allocated.
+		 * Follow back chain, which then equals the SP at entry.
+		 * Skips caller if wrong in topmost frame.
+		 */
+		sf = (void __user *)sp;
+		ra_addr = (unsigned long __user *)&sf->gprs[8];
+	}
+
+	frame->cfa.rule = UNWIND_USER_CFA_RULE_SP_OFFSET;
+	frame->cfa.offset = sp - state->sp + 160;
+	frame->sp_off = -160;
+	frame->fp.rule = UNWIND_USER_RULE_ZERO;	/* Cannot unwind FP. */
+	frame->ra.rule = UNWIND_USER_RULE_CFA_OFFSET_DEREF;
+	frame->ra.offset = (unsigned long)ra_addr - (state->sp + frame->cfa.offset);
+	frame->outermost = false;
+
+	return 0;
+}
+
+#endif /* CONFIG_HAVE_UNWIND_USER_FP */
+
 #include <asm-generic/unwind_user.h>
 
 #endif /* _ASM_S390_UNWIND_USER_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-01-27 15:20 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-27 15:19 [PATCH v4 00/12] s390: SFrame user space unwinding Jens Remus
2026-01-27 15:19 ` [PATCH v4 01/12] s390: asm/dwarf.h should only be included in assembly files Jens Remus
2026-01-27 15:19 ` [PATCH v4 02/12] s390/vdso: Avoid emitting DWARF CFI for non-vDSO Jens Remus
2026-01-27 15:19 ` [PATCH v4 03/12] s390/vdso: Keep function symbols in vDSO Jens Remus
2026-01-27 15:19 ` [PATCH v4 04/12] s390/vdso: Enable SFrame V3 generation " Jens Remus
2026-01-27 15:19 ` [PATCH v4 05/12] unwind_user: Enable archs that define CFA = SP_callsite + offset Jens Remus
2026-01-27 15:19 ` [PATCH v4 06/12] unwind_user/sframe: Enable archs with encoded SFrame CFA offsets Jens Remus
2026-01-27 15:19 ` [PATCH v4 07/12] s390/ptrace: Convert function macros to inline functions Jens Remus
2026-01-27 15:19 ` [PATCH v4 08/12] s390/ptrace: Provide frame_pointer() Jens Remus
2026-01-27 15:19 ` [PATCH v4 09/12] s390/unwind_user/sframe: Enable sframe unwinding on s390 Jens Remus
2026-01-27 15:19 ` [PATCH v4 10/12] unwind_user: Introduce FP/RA recovery rule unknown Jens Remus
2026-01-27 15:19 ` [PATCH v4 11/12] unwind_user/fp: Use arch-specific helper to initialize FP frame Jens Remus
2026-01-27 15:19 ` [PATCH v4 12/12] s390/unwind_user/fp: Enable back chain unwinding of user space Jens Remus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox