Changes for v2:

- Merged commit 11 into commit 1 (both sample_base_addr/sample_pc and
  set_initial_registers_sample make sense to introduce in the same
  commit).

- Added i386 backend and factored out common code.

* * *

First patch of a series that reworks eu-stacktrace functionality
into a library interface for other profiling tools.

* * *

As it happens, Linux perf_events and DWARF can prescribe completely
different layouts for the register file, requiring non-obvious code
for translation. This makes sense to put in libebl if we want
profilers to handle perf sample data with elfutils.

Start with the x86_64/i386 implementation taken from eu-stacktrace. The
code has been generalized to accept other perf register masks besides
the 'preferred' one.

* backends/Makefile.am (i386_SRCS): Add i386_initreg_sample.c.
  (x86_64_SRCS): Add x86_64_initreg_sample.c.
  (noinst_HEADERS): Add libebl_PERF_FLAGS.h,
  linux-perf-regs.c, x86_initreg_sample.c.
* backends/libebl_PERF_FLAGS.h: New file.
* backends/linux-perf-regs.c: New file.
  (perf_sample_find_reg): Index into a perf register array whose
  contents are described by regs_mask. The target index is the index
  of the register in the enum defined by
  linux arch/N/include/uapi/asm/perf_regs.h on arch N.
* backends/x86_initreg_sample.c: New file, implements a generalized
  version of the eu-stacktrace register shuffling for x86-64/i386.
* backends/x86_64_initreg_sample.c: New file, specializes
  x86_initreg_sample.c for x86-64.
* backends/i386_initreg_sample.c: New file, specializes
  i386_initreg_sample.c for i386.
* backends/x86_64_init.c (x86_64_init): Add initialization for
  set_initial_registers_sample, perf_frame_regs_mask,
  sample_base_addr, sample_pc.
* backends/i386_init.c (i386_init): Add initialization for
  set_initial_registers_sample, perf_frame_regs_mask,
  sample_base_addr, sample_pc.
* libebl/Makefile.am (libebl_a_SOURCES): Add eblinitreg_sample.c.
* libebl/ebl-hooks.h (set_initial_registers_sample): New hook.
  (sample_base_addr): Ditto.
  (sample_pc): Ditto.
* libebl/eblinitreg_sample.c: New file, implements ebl interface to
  set_initial_registers_sample, sample_base_addr, sample_pc
  backend hooks.
* libebl/libebl.h (ebl_set_initial_registers_sample): New function.
  (ebl_perf_frame_regs_mask): New function.
  (ebl_sample_base_addr): New function.
  (ebl_sample_pc): New function.
* libebl/libeblP.h (struct ebl): Add perf_frame_regs_mask field
  giving the preferred register mask.
---
 backends/Makefile.am             |  10 +--
 backends/i386_init.c             |  10 ++-
 backends/i386_initreg_sample.c   | 108 +++++++++++++++++++++++++++++++
 backends/libebl_PERF_FLAGS.h     |  58 +++++++++++++++++
 backends/linux-perf-regs.c       |  48 ++++++++++++++
 backends/x86_64_init.c           |   7 +-
 backends/x86_64_initreg_sample.c | 106 ++++++++++++++++++++++++++++++
 backends/x86_initreg_sample.c    |  90 ++++++++++++++++++++++++++
 libebl/Makefile.am               |   4 +-
 libebl/ebl-hooks.h               |  18 +++++-
 libebl/eblinitreg_sample.c       |  72 +++++++++++++++++++++
 libebl/libebl.h                  |  31 ++++++++-
 libebl/libeblP.h                 |   7 +-
 13 files changed, 557 insertions(+), 12 deletions(-)
 create mode 100644 backends/i386_initreg_sample.c
 create mode 100644 backends/libebl_PERF_FLAGS.h
 create mode 100644 backends/linux-perf-regs.c
 create mode 100644 backends/x86_64_initreg_sample.c
 create mode 100644 backends/x86_initreg_sample.c
 create mode 100644 libebl/eblinitreg_sample.c

