This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [tests patchv2] FYI unwinder: tests/ update


Hi Mark,

jankratochvil/unwindx86

(patch below is without the binary blobs)


On Mon, 02 Dec 2013 16:01:53 +0100, Mark Wielaard wrote:
> Yes, it works currently. But I rather not have other people rely on
> being able to mix report calls with other dwfl calls. So thanks for
> adding the comment that it violates the libdwfl interface. Now when
> people see this code and do something similar they have at least been
> warned it might break in the future.

BTW such policy should be easily enforceable by some asserts/errors in
elfutils.


> There is some middle ground between one giant testcase that does
> everything and lots of little test files that only differ in one line. I
> am happy you split the tests a bit and added comments explaining what
> which part does. But I think you overdone it in this particular case.
> Because these test the same thing. A backtrace over a core file. I would
> have grouped those together. But if you feel this split is easier then
> go with it. It just isn't how I would have done it.

OK, thanks, keeping it as is.

I like to be able to easily figure out from FAILed tests which part of the
codebase is wrong.  Whether it is backend (x86_64 vs. i386 vs. ppc etc.) or
whether it is in "frontend" - PID handling vs. core handling in this case.
It is useful when handling backport patches, multiple branches etc.


> Maybe I am reading backtrace-data.c wrongly. But I don't see it stop for
> main. If it would then indeed it doesn't seem necessary.
> backtrace-dwarf.c does seem to have this "stop at main" logic though. So
> maybe we are talking about different tests?

OK, you are right.  So I have put there:

	. $srcdir/backtrace-subr.sh

	tempfiles data.{bt,err}
	(set +ex; testrun ${abs_builddir}/backtrace-data 1>data.bt 2>data.err; true)
	cat data.{bt,err}
	check_unsupported data.err data
	check_all data.{bt,err} data


> Yes. I didn't have anything that looked like it must be changed. However
> you resolve any remaining issues is fine with me.

IIUC the tests/ patch is approved now?


Thanks,
Jan


8dcc79ecf4ce29a15da2db149fb17d76c1076b95 Mon Sep 17 00:00:00 2001
From: Jan Kratochvil <jan.kratochvil@redhat.com>
Date: Thu, 30 May 2013 14:37:38 +0200
Subject: [PATCH] Unwinder for x86*.

./
2013-11-22  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* configure.ac (CC_BIARCH): Remove AS_IF for it.

m4/
2013-11-22  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* biarch.m4 (utrace_BIARCH): Call AC_MSG_WARN if !BIARCH.

tests/
2013-11-22  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* Makefile.am (check_PROGRAMS): Add backtrace, backtrace-child,
	backtrace-data and backtrace-dwarf.
	(BUILT_SOURCES, clean-local, backtrace-child-biarch): New.
	(TESTS): Add run-backtrace-native.sh, run-backtrace-data.sh,
	run-backtrace-dwarf.sh, run-backtrace-native-biarch.sh,
	run-backtrace-native-core.sh, run-backtrace-native-core-biarch.sh,
	run-backtrace-core-x86_64.sh and run-backtrace-core-i386.sh.
	<!BIARCH> Add export of ELFUTILS_DISABLE_BIARCH.
	(EXTRA_DIST): Add run-backtrace-data.sh, run-backtrace-dwarf.sh,
	cleanup-13.c, run-backtrace-native.sh, run-backtrace-native-biarch.sh,
	run-backtrace-native-core.sh, run-backtrace-native-core-biarch.sh,
	run-backtrace-core-x86_64.sh, run-backtrace-core-i386.sh,
	backtrace-subr.sh, backtrace.i386.core.bz2, backtrace.i386.exec.bz2,
	backtrace.x86_64.core.bz2, backtrace.x86_64.exec.bz2.
	(backtrace_LDADD, backtrace_child_CFLAGS, backtrace_child_LDFLAGS)
	(backtrace_data_LDADD, backtrace_dwarf_CFLAGS, backtrace_dwarf_LDADD):
	New.
	* backtrace-child.c: New file.
	* backtrace-data.c: New file.
	* backtrace-dwarf.c: New file.
	* backtrace-subr.sh: New file.
	* backtrace.c: New file.
	* cleanup-13.c: New file.
	* backtrace.i386.core.bz2: New file.
	* backtrace.i386.exec.bz2: New file.
	* backtrace.x86_64.core.bz2: New file.
	* backtrace.x86_64.exec.bz2: New file.
	* run-backtrace-core-i386.sh: New file.
	* run-backtrace-core-x86_64.sh: New file.
	* run-backtrace-native-biarch.sh: New file.
	* run-backtrace-native-core-biarch.sh: New file.
	* run-backtrace-native-core.sh: New file.
	* run-backtrace-native.sh: New file.
	* run-backtrace-data.sh: New file.
	* run-backtrace-dwarf.sh: New file.

Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
---
 configure.ac                              |   4 +-
 m4/biarch.m4                              |   4 +-
 tests/Makefile.am                         |  40 ++-
 tests/backtrace-child.c                   | 221 +++++++++++++++
 tests/backtrace-data.c                    | 325 +++++++++++++++++++++
 tests/backtrace-dwarf.c                   | 195 +++++++++++++
 tests/backtrace-subr.sh                   | 115 ++++++++
 tests/backtrace.c                         | 452 ++++++++++++++++++++++++++++++
 tests/backtrace.i386.core.bz2             | Bin 0 -> 8777 bytes
 tests/backtrace.i386.exec.bz2             | Bin 0 -> 383356 bytes
 tests/backtrace.x86_64.core.bz2           | Bin 0 -> 11115 bytes
 tests/backtrace.x86_64.exec.bz2           | Bin 0 -> 401581 bytes
 tests/cleanup-13.c                        | 334 ++++++++++++++++++++++
 tests/run-backtrace-core-i386.sh          |  20 ++
 tests/run-backtrace-core-x86_64.sh        |  20 ++
 tests/run-backtrace-data.sh               |  24 ++
 tests/run-backtrace-dwarf.sh              |  20 ++
 tests/run-backtrace-native-biarch.sh      |  24 ++
 tests/run-backtrace-native-core-biarch.sh |  24 ++
 tests/run-backtrace-native-core.sh        |  20 ++
 tests/run-backtrace-native.sh             |  20 ++
 21 files changed, 1855 insertions(+), 7 deletions(-)
 create mode 100644 tests/backtrace-child.c
 create mode 100644 tests/backtrace-data.c
 create mode 100644 tests/backtrace-dwarf.c
 create mode 100644 tests/backtrace-subr.sh
 create mode 100644 tests/backtrace.c
 create mode 100644 tests/backtrace.i386.core.bz2
 create mode 100644 tests/backtrace.i386.exec.bz2
 create mode 100644 tests/backtrace.x86_64.core.bz2
 create mode 100644 tests/backtrace.x86_64.exec.bz2
 create mode 100644 tests/cleanup-13.c
 create mode 100755 tests/run-backtrace-core-i386.sh
 create mode 100755 tests/run-backtrace-core-x86_64.sh
 create mode 100755 tests/run-backtrace-data.sh
 create mode 100755 tests/run-backtrace-dwarf.sh
 create mode 100755 tests/run-backtrace-native-biarch.sh
 create mode 100755 tests/run-backtrace-native-core-biarch.sh
 create mode 100755 tests/run-backtrace-native-core.sh
 create mode 100755 tests/run-backtrace-native.sh

diff --git a/configure.ac b/configure.ac
index 99b74ae..72fb3e8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -332,9 +332,7 @@ AC_CHECK_SIZEOF(long)
 # Likewise in a 32-bit build on a host where $CC -m64 works.
 utrace_BIARCH
 # `$utrace_biarch' will be `-m64' even on an uniarch i386 machine.
-AS_IF([test $utrace_cv_cc_biarch = yes],
-      [CC_BIARCH="$CC $utrace_biarch"],
-      [CC_BIARCH="$CC"])
+CC_BIARCH="$CC $utrace_biarch"
 AC_SUBST([CC_BIARCH])
 
 AC_OUTPUT
diff --git a/m4/biarch.m4 b/m4/biarch.m4
index a15323e..04c8dba 100644
--- a/m4/biarch.m4
+++ b/m4/biarch.m4
@@ -41,5 +41,7 @@ save_CC="$CC"
 CC="$biarch_CC"
 AC_RUN_IFELSE([AC_LANG_PROGRAM([], [])],
 	      utrace_cv_cc_biarch=yes, utrace_cv_cc_biarch=no)
-CC="$save_CC"])], [utrace_cv_cc_biarch=no])])
+CC="$save_CC"])], [utrace_cv_cc_biarch=no])
+AS_IF([test $utrace_cv_cc_biarch != yes], [dnl
+AC_MSG_WARN([not running biarch tests, $biarch_CC does not work])])])
 AM_CONDITIONAL(BIARCH, [test $utrace_cv_cc_biarch = yes])])
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cedb684..a74830b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,10 +52,23 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
 		  test-flag-nobits dwarf-getstring rerequest_tag \
 		  alldts md5-sha1-test typeiter typeiter2 low_high_pc \
 		  test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
-		  dwfl-report-elf-align varlocs
+		  dwfl-report-elf-align varlocs backtrace backtrace-child \
+		  backtrace-data backtrace-dwarf
 asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
 	    asm-tst6 asm-tst7 asm-tst8 asm-tst9
 
