summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Kratochvil <jan.kratochvil@redhat.com>2013-12-02 20:54:28 +0100
committerJan Kratochvil <jan.kratochvil@redhat.com>2013-12-02 20:54:28 +0100
commit8ae9bc9d315be6ecbb97dd9754b9d8424b5a70f0 (patch)
tree5f3205e04dcfed593f29593eba024d2ca271a982
parent5ef96c979ca45d0bf8c44f082df4da6b780010db (diff)
downloadelfutils-8ae9bc9d315be6ecbb97dd9754b9d8424b5a70f0.tar.gz
Tests for unwinder of x86*.
Signed-off-by: Jan Kratochvil <jan.kratochvil@redhat.com>
-rw-r--r--ChangeLog4
-rw-r--r--configure.ac4
-rw-r--r--m4/ChangeLog4
-rw-r--r--m4/biarch.m44
-rw-r--r--tests/ChangeLog38
-rw-r--r--tests/Makefile.am40
-rw-r--r--tests/backtrace-child.c221
-rw-r--r--tests/backtrace-data.c325
-rw-r--r--tests/backtrace-dwarf.c195
-rw-r--r--tests/backtrace-subr.sh115
-rw-r--r--tests/backtrace.c452
-rw-r--r--tests/backtrace.i386.core.bz2bin0 -> 8777 bytes
-rw-r--r--tests/backtrace.i386.exec.bz2bin0 -> 383356 bytes
-rw-r--r--tests/backtrace.x86_64.core.bz2bin0 -> 11115 bytes
-rw-r--r--tests/backtrace.x86_64.exec.bz2bin0 -> 401581 bytes
-rw-r--r--tests/cleanup-13.c334
-rwxr-xr-xtests/run-backtrace-core-i386.sh20
-rwxr-xr-xtests/run-backtrace-core-x86_64.sh20
-rwxr-xr-xtests/run-backtrace-data.sh24
-rwxr-xr-xtests/run-backtrace-dwarf.sh20
-rwxr-xr-xtests/run-backtrace-native-biarch.sh24
-rwxr-xr-xtests/run-backtrace-native-core-biarch.sh24
-rwxr-xr-xtests/run-backtrace-native-core.sh20
-rwxr-xr-xtests/run-backtrace-native.sh20
24 files changed, 1901 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index df993090..1347f1fe 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2013-12-02 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * configure.ac (CC_BIARCH): Remove AS_IF for it.
+
2013-11-07 Jan Kratochvil <jan.kratochvil@redhat.com>
* configure.ac: New AC_CHECK_SIZEOF for long. Call utrace_BIARCH, new
diff --git a/configure.ac b/configure.ac
index 99b74ae5..72fb3e8d 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/ChangeLog b/m4/ChangeLog
index 0fc78713..c950e54b 100644
--- a/m4/ChangeLog
+++ b/m4/ChangeLog
@@ -1,3 +1,7 @@
+2013-12-02 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ * biarch.m4 (utrace_BIARCH): Call AC_MSG_WARN if !BIARCH.
+
2013-11-07 Roland McGrath <roland@redhat.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
diff --git a/m4/biarch.m4 b/m4/biarch.m4
index a15323e2..04c8dbab 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/ChangeLog b/tests/ChangeLog
index 6c0ec364..9bd2fe81 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,41 @@
+2013-12-02 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.
+
2013-11-27 Mark Wielaard <mjw@redhat.com>
* dwflsyms.c (gelf_bind_order): New function.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index cedb6845..a74830ba 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 00000000..2bc0eded
--- /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 00000000..f54263b9
--- /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 00000000..92c892dc
--- /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 00000000..39d40b3f
--- /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 00000000..06d38782
--- /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/backtrace.i386.core.bz2 b/tests/backtrace.i386.core.bz2
new file mode 100644
index 00000000..e120d9bd
--- /dev/null
+++ b/tests/backtrace.i386.core.bz2
Binary files differ
diff --git a/tests/backtrace.i386.exec.bz2 b/tests/backtrace.i386.exec.bz2
new file mode 100644
index 00000000..1b0f001c
--- /dev/null
+++ b/tests/backtrace.i386.exec.bz2
Binary files differ
diff --git a/tests/backtrace.x86_64.core.bz2 b/tests/backtrace.x86_64.core.bz2
new file mode 100644
index 00000000..1f34e206
--- /dev/null
+++ b/tests/backtrace.x86_64.core.bz2
Binary files differ
diff --git a/tests/backtrace.x86_64.exec.bz2 b/tests/backtrace.x86_64.exec.bz2
new file mode 100644
index 00000000..70a151be
--- /dev/null
+++ b/tests/backtrace.x86_64.exec.bz2
Binary files differ
diff --git a/tests/cleanup-13.c b/tests/cleanup-13.c
new file mode 100644
index 00000000..b87c6965
--- /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 00000000..7294ec3d
--- /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 00000000..d00cd6d6
--- /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 00000000..30127c34
--- /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 00000000..b24215e3
--- /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 00000000..3a407c89
--- /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 00000000..fbd80256
--- /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 00000000..cb025a56
--- /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 00000000..ddae345d
--- /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