The proposed Linux patch "alpha: add HAVE_PERF_REGS and
HAVE_PERF_USER_STACK_DUMP support" adds perf register sampling for
alpha, with the register numbering in
arch/alpha/include/uapi/asm/perf_regs.h. Implement the ebl hooks to
unwind from perf samples using it.

Perf register numbers 0-30 match the DWARF numbers and 31 is the pc, so
the default sample_perf_regs_mapping works, with index 31 standing for
the pc. Alpha's frame_nregs is 65, which is too large for the generic
ebl_set_initial_registers_sample, so provide a backend implementation.

The callee-saved $9-$15 are not in pt_regs on the interrupt path, so the
kernel cannot sample them. alpha_set_initial_registers_sample leaves
them unset, which means frames whose CFA is based on $15 (-O0 code, or
functions using alloca) cannot be unwound from a perf sample.

<asm/perf_regs.h> is only in newer kernel headers on alpha, so check for
it in configure and only allow --enable-stackprof on alpha when it is
present.

        * backends/alpha_initreg_sample.c: New file.
        * backends/Makefile.am (alpha_SRCS): Add alpha_initreg_sample.c.
        * backends/alpha_init.c: Include libebl_PERF_FLAGS.h.
        (alpha_init): Hook set_initial_registers_sample and sample_sp_pc.
        Set perf_frame_regs_mask.
        * backends/libebl_PERF_FLAGS.h: Include <asm/perf_regs.h> on alpha
        if present.
        (PERF_FRAME_REGISTERS_ALPHA): New define.
        * configure.ac: Check for asm/perf_regs.h. Set
        stackprof_arch_supported for alpha from it.
        * libdwfl_stacktrace/dwflst_sample_frame.c (dwflst_arch_from_uname):
        Handle alpha.
        (dwflst_arch_expected_frame_nregs): Likewise.
        (dwflst_arch_sp_dwarf_reg): Likewise.
        (dwflst_arch_sp_perf_reg): Likewise.
---
 backends/Makefile.am                     |  3 +-
 backends/alpha_init.c                    |  5 +++
 backends/alpha_initreg_sample.c          | 73 ++++++++++++++++++++++++++++++++
 backends/libebl_PERF_FLAGS.h             | 16 +++++++
 configure.ac                             |  6 ++-
 libdwfl_stacktrace/dwflst_sample_frame.c | 10 +++++
 6 files changed, 111 insertions(+), 2 deletions(-)

diff --git a/backends/Makefile.am b/backends/Makefile.am
index dac2c060..0effaaf2 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -53,7 +53,8 @@ x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c 
x86_64_cfi.c \
 ia64_SRCS = ia64_init.c ia64_symbol.c ia64_regs.c ia64_retval.c
 
 alpha_SRCS = alpha_init.c alpha_symbol.c alpha_retval.c alpha_regs.c \
-            alpha_corenote.c alpha_auxv.c alpha_cfi.c alpha_initreg.c
+            alpha_corenote.c alpha_auxv.c alpha_cfi.c alpha_initreg.c \
+            alpha_initreg_sample.c
 
 arm_SRCS = arm_init.c arm_symbol.c arm_regs.c arm_corenote.c \
           arm_auxv.c arm_attrs.c arm_retval.c arm_cfi.c arm_initreg.c \
diff --git a/backends/alpha_init.c b/backends/alpha_init.c
index 0e46e92f..88ef1edc 100644
--- a/backends/alpha_init.c
+++ b/backends/alpha_init.c
@@ -34,6 +34,7 @@
 #define BACKEND                alpha_
 #define RELOC_PREFIX   R_ALPHA_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on alpha_reloc.def.  */
 #include "common-reloc.c"
@@ -63,6 +64,10 @@ alpha_init (Elf *elf __attribute__ ((unused)),
      as the return address column.  */
   eh->frame_nregs = 65;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_sp_pc);
+  /* sample_perf_regs_mapping is default ver  */
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_ALPHA;
   eh->sysvhash_entrysize = sizeof (Elf64_Xword);
 
   return eh;
diff --git a/backends/alpha_initreg_sample.c b/backends/alpha_initreg_sample.c
new file mode 100644
index 00000000..0d34d099
--- /dev/null
+++ b/backends/alpha_initreg_sample.c
@@ -0,0 +1,73 @@
+/* Populate process registers from a linux perf_events sample.
+   Copyright (C) 2026 Matt Turner
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define BACKEND alpha_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+
+/* The default sample_perf_regs_mapping maps perf register numbers 1:1,
+   which gives the DWARF number for $0-$30.  PERF_REG_ALPHA_PC is 31,
+   which cannot be mistaken for $31 since the kernel does not sample
+   the zero register.  */
+#define SAMPLE_PC 31
+
+bool
+alpha_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs,
+                   const int *regs_mapping, size_t n_regs_mapping,
+                   Dwarf_Word *sp, Dwarf_Word *pc)
+{
+  return generic_sample_sp_pc (regs, n_regs, regs_mapping, n_regs_mapping,
+                              sp, 30 /* index of sp in dwarf_regs */,
+                              pc, SAMPLE_PC);
+}
+
+bool
+alpha_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs,
+                                   const int *regs_mapping, size_t 
n_regs_mapping,
+                                   ebl_tid_registers_t *setfunc,
+                                   void *arg)
+{
+  Dwarf_Word dwarf_regs[32] = { 0 };
+  for (size_t i = 0; i < n_regs && i < n_regs_mapping; i++)
+    if (regs_mapping[i] >= 0 && regs_mapping[i] < 32)
+      dwarf_regs[regs_mapping[i]] = regs[i];
+
+  Dwarf_Word pc = dwarf_regs[SAMPLE_PC];
+  dwarf_regs[31] = 0;
+
+  /* The kernel cannot sample the callee-saved $9-$15.  Leave them
+     unset so that the unwinder does not use a bogus value.  */
+  return (setfunc (0, 9, &dwarf_regs[0], arg)
+         && setfunc (16, 16, &dwarf_regs[16], arg)
+         && setfunc (-1, 1, &pc, arg));
+}
diff --git a/backends/libebl_PERF_FLAGS.h b/backends/libebl_PERF_FLAGS.h
index 07ffc38c..ebc7362b 100644
--- a/backends/libebl_PERF_FLAGS.h
+++ b/backends/libebl_PERF_FLAGS.h
@@ -37,6 +37,10 @@
 /* || defined(other_architecture)... */
 # include <asm/perf_regs.h>
 #endif