+if BIARCH
+check_PROGRAMS += backtrace-child-biarch
+endif
+
+# Substitute $(COMPILE).
+backtrace-child-biarch: backtrace-child.c
+	$(CC_BIARCH) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+		     $(AM_CPPFLAGS) $(CPPFLAGS) \
+		     $(AM_CFLAGS) $(CFLAGS) $(backtrace_child_CFLAGS) \
+		     $(AM_LDFLAGS) $(LDFLAGS) $(backtrace_child_LDFLAGS) \
+		     -o $@ $<
+
 TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
 	update1 update2 update3 update4 \
 	run-show-die-info.sh run-get-files.sh run-get-lines.sh \
@@ -89,7 +102,15 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
 	run-test-archive64.sh run-readelf-vmcoreinfo.sh \
 	run-readelf-mixed-corenote.sh run-dwfllines.sh \
 	run-dwfl-report-elf-align.sh run-addr2line-test.sh \
-	run-addr2line-i-test.sh run-varlocs.sh run-funcretval.sh
+	run-addr2line-i-test.sh run-varlocs.sh run-funcretval.sh \
+	run-backtrace-native.sh run-backtrace-data.sh run-backtrace-dwarf.sh \
+	run-backtrace-native-biarch.sh run-backtrace-native-core.sh \
+	run-backtrace-native-core-biarch.sh run-backtrace-core-x86_64.sh \
+	run-backtrace-core-i386.sh
+
+if !BIARCH
+export ELFUTILS_DISABLE_BIARCH = 1
+endif
 
 if !STANDALONE
 check_PROGRAMS += msg_tst md5-sha1-test
@@ -218,7 +239,13 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
 	     testfile_entry_value.c testfile_entry_value.bz2 \
 	     testfile_implicit_value.c testfile_implicit_value.bz2 \
 	     testfile_aarch64_core.bz2 \
-	     run-funcretval.sh funcretval_test.c funcretval_test_aarch64.bz2
+	     run-funcretval.sh funcretval_test.c funcretval_test_aarch64.bz2 \
+	     run-backtrace-data.sh run-backtrace-dwarf.sh cleanup-13.c \
+	     run-backtrace-native.sh run-backtrace-native-biarch.sh \
+	     run-backtrace-native-core.sh run-backtrace-native-core-biarch.sh \
+	     run-backtrace-core-x86_64.sh run-backtrace-core-i386.sh \
+	     backtrace-subr.sh backtrace.i386.core.bz2 backtrace.i386.exec.bz2 \
+	     backtrace.x86_64.core.bz2 backtrace.x86_64.exec.bz2
 
 if USE_VALGRIND
 valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
@@ -347,6 +374,13 @@ dwflsyms_LDADD = $(libdw) $(libelf) $(libmudflap)
 dwfllines_LDADD = $(libdw) $(libelf) $(libmudflap)
 dwfl_report_elf_align_LDADD = $(libdw) $(libmudflap)
 varlocs_LDADD = $(libdw) $(libelf) $(libmudflap)
+backtrace_LDADD = $(libdw) $(libelf) $(libmudflap)
+# backtrace-child-biarch also uses those *_CFLAGS and *_LDLAGS variables:
+backtrace_child_CFLAGS = -fPIE
+backtrace_child_LDFLAGS = -pie -pthread
+backtrace_data_LDADD = $(libdw) $(libelf) $(libmudflap)
+backtrace_dwarf_CFLAGS = -Wno-unused-parameter
+backtrace_dwarf_LDADD = $(libdw) $(libelf) $(libmudflap)
 
 if GCOV
 check: check-am coverage