diff --git a/backends/Makefile.am b/backends/Makefile.am
index 540d0c6c..8ccbdb50 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2000-2010, 2013, 2014 Red Hat, Inc.
+## Copyright (C) 2000-2010, 2013, 2014, 2025 Red Hat, Inc.
 ## Copyright (C) 2012 Tilera Corporation
 ## This file is part of elfutils.
 ##
@@ -41,13 +41,13 @@ modules = i386 sh x86_64 ia64 alpha arm aarch64 sparc ppc 
ppc64 s390 \
 
 i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c i386_cfi.c \
            i386_retval.c i386_regs.c i386_auxv.c \
-           i386_initreg.c i386_unwind.c
+           i386_initreg.c i386_initreg_sample.c i386_unwind.c
 
 sh_SRCS = sh_init.c sh_symbol.c sh_corenote.c sh_regs.c sh_retval.c
 
 x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c x86_64_cfi.c \
              x86_64_retval.c x86_64_regs.c x86_64_initreg.c \
-             x86_64_unwind.c x32_corenote.c
+             x86_64_initreg_sample.c x86_64_unwind.c x32_corenote.c
 
 
 ia64_SRCS = ia64_init.c ia64_symbol.c ia64_regs.c ia64_retval.c
@@ -119,7 +119,9 @@ libebl_backends_a_SOURCES = $(i386_SRCS) $(sh_SRCS) 
$(x86_64_SRCS) \
 libebl_backends_pic_a_SOURCES =
 am_libebl_backends_pic_a_OBJECTS = $(libebl_backends_a_SOURCES:.c=.os)
 
-noinst_HEADERS = libebl_CPU.h common-reloc.c linux-core-note.c x86_corenote.c
+noinst_HEADERS = libebl_CPU.h libebl_PERF_FLAGS.h common-reloc.c \
+           linux-core-note.c x86_corenote.c \
+           linux-perf-regs.c x86_initreg_sample.c
 
 EXTRA_DIST = $(modules:=_reloc.def)
 
diff --git a/backends/i386_init.c b/backends/i386_init.c
index 579e5fad..e64ef6ed 100644
--- a/backends/i386_init.c
+++ b/backends/i386_init.c
@@ -1,5 +1,5 @@
 /* Initialization of i386 specific backend library.
-   Copyright (C) 2000-2009, 2013, 2017 Red Hat, Inc.
+   Copyright (C) 2000-2009, 2013, 2017, 2025 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drep...@redhat.com>, 2000.
 
@@ -34,6 +34,7 @@
 #define BACKEND                i386_
 #define RELOC_PREFIX   R_386_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on i386_reloc.def.  */
 #include "common-reloc.c"
@@ -55,9 +56,14 @@ i386_init (Elf *elf __attribute__ ((unused)),
   HOOK (eh, auxv_info);
   HOOK (eh, disasm);
   HOOK (eh, abi_cfi);
-  /* gcc/config/ #define DWARF_FRAME_REGISTERS.  For i386 it is 17, why?  */
+  /* gcc/config/ #define DWARF_FRAME_REGISTERS.  For i386 it is 17, why?
+     (Likely an artifact of reusing that header between i386/x86_64.)  */
   eh->frame_nregs = 9;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_base_addr);
+  HOOK (eh, sample_pc);
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_I386;
   HOOK (eh, unwind);
 
   return eh;
