After developing the common pieces in prior patches, adding register sample support for another architecture is fairly predictable. Here we add support for 32-bit ARM.
* backends/Makefile.am (arm_SRCS): Add arm_initreg_sample.c. * backends/arm_init.c (arm_init): Add hooks for sample_sp_pc, perf_frame_regs_mask. * backends/arm_initreg_sample.c: New file. Use default arm_set_initial_registers_sample implementation. * backends/libebl_PERF_FLAGS.h (PERF_FRAME_REGISTERS_ARM): New constant describing registers needed for 32-bit arm unwinding. Also define it on 64-bit arm to allow profiling programs running in 32-bit compatibility mode. Signed-off-by: Serhei Makarov <[email protected]> --- backends/Makefile.am | 2 +- backends/aarch64_init.c | 3 +- backends/arm_init.c | 8 +++++- backends/arm_initreg_sample.c | 54 +++++++++++++++++++++++++++++++++++ backends/libebl_PERF_FLAGS.h | 29 ++++++++++++++++++- 5 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 backends/arm_initreg_sample.c diff --git a/backends/Makefile.am b/backends/Makefile.am index bebd990e..318acef8 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -57,7 +57,7 @@ alpha_SRCS = alpha_init.c alpha_symbol.c alpha_retval.c alpha_regs.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 \ - arm_machineflagname.c + arm_initreg_sample.c arm_machineflagname.c aarch64_SRCS = aarch64_init.c aarch64_regs.c aarch64_symbol.c \ aarch64_corenote.c aarch64_retval.c aarch64_cfi.c \ diff --git a/backends/aarch64_init.c b/backends/aarch64_init.c index f6505bd7..4ac37818 100644 --- a/backends/aarch64_init.c +++ b/backends/aarch64_init.c @@ -65,7 +65,8 @@ aarch64_init (Elf *elf __attribute__ ((unused)), 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_AARCH64; + eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_AARCH64 + | PERF_FRAME_REGISTERS_ARM; /* XXX try to include 32-bit compat mode regs */ __libebl_init_cached_regs_mapping (eh); HOOK (eh, unwind); diff --git a/backends/arm_init.c b/backends/arm_init.c index 70b75942..7fcfe3c3 100644 --- a/backends/arm_init.c +++ b/backends/arm_init.c @@ -1,5 +1,5 @@ /* Initialization of Arm specific backend library. - Copyright (C) 2002, 2005, 2009, 2013, 2014, 2015, 2017 Red Hat, Inc. + Copyright (C) 2002, 2005, 2009, 2013, 2014, 2015, 2017, 2026 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <[email protected]>, 2002. @@ -34,6 +34,7 @@ #define BACKEND arm_ #define RELOC_PREFIX R_ARM_ #include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" /* This defines the common reloc hooks based on arm_reloc.def. */ #include "common-reloc.c" @@ -64,6 +65,11 @@ arm_init (Elf *elf __attribute__ ((unused)), /* We only unwind the core integer registers. */ eh->frame_nregs = 16; HOOK (eh, set_initial_registers_tid); + /* set_initial_registers_sample is default ver */ + HOOK (eh, sample_sp_pc); + /* sample_perf_regs_mapping is default ver */ + eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_ARM; + __libebl_init_cached_regs_mapping (eh); /* Bit zero encodes whether an function address is THUMB or ARM. */ eh->func_addr_mask = ~(GElf_Addr)1; diff --git a/backends/arm_initreg_sample.c b/backends/arm_initreg_sample.c new file mode 100644 index 00000000..a6b44a19 --- /dev/null +++ b/backends/arm_initreg_sample.c @@ -0,0 +1,54 @@ +/* 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 arm_ +#include "libebl_CPU.h" +#include "libebl_PERF_FLAGS.h" + +bool +arm_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, 13 /* index of sp in dwarf_regs */, + pc, 15 /* index of pc in dwarf_regs */); +} + +/* XXX The default ebl_set_initial_registers_sample implementation can + be used -- whereas the ptrace code in arm_initreg.c has to unpack a + register file of 32-bit words into a Dwarf_Word array, here we + should already be provided an appropriately-packed array + originating from perf_events. */ diff --git a/backends/libebl_PERF_FLAGS.h b/backends/libebl_PERF_FLAGS.h index aa16f456..07ffc38c 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__) +#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) /* || defined(other_architecture)... */ # include <asm/perf_regs.h> #endif @@ -59,6 +59,20 @@ #define PERF_FRAME_REGISTERS_X86_64 0 #endif /* _ASM_X86_PERF_REGS_H */ +#if defined(_ASM_ARM_PERF_REGS_H) +#define REG(R) (1ULL << PERF_REG_ARM_ ## R) +/* Proper unwind set: callee-saved R4..R10, then R11 for FP, and SP, + LR, PC. Collecting all 16 regs would also be feasible. */ +#define PERF_FRAME_REGISTERS_ARM (REG(R0) | REG(R1) | REG(R2) | REG(R3) \ + | REG(R4) | REG(R5) | REG(R6) | REG(R7) | REG(R8) | REG(R9) | REG(R10) \ + | REG(FP) | REG(IP) | REG(SP) | REG(LR) | REG(PC)) +/* Register ordering defined in linux arch/arm/include/uapi/asm/perf_regs.h. */ +#elif !defined(_ASM_ARM64_PERF_REGS_H) +/* Since asm/perf_regs.h is absent, or gives the register layout for a + different arch, we can't unwind 32-bit ARM perf sample frames. */ +#define PERF_FRAME_REGISTERS_ARM 0 +#endif /* _ASM_ARM_PERF_REGS_H */ + #if defined(_ASM_ARM64_PERF_REGS_H) #define REG(R) (1ULL << PERF_REG_ARM64_ ## R) /* Proper unwind set: callee-saved X19..X28, then X29 for FP, @@ -67,6 +81,19 @@ | REG(X22) | REG(X23) | REG(X24) | REG(X25) | REG(X26) | REG(X27) \ | REG(X28) | REG(X29) /*FP*/ | REG(LR) | REG(SP) | REG(PC)) /* Register ordering defined in linux arch/arm64/include/uapi/asm/perf_regs.h. */ + +/* Likewise, for 32bit-on-64bit compat mode: */ +#define PERF_FRAME_REGISTERS_ARM (REG(X0) | REG(X1) | REG(X2) | REG(X3) \ + | REG(X4) | REG(X5) | REG(X6) | REG(X7) | REG(X8) | REG(X9) | REG(X10) \ + | REG(X11) /* FP */ | REG(X12) /* IP */ /* | skip X13..X29 */ | REG(LR) \ + | REG(SP) | REG(PC)) +/* XXX Then the profiler likely needs to be instructed to request the + intersection of these register sets rather than just + PERF_FRAME_REGISTERS_AARCH64. Thus, in aarch64_init.c, we have: + + eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_AARCH64 + | PERF_FRAME_REGISTERS_ARM; +*/ #else /* Since asm/perf_regs.h is absent, or gives the register layout for a different arch, we can't unwind aarch64 perf sample frames. */ -- 2.53.0
