[PATCH v1 17/23] perf dwarf-regs: Add powerpc perf to dwarf register number mapping functions

Ian Rogers
2026-01-17T05:29:53Z

These functions allow the generic initial register state code in
unwind-libdw to be used. Note, the link register was being coped to
dwarf register 65 that the SysV ABI spec claims is FPSCR. It is
corrected here to 108, but this is unlikely to matter as FPSCR has
little to no impact on unwinding.

Signed-off-by: Ian Rogers <irogers@google.com>
---
.../util/dwarf-regs-arch/dwarf-regs-powerpc.c | 77 ++++++++++++++++++-
tools/perf/util/dwarf-regs.c | 4 +
tools/perf/util/include/dwarf-regs.h | 1 +
tools/perf/util/unwind-libdw-arch/Build | 1 -
.../unwind-libdw-arch/unwind-libdw-powerpc.c | 76 ------------------
tools/perf/util/unwind-libdw.c | 5 +-
tools/perf/util/unwind-libdw.h | 1 -
7 files changed, 82 insertions(+), 83 deletions(-)
delete mode 100644 tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c

diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c b/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
index caf77a234c78..51892a09725b 100644
--- a/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
+++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-powerpc.c
@@ -4,8 +4,9 @@
*
* Copyright (C) 2010 Ian Munsie, IBM Corporation.
*/
-
+#include <errno.h>
#include <dwarf-regs.h>
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"

#define PPC_OP(op) (((op) >> 26) & 0x3F)
#define PPC_RA(a) (((a) >> 16) & 0x1f)
@@ -59,3 +60,77 @@ void get_powerpc_regs(u32 raw_insn, int is_source,
if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31))
op_loc->offset = get_offset_opcode(raw_insn);
}
+
+int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum)
+{
+ static const int dwarf_powerpc_regnums[] = {
+ [PERF_REG_POWERPC_R0] = 0,
+ [PERF_REG_POWERPC_R1] = 1,
+ [PERF_REG_POWERPC_R2] = 2,
+ [PERF_REG_POWERPC_R3] = 3,
+ [PERF_REG_POWERPC_R4] = 4,
+ [PERF_REG_POWERPC_R5] = 5,
+ [PERF_REG_POWERPC_R6] = 6,
+ [PERF_REG_POWERPC_R7] = 7,
+ [PERF_REG_POWERPC_R8] = 8,
+ [PERF_REG_POWERPC_R9] = 9,
+ [PERF_REG_POWERPC_R10] = 10,
+ [PERF_REG_POWERPC_R11] = 11,
+ [PERF_REG_POWERPC_R12] = 12,
+ [PERF_REG_POWERPC_R13] = 13,
+ [PERF_REG_POWERPC_R14] = 14,
+ [PERF_REG_POWERPC_R15] = 15,
+ [PERF_REG_POWERPC_R16] = 16,
+ [PERF_REG_POWERPC_R17] = 17,
+ [PERF_REG_POWERPC_R18] = 18,
+ [PERF_REG_POWERPC_R19] = 19,
+ [PERF_REG_POWERPC_R20] = 20,
+ [PERF_REG_POWERPC_R21] = 21,
+ [PERF_REG_POWERPC_R22] = 22,
+ [PERF_REG_POWERPC_R23] = 23,
+ [PERF_REG_POWERPC_R24] = 24,
+ [PERF_REG_POWERPC_R25] = 25,
+ [PERF_REG_POWERPC_R26] = 26,
+ [PERF_REG_POWERPC_R27] = 27,
+ [PERF_REG_POWERPC_R28] = 28,
+ [PERF_REG_POWERPC_R29] = 29,
+ [PERF_REG_POWERPC_R30] = 30,
+ [PERF_REG_POWERPC_R31] = 31,
+ /* TODO: PERF_REG_POWERPC_NIP */
+ [PERF_REG_POWERPC_MSR] = 66,
+ /* TODO: PERF_REG_POWERPC_ORIG_R3 */
+ [PERF_REG_POWERPC_CTR] = 109,
+ [PERF_REG_POWERPC_LINK] = 108, /* Note, previously in perf encoded as 65? */
+ [PERF_REG_POWERPC_XER] = 101,
+ /* TODO: PERF_REG_POWERPC_CCR */
+ /* TODO: PERF_REG_POWERPC_SOFTE */
+ /* TODO: PERF_REG_POWERPC_TRAP */
+ /* TODO: PERF_REG_POWERPC_DAR */
+ /* TODO: PERF_REG_POWERPC_DSISR */
+ /* TODO: PERF_REG_POWERPC_SIER */
+ /* TODO: PERF_REG_POWERPC_MMCRA */
+ /* TODO: PERF_REG_POWERPC_MMCR0 */
+ /* TODO: PERF_REG_POWERPC_MMCR1 */
+ /* TODO: PERF_REG_POWERPC_MMCR2 */
+ /* TODO: PERF_REG_POWERPC_MMCR3 */
+ /* TODO: PERF_REG_POWERPC_SIER2 */
+ /* TODO: PERF_REG_POWERPC_SIER3 */
+ /* TODO: PERF_REG_POWERPC_PMC1 */
+ /* TODO: PERF_REG_POWERPC_PMC2 */
+ /* TODO: PERF_REG_POWERPC_PMC3 */
+ /* TODO: PERF_REG_POWERPC_PMC4 */
+ /* TODO: PERF_REG_POWERPC_PMC5 */
+ /* TODO: PERF_REG_POWERPC_PMC6 */
+ /* TODO: PERF_REG_POWERPC_SDAR */
+ /* TODO: PERF_REG_POWERPC_SIAR */
+ };
+
+ if (perf_regnum == 0)
+ return 0;
+
+ if (perf_regnum < 0 || perf_regnum > (int)ARRAY_SIZE(dwarf_powerpc_regnums) ||
+ dwarf_powerpc_regnums[perf_regnum] == 0)
+ return -ENOENT;
+
+ return dwarf_powerpc_regnums[perf_regnum];
+}
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 033218f14b36..3b1c2a436806 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -205,6 +205,10 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
case EM_CSKY:
reg = __get_dwarf_regnum_for_perf_regnum_csky(perf_regnum, flags);
break;
+ case EM_PPC:
+ case EM_PPC64:
+ reg = __get_dwarf_regnum_for_perf_regnum_powerpc(perf_regnum);
+ break;
case EM_LOONGARCH:
reg = __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum);
break;
diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index bec15fb53e73..9ebb3ba33fba 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -110,6 +110,7 @@ int __get_dwarf_regnum_for_perf_regnum_arm64(int perf_regnum);