diff --git a/backends/i386_initreg_sample.c b/backends/i386_initreg_sample.c
new file mode 100644
index 00000000..3df74412
--- /dev/null
+++ b/backends/i386_initreg_sample.c
@@ -0,0 +1,108 @@
+/* Populate process registers from a linux perf_events sample.
+   Copyright (C) 2025 Red Hat, Inc.
+   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
+
+#include <stdlib.h>
+#if (defined __i386__ || defined __x86_64__) && defined(__linux__)
+# include <linux/perf_event.h>
+# include <asm/perf_regs.h>
+#endif
+
+#define BACKEND i386_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+#if (defined __i386__ || defined __x86_64__) && defined(__linux__)
+# include "linux-perf-regs.c"
+# include "x86_initreg_sample.c"
+#endif
+
+/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h,
+   enum perf_event_x86_regs: */
+Dwarf_Word
+i386_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs,
+                         uint64_t regs_mask,
+                        /* XXX hypothetically needed if abi varies
+                           between samples in the same process;
+                           not needed on x86 */
+                        uint32_t abi __attribute__((unused)))
+{
+#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  return 0;
+#else /* __i386__ || __x86_64__ */
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  return perf_sample_find_reg(regs, n_regs, regs_mask,
+                             7 /* index into perf_event_x86_regs */);
+#endif
+}
+
+Dwarf_Word
+i386_sample_pc (const Dwarf_Word *regs, uint32_t n_regs,
+               uint64_t regs_mask,
+               uint32_t abi __attribute__((unused)))
+{
+#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  return 0;
+#else /* __i386__ || __x86_64__ */
+  return perf_sample_find_reg(regs, n_regs, regs_mask,
+                             8 /* index into perf_event_x86_regs */);
+#endif
+}
+
+bool
+i386_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs,
+                                  uint64_t regs_mask, uint32_t abi,
+                                  ebl_tid_registers_t *setfunc,
+                                  void *arg)
+{
+#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  (void)abi;
+  (void)setfunc;
+  (void)arg;
+  return false;
+#else /* __i386__ || __x86_64__ */
+  Dwarf_Word dwarf_regs[9];
+  if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask,
+                                        abi, dwarf_regs, 9))
+    return false;
+  return setfunc (0, 9, dwarf_regs, arg);
+#endif
+}
diff --git a/backends/libebl_PERF_FLAGS.h b/backends/libebl_PERF_FLAGS.h
new file mode 100644
index 00000000..2ed45f0f
--- /dev/null
+++ b/backends/libebl_PERF_FLAGS.h
@@ -0,0 +1,58 @@
+/* Linux perf_events sample_regs_user flags required for unwinding.
+   Internal only; elfutils library users should use ebl_perf_frame_regs_mask().
+
+   Copyright (C) 2025 Red Hat, Inc.
+   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/>.  */
+
+#ifndef _LIBEBL_PERF_FLAGS_H
+#define _LIBEBL_PERF_FLAGS_H 1
+
+#if defined(__linux__)
+# include <asm/perf_regs.h>
+#endif
+
+#if defined(_ASM_X86_PERF_REGS_H)
+/* See the code in x86_initreg_sample.c for list of required regs and
+   linux arch/.../include/asm/ptrace.h for matching pt_regs struct.  */
+#define REG(R) (1ULL << PERF_REG_X86_ ## R)
+/* FLAGS and segment regs are excluded from the following masks,
+   since they're not needed for unwinding.  */
+#define PERF_FRAME_REGISTERS_I386 (REG(AX) | REG(BX) | REG(CX) | REG(DX) \
+  | REG(SI) | REG(DI) | REG(BP) | REG(SP) | REG(IP))
+#define PERF_FRAME_REGISTERS_X86_64 (PERF_FRAME_REGISTERS_I386 | REG(R8) \
+  | REG(R9) | REG(R10) | REG(R11) | REG(R12) | REG(R13) | REG(R14) | REG(R15))
+/* Register ordering defined in linux arch/x86/include/uapi/asm/perf_regs.h;
+   see the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs()
+   and note how regs are added in the same order as the perf_regs.h enum.  */
+#else
+/* Since asm/perf_regs.h gives the register layout for a different arch,
+   we can't unwind x86_64 frames.  */
+#define PERF_FRAME_REGISTERS_I386 0
+#define PERF_FRAME_REGISTERS_X86_64 0
+#endif
+
+#endif /* libebl_PERF_FLAGS.h */
diff --git a/backends/linux-perf-regs.c b/backends/linux-perf-regs.c
new file mode 100644
index 00000000..22ad67c6
--- /dev/null
+++ b/backends/linux-perf-regs.c
@@ -0,0 +1,48 @@
+/* Common pieces for handling registers in a linux perf_events sample.
+   Copyright (C) 2025 Red Hat, Inc.
+   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/>.  */
+
+static Dwarf_Word
+perf_sample_find_reg (const Dwarf_Word *regs, uint32_t n_regs,
+                     uint64_t regs_mask,
+                     int target)
+{
+  int j, k; uint64_t bit;
+  for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1)
+    {
+      if (bit & regs_mask) {
+       if (n_regs <= (uint32_t) j)
+         return 0; /* regs_mask count doesn't match n_regs */
+       if (k == target)
+         return regs[j];
+       if (k > target)
+         return 0; /* regs_mask doesn't include desired reg */
+       j++;
+      }
+    }
+  return 0;
+}
diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c
index be965fa6..6a1cbc4b 100644
--- a/backends/x86_64_init.c
+++ b/backends/x86_64_init.c
@@ -1,5 +1,5 @@
 /* Initialization of x86-64 specific backend library.
-   Copyright (C) 2002-2009, 2013, 2018 Red Hat, Inc.
+   Copyright (C) 2002-2009, 2013, 2018, 2025 Red Hat, Inc.
    Copyright (C) H.J. Lu <hjl.to...@gmail.com>, 2015.
    This file is part of elfutils.
    Written by Ulrich Drepper <drep...@redhat.com>, 2002.
@@ -35,6 +35,7 @@
 #define BACKEND                x86_64_
 #define RELOC_PREFIX   R_X86_64_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on x86_64_reloc.def.  */
 #include "common-reloc.c"
