]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Add frame pointer unwinding for aarch64 and relax backtrace testcases.
authorUlf Hermann <ulf.hermann@qt.io>
Thu, 20 Apr 2017 12:41:35 +0000 (14:41 +0200)
committerMark Wielaard <mark@klomp.org>
Tue, 2 May 2017 14:37:51 +0000 (16:37 +0200)
If we don't find any debug information for a given frame, we usually
cannot unwind any further. However, the binary in question might have
been compiled with frame pointers, in which case we can look up the
well known frame pointer locations in the stack snapshot and use them
to bridge the frames without debug information.

Relax the backtrace core testcases a little by allowing a duplicate
sigusr2 frame or a backtrace ending with an invalid register. Both of
which can happen if the frame pointer unwinder guesses slightly wrong.

Signed-off-by: Ulf Hermann <ulf.hermann@qt.io>
Signed-off-by: Mark Wielaard <mark@klomp.org>
backends/ChangeLog
backends/Makefile.am
backends/aarch64_init.c
backends/aarch64_unwind.c [new file with mode: 0644]
tests/ChangeLog
tests/Makefile.am
tests/backtrace-subr.sh
tests/backtrace.aarch64.fp.core.bz2 [new file with mode: 0644]
tests/backtrace.aarch64.fp.exec.bz2 [new file with mode: 0644]
tests/backtrace.c
tests/run-backtrace-fp-core-aarch64.sh [new file with mode: 0755]

index 8b1c4f52376377fc4693358bbd4dec12ee99bc23..1ac5bab3a1667ce2619cec51b6e4276d27b80d53 100644 (file)
@@ -4,6 +4,12 @@
        * i386_init.c: Hook i386_unwind.
        * Makefile.am (i386_SRCS): Add i386_unwind.c
 
+2017-02-09  Ulf Hermann  <ulf.hermann@qt.io>
+
+       * aarch64_unwind.c: New file
+       * Makefile.am (aarch64_SRCS): Add aarch64_unwind.c
+       * aarch64_init.c (aarch64_init): Hook aarch64_unwind
+
 2017-02-09  Ulf Hermann  <ulf.hermann@qt.io>
 
        * x86_64_unwind.c: New file
index 22eb6ac6f1cf121ddd018dd61a7c51c8b6132990..ff80a82c07e3a5f76c56edaffe16578e30259531 100644 (file)
@@ -80,7 +80,7 @@ am_libebl_arm_pic_a_OBJECTS = $(arm_SRCS:.c=.os)
 
 aarch64_SRCS = aarch64_init.c aarch64_regs.c aarch64_symbol.c  \
               aarch64_corenote.c aarch64_retval.c aarch64_cfi.c \
-              aarch64_initreg.c
+              aarch64_initreg.c aarch64_unwind.c
 libebl_aarch64_pic_a_SOURCES = $(aarch64_SRCS)
 am_libebl_aarch64_pic_a_OBJECTS = $(aarch64_SRCS:.c=.os)
 
index 6395f1171526e07106a15e2d68ddfc4529f0fafa..08664943d08cb4a12176f6f6a5254c6144308820 100644 (file)
@@ -63,6 +63,7 @@ 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, unwind);
 
   return MODVERSION;
 }
diff --git a/backends/aarch64_unwind.c b/backends/aarch64_unwind.c
new file mode 100644 (file)
index 0000000..e0a7e96
--- /dev/null
@@ -0,0 +1,83 @@
+/* Get previous frame state for an existing frame state.
+   Copyright (C) 2016 The Qt Company Ltd.
+   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
+
+#define BACKEND aarch64_
+#define FP_REG 29
+#define LR_REG 30
+#define SP_REG 31
+#define FP_OFFSET 0
+#define LR_OFFSET 8
+#define SP_OFFSET 16
+
+#include "libebl_CPU.h"
+
+/* There was no CFI. Maybe we happen to have a frame pointer and can unwind from that?  */
+
+bool
+EBLHOOK(unwind) (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc __attribute__ ((unused)),
+                 ebl_tid_registers_t *setfunc, ebl_tid_registers_get_t *getfunc,
+                 ebl_pid_memory_read_t *readfunc, void *arg,
+                 bool *signal_framep __attribute__ ((unused)))
+{
+  Dwarf_Word fp, lr, sp;
+
+  if (!getfunc(LR_REG, 1, &lr, arg))
+    return false;
+
+  if (lr == 0 || !setfunc(-1, 1, &lr, arg))
+    return false;
+
+  if (!getfunc(FP_REG, 1, &fp, arg))
+    fp = 0;
+
+  if (!getfunc(SP_REG, 1, &sp, arg))
+    sp = 0;
+
+  Dwarf_Word newLr, newFp, newSp;
+
+  if (!readfunc(fp + LR_OFFSET, &newLr, arg))
+    newLr = 0;
+
+  if (!readfunc(fp + FP_OFFSET, &newFp, arg))
+    newFp = 0;
+
+  newSp = fp + SP_OFFSET;
+
+  // These are not fatal if they don't work. They will just prevent unwinding at the next frame.
+  setfunc(LR_REG, 1, &newLr, arg);
+  setfunc(FP_REG, 1, &newFp, arg);
+  setfunc(SP_REG, 1, &newSp, arg);
+
+  // If the fp is invalid, we might still have a valid lr.
+  // But if the fp is valid, then the stack should be moving in the right direction.
+  return fp == 0 || newSp > sp;
+}
index 81b5f7a26bef7eb6d5e37b71f6fd3cef54117750..5b0d486e28b049fdc0bfb47dbce935c3db801e0a 100644 (file)
@@ -1,3 +1,13 @@
+2017-02-13  Ulf Hermann  <ulf.hermann@qt.io>
+           Mark Wielaard  <mark@klomp.org>
+
+       * Makefile.am: Add test for unwinding with frame pointers on aarch64
+       * backtrace.aarch64.fp.core.bz2: New file
+       * backtrace.aarch64.fp.exec.bz2: New file
+       * run-backtrace-fp-core-aarch64.sh: New file
+       * backtrace-subr.sh (check_err): Allow Invalid register.
+       * backtrace.c (callback_verify): Allow duplicate_sigusr2 frames.
+
 2017-04-06  Mark Wielaard  <mark@klomp.org>
 
        * run-backtrace-fp-core-i386.sh: New test.
