]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
backends/: sketch powerpc sample_regs support
authorSerhei Makarov <serhei@serhei.io>
Thu, 12 Mar 2026 19:29:04 +0000 (15:29 -0400)
committerSerhei Makarov <serhei@serhei.io>
Fri, 20 Mar 2026 18:12:58 +0000 (14:12 -0400)
{speculative, but it should look roughly like this}

backends/Makefile.am
backends/libebl_PERF_FLAGS.h
backends/ppc64_init.c
backends/ppc_init.c
backends/ppc_initreg_sample.c [new file with mode: 0644]

index 318acef8aa1b5884ddf27c9e366107518df05493..7a8f8169e6384a8a96295eee397e139121fb6b64 100644 (file)
@@ -69,7 +69,7 @@ sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c \
 
 ppc_SRCS = ppc_init.c ppc_symbol.c ppc_retval.c ppc_regs.c \
           ppc_corenote.c ppc_auxv.c ppc_attrs.c \
-          ppc_cfi.c ppc_initreg.c
+          ppc_cfi.c ppc_initreg.c ppc_initreg_sample.c
 
 ppc64_SRCS = ppc64_init.c ppc64_symbol.c ppc64_retval.c ppc64_corenote.c \
             ppc64_unwind.c ppc64_resolve_sym.c
index 3dcb993814f363022ba75f16d4f8bf30206c77a5..4e8b000cc28d2cc89bae1f1a4b5565bbeb45b4a7 100644 (file)
@@ -33,7 +33,7 @@
 
 #if defined(__linux__)
 /* XXX Need to exclude __linux__ arches without perf_regs.h. */
-#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__powerpc__)
 /* || defined(other_architecture)... */
 # include <asm/perf_regs.h>
 #endif
 #define PERF_FRAME_REGISTERS_AARCH64 0
 #endif /* _ASM_ARM64_PERF_REGS_H */
 
+#if defined(_UAPI_ASM_POWERPC_PERF_REGS_H)
+#define REG(R) (1ULL << PERF_REG_POWERPC_ ## R)
+/* TODO(REVIEW) The same register file is provided for 32-bit and
+   64-bit powerpc architectures. Are the same registers needed for
+   unwinding?  */
+#define PERF_FRAME_REGISTERS_POWERPC (REG(R1) | REG(R2) | REG(R3) | REG(R4) \
+  | REG(R5) | REG(R6) | REG(R7) | REG(R8) | REG(R9) | REG(R10) | REG(R11)   \
+  | REG(R12) | REG(R13) | REG(R14) | REG(R15) | REG(R16) | REG(R17)         \
+  | REG(R18) | REG(R19) | REG(R20) | REG(R21) | REG(R22) | REG(R23)         \
+  | REG(R24) | REG(R25) | REG(R26) | REG(R27) | REG(R28) | REG(R29)         \
+  | REG(R22) | REG(R23) | REG(R24) | REG(R25) | REG(R26) | REG(R27)         \
+  | REG(R28) | REG(R29) | REG(R30) | REG(R31) | REG(NIP) | REG(LINK))
+/* Register ordering defined in linux arch/powerpc/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 powerpc perf sample frames.  */
+#define PERF_FRAME_REGISTERS_POWERPC 0
+#endif /* _UAPI_ASM_POWERPC_PERF_REGS_H */
+
 /* TODO(REVIEW) Replaces x86_sample_sp_pc -- is this header the right location for it? */
 static inline bool
 generic_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs,
index ffc9842cc084fc19ef1f9989cdc365cb16a4cda6..edc2a33c5851dc8f045bf5cbecf6814e6f216019 100644 (file)
@@ -1,5 +1,5 @@
 /* Initialization of PPC64 specific backend library.
-   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013, 2014 Red Hat, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013, 2014, 2026 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -36,6 +36,7 @@
 #define BACKEND                ppc64_
 #define RELOC_PREFIX   R_PPC64_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on ppc64_reloc.def.  */
 #include "common-reloc.c"
@@ -65,6 +66,11 @@ ppc64_init (Elf *elf __attribute__ ((unused)),
   /* gcc/config/ #define DWARF_FRAME_REGISTERS.  */
   eh->frame_nregs = (114 - 1) + 32;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_sp_pc);
+  HOOK (eh, sample_perf_regs_mapping);
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_POWERPC;
+  __libebl_init_cached_regs_mapping(eh);
   HOOK (eh, dwarf_to_regno);
   HOOK (eh, unwind);
   HOOK (eh, resolve_sym_value);
index 08468f8ffb96c2a03905c8c50c907fb7df1753b6..45541d3010dc3c17ff94488840d4d2acf6e22d1d 100644 (file)
@@ -1,5 +1,5 @@
 /* Initialization of PPC specific backend library.
-   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013 Red Hat, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007, 2008, 2013, 2026 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -34,6 +34,7 @@
 #define BACKEND                ppc_
 #define RELOC_PREFIX   R_PPC_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on ppc_reloc.def.  */
 #include "common-reloc.c"
@@ -61,6 +62,11 @@ ppc_init (Elf *elf __attribute__ ((unused)),
   /* gcc/config/ #define DWARF_FRAME_REGISTERS.  */
   eh->frame_nregs = (114 - 1) + 32;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_sp_pc);
+  HOOK (eh, sample_perf_regs_mapping);
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_POWERPC;
+  __libebl_init_cached_regs_mapping(eh);
   HOOK (eh, dwarf_to_regno);
 
   return eh;
