[PATCH v1 3/4] arm64/vdso: Enable SFrame generation in vDSO

Jens Remus
2026-04-17T15:09:01Z

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