* [PATCH v1 1/4] arm64/unwind_user/fp: Enable HAVE_UNWIND_USER_FP
2026-04-17 15:08 [PATCH v1 0/4] arm64: SFrame user space unwinding Jens Remus
@ 2026-04-17 15:08 ` Jens Remus
2026-04-17 15:08 ` [PATCH v1 2/4] arm64/uaccess: Add unsafe_copy_from_user() implementation Jens Remus
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Jens Remus @ 2026-04-17 15:08 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Steven Rostedt, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Dylan Hatch, Weinan Liu
Cc: Jens Remus, linux-arm-kernel, linux-kernel, Heiko Carstens,
Ilya Leoshkevich
Add arm64 support for unwinding of user space using frame pointer (FP).
For this purpose enable the config option HAVE_UNWIND_USER_FP and
provide an arm64-specific ARCH_INIT_USER_FP_FRAME definition (specifying
the CFA offset from FP and the FP and RA offsets from CFA). Unlike x86,
as there is no mean to determine whether the user space IP in the
topmost frame is at function entry, rely on the common definition of
unwind_user_at_function_start(), which always returns false, and common
dummy definition of ARCH_INIT_USER_FP_ENTRY_FRAME.
For unwind user in general provide an arm64-specific implementation
of unwind_user_word_size().
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/unwind_user.h | 42 ++++++++++++++++++++++++++++
2 files changed, 43 insertions(+)
create mode 100644 arch/arm64/include/asm/unwind_user.h
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 38dba5f7e4d2..994fd5162a1d 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -253,6 +253,7 @@ config ARM64
select HAVE_RUST if RUSTC_SUPPORTS_ARM64
select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
+ select HAVE_UNWIND_USER_FP
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_GENERIC_VDSO
diff --git a/arch/arm64/include/asm/unwind_user.h b/arch/arm64/include/asm/unwind_user.h
new file mode 100644
index 000000000000..0641d4d97b0f
--- /dev/null
+++ b/arch/arm64/include/asm/unwind_user.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ARM64_UNWIND_USER_H
+#define _ASM_ARM64_UNWIND_USER_H
+
+#include <linux/sched/task_stack.h>
+#include <linux/types.h>
+
+#ifdef CONFIG_UNWIND_USER
+
+static inline int unwind_user_word_size(struct pt_regs *regs)
+{
+#ifdef COMPAT
+ if (compat_user_mode(regs))
+ return sizeof(int);
+#endif
+ return sizeof(long);
+}
+
+#endif /* CONFIG_UNWIND_USER */
+
+#ifdef CONFIG_HAVE_UNWIND_USER_FP
+
+#define ARCH_INIT_USER_FP_FRAME(ws) \
+ .cfa = { \
+ .rule = UNWIND_USER_CFA_RULE_FP_OFFSET, \
+ .offset = 2*(ws), \
+ }, \
+ .ra = { \
+ .rule = UNWIND_USER_RULE_CFA_OFFSET_DEREF, \
+ .offset = -1*(ws), \
+ }, \
+ .fp = { \
+ .rule = UNWIND_USER_RULE_CFA_OFFSET_DEREF, \
+ .offset = -2*(ws), \
+ }, \
+ .outermost = false,
+
+#endif /* CONFIG_HAVE_UNWIND_USER_FP */
+
+#include <asm-generic/unwind_user.h>
+
+#endif /* _ASM_ARM64_UNWIND_USER_H */
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v1 2/4] arm64/uaccess: Add unsafe_copy_from_user() implementation
2026-04-17 15:08 [PATCH v1 0/4] arm64: SFrame user space unwinding Jens Remus
2026-04-17 15:08 ` [PATCH v1 1/4] arm64/unwind_user/fp: Enable HAVE_UNWIND_USER_FP Jens Remus
@ 2026-04-17 15:08 ` Jens Remus
2026-04-17 15:08 ` [PATCH v1 3/4] arm64/vdso: Enable SFrame generation in vDSO Jens Remus
2026-04-17 15:08 ` [PATCH v1 4/4] arm64/unwind_user/sframe: Enable sframe unwinding on arm64 Jens Remus
3 siblings, 0 replies; 5+ messages in thread
From: Jens Remus @ 2026-04-17 15:08 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Steven Rostedt, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Dylan Hatch, Weinan Liu
Cc: Jens Remus, linux-arm-kernel, linux-kernel, Heiko Carstens,
Ilya Leoshkevich
This replicates Josh's x86 patch "x86/uaccess:
Add unsafe_copy_from_user() implementation" [1] for arm64.
Add an arm64 implementation of unsafe_copy_from_user() similar to the
existing unsafe_copy_to_user().
For this purpose rename the unsafe_copy_loop() helper to
unsafe_copy_to_user_loop() and introduce a unsafe_copy_from_user_loop()
helper.
While at it rename the unsafe_copy_to_user() local variables
__ucu_{dst|src|len} to __{dst|src|len} and change their pointer
type to void * to align to the x86 patch.
[1]: x86/uaccess: Add unsafe_copy_from_user() implementation,
https://lore.kernel.org/all/20251119132323.1281768-4-jremus@linux.ibm.com/
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
arch/arm64/include/asm/uaccess.h | 39 ++++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 9810106a3f66..37d7d16b86a9 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -437,7 +437,7 @@ static inline void user_access_restore(unsigned long enabled) { }
* We want the unsafe accessors to always be inlined and use
* the error labels - thus the macro games.
*/
-#define unsafe_copy_loop(dst, src, len, type, label) \
+#define unsafe_copy_to_user_loop(dst, src, len, type, label) \
while (len >= sizeof(type)) { \
unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \
dst += sizeof(type); \
@@ -445,15 +445,34 @@ static inline void user_access_restore(unsigned long enabled) { }
len -= sizeof(type); \
}
-#define unsafe_copy_to_user(_dst,_src,_len,label) \
-do { \
- char __user *__ucu_dst = (_dst); \
- const char *__ucu_src = (_src); \
- size_t __ucu_len = (_len); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \
- unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \
+#define unsafe_copy_to_user(_dst, _src, _len, label) \
+do { \
+ void __user *__dst = (_dst); \
+ const void *__src = (_src); \
+ size_t __len = (_len); \
+ unsafe_copy_to_user_loop(__dst, __src, __len, u64, label); \
+ unsafe_copy_to_user_loop(__dst, __src, __len, u32, label); \
+ unsafe_copy_to_user_loop(__dst, __src, __len, u16, label); \
+ unsafe_copy_to_user_loop(__dst, __src, __len, u8, label); \
+} while (0)
+
+#define unsafe_copy_from_user_loop(dst, src, len, type, label) \
+ while (len >= sizeof(type)) { \
+ unsafe_get_user(*(type *)(dst), (type __user *)(src), label); \
+ dst += sizeof(type); \
+ src += sizeof(type); \
+ len -= sizeof(type); \
+ }
+
+#define unsafe_copy_from_user(_dst, _src, _len, label) \
+do { \
+ void *__dst = (_dst); \
+ void __user *__src = (_src); \
+ size_t __len = (_len); \
+ unsafe_copy_from_user_loop(__dst, __src, __len, u64, label); \
+ unsafe_copy_from_user_loop(__dst, __src, __len, u32, label); \
+ unsafe_copy_from_user_loop(__dst, __src, __len, u16, label); \
+ unsafe_copy_from_user_loop(__dst, __src, __len, u8, label); \
} while (0)
#define INLINE_COPY_TO_USER
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v1 3/4] arm64/vdso: Enable SFrame generation in vDSO
2026-04-17 15:08 [PATCH v1 0/4] arm64: SFrame user space unwinding Jens Remus
2026-04-17 15:08 ` [PATCH v1 1/4] arm64/unwind_user/fp: Enable HAVE_UNWIND_USER_FP Jens Remus
2026-04-17 15:08 ` [PATCH v1 2/4] arm64/uaccess: Add unsafe_copy_from_user() implementation Jens Remus
@ 2026-04-17 15:08 ` Jens Remus
2026-04-17 15:08 ` [PATCH v1 4/4] arm64/unwind_user/sframe: Enable sframe unwinding on arm64 Jens Remus
3 siblings, 0 replies; 5+ messages in thread
From: Jens Remus @ 2026-04-17 15:08 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Steven Rostedt, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Dylan Hatch, Weinan Liu
Cc: Jens Remus, linux-arm-kernel, linux-kernel, Heiko Carstens,
Ilya Leoshkevich
This replicates Josh's x86 patch "x86/vdso: Enable sframe generation
in VDSO" [1] for arm64.
Enable .sframe generation in the vDSO library so kernel and user space
can unwind through it. Keep all function symbols in the vDSO .symtab
for stack trace purposes. This enables perf to lookup these function
symbols in addition to those already exported in vDSO .dynsym.
Starting with binutils 2.46 both GNU assembler and GNU linker
exclusively support generating and merging .sframe in SFrame V3 format.
For vDSO, only if supported by the assembler, generate .sframe, collect
it, mark it as KEEP, and generate a GNU_SFRAME program table entry.
Otherwise explicitly discard any .sframe.
[1]: x86/vdso: Enable sframe generation in VDSO,
https://lore.kernel.org/all/20260211141357.271402-7-jremus@linux.ibm.com/
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
@Dylan: Adding -Wa,--gsframe-3 to the VDSO CC_FLAGS_ADD_VDSO (and
AS_FLAGS_ADD_VDSO) may clash with your patch [1] that adds likewise
to the CC_FLAGS_REMOVE_VDSO. Any idea how to resolve?
[1]: [PATCH v3 2/8] arm64, unwind: build kernel with sframe V3 info,
https://lore.kernel.org/all/20260406185000.1378082-3-dylanbhatch@google.com/
arch/arm64/kernel/vdso/Makefile | 14 ++++++++++++--
arch/arm64/kernel/vdso/vdso.lds.S | 21 +++++++++++++++++++++
2 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index 7dec05dd33b7..1f2f01673397 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -15,6 +15,10 @@ obj-vdso := vgettimeofday.o note.o sigreturn.o vgetrandom.o vgetrandom-chacha.o
targets := $(obj-vdso) vdso.so vdso.so.dbg
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+ifeq ($(CONFIG_AS_SFRAME3),y)
+ SFRAME_CFLAGS := -Wa,--gsframe-3
+endif
+
btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti
# -Bsymbolic has been added for consistency with arm, the compat vDSO and
@@ -41,7 +45,9 @@ CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \
$(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \
-Wmissing-prototypes -Wmissing-declarations
-CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables
+CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables $(SFRAME_CFLAGS)
+
+AS_FLAGS_ADD_VDSO := $(SFRAME_CFLAGS)
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_REMOVE_VDSO)
CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO)
@@ -49,6 +55,10 @@ CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO)
CFLAGS_vgettimeofday.o = $(CC_FLAGS_ADD_VDSO)
CFLAGS_vgetrandom.o = $(CC_FLAGS_ADD_VDSO)
+AFLAGS_sigreturn.o = $(AS_FLAGS_ADD_VDSO)
+
+AFLAGS_vgetrandom-chacha.o = $(AS_FLAGS_ADD_VDSO)
+
ifneq ($(c-gettimeofday-y),)
CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
endif
@@ -65,7 +75,7 @@ $(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
$(call if_changed,vdsold_and_vdso_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)
diff --git a/arch/arm64/kernel/vdso/vdso.lds.S b/arch/arm64/kernel/vdso/vdso.lds.S
index 52314be29191..527e107ca4b5 100644
--- a/arch/arm64/kernel/vdso/vdso.lds.S
+++ b/arch/arm64/kernel/vdso/vdso.lds.S
@@ -15,6 +15,8 @@
#include <asm-generic/vmlinux.lds.h>
#include <vdso/datapage.h>
+#define KEEP_SFRAME IS_ENABLED(CONFIG_AS_SFRAME)
+
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
@@ -68,6 +70,13 @@ SECTIONS
*(.igot .igot.plt)
} :text
+#if KEEP_SFRAME
+ .sframe : {
+ KEEP (*(.sframe))
+ *(.sframe.*)
+ } :text :sframe
+#endif
+
_end = .;
PROVIDE(end = .);
@@ -78,9 +87,18 @@ SECTIONS
*(.data .data.* .gnu.linkonce.d.* .sdata*)
*(.bss .sbss .dynbss .dynsbss)
*(.eh_frame .eh_frame_hdr)
+#if !KEEP_SFRAME
+ *(.sframe)
+ *(.sframe.*)
+#endif
}
}
+/*
+ * Very old versions of ld do not recognize this name token; use the constant.
+ */
+#define PT_GNU_SFRAME 0x6474e554
+
/*
* We must supply the ELF program headers explicitly to get just one
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
@@ -90,6 +108,9 @@ PHDRS
text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
note PT_NOTE FLAGS(4); /* PF_R */
+#if KEEP_SFRAME
+ sframe PT_GNU_SFRAME FLAGS(4); /* PF_R */
+#endif
}
/*
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v1 4/4] arm64/unwind_user/sframe: Enable sframe unwinding on arm64
2026-04-17 15:08 [PATCH v1 0/4] arm64: SFrame user space unwinding Jens Remus
` (2 preceding siblings ...)
2026-04-17 15:08 ` [PATCH v1 3/4] arm64/vdso: Enable SFrame generation in vDSO Jens Remus
@ 2026-04-17 15:08 ` Jens Remus
3 siblings, 0 replies; 5+ messages in thread
From: Jens Remus @ 2026-04-17 15:08 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Steven Rostedt, Josh Poimboeuf,
Indu Bhagat, Peter Zijlstra, Dylan Hatch, Weinan Liu
Cc: Jens Remus, linux-arm-kernel, linux-kernel, Heiko Carstens,
Ilya Leoshkevich
Add arm64 support for unwinding of user space using SFrame.
This leverages the unwind user (sframe) support for s390 which
enables architectures that pass the return address in a register,
may not necessarily save the return address on the stack (for
instance in leaf functions), and have SP at call site equal
SP at entry.
For this purpose provide arm64-specific unwind_user_get_ra_reg() and
unwind_user_get_reg() implementations, which return the value of the
link register (LR) or an arbitrary register in the topmost user space
frame. Define the arm64 SP and FP DWARF register numbers.
Signed-off-by: Jens Remus <jremus@linux.ibm.com>
---
Notes (jremus):
Note: An arm64 implementation of unwind_user_get_reg() is strictly
only needed, if SFrame V3 flexible FDE would get generated for aarch64,
which is currently not the case in GNU Binutils 2.46.
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/unwind_user.h | 23 +++++++++++++++++++++
arch/arm64/include/asm/unwind_user_sframe.h | 8 +++++++
3 files changed, 32 insertions(+)
create mode 100644 arch/arm64/include/asm/unwind_user_sframe.h
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 994fd5162a1d..641a3a5fe5c9 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -254,6 +254,7 @@ config ARM64
select HAVE_STACKPROTECTOR
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_UNWIND_USER_FP
+ select HAVE_UNWIND_USER_SFRAME
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_GENERIC_VDSO
diff --git a/arch/arm64/include/asm/unwind_user.h b/arch/arm64/include/asm/unwind_user.h
index 0641d4d97b0f..3c7fd8c4ba5b 100644
--- a/arch/arm64/include/asm/unwind_user.h
+++ b/arch/arm64/include/asm/unwind_user.h
@@ -4,6 +4,7 @@
#include <linux/sched/task_stack.h>
#include <linux/types.h>
+#include <asm/insn.h>
#ifdef CONFIG_UNWIND_USER
@@ -16,6 +17,28 @@ static inline int unwind_user_word_size(struct pt_regs *regs)
return sizeof(long);
}
+static inline int unwind_user_get_ra_reg(unsigned long *val)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ *val = regs->regs[AARCH64_INSN_REG_LR];
+ return 0;
+}
+#define unwind_user_get_ra_reg unwind_user_get_ra_reg
+
+static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
+{
+ const struct pt_regs *regs = task_pt_regs(current);
+
+ if (regnum <= 30)
+ /* DWARF register numbers 0..15 */
+ *val = regs->regs[regnum];
+ else
+ return -EINVAL;
+
+ return 0;
+}
+#define unwind_user_get_reg unwind_user_get_reg
+
#endif /* CONFIG_UNWIND_USER */
#ifdef CONFIG_HAVE_UNWIND_USER_FP
diff --git a/arch/arm64/include/asm/unwind_user_sframe.h b/arch/arm64/include/asm/unwind_user_sframe.h
new file mode 100644
index 000000000000..65c0a6b6c835
--- /dev/null
+++ b/arch/arm64/include/asm/unwind_user_sframe.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_ARM64_UNWIND_USER_SFRAME_H
+#define _ASM_ARM64_UNWIND_USER_SFRAME_H
+
+#define SFRAME_REG_SP 31
+#define SFRAME_REG_FP 29
+
+#endif /* _ASM_ARM64_UNWIND_USER_SFRAME_H */
--
2.51.0
^ permalink raw reply related [flat|nested] 5+ messages in thread