These additional libebl hooks are needed to extract the location of the stack frame in-memory from the perf_events register sample, when dwfl_perf_sample_getframes is implemented in the next patch.
Implementing x86_64 for now, just as with the earlier ebl patches. * libebl/libebl.h (ebl_sample_base_addr): New function. (ebl_sample_pc): New function. * libebl/eblinitreg_sample.c (ebl_sample_base_addr): New function. (ebl_sample_pc): New function. * libebl/ebl-hooks.h (sample_base_addr): New EBLHOOK. (sample_pc): New EBLHOOK. * backends/x86_64_init.c (x86_64_init): Add sample_base_addr, sample_pc hooks. * backends/x86_64_initreg_sample.c (find_reg): New function to index into a perf register array whose contents are described by regs_mask. The target index is the index of the register in linux arch/x86/include/uapi/asm/perf_regs.h, enum perf_event_x86_regs. Note that on other arches the 'abi' argument may be required, but on x86 this is an unused arg. (x86_64_sample_base_addr): Extract stack pointer (reg7). (x86_64_sample_pc): Extract program counter (reg8). --- backends/x86_64_init.c | 2 ++ backends/x86_64_initreg_sample.c | 50 ++++++++++++++++++++++++++++++++ libebl/ebl-hooks.h | 8 +++++ libebl/eblinitreg_sample.c | 18 ++++++++++++ libebl/libebl.h | 13 +++++++++ 5 files changed, 91 insertions(+) diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c index 2e6d1df1..6a1cbc4b 100644 --- a/backends/x86_64_init.c +++ b/backends/x86_64_init.c @@ -64,6 +64,8 @@ x86_64_init (Elf *elf __attribute__ ((unused)), 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); eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_X86_64; 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 182f8197..e49b4faf 100644 --- a/backends/x86_64_initreg_sample.c +++ b/backends/x86_64_initreg_sample.c @@ -40,6 +40,56 @@ #include "libebl_CPU.h" #include "libebl_PERF_FLAGS.h" +Dwarf_Word +find_reg (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, + /* XXX hypothetically needed if the register position varies + for 32-bit perf_events samples; not needed on x86 */ + uint32_t abi __attribute__((unused)), + 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; +} + +/* 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, uint32_t abi) +{ +#if !defined(__x86_64__) || !defined(__linux__) + return 0; +#else /* __x86_64__ */ + return find_reg(regs, n_regs, regs_mask, abi, + 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) +{ +#if !defined(__x86_64__) || !defined(__linux__) + return 0; +#else /* __x86_64__ */ + return find_reg(regs, n_regs, regs_mask, abi, + 8 /* index into perf_event_x86_regs */); +#endif +} + bool x86_64_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs, uint64_t regs_mask, uint32_t abi, diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h index f0475cdf..05474fbc 100644 --- a/libebl/ebl-hooks.h +++ b/libebl/ebl-hooks.h @@ -166,6 +166,14 @@ bool EBLHOOK(set_initial_registers_sample) (const Dwarf_Word *regs, uint32_t n_r ebl_tid_registers_t *setfunc, void *arg); +/* Extract the stack address from a perf_events register sample. */ +Dwarf_Word EBLHOOK(sample_base_addr) (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi); + +/* Extract the instruction pointer from a perf_events register sample. */ +Dwarf_Word EBLHOOK(sample_pc) (const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi); + /* Convert *REGNO as is in DWARF to a lower range suitable for Dwarf_Frame->REGS indexing. */ bool EBLHOOK(dwarf_to_regno) (Ebl *ebl, unsigned *regno); diff --git a/libebl/eblinitreg_sample.c b/libebl/eblinitreg_sample.c index 2a387b7a..53244d1e 100644 --- a/libebl/eblinitreg_sample.c +++ b/libebl/eblinitreg_sample.c @@ -34,6 +34,24 @@ #include <libeblP.h> #include <assert.h> +Dwarf_Word +ebl_sample_base_addr (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) +{ + assert (ebl->sample_base_addr != NULL); + return ebl->sample_base_addr (regs, n_regs, regs_mask, abi); +} + +Dwarf_Word +ebl_sample_pc (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) +{ + assert (ebl->sample_pc != NULL); + return ebl->sample_pc (regs, n_regs, regs_mask, abi); +} + bool ebl_set_initial_registers_sample (Ebl *ebl, const Dwarf_Word *regs, uint32_t n_regs, diff --git a/libebl/libebl.h b/libebl/libebl.h index d87efeb5..a64d70e9 100644 --- a/libebl/libebl.h +++ b/libebl/libebl.h @@ -350,6 +350,19 @@ extern bool ebl_set_initial_registers_sample (Ebl *ebl, void *arg) __nonnull_attribute__ (1, 2, 6); +/* Extract the stack address from a perf_events register sample. */ +Dwarf_Word ebl_sample_base_addr (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) + __nonnull_attribute__ (1, 2); + +/* Extract the instruction pointer from a perf_events register sample. */ +Dwarf_Word ebl_sample_pc (Ebl *ebl, + const Dwarf_Word *regs, uint32_t n_regs, + uint64_t regs_mask, uint32_t abi) + __nonnull_attribute__ (1, 2); + + /* Preferred sample_regs_user mask to request from linux perf_events to allow unwinding on EBL architecture. Omitting some of these registers may result in failed or inaccurate unwinding. */ -- 2.47.0