]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
backends/: draft aarch64 sample_regs support
authorSerhei Makarov <serhei@serhei.io>
Fri, 6 Mar 2026 17:10:29 +0000 (12:10 -0500)
committerSerhei Makarov <serhei@serhei.io>
Fri, 20 Mar 2026 18:12:35 +0000 (14:12 -0400)
{detailed changelog TBA pending testing}

backends/Makefile.am
backends/aarch64_init.c
backends/aarch64_initreg_sample.c [new file with mode: 0644]
backends/libebl_PERF_FLAGS.h
libebl/eblinitreg_sample.c
libebl/eblopenbackend.c
libebl/libeblP.h

index 7a820df0482766802f4dd7e851ea4d67d7a26869..bebd990eb03d1d300dd69e14d492257e20562e0b 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2000-2010, 2013, 2014, 2025 Red Hat, Inc.
+## Copyright (C) 2000-2010, 2013, 2014, 2025-2026 Red Hat, Inc.
 ## Copyright (C) 2012 Tilera Corporation
 ## This file is part of elfutils.
 ##
@@ -61,7 +61,7 @@ arm_SRCS = arm_init.c arm_symbol.c arm_regs.c arm_corenote.c \
 
 aarch64_SRCS = aarch64_init.c aarch64_regs.c aarch64_symbol.c  \
               aarch64_corenote.c aarch64_retval.c aarch64_cfi.c \
-              aarch64_initreg.c aarch64_unwind.c
+              aarch64_initreg.c aarch64_initreg_sample.c aarch64_unwind.c
 
 sparc_SRCS = sparc_init.c sparc_symbol.c sparc_regs.c sparc_retval.c \
             sparc_corenote.c sparc64_corenote.c sparc_auxv.c sparc_attrs.c \
index c61767d56636235bf6f6123b2280c422a44ca042..f6505bd7f8ba2398fe487d166d67796313f7b739 100644 (file)
@@ -1,5 +1,5 @@
 /* Initialization of AArch64 specific backend library.
-   Copyright (C) 2013, 2017 Red Hat, Inc.
+   Copyright (C) 2013, 2017, 2026 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -33,6 +33,7 @@
 #define BACKEND                aarch64_
 #define RELOC_PREFIX   R_AARCH64_
 #include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
 
 /* This defines the common reloc hooks based on aarch64_reloc.def.  */
 #include "common-reloc.c"