@@ -62,6 +63,10 @@ x86_64_init (Elf *elf __attribute__ ((unused)),
   /* gcc/config/ #define DWARF_FRAME_REGISTERS.  */
   eh->frame_nregs = 17;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_base_addr);
+  HOOK (eh, sample_pc);
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_X86_64;
   HOOK (eh, unwind);
   HOOK (eh, check_reloc_target_type);
 
diff --git a/backends/x86_64_initreg_sample.c b/backends/x86_64_initreg_sample.c
new file mode 100644
index 00000000..71d594bd
--- /dev/null
+++ b/backends/x86_64_initreg_sample.c
@@ -0,0 +1,106 @@
+/* Populate process registers from a linux perf_events sample.
+   Copyright (C) 2025 Red Hat, Inc.
+   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
+
+#include <stdlib.h>
+#if defined(__x86_64__) && defined(__linux__)
+# include <linux/perf_event.h>
+# include <asm/perf_regs.h>
+#endif
+
+#define BACKEND x86_64_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+#if defined(__x86_64__) && defined(__linux__)
+# include "linux-perf-regs.c"
+# include "x86_initreg_sample.c"
+#endif
+
+/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h,
+   enum perf_event_x86_regs: */
+Dwarf_Word
+x86_64_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs,
+                        uint64_t regs_mask,
+                        /* XXX hypothetically needed if abi varies
+                           between samples in the same process;
+                           not needed on x86*/
+                        uint32_t abi __attribute__((unused)))
+{
+#if !defined(__x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  return 0;
+#else /* __x86_64__ */
+  return perf_sample_find_reg(regs, n_regs, regs_mask,
+                             7 /* index into perf_event_x86_regs */);
+#endif
+}
+
+Dwarf_Word
+x86_64_sample_pc (const Dwarf_Word *regs, uint32_t n_regs,
+                 uint64_t regs_mask,
+                 uint32_t abi __attribute__((unused)))
+{
+#if !defined(__x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  return 0;
+#else /* __x86_64__ */
+  return perf_sample_find_reg(regs, n_regs, regs_mask,
+                               8 /* index into perf_event_x86_regs */);
+#endif
+}
+
+bool
+x86_64_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs,
+                                    uint64_t regs_mask, uint32_t abi,
+                                    ebl_tid_registers_t *setfunc,
+                                    void *arg)
+{
+#if !defined(__x86_64__) || !defined(__linux__)
+  (void)regs;
+  (void)n_regs;
+  (void)regs_mask;
+  (void)abi;
+  (void)setfunc;
+  (void)arg;
+  return false;
+#else /* __x86_64__ */
+  Dwarf_Word dwarf_regs[17];
+  if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask,
+                                        abi, dwarf_regs, 9))
+    return false;
+  return setfunc (0, 17, dwarf_regs, arg);
+#endif
+}
+
diff --git a/backends/x86_initreg_sample.c b/backends/x86_initreg_sample.c
new file mode 100644
index 00000000..e323bada
--- /dev/null
+++ b/backends/x86_initreg_sample.c
@@ -0,0 +1,90 @@
+/* x86 linux perf_events register handling, pieces common to x86-64 and i386.
+   Copyright (C) 2025 Red Hat, Inc.
+   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/>.  */
+
+static bool
+x86_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs,
+                                 uint64_t regs_mask, uint32_t abi,
+                                 Dwarf_Word *dwarf_regs, int expected_regs)
+{
+#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__)
+  return false;
+#else /* __i386__ || __x86_64__ */
+  /* The following facts are needed to translate x86 registers correctly:
+     - perf register order seen in linux arch/x86/include/uapi/asm/perf_regs.h
+       The registers array is built in the same order as the enum!
+       (See the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs().)
+     - EBL PERF_FRAME_REGS_MASK specifies all registers except segment and
+       flags.  However, regs_mask might be a different set of registers.
+       Again, regs_mask bits are in asm/perf_regs.h enum order.
+     - dwarf register order seen in elfutils backends/{x86_64,i386}_initreg.c
+       (matching pt_regs struct in linux arch/x86/include/asm/ptrace.h)
+       and it's a fairly different register order!
+
+     For comparison, you can study 
codereview.qt-project.org/gitweb?p=qt-creator/perfparser.git;a=blob;f=app/perfregisterinfo.cpp;hb=HEAD
+     and follow the code which uses those tables of magic numbers.
+     But it's better to follow original sources of truth for this.  */
+
+  bool is_abi32 = (abi == PERF_SAMPLE_REGS_ABI_32);
+
+  /* Locations of dwarf_regs in the perf_event_x86_regs enum order,
+     not the regs[i] array (which will include a subset of the regs): */
+  static const int regs_i386[] = {0, 2, 3, 1, 7/*sp*/, 6, 4, 5, 8/*ip*/};
+  static const int regs_x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7/*sp*/,
+                                   16/*r8 after flags+segment*/, 17, 18, 19, 
20, 21, 22, 23,
+                                   8/*ip*/};
+  const int *dwarf_to_perf = is_abi32 ? regs_i386 : regs_x86_64;
+
+  /* Locations of perf_regs in the regs[] array, according to regs_mask: */
+  int perf_to_regs[PERF_REG_X86_64_MAX];
+  uint64_t expected_mask = is_abi32 ? PERF_FRAME_REGISTERS_I386 : 
PERF_FRAME_REGISTERS_X86_64;
+  int j, k; uint64_t bit;
+  /* TODO(REVIEW): Is it worth caching this perf_to_regs computation
+     as long as regs_mask is kept the same across repeated calls? */
+  for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1)
+    {
+      if ((bit & expected_mask) && (bit & regs_mask)) {
+       if (n_regs <= (uint32_t)j)
+         return false; /* regs_mask count doesn't match n_regs */
+       perf_to_regs[k] = j;
+       j++;
+      } else {
+       perf_to_regs[k] = -1;
+      }
+    }
+
+  for (int i = 0; i < expected_regs; i++)
+    {
+      k = dwarf_to_perf[i];
+      j = perf_to_regs[k];
+      if (j < 0) continue;
+      if (n_regs <= (uint32_t)j) continue;
+      dwarf_regs[i] = regs[j];
+    }
+  return true;
+#endif /* __i386__ || __x86_64__ */
+}
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index ea092b5a..3df12ce2 100644
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2000-2010, 2013, 2016, 2017 Red Hat, Inc.
+## Copyright (C) 2000-2010, 2013, 2016, 2017, 2025 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -51,7 +51,7 @@ libebl_a_SOURCES = eblopenbackend.c eblclosebackend.c 
eblreloctypename.c \
                   eblbsspltp.c eblretval.c eblreginfo.c eblnonerelocp.c \
                   eblrelativerelocp.c eblsysvhashentrysize.c eblauxvinfo.c \
                   eblcheckobjattr.c ebl_check_special_section.c \
