]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
src/stacktrace.c: Enable ARM support
authorSerhei Makarov <serhei@serhei.io>
Mon, 23 Mar 2026 18:53:56 +0000 (14:53 -0400)
committerSerhei Makarov <serhei@serhei.io>
Tue, 14 Apr 2026 20:51:47 +0000 (16:51 -0400)
Since we plan to replace stacktrace with stackprof (& the associated
test case) in a subsequent patchset, this is a minimal patch to
illustrate where the existing code using libdwfl_stacktrace needs to
generalize.

* configure.ac: Expand ac_cv_has_asm_x86_perf_regs_h
  to ac_cv_has_compat_asm_perf_regs_h also allowing arm/arm64.
* src/stacktrace.c: Enable for ARM architectures.
  (expected_frame_nregs): New function.
  (sp_reg_index): New function.
  (sysprof_find_dwfl): Replace hardcoded SP index with new functions.
  (sysprof_unwind_frame_cb): Likewise.
  (main): Open default_ebl backend appropriate to the current arch.

Signed-off-by: Serhei Makarov <serhei@serhei.io>
configure.ac
src/stacktrace.c

index f22a3f907a881e11dd526bf54d45b1138bd52959..82ccde1900017915ec69ff9fd43c17d81a3d6b03 100644 (file)
@@ -1003,22 +1003,22 @@ AC_CONFIG_FILES([config/profile.sh config/profile.csh config/profile.fish])
 
 # XXX Currently, eu-stacktrace can only work with sysprof/x86, hence:
 AC_ARG_ENABLE([stacktrace],AS_HELP_STRING([--enable-stacktrace], [Enable eu-stacktrace]))
-# check for x86, or more precisely _ASM_X86_PERF_REGS_H
+# check for x86/arm, or more precisely _ASM_{X86,ARM,ARM64}_PERF_REGS_H
 AS_IF([test "x$enable_stacktrace" = "xyes"], [
    enable_stacktrace=no
    AC_LANG([C])
-   AC_CACHE_CHECK([for _ASM_X86_PERF_REGS_H], ac_cv_has_asm_x86_perf_regs_h,
+   AC_CACHE_CHECK([for _ASM_{X86,ARM,ARM64}_PERF_REGS_H], ac_cv_has_compat_asm_perf_regs_h,
       [AC_COMPILE_IFELSE([AC_LANG_SOURCE([
 #include <asm/perf_regs.h>
-#ifndef _ASM_X86_PERF_REGS_H
-#error "_ASM_X86_PERF_REGS_H not found"
+#if !defined(_ASM_X86_PERF_REGS_H) && !defined (_ASM_ARM_PERF_REGS_H) && !defined(_ASM_ARM64_PERF_REGS_H)
+#error "_ASM_{X86,ARM,ARM64}_PERF_REGS_H not found"
 #endif
-])], ac_cv_has_asm_x86_perf_regs_h=yes, ac_cv_has_asm_x86_perf_regs_h=no)])
-   AS_IF([test "x$ac_cv_has_asm_x86_perf_regs_h" = xyes], [
+])], ac_cv_has_compat_asm_perf_regs_h=yes, ac_cv_has_compat_asm_perf_regs_h=no)])
+   AS_IF([test "x$ac_cv_has_compat_asm_perf_regs_h" = xyes], [
       enable_stacktrace=yes
    ])
    if test "x$enable_stacktrace" = "xno"; then
-      AC_MSG_ERROR([${program_prefix}stacktrace currently only supports x86, use --disable-stacktrace to disable.])
+      AC_MSG_ERROR([${program_prefix}stacktrace currently only supports x86/arm, use --disable-stacktrace to disable.])
    fi
 ])
 # check for sysprof headers:
index 1c9faeeb30591885f8195f8564c3f736ee0f982e..83fb3d96455e07659ec5fe8359db0677f43ba1e4 100644 (file)
 #include <locale.h>
 
 #include <system.h>
+#include <sys/utsname.h>
 
 #include <linux/perf_event.h>
 
-/* TODO: Need to generalize the code beyond x86 architectures. */
+/* TODO: Need to generalize the code beyond x86/ARM architectures. */
 #include <asm/perf_regs.h>
