Branch data Line data Source code
1 : : /* Linux perf_events sample_regs_user flags required for unwinding.
2 : : Internal only; elfutils library users should use ebl_perf_frame_regs_mask().
3 : :
4 : : Copyright (C) 2025-2026 Red Hat, Inc.
5 : : This file is part of elfutils.
6 : :
7 : : This file is free software; you can redistribute it and/or modify
8 : : it under the terms of either
9 : :
10 : : * the GNU Lesser General Public License as published by the Free
11 : : Software Foundation; either version 3 of the License, or (at
12 : : your option) any later version
13 : :
14 : : or
15 : :
16 : : * the GNU General Public License as published by the Free
17 : : Software Foundation; either version 2 of the License, or (at
18 : : your option) any later version
19 : :
20 : : or both in parallel, as here.
21 : :
22 : : elfutils is distributed in the hope that it will be useful, but
23 : : WITHOUT ANY WARRANTY; without even the implied warranty of
24 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 : : General Public License for more details.
26 : :
27 : : You should have received copies of the GNU General Public License and
28 : : the GNU Lesser General Public License along with this program. If
29 : : not, see <http://www.gnu.org/licenses/>. */
30 : :
31 : : #ifndef _LIBEBL_PERF_FLAGS_H
32 : : #define _LIBEBL_PERF_FLAGS_H 1
33 : :
34 : : #if defined(__linux__)
35 : : /* XXX Need to exclude __linux__ arches without perf_regs.h. */
36 : : #if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__) || defined(__arm__)
37 : : /* || defined(other_architecture)... */
38 : : # include <asm/perf_regs.h>
39 : : #endif
40 : : /* Alpha's asm/perf_regs.h is only in newer kernel headers. */
41 : : #if defined(__alpha__) && defined(HAVE_ASM_PERF_REGS_H)
42 : : # include <asm/perf_regs.h>
43 : : #endif
44 : : #endif
45 : :
46 : : #if defined(_ASM_X86_PERF_REGS_H)
47 : : /* See the code in x86_initreg_sample.c for list of required regs and
48 : : linux arch/.../include/asm/ptrace.h for matching pt_regs struct. */
49 : : #define REG(R) (1ULL << PERF_REG_X86_ ## R)
50 : : /* FLAGS and segment regs are excluded from the following masks,
51 : : since they're not needed for unwinding. */
52 : : #define PERF_FRAME_REGISTERS_I386 (REG(AX) | REG(BX) | REG(CX) | REG(DX) \
53 : : | REG(SI) | REG(DI) | REG(BP) | REG(SP) | REG(IP))
54 : : #define PERF_FRAME_REGISTERS_X86_64 (PERF_FRAME_REGISTERS_I386 | REG(R8) \
55 : : | REG(R9) | REG(R10) | REG(R11) | REG(R12) | REG(R13) | REG(R14) | REG(R15))
56 : : /* Register ordering defined in linux arch/x86/include/uapi/asm/perf_regs.h;
57 : : see the code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs()
58 : : and note how regs are added in the same order as the perf_regs.h enum. */
59 : : #else
60 : : /* Since asm/perf_regs.h is absent, or gives the register layout for a
61 : : different arch, we can't unwind i386 and x86_64 perf sample frames. */
62 : : #define PERF_FRAME_REGISTERS_I386 0
63 : : #define PERF_FRAME_REGISTERS_X86_64 0
64 : : #endif /* _ASM_X86_PERF_REGS_H */
65 : :
66 : : #if defined(_ASM_ARM_PERF_REGS_H)
67 : : #define REG(R) (1ULL << PERF_REG_ARM_ ## R)
68 : : /* Proper unwind set: callee-saved R4..R10, then R11 for FP, and SP,
69 : : LR, PC. Collecting all 16 regs would also be feasible. */
70 : : #define PERF_FRAME_REGISTERS_ARM (REG(R0) | REG(R1) | REG(R2) | REG(R3) \
71 : : | REG(R4) | REG(R5) | REG(R6) | REG(R7) | REG(R8) | REG(R9) | REG(R10) \
72 : : | REG(FP) | REG(IP) | REG(SP) | REG(LR) | REG(PC))
73 : : /* Register ordering defined in linux arch/arm/include/uapi/asm/perf_regs.h. */
74 : : #elif !defined(_ASM_ARM64_PERF_REGS_H)
75 : : /* Since asm/perf_regs.h is absent, or gives the register layout for a
76 : : different arch, we can't unwind 32-bit ARM perf sample frames. */
77 : : #define PERF_FRAME_REGISTERS_ARM 0
78 : : #endif /* _ASM_ARM_PERF_REGS_H */
79 : :
80 : : #if defined(_ASM_ARM64_PERF_REGS_H)
81 : : #define REG(R) (1ULL << PERF_REG_ARM64_ ## R)
82 : : /* Proper unwind set: callee-saved X19..X28, then X29 for FP,
83 : : LR for return addr, and SP, PC. */
84 : : #define PERF_FRAME_REGISTERS_AARCH64 (REG(X19) | REG(X20) | REG(X21) \
85 : : | REG(X22) | REG(X23) | REG(X24) | REG(X25) | REG(X26) | REG(X27) \
86 : : | REG(X28) | REG(X29) /*FP*/ | REG(LR) | REG(SP) | REG(PC))
87 : : /* Register ordering defined in linux arch/arm64/include/uapi/asm/perf_regs.h. */
88 : :
89 : : /* Likewise, for 32bit-on-64bit compat mode: */
90 : : #define PERF_FRAME_REGISTERS_ARM (REG(X0) | REG(X1) | REG(X2) | REG(X3) \
91 : : | REG(X4) | REG(X5) | REG(X6) | REG(X7) | REG(X8) | REG(X9) | REG(X10) \
92 : : | REG(X11) /* FP */ | REG(X12) /* IP */ /* | skip X13..X29 */ | REG(LR) \
93 : : | REG(SP) | REG(PC))
94 : : /* XXX Then the profiler likely needs to be instructed to request the
95 : : intersection of these register sets rather than just
96 : : PERF_FRAME_REGISTERS_AARCH64. Thus, in aarch64_init.c, we have:
97 : :
98 : : eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_AARCH64
99 : : | PERF_FRAME_REGISTERS_ARM;
100 : : */
101 : : #else
102 : : /* Since asm/perf_regs.h is absent, or gives the register layout for a
103 : : different arch, we can't unwind aarch64 perf sample frames. */
104 : : #define PERF_FRAME_REGISTERS_AARCH64 0
105 : : #endif /* _ASM_ARM64_PERF_REGS_H */
106 : :
107 : : #if defined(_ASM_ALPHA_PERF_REGS_H)
108 : : /* Everything the kernel can sample. The callee-saved $9-$15 are not
109 : : in pt_regs. */
110 : : #define PERF_FRAME_REGISTERS_ALPHA (((1ULL << PERF_REG_ALPHA_MAX) - 1) \
111 : : & ~(0x7fULL << PERF_REG_ALPHA_R9))
112 : : /* Register ordering defined in linux arch/alpha/include/uapi/asm/perf_regs.h. */
113 : : #else
114 : : /* Since asm/perf_regs.h is absent, or gives the register layout for a
115 : : different arch, we can't unwind alpha perf sample frames. */
116 : : #define PERF_FRAME_REGISTERS_ALPHA 0
117 : : #endif /* _ASM_ALPHA_PERF_REGS_H */
118 : :
119 : : static inline bool
120 : 11826 : generic_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs,
121 : : const int *regs_mapping, size_t n_regs_mapping,
122 : : Dwarf_Word *sp, uint sp_index /* into dwarf_regs */,
123 : : Dwarf_Word *pc, uint pc_index /* into dwarf_regs */)
124 : : {
125 [ + - ]: 11826 : if (sp != NULL) *sp = 0;
126 [ + - ]: 11826 : if (pc != NULL) *pc = 0;
127 : : /* TODO: Register locations could be cached and rechecked on a
128 : : fastpath without needing to loop, though the overhead reduction
129 : : is minimal. */
130 : 11826 : int need_sp = (sp != NULL), need_pc = (pc != NULL);
131 [ + + + - ]: 118260 : for (size_t j = 0; (need_sp || need_pc) && n_regs_mapping > j; j++)
132 : : {
133 [ + - ]: 106434 : if (n_regs <= (uint32_t)j) break;
134 [ + + + + ]: 106434 : if (need_sp && regs_mapping[j] == (int)sp_index)
135 : : {
136 : 11826 : *sp = regs[j]; need_sp = false;
137 : : }
138 [ + - + + ]: 106434 : if (need_pc && regs_mapping[j] == (int)pc_index)
139 : : {
140 : 11826 : *pc = regs[j]; need_pc = false;
141 : : }
142 : : }
143 : 11826 : return (!need_sp && !need_pc);
144 : : }
145 : :
146 : : #endif /* libebl_PERF_FLAGS.h */
|