diff options
Diffstat (limited to 'backends')
| -rw-r--r-- | backends/Makefile.am | 2 | ||||
| -rw-r--r-- | backends/i386_init.c | 9 | ||||
| -rw-r--r-- | backends/i386_initreg_sample.c | 72 | ||||
| -rw-r--r-- | backends/linux-perf-regs.c | 48 | ||||
| -rw-r--r-- | backends/x86_64_init.c | 9 | ||||
| -rw-r--r-- | backends/x86_64_initreg_sample.c | 70 | ||||
| -rw-r--r-- | backends/x86_initreg_sample.c | 108 |
7 files changed, 126 insertions, 192 deletions
diff --git a/backends/Makefile.am b/backends/Makefile.am index 8ccbdb50..7a820df0 100644 --- a/backends/Makefile.am +++ b/backends/Makefile.am @@ -121,7 +121,7 @@ am_libebl_backends_pic_a_OBJECTS = $(libebl_backends_a_SOURCES:.c=.os) noinst_HEADERS = libebl_CPU.h libebl_PERF_FLAGS.h common-reloc.c \ linux-core-note.c x86_corenote.c \ - linux-perf-regs.c x86_initreg_sample.c + x86_initreg_sample.c EXTRA_DIST = $(modules:=_reloc.def) diff --git a/backends/i386_init.c b/backends/i386_init.c index e64ef6ed..a980e71a 100644 --- a/backends/i386_init.c +++ b/backends/i386_init.c @@ -60,10 +60,13 @@ i386_init (Elf *elf __attribute__ ((unused)), (Likely an artifact of reusing that header between i386/x86_64.) */ eh->frame_nregs = 9; HOOK (eh, set_initial_registers_tid); - HOOK (eh, set_initial_registers_sample); - HOOK (eh, sample_base_addr); - HOOK (eh, sample_pc); + /* set_initial_registers_sample is default ver */ + HOOK (eh, sample_sp_pc); + HOOK (eh, sample_perf_regs_mapping); eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_I386; + eh->cached_perf_regs_mask = 0; + eh->cached_regs_mapping = NULL; + eh->cached_n_regs_mapping = -1; HOOK (eh, unwind); return eh; diff --git a/backends/i386_initreg_sample.c b/backends/i386_initreg_sample.c index 677393c9..94955191 100644 --- a/backends/i386_initreg_sample.c +++ b/backends/i386_initreg_sample.c @@ -31,6 +31,7 @@ #endif #include <stdlib.h> +#include <assert.h> #if (defined __i386__ || defined __x86_64__) && defined(__linux__) # include <linux/perf_event.h> # include <asm/perf_regs.h> @@ -40,69 +41,26 @@ #include "libebl_CPU.h" #include "libebl_PERF_FLAGS.h" #if (defined __i386__ || defined __x86_64__) && defined(__linux__) -# include "linux-perf-regs.c" # include "x86_initreg_sample.c" #endif -/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h, - enum perf_event_x86_regs: */ -Dwarf_Word -i386_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, - /* XXX hypothetically needed if abi varies - between samples in the same process; - not needed on x86 */ - uint32_t abi __attribute__((unused))) -{ -#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - return 0; -#else /* __i386__ || __x86_64__ */ - (void)regs; - (void)n_regs; - (void)regs_mask; - return perf_sample_find_reg (regs, n_regs, regs_mask, - 7 /* index into perf_event_x86_regs */); -#endif -} - -Dwarf_Word -i386_sample_pc (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, - uint32_t abi __attribute__((unused))) +bool +i386_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs, + const int *regs_mapping, uint32_t n_regs_mapping, + Dwarf_Word *sp, Dwarf_Word *pc) { -#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - return 0; -#else /* __i386__ || __x86_64__ */ - return perf_sample_find_reg (regs, n_regs, regs_mask, - 8 /* index into perf_event_x86_regs */); -#endif + /* XXX for dwarf_regs indices, compare i386_initreg.c */ + return x86_sample_sp_pc (regs, n_regs, regs_mapping, n_regs_mapping, + sp, 4 /* index of sp in dwarf_regs */, + pc, 8 /* index of pc in dwarf_regs */); } bool -i386_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, uint32_t abi, - ebl_tid_registers_t *setfunc, - void *arg) +i386_sample_perf_regs_mapping (Ebl *ebl, + uint64_t perf_regs_mask, uint32_t abi, + const int **regs_mapping, + size_t *n_regs_mapping) { -#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - (void)abi; - (void)setfunc; - (void)arg; - return false; -#else /* __i386__ || __x86_64__ */ - Dwarf_Word dwarf_regs[9]; - if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask, - abi, dwarf_regs, 9)) - return false; - return setfunc (0, 9, dwarf_regs, arg); -#endif + return x86_sample_perf_regs_mapping (ebl, perf_regs_mask, abi, + regs_mapping, n_regs_mapping); } diff --git a/backends/linux-perf-regs.c b/backends/linux-perf-regs.c deleted file mode 100644 index 22ad67c6..00000000 --- a/backends/linux-perf-regs.c +++ /dev/null @@ -1,48 +0,0 @@ -/* Common pieces for handling registers in a linux perf_events sample. - Copyright (C) 2025 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/>. */ - -static Dwarf_Word -perf_sample_find_reg (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, - int target) -{ - int j, k; uint64_t bit; - for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1) - { - if (bit & regs_mask) { - if (n_regs <= (uint32_t) j) - return 0; /* regs_mask count doesn't match n_regs */ - if (k == target) - return regs[j]; - if (k > target) - return 0; /* regs_mask doesn't include desired reg */ - j++; - } - } - return 0; -} diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c index 6a1cbc4b..5f929758 100644 --- a/backends/x86_64_init.c +++ b/backends/x86_64_init.c @@ -63,10 +63,13 @@ x86_64_init (Elf *elf __attribute__ ((unused)), /* gcc/config/ #define DWARF_FRAME_REGISTERS. */ eh->frame_nregs = 17; HOOK (eh, set_initial_registers_tid); - HOOK (eh, set_initial_registers_sample); - HOOK (eh, sample_base_addr); - HOOK (eh, sample_pc); + /* set_initial_registers_sample is default ver */ + HOOK (eh, sample_sp_pc); + HOOK (eh, sample_perf_regs_mapping); eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_X86_64; + eh->cached_perf_regs_mask = 0; + eh->cached_regs_mapping = NULL; + eh->cached_n_regs_mapping = -1; HOOK (eh, unwind); HOOK (eh, check_reloc_target_type); diff --git a/backends/x86_64_initreg_sample.c b/backends/x86_64_initreg_sample.c index 48d14bc8..9dd708c9 100644 --- a/backends/x86_64_initreg_sample.c +++ b/backends/x86_64_initreg_sample.c @@ -31,6 +31,7 @@ #endif #include <stdlib.h> +#include <assert.h> #if defined(__x86_64__) && defined(__linux__) # include <linux/perf_event.h> # include <asm/perf_regs.h> @@ -40,67 +41,26 @@ #include "libebl_CPU.h" #include "libebl_PERF_FLAGS.h" #if defined(__x86_64__) && defined(__linux__) -# include "linux-perf-regs.c" # include "x86_initreg_sample.c" #endif -/* Register ordering cf. linux arch/x86/include/uapi/asm/perf_regs.h, - enum perf_event_x86_regs: */ -Dwarf_Word -x86_64_sample_base_addr (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, - /* XXX hypothetically needed if abi varies - between samples in the same process; - not needed on x86*/ - uint32_t abi __attribute__((unused))) -{ -#if !defined(__x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - return 0; -#else /* __x86_64__ */ - return perf_sample_find_reg (regs, n_regs, regs_mask, - 7 /* index into perf_event_x86_regs */); -#endif -} - -Dwarf_Word -x86_64_sample_pc (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, - uint32_t abi __attribute__((unused))) +bool +x86_64_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs, + const int *regs_mapping, uint32_t n_regs_mapping, + Dwarf_Word *sp, Dwarf_Word *pc) { -#if !defined(__x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - return 0; -#else /* __x86_64__ */ - return perf_sample_find_reg (regs, n_regs, regs_mask, - 8 /* index into perf_event_x86_regs */); -#endif + /* XXX for dwarf_regs indices, compare x86_64_initreg.c */ + return x86_sample_sp_pc (regs, n_regs, regs_mapping, n_regs_mapping, + sp, 7 /* index of sp in dwarf_regs */, + pc, 16 /* index of pc in dwarf_regs */); } bool -x86_64_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, uint32_t abi, - ebl_tid_registers_t *setfunc, - void *arg) +x86_64_sample_perf_regs_mapping (Ebl *ebl, + uint64_t perf_regs_mask, uint32_t abi, + const int **regs_mapping, + size_t *n_regs_mapping) { -#if !defined(__x86_64__) || !defined(__linux__) - (void)regs; - (void)n_regs; - (void)regs_mask; - (void)abi; - (void)setfunc; - (void)arg; - return false; -#else /* __x86_64__ */ - Dwarf_Word dwarf_regs[17]; - if (!x86_set_initial_registers_sample (regs, n_regs, regs_mask, - abi, dwarf_regs, 9)) - return false; - return setfunc (0, 17, dwarf_regs, arg); -#endif + return x86_sample_perf_regs_mapping (ebl, perf_regs_mask, abi, + regs_mapping, n_regs_mapping); } - diff --git a/backends/x86_initreg_sample.c b/backends/x86_initreg_sample.c index 8d6b471b..47cd91c2 100644 --- a/backends/x86_initreg_sample.c +++ b/backends/x86_initreg_sample.c @@ -1,4 +1,4 @@ -/* x86 linux perf_events register handling, pieces common to x86-64 and i386. +/* x86 stack sample register handling, pieces common to x86-64 and i386. Copyright (C) 2025 Red Hat, Inc. This file is part of elfutils. @@ -27,13 +27,52 @@ not, see <http://www.gnu.org/licenses/>. */ static bool -x86_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, - uint64_t regs_mask, uint32_t abi, - Dwarf_Word *dwarf_regs, int expected_regs) +x86_sample_sp_pc (const Dwarf_Word *regs, uint32_t n_regs, + const int *regs_mapping, uint32_t n_regs_mapping, + Dwarf_Word *sp, uint sp_index /* into dwarf_regs */, + Dwarf_Word *pc, uint pc_index /* into dwarf_regs */) { -#if (!defined __i386__ && !defined __x86_64__) || !defined(__linux__) + if (sp != NULL) *sp = 0; + if (pc != NULL) *pc = 0; +#if !defined(__x86_64__) + (void)regs; + (void)n_regs; + (void)regs_mapping; + (void)n_regs_mapping; return false; -#else /* __i386__ || __x86_64__ */ +#else /* __x86_64__ */ + /* TODO: Register locations could be cached and rechecked on a + fastpath without needing to loop? */ + int j, need_sp = (sp != NULL), need_pc = (pc != NULL); + for (j = 0; (need_sp || need_pc) && n_regs_mapping > (uint32_t)j; j++) + { + if (n_regs < (uint32_t)j) break; + if (need_sp && regs_mapping[j] == (int)sp_index) + { + *sp = regs[j]; need_sp = false; + } + if (need_pc && regs_mapping[j] == (int)pc_index) + { + *pc = regs[j]; need_pc = false; + } + } + return (!need_sp && !need_pc); +#endif +} + +static bool +x86_sample_perf_regs_mapping (Ebl *ebl, + uint64_t perf_regs_mask, uint32_t abi, + const int **regs_mapping, + size_t *n_regs_mapping) +{ + if (perf_regs_mask != 0 && ebl->cached_perf_regs_mask == perf_regs_mask) + { + *regs_mapping = ebl->cached_regs_mapping; + *n_regs_mapping = ebl->cached_n_regs_mapping; + return true; + } + /* The following facts are needed to translate x86 registers correctly: - perf register order seen in linux arch/x86/include/uapi/asm/perf_regs.h The registers array is built in the same order as the enum! @@ -52,39 +91,58 @@ x86_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, bool is_abi32 = (abi == PERF_SAMPLE_REGS_ABI_32); /* Locations of dwarf_regs in the perf_event_x86_regs enum order, - not the regs[i] array (which will include a subset of the regs): */ + not the regs[] array (which will include a subset of the regs): */ static const int regs_i386[] = {0, 2, 3, 1, 7/*sp*/, 6, 4, 5, 8/*ip*/}; static const int regs_x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7/*sp*/, 16/*r8 after flags+segment*/, 17, 18, 19, 20, 21, 22, 23, 8/*ip*/}; const int *dwarf_to_perf = is_abi32 ? regs_i386 : regs_x86_64; - /* Locations of perf_regs in the regs[] array, according to regs_mask: */ - int perf_to_regs[PERF_REG_X86_64_MAX]; - uint64_t expected_mask = is_abi32 ? PERF_FRAME_REGISTERS_I386 : PERF_FRAME_REGISTERS_X86_64; - int j, k; uint64_t bit; - /* TODO: Is it worth caching this perf_to_regs computation as long - as regs_mask is kept the same across repeated calls? */ - for (j = 0, k = 0, bit = 1; k < PERF_REG_X86_64_MAX; k++, bit <<= 1) + /* Count bits and allocate regs_mapping: */ + int j, k, kmax, count; uint64_t bit; + for (k = 0, kmax = -1, count = 0, bit = 1; + k < PERF_REG_X86_64_MAX; k++, bit <<= 1) { - if ((bit & expected_mask) && (bit & regs_mask)) { - if (n_regs <= (uint32_t)j) - return false; /* regs_mask count doesn't match n_regs */ - perf_to_regs[k] = j; - j++; - } else { - perf_to_regs[k] = -1; + if ((bit & perf_regs_mask)) { + count++; + kmax = k; } } + ebl->cached_perf_regs_mask = perf_regs_mask; + ebl->cached_regs_mapping = (int *)calloc (count, sizeof(int)); + ebl->cached_n_regs_mapping = count; - for (int i = 0; i < expected_regs; i++) + /* Locations of perf_regs in the regs[] array, according to + perf_regs_mask: */ + int perf_to_regs[PERF_REG_X86_64_MAX]; + uint64_t expected_mask = is_abi32 ? + PERF_FRAME_REGISTERS_I386 : PERF_FRAME_REGISTERS_X86_64; + for (j = 0, k = 0, bit = 1; k <= kmax; k++, bit <<= 1) + { + if ((bit & expected_mask) && (bit & perf_regs_mask)) + { + perf_to_regs[k] = j; + j++; + } + else + { + perf_to_regs[k] = -1; + } + } + if (j > (int)ebl->cached_n_regs_mapping) + return false; + + /* Locations of perf_regs in the dwarf_regs array, according to + perf_regs_mask and perf_to_regs[]: */ + for (size_t i = 0; i < ebl->frame_nregs; i++) { k = dwarf_to_perf[i]; j = perf_to_regs[k]; if (j < 0) continue; - if (n_regs <= (uint32_t)j) continue; - dwarf_regs[i] = regs[j]; + ebl->cached_regs_mapping[j] = i; } + + *regs_mapping = ebl->cached_regs_mapping; + *n_regs_mapping = ebl->cached_n_regs_mapping; return true; -#endif /* __i386__ || __x86_64__ */ } |
