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

It seems ready to me, there were not many/any parts for further discussion
IMO.

Patch at the end does not contain the .bz2 binary blobs.


On Sat, 30 Nov 2013 17:09:34 +0100, Mark Wielaard wrote:
> On Sat, 2013-11-23 at 21:57 +0100, Jan Kratochvil wrote: 
> > +if BIARCH
> > +BUILT_SOURCES = backtrace-child-biarch
> > +endif
> 
> This makes sure that backtrace-child-biarch is built before any other
> target if BIARCH. Why is that necessary? Could you use something like
> check_PROGRAMS += backtrace-child-biarch instead or does that not work
> with a custom target?

I do not remember why I made it that way.  'check_PROGRAMS +=' works now, used
that, thanks.


> > +clean-local:
> > +	$(RM) backtrace-child-biarch

So this is gone now.


> > +# 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 $@ $<

backtrace-child-biarch is correctly present only once in Makefile.in.


> > diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c
> > +/* Test program for unwinding of frames.
> > +#ifndef __x86_64__
> > +
> > +int
> > +main (void)
> > +{
> > +  return 77;
> > +}
> > +
> > +#else /* __x86_64__ */
> 
> You might want to add a comment that this is test mimics what a native
> ptrace based backend would do.

First line is now:
/* Test custom provided Dwfl_Thread_Callbacks vector.

And initial comment is:
/* Test custom provided Dwfl_Thread_Callbacks vector.  Test mimics what
   a ptrace based vector would do.  */


> And that the only arch specific code is the set_initial_registers function.
> So that porters know how to adapt it to their arch.

At the suggested place is now:
/* The only arch specific code is set_initial_registers.  */


> > +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.  */
> > +  *thread_argp = thread_argp;
> > +  return dwfl_pid (dwfl);
> > +}
> 
> Why that *thread_argp assignment?

Updated the comment:

  /* Put arbitrary non-NULL value into *THREAD_ARGP as a marker so that this 
     function returns non-zero PID only once.  */


> > /* Add module containing ADDR to the DWFL address space.  */
> > 
> > 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;
> > }
> 
> I am not sure this is really a supported way of manipulating the Dwfl
> on-the-fly. You should do the dwfl_report between dwfl_report_begin_add
> and dwfl_report_end.
> 
> This being called from a Dwfl user callback is slightly iffy IMHO. If
> you really cannot report the modules at the start before calling
> dwfl_getthreads then please do add a comment/warning here that setting
> up new Dwfl_Modules from a Dwfl user callback is risky.

Primarily it probably works because:

dwfl_report_begin_add (Dwfl *dwfl __attribute__ ((unused)))
{ 
  /* The lookup table will be cleared on demand, there is nothing we need
     to do here.  */
} 

But good you have found it.

This testcase is modeled according to perf which I expected it should be
cheaper to report only the needed modules.  Although with elfutils on-demand
loading of modules the performance may be the same.  I did not benchmark it.


> > +static Dwfl *
> > +dwfl_offline (void)
> > +{
[...]
> > +}
> 
> I believe you don't need these two functions and the executable var,
> since you don't do cores.

This has been all simplified/removed now with dwfl_standard_argp ().


> > +  Dwfl *dwfl = pid_to_dwfl (pid);
> > +  dwfl_getthreads (dwfl, thread_callback, NULL);
> > +  error (1, 0, "dwfl_getthreads: %s", dwfl_errmsg (-1));
> 
> Might want to add a comment here, for people like me that don't
> immediately realize you exit (0) from deep inside the
> frame/thread_callback.

Added there:

  /* There is an exit (0) call if we find the "main" frame,  */