@@ -61,6 +62,11 @@ aarch64_init (Elf *elf __attribute__ ((unused)),
      + ALT_FRAME_RETURN_COLUMN (used when LR isn't used) = 97 DWARF regs. */
   eh->frame_nregs = 97;
   HOOK (eh, set_initial_registers_tid);
+  HOOK (eh, set_initial_registers_sample);
+  HOOK (eh, sample_sp_pc);
+  /* sample_perf_regs_mapping is default ver  */
+  eh->perf_frame_regs_mask = PERF_FRAME_REGISTERS_AARCH64;
+  __libebl_init_cached_regs_mapping (eh);
   HOOK (eh, unwind);
 
   return eh;
diff --git a/backends/aarch64_initreg_sample.c b/backends/aarch64_initreg_sample.c
new file mode 100644 (file)
index 0000000..23341f1
--- /dev/null
@@ -0,0 +1,103 @@
+/* Populate process registers from a register sample.
+   Copyright (C) 2026 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/>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define BACKEND aarch64_
+#include "libebl_CPU.h"
+#include "libebl_PERF_FLAGS.h"
+/* TODO(REVIEW): PERF_FLAGS includes generic_sample_sp_pc -- rename?  */
+
+bool
+aarch64_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)
+{
+  return generic_sample_sp_pc (regs, n_regs, regs_mapping, n_regs_mapping,
+                              sp, 31 /* index of sp in dwarf_regs */,
+                              pc, 32 /* index of pc in dwarf_regs */);
+}
+
+bool
+aarch64_set_initial_registers_sample (const Dwarf_Word *regs, uint32_t n_regs,
+                                     const int *regs_mapping, size_t n_regs_mapping,
+                                     ebl_tid_registers_t *setfunc,
+                                     void *arg)
+{
+/* TODO(REVIEW): The #ifdef here seems strictly optional as we don't
+   refer to perf_events or ptrace arch-specific declarations. */
+#if !defined(__aarch64__)
+  (void)regs; (void)n_regs;
+  (void)regs_mapping; (void)n_regs_mapping;
+  (void)setfunc; (void)arg;
+  return false;
+#else
+  /* TODO(REVIEW) verify here and above following the convention in aarch64_initreg.c */
+#define N_GREGS 33
+  Dwarf_Word dwarf_regs[N_GREGS];
+  bool scratch_present = false;
+  size_t i;
+  for (i = 0; i < N_GREGS; i++)
+    dwarf_regs[i] = 0x0;
+  for (i = 0; i < n_regs; i++)
+    {
+      if (i >= n_regs_mapping)
+       break;
+      if (regs_mapping[i] < 0 || regs_mapping[i] >= N_GREGS)
+       continue;
+      if (regs_mapping[i] < 19)
+       scratch_present = true;
+      dwarf_regs[regs_mapping[i]] = regs[i];
+    }
+
+  /* X0..X18 only if present.  */
+  if (scratch_present && ! setfunc (0, 19, &dwarf_regs[0], arg))
+    return false;
+
+  /* X19..X29, X30(LR) plus SP.  */
+  if (! setfunc (19, 32 - 18, &dwarf_regs[19], arg))
+    return false;
+
+  /* PC.  */
+  if (! setfunc (-1, 1, &dwarf_regs[32], arg))
+    return false;
+
+  /* TODO(REVIEW) Need to obtain PAC mask since the unwinder needs to
+     strip it from LR/X30 to handle pointer authentication.  */
+
+  /* Skip ELR, RA_SIGN_STATE  */
+
+  /* XXX Skip FP registers.  */
+  return true;
+#endif /* __aarch64__ */
+}
index 51c20ea682f0bc10e8ac8251731cb0a860b991b1..f0ed9f5988ab335694bd07a9dbc6e633f8432868 100644 (file)
@@ -1,7 +1,7 @@
 /* Linux perf_events sample_regs_user flags required for unwinding.
    Internal only; elfutils library users should use ebl_perf_frame_regs_mask().
 
-   Copyright (C) 2025 Red Hat, Inc.
+   Copyright (C) 2025-2026 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -33,7 +33,7 @@
 
 #if defined(__linux__)
 /* XXX Need to exclude __linux__ arches without perf_regs.h. */
-#if defined(__x86_64__) || defined(__i386__)
+#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__)
 /* || defined(other_architecture)... */
 # include <asm/perf_regs.h>
 #endif
    different arch, we can't unwind i386 and x86_64 frames. */
 #define PERF_FRAME_REGISTERS_I386 0
 #define PERF_FRAME_REGISTERS_X86_64 0
-#endif
+#endif /* _ASM_X86_PERF_REGS_H */
+
+#if defined(_ASM_ARM64_PERF_REGS_H)
+#define REG(R) (1ULL << PERF_REG_ARM64_ ## R)
+/* TODO(REVIEW): Proper unwind set seems to be: callee-saved X19..X28,
+   then X29 for FP, LR for return addr, and SP, PC.  */
+#define PERF_FRAME_REGISTERS_AARCH64 (REG(X19) | REG(X20) | REG(X21) \
+  | REG(X22) | REG(X23) | REG(X24) | REG(X25) | REG(X26) | REG(X27)  \
+  | REG(X28) | REG(X29) /*FP*/ | REG(LR) | REG(SP) | REG(PC))
+/* Register ordering defined in linux arch/arm64/include/uapi/asm/perf_regs.h.  */
+#else
+/* Since asm/perf_regs.h is absent, or gives the register layout for a
+   different arch, we can't unwind aarch64 perf sample frames.  */
+#define PERF_FRAME_REGISTERS_AARCH64 0
+#endif /* _ASM_ARM64_PERF_REGS_H */
+
+/* TODO(REVIEW) Replaces x86_sample_sp_pc -- is this header the right location for it? */
+static inline bool
+generic_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 (sp != NULL) *sp = 0;
+  if (pc != NULL) *pc = 0;
+  /* TODO(REVIEW): 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 /* libebl_PERF_FLAGS.h */
index d5704dfa60e26f00a97306752aad9b5409d83998..51fbe65d280785e455ec571e0df97e6c2d64b7ac 100644 (file)
@@ -1,6 +1,6 @@
 /* Populate process Dwfl_Frame from perf_events sample.
 
-   Copyright (C) 2025 Red Hat, Inc.
+   Copyright (C) 2025-2026 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
 # include <config.h>
 #endif
 
-#include <libeblP.h>
+#include <stdlib.h>
 #include <assert.h>
 
+#include <libeblP.h>
+
 bool
 ebl_sample_sp_pc (Ebl *ebl,
                  const Dwarf_Word *regs, uint32_t n_regs,
@@ -83,10 +85,46 @@ ebl_sample_perf_regs_mapping (Ebl *ebl,
                              uint64_t perf_regs_mask, uint32_t abi,
                              const int **regs_mapping, size_t *n_regs_mapping)
 {
-  /* If sample_perf_regs_mapping is unsupported then PERF_FRAME_REGS_MASK is zero.  */
-  assert (ebl->sample_perf_regs_mapping != NULL);
-  return ebl->sample_perf_regs_mapping (ebl, perf_regs_mask, abi,
-                                       regs_mapping, n_regs_mapping);
+  /* If sample_perf_regs_mapping is unsupported then perf_frame_regs_mask is zero.  */
+  assert (ebl->perf_frame_regs_mask != 0);
+
+  /* If sample_perf_regs_mapping is defined for this arch, use it.  */
+  if (ebl->sample_perf_regs_mapping != NULL)
+    return ebl->sample_perf_regs_mapping (ebl, perf_regs_mask, abi,
+                                         regs_mapping, n_regs_mapping);
+
+  /* If sample_perf_regs_mapping is unspecified, then it is safe
+     to return a linear 1:1 mapping between perf_regs and dwarf_regs.  */
+
+  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;
+    }
+
+  /* XXX Unwind-relevant register file should be no bigger than this:  */
+  int count = 64;
+
+  ebl->cached_regs_mapping = (int *)calloc (count, sizeof(int));
+  ebl->cached_n_regs_mapping = count;
+
+  /* TODO(REVIEW) Check correctness. */
+  int j, k; uint64_t bit;
+  for (j = 0, k = 0, bit = 1;
+       k < count; k++, bit <<= 1)
+    {
+      ebl->cached_regs_mapping[k] = -1;
+      if ((bit & perf_regs_mask)) {
+       ebl->cached_regs_mapping[j] = k;
+       j++;
+      }
+    }
+
+  *regs_mapping = ebl->cached_regs_mapping;
+  *n_regs_mapping = ebl->cached_n_regs_mapping;
+  return true;
+
 }
 
 uint64_t