-                  eblabicfi.c eblstother.c eblinitreg.c \
+                  eblabicfi.c eblstother.c eblinitreg.c eblinitreg_sample.c \
                   ebldwarftoregno.c eblnormalizepc.c eblunwind.c \
                   eblresolvesym.c eblcheckreloctargettype.c \
                   ebl_data_marker_symbol.c
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index d6437e53..05474fbc 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -1,5 +1,5 @@
 /* Backend hook signatures internal interface for libebl.
-   Copyright (C) 2000-2011, 2013, 2014, 2016, 2017 Red Hat, Inc.
+   Copyright (C) 2000-2011, 2013, 2014, 2016, 2017, 2025 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -158,6 +158,22 @@ bool EBLHOOK(set_initial_registers_tid) (pid_t tid,
                                         ebl_tid_registers_t *setfunc,
                                         void *arg);
 
+/* Set process data from a perf_events sample and call SETFUNC one or more 
times.
+   Method should be present only when EBL_PERF_FRAME_REGS_MASK > 0, otherwise 
the
+   backend doesn't support unwinding from perf_events data.  */
+bool EBLHOOK(set_initial_registers_sample) (const Dwarf_Word *regs, uint32_t 
n_regs,
+                                           uint64_t regs_mask, uint32_t abi,
+                                           ebl_tid_registers_t *setfunc,
+                                           void *arg);
+
+/* Extract the stack address from a perf_events register sample.  */
+Dwarf_Word EBLHOOK(sample_base_addr) (const Dwarf_Word *regs, uint32_t n_regs,
+                                     uint64_t regs_mask, uint32_t abi);
+
+/* Extract the instruction pointer from a perf_events register sample.  */
+Dwarf_Word EBLHOOK(sample_pc) (const Dwarf_Word *regs, uint32_t n_regs,
+                              uint64_t regs_mask, uint32_t abi);
+
 /* Convert *REGNO as is in DWARF to a lower range suitable for
    Dwarf_Frame->REGS indexing.  */
 bool EBLHOOK(dwarf_to_regno) (Ebl *ebl, unsigned *regno);
