Branch data Line data Source code
1 : : /* Get Dwarf Frame state for perf stack sample data.
2 : : Copyright (C) 2025 Red Hat, Inc.
3 : : This file is part of elfutils.
4 : :
5 : : This file is free software; you can redistribute it and/or modify
6 : : it under the terms of either
7 : :
8 : : * the GNU Lesser General Public License as published by the Free
9 : : Software Foundation; either version 3 of the License, or (at
10 : : your option) any later version
11 : :
12 : : or
13 : :
14 : : * the GNU General Public License as published by the Free
15 : : Software Foundation; either version 2 of the License, or (at
16 : : your option) any later version
17 : :
18 : : or both in parallel, as here.
19 : :
20 : : elfutils is distributed in the hope that it will be useful, but
21 : : WITHOUT ANY WARRANTY; without even the implied warranty of
22 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 : : General Public License for more details.
24 : :
25 : : You should have received copies of the GNU General Public License and
26 : : the GNU Lesser General Public License along with this program. If
27 : : not, see <http://www.gnu.org/licenses/>. */
28 : :
29 : : #ifdef HAVE_CONFIG_H
30 : : # include <config.h>
31 : : #endif
32 : :
33 : : #include "libdwfl_stacktraceP.h"
34 : :
35 : : /* Various functions providing arch-specific info: */
36 : :
37 : : Ebl *default_ebl = NULL;
38 : : GElf_Half default_ebl_machine = EM_NONE;
39 : :
40 : : uint64_t
41 : 0 : dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
42 : : {
43 : : /* XXX The most likely case is that this will only be called once,
44 : : for the current architecture. So we keep one Ebl* around for
45 : : answering this query and replace it in the unlikely case of
46 : : getting called with different architectures. */
47 [ # # # # ]: 0 : if (default_ebl != NULL && default_ebl_machine != machine)
48 : : {
49 : 0 : ebl_closebackend(default_ebl);
50 : 0 : default_ebl = NULL;
51 : : }
52 [ # # ]: 0 : if (default_ebl == NULL)
53 : : {
54 : 0 : default_ebl = ebl_openbackend_machine(machine);
55 : 0 : default_ebl_machine = machine;
56 : : }
57 [ # # ]: 0 : if (default_ebl != NULL)
58 : 0 : return ebl_perf_frame_regs_mask (default_ebl);
59 : : return 0;
60 : : }
61 : :
62 : : GElf_Half
63 : 8 : dwflst_arch_from_uname (const char *umachine)
64 : : {
65 [ - + ]: 8 : if (strncmp(umachine, "x86_64", 6) == 0)
66 : : return EM_X86_64;
67 [ # # ]: 0 : else if (strncmp(umachine, "i686", 4) == 0
68 [ # # ]: 0 : || strncmp(umachine, "i386", 4) == 0)
69 : : return EM_386;
70 [ # # ]: 0 : else if (strncmp(umachine, "aarch64", 7) == 0)
71 : : return EM_AARCH64;
72 [ # # ]: 0 : else if (strncmp(umachine, "armv7l", 6) == 0)
73 : 0 : return EM_ARM;
74 : : /* XXX Other architectures not yet supported. */
75 : : return EM_NONE;
76 : : }
77 : :
78 : : /* XXX The per-machine switches suggest the following implementations
79 : : could be folded into backends/, but we would need to create a Ebl
80 : : to handle the dispatch: */
81 : :
82 : : uint32_t
83 : 0 : dwflst_arch_expected_frame_nregs (GElf_Half machine)
84 : : {
85 : : /* For aarch64, we actually use fewer than ebl->frame_nregs to unwind: */
86 [ # # ]: 0 : if (machine == EM_AARCH64)
87 : : return 14;
88 [ # # ]: 0 : if (machine == EM_ARM)
89 : : return 16;
90 : : /* On x86, expect everything except FLAGS: */
91 [ # # ]: 0 : if (machine == EM_X86_64 || machine == EM_386)
92 : : /* XXX An external user of the library can't access the Ebl, hence
93 : : can't conveniently provide it to us it here. We provide the
94 : : constant directly rather than initializing a new Ebl. */
95 [ # # ]: 0 : return machine == EM_X86_64 ? 17 : 9;
96 : : /* return ebl_frame_nregs(ebl); */
97 : : /* XXX Other architectures are not supported yet.
98 : : In general, it's fine to be on the permissive side here
99 : : for a minimum expected number of registers. */
100 : : return 0;
101 : : }
102 : :
103 : : int
104 : 0 : dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool force_abi32)
105 : : {
106 [ # # # # : 0 : switch (machine) {
# ]
107 : 0 : case EM_X86_64:
108 [ # # ]: 0 : return force_abi32 ? 4 : 7;
109 : : case EM_386:
110 : : return 4;
111 : : case EM_ARM:
112 : : return 13;
113 : 0 : case EM_AARCH64:
114 [ # # ]: 0 : return force_abi32 ? 13 : 31;
115 : : default:
116 : : /* XXX Other architectures are not supported yet. */
117 : : return -1;
118 : : }
119 : : }
120 : :
121 : : int
122 : 0 : dwflst_arch_sp_perf_reg (GElf_Half machine,
123 : : uint64_t perf_regs_mask, bool is_abi32)
124 : : {
125 : 0 : int sp_perf_index;
126 [ # # ]: 0 : if (machine == EM_X86_64 || machine == EM_386)
127 : : sp_perf_index = 7; /* uniform on 32/64-bit abi */
128 : : /* compare backends/x86_initreg_sample.c;
129 : : for dwarf_index, would be (is_abi32 ? 4 : 7) */
130 [ # # ]: 0 : else if (machine == EM_ARM || machine == EM_AARCH64)
131 [ # # ]: 0 : sp_perf_index = (is_abi32 ? 13 : 31);
132 : : /* basic linear mapping of perf_regs<->dwarf_regs */
133 : : else
134 : : /* XXX Other architectures are not supported yet. */
135 : : return -1;
136 : :
137 [ # # ]: 0 : if (perf_regs_mask == 0)
138 : : /* Assume all registers present: */
139 : : return sp_perf_index;
140 : :
141 : : /* Iterate perf_regs_mask to find
142 : : k == index of sp in perf_regs_mask
143 : : j == index of sp in regs[] */
144 : : int j, k; uint64_t bit;
145 : : for (k = 0, j = -1, bit = 1;
146 [ # # ]: 0 : k <= sp_perf_index; k++, bit <<= 1)
147 : : {
148 [ # # ]: 0 : if ((bit & perf_regs_mask))
149 : 0 : j++;
150 : : else
151 : 0 : continue; /* sp may not be present */
152 [ # # ]: 0 : if (k == sp_perf_index)
153 : : return j;
154 : : }
155 : : return -2;
156 : : }
157 : :
158 : : /* Stack sample handling: */
159 : :
160 : : struct sample_info {
161 : : pid_t pid;
162 : : pid_t tid;
163 : : Dwarf_Addr base_addr;
164 : : const uint8_t *stack;
165 : : size_t stack_size;
166 : : const Dwarf_Word *regs;
167 : : uint n_regs;
168 : : const int *regs_mapping;
169 : : size_t n_regs_mapping;
170 : : int elfclass;
171 : : Dwarf_Addr pc;
172 : : };
173 : :
174 : : /* The next few functions imitate the corefile interface for a single
175 : : stack sample, with very restricted access to registers and memory. */
176 : :
177 : : /* Just yield the single thread id matching the sample. */
178 : : static pid_t
179 : 0 : sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
180 : : void **thread_argp)
181 : : {
182 : 0 : struct sample_info *sample_arg =
183 : : (struct sample_info *)dwfl_arg;
184 [ # # ]: 0 : if (*thread_argp == NULL)
185 : : {
186 : 0 : *thread_argp = (void *)0xea7b3375;
187 : 0 : return sample_arg->tid;
188 : : }
189 : : else
190 : : return 0;
191 : : }
192 : :
193 : : /* Just check that the thread id matches the sample. */
194 : : static bool
195 : 0 : sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
196 : : void *dwfl_arg, void **thread_argp)
197 : : {
198 : 0 : struct sample_info *sample_arg =
199 : : (struct sample_info *)dwfl_arg;
200 : 0 : *thread_argp = (void *)sample_arg;
201 [ # # ]: 0 : if (sample_arg->tid != tid)
202 : : {
203 : 0 : __libdwfl_seterrno(DWFL_E_INVALID_ARGUMENT);
204 : 0 : return false;
205 : : }
206 : : return true;
207 : : }
208 : :
209 : : #define copy_word_64(result, d) \
210 : : if ((((uintptr_t) (d)) & (sizeof (uint64_t) - 1)) == 0) \
211 : : *(result) = *(uint64_t *)(d); \
212 : : else \
213 : : memcpy ((result), (d), sizeof (uint64_t));
214 : :
215 : : #define copy_word_32(result, d) \
216 : : if ((((uintptr_t) (d)) & (sizeof (uint32_t) - 1)) == 0) \
217 : : *(result) = *(uint32_t *)(d); \
218 : : else \
219 : : memcpy ((result), (d), sizeof (uint32_t));
220 : :
221 : : #define copy_word(result, d, elfclass) \
222 : : if ((elfclass) == ELFCLASS64) \
223 : : { copy_word_64((result), (d)); } \
224 : : else if ((elfclass) == ELFCLASS32) \
225 : : { copy_word_32((result), (d)); } \
226 : : else \
227 : : *(result) = 0;
228 : :
229 : : static bool
230 : 0 : elf_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
231 : : {
232 : 0 : struct sample_info *sample_arg =
233 : : (struct sample_info *)arg;
234 : 0 : Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, addr);
235 : 0 : Dwarf_Addr bias;
236 : 0 : Elf_Scn *section = INTUSE(dwfl_module_address_section) (mod, &addr, &bias);
237 : :
238 [ # # ]: 0 : if (!section)
239 : : {
240 : 0 : __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE);
241 : 0 : return false;
242 : : }
243 : :
244 : 0 : Elf_Data *data = elf_getdata(section, NULL);
245 [ # # # # : 0 : if (data && data->d_buf && data->d_size > addr) {
# # ]
246 : 0 : uint8_t *d = ((uint8_t *)data->d_buf) + addr;
247 [ # # # # : 0 : copy_word(result, d, sample_arg->elfclass);
# # # # ]
248 : 0 : return true;
249 : : }
250 : 0 : __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE);
251 : 0 : return false;
252 : : }
253 : :
254 : : static bool
255 : 0 : sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
256 : : {
257 : 0 : struct sample_info *sample_arg =
258 : : (struct sample_info *)arg;
259 : : /* Imitate read_cached_memory() with the stack sample data as the cache. */
260 [ # # ]: 0 : if (addr < sample_arg->base_addr ||
261 [ # # ]: 0 : addr - sample_arg->base_addr >= sample_arg->stack_size)
262 : 0 : return elf_memory_read(dwfl, addr, result, arg);
263 : 0 : const uint8_t *d = &sample_arg->stack[addr - sample_arg->base_addr];
264 [ # # # # : 0 : copy_word(result, d, sample_arg->elfclass);
# # # # ]
265 : : return true;
266 : : }
267 : :
268 : :
269 : : static bool
270 : 0 : sample_set_initial_registers (Dwfl_Thread *thread, void *arg)
271 : : {
272 : 0 : struct sample_info *sample_arg =
273 : : (struct sample_info *)arg;
274 : 0 : INTUSE(dwfl_thread_state_register_pc) (thread, sample_arg->pc);
275 : 0 : Dwfl_Process *process = thread->process;
276 : 0 : Ebl *ebl = process->ebl;
277 : 0 : return ebl_set_initial_registers_sample
278 : : (ebl, sample_arg->regs, sample_arg->n_regs,
279 : : sample_arg->regs_mapping, sample_arg->n_regs_mapping,
280 : : __libdwfl_set_initial_registers_thread, thread);
281 : : }
282 : :
283 : : static void
284 : 0 : sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
285 : : {
286 : 0 : struct sample_info *sample_arg =
287 : : (struct sample_info *)dwfl_arg;
288 : 0 : free (sample_arg);
289 : 0 : }
290 : :
291 : : static const Dwfl_Thread_Callbacks sample_thread_callbacks =
292 : : {
293 : : sample_next_thread,
294 : : sample_getthread,
295 : : sample_memory_read,
296 : : sample_set_initial_registers,
297 : : sample_detach,
298 : : NULL, /* sample_thread_detach */
299 : : };
300 : :
301 : : int
302 : 0 : dwflst_sample_getframes (Dwfl *dwfl, Elf *elf,
303 : : pid_t pid, pid_t tid,
304 : : const void *stack, size_t stack_size,
305 : : const Dwarf_Word *regs, uint n_regs,
306 : : const int *regs_mapping, size_t n_regs_mapping,
307 : : int (*callback) (Dwfl_Frame *state, void *arg),
308 : : void *arg)
309 : : {
310 : : /* TODO: Lock the dwfl to ensure attach_state does not interfere
311 : : with other dwfl_perf_sample_getframes calls. */
312 : :
313 : 0 : struct sample_info *sample_arg;
314 : 0 : bool attached = false;
315 [ # # ]: 0 : if (dwfl->process != NULL)
316 : : {
317 : 0 : sample_arg = dwfl->process->callbacks_arg;
318 : 0 : attached = true;
319 : : }
320 : : else
321 : : {
322 : 0 : sample_arg = malloc (sizeof *sample_arg);
323 [ # # ]: 0 : if (sample_arg == NULL)
324 : : {
325 : 0 : __libdwfl_seterrno(DWFL_E_NOMEM);
326 : 0 : return -1;
327 : : }
328 : : }
329 : :
330 : 0 : sample_arg->pid = pid;
331 : 0 : sample_arg->tid = tid;
332 : 0 : sample_arg->stack = (const uint8_t *)stack;
333 : 0 : sample_arg->stack_size = stack_size;
334 : 0 : sample_arg->regs = regs;
335 : 0 : sample_arg->n_regs = n_regs;
336 : 0 : sample_arg->regs_mapping = regs_mapping;
337 : 0 : sample_arg->n_regs_mapping = n_regs_mapping;
338 : :
339 [ # # ]: 0 : if (! attached
340 [ # # ]: 0 : && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid,
341 : : &sample_thread_callbacks, sample_arg))
342 : : {
343 : 0 : free(sample_arg);
344 : 0 : return -1;
345 : : }
346 : :
347 : 0 : Dwfl_Process *process = dwfl->process;
348 : 0 : Ebl *ebl = process->ebl;
349 : 0 : sample_arg->elfclass = ebl_get_elfclass(ebl);
350 : 0 : ebl_sample_sp_pc(ebl, regs, n_regs,
351 : : regs_mapping, n_regs_mapping,
352 : 0 : &sample_arg->base_addr, &sample_arg->pc);
353 : :
354 : 0 : return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg);
355 : : }
356 : :
357 : : int
358 : 0 : dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf,
359 : : pid_t pid, pid_t tid,
360 : : const void *stack, size_t stack_size,
361 : : const Dwarf_Word *regs, uint32_t n_regs,
362 : : uint64_t perf_regs_mask, uint32_t abi,
363 : : int (*callback) (Dwfl_Frame *state, void *arg),
364 : : void *arg)
365 : : {
366 : : /* TODO: Lock the dwfl to ensure attach_state does not interfere
367 : : with other dwfl_perf_sample_getframes calls. */
368 : :
369 : 0 : struct sample_info *sample_arg;
370 : 0 : bool attached = false;
371 [ # # ]: 0 : if (dwfl->process != NULL)
372 : : {
373 : 0 : sample_arg = dwfl->process->callbacks_arg;
374 : 0 : attached = true;
375 : : }
376 : : else
377 : : {
378 : 0 : sample_arg = malloc (sizeof *sample_arg);
379 [ # # ]: 0 : if (sample_arg == NULL)
380 : : {
381 : 0 : __libdwfl_seterrno(DWFL_E_NOMEM);
382 : 0 : return -1;
383 : : }
384 : : }
385 : :
386 : 0 : sample_arg->pid = pid;
387 : 0 : sample_arg->tid = tid;
388 : 0 : sample_arg->stack = (const uint8_t *)stack;
389 : 0 : sample_arg->stack_size = stack_size;
390 : 0 : sample_arg->regs = regs;
391 : 0 : sample_arg->n_regs = n_regs;
392 : :
393 [ # # ]: 0 : if (! attached
394 [ # # ]: 0 : && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid,
395 : : &sample_thread_callbacks, sample_arg))
396 : : {
397 : 0 : free(sample_arg);
398 : 0 : return -1;
399 : : }
400 : :
401 : : /* Select the regs_mapping based on architecture. This will be
402 : : cached in ebl to avoid having to recompute the regs_mapping array
403 : : when perf_regs_mask is consistent for the entire session: */
404 : 0 : Dwfl_Process *process = dwfl->process;
405 : 0 : Ebl *ebl = process->ebl;
406 [ # # ]: 0 : if (!ebl_sample_perf_regs_mapping(ebl,
407 : : perf_regs_mask, abi,
408 : : &sample_arg->regs_mapping, &sample_arg->n_regs_mapping))
409 : : {
410 : 0 : __libdwfl_seterrno(DWFL_E_LIBEBL_BAD);
411 : 0 : return -1;
412 : : }
413 : 0 : sample_arg->elfclass = ebl_get_elfclass(ebl);
414 : 0 : ebl_sample_sp_pc(ebl, regs, n_regs,
415 : : sample_arg->regs_mapping, sample_arg->n_regs_mapping,
416 : 0 : &sample_arg->base_addr, &sample_arg->pc);
417 : :
418 : : /* XXX May want to check if abi matches ebl_get_elfclass(ebl). */
419 : 0 : return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg);
420 : : }
|