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

Reply via email to