diff --git a/libebl/eblinitreg_sample.c b/libebl/eblinitreg_sample.c
new file mode 100644
index 00000000..53244d1e
--- /dev/null
+++ b/libebl/eblinitreg_sample.c
@@ -0,0 +1,72 @@
+/* Populate process Dwfl_Frame from perf_events sample.
+
+   Copyright (C) 2025 Red Hat, Inc.
+   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
+
+#include <libeblP.h>
+#include <assert.h>
+
+Dwarf_Word
+ebl_sample_base_addr (Ebl *ebl,
+                      const Dwarf_Word *regs, uint32_t n_regs,
+                     uint64_t regs_mask, uint32_t abi)
+{
+  assert (ebl->sample_base_addr != NULL);
+  return ebl->sample_base_addr (regs, n_regs, regs_mask, abi);
+}
+
+Dwarf_Word
+ebl_sample_pc (Ebl *ebl,
+              const Dwarf_Word *regs, uint32_t n_regs,
+              uint64_t regs_mask, uint32_t abi)
+{
+  assert (ebl->sample_pc != NULL);
+  return ebl->sample_pc (regs, n_regs, regs_mask, abi);
+}
+
+bool
+ebl_set_initial_registers_sample (Ebl *ebl,
+                                 const Dwarf_Word *regs, uint32_t n_regs,
+                                 uint64_t regs_mask, uint32_t abi,
+                                 ebl_tid_registers_t *setfunc,
+                                 void *arg)
+{
+  /* If set_initial_registers_sample is unsupported then PERF_FRAME_REGS_MASK 
is zero.  */
+  assert (ebl->set_initial_registers_sample != NULL);
+  return ebl->set_initial_registers_sample (regs, n_regs, regs_mask, abi, 
setfunc, arg);
+}
+
+uint64_t
+ebl_perf_frame_regs_mask (Ebl *ebl)
+{
+  /* ebl is declared NN */
+  return ebl->perf_frame_regs_mask;
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index 731001d3..a64d70e9 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -1,5 +1,5 @@
 /* Interface for libebl.
-   Copyright (C) 2000-2010, 2013, 2014, 2015, 2016, 2017 Red Hat, Inc.
+   Copyright (C) 2000-2010, 2013, 2014, 2015, 2016, 2017, 2025 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -340,6 +340,35 @@ extern bool ebl_set_initial_registers_tid (Ebl *ebl,
 extern size_t ebl_frame_nregs (Ebl *ebl)
   __nonnull_attribute__ (1);
 
+/* Callback to set process data from a linux perf_events sample.
+   EBL architecture has to have EBL_PERF_FRAME_REGS_MASK > 0, otherwise the
+   backend doesn't support unwinding from perf_events sample data.  */
+extern bool ebl_set_initial_registers_sample (Ebl *ebl,
+                                             const Dwarf_Word *regs, uint32_t 
n_regs,
+                                             uint64_t regs_mask, uint32_t abi,
+                                             ebl_tid_registers_t *setfunc,
+                                             void *arg)
+  __nonnull_attribute__ (1, 2, 6);
+
+/* Extract the stack address from a perf_events register sample.  */
+Dwarf_Word ebl_sample_base_addr (Ebl *ebl,
+                                const Dwarf_Word *regs, uint32_t n_regs,
+                                uint64_t regs_mask, uint32_t abi)
+  __nonnull_attribute__ (1, 2);
+
+/* Extract the instruction pointer from a perf_events register sample.  */
+Dwarf_Word ebl_sample_pc (Ebl *ebl,
+                         const Dwarf_Word *regs, uint32_t n_regs,
+                         uint64_t regs_mask, uint32_t abi)
+  __nonnull_attribute__ (1, 2);
+
+
+/* Preferred sample_regs_user mask to request from linux perf_events
+   to allow unwinding on EBL architecture.  Omitting some of these
+   registers may result in failed or inaccurate unwinding. */
+extern uint64_t ebl_perf_frame_regs_mask (Ebl *ebl)
+  __nonnull_attribute__ (1);
+
 /* Offset to apply to the value of the return_address_register, as
    fetched from a Dwarf CFI.  This is used by some backends, where the
    return_address_register actually contains the call address.  */
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index c408ed97..be14cc20 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -1,5 +1,5 @@
 /* Internal definitions for interface for libebl.
-   Copyright (C) 2000-2009, 2013, 2014 Red Hat, Inc.
+   Copyright (C) 2000-2009, 2013, 2014, 2025 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -60,6 +60,11 @@ struct ebl
      Ebl architecture can unwind iff FRAME_NREGS > 0.  */
   size_t frame_nregs;
 
+  /* Preferred sample_regs_user mask to request from linux perf_events
+     to allow unwinding.  Ebl architecture supports unwinding from
+     perf_events sample data iff PERF_FRAME_REGS_MASK > 0.  */
+  uint64_t perf_frame_regs_mask;
+
   /* Offset to apply to the value of the return_address_register, as
      fetched from a Dwarf CFI.  This is used by some backends, where
      the return_address_register actually contains the call
-- 
2.47.0

Reply via email to