#if defined(__linux__)
/* XXX Need to exclude __linux__ arches without perf_regs.h. */
-#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__powerpc__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__) || defined(__powerpc__) || defined(__s390__)
/* || defined(other_architecture)... */
# include <asm/perf_regs.h>
#endif
#define PERF_FRAME_REGISTERS_POWERPC 0
#endif /* _UAPI_ASM_POWERPC_PERF_REGS_H */
+#if defined(_ASM_S390_PERF_REGS_H)
+#define REG(R) (1ULL << PERF_REG_S390_ ## R)
+/* TODO(REVIEW) Proper unwind set seems to be: callee-saved R6..R13,
+ then R14/LR, R15/SP, and PSWA/PC. Collecting all 32 regs is feasible. */
+#define PERF_FRAME_REGISTERS_S390 (REG(R1) | REG(R2) | REG(R3) | REG(R4) \
+ | REG(R5) | REG(R6) | REG(R7) | REG(R8) | REG(R9) | REG(R10) \
+ | REG(R11) /* FP */ | REG(R12) | REG(R13) | REG(R14) /* LR */ \
+ | REG(R15) /* SP */ | REG(PC) /* PSWA */)
+/* TODO (REVIEW): Is also including REG(MASK) at all helpful? */
+/* Register ordering defined in linux arch/s390/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 s390 perf sample frames. */
+#define PERF_FRAME_REGISTERS_S390 0
+#endif
+
/* 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,
/* Initialization of S/390 specific backend library.
- Copyright (C) 2005, 2006, 2013 Red Hat, Inc.
+ Copyright (C) 2005, 2006, 2013, 2026 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
#define BACKEND s390_
#define RELOC_PREFIX R_390_
#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
/* This defines the common reloc hooks based on arm_reloc.def. */
#include "common-reloc.c"
unwinding. */
eh->frame_nregs = 32;
HOOK (eh, set_initial_registers_tid);
+ HOOK (eh, set_initial_registers_sample);
+ HOOK (eh, sample_sp_pc);
+ /* TODO(REVIEW) sample_perf_regs_mapping is default ver */
+ eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_S390;
if (eh->class == ELFCLASS32)
HOOK (eh, normalize_pc);
HOOK (eh, unwind);
--- /dev/null
+/* 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 s390_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+
+bool
+s390_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, 31 /* index of sp in dwarf_regs */,
+ pc, 32 /* index of pc in dwarf_regs */);
+}
+
+bool
+s390_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(__s390__)
+ (void)regs; (void)n_regs;
+ (void)regs_mapping; (void)n_regs_mapping;
+ (void)setfunc; (void)arg;
+ return false;
+#else
+#define N_GREGS 16
+ Dwarf_Word dwarf_regs[N_GREGS];
+ Dwarf_Word psw = 0x0;
+ bool scratch_present = false;
+ size_t i;
+ for (i = 0; i < N_GREGS; i++)
+ dwarf_regs[i] = 0x0;
+ for (i = 0; i < n_regs; i++)
+ {
+ if (i >= n_regs_mapping)
+ break;
+ if (regs_mapping[i] == 16)
+ psw = regs[i]; /* TODO(REVIEW): NEED to correctly extract psw.addr? */
+ if (regs_mapping[i] < 0 || regs_mapping[i] >= N_GREGS)
+ continue;
+ if (regs_mapping[i] < 6)
+ scratch_present = true;
+ dwarf_regs[regs_mapping[i]] = regs[i];
+ }
+
+ /* R0..R5 only if present. */
+ if (scratch_present && ! setfunc (0, 6, &dwarf_regs[0], arg))
+ return false;
+
+ /* R6..R13, R14(LR), R15(SP). */
+ if (! setfunc (6, 15 - 5, &dwarf_regs[6], arg))
+ return false;
+
+ /* TODO(REVIEW): Do we also need the floating-point regs? */
+
+ return setfunc (-1, 1, &psw, arg);
+#endif /* __s390__ */
+}