+/* Alpha's asm/perf_regs.h is only in newer kernel headers.  */
+#if defined(__alpha__) && defined(HAVE_ASM_PERF_REGS_H)
+# include <asm/perf_regs.h>
+#endif
 #endif
 
 #if defined(_ASM_X86_PERF_REGS_H)
@@ -100,6 +104,18 @@
 #define PERF_FRAME_REGISTERS_AARCH64 0
 #endif /* _ASM_ARM64_PERF_REGS_H */
 
+#if defined(_ASM_ALPHA_PERF_REGS_H)
+/* Everything the kernel can sample.  The callee-saved $9-$15 are not
+   in pt_regs.  */
+#define PERF_FRAME_REGISTERS_ALPHA (((1ULL << PERF_REG_ALPHA_MAX) - 1) \
+  & ~(0x7fULL << PERF_REG_ALPHA_R9))
+/* Register ordering defined in linux arch/alpha/include/uapi/asm/perf_regs.h. 
 */
+#else
+/* Since asm/perf_regs.h is absent, or gives the register layout for a
+   different arch, we can't unwind alpha perf sample frames.  */
+#define PERF_FRAME_REGISTERS_ALPHA 0
+#endif /* _ASM_ALPHA_PERF_REGS_H */
+
 static inline bool
 generic_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs,
                      const int *regs_mapping, size_t n_regs_mapping,
diff --git a/configure.ac b/configure.ac
index b0d1b48d..536fa72a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -937,7 +937,7 @@ AC_CHECK_HEADERS([perfmon/pfmlib_perf_event.h])
 AM_CONDITIONAL([HAVE_LIBPFM], [test 
"x${ac_cv_header_perfmon_pfmlib_perf_event_h}" = "xyes" ])
 AC_CHECK_LIB(pfm, pfm_get_os_event_encoding, [AC_SUBST(libpfm_LIBS, '-lpfm')])
 # required:
-AC_CHECK_HEADERS([linux/perf_event.h])
+AC_CHECK_HEADERS([linux/perf_event.h asm/perf_regs.h])
 AC_CHECK_MEMBER([struct perf_event_attr.build_id],
   [have_perf_event_attr_build_id=yes],
   [have_perf_event_attr_build_id=no],
@@ -957,6 +957,10 @@ case $host_cpu in
   i?86|x86_64|amd64|arm*|aarch64*)
     stackprof_arch_supported=yes
     ;;
+  alpha*)
+    # Alpha's <asm/perf_regs.h> is only in newer kernel headers.
+    stackprof_arch_supported=${ac_cv_header_asm_perf_regs_h}
+    ;;
   *)
     stackprof_arch_supported=no
     ;;
diff --git a/libdwfl_stacktrace/dwflst_sample_frame.c 
b/libdwfl_stacktrace/dwflst_sample_frame.c
index cbd5a79a..a555fccf 100644
--- a/libdwfl_stacktrace/dwflst_sample_frame.c
+++ b/libdwfl_stacktrace/dwflst_sample_frame.c
@@ -71,6 +71,8 @@ dwflst_arch_from_uname (const char *umachine)
     return EM_AARCH64;
   else if (strncmp(umachine, "armv7l", 6) == 0)
     return EM_ARM;
+  else if (strncmp(umachine, "alpha", 5) == 0)
+    return EM_ALPHA;
   /* XXX Other architectures not yet supported. */
   return EM_NONE;
 }
@@ -87,6 +89,9 @@ dwflst_arch_expected_frame_nregs (GElf_Half machine)
     return 14;
   if (machine == EM_ARM)
     return 16;
+  /* On alpha, expect everything the kernel can sample:  */
+  if (machine == EM_ALPHA)
+    return 25;
   /* On x86, expect everything except FLAGS:  */
   if (machine == EM_X86_64 || machine == EM_386)
     /* XXX An external user of the library can't access the Ebl, hence
@@ -112,6 +117,8 @@ dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool 
force_abi32)
     return 13;
   case EM_AARCH64:
     return force_abi32 ? 13 : 31;
+  case EM_ALPHA:
+    return 30;
   default:
     /* XXX Other architectures are not supported yet.  */
     return -1;
@@ -130,6 +137,9 @@ dwflst_arch_sp_perf_reg (GElf_Half machine,
   else if (machine == EM_ARM || machine == EM_AARCH64)
     sp_perf_index = (is_abi32 ? 13 : 31);
     /* basic linear mapping of perf_regs<->dwarf_regs */
+  else if (machine == EM_ALPHA)
+    sp_perf_index = 30;
+    /* PERF_REG_ALPHA_SP */
   else
     /* XXX Other architectures are not supported yet.  */
     return -1;

-- 
2.55.0

Reply via email to