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