> > +  if [ $(egrep -v <$1 '/backtrace: dwfl_thread_getframes: (No DWARF information found|no matching address range)$' \
[...]
> OK. But you might want the egrep on /backtrace(-[a-z]+)?: (see below).

OK, true, I have put there now just:

  if [ $(egrep -v <$1 'dwfl_thread_getframes: (No DWARF information found|no matching address range)$' \


> Maybe you also want a check_native() (see below)

Yes, done.


> > +static void
> > +selfdump_callback (pid_t tid, unsigned frameno, Dwarf_Addr pc,
> > +		   const char *symname, Dwfl *dwfl, void *data)
> > +{
> > +  pid_t check_tid = (intptr_t) data;
> > +  bool is_x86_64 = check_tid < 0;
> > +  if (is_x86_64)
> > +    check_tid = -check_tid;
> > +  static bool seen_main = false;
> > +  if (symname && *symname == '.')
> > +    symname++;
> 
> This seems new. I assume it is for the fake ppc function entry symbols.
> Seems we should do this dot-prefix cleanup in dwfl_module_getsym () so
> that all arches produce normal function symbol names.

This is left as is.  More about the dot-discussion in the ppc64 descriptors
support mail.


> > diff --git a/tests/run-backtrace-core-i386.sh b/tests/run-backtrace-core-i386.sh
> > [...]
> > +. $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
> > [...]
> > +. $srcdir/backtrace-subr.sh
> > +
> > +check_core x86_64
> 
> Might want to have one run-backtrace-core.sh file with simply:
> check_core i386
> check_core x86_64

Sorry but this seems a bit inconsistent to me.  First you wanted to split the
single backtrace testfile.  Now you want to merge some of the testfiles back.

If one should really see different parts of elfutils code to have effect on
different testfiles then each arch really should be separate.  i386 vs. x86_64
test tests different backend implementation.

So please justify why some testcases should be merged and some should be
separate.  I do not see the rule/reason.


> > diff --git a/tests/run-backtrace-data.sh b/tests/run-backtrace-data.sh
> > +. $srcdir/test-subr.sh
> > +
> > +testrun ${abs_builddir}/backtrace-data
> 
> This might need the same treatment as run-backtrace-native.sh because it
> is mostly a native test, checking against a native libc.so. So it might
> need at least the check_err on some systems.

I do not think so.  The reason for check_err is that ./backtrace tries to
unwind through main() also _start and it is difficult to find the termination
there.

Contrary to it run-backtrace-data.sh stops already at main() so it is safe.


> > diff --git a/tests/run-backtrace-native-core.sh b/tests/run-backtrace-native-core.sh
> > [...]
> > +. $srcdir/backtrace-subr.sh
> > +
> > +child=backtrace-child
> > +
> > +# Backtrace core file.
> > +core="core.`ulimit -c unlimited; set +ex; testrun ${abs_builddir}/$child --gencore --run; 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 ${abs_builddir}/$child $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
> 
> OK, but maybe wrap this in a check_native() in backtrace-subr.sh to
> share with run-backtrace-data.sh and run-backtrace-child.sh?

There is no run-backtrace-child.sh.  run-backtrace-data.sh is trivial and I do
not see there an opportunity for code merging.

I have merged the normal+biarch code into check_native() and
check_native_core().

Merging check_native() and check_native_core() IMO does not make sense, they
are already mostly merged thanks to check_unsupported() and check_all().


> > diff --git a/tests/run-backtrace-native.sh b/tests/run-backtrace-native.sh
> > [...]
> > +. $srcdir/backtrace-subr.sh
> > +
> > +child=backtrace-child
> > +
> > +# Backtrace live process.
> > +# Do not abort on non-zero exit code due to some warnings of ./backtrace
> > +# - see function check_err.
> > +tempfiles $child.{bt,err}
> > +(set +ex; testrun ${abs_builddir}/backtrace ${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
> 
> OK, but maybe wrap this in a check_native() in backtrace-subr.sh?

Done.


Thanks,
Jan


575c019bed339523a8b4ebbe3bf5b2d089c71061 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                   | 194 +++++++++++++
 tests/backtrace-subr.sh                   | 115 ++++++++
 tests/backtrace.c                         | 444 ++++++++++++++++++++++++++++++
 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               |  20 ++
 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, 1842 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..7f7e007
--- /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 ());
+  errno = 0;
+  pthread_t thread;
+  int i = pthread_create (&thread, NULL, start, NULL);
+  assert_perror (errno);
+  assert (i == 0);
+  if (ptraceme)
+    {
+      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..0ab21f4
--- /dev/null
+++ b/tests/backtrace-dwarf.c
@@ -0,0 +1,194 @@
+/* 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)
+    {
+      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..bd364e6
--- /dev/null
+++ b/tests/backtrace.c
@@ -0,0 +1,444 @@
+/* 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;
+}
+
+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..a43d08f
--- /dev/null
+++ b/tests/run-backtrace-data.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-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]