int __get_dwarf_regnum_for_perf_regnum_csky(int perf_regnum, unsigned int flags);
int __get_dwarf_regnum_for_perf_regnum_loongarch(int perf_regnum);
+int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum);

/*
* get_dwarf_regnum - Returns DWARF regnum from register name
diff --git a/tools/perf/util/unwind-libdw-arch/Build b/tools/perf/util/unwind-libdw-arch/Build
index 62a4cbf2dca8..e6c97e842cd6 100644
--- a/tools/perf/util/unwind-libdw-arch/Build
+++ b/tools/perf/util/unwind-libdw-arch/Build
@@ -1,3 +1,2 @@
-perf-util-y += unwind-libdw-powerpc.o
perf-util-y += unwind-libdw-riscv.o
perf-util-y += unwind-libdw-s390.o
diff --git a/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c b/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
deleted file mode 100644
index 1560db45e7b4..000000000000
--- a/tools/perf/util/unwind-libdw-arch/unwind-libdw-powerpc.c
+++ /dev/null
@@ -1,76 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <elfutils/libdwfl.h>
-#include <linux/kernel.h>
-#include "../arch/powerpc/include/uapi/asm/perf_regs.h"
-#include "util/unwind-libdw.h"
-#include "util/perf_regs.h"
-#include "util/sample.h"
-
-/* See backends/ppc_initreg.c and backends/ppc_regs.c in elfutils. */
-static const int special_regs[3][2] = {
- { 65, PERF_REG_POWERPC_LINK },
- { 101, PERF_REG_POWERPC_XER },
- { 109, PERF_REG_POWERPC_CTR },
-};
-
-bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg)
-{
- struct unwind_info *ui = arg;
- struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
- Dwarf_Word dwarf_regs[32], dwarf_nip;
- size_t i;
-
-#define REG(r) ({ \
- Dwarf_Word val = 0; \
- perf_reg_value(&val, user_regs, PERF_REG_POWERPC_##r); \
- val; \
-})
-
- dwarf_regs[0] = REG(R0);
- dwarf_regs[1] = REG(R1);
- dwarf_regs[2] = REG(R2);
- dwarf_regs[3] = REG(R3);
- dwarf_regs[4] = REG(R4);
- dwarf_regs[5] = REG(R5);
- dwarf_regs[6] = REG(R6);
- dwarf_regs[7] = REG(R7);
- dwarf_regs[8] = REG(R8);
- dwarf_regs[9] = REG(R9);
- dwarf_regs[10] = REG(R10);
- dwarf_regs[11] = REG(R11);
- dwarf_regs[12] = REG(R12);
- dwarf_regs[13] = REG(R13);
- dwarf_regs[14] = REG(R14);
- dwarf_regs[15] = REG(R15);
- dwarf_regs[16] = REG(R16);
- dwarf_regs[17] = REG(R17);
- dwarf_regs[18] = REG(R18);
- dwarf_regs[19] = REG(R19);
- dwarf_regs[20] = REG(R20);
- dwarf_regs[21] = REG(R21);
- dwarf_regs[22] = REG(R22);
- dwarf_regs[23] = REG(R23);
- dwarf_regs[24] = REG(R24);
- dwarf_regs[25] = REG(R25);
- dwarf_regs[26] = REG(R26);
- dwarf_regs[27] = REG(R27);
- dwarf_regs[28] = REG(R28);
- dwarf_regs[29] = REG(R29);
- dwarf_regs[30] = REG(R30);
- dwarf_regs[31] = REG(R31);
- if (!dwfl_thread_state_registers(thread, 0, 32, dwarf_regs))
- return false;
-
- dwarf_nip = REG(NIP);
- dwfl_thread_state_register_pc(thread, dwarf_nip);
- for (i = 0; i < ARRAY_SIZE(special_regs); i++) {
- Dwarf_Word val = 0;
- perf_reg_value(&val, user_regs, special_regs[i][1]);
- if (!dwfl_thread_state_registers(thread,
- special_regs[i][0], 1,
- &val))
- return false;
- }
-
- return true;
-}
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c
index 9c8dad643cd0..e9ba050e7ab1 100644
--- a/tools/perf/util/unwind-libdw.c
+++ b/tools/perf/util/unwind-libdw.c
@@ -292,15 +292,12 @@ static const Dwfl_Thread_Callbacks callbacks_generic = {
.set_initial_registers = libdw_set_initial_registers_generic,
};

-DEFINE_DWFL_THREAD_CALLBACKS(powerpc);
DEFINE_DWFL_THREAD_CALLBACKS(riscv);
DEFINE_DWFL_THREAD_CALLBACKS(s390);

static const Dwfl_Thread_Callbacks *get_thread_callbacks(const char *arch)
{
- if (!strcmp(arch, "powerpc"))
- return &callbacks_powerpc;
- else if (!strcmp(arch, "riscv"))
+ if (!strcmp(arch, "riscv"))
return &callbacks_riscv;
else if (!strcmp(arch, "s390"))
return &callbacks_s390;
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h
index 9d177d70f15c..0ec1abdabbe7 100644
--- a/tools/perf/util/unwind-libdw.h
+++ b/tools/perf/util/unwind-libdw.h
@@ -10,7 +10,6 @@ struct perf_sample;
struct thread;

bool libdw_set_initial_registers_mips(Dwfl_Thread *thread, void *arg);
-bool libdw_set_initial_registers_powerpc(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_riscv(Dwfl_Thread *thread, void *arg);
bool libdw_set_initial_registers_s390(Dwfl_Thread *thread, void *arg);

--
2.52.0.457.g6b5491de43-goog