diff --git a/backends/ppc_initreg_sample.c b/backends/ppc_initreg_sample.c
new file mode 100644 (file)
index 0000000..7359f97
--- /dev/null
@@ -0,0 +1,176 @@
+/* Populate process registers from a register sample.
+   Copyright (C) 2026 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>
+#include <assert.h>
+
+#define BACKEND ppc_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+
+/* LINK at dwarf_reg 65 from perf_reg 36 */
+#define LINK_DWARF 65
+#define LINK_PERF 36
+/* pc/NIP at dwarf_reg 64 from perf_reg 32 */
+#define NIP_DWARF 64
+#define NIP_PERF 32
+/* GPRS R0..R31 */
+#define GPRS 32
+
+
+bool
+ppc_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, 1 /* index of sp/GPR1 in dwarf_regs */,
+                              pc, 64 /* index of pc/NIP in dwarf_regs */);
+}
+
+bool
+ppc_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)
+{
+/* TODO(REVIEW): The #ifdef here seems strictly optional as we don't
+   refer to perf_events or ptrace arch-specific declarations. */
+#if !defined(__powerpc__)
+  (void)regs; (void)n_regs;
+  (void)regs_mapping; (void)n_regs_mapping;
+  (void)setfunc; (void)arg;
+  return false;
+#else /* __powerpc__ */
+  Dwarf_Word link = 0x0;
+  Dwarf_Word pc = 0x0;
+  Dwarf_Word dwarf_regs[GPRS];
+  for (i = 0; i < GPRS; i++)
+    dwarf_regs[i] = 0x0;
+  for (i = 0; i < n_regs; i++)
+    {
+      if (i >= n_regs_mapping)
+       break;
+      if (regs_mapping[i] == LINK_DWARF)
+       link = regs[i];
+      if (regs_mapping[i] == NIP_DWARF)
+       pc = regs[i];
+      if (regs_mapping[i] < 0 || regs_mapping[i] >= GPRS)
+       continue;
+      dwarf_regs[regs_mapping[i]] = regs[i];
+    }
+
+  if (!setfunc (0, GPRS, dwarf_regs, arg))
+    return false;
+  if (!setfunc (65, 1, &link, arg))
+    return false;
+  return setfunc (-1, 1, &pc, arg);
+#endif
+}
+
+bool
+ppc_sample_perf_regs_mapping (Ebl *ebl,
+                             uint64_t perf_regs_mask,
+                             uint32_t abi __attribute__((unused)),
+                             const int **regs_mapping,
+                             size_t *n_regs_mapping)
+{
+#if !defined(__powerpc__)
+  (void)ebl; (void)perf_regs_mask;
+  (void)regs_mapping; (void)n_regs_mapping;
+  return false;
+#else /* __powerpc__ */
+  if (perf_regs_mask != 0 && ebl->cached_perf_regs_mask == perf_regs_mask)
+    {
+      *regs_mapping = ebl->cached_regs_mapping;
+      *n_regs_mapping = ebl->cached_n_regs_mapping;
+      return true;
+    }
+
+  /* Only slight remapping of perf_regs to dwarf_regs needed:
+     - GPRS 0..31 unchanged;
+     - PC 32 sent to dwarf_reg 64 (then to -1 when using setfunc);
+     - LC 36 sent to dwarf_reg 65.
+     - Other registers not used for unwinding cf ppc_initreg.c.  */
+
+  /* Count bits and allocate regs_mapping:  */
+  int j, k, kmax, count; uint64_t bit;
+  for (k = 0, kmax = -1, count = 0, bit = 1;
+       k < PERF_REG_POWERPC_MAX; k++, bit <<= 1)
+    {
+      if ((bit & perf_regs_mask)) {
+       count++;
+       kmax = k;
+      }
+    }
+  ebl->cached_perf_regs_mask = perf_regs_mask;
+  ebl->cached_regs_mapping = (int *)calloc (count, sizeof(int));
+  ebl->cached_n_regs_mapping = count;
+
+  /* Locations of perf_regs in the dwarf_regs array,
+     according to perf_regs_mask: */
+  for (j = 0, k = 0, bit = 1; k <= kmax; k++, bit <<= 1)
+    {
+      int i = -1;
+      if (!(bit & perf_regs_mask))
+       {
+         continue;
+       }
+      if (0 <= k && k < GPRS)
+       i = k;
+      else if (k == LINK_PERF)
+       i = LINK_DWARF;
+      else if (k == NIP_PERF)
+       i = NIP_DWARF;
+      ebl->cached_regs_mapping[j] = i;
+      j++;
+    }
+  for (; j < count; j++)
+    ebl->cached_regs_mapping[j] = -1;
+
+  *regs_mapping = ebl->cached_regs_mapping;
+  *n_regs_mapping = ebl->cached_n_regs_mapping;
+  return true;
+#endif /* __powerpc__ */
+}
+
+__typeof (ppc_sample_sp_pc)
+     ppc64_sample_sp_pc
+     __attribute__ ((alias ("ppc_sample_sp_pc")));
+
+__typeof (ppc_set_initial_registers_sample)
+     ppc64_set_initial_registers_sample
+     __attribute__ ((alias ("ppc_set_initial_registers_sample")));
+
+__typeof (ppc_sample_perf_regs_mapping)
+     ppc64_sample_perf_regs_mapping
+     __attribute__ ((alias ("ppc_sample_perf_regs_mapping")));