index b68dea7a08092c039ec5ed0dd9bbd34141ffb323..1be0e5fe6949a9c299f84ee82428ec86e7d914a3 100644 (file)
@@ -1,5 +1,5 @@
 /* Generate ELF backend handle.
-   Copyright (C) 2000-2017 Red Hat, Inc.
+   Copyright (C) 2000-2017, 2026 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -271,6 +271,17 @@ fill_defaults (Ebl *result)
   result->sysvhash_entrysize = sizeof (Elf32_Word);
 }
 
+/* Called by the initialization functions for backends which support
+   hook sample_perf_regs_mapping().  */
+void
+internal_function
+__libebl_init_cached_regs_mapping (Ebl *eh)
+{
+  eh->cached_perf_regs_mask = 0;
+  eh->cached_regs_mapping = NULL;
+  eh->cached_n_regs_mapping = -1;
+}
+
 /* Find an appropriate backend for the file associated with ELF.  */
 static Ebl *
 openbackend (Elf *elf, const char *emulation, GElf_Half machine)
index 348da49e58a7522bf7e53d22cc8802fdf9540729..f9b76d4efbd8dd1357601b2eb4ed29031798ca4c 100644 (file)
@@ -1,5 +1,5 @@
 /* Internal definitions for interface for libebl.
-   Copyright (C) 2000-2009, 2013, 2014, 2025 Red Hat, Inc.
+   Copyright (C) 2000-2009, 2013, 2014, 2025-2026 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
@@ -96,6 +96,9 @@ struct ebl
    initialize for the given Elf or machine.  */
 typedef Ebl *(*ebl_bhinit_t) (Elf *, GElf_Half, Ebl *);
 
+/* Additional helper to init cached perf_events mapping data.  */
+void __libebl_init_cached_regs_mapping (Ebl *ebl)
+  internal_function;
 
 /* LEB128 constant helper macros.  */
 #define ULEB128_7(x)   (BUILD_BUG_ON_ZERO ((x) >= (1U << 7)) + (x))