-#ifndef _ASM_X86_PERF_REGS_H
-#error "eu-stacktrace is currently limited to x86 architectures"
+#if !defined(_ASM_X86_PERF_REGS_H) && !defined(_ASM_ARM_PERF_REGS_H) && !defined(_ASM_ARM64_PERF_REGS_H)
+#error "eu-stacktrace is currently limited to x86/ARM architectures"
 #endif
 
 /*************************************
@@ -854,6 +855,26 @@ sysprof_init_dwfl_cb (Dwflst_Process_Tracker *cb_tracker,
   return dwfl;
 }
 
+uint32_t expected_frame_nregs (Ebl *ebl)
+{
+  int m = ebl_get_elfmachine(ebl);
+  /* For aarch64, we use fewer and ebl->frame_nregs to unwind.  */
+  if (m == EM_ARM || m == EM_AARCH64)
+    return 14;
+  if (m == EM_X86_64 || m == EM_386)
+    return ebl_frame_nregs(ebl);
+  /* In general, it's better to be on the permissive side.  */
+  return 1;
+}
+
+int sp_reg_index (Ebl *ebl, bool is_abi32)
+{
+  int m = ebl_get_elfmachine(ebl);
+  if (m == EM_X86_64 || m == EM_386) return is_abi32 ? 4 : 7;
+  else if (m == EM_ARM || m == EM_AARCH64) return is_abi32 ? 13 : 31;
+  else return 0; /* XXX unwinding will likely not continue */
+}
+
 Dwfl *
 sysprof_find_dwfl (struct sysprof_unwind_info *sui,
                   SysprofCaptureStackUser *ev,
@@ -861,10 +882,7 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui,
                   Elf **out_elf)
 {
   pid_t pid = ev->frame.pid;
-  /* XXX: Note that sysprof requesting the x86_64 register file from
-     perf_events will result in an array of 17 regs even for 32-bit
-     applications. */
-  if (regs->n_regs < ebl_frame_nregs(default_ebl)) /* XXX expecting everything except FLAGS */
+  if (regs->n_regs < expected_frame_nregs(default_ebl))
     {
       if (show_failures)
        fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %ld\n"),
@@ -892,12 +910,11 @@ sysprof_find_dwfl (struct sysprof_unwind_info *sui,
     }
 
  reuse:
-  /* TODO: Generalize to other architectures than x86_64. */
-  sui->last_sp = regs->regs[7];
+  bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
+  sui->last_sp = regs->regs[sp_reg_index(default_ebl, is_abi32)];
   sui->last_base = sui->last_sp;
 
   if (show_frames) {
-    bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
     fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx sp=%lx+(%lx)\n",
            (long long) pid, cached ? " (cached)" : "",
            ev->size, is_abi32 ? " (32-bit)" : "",
@@ -925,11 +942,9 @@ sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
 
   Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
   Dwarf_Addr sp;
-  /* TODO: Need to generalize this code beyond x86 architectures. */
   struct sysprof_unwind_info *sui = (struct sysprof_unwind_info *)arg;
   int is_abi32 = (sui->last_abi == PERF_SAMPLE_REGS_ABI_32);
-  /* DWARF register order cf. elfutils backends/{x86_64,i386}_initreg.c: */
-  int user_regs_sp = is_abi32 ? 4 : 7;
+  int user_regs_sp = sp_reg_index(default_ebl, is_abi32);
   int rc = dwfl_frame_reg (state, user_regs_sp, &sp);
   if (rc < 0)
     {
@@ -1353,7 +1368,16 @@ https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu
        error (EXIT_BAD, errno, N_("Could not initialize Dwfl table"));
 
       /* TODO: Generalize to other architectures. */
-      default_ebl = ebl_openbackend_machine(EM_X86_64);
+      struct utsname u;
+      uname(&u);
+      int em = EM_NONE;
+      if (strcmp(u.machine, "x86_64") == 0) em = EM_X86_64;
+      else if (strcmp(u.machine, "i686") == 0 || strcmp(u.machine, "i386") == 0) em = EM_386;
+      else if (strcmp(u.machine, "aarch64") == 0) em = EM_AARCH64;
+      else if (strcmp(u.machine, "armv7l") == 0) em = EM_ARM;
+      else
+       error (EXIT_BAD, errno, N_("Unsupported architecture: %s"), u.machine);
+      default_ebl = ebl_openbackend_machine(em);
 
       struct sysprof_unwind_info sui;
       sui.output_fd = output_fd;