diff --git a/tests/backtrace-child.c b/tests/backtrace-child.c
new file mode 100644
index 0000000..2bc0ede
--- /dev/null
+++ b/tests/backtrace-child.c
@@ -0,0 +1,221 @@
+/* Test child for parent backtrace test.
+   Copyright (C) 2013 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 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/>.  */
+
+/* Command line syntax: ./backtrace-child [--ptraceme|--gencore]
+   --ptraceme will call ptrace (PTRACE_TRACEME) in the two threads.
+   --gencore will call abort () at its end.
+   Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
+   On x86_64 only:
+     PC will get changed to function 'jmp' by backtrace.c function
+     prepare_thread.  Then SIGUSR2 will be signalled to backtrace-child
+     which will invoke function sigusr2.
+     This is all done so that signal interrupts execution of the very first
+     instruction of a function.  Properly handled unwind should not slip into
+     the previous unrelated function.
+     The tested functionality is arch-independent but the code reproducing it
+     has to be arch-specific.
+   On non-x86_64:
+     sigusr2 gets called by normal function call from function stdarg.
+   On any arch then sigusr2 calls raise (SIGUSR1) for --ptraceme.
+   abort () is called otherwise, expected for --gencore core dump.
+
+   Expected x86_64 output:
+   TID 10276:
+   # 0 0x7f7ab61e9e6b      raise
+   # 1 0x7f7ab661af47 - 1  main
+   # 2 0x7f7ab5e3bb45 - 1  __libc_start_main
+   # 3 0x7f7ab661aa09 - 1  _start
+   TID 10278:
+   # 0 0x7f7ab61e9e6b      raise
+   # 1 0x7f7ab661ab3c - 1  sigusr2
+   # 2 0x7f7ab5e4fa60      __restore_rt
+   # 3 0x7f7ab661ab47      jmp
+   # 4 0x7f7ab661ac92 - 1  stdarg
+   # 5 0x7f7ab661acba - 1  backtracegen
+   # 6 0x7f7ab661acd1 - 1  start
+   # 7 0x7f7ab61e2c53 - 1  start_thread
+   # 8 0x7f7ab5f0fdbd - 1  __clone
+
+   Expected non-x86_64 (i386) output; __kernel_vsyscall are skipped if found:
+   TID 10408:
+   # 0 0xf779f430          __kernel_vsyscall
+   # 1 0xf7771466 - 1      raise
+   # 2 0xf77c1d07 - 1      main
+   # 3 0xf75bd963 - 1      __libc_start_main
+   # 4 0xf77c1761 - 1      _start
+   TID 10412:
+   # 0 0xf779f430          __kernel_vsyscall
+   # 1 0xf7771466 - 1      raise
+   # 2 0xf77c18f4 - 1      sigusr2
+   # 3 0xf77c1a10 - 1      stdarg
+   # 4 0xf77c1a2c - 1      backtracegen
+   # 5 0xf77c1a48 - 1      start
+   # 6 0xf77699da - 1      start_thread
+   # 7 0xf769bbfe - 1      __clone
+   */
+
+#include <config.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define NOINLINE_NOCLONE __attribute__ ((noinline, noclone))
+#else
+#define NOINLINE_NOCLONE __attribute__ ((noinline))
+#endif
+
+#define NORETURN __attribute__ ((noreturn))
+#define UNUSED __attribute__ ((unused))
+#define USED __attribute__ ((used))
+
+static int ptraceme, gencore;
+
+/* Execution will arrive here from jmp by an artificial ptrace-spawn signal.  */
+
+static void
+sigusr2 (int signo)
+{
+  assert (signo == SIGUSR2);
+  if (! gencore)
+    {
+      raise (SIGUSR1);
+      /* It should not be reached.  */
+      abort ();
+    }
+  /* Here we dump the core for --gencore.  */
+  raise (SIGABRT);
+}
+
+static NOINLINE_NOCLONE void
+dummy1 (void)
+{
+  asm volatile ("");
+}
+
+#ifdef __x86_64__
+static NOINLINE_NOCLONE USED void
+jmp (void)
+{
+  /* Not reached, signal will get ptrace-spawn to jump into sigusr2.  */
+  abort ();
+}
+#endif
+
+static NOINLINE_NOCLONE void
+dummy2 (void)
+{
+  asm volatile ("");
+}
+
+static NOINLINE_NOCLONE NORETURN void
+stdarg (int f UNUSED, ...)
+{
+  sighandler_t sigusr2_orig = signal (SIGUSR2, sigusr2);
+  assert (sigusr2_orig == SIG_DFL);
+  errno = 0;
+  if (ptraceme)
+    {
+      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      assert_perror (errno);
+      assert (l == 0);
+    }
+#ifdef __x86_64__
+  if (! gencore)
+    {
+      /* Execution will get PC patched into function jmp.  */
+      raise (SIGUSR1);
+    }
+#endif
+  sigusr2 (SIGUSR2);
+  /* Not reached.  */
+  abort ();
+}
+
+static NOINLINE_NOCLONE void
+dummy3 (void)
+{
+  asm volatile ("");
+}
+
+static NOINLINE_NOCLONE void
+backtracegen (void)
+{
+  stdarg (1);
+  /* Here should be no instruction after the stdarg call as it is noreturn
+     function.  It must be stdarg so that it is a call and not jump (jump as
+     a tail-call).  */
+}
+
+static NOINLINE_NOCLONE void
+dummy4 (void)
+{
+  asm volatile ("");
+}
+
+static void *
+start (void *arg UNUSED)
+{
+  backtracegen ();
+  /* Not reached.  */
+  abort ();
+}
+
+int
+main (int argc UNUSED, char **argv)
+{
+  setbuf (stdout, NULL);
+  assert (*argv++);
+  ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
+  argv += ptraceme;
+  gencore = (*argv && strcmp (*argv, "--gencore") == 0);
+  argv += gencore;
+  assert (!*argv);
+  /* These dummy* functions are there so that each of their surrounding
+     functions has some unrelated code around.  The purpose of some of the
+     tests is verify unwinding the very first / after the very last instruction
+     does not inappropriately slip into the unrelated code around.  */
+  dummy1 ();
+  dummy2 ();
+  dummy3 ();
+  dummy4 ();
+  if (gencore)
+    printf ("%ld\n", (long) getpid ());
+  pthread_t thread;
+  int i = pthread_create (&thread, NULL, start, NULL);
+  // pthread_* functions do not set errno.
+  assert (i == 0);
+  if (ptraceme)
+    {
+      errno = 0;
+      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      assert_perror (errno);
+      assert (l == 0);
+    }
+  if (gencore)
+    pthread_join (thread, NULL);
+  else
+    raise (SIGUSR2);
+  /* Not reached.  */
+  abort ();
+}
diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c
new file mode 100644
index 0000000..f54263b
--- /dev/null
+++ b/tests/backtrace-data.c
@@ -0,0 +1,325 @@
+/* Test custom provided Dwfl_Thread_Callbacks vector.
+   Copyright (C) 2013 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 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/>.  */
+
+/* Test custom provided Dwfl_Thread_Callbacks vector.  Test mimics what
+   a ptrace based vector would do.  */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <dwarf.h>
+#include <sys/resource.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <string.h>
+#include ELFUTILS_HEADER(dwfl)
+
+#ifndef __x86_64__
+
+int
+main (void)
+{
+  return 77;
+}
+
+#else /* __x86_64__ */
+
+/* The only arch specific code is set_initial_registers.  */
+
+static int
+find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+	  void **userdata __attribute__ ((unused)),
+	  const char *modname __attribute__ ((unused)),
+	  Dwarf_Addr base __attribute__ ((unused)),
+	  char **file_name __attribute__ ((unused)),
+	  Elf **elfp __attribute__ ((unused)))
+{
+  /* Not used as modules are reported explicitly.  */
+  assert (0);
+}
+
+static bool
+memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
+	     void *dwfl_arg __attribute__ ((unused)))
+{
+  pid_t child = dwfl_pid (dwfl);
+
+  errno = 0;
+  long l = ptrace (PTRACE_PEEKDATA, child, (void *) (uintptr_t) addr, NULL);
+  assert_perror (errno);
+  *result = l;
+
+  /* We could also return false for failed ptrace.  */
+  return true;
+}
+
+/* Return filename and VMA address *BASEP where its mapping starts which
+   contains ADDR.  */
+
+static char *
+maps_lookup (pid_t pid, Dwarf_Addr addr, GElf_Addr *basep)
+{
+  char *fname;
+  int i = asprintf (&fname, "/proc/%ld/maps", (long) pid);
+  assert_perror (errno);
+  assert (i > 0);
+  FILE *f = fopen (fname, "r");
+  assert_perror (errno);
+  assert (f);
+  free (fname);
+  for (;;)
+    {
+      // 37e3c22000-37e3c23000 rw-p 00022000 00:11 49532 /lib64/ld-2.14.90.so */
+      unsigned long start, end, offset;
+      i = fscanf (f, "%lx-%lx %*s %lx %*x:%*x %*x", &start, &end, &offset);
+      assert_perror (errno);
+      assert (i == 3);
+      char *filename = strdup ("");
+      assert (filename);
+      size_t filename_len = 0;
+      for (;;)
+	{
+	  int c = fgetc (f);
+	  assert (c != EOF);
+	  if (c == '\n')
+	    break;
+	  if (c == ' ' && *filename == '\0')
+	    continue;
+	  filename = realloc (filename, filename_len + 2);
+	  assert (filename);
+	  filename[filename_len++] = c;
+	  filename[filename_len] = '\0';
+	}
+      if (start <= addr && addr < end)
+	{
+	  i = fclose (f);
+	  assert_perror (errno);
+	  assert (i == 0);
+
+	  *basep = start - offset;
+	  return filename;
+	}
+      free (filename);
+    }
+}
+
+/* Add module containing ADDR to the DWFL address space.
+
+   dwfl_report_elf call here violates Dwfl manipulation as one should call
+   dwfl_report only between dwfl_report_begin_add and dwfl_report_end.
+   Current elfutils implementation does not mind as dwfl_report_begin_add is
+   empty.  */
+
+static Dwfl_Module *
+report_module (Dwfl *dwfl, pid_t child, Dwarf_Addr addr)
+{
+  GElf_Addr base;
+  char *long_name = maps_lookup (child, addr, &base);
+  Dwfl_Module *mod = dwfl_report_elf (dwfl, long_name, long_name, -1,
+				      base, false /* add_p_vaddr */);
+  assert (mod);
+  free (long_name);
+  assert (dwfl_addrmodule (dwfl, addr) == mod);
+  return mod;
+}
+
+static pid_t
+next_thread (Dwfl *dwfl, void *dwfl_arg __attribute__ ((unused)),
+	     void **thread_argp)
+{
+  if (*thread_argp != NULL)
+    return 0;
+  /* Put arbitrary non-NULL value into *THREAD_ARGP as a marker so that this
+     function returns non-zero PID only once.  */
+  *thread_argp = thread_argp;
+  return dwfl_pid (dwfl);
+}
+
+static bool
+set_initial_registers (Dwfl_Thread *thread,
+		       void *thread_arg __attribute__ ((unused)))
+{
+  pid_t child = dwfl_pid (dwfl_thread_dwfl (thread));
+
+  struct user_regs_struct user_regs;
+  long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
+  assert_perror (errno);
+  assert (l == 0);
+
+  Dwarf_Word dwarf_regs[17];
+  dwarf_regs[0] = user_regs.rax;
+  dwarf_regs[1] = user_regs.rdx;
+  dwarf_regs[2] = user_regs.rcx;
+  dwarf_regs[3] = user_regs.rbx;
+  dwarf_regs[4] = user_regs.rsi;
+  dwarf_regs[5] = user_regs.rdi;
+  dwarf_regs[6] = user_regs.rbp;
+  dwarf_regs[7] = user_regs.rsp;
+  dwarf_regs[8] = user_regs.r8;
+  dwarf_regs[9] = user_regs.r9;
+  dwarf_regs[10] = user_regs.r10;
+  dwarf_regs[11] = user_regs.r11;
+  dwarf_regs[12] = user_regs.r12;
+  dwarf_regs[13] = user_regs.r13;
+  dwarf_regs[14] = user_regs.r14;
+  dwarf_regs[15] = user_regs.r15;
+  dwarf_regs[16] = user_regs.rip;
+  bool ok = dwfl_thread_state_registers (thread, 0, 17, dwarf_regs);
+  assert (ok);
+
+  /* x86_64 has PC contained in its CFI subset of DWARF register set so
+     elfutils will figure out the real PC value from REGS.
+     So no need to explicitly call dwfl_thread_state_register_pc.  */
+
+  return true;
+}
+
+static const Dwfl_Thread_Callbacks callbacks =
+{
+  next_thread,
+  memory_read,
+  set_initial_registers,
+  NULL, /* detach */
+  NULL, /* thread_detach */
+};
+
+static int
+frame_callback (Dwfl_Frame *state, void *arg)
+{
+  unsigned *framenop = arg;
+  Dwarf_Addr pc;
+  bool isactivation;
+  if (! dwfl_frame_pc (state, &pc, &isactivation))
+    {
+      error (1, 0, "%s", dwfl_errmsg (-1));
+      return 1;
+    }
+  Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+  /* Get PC->SYMNAME.  */
+  Dwfl *dwfl = dwfl_thread_dwfl (dwfl_frame_thread (state));
+  Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+  if (mod == NULL)
+    mod = report_module (dwfl, dwfl_pid (dwfl), pc_adjusted);
+  const char *symname = NULL;
+  symname = dwfl_module_addrname (mod, pc_adjusted);
+
+  printf ("#%2u %#" PRIx64 "%4s\t%s\n", (*framenop)++, (uint64_t) pc,
+	  ! isactivation ? "- 1" : "", symname);
+  return DWARF_CB_OK;
+}
+
+static int
+thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__ ((unused)))
+{
+  unsigned frameno = 0;
+  switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
+    {
+    case 0:
+      break;
+    case -1:
+      error (1, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+    default:
+      abort ();
+    }
+  return DWARF_CB_OK;
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv __attribute__ ((unused)))
+{
+  /* We use no threads here which can interfere with handling a stream.  */
+  __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+  __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+  __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  elf_version (EV_CURRENT);
+
+  pid_t child = fork ();
+  switch (child)
+  {
+    case -1:
+      assert_perror (errno);
+      assert (0);
+    case 0:;
+      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      assert_perror (errno);
+      assert (l == 0);
+      raise (SIGUSR1);
+      assert (0);
+    default:
+      break;
+  }
+
+  int status;
+  pid_t pid = waitpid (child, &status, 0);
+  assert_perror (errno);
+  assert (pid == child);
+  assert (WIFSTOPPED (status));
+  assert (WSTOPSIG (status) == SIGUSR1);
+
+  static char *debuginfo_path;
+  static const Dwfl_Callbacks offline_callbacks =
+    {
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .debuginfo_path = &debuginfo_path,
+      .section_address = dwfl_offline_section_address,
+      .find_elf = find_elf,
+    };
+  Dwfl *dwfl = dwfl_begin (&offline_callbacks);
+  assert (dwfl);
+
+  struct user_regs_struct user_regs;
+  long l = ptrace (PTRACE_GETREGS, child, NULL, &user_regs);
+  assert_perror (errno);
+  assert (l == 0);
+  report_module (dwfl, child, user_regs.rip);
+
+  bool ok = dwfl_attach_state (dwfl, EM_NONE, child, &callbacks, NULL);
+  assert (ok);
+
+  /* Multiple threads are not handled here.  */
+  int err = dwfl_getthreads (dwfl, thread_callback, NULL);
+  assert (! err);
+
+  dwfl_end (dwfl);
+  kill (child, SIGKILL);
+  pid = waitpid (child, &status, 0);
+  assert_perror (errno);
+  assert (pid == child);
+  assert (WIFSIGNALED (status));
+  assert (WTERMSIG (status) == SIGKILL);
+
+  return EXIT_SUCCESS;
+}
+
+#endif /* x86_64 */
diff --git a/tests/backtrace-dwarf.c b/tests/backtrace-dwarf.c
new file mode 100644
index 0000000..92c892d
--- /dev/null
+++ b/tests/backtrace-dwarf.c
@@ -0,0 +1,195 @@
+/* Test program for unwinding of complicated DWARF expressions.
+   Copyright (C) 2013 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 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/>.  */
+
+#include <config.h>
+#include <assert.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/ptrace.h>
+#include ELFUTILS_HEADER(dwfl)
+
+static void cleanup_13_abort (void);
+#define main cleanup_13_main
+#include "cleanup-13.c"
+#undef main
+
+static void
+report_pid (Dwfl *dwfl, pid_t pid)
+{
+  int result = dwfl_linux_proc_report (dwfl, pid);
+  if (result < 0)
+    error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+  else if (result > 0)
+    error (2, result, "dwfl_linux_proc_report");
+
+  if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+    error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+}
+
+static Dwfl *
+pid_to_dwfl (pid_t pid)
+{
+  static char *debuginfo_path;
+  static const Dwfl_Callbacks proc_callbacks =
+    {
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .debuginfo_path = &debuginfo_path,
+
+      .find_elf = dwfl_linux_proc_find_elf,
+    };
+  Dwfl *dwfl = dwfl_begin (&proc_callbacks);
+  if (dwfl == NULL)
+    error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+  report_pid (dwfl, pid);
+  return dwfl;
+}
+
+static const char *executable = "/proc/self/exe";
+
+static int
+find_elf (Dwfl_Module *mod, void **userdata, const char *modname,
+	  Dwarf_Addr base, char **file_name, Elf **elfp)
+{
+  if (executable && modname != NULL
+      && (strcmp (modname, "[exe]") == 0 || strcmp (modname, "[pie]") == 0))
+    {
+      char *executable_dup = strdup (executable);
+      if (executable_dup)
+	{
+	  free (*file_name);
+	  *file_name = executable_dup;
+	  return -1;
+	}
+    }
+  return dwfl_build_id_find_elf (mod, userdata, modname, base, file_name, elfp);
+}
+
+static Dwfl *
+dwfl_offline (void)
+{
+  static char *debuginfo_path;
+  static const Dwfl_Callbacks offline_callbacks =
+    {
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .debuginfo_path = &debuginfo_path,
+
+      .section_address = dwfl_offline_section_address,
+
+      /* We use this table for core files too.  */
+      .find_elf = find_elf,
+    };
+  Dwfl *dwfl = dwfl_begin (&offline_callbacks);
+  if (dwfl == NULL)
+    error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+  return dwfl;
+}
+
+static int
+frame_callback (Dwfl_Frame *state, void *frame_arg)
+{
+  Dwarf_Addr pc;
+  bool isactivation;
+  if (! dwfl_frame_pc (state, &pc, &isactivation))
+    {
+      error (0, 0, "%s", dwfl_errmsg (-1));
+      return DWARF_CB_ABORT;
+    }
+  Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+  /* Get PC->SYMNAME.  */
+  Dwfl_Thread *thread = dwfl_frame_thread (state);
+  Dwfl *dwfl = dwfl_thread_dwfl (thread);
+  Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+  const char *symname = NULL;
+  if (mod)
+    symname = dwfl_module_addrname (mod, pc_adjusted);
+
+  printf ("%#" PRIx64 "\t%s\n", (uint64_t) pc, symname);
+
+  if (symname && (strcmp (symname, "main") == 0
+		  || strcmp (symname, ".main") == 0))
+    {
+      kill (dwfl_pid (dwfl), SIGKILL);
+      exit (0);
+    }
+
+  return DWARF_CB_OK;
+}
+
+static int
+thread_callback (Dwfl_Thread *thread, void *thread_arg)
+{
+  dwfl_thread_getframes (thread, frame_callback, NULL);
+  error (1, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+}
+
+static void
+ptrace_detach_stopped (pid_t pid)
+{
+  errno = 0;
+  long l = ptrace (PTRACE_DETACH, pid, NULL, (void *) (intptr_t) SIGSTOP);
+  assert_perror (errno);
+  assert (l == 0);
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv)
+{
+  /* We use no threads here which can interfere with handling a stream.  */
+  __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+  __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+  __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  elf_version (EV_CURRENT);
+
+  pid_t pid = fork ();
+  switch (pid)
+  {
+    case -1:
+      abort ();
+    case 0:;
+      long l = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+      assert_perror (errno);
+      assert (l == 0);
+      cleanup_13_main ();
+      abort ();
+    default:
+      break;
+  }
+
+  errno = 0;
+  int status;
+  pid_t got = waitpid (pid, &status, 0);
+  assert_perror (errno);
+  assert (got == pid);
+  assert (WIFSTOPPED (status));
+  assert (WSTOPSIG (status) == SIGABRT);
+
+  ptrace_detach_stopped (pid);
+
+  Dwfl *dwfl = pid_to_dwfl (pid);
+  dwfl_getthreads (dwfl, thread_callback, NULL);
+
+  /* There is an exit (0) call if we find the "main" frame,  */
+  error (1, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
+}
diff --git a/tests/backtrace-subr.sh b/tests/backtrace-subr.sh
new file mode 100644
index 0000000..39d40b3
--- /dev/null
+++ b/tests/backtrace-subr.sh
@@ -0,0 +1,115 @@
+# Copyright (C) 2013 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 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/test-subr.sh
+
+# Verify one of the backtraced threads contains function 'main'.
+check_main()
+{
+  if grep -w main $1; then
+    return
+  fi
+  echo >&2 $2: no main
+  false
+}
+
+# Without proper ELF symbols resolution we could get inappropriate weak
+# symbol "gsignal" with the same address as the correct symbol "raise".
+# It was fixed by GIT commit 78dec228b3cfb2f9300cd0b682ebf416c9674c91 .
+# [patch] Improve ELF symbols preference (global > weak)
+# https://lists.fedorahosted.org/pipermail/elfutils-devel/2012-October/002624.html
+check_gsignal()
+{
+  if ! grep -w gsignal $1; then
+    return
+  fi
+  echo >&2 $2: found gsignal
+  false
+}
+
+# Verify the STDERR output does not contain unexpected errors.
+# In some cases we cannot reliably find out we got behind _start as some
+# operating system do not properly terminate CFI by undefined PC.
+# 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)$' \
+         | wc -c) \
+       -eq 0 ]
+  then
+    return
+  fi
+  echo >&2 $2: neither empty nor just out of DWARF
+  false
+}
+
+check_all()
+{
+  bt=$1
+  err=$2
+  testname=$3
+  check_main $bt $testname
+  check_gsignal $bt $testname
+  check_err $err $testname
+}
+
+check_unsupported()
+{
+  err=$1
+  testname=$2
+  if grep -q ': Unwinding not supported for this architecture$' $err; then
+    echo >&2 $testname: arch not supported
+    exit 77
+  fi
+}
+
+check_core()
+{
+  arch=$1
+  testfiles backtrace.$arch.{exec,core}
+  tempfiles backtrace.$arch.{bt,err}
+  echo ./backtrace ./backtrace.$arch.{exec,core}
+  testrun ${abs_builddir}/backtrace -e ./backtrace.$arch.exec --core=./backtrace.$arch.core 1>backtrace.$arch.bt 2>backtrace.$arch.err || true
+  cat backtrace.$arch.{bt,err}
+  check_all backtrace.$arch.{bt,err} backtrace.$arch.core
+}
+
+# Backtrace live process.
+# Do not abort on non-zero exit code due to some warnings of ./backtrace
+# - see function check_err.
+check_native()
+{
+  child=$1
+  tempfiles $child.{bt,err}
+  (set +ex; testrun ${abs_builddir}/backtrace --backtrace-exec=${abs_builddir}/$child 1>$child.bt 2>$child.err; true)
+  cat $child.{bt,err}
+  check_unsupported $child.err $child
+  check_all $child.{bt,err} $child
+}
+
+# Backtrace core file.
+check_native_core()
+{
+  child=$1
+  core="core.`ulimit -c unlimited; set +ex; testrun ${abs_builddir}/$child --gencore; true`"
+  # Do not abort on non-zero exit code due to some warnings of ./backtrace
+  # - see function check_err.
+  tempfiles $core{,.{bt,err}}
+  (set +ex; testrun ${abs_builddir}/backtrace -e ${abs_builddir}/$child --core=$core 1>$core.bt 2>$core.err; true)
+  cat $core.{bt,err}
+  check_unsupported $core.err $child-$core
+  check_all $core.{bt,err} $child-$core
+}
diff --git a/tests/backtrace.c b/tests/backtrace.c
new file mode 100644
index 0000000..06d3878
--- /dev/null
+++ b/tests/backtrace.c
@@ -0,0 +1,452 @@
+/* Test program for unwinding of frames.
+   Copyright (C) 2013 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 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/>.  */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <dwarf.h>
+#include <sys/resource.h>
+#include <sys/ptrace.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/user.h>
+#include <fcntl.h>
+#include <string.h>
+#include <argp.h>
+#include ELFUTILS_HEADER(dwfl)
+
+static int
+dump_modules (Dwfl_Module *mod, void **userdata __attribute__ ((unused)),
+	      const char *name, Dwarf_Addr start,
+	      void *arg __attribute__ ((unused)))
+{
+  Dwarf_Addr end;
+  dwfl_module_info (mod, NULL, NULL, &end, NULL, NULL, NULL, NULL);
+  printf ("%#" PRIx64 "\t%#" PRIx64 "\t%s\n", (uint64_t) start, (uint64_t) end,
+	  name);
+  return DWARF_CB_OK;
+}
+
+static bool is_x86_64_native;
+static pid_t check_tid;
+
+static void
+callback_verify (pid_t tid, unsigned frameno, Dwarf_Addr pc,
+		 const char *symname, Dwfl *dwfl)
+{
+  static bool seen_main = false;
+  if (symname && *symname == '.')
+    symname++;
+  if (symname && strcmp (symname, "main") == 0)
+    seen_main = true;
+  if (pc == 0)
+    {
+      assert (seen_main);
+      return;
+    }
+  if (check_tid == 0)
+    check_tid = tid;
+  if (tid != check_tid)
+    {
+      // For the main thread we are only interested if we can unwind till
+      // we see the "main" symbol.
+      return;
+    }
+  Dwfl_Module *mod;
+  static bool reduce_frameno = false;
+  if (reduce_frameno)
+    frameno--;
+  if (! is_x86_64_native && frameno >= 2)
+    frameno += 2;
+  const char *symname2 = NULL;
+  switch (frameno)
+  {
+    case 0:
+      if (! reduce_frameno && symname
+	       && strcmp (symname, "__kernel_vsyscall") == 0)
+	reduce_frameno = true;
+      else
+	assert (symname && strcmp (symname, "raise") == 0);
+      break;
+    case 1:
+      assert (symname != NULL && strcmp (symname, "sigusr2") == 0);
+      break;
+    case 2: // x86_64 only
+      /* __restore_rt - glibc maybe does not have to have this symbol.  */
+      break;
+    case 3: // x86_64 only
+      if (is_x86_64_native)
+	{
+	  /* Verify we trapped on the very first instruction of jmp.  */
+	  assert (symname != NULL && strcmp (symname, "jmp") == 0);
+	  mod = dwfl_addrmodule (dwfl, pc - 1);
+	  if (mod)
+	    symname2 = dwfl_module_addrname (mod, pc - 1);
+	  assert (symname2 == NULL || strcmp (symname2, "jmp") != 0);
+	  break;
+	}
+      /* PASSTHRU */
+    case 4:
+      assert (symname != NULL && strcmp (symname, "stdarg") == 0);
+      break;
+    case 5:
+      /* Verify we trapped on the very last instruction of child.  */
+      assert (symname != NULL && strcmp (symname, "backtracegen") == 0);
+      mod = dwfl_addrmodule (dwfl, pc);
+      if (mod)
+	symname2 = dwfl_module_addrname (mod, pc);
+      assert (symname2 == NULL || strcmp (symname2, "backtracegen") != 0);
+      break;
+  }
+}
+
+static int
+frame_callback (Dwfl_Frame *state, void *frame_arg)
+{
+  int *framenop = frame_arg;
+  Dwarf_Addr pc;
+  bool isactivation;
+  if (! dwfl_frame_pc (state, &pc, &isactivation))
+    {
+      error (0, 0, "%s", dwfl_errmsg (-1));
+      return DWARF_CB_ABORT;
+    }
+  Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+  /* Get PC->SYMNAME.  */
+  Dwfl_Thread *thread = dwfl_frame_thread (state);
+  Dwfl *dwfl = dwfl_thread_dwfl (thread);
+  Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
+  const char *symname = NULL;
+  if (mod)
+    symname = dwfl_module_addrname (mod, pc_adjusted);
+
+  printf ("#%2d %#" PRIx64 "%4s\t%s\n", *framenop, (uint64_t) pc,
+	  ! isactivation ? "- 1" : "", symname);
+  pid_t tid = dwfl_thread_tid (thread);
+  callback_verify (tid, *framenop, pc, symname, dwfl);
+  (*framenop)++;
+
+  return DWARF_CB_OK;
+}
+
+static int
+thread_callback (Dwfl_Thread *thread, void *thread_arg __attribute__((unused)))
+{
+  printf ("TID %ld:\n", (long) dwfl_thread_tid (thread));
+  int frameno = 0;
+  switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
+    {
+    case 0:
+      break;
+    case DWARF_CB_ABORT:
+      return DWARF_CB_ABORT;
+    case -1:
+      error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+      /* All platforms do not have yet proper unwind termination.  */
+      break;
+    default:
+      abort ();
+    }
+  return DWARF_CB_OK;
+}
+
+static void
+dump (Dwfl *dwfl)
+{
+  ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, dump_modules, NULL, 0);
+  assert (ptrdiff == 0);
+  bool err = false;
+  switch (dwfl_getthreads (dwfl, thread_callback, NULL))
+    {
+    case 0:
+      break;
+    case DWARF_CB_ABORT:
+      err = true;
+      break;
+    case -1:
+      error (0, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
+      err = true;
+      break;
+    default:
+      abort ();
+    }
+  callback_verify (0, 0, 0, NULL, dwfl);
+  if (err)
+    exit (EXIT_FAILURE);
+}
+
+struct see_exec_module
+{
+  Dwfl_Module *mod;
+  char selfpath[PATH_MAX + 1];
+};
+
+static int
+see_exec_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)),
+		 const char *name __attribute__ ((unused)),
+		 Dwarf_Addr start __attribute__ ((unused)), void *arg)
+{
+  struct see_exec_module *data = arg;
+  if (strcmp (name, data->selfpath) != 0)
+    return DWARF_CB_OK;
+  assert (data->mod == NULL);
+  data->mod = mod;
+  return DWARF_CB_OK;
+}
+
+/* On x86_64 only:
+     PC will get changed to function 'jmp' by backtrace.c function
+     prepare_thread.  Then SIGUSR2 will be signalled to backtrace-child
+     which will invoke function sigusr2.
+     This is all done so that signal interrupts execution of the very first
+     instruction of a function.  Properly handled unwind should not slip into
+     the previous unrelated function.  */
+
+static void
+prepare_thread (pid_t pid2 __attribute__ ((unused)),
+		void (*jmp) (void) __attribute__ ((unused)))
+{
+#ifndef __x86_64__
+  abort ();
+#else /* x86_64 */
+  long l;
+  errno = 0;
+  l = ptrace (PTRACE_POKEUSER, pid2,
+	      (void *) (intptr_t) offsetof (struct user_regs_struct, rip), jmp);
+  assert_perror (errno);
+  assert (l == 0);
+  l = ptrace (PTRACE_CONT, pid2, NULL, (void *) (intptr_t) SIGUSR2);
+  int status;
+  pid_t got = waitpid (pid2, &status, __WALL);
+  assert_perror (errno);
+  assert (got == pid2);
+  assert (WIFSTOPPED (status));
+  assert (WSTOPSIG (status) == SIGUSR1);
+#endif /* __x86_64__ */
+}
+
+#include <asm/unistd.h>
+#include <unistd.h>
+#define tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig))
+
+static void
+ptrace_detach_stopped (pid_t pid)
+{
+  errno = 0;
+  long l = ptrace (PTRACE_DETACH, pid, NULL, (void *) (intptr_t) SIGSTOP);
+  assert_perror (errno);
+  assert (l == 0);
+}
+
+static void
+report_pid (Dwfl *dwfl, pid_t pid)
+{
+  int result = dwfl_linux_proc_report (dwfl, pid);
+  if (result < 0)
+    error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1));
+  else if (result > 0)
+    error (2, result, "dwfl_linux_proc_report");
+
+  if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+    error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+}
+
+static Dwfl *
+pid_to_dwfl (pid_t pid)
+{
+  static char *debuginfo_path;
+  static const Dwfl_Callbacks proc_callbacks =
+    {
+      .find_debuginfo = dwfl_standard_find_debuginfo,
+      .debuginfo_path = &debuginfo_path,
+
+      .find_elf = dwfl_linux_proc_find_elf,
+    };
+  Dwfl *dwfl = dwfl_begin (&proc_callbacks);
+  if (dwfl == NULL)
+    error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1));
+  report_pid (dwfl, pid);
+  return dwfl;
+}
+
+static void
+exec_dump (const char *exec)
+{
+  pid_t pid = fork ();
+  switch (pid)
+  {
+    case -1:
+      abort ();
+    case 0:
+      execl (exec, exec, "--ptraceme", NULL);
+      abort ();
+    default:
+      break;
+  }
+
+  /* Catch the main thread.  Catch it first otherwise the /proc evaluation of
+     PID may have caught still ourselves before executing execl above.  */
+  errno = 0;
+  int status;
+  pid_t got = waitpid (pid, &status, 0);
+  assert_perror (errno);
+  assert (got == pid);
+  assert (WIFSTOPPED (status));
+  // Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
+  assert (WSTOPSIG (status) == SIGUSR2);
+
+  /* Catch the spawned thread.  Do not use __WCLONE as we could get racy
+     __WCLONE, probably despite pthread_create already had to be called the new
+     task is not yet alive enough for waitpid.  */
+  pid_t pid2 = waitpid (-1, &status, __WALL);
+  assert_perror (errno);
+  assert (pid2 > 0);
+  assert (pid2 != pid);
+  assert (WIFSTOPPED (status));
+  // Main thread will signal SIGUSR2.  Other thread will signal SIGUSR1.
+  assert (WSTOPSIG (status) == SIGUSR1);
+
+  Dwfl *dwfl = pid_to_dwfl (pid);
+  char *selfpathname;
+  int i = asprintf (&selfpathname, "/proc/%ld/exe", (long) pid);
+  assert (i > 0);
+  struct see_exec_module data;
+  ssize_t ssize = readlink (selfpathname, data.selfpath,
+			    sizeof (data.selfpath));
+  free (selfpathname);
+  assert (ssize > 0 && ssize < (ssize_t) sizeof (data.selfpath));
+  data.selfpath[ssize] = '\0';
+  data.mod = NULL;
+  ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, see_exec_module, &data, 0);
+  assert (ptrdiff == 0);
+  assert (data.mod != NULL);
+  GElf_Addr loadbase;
+  Elf *elf = dwfl_module_getelf (data.mod, &loadbase);
+  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+  assert (ehdr != NULL);
+  /* It is false also on x86_64 with i386 inferior.  */
+#ifndef __x86_64__
+  is_x86_64_native = false;
+#else /* __x86_64__ */
+  is_x86_64_native = ehdr->e_ident[EI_CLASS] == ELFCLASS64;
+#endif /* __x86_64__ */
+  void (*jmp) (void);
+  if (is_x86_64_native)
+    {
+      // Find inferior symbol named "jmp".
+      int nsym = dwfl_module_getsymtab (data.mod);
+      int symi;
+      for (symi = 1; symi < nsym; ++symi)
+	{
+	  GElf_Sym symbol;
+	  const char *symbol_name = dwfl_module_getsym (data.mod, symi, &symbol, NULL);
+	  if (symbol_name == NULL)
+	    continue;
+	  switch (GELF_ST_TYPE (symbol.st_info))
+	    {
+	    case STT_SECTION:
+	    case STT_FILE:
+	    case STT_TLS:
+	      continue;
+	    default:
+	      if (strcmp (symbol_name, "jmp") != 0)
+		continue;
+	      break;
+	    }
+	  /* LOADBASE is already applied here.  */
+	  jmp = (void (*) (void)) (uintptr_t) symbol.st_value;
+	  break;
+	}
+      assert (symi < nsym);
+      prepare_thread (pid2, jmp);
+    }
+  dwfl_end (dwfl);
+  ptrace_detach_stopped (pid);
+  ptrace_detach_stopped (pid2);
+  check_tid = pid2;
+  dwfl = pid_to_dwfl (pid);
+  dump (dwfl);
+  dwfl_end (dwfl);
+}
+
+#define OPT_BACKTRACE_EXEC 0x100
+
+static const struct argp_option options[] =
+  {
+    { "backtrace-exec", OPT_BACKTRACE_EXEC, "EXEC", 0, N_("Run executable"), 0 },
+    { NULL, 0, NULL, 0, NULL, 0 }
+  };
+
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+  switch (key)
+    {
+    case ARGP_KEY_INIT:
+      state->child_inputs[0] = state->input;
+      break;
+
+    case OPT_BACKTRACE_EXEC:
+      exec_dump (arg);
+      exit (0);
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+  return 0;
+}
+
+int
+main (int argc __attribute__ ((unused)), char **argv)
+{
+  /* We use no threads here which can interfere with handling a stream.  */
+  __fsetlocking (stdin, FSETLOCKING_BYCALLER);
+  __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+  __fsetlocking (stderr, FSETLOCKING_BYCALLER);
+
+  /* Set locale.  */
+  (void) setlocale (LC_ALL, "");
+
+  elf_version (EV_CURRENT);
+
+  Dwfl *dwfl = NULL;
+  const struct argp_child argp_children[] =
+    {
+      { .argp = dwfl_standard_argp () },
+      { .argp = NULL }
+    };
+  const struct argp argp =
+    {
+      options, parse_opt, NULL, NULL, argp_children, NULL, NULL
+    };
+  (void) argp_parse (&argp, argc, argv, 0, NULL, &dwfl);
+  assert (dwfl != NULL);
+  dump (dwfl);
+  dwfl_end (dwfl);
+  return 0;
+}
diff --git a/tests/cleanup-13.c b/tests/cleanup-13.c
new file mode 100644
index 0000000..b87c696
--- /dev/null
+++ b/tests/cleanup-13.c
@@ -0,0 +1,334 @@
+// http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/testsuite/gcc.dg/cleanup-13.c?view=co&content-type=text%2Fplain
+
+/* HP-UX libunwind.so doesn't provide _UA_END_OF_STACK */
+/* { dg-do run } */
+/* { dg-options "-fexceptions" } */
+/* { dg-skip-if "" { "ia64-*-hpux11.*" }  { "*" } { "" } } */
+/* Verify DW_OP_* handling in the unwinder.  */
+
+#include <unwind.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* #define OP_addr(x) 0x06, ... */
+#define OP_deref 0x06,
+#define SLEB128(x) (x)&0x7f	/* Assume here the value is -0x40 ... 0x3f.  */
+#define ULEB128(x) (x)&0x7f	/* Assume here the value is 0 ... 0x7f.  */
+#define VAL1(x) (x)&0xff
+#if defined (__BIG_ENDIAN__)
+#define VAL2(x) ((x)>>8)&0xff,(x)&0xff
+#define VAL4(x) ((x)>>24)&0xff,((x)>>16)&0xff,((x)>>8)&0xff,(x)&0xff
+#define VAL8(x) ((x)>>56)&0xff,((x)>>48)&0xff,((x)>>40)&0xff,((x)>>32)&0xff,((x)>>24)&0xff,((x)>>16)&0xff,((x)>>8)&0xff,(x)&0xff
+#elif defined(__LITTLE_ENDIAN__) || defined(__x86_64__) || defined(__i386__)
+#define VAL2(x) (x)&0xff,((x)>>8)&0xff
+#define VAL4(x) (x)&0xff,((x)>>8)&0xff,((x)>>16)&0xff,((x)>>24)&0xff
+#define VAL8(x) (x)&0xff,((x)>>8)&0xff,((x)>>16)&0xff,((x)>>24)&0xff,((x)>>32)&0xff,((x)>>40)&0xff,((x)>>48)&0xff,((x)>>56)&0xff
+#endif
+#define OP_const1u(x) 0x08,VAL1(x),
+#define OP_const1s(x) 0x09,VAL1(x),
+#define OP_const2u(x) 0x0a,VAL2(x),
+#define OP_const2s(x) 0x0b,VAL2(x),
+#define OP_const4u(x) 0x0c,VAL4(x),
+#define OP_const4s(x) 0x0d,VAL4(x),
+#define OP_const8u(x) 0x0e,VAL8(x),
+#define OP_const8s(x) 0x0f,VAL8(x),
+#define OP_constu(x) 0x10,ULEB128(x),
+#define OP_consts(x) 0x11,SLEB128(x),
+#define OP_dup 0x12,
+#define OP_drop 0x13,
+#define OP_over 0x14,
+#define OP_pick(x) 0x15,VAL1(x),
+#define OP_swap 0x16,
+#define OP_rot 0x17,
+#define OP_xderef 0x18,
+#define OP_abs 0x19,
+#define OP_and 0x1a,
+#define OP_div 0x1b,
+#define OP_minus 0x1c,
+#define OP_mod 0x1d,
+#define OP_mul 0x1e,
+#define OP_neg 0x1f,
+#define OP_not 0x20,
+#define OP_or 0x21,
+#define OP_plus 0x22,
+#define OP_plus_uconst(x) 0x23,ULEB128(x),
+#define OP_shl 0x24,
+#define OP_shr 0x25,
+#define OP_shra 0x26,
+#define OP_xor 0x27,
+#define OP_bra(x) 0x28,VAL2(x),
+#define OP_eq 0x29,
+#define OP_ge 0x2a,
+#define OP_gt 0x2b,
+#define OP_le 0x2c,
+#define OP_lt 0x2d,
+#define OP_ne 0x2e,
+#define OP_skip(x) 0x2f,VAL2(x),
+#define OP_lit0 0x30,
+#define OP_lit1 0x31,
+#define OP_lit2 0x32,
+#define OP_lit3 0x33,
+#define OP_lit4 0x34,
+#define OP_lit5 0x35,
+#define OP_lit6 0x36,
+#define OP_lit7 0x37,
+#define OP_lit8 0x38,
+#define OP_lit9 0x39,
+#define OP_lit10 0x3a,
+#define OP_lit11 0x3b,
+#define OP_lit12 0x3c,
+#define OP_lit13 0x3d,
+#define OP_lit14 0x3e,
+#define OP_lit15 0x3f,
+#define OP_lit16 0x40,
+#define OP_lit17 0x41,
+#define OP_lit18 0x42,
+#define OP_lit19 0x43,
+#define OP_lit20 0x44,
+#define OP_lit21 0x45,
+#define OP_lit22 0x46,
+#define OP_lit23 0x47,
+#define OP_lit24 0x48,
+#define OP_lit25 0x49,
+#define OP_lit26 0x4a,
+#define OP_lit27 0x4b,
+#define OP_lit28 0x4c,
+#define OP_lit29 0x4d,
+#define OP_lit30 0x4e,
+#define OP_lit31 0x4f,
+#define OP_reg0 0x50,
+#define OP_reg1 0x51,
+#define OP_reg2 0x52,
+#define OP_reg3 0x53,
+#define OP_reg4 0x54,
+#define OP_reg5 0x55,
+#define OP_reg6 0x56,
+#define OP_reg7 0x57,
+#define OP_reg8 0x58,
+#define OP_reg9 0x59,
+#define OP_reg10 0x5a,
+#define OP_reg11 0x5b,
+#define OP_reg12 0x5c,
+#define OP_reg13 0x5d,
+#define OP_reg14 0x5e,
+#define OP_reg15 0x5f,
+#define OP_reg16 0x60,
+#define OP_reg17 0x61,
+#define OP_reg18 0x62,
+#define OP_reg19 0x63,
+#define OP_reg20 0x64,
+#define OP_reg21 0x65,
+#define OP_reg22 0x66,
+#define OP_reg23 0x67,
+#define OP_reg24 0x68,
+#define OP_reg25 0x69,
+#define OP_reg26 0x6a,
+#define OP_reg27 0x6b,
+#define OP_reg28 0x6c,
+#define OP_reg29 0x6d,
+#define OP_reg30 0x6e,
+#define OP_reg31 0x6f,
+#define OP_breg0(x) 0x70,SLEB128(x),
+#define OP_breg1(x) 0x71,SLEB128(x),
+#define OP_breg2(x) 0x72,SLEB128(x),
+#define OP_breg3(x) 0x73,SLEB128(x),
+#define OP_breg4(x) 0x74,SLEB128(x),
+#define OP_breg5(x) 0x75,SLEB128(x),
+#define OP_breg6(x) 0x76,SLEB128(x),
+#define OP_breg7(x) 0x77,SLEB128(x),
+#define OP_breg8(x) 0x78,SLEB128(x),
+#define OP_breg9(x) 0x79,SLEB128(x),
+#define OP_breg10(x) 0x7a,SLEB128(x),
+#define OP_breg11(x) 0x7b,SLEB128(x),
+#define OP_breg12(x) 0x7c,SLEB128(x),
+#define OP_breg13(x) 0x7d,SLEB128(x),
+#define OP_breg14(x) 0x7e,SLEB128(x),
+#define OP_breg15(x) 0x7f,SLEB128(x),
+#define OP_breg16(x) 0x80,SLEB128(x),
+#define OP_breg17(x) 0x81,SLEB128(x),
+#define OP_breg18(x) 0x82,SLEB128(x),
+#define OP_breg19(x) 0x83,SLEB128(x),
+#define OP_breg20(x) 0x84,SLEB128(x),
+#define OP_breg21(x) 0x85,SLEB128(x),
+#define OP_breg22(x) 0x86,SLEB128(x),
+#define OP_breg23(x) 0x87,SLEB128(x),
+#define OP_breg24(x) 0x88,SLEB128(x),
+#define OP_breg25(x) 0x89,SLEB128(x),
+#define OP_breg26(x) 0x8a,SLEB128(x),
+#define OP_breg27(x) 0x8b,SLEB128(x),
+#define OP_breg28(x) 0x8c,SLEB128(x),
+#define OP_breg29(x) 0x8d,SLEB128(x),
+#define OP_breg30(x) 0x8e,SLEB128(x),
+#define OP_breg31(x) 0x8f,SLEB128(x),
+#define OP_regx(x) 0x90,SLEB128(x),
+#define OP_fbreg(x) 0x91,SLEB128(x),
+#define OP_bregx(x,y) 0x92,ULEB128(x),SLEB128(y),
+#define OP_piece(x) 0x93,ULEB128(x),
+#define OP_deref_size(x) 0x94,VAL1(x),
+#define OP_xderef_size(x) 0x95,VAL1(x),
+#define OP_nop 0x96,
+#define OP_nop_termination 0x96
+#define OP_push_object_address 0x97,
+#define OP_call2(x) 0x98,VAL2(x),
+#define OP_call4(x) 0x99,VAL4(x),
+/* #define OP_call_ref(x) 0x9a,... */
+#define OP_form_tls_address(x) 0x9b,
+#define OP_call_frame_cfa 0x9c,
+#define OP_bit_piece(x) 0x9d,ULEB128(x),
+/* #define OP_implicit_value(x...) 0x9e,... */
+#define OP_stack_value 0x9f,
+#define OP_GNU_push_tls_address 0xe0,
+/* #define OP_GNU_encoded_addr(x...) 0xf1, */
+
+#define ASSERT_TOS_NON0 OP_bra(3) OP_skip(-3)
+#define ASSERT_TOS_0 OP_lit0 OP_eq ASSERT_TOS_NON0
+
+/* Initially there is CFA value on the stack, we want to
+   keep it there at the end.  */
+#define CFI_PROGRAM \
+OP_lit0 OP_nop ASSERT_TOS_0						\
+OP_lit1 ASSERT_TOS_NON0							\
+OP_lit1 OP_const1u(1) OP_eq ASSERT_TOS_NON0				\
+OP_lit16 OP_const2u(16) OP_eq ASSERT_TOS_NON0				\
+OP_lit31 OP_const4u(31) OP_ne ASSERT_TOS_0				\
+OP_lit1 OP_neg OP_const1s(-1) OP_eq ASSERT_TOS_NON0			\
+OP_lit16 OP_neg OP_const2s(-16) OP_ne ASSERT_TOS_0			\
+OP_lit31 OP_const4s(-31) OP_neg OP_ne ASSERT_TOS_0			\
+OP_lit7 OP_dup OP_plus_uconst(2) OP_lit9 OP_eq ASSERT_TOS_NON0		\
+  OP_lit7 OP_eq ASSERT_TOS_NON0						\
+OP_lit20 OP_lit1 OP_drop OP_lit20 OP_eq ASSERT_TOS_NON0			\
+OP_lit17 OP_lit19 OP_over OP_lit17 OP_eq ASSERT_TOS_NON0		\
+  OP_lit19 OP_eq ASSERT_TOS_NON0 OP_lit17 OP_eq ASSERT_TOS_NON0		\
+OP_lit1 OP_lit2 OP_lit3 OP_lit4 OP_pick(2) OP_lit2 OP_eq ASSERT_TOS_NON0\
+  OP_lit4 OP_eq ASSERT_TOS_NON0 OP_lit3 OP_eq ASSERT_TOS_NON0		\
+  OP_pick(0) OP_lit2 OP_eq ASSERT_TOS_NON0				\
+  OP_lit2 OP_eq ASSERT_TOS_NON0 OP_lit1 OP_eq ASSERT_TOS_NON0		\
+OP_lit6 OP_lit12 OP_swap OP_lit6 OP_eq ASSERT_TOS_NON0			\
+  OP_lit12 OP_eq ASSERT_TOS_NON0					\
+OP_lit7 OP_lit8 OP_lit9 OP_rot OP_lit8 OP_eq ASSERT_TOS_NON0		\
+  OP_lit7 OP_eq ASSERT_TOS_NON0 OP_lit9 OP_eq ASSERT_TOS_NON0		\
+OP_lit7 OP_abs OP_lit7 OP_eq ASSERT_TOS_NON0				\
+OP_const1s(-123) OP_abs OP_const1u(123) OP_eq ASSERT_TOS_NON0		\
+OP_lit3 OP_lit6 OP_and OP_lit2 OP_eq ASSERT_TOS_NON0			\
+OP_lit3 OP_lit6 OP_or OP_lit7 OP_eq ASSERT_TOS_NON0			\
+OP_lit17 OP_lit2 OP_minus OP_lit15 OP_eq ASSERT_TOS_NON0		\
+/* Divide is signed truncating toward zero.  */				\
+OP_const1s(-6) OP_const1s(-2) OP_div OP_lit3 OP_eq ASSERT_TOS_NON0	\
+OP_const1s(-7) OP_const1s(3) OP_div OP_const1s(-2)			\
+  OP_eq ASSERT_TOS_NON0							\
+/* Modulo is unsigned.  */						\
+OP_const1s(-6) OP_const1s(-4) OP_mod OP_const1s(-6)			\
+  OP_eq ASSERT_TOS_NON0							\
+OP_const1s(-6) OP_lit4 OP_mod OP_lit2 OP_eq ASSERT_TOS_NON0		\
+OP_lit6 OP_const1s(-4) OP_mod OP_lit6 OP_eq ASSERT_TOS_NON0		\
+/* Signed modulo can be implemented using "over over div mul minus".  */\
+OP_const1s(-6) OP_const1s(-4) OP_over OP_over OP_div OP_mul OP_minus	\
+  OP_const1s(-2) OP_eq ASSERT_TOS_NON0					\
+OP_const1s(-7) OP_lit3 OP_over OP_over OP_div OP_mul OP_minus		\
+  OP_const1s(-1) OP_eq ASSERT_TOS_NON0					\
+OP_lit7 OP_const1s(-3) OP_over OP_over OP_div OP_mul OP_minus		\
+  OP_lit1 OP_eq ASSERT_TOS_NON0						\
+OP_lit16 OP_lit31 OP_plus_uconst(1) OP_mul OP_const2u(512)		\
+  OP_eq ASSERT_TOS_NON0							\
+OP_lit5 OP_not OP_lit31 OP_and OP_lit26 OP_eq ASSERT_TOS_NON0		\
+OP_lit12 OP_lit31 OP_plus OP_const1u(43) OP_eq ASSERT_TOS_NON0		\
+OP_const1s(-6) OP_lit2 OP_plus OP_const1s(-4) OP_eq ASSERT_TOS_NON0	\
+OP_const1s(-6) OP_plus_uconst(3) OP_const1s(-3) OP_eq ASSERT_TOS_NON0	\
+OP_lit16 OP_lit4 OP_shl OP_const2u(256) OP_eq ASSERT_TOS_NON0		\
+OP_lit16 OP_lit3 OP_shr OP_lit2 OP_eq ASSERT_TOS_NON0			\
+OP_const1s(-16) OP_lit3 OP_shra OP_const1s(-2) OP_eq ASSERT_TOS_NON0	\
+OP_lit3 OP_lit6 OP_xor OP_lit5 OP_eq ASSERT_TOS_NON0			\
+OP_lit3 OP_lit6 OP_le ASSERT_TOS_NON0					\
+OP_lit3 OP_lit3 OP_le ASSERT_TOS_NON0					\
+OP_lit6 OP_lit3 OP_le ASSERT_TOS_0					\
+OP_lit3 OP_lit6 OP_lt ASSERT_TOS_NON0					\
+OP_lit3 OP_lit3 OP_lt ASSERT_TOS_0					\
+OP_lit6 OP_lit3 OP_lt ASSERT_TOS_0					\
+OP_lit3 OP_lit6 OP_ge ASSERT_TOS_0					\
+OP_lit3 OP_lit3 OP_ge ASSERT_TOS_NON0					\
+OP_lit6 OP_lit3 OP_ge ASSERT_TOS_NON0					\
+OP_lit3 OP_lit6 OP_gt ASSERT_TOS_0					\
+OP_lit3 OP_lit3 OP_gt ASSERT_TOS_0					\
+OP_lit6 OP_lit3 OP_gt ASSERT_TOS_NON0					\
+OP_const1s(-6) OP_lit1 OP_shr OP_lit0 OP_gt ASSERT_TOS_NON0		\
+OP_const1s(-6) OP_lit1 OP_shra OP_lit0 OP_lt ASSERT_TOS_NON0
+
+#define CFI_ESCAPE_VAL_2(VALUES...) #VALUES
+#define CFI_ESCAPE_VAL_1(VALUES...) CFI_ESCAPE_VAL_2(VALUES)
+#define CFI_ESCAPE_VAL(VALUES...) CFI_ESCAPE_VAL_1(VALUES)
+#define CFI_ESCAPE do { } while (0)
+#define CFI_ARCH_PROGRAM OP_nop_termination
+#ifdef __GCC_HAVE_DWARF2_CFI_ASM
+#if defined (__x86_64__)
+#undef CFI_ESCAPE
+#undef CFI_ARCH_PROGRAM
+#define CFI_ARCH_PROGRAM CFI_PROGRAM OP_lit8 OP_minus OP_nop_termination
+unsigned char cfi_arch_program[] = { CFI_ARCH_PROGRAM };
+extern char verify_it[sizeof (cfi_arch_program) - 0x80 < 0x3f80 ? 1 : -1];
+/* DW_CFA_expression %rip, uleb128(l2-l1), l1: program DW_OP_lit8 DW_OP_minus DW_OP_nop l2: */
+#define CFI_ESCAPE \
+  asm volatile (".cfi_escape 0x10, 0x10, (%P0&0x7f)+0x80, %P0>>7, " \
+		CFI_ESCAPE_VAL (CFI_ARCH_PROGRAM) \
+		: : "i" (sizeof (cfi_arch_program)))
+#elif defined (__i386__)
+#undef CFI_ESCAPE
+#undef CFI_ARCH_PROGRAM
+#define CFI_ARCH_PROGRAM CFI_PROGRAM OP_lit4 OP_minus OP_nop_termination
+unsigned char cfi_arch_program[] = { CFI_ARCH_PROGRAM };
+extern char verify_it[sizeof (cfi_arch_program) - 0x80 < 0x3f80 ? 1 : -1];
+/* DW_CFA_expression %eip, uleb128(l2-l1), l1: program DW_OP_lit4 DW_OP_minus DW_OP_nop l2: */
+#define CFI_ESCAPE \
+  asm volatile (".cfi_escape 0x10, 8, (%P0&0x7f)+0x80, %P0>>7, " \
+		CFI_ESCAPE_VAL (CFI_ARCH_PROGRAM) \
+		: : "i" (sizeof (cfi_arch_program)))
+#endif
+#endif
+static _Unwind_Reason_Code
+force_unwind_stop (int version, _Unwind_Action actions,
+		   _Unwind_Exception_Class exc_class,
+		   struct _Unwind_Exception *exc_obj,
+		   struct _Unwind_Context *context,
+		   void *stop_parameter)
+{
+  if (actions & _UA_END_OF_STACK)
+    abort ();
+  return _URC_NO_REASON;
+}
+
+static void force_unwind ()
+{
+  struct _Unwind_Exception *exc = malloc (sizeof (*exc));
+  memset (&exc->exception_class, 0, sizeof (exc->exception_class));
+  exc->exception_cleanup = 0;
+
+#ifndef __USING_SJLJ_EXCEPTIONS__
+  _Unwind_ForcedUnwind (exc, force_unwind_stop, 0);
+#else
+  _Unwind_SjLj_ForcedUnwind (exc, force_unwind_stop, 0);
+#endif
+
+  abort ();
+}
+
+static void handler (void *p __attribute__((unused)))
+{
+  exit (0);
+}
+
+__attribute__((noinline)) static void callme ()
+{
+  CFI_ESCAPE;
+  force_unwind ();
+}
+
+__attribute__((noinline)) static void doit ()
+{
+  char dummy __attribute__((cleanup (handler)));
+  callme ();
+}
+
+int main()
+{ 
+  doit ();
+  abort ();
+}
diff --git a/tests/run-backtrace-core-i386.sh b/tests/run-backtrace-core-i386.sh
new file mode 100755
index 0000000..7294ec3
--- /dev/null
+++ b/tests/run-backtrace-core-i386.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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
+
+check_core i386
diff --git a/tests/run-backtrace-core-x86_64.sh b/tests/run-backtrace-core-x86_64.sh
new file mode 100755
index 0000000..d00cd6d
--- /dev/null
+++ b/tests/run-backtrace-core-x86_64.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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
+
+check_core x86_64
diff --git a/tests/run-backtrace-data.sh b/tests/run-backtrace-data.sh
new file mode 100755
index 0000000..30127c3
--- /dev/null
+++ b/tests/run-backtrace-data.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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
+
+tempfiles data.{bt,err}
+(set +ex; testrun ${abs_builddir}/backtrace-data 1>data.bt 2>data.err; true)
+cat data.{bt,err}
+check_unsupported data.err data
+check_all data.{bt,err} data
diff --git a/tests/run-backtrace-dwarf.sh b/tests/run-backtrace-dwarf.sh
new file mode 100755
index 0000000..b24215e
--- /dev/null
+++ b/tests/run-backtrace-dwarf.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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/test-subr.sh
+
+testrun ${abs_builddir}/backtrace-dwarf
diff --git a/tests/run-backtrace-native-biarch.sh b/tests/run-backtrace-native-biarch.sh
new file mode 100755
index 0000000..3a407c8
--- /dev/null
+++ b/tests/run-backtrace-native-biarch.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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/>.
+
+if test -n "$ELFUTILS_DISABLE_BIARCH"; then
+  exit 77
+fi
+
+. $srcdir/backtrace-subr.sh
+
+check_native backtrace-child-biarch
diff --git a/tests/run-backtrace-native-core-biarch.sh b/tests/run-backtrace-native-core-biarch.sh
new file mode 100755
index 0000000..fbd8025
--- /dev/null
+++ b/tests/run-backtrace-native-core-biarch.sh
@@ -0,0 +1,24 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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/>.
+
+if test -n "$ELFUTILS_DISABLE_BIARCH"; then
+  exit 77
+fi
+
+. $srcdir/backtrace-subr.sh
+
+check_native_core backtrace-child-biarch
diff --git a/tests/run-backtrace-native-core.sh b/tests/run-backtrace-native-core.sh
new file mode 100755
index 0000000..cb025a5
--- /dev/null
+++ b/tests/run-backtrace-native-core.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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
+
+check_native_core backtrace-child
diff --git a/tests/run-backtrace-native.sh b/tests/run-backtrace-native.sh
new file mode 100755
index 0000000..ddae345
--- /dev/null
+++ b/tests/run-backtrace-native.sh
@@ -0,0 +1,20 @@
+#! /bin/bash
+# Copyright (C) 2013 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 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
+
+check_native backtrace-child
-- 
1.8.3.1


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]