From: Serhei Makarov Date: Thu, 12 Mar 2026 19:29:04 +0000 (-0400) Subject: backends/: sketch powerpc sample_regs support X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=62ae2b8d26b82dcbffd8933c1838e026f5fe57fa;p=thirdparty%2Felfutils.git backends/: sketch powerpc sample_regs support {speculative, but it should look roughly like this} --- diff --git a/backends/Makefile.am b/backends/Makefile.am index 318acef8..7a8f8169 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -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 diff --git a/backends/libebl_PERF_FLAGS.h b/backends/libebl_PERF_FLAGS.h index 3dcb9938..4e8b000c 100644 --- a/backends/libebl_PERF_FLAGS.h +++ b/backends/libebl_PERF_FLAGS.h @@ -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 #endif @@ -99,6 +99,25 @@ #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, diff --git a/backends/ppc64_init.c b/backends/ppc64_init.c index ffc9842c..edc2a33c 100644 --- a/backends/ppc64_init.c +++ b/backends/ppc64_init.c @@ -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 , 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); diff --git a/backends/ppc_init.c b/backends/ppc_init.c index 08468f8f..45541d30 100644 --- a/backends/ppc_init.c +++ b/backends/ppc_init.c @@ -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 , 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 index 00000000..7359f974 --- /dev/null +++ b/backends/ppc_initreg_sample.c @@ -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 . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#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")));