index 2c82bfd8f33276dcfff2c18732cbd0031226507a..3a12fe3a8a6d08d73d9bdfa225a22d587554bfda 100644 (file)
@@ -116,6 +116,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
        run-backtrace-native-biarch.sh run-backtrace-native-core.sh \
        run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \
        run-backtrace-fp-core-x86_64.sh \
+       run-backtrace-fp-core-aarch64.sh \
        run-backtrace-core-x32.sh \
        run-backtrace-core-i386.sh run-backtrace-fp-core-i386.sh \
        run-backtrace-core-ppc.sh \
@@ -297,6 +298,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
             run-backtrace-core-x86_64.sh run-backtrace-core-i386.sh \
             run-backtrace-fp-core-x86_64.sh \
             run-backtrace-core-x32.sh \
+            run-backtrace-fp-core-aarch64.sh \
+            backtrace.aarch64.fp.core.bz2 backtrace.aarch64.fp.exec.bz2 \
             backtrace-subr.sh backtrace.i386.core.bz2 backtrace.i386.exec.bz2 \
             run-backtrace-fp-core-i386.sh \
             backtrace.i386.fp.core.bz2 backtrace.i386.fp.exec.bz2 \
index a303e32c8c2ef3dc6fc69675ac3a6db7f0ae0cab..9731c43aadfdb31d62c96bce1f31623f49ed9047 100644 (file)
@@ -59,7 +59,7 @@ check_backtracegen()
 # Ignore it here as it is a bug of OS, not a bug of elfutils.
 check_err()
 {
-  if [ $(egrep -v <$1 'dwfl_thread_getframes: (No DWARF information found|no matching address range|address out of range)$' \
+  if [ $(egrep -v <$1 'dwfl_thread_getframes: (No DWARF information found|no matching address range|address out of range|Invalid register)$' \
          | wc -c) \
        -eq 0 ]
   then
diff --git a/tests/backtrace.aarch64.fp.core.bz2 b/tests/backtrace.aarch64.fp.core.bz2
new file mode 100644 (file)
index 0000000..ff86788
Binary files /dev/null and b/tests/backtrace.aarch64.fp.core.bz2 differ
diff --git a/tests/backtrace.aarch64.fp.exec.bz2 b/tests/backtrace.aarch64.fp.exec.bz2
new file mode 100644 (file)
index 0000000..9d06db1
Binary files /dev/null and b/tests/backtrace.aarch64.fp.exec.bz2 differ
index 1ff6353c2d6f1fa595ad835ff71527d03e613df7..21abe8af1cc33dbc28daa05d7eef01f9b45641fb 100644 (file)
@@ -90,6 +90,10 @@ callback_verify (pid_t tid, unsigned frameno, Dwarf_Addr pc,
       return;
     }
   Dwfl_Module *mod;
+  /* See case 4. Special case to help out simple frame pointer unwinders. */
+  static bool duplicate_sigusr2 = false;
+  if (duplicate_sigusr2)
+    frameno--;
   static bool reduce_frameno = false;
   if (reduce_frameno)
     frameno--;
@@ -125,6 +129,14 @@ callback_verify (pid_t tid, unsigned frameno, Dwarf_Addr pc,
        }
       /* FALLTHRU */
     case 4:
+      /* Some simple frame unwinders get this wrong and think sigusr2
+        is calling itself again. Allow it and just pretend there is
+        an extra sigusr2 frame. */
+      if (symname != NULL && strcmp (symname, "sigusr2") == 0)
+       {
+         duplicate_sigusr2 = true;
+         break;
+       }
       assert (symname != NULL && strcmp (symname, "stdarg") == 0);
       break;
     case 5:
diff --git a/tests/run-backtrace-fp-core-aarch64.sh b/tests/run-backtrace-fp-core-aarch64.sh
new file mode 100755 (executable)
index 0000000..fda88d3
--- /dev/null
@@ -0,0 +1,28 @@
+#! /bin/bash
+# Copyright (C) 2017 The Qt Company
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/backtrace-subr.sh
+
+# The binary is generated by compiling with eh_frame CFI, but with frame
+# pointers.
+#
+# gcc -static -O2 -fno-omit-frame-pointer -fno-asynchronous-unwind-tables \
+#     -D_GNU_SOURCE -pthread -o tests/backtrace.aarch64.fp.exec -I. -Ilib \
+#     tests/backtrace-child.c
+# The core is generated by calling the binary with --gencore
+
+check_core aarch64.fp