[PATCH] libdw: Make dwarf_getfuncs find all (defining) DW_TAG_subprogram DIEs.
by Mark Wielaard
dwarf_getfuncs used to return only the DW_TAG_subprogram DIEs that were
direct children of the given CU. This is normally how GCC outputs the
subprogram DIEs. But not always. For nested functions the subprogram DIE
is placed under the subprogram DIE where it is nested. Other compilers
might output the defining subprogram DIE of a C++ class function under
the DW_TAG_namespace DIE where it was defined. Both such constructs seem
allowed by the DWARF specification. So just searching the CU DIE children
was wrong.
To find all (defining) subprogram DIEs in a CU dwarf_getfuncs should
use __libdw_visit_scopes to walk the tree. The only tricky part is
making sure the offset returned and used when the callback returns
DWARF_CB_ABORT is correct and the search continues at the right spot
in the CU DIE tree.
Two new testcases were added that fail without this patch. And the
allfcts test was tweaked so that it always returns DWARF_CB_ABORT
from its callback to make sure the offset handling is correct.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 6 +++
libdw/dwarf_getfuncs.c | 79 ++++++++++++++++++++++++++-------------
tests/ChangeLog | 10 +++++
tests/Makefile.am | 1 +
tests/allfcts.c | 12 ++++-
tests/run-allfcts.sh | 56 +++++++++++++++++++++++++++-
tests/testfile_class_func.bz2 | Bin 0 -> 2962 bytes
tests/testfile_nested_funcs.bz2 | Bin 0 -> 3045 bytes
8 files changed, 134 insertions(+), 30 deletions(-)
create mode 100755 tests/testfile_class_func.bz2
create mode 100755 tests/testfile_nested_funcs.bz2
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index c8398b2..07090ba 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,9 @@
+2013-09-18 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_getfuncs.c (visitor_info): New struct.
+ (tree_visitor): New function.
+ (dwarf_getfuncs): Use __libdw_visit_scopes with tree_visitor.
+
2013-08-24 Mark Wielaard <mjw(a)redhat.com>
* dwarf_getlocation.c (store_implicit_value): Don't take data
diff --git a/libdw/dwarf_getfuncs.c b/libdw/dwarf_getfuncs.c
index afc5b6e..c5a7618 100644
--- a/libdw/dwarf_getfuncs.c
+++ b/libdw/dwarf_getfuncs.c
@@ -1,5 +1,5 @@
/* Get function information.
- Copyright (C) 2005 Red Hat, Inc.
+ Copyright (C) 2005, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
@@ -35,6 +35,51 @@
#include "libdwP.h"
+struct visitor_info
+{
+ /* The user callback of dwarf_getfuncs. */
+ int (*callback) (Dwarf_Die *, void *);
+
+ /* The user arg value to dwarf_getfuncs. */
+ void *arg;
+
+ /* The DIE offset where to (re)start the search. */
+ Dwarf_Off offset;
+};
+
+static int
+tree_visitor (unsigned int depth __attribute__ ((unused)),
+ struct Dwarf_Die_Chain *chain, void *arg)
+{
+ struct visitor_info *const v = arg;
+ Dwarf_Die *die = &chain->die;
+ Dwarf_Off offset = v->offset;
+ Dwarf_Off die_offset = INTUSE(dwarf_dieoffset) (die);
+
+ /* Skip any subtrees till we find the first DIE offset we are
+ interested in. */
+ if (die_offset < offset)
+ {
+ Dwarf_Die sibling;
+ if (INTUSE(dwarf_siblingof) (die, &sibling) == 0
+ && INTUSE(dwarf_dieoffset) (&sibling) <= offset)
+ {
+ chain->prune = true;
+ return DWARF_CB_OK;
+ }
+ }
+
+ /* If we aren't past the requested DIE offset or this isn't a
+ (defining) subprogram entity, skip DIE. */
+ if (die_offset <= offset
+ || INTUSE(dwarf_tag) (die) != DW_TAG_subprogram
+ || INTUSE(dwarf_hasattr) (die, DW_AT_declaration))
+ return DWARF_CB_OK;
+
+ v->offset = die_offset;
+ return (*v->callback) (die, v->arg);
+}
+
ptrdiff_t
dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *),
void *arg, ptrdiff_t offset)
@@ -43,31 +88,13 @@ dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *),
|| INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit))
return -1;
- Dwarf_Die die_mem;
- Dwarf_Die *die;
+ struct visitor_info v = { callback, arg, offset };
+ struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu),
+ .parent = NULL };
+ int res = __libdw_visit_scopes (0, &chain, &tree_visitor, NULL, &v);
- int res;
- if (offset == 0)
- res = INTUSE(dwarf_child) (cudie, &die_mem);
+ if (res == DWARF_CB_ABORT)
+ return v.offset;
else
- {
- die = INTUSE(dwarf_offdie) (cudie->cu->dbg, offset, &die_mem);
- res = INTUSE(dwarf_siblingof) (die, &die_mem);
- }
- die = res != 0 ? NULL : &die_mem;
-
- while (die != NULL)
- {
- if (INTUSE(dwarf_tag) (die) == DW_TAG_subprogram)
- {
- if (callback (die, arg) != DWARF_CB_OK)
- return INTUSE(dwarf_dieoffset) (die);
- }
-
- if (INTUSE(dwarf_siblingof) (die, &die_mem) != 0)
- break;
- }
-
- /* That's all. */
- return 0;
+ return res;
}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 9ea285f..810c89a 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,13 @@
+2013-09-18 Mark Wielaard <mjw(a)redhat.com>
+
+ * allfcts.c (cb): Return DWARF_CB_ABORT.
+ (main): Iterate over all offsets returned by dwarf_getfuncs.
+ * run-allfcts.sh: Add nested_funcs and class_func testcases.
+ * testfile_nested_funcs.bz2: New test file.
+ * testfile_class_func.bz2: Likewise.
+ * Makefile.am (EXTRA_DIST): Add testfile_class_func.bz2 and
+ testfile_nested_funcs.bz2.
+
2013-08-30 Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (check_PROGRAMS): Add varlocs.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e06d914..58db6c3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -119,6 +119,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile5.bz2 testfile6.bz2 testfile7.bz2 testfile8.bz2 \
testfile9.bz2 testfile10.bz2 testfile11.bz2 testfile12.bz2 \
testfile13.bz2 run-strip-test3.sh run-allfcts.sh \
+ testfile_class_func.bz2 testfile_nested_funcs.bz2 \
run-line2addr.sh run-elflint-test.sh testfile14.bz2 \
run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \
run-strip-test7.sh run-strip-test8.sh run-strip-groups.sh \
diff --git a/tests/allfcts.c b/tests/allfcts.c
index f14b493..7803722 100644
--- a/tests/allfcts.c
+++ b/tests/allfcts.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Red Hat, Inc.
+/* Copyright (C) 2005, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -34,7 +34,7 @@ cb (Dwarf_Die *func, void *arg __attribute__ ((unused)))
printf ("%s:%d:%s\n", file, line, fct);
- return DWARF_CB_OK;
+ return DWARF_CB_ABORT;
}
@@ -57,7 +57,13 @@ main (int argc, char *argv[])
Dwarf_Die die_mem;
Dwarf_Die *die = dwarf_offdie (dbg, off + cuhl, &die_mem);
- (void) dwarf_getfuncs (die, cb, NULL, 0);
+ /* Explicitly stop in the callback and then resume each time. */
+ ptrdiff_t doff = 0;
+ do
+ {
+ doff = dwarf_getfuncs (die, cb, NULL, doff);
+ }
+ while (doff > 0);
off = noff;
}
diff --git a/tests/run-allfcts.sh b/tests/run-allfcts.sh
index 30f7dd4..6eaf13c 100755
--- a/tests/run-allfcts.sh
+++ b/tests/run-allfcts.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 2005 Red Hat, Inc.
+# Copyright (C) 2005, 2013 Red Hat, Inc.
# This file is part of elfutils.
# Written by Ulrich Drepper <drepper(a)redhat.com>, 2005.
#
@@ -37,4 +37,58 @@ testrun_compare ${abs_builddir}/allfcts testfile testfile2 testfile8 <<\EOF
/home/drepper/gnu/elfutils/build/src/../../src/strip.c:313:handle_elf
EOF
+# = nested_funcs.c =
+#
+# static int
+# foo (int x)
+# {
+# int bar (int y)
+# {
+# return x - y;
+# }
+#
+# return bar (x * 2);
+# }
+#
+# int
+# main (int argc, char ** argv)
+# {
+# return foo (argc);
+# }
+#
+# gcc -g -o nested_funcs nested_funcs.c
+
+# = class_func.cxx =
+#
+# namespace foobar
+# {
+# class Foo
+# {
+# public:
+# int bar(int x);
+# };
+#
+# int Foo::bar(int x) { return x - 42; }
+# };
+#
+# int
+# main (int argc, char **argv)
+# {
+# foobar::Foo foo;
+#
+# return foo.bar (42);
+# }
+#
+# clang++ -g -o class_func class_func.cxx
+
+testfiles testfile_nested_funcs testfile_class_func
+
+testrun_compare ${abs_builddir}/allfcts testfile_nested_funcs testfile_class_func <<\EOF
+/home/mark/src/tests/nested/nested_funcs.c:2:foo
+/home/mark/src/tests/nested/nested_funcs.c:4:bar
+/home/mark/src/tests/nested/nested_funcs.c:13:main
+/home/mark/src/tests/nested/class_func.cxx:6:bar
+/home/mark/src/tests/nested/class_func.cxx:13:main
+EOF
+
exit 0
diff --git a/tests/testfile_class_func.bz2 b/tests/testfile_class_func.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..e40dcf2623637a709110115e013273e3e8277eaa
GIT binary patch
literal 2962
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
I0;uKJAjNljEdT%j
literal 0
HcmV?d00001
diff --git a/tests/testfile_nested_funcs.bz2 b/tests/testfile_nested_funcs.bz2
new file mode 100755
index 0000000000000000000000000000000000000000..d36b603ebd25ddd53367909dc7512b05930fdd6b
GIT binary patch
literal 3045
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
literal 0
HcmV?d00001
--
1.7.1
10 years, 1 month
[PATCH 1/2] Show contents NT_SIGINFO core note in readelf
by Petr Machata
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
src/ChangeLog | 6 ++
src/readelf.c | 103 +++++++++++++++++++++++++++++++++++
tests/ChangeLog | 6 ++
tests/Makefile.am | 2 +-
tests/run-readelf-mixed-corenote.sh | 59 ++++++++++++++++++++-
tests/testfile71.bz2 | Bin 0 -> 18164 bytes
6 files changed, 174 insertions(+), 2 deletions(-)
create mode 100644 tests/testfile71.bz2
diff --git a/src/ChangeLog b/src/ChangeLog
index 6788087..9d4c2e2 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,9 @@
+2013-09-26 Petr Machata <pmachata(a)redhat.com>
+
+ * readelf.c (handle_siginfo_note): New function.
+ (handle_notes_data): Call it to handle NT_SIGINFO notes.
+ (buf_read_int, buf_read_ulong, buf_has_data): New functions.
+
2013-08-13 Mark Wielaard <mjw(a)redhat.com>
* addr2line.c (options): Add "inlines", 'i'.
diff --git a/src/readelf.c b/src/readelf.c
index 119c100..da3661c 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -42,6 +42,7 @@
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
+#include <signal.h>
#include <system.h>
#include "../libelf/libelfP.h"
@@ -8616,6 +8617,104 @@ handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos)
}
}
+static bool
+buf_has_data (unsigned char const *ptr, unsigned char const *end, size_t sz)
+{
+ return ptr < end && (size_t) (end - ptr) >= sz;
+}
+
+static bool
+buf_read_int (Elf *core, unsigned char const **ptrp, unsigned char const *end,
+ int *retp)
+{
+ if (! buf_has_data (*ptrp, end, 4))
+ return false;
+
+ *ptrp = convert (core, ELF_T_WORD, 1, retp, *ptrp, 4);
+ return true;
+}
+
+static bool
+buf_read_ulong (Elf *core, unsigned char const **ptrp, unsigned char const *end,
+ uint64_t *retp)
+{
+ size_t sz = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT);
+ if (! buf_has_data (*ptrp, end, sz))
+ return false;
+
+ union
+ {
+ uint64_t u64;
+ uint32_t u32;
+ } u;
+
+ *ptrp = convert (core, ELF_T_ADDR, 1, &u, *ptrp, sizeof u);
+
+ if (sz == 4)
+ *retp = u.u32;
+ else
+ *retp = u.u64;
+ return true;
+}
+
+static void
+handle_siginfo_note (Elf *core, GElf_Word descsz, GElf_Off desc_pos)
+{
+ Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_BYTE);
+ if (data == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+ unsigned char const *ptr = data->d_buf;
+ unsigned char const *const end = data->d_buf + data->d_size;
+
+ /* Siginfo head is three ints: signal number, error number, origin
+ code. */
+ int si_signo, si_errno, si_code;
+ if (! buf_read_int (core, &ptr, end, &si_signo)
+ || ! buf_read_int (core, &ptr, end, &si_errno)
+ || ! buf_read_int (core, &ptr, end, &si_code))
+ {
+ fail:
+ printf (" Not enough data in NT_SIGINFO note.\n");
+ return;
+ }
+
+ /* Next is a pointer-aligned union of structures. On 64-bit
+ machines, that implies a word of padding. */
+ if (gelf_getclass (core) == ELFCLASS64)
+ ptr += 4;
+
+ printf (" si_signo: %d, si_errno: %d, si_code: %d\n",
+ si_signo, si_errno, si_code);
+
+ if (si_code > 0)
+ switch (si_signo)
+ {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ {
+ uint64_t addr;
+ if (! buf_read_ulong (core, &ptr, end, &addr))
+ goto fail;
+ printf (" fault address: %#" PRIx64 "\n", addr);
+ break;
+ }
+ default:
+ ;
+ }
+ else if (si_code == SI_USER)
+ {
+ int pid, uid;
+ if (! buf_read_int (core, &ptr, end, &pid)
+ || ! buf_read_int (core, &ptr, end, &uid))
+ goto fail;
+ printf (" sender PID: %d, sender UID: %d\n", pid, uid);
+ }
+}
+
static void
handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr,
const char *name, const void *desc)
@@ -8689,6 +8788,10 @@ handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr,
&& !memcmp (name, "CORE", 4))
handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz,
start + desc_offset);
+ else if (nhdr.n_type == NT_SIGINFO
+ && nhdr.n_namesz == 5 && strcmp (name, "CORE") == 0)
+ handle_siginfo_note (ebl->elf, nhdr.n_descsz,
+ start + desc_offset);
else
handle_core_note (ebl, &nhdr, name, desc);
}
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 34cffd4..7e71661 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,9 @@
+2013-09-26 Petr Machata <pmachata(a)redhat.com>
+
+ * Makefile.am (EXTRA_DIST): Add testfile71.bz2.
+ * run-readelf-mixed-corenote.sh: New test for this file.
+ * testfile71.bz2: New file.
+
2013-09-20 Mark Wielaard <mjw(a)redhat.com>
* allfcts.c (cb): Return DWARF_CB_ABORT.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 58db6c3..0024395 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -198,7 +198,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-readelf-mixed-corenote.sh testfile63.bz2 testfile64.bz2 \
testfile65.bz2 testfile67.bz2 testfile68.bz2 \
testfile69.core.bz2 testfile69.so.bz2 \
- testfile70.core.bz2 testfile70.exec.bz2 \
+ testfile70.core.bz2 testfile70.exec.bz2 testfile71.bz2 \
run-dwfllines.sh run-dwfl-report-elf-align.sh \
testfile-dwfl-report-elf-align-shlib.so.bz2 \
testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \
diff --git a/tests/run-readelf-mixed-corenote.sh b/tests/run-readelf-mixed-corenote.sh
index 915bdeb..8823c3e 100755
--- a/tests/run-readelf-mixed-corenote.sh
+++ b/tests/run-readelf-mixed-corenote.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 2012 Red Hat, Inc.
+# Copyright (C) 2012, 2013 Red Hat, Inc.
# This file is part of elfutils.
#
# This file is free software; you can redistribute it and/or modify
@@ -217,4 +217,61 @@ Note segment of 852 bytes at offset 0x94:
high_r15: 0x00000000
EOF
+# To reproduce this core dump, do this on x86_64 machine with Linux
+# 3.7 or later:
+# $ gcc -x c <(echo 'int main () { return *(int *)0x12345678; }')
+# $ ./a.out
+testfiles testfile71
+testrun_compare ${abs_top_builddir}/src/readelf -n testfile71 <<\EOF
+
+Note segment of 1476 bytes at offset 0x430:
+ Owner Data size Type
+ CORE 336 PRSTATUS
+ info.si_signo: 11, info.si_code: 0, info.si_errno: 0, cursig: 11
+ sigpend: <>
+ sighold: <>
+ pid: 9664, ppid: 2868, pgrp: 9664, sid: 2868
+ utime: 0.000000, stime: 0.004000, cutime: 0.000000, cstime: 0.000000
+ orig_rax: -1, fpvalid: 0
+ r15: 0 r14: 0
+ r13: 140734971656848 r12: 4195328
+ rbp: 0x00007fff69fe39b0 rbx: 0
+ r11: 266286012928 r10: 140734971656256
+ r9: 0 r8: 266289790592
+ rax: 305419896 rcx: 4195584
+ rdx: 140734971656872 rsi: 140734971656856
+ rdi: 1 rip: 0x00000000004004f9
+ rflags: 0x0000000000010246 rsp: 0x00007fff69fe39b0
+ fs.base: 0x00007fa1c8933740 gs.base: 0x0000000000000000
+ cs: 0x0033 ss: 0x002b ds: 0x0000 es: 0x0000 fs: 0x0000 gs: 0x0000
+ CORE 136 PRPSINFO
+ state: 0, sname: R, zomb: 0, nice: 0, flag: 0x0000000000000200
+ uid: 1000, gid: 1000, pid: 9664, ppid: 2868, pgrp: 9664, sid: 2868
+ fname: a.out, psargs: ./a.out
+ CORE 128 SIGINFO
+ si_signo: 11, si_errno: 0, si_code: 1
+ fault address: 0x12345678
+ CORE 304 AUXV
+ SYSINFO_EHDR: 0x7fff69ffe000
+ HWCAP: 0xafebfbff <fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss tm pbe>
+ PAGESZ: 4096
+ CLKTCK: 100
+ PHDR: 0x400040
+ PHENT: 56
+ PHNUM: 9
+ BASE: 0
+ FLAGS: 0
+ ENTRY: 0x400400
+ UID: 1000
+ EUID: 1000
+ GID: 1000
+ EGID: 1000
+ SECURE: 0
+ RANDOM: 0x7fff69fe3d19
+ EXECFN: 0x7fff69fe4ff0
+ PLATFORM: 0x7fff69fe3d29
+ NULL
+ CORE 469 FILE
+EOF
+
exit 0
diff --git a/tests/testfile71.bz2 b/tests/testfile71.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..ce5b08fea4cad214c72e1d7773cfc34357957a1f
GIT binary patch
literal 18164
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
literal 0
HcmV?d00001
--
1.7.6.5
10 years, 1 month
[patch v4] unwinder: The unwinder (x86* only)
by Jan Kratochvil
Hi Mark,
jankratochvil/unwindx86
here is the refactored version addressing the paragraph from my last post
[patch v3] unwinder: The unwinder (x86* only)x
https://lists.fedorahosted.org/pipermail/elfutils-devel/2013-June/003109....
Message-ID: <20130623183057.GA9934(a)host2.jankratochvil.net>
# I am just aware that maybe there should have been special "user data"/"arg"
# carried between next_thread -> set_initial_registers. One cannot depend in
# set_initial_registers that passed THREAD is the one returned by last
# next_thread call. This leads to currently quadratic (possibly n*log(n) with
# more complicated code) search in core_set_initial_registers.
So former 'arg' is now split into 'dwfl_arg' and 'thread_arg' and
core_set_initial_registers complexity went linear->constant.
Thanks,
Jan
commit 09dab4587cd0d61cb19f5d2e5204e9ccf1204ac8
Author: Jan Kratochvil <jan.kratochvil(a)redhat.com>
Date: Thu May 30 14:37:38 2013 +0200
Unwinder for x86*.
backends/
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (AM_CPPFLAGS): Add ../libdwfl.
(i386_SRCS): Add i386_initreg.c.
(x86_64_SRCS): Add x86_64_initreg.c.
* i386_initreg.c: New file.
* i386_init.c (i386_init): Initialize frame_nregs and
set_initial_registers_tid.
* x86_64_initreg.c: New file.
* x86_64_init.c (x86_64_init): Initialize frame_nregs and
set_initial_registers_tid.
./
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* configure.ac: New AC_CHECK_SIZEOF for long. Call utrace_BIARCH, new
AC_SUBST for CC_BIARCH.
libdw/
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* cfi.h (struct Dwarf_Frame_s): Make the comment more specific.
* libdw.map (ELFUTILS_0.156): Add dwfl_attach_state, dwfl_pid,
dwfl_thread_dwfl, dwfl_thread_tid, dwfl_frame_thread,
dwfl_thread_state_registers, dwfl_thread_state_register_pc,
dwfl_next_thread, dwfl_thread_getframes and dwfl_frame_pc.
libdwfl/
2013-09-02 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (AM_CPPFLAGS): Add ../libasm.
(libdwfl_a_SOURCES): Add dwfl_frame.c, dwfl_frame_unwind.c,
dwfl_frame_pc.c, dwfl_frame_pid.c, dwfl_frame_core.c and
dwfl_frame_regs.c.
* core-file.c (dwfl_core_file_report): Call
__libdwfl_attach_state_for_core.
* dwfl_end.c (dwfl_end): Call __libdwfl_process_free.
* dwfl_frame.c: New file.
* dwfl_frame_unwind.c: New file.
* dwfl_frame_pc.c: New file.
* dwfl_frame_pid.c: New file.
* dwfl_frame_core.c: New file.
* dwfl_frame_regs.c: New file.
* libdwfl.h: Include libebl.h.
(Dwfl_Thread, Dwfl_Frame): New typedefs.
(dwfl_core_file_report, dwfl_linux_proc_report): Extend comments.
(Dwfl_Thread_Callbacks): New definition.
(dwfl_attach_state, dwfl_pid, dwfl_thread_dwfl, dwfl_thread_tid)
(dwfl_frame_thread, dwfl_thread_state_registers)
(dwfl_thread_state_register_pc, dwfl_next_thread, dwfl_thread_getframes)
(dwfl_frame_pc): New declarations.
* libdwflP.h: Include libeblP.h.
(Dwfl_Process): New typedef.
(LIBEBL_BAD, CORE_MISSING, INVALID_REGISTER, PROCESS_MEMORY_READ)
(PROCESS_NO_ARCH, PARSE_PROC, NO_THREAD, INVALID_DWARF)
(UNSUPPORTED_DWARF, NEXT_THREAD_FAIL, ATTACH_STATE_CONFLICT)
(NO_ATTACH_STATE): New DWFL_ERROR entries.
(struct Dwfl): New entry process.
(struct Dwfl_Process, struct Dwfl_Thread, struct Dwfl_Frame)
(dwfl_frame_reg_get, dwfl_frame_reg_set): New definitions.
(__libdwfl_process_free, __libdwfl_frame_unwind)
(__libdwfl_attach_state_for_pid, __libdwfl_attach_state_for_core)
(__libdwfl_segment_start, __libdwfl_segment_end): New declarations.
(dwfl_attach_state, dwfl_pid, dwfl_thread_dwfl, dwfl_thread_tid)
(dwfl_frame_thread, dwfl_thread_state_registers)
(dwfl_thread_state_register_pc, dwfl_next_thread, dwfl_thread_getframes)
(dwfl_frame_pc): New INTDECL entries.
* linux-proc-maps.c (dwfl_linux_proc_report): Call
__libdwfl_attach_state_for_pid.
* segment.c (segment_start): Rename to ...
(__libdwfl_segment_start): ... here and make it internal_function.
(segment_end): Rename to ...
(__libdwfl_segment_end): ... here and make it internal_function.
(reify_segments, dwfl_report_segment): Rename them at the callers.
libebl/
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* Makefile.am (AM_CPPFLAGS): Add ../libdwfl.
(gen_SOURCES): Add eblinitreg.c.
* ebl-hooks.h (set_initial_registers_tid): New entry.
* eblinitreg.c: New file.
* libebl.h (dwfl_thread_state_registers_t): New definition.
(ebl_set_initial_registers_tid, ebl_frame_nregs): New declarations.
* libeblP.h (Dwfl_Module): New declaration.
(struct ebl): New entry frame_nregs.
m4/
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
* biarch.m4: New file.
src/
2013-06-23 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (bin_PROGRAMS): Add stack.
(stack_LDADD): New.
* stack.c: New file.
tests/
2013-09-02 Jan Kratochvil <jan.kratochvil(a)redhat.com>
Mark Wielaard <mjw(a)redhat.com>
* Makefile.am (check_PROGRAMS): Add backtrace, backtrace-child and
backtrace-data.
(BUILT_SOURCES, clean-local, backtrace-child-biarch): New.
(TESTS): Add run-backtrace.sh.
(backtrace_LDADD, backtrace_child_CFLAGS, backtrace_child_LDFLAGS)
(backtrace_data_LDADD): New.
* backtrace-child.c: New file.
* backtrace-data.c: New file.
* backtrace.c: New file.
* run-backtrace.sh: New file.
Signed-off-by: Jan Kratochvil <jan.kratochvil(a)redhat.com>
diff --git a/backends/Makefile.am b/backends/Makefile.am
index 1923702..d1affee 100644
--- a/backends/Makefile.am
+++ b/backends/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to create Makefile.in
##
-## Copyright (C) 2000-2010 Red Hat, Inc.
+## Copyright (C) 2000-2010, 2013 Red Hat, Inc.
## Copyright (C) 2012 Tilera Corporation
## This file is part of elfutils.
##
@@ -29,7 +29,8 @@
## not, see <http://www.gnu.org/licenses/>.
include $(top_srcdir)/config/eu.am
AM_CPPFLAGS += -I$(top_srcdir)/libebl -I$(top_srcdir)/libasm \
- -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw
+ -I$(top_srcdir)/libelf -I$(top_srcdir)/libdw \
+ -I$(top_srcdir)/libdwfl
modules = i386 sh x86_64 ia64 alpha arm sparc ppc ppc64 s390 tilegx
@@ -50,7 +51,8 @@ libdw = ../libdw/libdw.so
endif
i386_SRCS = i386_init.c i386_symbol.c i386_corenote.c i386_cfi.c \
- i386_retval.c i386_regs.c i386_auxv.c i386_syscall.c
+ i386_retval.c i386_regs.c i386_auxv.c i386_syscall.c \
+ i386_initreg.c
cpu_i386 = ../libcpu/libcpu_i386.a
libebl_i386_pic_a_SOURCES = $(i386_SRCS)
am_libebl_i386_pic_a_OBJECTS = $(i386_SRCS:.c=.os)
@@ -60,7 +62,8 @@ libebl_sh_pic_a_SOURCES = $(sh_SRCS)
am_libebl_sh_pic_a_OBJECTS = $(sh_SRCS:.c=.os)
x86_64_SRCS = x86_64_init.c x86_64_symbol.c x86_64_corenote.c x86_64_cfi.c \
- x86_64_retval.c x86_64_regs.c i386_auxv.c x86_64_syscall.c
+ x86_64_retval.c x86_64_regs.c i386_auxv.c x86_64_syscall.c \
+ x86_64_initreg.c
cpu_x86_64 = ../libcpu/libcpu_x86_64.a
libebl_x86_64_pic_a_SOURCES = $(x86_64_SRCS)
am_libebl_x86_64_pic_a_OBJECTS = $(x86_64_SRCS:.c=.os)
diff --git a/backends/i386_init.c b/backends/i386_init.c
index cc9b2d7..1e0b486 100644
--- a/backends/i386_init.c
+++ b/backends/i386_init.c
@@ -1,5 +1,5 @@
/* Initialization of i386 specific backend library.
- Copyright (C) 2000-2009 Red Hat, Inc.
+ Copyright (C) 2000-2009, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2000.
@@ -63,6 +63,9 @@ i386_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, disasm);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. For i386 it is 17, why? */
+ eh->frame_nregs = 9;
+ HOOK (eh, set_initial_registers_tid);
return MODVERSION;
}
diff --git a/backends/i386_initreg.c b/backends/i386_initreg.c
new file mode 100644
index 0000000..4f4643b
--- /dev/null
+++ b/backends/i386_initreg.c
@@ -0,0 +1,80 @@
+/* Fetch process data from STATE->base->pid or STATE->base->core.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined __i386__ || defined __x86_64__
+# include <sys/types.h>
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND i386_
+#include "libebl_CPU.h"
+
+bool
+i386_set_initial_registers_tid (Dwfl_Thread *thread, pid_t tid,
+ dwfl_thread_state_registers_t *setfunc)
+{
+#if !defined __i386__ && !defined __x86_64__
+ return false;
+#else /* __i386__ || __x86_64__ */
+ struct user_regs_struct user_regs;
+ if (ptrace (PTRACE_GETREGS, tid, NULL, &user_regs) != 0)
+ return false;
+ Dwarf_Word dwarf_regs[9];
+# if defined __i386__
+ dwarf_regs[0] = user_regs.eax;
+ dwarf_regs[1] = user_regs.ecx;
+ dwarf_regs[2] = user_regs.edx;
+ dwarf_regs[3] = user_regs.ebx;
+ dwarf_regs[4] = user_regs.esp;
+ dwarf_regs[5] = user_regs.ebp;
+ dwarf_regs[6] = user_regs.esi;
+ dwarf_regs[7] = user_regs.edi;
+ dwarf_regs[8] = user_regs.eip;
+# elif defined __x86_64__
+ dwarf_regs[0] = user_regs.rax;
+ dwarf_regs[1] = user_regs.rcx;
+ dwarf_regs[2] = user_regs.rdx;
+ dwarf_regs[3] = user_regs.rbx;
+ dwarf_regs[4] = user_regs.rsp;
+ dwarf_regs[5] = user_regs.rbp;
+ dwarf_regs[6] = user_regs.rsi;
+ dwarf_regs[7] = user_regs.rdi;
+ dwarf_regs[8] = user_regs.rip;
+# else /* (__i386__ || __x86_64__) && (!__i386__ && !__x86_64__) */
+# error
+# endif /* (__i386__ || __x86_64__) && (!__i386__ && !__x86_64__) */
+ return setfunc (thread, 0, 9, dwarf_regs);
+#endif /* __i386__ || __x86_64__ */
+ return true;
+}
diff --git a/backends/x86_64_init.c b/backends/x86_64_init.c
index 67a5880..b885558 100644
--- a/backends/x86_64_init.c
+++ b/backends/x86_64_init.c
@@ -1,5 +1,5 @@
/* Initialization of x86-64 specific backend library.
- Copyright (C) 2002-2009 Red Hat, Inc.
+ Copyright (C) 2002-2009, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -60,6 +60,9 @@ x86_64_init (elf, machine, eh, ehlen)
HOOK (eh, auxv_info);
HOOK (eh, disasm);
HOOK (eh, abi_cfi);
+ /* gcc/config/ #define DWARF_FRAME_REGISTERS. */
+ eh->frame_nregs = 17;
+ HOOK (eh, set_initial_registers_tid);
return MODVERSION;
}
diff --git a/backends/x86_64_initreg.c b/backends/x86_64_initreg.c
new file mode 100644
index 0000000..65bc616
--- /dev/null
+++ b/backends/x86_64_initreg.c
@@ -0,0 +1,73 @@
+/* Fetch live process Dwfl_Frame from PID.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#ifdef __x86_64__
+# include <sys/user.h>
+# include <sys/ptrace.h>
+#endif
+#include "libdwflP.h"
+
+#define BACKEND x86_64_
+#include "libebl_CPU.h"
+
+bool
+x86_64_set_initial_registers_tid (Dwfl_Thread *thread, pid_t tid,
+ dwfl_thread_state_registers_t *setfunc)
+{
+#ifndef __x86_64__
+ return false;
+#else /* __x86_64__ */
+ struct user_regs_struct user_regs;
+ if (ptrace (PTRACE_GETREGS, tid, NULL, &user_regs) != 0)
+ return false;
+ 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;
+ return setfunc (thread, 0, 17, dwarf_regs);
+#endif /* __x86_64__ */
+}
diff --git a/configure.ac b/configure.ac
index f2a24a0..ab68110 100644
--- a/configure.ac
+++ b/configure.ac
@@ -317,4 +317,15 @@ esac
# Round up to the next release API (x.y) version.
eu_version=$(( (eu_version + 999) / 1000 ))
+AC_CHECK_SIZEOF(long)
+
+# On a 64-bit host where can can use $CC -m32, we'll run two sets of tests.
+# 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"])
+AC_SUBST([CC_BIARCH])
+
AC_OUTPUT
diff --git a/libdw/cfi.h b/libdw/cfi.h
index 8949833..98ac6cf 100644
--- a/libdw/cfi.h
+++ b/libdw/cfi.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdw CFI interpreter.
- Copyright (C) 2009-2010 Red Hat, Inc.
+ Copyright (C) 2009-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -150,8 +150,8 @@ struct dwarf_frame_register
Dwarf_Sword value:(sizeof (Dwarf_Sword) * 8 - 3);
};
-/* This holds everything we know about the state of the frame
- at a particular PC location described by an FDE. */
+/* This holds instructions for unwinding frame at a particular PC location
+ described by an FDE. */
struct Dwarf_Frame_s
{
/* This frame description covers PC values in [start, end). */
diff --git a/libdw/libdw.map b/libdw/libdw.map
index d38a8ef..b9bc74b 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -259,4 +259,14 @@ ELFUTILS_0.156 {
global:
# Replaced ELFUTILS_0.122 version, which has a wrapper without add_p_vaddr.
dwfl_report_elf;
+ dwfl_attach_state;
+ dwfl_pid;
+ dwfl_thread_dwfl;
+ dwfl_thread_tid;
+ dwfl_frame_thread;
+ dwfl_thread_state_registers;
+ dwfl_thread_state_register_pc;
+ dwfl_next_thread;
+ dwfl_thread_getframes;
+ dwfl_frame_pc;
} ELFUTILS_0.149;
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 3ef4dd6..0ba2542 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -2,7 +2,7 @@
##
## Process this file with automake to create Makefile.in
##
-## Copyright (C) 2005-2010 Red Hat, Inc.
+## Copyright (C) 2005-2010, 2013 Red Hat, Inc.
## This file is part of elfutils.
##
## This file is free software; you can redistribute it and/or modify
@@ -31,7 +31,7 @@
##
include $(top_srcdir)/config/eu.am
AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
- -I$(srcdir)/../libdw
+ -I$(srcdir)/../libdw -I$(srcdir)/../libasm
VERSION = 1
noinst_LIBRARIES = libdwfl.a
@@ -68,7 +68,9 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \
dwfl_module_return_value_location.c \
dwfl_module_register_names.c \
dwfl_segment_report_module.c \
- link_map.c core-file.c open.c image-header.c
+ link_map.c core-file.c open.c image-header.c \
+ dwfl_frame.c dwfl_frame_unwind.c dwfl_frame_pc.c \
+ dwfl_frame_pid.c dwfl_frame_core.c dwfl_frame_regs.c
if ZLIB
libdwfl_a_SOURCES += gzip.c
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index 7207591..cc1da6c 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -1,5 +1,5 @@
/* Core file handling.
- Copyright (C) 2008-2010 Red Hat, Inc.
+ Copyright (C) 2008-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -521,6 +521,14 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf)
/* We return the number of modules we found if we found any.
If we found none, we return -1 instead of 0 if there was an
error rather than just nothing found. */
- return sniffed || listed >= 0 ? listed + sniffed : listed;
+ int retval = sniffed || listed >= 0 ? listed + sniffed : listed;
+ if (retval > 0)
+ {
+ /* Possible error is ignored, DWFL still may be useful for non-unwinding
+ operations. */
+ __libdwfl_attach_state_for_core (dwfl, elf);
+ }
+
+ return retval;
}
INTDEF (dwfl_core_file_report)
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
index 94fcfc6..33cae48 100644
--- a/libdwfl/dwfl_end.c
+++ b/libdwfl/dwfl_end.c
@@ -1,5 +1,5 @@
/* Finish a session using libdwfl.
- Copyright (C) 2005, 2008, 2012 Red Hat, Inc.
+ Copyright (C) 2005, 2008, 2012-2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -34,6 +34,9 @@ dwfl_end (Dwfl *dwfl)
if (dwfl == NULL)
return;
+ if (dwfl->process)
+ __libdwfl_process_free (dwfl->process);
+
free (dwfl->lookup_addr);
free (dwfl->lookup_module);
free (dwfl->lookup_segndx);
diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
new file mode 100644
index 0000000..58878f9
--- /dev/null
+++ b/libdwfl/dwfl_frame.c
@@ -0,0 +1,357 @@
+/* Get Dwarf Frame state for target PID or core file.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <sys/ptrace.h>
+#include <unistd.h>
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Set STATE->pc_set from STATE->regs according to the backend. Return true on
+ success, false on error. */
+static bool
+state_fetch_pc (Dwfl_Frame *state)
+{
+ switch (state->pc_state)
+ {
+ case DWFL_FRAME_STATE_PC_SET:
+ return true;
+ case DWFL_FRAME_STATE_PC_UNDEFINED:
+ abort ();
+ case DWFL_FRAME_STATE_ERROR:;
+ Ebl *ebl = state->thread->process->ebl;
+ Dwarf_CIE abi_info;
+ if (ebl_abi_cfi (ebl, &abi_info) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return false;
+ }
+ unsigned ra = abi_info.return_address_register;
+ /* dwarf_frame_state_reg_is_set is not applied here. */
+ if (ra >= ebl_frame_nregs (ebl))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL_BAD);
+ return false;
+ }
+ state->pc = state->regs[ra];
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+ return true;
+ }
+ abort ();
+}
+
+/* Do not call it on your own, to be used by thread_* functions only. */
+
+static void
+state_free (Dwfl_Frame *state)
+{
+ Dwfl_Thread *thread = state->thread;
+ assert (thread->unwound == state);
+ thread->unwound = state->unwound;
+ free (state);
+}
+
+/* Do not call it on your own, to be used by thread_* functions only. */
+
+static Dwfl_Frame *
+state_alloc (Dwfl_Thread *thread)
+{
+ assert (thread->unwound == NULL);
+ Ebl *ebl = thread->process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ if (nregs == 0)
+ return NULL;
+ assert (nregs < sizeof (((Dwfl_Frame *) NULL)->regs_set) * 8);
+ Dwfl_Frame *state = malloc (sizeof (*state) + sizeof (*state->regs) * nregs);
+ if (state == NULL)
+ return NULL;
+ state->thread = thread;
+ state->signal_frame = false;
+ state->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (state->regs_set, 0, sizeof (state->regs_set));
+ thread->unwound = state;
+ state->unwound = NULL;
+ return state;
+}
+
+/* Free and unlink THREAD from the internal lists. PREV_THREAD must be NULL if
+ THREAD was the first one or PREV_THREAD must be the preceding thread. */
+static void
+thread_free (Dwfl_Thread *thread, Dwfl_Thread *prev_thread)
+{
+ Dwfl_Process *process = thread->process;
+ assert (prev_thread == NULL || prev_thread->process == process);
+ assert (prev_thread != NULL || process->thread == thread);
+ assert (prev_thread == NULL || prev_thread->next == thread);
+ if (thread->thread_detach_needed)
+ {
+ assert (thread->tid > 0);
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ }
+ while (thread->unwound)
+ state_free (thread->unwound);
+ if (prev_thread == NULL)
+ process->thread = thread->next;
+ else
+ prev_thread->next = thread->next;
+ free (thread);
+}
+
+/* Allocate new Dwfl_Thread and link it to PROCESS. PREV_THREAD must be NULL
+ if this is the first thread for PROCESS, otherwise PREV_THREAD must be the
+ last thread of PROCESS. */
+static Dwfl_Thread *
+thread_alloc (Dwfl_Process *process, Dwfl_Thread *prev_thread)
+{
+ assert (prev_thread == NULL || prev_thread->process == process);
+ assert (prev_thread != NULL || process->thread == NULL);
+ assert (prev_thread == NULL || prev_thread->next == NULL);
+ Dwfl_Thread *thread = malloc (sizeof (*thread));
+ if (thread == NULL)
+ return NULL;
+ thread->process = process;
+ thread->unwound = NULL;
+ thread->tid = 0;
+ thread->next = NULL;
+ if (prev_thread == NULL)
+ process->thread = thread;
+ else
+ prev_thread->next = thread;
+ return thread;
+}
+
+void
+internal_function
+__libdwfl_process_free (Dwfl_Process *process)
+{
+ Dwfl *dwfl = process->dwfl;
+ if (process->callbacks->detach != NULL)
+ process->callbacks->detach (dwfl, process->callbacks_arg);
+ while (process->thread)
+ thread_free (process->thread, NULL);
+ assert (dwfl->process == process);
+ dwfl->process = NULL;
+ free (process);
+}
+
+/* Allocate new Dwfl_Process for DWFL. */
+static void
+process_alloc (Dwfl *dwfl)
+{
+ Dwfl_Process *process = malloc (sizeof (*process));
+ if (process == NULL)
+ return;
+ process->dwfl = dwfl;
+ process->thread = NULL;
+ dwfl->process = process;
+}
+
+bool
+dwfl_attach_state (Dwfl *dwfl, Ebl *ebl, pid_t pid,
+ const Dwfl_Thread_Callbacks *thread_callbacks, void *arg)
+{
+ if (thread_callbacks == NULL || thread_callbacks->next_thread == NULL
+ || thread_callbacks->set_initial_registers == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_UNKNOWN_ERROR);
+ return false;
+ }
+ if (dwfl->process != NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_ATTACH_STATE_CONFLICT);
+ return false;
+ }
+ if (ebl == NULL)
+ {
+ for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
+ {
+ Dwfl_Error error = __libdwfl_module_getebl (mod);
+ if (error != DWFL_E_NOERROR)
+ continue;
+ ebl = mod->ebl;
+ break;
+ }
+ if (ebl == NULL)
+ {
+ /* Not identified EBL from any of the modules. */
+ __libdwfl_seterrno (DWFL_E_PROCESS_NO_ARCH);
+ return false;
+ }
+ }
+ process_alloc (dwfl);
+ Dwfl_Process *process = dwfl->process;
+ if (process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ process->ebl = ebl;
+ process->pid = pid;
+ process->callbacks = thread_callbacks;
+ process->callbacks_arg = arg;
+ return true;
+}
+INTDEF(dwfl_attach_state)
+
+pid_t
+dwfl_pid (Dwfl *dwfl)
+{
+ if (dwfl->process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
+ return -1;
+ }
+ return dwfl->process->pid;
+}
+INTDEF(dwfl_pid)
+
+Dwfl *
+dwfl_thread_dwfl (Dwfl_Thread *thread)
+{
+ return thread->process->dwfl;
+}
+INTDEF(dwfl_thread_dwfl)
+
+pid_t
+dwfl_thread_tid (Dwfl_Thread *thread)
+{
+ return thread->tid;
+}
+INTDEF(dwfl_thread_tid)
+
+Dwfl_Thread *
+dwfl_frame_thread (Dwfl_Frame *state)
+{
+ return state->thread;
+}
+INTDEF(dwfl_frame_thread)
+
+Dwfl_Thread *
+dwfl_next_thread (Dwfl *dwfl, Dwfl_Thread *prev_thread)
+{
+ assert (prev_thread == NULL || prev_thread->process->dwfl == dwfl);
+ if (prev_thread != NULL && prev_thread->next != NULL)
+ return prev_thread->next;
+ Dwfl_Process *process = dwfl->process;
+ if (process == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_ATTACH_STATE);
+ return NULL;
+ }
+ Dwfl_Thread *nthread = thread_alloc (process, prev_thread);
+ if (nthread == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+ nthread->tid = process->callbacks->next_thread (dwfl, nthread,
+ process->callbacks_arg,
+ &nthread->callbacks_arg);
+ if (nthread->tid < 0)
+ {
+ thread_free (nthread, prev_thread);
+ __libdwfl_seterrno (DWFL_E_NEXT_THREAD_FAIL);
+ return NULL;
+ }
+ if (nthread->tid == 0)
+ {
+ thread_free (nthread, prev_thread);
+ __libdwfl_seterrno (DWFL_E_NOERROR);
+ return NULL;
+ }
+ return nthread;
+}
+INTDEF(dwfl_next_thread)
+
+int
+dwfl_thread_getframes (Dwfl_Thread *thread,
+ int (*callback) (Dwfl_Frame *state, void *arg),
+ void *arg)
+{
+ assert (thread->unwound == NULL);
+ if (state_alloc (thread) == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ Dwfl_Process *process = thread->process;
+ if (! process->callbacks->set_initial_registers (thread,
+ thread->callbacks_arg))
+ {
+ while (thread->unwound)
+ state_free (thread->unwound);
+ return -1;
+ }
+ thread->thread_detach_needed = true;
+ if (! state_fetch_pc (thread->unwound))
+ {
+ assert (thread->thread_detach_needed);
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ thread->thread_detach_needed = false;
+ while (thread->unwound)
+ state_free (thread->unwound);
+ return -1;
+ }
+
+ Dwfl_Frame *state = thread->unwound;
+ do
+ {
+ int err = callback (state, arg);
+ if (err != DWARF_CB_OK)
+ {
+ assert (thread->thread_detach_needed);
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ thread->thread_detach_needed = false;
+ while (thread->unwound)
+ state_free (thread->unwound);
+ return err;
+ }
+ __libdwfl_frame_unwind (state);
+ state = state->unwound;
+ }
+ while (state && state->pc_state == DWFL_FRAME_STATE_PC_SET);
+
+ Dwfl_Error err = dwfl_errno ();
+ assert (thread->thread_detach_needed);
+ if (process->callbacks->thread_detach)
+ process->callbacks->thread_detach (thread, thread->callbacks_arg);
+ thread->thread_detach_needed = false;
+ if (state == NULL || state->pc_state == DWFL_FRAME_STATE_ERROR)
+ {
+ __libdwfl_seterrno (err);
+ return -1;
+ }
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_UNDEFINED);
+ return 0;
+}
+INTDEF(dwfl_thread_getframes)
diff --git a/libdwfl/dwfl_frame_core.c b/libdwfl/dwfl_frame_core.c
new file mode 100644
index 0000000..f9f8a41
--- /dev/null
+++ b/libdwfl/dwfl_frame_core.c
@@ -0,0 +1,369 @@
+/* Get Dwarf Frame state for target core file.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include "system.h"
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct core_arg
+{
+ Elf *core;
+ Elf_Data *note_data;
+ size_t thread_note_offset;
+ Ebl *ebl;
+};
+
+struct thread_arg
+{
+ struct core_arg *core_arg;
+ size_t note_offset;
+};
+
+static bool
+core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
+ void *dwfl_arg)
+{
+ Dwfl_Process *process = dwfl->process;
+ struct core_arg *core_arg = dwfl_arg;
+ Elf *core = core_arg->core;
+ assert (core != NULL);
+ static size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ return false;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr == NULL || phdr->p_type != PT_LOAD)
+ continue;
+ /* Bias is zero here, a core file itself has no bias. */
+ GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ phdr->p_vaddr + phdr->p_memsz);
+ unsigned bytes = process->ebl->class == ELFCLASS64 ? 8 : 4;
+ if (addr < start || addr + bytes > end)
+ continue;
+ Elf_Data *data;
+ data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
+ bytes, ELF_T_ADDR);
+ if (data == NULL)
+ return false;
+ assert (data->d_size == bytes);
+ /* FIXME: Currently any arch supported for unwinding supports
+ unaligned access. */
+ if (bytes == 8)
+ *result = *(const uint64_t *) data->d_buf;
+ else
+ *result = *(const uint32_t *) data->d_buf;
+ return true;
+ }
+ return false;
+}
+
+static pid_t
+core_next_thread (Dwfl *dwfl __attribute__ ((unused)),
+ Dwfl_Thread *nthread __attribute__ ((unused)), void *dwfl_arg,
+ void **thread_argp)
+{
+ struct core_arg *core_arg = dwfl_arg;
+ Elf *core = core_arg->core;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ Elf_Data *note_data = core_arg->note_data;
+ size_t offset;
+ while (offset = core_arg->thread_note_offset, offset < note_data->d_size
+ && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
+ &nhdr, &name_offset,
+ &desc_offset)) > 0)
+ {
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = note_data->d_buf + name_offset;
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ if (! ebl_core_note (core_arg->ebl, &nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ {
+ /* This note may be just not recognized, skip it. */
+ continue;
+ }
+ if (nhdr.n_type != NT_PRSTATUS)
+ continue;
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ if (item == items + nitems)
+ continue;
+ uint32_t val32 = *(const uint32_t *) (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ pid_t tid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof tid);
+ struct thread_arg *thread_arg = malloc (sizeof (*thread_arg));
+ if (thread_arg == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return 0;
+ }
+ thread_arg->core_arg = core_arg;
+ thread_arg->note_offset = offset;
+ *thread_argp = thread_arg;
+ return tid;
+ }
+ return 0;
+}
+
+static bool
+core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
+{
+ struct thread_arg *thread_arg = thread_arg_voidp;
+ struct core_arg *core_arg = thread_arg->core_arg;
+ Elf *core = core_arg->core;
+ size_t offset = thread_arg->note_offset;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ Elf_Data *note_data = core_arg->note_data;
+ size_t nregs = ebl_frame_nregs (core_arg->ebl);
+ assert (offset < note_data->d_size);
+ size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
+ &desc_offset);
+ assert (getnote_err != 0);
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = note_data->d_buf + name_offset;
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, ®s_offset,
+ &nregloc, ®locs, &nitems, &items);
+ assert (core_note_err != 0);
+ assert (nhdr.n_type == NT_PRSTATUS);
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ assert (item < items + nitems);
+ pid_t tid;
+ {
+ uint32_t val32 = *(const uint32_t *) (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ tid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof tid);
+ }
+ assert (tid == INTUSE(dwfl_thread_tid) (thread));
+ desc += regs_offset;
+ for (size_t regloci = 0; regloci < nregloc; regloci++)
+ {
+ const Ebl_Register_Location *regloc = reglocs + regloci;
+ if (regloc->regno >= nregs)
+ continue;
+ assert (regloc->bits == 32 || regloc->bits == 64);
+ const char *reg_desc = desc + regloc->offset;
+ for (unsigned regno = regloc->regno;
+ regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs);
+ regno++)
+ {
+ /* PPC provides DWARF register 65 irrelevant for
+ CFI which clashes with register 108 (LR) we need.
+ LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
+ FIXME: It depends now on their order in core notes.
+ FIXME: It uses private function. */
+ if (dwfl_frame_reg_get (thread->unwound, regno, NULL))
+ continue;
+ Dwarf_Word val;
+ switch (regloc->bits)
+ {
+ case 32:;
+ uint32_t val32 = *(const uint32_t *) reg_desc;
+ reg_desc += sizeof val32;
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ /* Do a host width conversion. */
+ val = val32;
+ break;
+ case 64:;
+ uint64_t val64 = *(const uint64_t *) reg_desc;
+ reg_desc += sizeof val64;
+ val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be64toh (val64) : le64toh (val64));
+ assert (sizeof (*thread->unwound->regs) == sizeof val64);
+ val = val64;
+ break;
+ default:
+ abort ();
+ }
+ /* Registers not valid for CFI are just ignored. */
+ INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
+ reg_desc += regloc->pad;
+ }
+ }
+ return true;
+}
+
+static void
+core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
+{
+ struct core_arg *core_arg = dwfl_arg;
+ ebl_closebackend (core_arg->ebl);
+ free (core_arg);
+}
+
+static const Dwfl_Thread_Callbacks core_thread_callbacks =
+{
+ core_next_thread,
+ core_memory_read,
+ core_set_initial_registers,
+ core_detach,
+ NULL, /* core_thread_detach */
+};
+
+bool
+internal_function
+__libdwfl_attach_state_for_core (Dwfl *dwfl, Elf *core)
+{
+ Ebl *ebl = ebl_openbackend (core);
+ if (ebl == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return false;
+ }
+ size_t nregs = ebl_frame_nregs (ebl);
+ if (nregs == 0)
+ {
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_LIBEBL);
+ return false;
+ }
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ assert (ehdr->e_type == ET_CORE);
+ size_t phnum;
+ if (elf_getphdrnum (core, &phnum) < 0)
+ {
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return false;
+ }
+ pid_t pid = -1;
+ Elf_Data *note_data = NULL;
+ for (size_t cnt = 0; cnt < phnum; ++cnt)
+ {
+ GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
+ if (phdr != NULL && phdr->p_type == PT_NOTE)
+ {
+ note_data = elf_getdata_rawchunk (core, phdr->p_offset,
+ phdr->p_filesz, ELF_T_NHDR);
+ break;
+ }
+ }
+ if (note_data == NULL)
+ {
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ size_t offset = 0;
+ GElf_Nhdr nhdr;
+ size_t name_offset;
+ size_t desc_offset;
+ while (offset < note_data->d_size
+ && (offset = gelf_getnote (note_data, offset,
+ &nhdr, &name_offset, &desc_offset)) > 0)
+ {
+ /* Do not check NAME for now, help broken Linux kernels. */
+ const char *name = note_data->d_buf + name_offset;
+ const char *desc = note_data->d_buf + desc_offset;
+ GElf_Word regs_offset;
+ size_t nregloc;
+ const Ebl_Register_Location *reglocs;
+ size_t nitems;
+ const Ebl_Core_Item *items;
+ if (! ebl_core_note (ebl, &nhdr, name,
+ ®s_offset, &nregloc, ®locs, &nitems, &items))
+ {
+ /* This note may be just not recognized, skip it. */
+ continue;
+ }
+ if (nhdr.n_type != NT_PRPSINFO)
+ continue;
+ const Ebl_Core_Item *item;
+ for (item = items; item < items + nitems; item++)
+ if (strcmp (item->name, "pid") == 0)
+ break;
+ if (item == items + nitems)
+ continue;
+ uint32_t val32 = *(const uint32_t *) (desc + item->offset);
+ val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
+ ? be32toh (val32) : le32toh (val32));
+ pid = (int32_t) val32;
+ eu_static_assert (sizeof val32 <= sizeof pid);
+ break;
+ }
+ if (pid == -1)
+ {
+ /* No valid NT_PRPSINFO recognized in this CORE. */
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return false;
+ }
+ struct core_arg *core_arg = malloc (sizeof *core_arg);
+ if (core_arg == NULL)
+ {
+ ebl_closebackend (ebl);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ core_arg->core = core;
+ core_arg->note_data = note_data;
+ core_arg->thread_note_offset = 0;
+ core_arg->ebl = ebl;
+ if (! INTUSE(dwfl_attach_state) (dwfl, ebl, pid, &core_thread_callbacks, core_arg))
+ {
+ free (core_arg);
+ ebl_closebackend (ebl);
+ return false;
+ }
+ return true;
+}
diff --git a/libdwfl/dwfl_frame_pc.c b/libdwfl/dwfl_frame_pc.c
new file mode 100644
index 0000000..9781777
--- /dev/null
+++ b/libdwfl/dwfl_frame_pc.c
@@ -0,0 +1,61 @@
+/* Get return address register value for frame.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "libdwflP.h"
+
+bool
+dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
+{
+ assert (state->pc_state == DWFL_FRAME_STATE_PC_SET);
+ *pc = state->pc;
+ if (isactivation)
+ {
+ /* Bottom frame? */
+ if (state == state->thread->unwound)
+ *isactivation = true;
+ /* *ISACTIVATION is logical or of current and previous frame state. */
+ else if (state->signal_frame)
+ *isactivation = true;
+ else
+ {
+ /* Not affected by unsuccessfully unwound frame. */
+ __libdwfl_frame_unwind (state);
+ if (state->unwound == NULL
+ || state->unwound->pc_state != DWFL_FRAME_STATE_PC_SET)
+ *isactivation = false;
+ else
+ *isactivation = state->unwound->signal_frame;
+ }
+ }
+ return true;
+}
+INTDEF (dwfl_frame_pc)
diff --git a/libdwfl/dwfl_frame_pid.c b/libdwfl/dwfl_frame_pid.c
new file mode 100644
index 0000000..5d23c37
--- /dev/null
+++ b/libdwfl/dwfl_frame_pid.c
@@ -0,0 +1,219 @@
+/* Get Dwarf Frame state for target live PID process.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+struct pid_arg
+{
+ DIR *dir;
+ size_t tids_attached_size, tids_attached_used;
+ pid_t *tids_attached;
+};
+
+static bool
+ptrace_attach (pid_t tid)
+{
+ if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
+ return false;
+ /* FIXME: Handle missing SIGSTOP on old Linux kernels. */
+ for (;;)
+ {
+ int status;
+ if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status))
+ {
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ return false;
+ }
+ if (WSTOPSIG (status) == SIGSTOP)
+ break;
+ if (ptrace (PTRACE_CONT, tid, NULL,
+ (void *) (uintptr_t) WSTOPSIG (status)) != 0)
+ {
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool
+pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
+{
+ struct pid_arg *pid_arg = arg;
+ assert (pid_arg->tids_attached_used > 0);
+ pid_t tid = pid_arg->tids_attached[0];
+ Dwfl_Process *process = dwfl->process;
+ if (process->ebl->class == ELFCLASS64)
+ {
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
+ return errno == 0;
+ }
+#if SIZEOF_LONG == 8
+ /* We do not care about reads unaliged to 4 bytes boundary.
+ But 0x...ffc read of 8 bytes could overrun a page. */
+ bool lowered = (addr & 4) != 0;
+ if (lowered)
+ addr -= 4;
+#endif /* SIZEOF_LONG == 8 */
+ errno = 0;
+ *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
+ if (errno != 0)
+ return false;
+#if SIZEOF_LONG == 8
+# if BYTE_ORDER == BIG_ENDIAN
+ if (! lowered)
+ *result >>= 32;
+# else
+ if (lowered)
+ *result >>= 32;
+# endif
+#endif /* SIZEOF_LONG == 8 */
+ *result &= 0xffffffff;
+ return true;
+}
+
+static pid_t
+pid_next_thread (Dwfl *dwfl __attribute__ ((unused)),
+ Dwfl_Thread *nthread __attribute__ ((unused)), void *dwfl_arg,
+ void **thread_argp)
+{
+ struct pid_arg *pid_arg = dwfl_arg;
+ struct dirent *dirent;
+ do
+ {
+ errno = 0;
+ dirent = readdir (pid_arg->dir);
+ if (dirent == NULL)
+ return errno == 0 ? 0 : -1;
+ }
+ while (strcmp (dirent->d_name, ".") == 0
+ || strcmp (dirent->d_name, "..") == 0);
+ char *end;
+ errno = 0;
+ long tidl = strtol (dirent->d_name, &end, 10);
+ if (errno != 0)
+ return -1;
+ pid_t tid = tidl;
+ if (tidl <= 0 || (end && *end) || tid != tidl)
+ return -1;
+ *thread_argp = dwfl_arg;
+ return tid;
+}
+
+static bool
+pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
+{
+ struct pid_arg *pid_arg = thread_arg;
+ pid_t tid = INTUSE(dwfl_thread_tid) (thread);
+ if (! ptrace_attach (tid))
+ return false;
+ if (pid_arg->tids_attached_used == pid_arg->tids_attached_size)
+ {
+ pid_arg->tids_attached_size *= 2;
+ pid_arg->tids_attached_size = MAX (64, pid_arg->tids_attached_size);
+ pid_arg->tids_attached = realloc (pid_arg->tids_attached,
+ (pid_arg->tids_attached_size
+ * sizeof *pid_arg->tids_attached));
+ }
+ pid_arg->tids_attached[pid_arg->tids_attached_used++] = tid;
+ return ebl_set_initial_registers_tid (thread, tid,
+ INTUSE(dwfl_thread_state_registers));
+}
+
+static void
+pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
+{
+ struct pid_arg *pid_arg = dwfl_arg;
+ closedir (pid_arg->dir);
+ free (pid_arg->tids_attached);
+ free (pid_arg);
+}
+
+static void
+pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
+{
+ struct pid_arg *pid_arg = thread_arg;
+ pid_t tid = INTUSE(dwfl_thread_tid) (thread);
+ size_t ix;
+ for (ix = 0; ix < pid_arg->tids_attached_used; ix++)
+ if (pid_arg->tids_attached[ix] == tid)
+ break;
+ assert (ix < pid_arg->tids_attached_used);
+ pid_arg->tids_attached[ix]
+ = pid_arg->tids_attached[--pid_arg->tids_attached_used];
+ ptrace (PTRACE_DETACH, tid, NULL, NULL);
+}
+
+static const Dwfl_Thread_Callbacks pid_thread_callbacks =
+{
+ pid_next_thread,
+ pid_memory_read,
+ pid_set_initial_registers,
+ pid_detach,
+ pid_thread_detach,
+};
+
+bool
+internal_function
+__libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
+{
+ char dirname[64];
+ int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid);
+ assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1);
+ DIR *dir = opendir (dirname);
+ if (dir == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_PARSE_PROC);
+ return NULL;
+ }
+ struct pid_arg *pid_arg = malloc (sizeof *pid_arg);
+ if (pid_arg == NULL)
+ {
+ closedir (dir);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ pid_arg->dir = dir;
+ pid_arg->tids_attached_size = 0;
+ pid_arg->tids_attached_used = 0;
+ pid_arg->tids_attached = NULL;
+ if (! INTUSE(dwfl_attach_state) (dwfl, NULL, pid, &pid_thread_callbacks, pid_arg))
+ {
+ free (pid_arg);
+ return false;
+ }
+ return true;
+}
diff --git a/libdwfl/dwfl_frame_regs.c b/libdwfl/dwfl_frame_regs.c
new file mode 100644
index 0000000..c1a08ba
--- /dev/null
+++ b/libdwfl/dwfl_frame_regs.c
@@ -0,0 +1,55 @@
+/* Get Dwarf Frame state from modules present in DWFL.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#include "libdwflP.h"
+
+bool
+dwfl_thread_state_registers (Dwfl_Thread *thread, const int firstreg,
+ unsigned nregs, const Dwarf_Word *regs)
+{
+ Dwfl_Frame *state = thread->unwound;
+ assert (state && state->unwound == NULL);
+ for (unsigned regno = firstreg; regno < firstreg + nregs; regno++)
+ if (! dwfl_frame_reg_set (state, regno, regs[regno - firstreg]))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return false;
+ }
+ return true;
+}
+INTDEF(dwfl_thread_state_registers)
+
+void
+dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc)
+{
+ Dwfl_Frame *state = thread->unwound;
+ assert (state && state->unwound == NULL);
+ state->pc = pc;
+ state->pc_state = DWFL_FRAME_STATE_PC_SET;
+}
+INTDEF(dwfl_thread_state_register_pc)
diff --git a/libdwfl/dwfl_frame_unwind.c b/libdwfl/dwfl_frame_unwind.c
new file mode 100644
index 0000000..d1119fe
--- /dev/null
+++ b/libdwfl/dwfl_frame_unwind.c
@@ -0,0 +1,416 @@
+/* Get previous frame state for an existing frame state.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "cfi.h"
+#include <stdlib.h>
+#include "libdwflP.h"
+#include "../libdw/dwarf.h"
+#include <sys/ptrace.h>
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+static bool
+state_get_reg (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
+{
+ if (! dwfl_frame_reg_get (state, regno, val))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ return false;
+ }
+ return true;
+}
+
+static int
+bra_compar (const void *key_voidp, const void *elem_voidp)
+{
+ Dwarf_Word offset = (uintptr_t) key_voidp;
+ const Dwarf_Op *op = elem_voidp;
+ return (offset > op->offset) - (offset < op->offset);
+}
+
+/* FIXME: Handle bytecode deadlocks and overflows. */
+
+static bool
+expr_eval (Dwfl_Frame *state, Dwarf_Frame *frame, const Dwarf_Op *ops,
+ size_t nops, Dwarf_Addr *result)
+{
+ Dwfl_Process *process = state->thread->process;
+ if (nops == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ Dwarf_Addr *stack = NULL;
+ size_t stack_used = 0, stack_allocated = 0;
+ bool
+ push (Dwarf_Addr val)
+ {
+ if (stack_used == stack_allocated)
+ {
+ stack_allocated = MAX (stack_allocated * 2, 32);
+ Dwarf_Addr *stack_new = realloc (stack, stack_allocated * sizeof (*stack));
+ if (stack_new == NULL)
+ {
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ stack = stack_new;
+ }
+ stack[stack_used++] = val;
+ return true;
+ }
+ bool
+ pop (Dwarf_Addr *val)
+ {
+ if (stack_used == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ *val = stack[--stack_used];
+ return true;
+ }
+ Dwarf_Addr val1, val2;
+ bool is_location = false;
+ for (const Dwarf_Op *op = ops; op < ops + nops; op++)
+ switch (op->atom)
+ {
+ case DW_OP_reg0 ... DW_OP_reg31:
+ if (! state_get_reg (state, op->atom - DW_OP_reg0, &val1)
+ || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_regx:
+ if (! state_get_reg (state, op->number, &val1) || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_breg0 ... DW_OP_breg31:
+ if (! state_get_reg (state, op->atom - DW_OP_breg0, &val1))
+ {
+ free (stack);
+ return false;
+ }
+ val1 += op->number;
+ if (! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_bregx:
+ if (! state_get_reg (state, op->number, &val1))
+ {
+ free (stack);
+ return false;
+ }
+ val1 += op->number2;
+ if (! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_lit0 ... DW_OP_lit31:
+ if (! push (op->atom - DW_OP_lit0))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_plus_uconst:
+ if (! pop (&val1) || ! push (val1 + op->number))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_call_frame_cfa:;
+ Dwarf_Op *cfa_ops;
+ size_t cfa_nops;
+ Dwarf_Addr cfa;
+ if (dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops) != 0
+ || ! expr_eval (state, frame, cfa_ops, cfa_nops, &cfa)
+ || ! push (cfa))
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ free (stack);
+ return false;
+ }
+ is_location = true;
+ break;
+ case DW_OP_stack_value:
+ is_location = false;
+ break;
+ case DW_OP_deref:
+ if (! pop (&val1)
+ || process->callbacks->memory_read == NULL
+ || ! process->callbacks->memory_read (process->dwfl, val1, &val1,
+ process->callbacks_arg)
+ || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_nop:
+ break;
+ case DW_OP_dup:
+ if (! pop (&val1) || ! push (val1) || ! push (val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ if (! push (op->number))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+ case DW_OP_bra:
+ if (! pop (&val1))
+ {
+ free (stack);
+ return false;
+ }
+ if (val1 == 0)
+ break;
+ /* FALLTHRU */
+ case DW_OP_skip:;
+ Dwarf_Word offset = op->offset + 1 + 2 + (int16_t) op->number;
+ const Dwarf_Op *found = bsearch ((void *) (uintptr_t) offset, ops, nops,
+ sizeof (*ops), bra_compar);
+ if (found == NULL)
+ {
+ free (stack);
+ /* PPC32 vDSO has such invalid operations. */
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ return false;
+ }
+ /* Undo the 'for' statement increment. */
+ op = found - 1;
+ break;
+ case DW_OP_drop:
+ if (! pop (&val1))
+ {
+ free (stack);
+ return false;
+ }
+ break;
+#define BINOP(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) || ! push (val1 op val2)) \
+ { \
+ free (stack); \
+ return false; \
+ } \
+ break;
+ BINOP (DW_OP_and, &)
+ BINOP (DW_OP_shl, <<)
+ BINOP (DW_OP_plus, +)
+ BINOP (DW_OP_mul, *)
+#undef BINOP
+#define BINOP_SIGNED(atom, op) \
+ case atom: \
+ if (! pop (&val2) || ! pop (&val1) \
+ || ! push ((int64_t) val1 op (int64_t) val2)) \
+ { \
+ free (stack); \
+ return false; \
+ } \
+ break;
+ BINOP_SIGNED (DW_OP_le, <=)
+ BINOP_SIGNED (DW_OP_ge, >=)
+ BINOP_SIGNED (DW_OP_eq, ==)
+ BINOP_SIGNED (DW_OP_lt, <)
+ BINOP_SIGNED (DW_OP_gt, >)
+ BINOP_SIGNED (DW_OP_ne, !=)
+#undef BINOP_SIGNED
+ default:
+ __libdwfl_seterrno (DWFL_E_UNSUPPORTED_DWARF);
+ return false;
+ }
+ if (! pop (result))
+ {
+ free (stack);
+ return false;
+ }
+ free (stack);
+ if (is_location
+ && (process->callbacks->memory_read == NULL
+ || ! process->callbacks->memory_read (process->dwfl, *result, result,
+ process->callbacks_arg)))
+ return false;
+ return true;
+}
+
+/* The logic is to call __libdwfl_seterrno for any CFI bytecode interpretation
+ error so one can easily catch the problem with a debugger. Still there are
+ archs with invalid CFI for some registers where the registers are never used
+ later. Therefore we continue unwinding leaving the registers undefined.
+
+ The only exception is PC itself, when there is an error unwinding PC we
+ return false. Otherwise we would return successful end of backtrace seeing
+ an undefined PC register (due to an error unwinding it). */
+
+static void
+handle_cfi (Dwfl_Frame *state, Dwarf_Addr pc, Dwarf_CFI *cfi)
+{
+ Dwarf_Frame *frame;
+ if (INTUSE(dwarf_cfi_addrframe) (cfi, pc, &frame) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ return;
+ }
+ Dwfl_Thread *thread = state->thread;
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ size_t nregs = ebl_frame_nregs (ebl);
+ Dwfl_Frame *unwound;
+ unwound = malloc (sizeof (*unwound) + sizeof (*unwound->regs) * nregs);
+ state->unwound = unwound;
+ unwound->thread = thread;
+ unwound->unwound = NULL;
+ unwound->signal_frame = frame->fde->cie->signal_frame;
+ unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
+ for (unsigned regno = 0; regno < nregs; regno++)
+ {
+ Dwarf_Op reg_ops_mem[3], *reg_ops;
+ size_t reg_nops;
+ if (dwarf_frame_register (frame, regno, reg_ops_mem, ®_ops,
+ ®_nops) != 0)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBDW);
+ continue;
+ }
+ Dwarf_Addr regval;
+ if (reg_nops == 0)
+ {
+ if (reg_ops == reg_ops_mem)
+ {
+ /* REGNO is undefined. */
+ unsigned ra = frame->fde->cie->return_address_register;
+ if (regno == ra)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ continue;
+ }
+ else if (reg_ops == NULL)
+ {
+ /* REGNO is same-value. */
+ if (! state_get_reg (state, regno, ®val))
+ continue;
+ }
+ else
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_DWARF);
+ continue;
+ }
+ }
+ else if (! expr_eval (state, frame, reg_ops, reg_nops, ®val))
+ {
+ /* PPC32 vDSO has various invalid operations, ignore them. The
+ register will look as unset causing an error later, if used.
+ But PPC32 does not use such registers. */
+ continue;
+ }
+ if (! dwfl_frame_reg_set (unwound, regno, regval))
+ {
+ __libdwfl_seterrno (DWFL_E_INVALID_REGISTER);
+ continue;
+ }
+ }
+ if (unwound->pc_state == DWFL_FRAME_STATE_ERROR
+ && dwfl_frame_reg_get (unwound, frame->fde->cie->return_address_register,
+ &unwound->pc))
+ {
+ /* PPC32 __libc_start_main properly CFI-unwinds PC as zero. Currently
+ none of the archs supported for unwinding have zero as a valid PC. */
+ if (unwound->pc == 0)
+ unwound->pc_state = DWFL_FRAME_STATE_PC_UNDEFINED;
+ else
+ unwound->pc_state = DWFL_FRAME_STATE_PC_SET;
+ }
+}
+
+void
+internal_function
+__libdwfl_frame_unwind (Dwfl_Frame *state)
+{
+ if (state->unwound)
+ return;
+ Dwarf_Addr pc;
+ bool ok = INTUSE(dwfl_frame_pc) (state, &pc, NULL);
+ assert (ok);
+ /* Do not ask dwfl_frame_pc for ISACTIVATION, it would try to unwind STATE
+ which would deadlock us. */
+ if (state != state->thread->unwound && ! state->signal_frame)
+ pc--;
+ Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (state->thread->process->dwfl, pc);
+ if (mod != NULL)
+ {
+ Dwarf_Addr bias;
+ Dwarf_CFI *cfi_eh = INTUSE(dwfl_module_eh_cfi) (mod, &bias);
+ if (cfi_eh)
+ {
+ handle_cfi (state, pc - bias, cfi_eh);
+ if (state->unwound)
+ return;
+ }
+ Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
+ if (cfi_dwarf)
+ {
+ handle_cfi (state, pc - bias, cfi_dwarf);
+ if (state->unwound)
+ return;
+ }
+ }
+ __libdwfl_seterrno (DWFL_E_NO_DWARF);
+}
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 2b70e28..511f172 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -1,5 +1,5 @@
/* Interfaces for libdwfl.
- Copyright (C) 2005-2010 Red Hat, Inc.
+ Copyright (C) 2005-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -30,6 +30,7 @@
#define _LIBDWFL_H 1
#include "libdw.h"
+#include "libebl.h"
#include <stdio.h>
/* Handle for a session using the library. */
@@ -41,6 +42,14 @@ typedef struct Dwfl_Module Dwfl_Module;
/* Handle describing a line record. */
typedef struct Dwfl_Line Dwfl_Line;
+/* This holds information common for all the frames of one backtrace for
+ a partical thread/task/TID. Several threads belong to one Dwfl. */
+typedef struct Dwfl_Thread Dwfl_Thread;
+
+/* This holds everything we know about the state of the frame at a particular
+ PC location described by an FDE belonging to Dwfl_Thread. */
+typedef struct Dwfl_Frame Dwfl_Frame;
+
/* Callbacks. */
typedef struct
{
@@ -352,10 +361,14 @@ extern int dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release,
segment to locate its PT_DYNAMIC in the dump. This might call
dwfl_report_elf on file names found in the dump if reading some
link_map files is the only way to ascertain those modules' addresses.
+ dwfl_attach_state is also called for DWFL, dwfl_core_file_report does
+ not fail if the dwfl_attach_state call has failed.
Returns the number of modules reported, or -1 for errors. */
extern int dwfl_core_file_report (Dwfl *dwfl, Elf *elf);
/* Call dwfl_report_module for each file mapped into the address space of PID.
+ dwfl_attach_state is also called for DWFL, dwfl_linux_proc_report does
+ not fail if the dwfl_attach_state call has failed.
Returns zero on success, -1 if dwfl_report_module failed,
or an errno code if opening the kernel binary failed. */
extern int dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid);
@@ -565,6 +578,119 @@ extern Dwarf_CFI *dwfl_module_dwarf_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
extern Dwarf_CFI *dwfl_module_eh_cfi (Dwfl_Module *mod, Dwarf_Addr *bias);
+typedef struct
+{
+ /* Called to iterate through threads. Returns next TID (thread ID) on
+ success, a negative number on failure and zero if there are no more
+ threads. NTHREAD is the new thread being created. *THREAD_ARGP may be
+ optionally set by the implementation, THREAD_ARGP is never NULL.
+ This method must not be NULL. */
+ pid_t (*next_thread) (Dwfl *dwfl, Dwfl_Thread *nthread, void *dwfl_arg,
+ void **thread_argp)
+ __nonnull_attribute__ (1, 2);
+
+ /* Called during unwinding to access memory (stack) state. Returns true for
+ successfully read *RESULT or false and sets dwfl_errno () on failure.
+ This method may be NULL - in such case dwfl_thread_getframes will return
+ only the initial frame. */
+ bool (*memory_read) (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
+ void *dwfl_arg)
+ __nonnull_attribute__ (1, 3);
+
+ /* Called on initial unwind to get the initial register state of the first
+ frame. Should call dwfl_thread_state_registers, possibly multiple times
+ for different ranges and possibly also dwfl_thread_state_register_pc, to
+ fill in initial (DWARF) register values. After this call, till at least
+ thread_detach is called, the thread is assumed to be frozen, so that it is
+ safe to unwind. Returns true on success or false and sets dwfl_err () on
+ failure. This method must not be NULL. */
+ bool (*set_initial_registers) (Dwfl_Thread *thread, void *thread_arg)
+ __nonnull_attribute__ (1);
+
+ /* Called by dwfl_end. All thread_detach method calls have been already
+ done. This method may be NULL. */
+ void (*detach) (Dwfl *dwfl, void *dwfl_arg)
+ __nonnull_attribute__ (1);
+
+ /* Called when unwinding is done. No callback will be called after
+ this method has been called. Iff set_initial_registers was called for
+ a TID thread_detach will be called before the detach method above.
+ This method may be NULL. */
+ void (*thread_detach) (Dwfl_Thread *thread, void *thread_arg)
+ __nonnull_attribute__ (1);
+} Dwfl_Thread_Callbacks;
+
+/* PID is the process id associated with the DWFL state. Architecture of DWFL
+ modules is specified by EBL. If EBL is NULL the function will detect it
+ from arbitrary Dwfl_Module of DWFL. DWFL_ARG is the callback backend state.
+ DWFL_ARG will be provided to the callbacks. *THREAD_CALLBACKS function
+ pointers must remain valid during lifetime of DWFL. Function returns true
+ on success, false otherwise. */
+bool dwfl_attach_state (Dwfl *dwfl, Ebl *ebl, pid_t pid,
+ const Dwfl_Thread_Callbacks *thread_callbacks,
+ void *dwfl_arg)
+ __nonnull_attribute__ (1, 4);
+
+/* Return PID for the process associated with DWFL. Function returns -1 if
+ dwfl_attach_state was not called for DWFL. */
+pid_t dwfl_pid (Dwfl *dwfl)
+ __nonnull_attribute__ (1);
+
+/* Return DWFL from which THREAD was created using dwfl_next_thread. */
+Dwfl *dwfl_thread_dwfl (Dwfl_Thread *thread)
+ __nonnull_attribute__ (1);
+
+/* Return positive TID (thread ID) for THREAD. This function never fails. */
+pid_t dwfl_thread_tid (Dwfl_Thread *thread)
+ __nonnull_attribute__ (1);
+
+/* Return thread for frame STATE. This function never fails. */
+Dwfl_Thread *dwfl_frame_thread (Dwfl_Frame *state)
+ __nonnull_attribute__ (1);
+
+/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
+ For every known continuous block of registers <FIRSTREG..FIRSTREG+NREGS)
+ (inclusive..exclusive) set their content to REGS (array of NREGS items).
+ Function returns false if any of the registers has invalid number. */
+bool dwfl_thread_state_registers (Dwfl_Thread *thread, const int firstreg,
+ unsigned nregs, const Dwarf_Word *regs)
+ __nonnull_attribute__ (1, 4);
+dwfl_thread_state_registers_t dwfl_thread_state_registers;
+
+/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
+ If PC is not contained among DWARF registers passed by
+ dwfl_thread_state_registers on the target architecture pass the PC value
+ here. */
+void dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc)
+ __nonnull_attribute__ (1);
+
+/* Gets the next known thread, if any. To get the initial thread
+ provide NULL as previous thread PREV_THREAD. On error function returns NULL
+ and sets dwfl_errno (). When no more threads are found function returns
+ NULL and dwfl_errno () is set to 0 - dwfl_errmsg (0) returns NULL then. */
+Dwfl_Thread *dwfl_next_thread (Dwfl *dwfl, Dwfl_Thread *prev_thread)
+ __nonnull_attribute__ (1);
+
+/* Iterate through the frames for a thread. Returns zero if all frames
+ have been processed by the callback, returns -1 on error, or the
+ value of the callback when not DWARF_CB_OK. Keeps calling the
+ callback with the next frame while the callback returns
+ DWARF_CB_OK, till there are no more frames. On start will call the
+ set_initial_registers callback and on return will call the
+ detach_thread callback of the Dwfl_Thread. */
+int dwfl_thread_getframes (Dwfl_Thread *thread,
+ int (*callback) (Dwfl_Frame *state, void *arg),
+ void *arg)
+ __nonnull_attribute__ (1, 2);
+
+/* Return *PC (program counter) for thread-specific frame STATE.
+ Set *ISACTIVATION according to DWARF frame "activation" definition.
+ Typically you need to substract 1 from *PC if *ACTIVATION is false to safely
+ find function of the caller. ACTIVATION may be NULL. PC must not be NULL.
+ Function returns false if it failed to find *PC. */
+bool dwfl_frame_pc (Dwfl_Frame *state, Dwarf_Addr *pc, bool *isactivation)
+ __nonnull_attribute__ (1, 2);
+
#ifdef __cplusplus
}
#endif
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 1d4899b..b73d4f3 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2012 Red Hat, Inc.
+ Copyright (C) 2005-2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -41,6 +41,9 @@
#include <string.h>
#include "../libdw/libdwP.h" /* We need its INTDECLs. */
+#include "libeblP.h"
+
+typedef struct Dwfl_Process Dwfl_Process;
/* gettext helper macros. */
#define _(Str) dgettext ("elfutils", Str)
@@ -74,7 +77,19 @@
DWFL_ERROR (BADELF, N_("not a valid ELF file")) \
DWFL_ERROR (WEIRD_TYPE, N_("cannot handle DWARF type description")) \
DWFL_ERROR (WRONG_ID_ELF, N_("ELF file does not match build ID")) \
- DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data"))
+ DWFL_ERROR (BAD_PRELINK, N_("corrupt .gnu.prelink_undo section data")) \
+ DWFL_ERROR (LIBEBL_BAD, N_("Internal error due to ebl")) \
+ DWFL_ERROR (CORE_MISSING, N_("Missing data in core file")) \
+ DWFL_ERROR (INVALID_REGISTER, N_("Invalid register")) \
+ DWFL_ERROR (PROCESS_MEMORY_READ, N_("Error reading process memory")) \
+ DWFL_ERROR (PROCESS_NO_ARCH, N_("Have not found ELF module in a process")) \
+ DWFL_ERROR (PARSE_PROC, N_("Error parsing /proc filesystem")) \
+ DWFL_ERROR (NO_THREAD, N_("No thread found")) \
+ DWFL_ERROR (INVALID_DWARF, N_("Invalid DWARF")) \
+ DWFL_ERROR (UNSUPPORTED_DWARF, N_("Unsupported DWARF")) \
+ DWFL_ERROR (NEXT_THREAD_FAIL, N_("Unable to find more threads")) \
+ DWFL_ERROR (ATTACH_STATE_CONFLICT, N_("Dwfl already has attached state")) \
+ DWFL_ERROR (NO_ATTACH_STATE, N_("Dwfl has no attached state"))
#define DWFL_ERROR(name, text) DWFL_E_##name,
typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
@@ -92,6 +107,8 @@ struct Dwfl
Dwfl_Module *modulelist; /* List in order used by full traversals. */
+ Dwfl_Process *process;
+
GElf_Addr offline_next_address;
GElf_Addr segment_align; /* Smallest granularity of segments. */
@@ -189,7 +206,94 @@ struct Dwfl_Module
bool gc; /* Mark/sweep flag. */
};
+/* This holds information common for all the threads/tasks/TIDs of one process
+ for backtraces. */
+
+struct Dwfl_Process
+{
+ struct Dwfl *dwfl;
+ pid_t pid;
+ const Dwfl_Thread_Callbacks *callbacks;
+ void *callbacks_arg;
+ struct ebl *ebl;
+ Dwfl_Thread *thread;
+};
+
+/* See its typedef in libdwfl.h. */
+
+struct Dwfl_Thread
+{
+ Dwfl_Process *process;
+ Dwfl_Thread *next;
+ pid_t tid;
+ bool thread_detach_needed : 1;
+ /* Bottom frame. */
+ Dwfl_Frame *unwound;
+ void *callbacks_arg;
+};
+
+/* See its typedef in libdwfl.h. */
+
+struct Dwfl_Frame
+{
+ Dwfl_Thread *thread;
+ /* Previous (outer) frame. */
+ Dwfl_Frame *unwound;
+ bool signal_frame : 1;
+ enum
+ {
+ /* This structure is still being initialized or there was an error
+ initializing it. */
+ DWFL_FRAME_STATE_ERROR,
+ /* PC field is valid. */
+ DWFL_FRAME_STATE_PC_SET,
+ /* PC field is undefined, this means the next (inner) frame was the
+ outermost frame. */
+ DWFL_FRAME_STATE_PC_UNDEFINED
+ } pc_state;
+ /* Either initialized from appropriate REGS element or on some archs
+ initialized separately as the return address has no DWARF register. */
+ Dwarf_Addr pc;
+ /* (1 << X) bitmask where 0 <= X < ebl_frame_nregs. */
+ uint64_t regs_set[3];
+ /* REGS array size is ebl_frame_nregs. */
+ Dwarf_Addr regs[];
+};
+
+/* Fetch value from Dwfl_Frame->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+
+static inline bool
+dwfl_frame_reg_get (Dwfl_Frame *state, unsigned regno, Dwarf_Addr *val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (regno >= ebl->frame_nregs)
+ return false;
+ if ((state->regs_set[regno / sizeof (*state->regs_set) / 8]
+ & (1U << (regno % (sizeof (*state->regs_set) * 8)))) == 0)
+ return false;
+ if (val)
+ *val = state->regs[regno];
+ return true;
+}
+/* Store value to Dwfl_Frame->regs indexed by DWARF REGNO.
+ No error code is set if the function returns FALSE. */
+
+static inline bool
+dwfl_frame_reg_set (Dwfl_Frame *state, unsigned regno, Dwarf_Addr val)
+{
+ Ebl *ebl = state->thread->process->ebl;
+ if (regno >= ebl->frame_nregs)
+ return false;
+ /* For example i386 user_regs_struct has signed fields. */
+ if (ebl->class == ELFCLASS32)
+ val &= 0xffffffff;
+ state->regs_set[regno / sizeof (*state->regs_set) / 8] |=
+ (1U << (regno % (sizeof (*state->regs_set) * 8)));
+ state->regs[regno] = val;
+ return true;
+}
/* Information cached about each CU in Dwfl_Module.dw. */
struct dwfl_cu
@@ -415,6 +519,29 @@ extern Dwfl_Module *__libdwfl_report_offline (Dwfl *dwfl, const char *name,
const char *))
internal_function;
+/* Free PROCESS. Unlink and free also any structures it references. */
+extern void __libdwfl_process_free (Dwfl_Process *process)
+ internal_function;
+
+/* Update STATE->UNWOUND for the unwound frame.
+ Functions sets dwfl_errno (). */
+extern void __libdwfl_frame_unwind (Dwfl_Frame *state)
+ internal_function;
+
+/* Call dwfl_attach_state for PID, return true if successful. */
+extern bool __libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
+ internal_function;
+
+/* Call dwfl_attach_state for CORE, return true if successful. */
+extern bool __libdwfl_attach_state_for_core (Dwfl *dwfl, Elf *core)
+ internal_function;
+
+/* Align segment START downwards or END upwards addresses according to DWFL. */
+extern GElf_Addr __libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
+ internal_function;
+extern GElf_Addr __libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
+ internal_function;
+
/* Decompression wrappers: decompress whole file into memory. */
extern Dwfl_Error __libdw_gunzip (int fd, off64_t start_offset,
void *mapped, size_t mapped_size,
@@ -557,6 +684,16 @@ INTDECL (dwfl_offline_section_address)
INTDECL (dwfl_module_relocate_address)
INTDECL (dwfl_module_dwarf_cfi)
INTDECL (dwfl_module_eh_cfi)
+INTDECL (dwfl_attach_state)
+INTDECL (dwfl_pid)
+INTDECL (dwfl_thread_dwfl)
+INTDECL (dwfl_thread_tid)
+INTDECL (dwfl_frame_thread)
+INTDECL (dwfl_thread_state_registers)
+INTDECL (dwfl_thread_state_register_pc)
+INTDECL (dwfl_next_thread)
+INTDECL (dwfl_thread_getframes)
+INTDECL (dwfl_frame_pc)
/* Leading arguments standard to callbacks passed a Dwfl_Module. */
#define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
index 67ff509..a5bcaaa 100644
--- a/libdwfl/linux-proc-maps.c
+++ b/libdwfl/linux-proc-maps.c
@@ -1,5 +1,5 @@
/* Standard libdwfl callbacks for debugging a live Linux process.
- Copyright (C) 2005-2010 Red Hat, Inc.
+ Copyright (C) 2005-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -301,6 +301,13 @@ dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
fclose (f);
+ if (result == 0)
+ {
+ /* Possible error is ignored, DWFL still may be useful for non-unwinding
+ operations. */
+ __libdwfl_attach_state_for_pid (dwfl, pid);
+ }
+
return result;
}
INTDEF (dwfl_linux_proc_report)
diff --git a/libdwfl/segment.c b/libdwfl/segment.c
index 496b4fd..9276917 100644
--- a/libdwfl/segment.c
+++ b/libdwfl/segment.c
@@ -1,5 +1,5 @@
/* Manage address space lookup table for libdwfl.
- Copyright (C) 2008, 2009, 2010 Red Hat, Inc.
+ Copyright (C) 2008, 2009, 2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -28,16 +28,18 @@
#include "libdwflP.h"
-static GElf_Addr
-segment_start (Dwfl *dwfl, GElf_Addr start)
+GElf_Addr
+internal_function
+__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
{
if (dwfl->segment_align > 1)
start &= -dwfl->segment_align;
return start;
}
-static GElf_Addr
-segment_end (Dwfl *dwfl, GElf_Addr end)
+GElf_Addr
+internal_function
+__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
{
if (dwfl->segment_align > 1)
end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
@@ -156,8 +158,8 @@ reify_segments (Dwfl *dwfl)
for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
if (! mod->gc)
{
- const GElf_Addr start = segment_start (dwfl, mod->low_addr);
- const GElf_Addr end = segment_end (dwfl, mod->high_addr);
+ const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
+ const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
bool resized = false;
int idx = lookup (dwfl, start, hint);
@@ -296,8 +298,9 @@ dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
dwfl->lookup_module = NULL;
}
- GElf_Addr start = segment_start (dwfl, bias + phdr->p_vaddr);
- GElf_Addr end = segment_end (dwfl, bias + phdr->p_vaddr + phdr->p_memsz);
+ GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
+ GElf_Addr end = __libdwfl_segment_end (dwfl,
+ bias + phdr->p_vaddr + phdr->p_memsz);
/* Coalesce into the last one if contiguous and matching. */
if (ndx != dwfl->lookup_tail_ndx
diff --git a/libebl/Makefile.am b/libebl/Makefile.am
index 4d62fad..efcb934 100644
--- a/libebl/Makefile.am
+++ b/libebl/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to create Makefile.in
##
-## Copyright (C) 2000-2010 Red Hat, Inc.
+## Copyright (C) 2000-2010, 2013 Red Hat, Inc.
## This file is part of elfutils.
##
## This file is free software; you can redistribute it and/or modify
@@ -29,7 +29,8 @@
##
include $(top_srcdir)/config/eu.am
AM_CFLAGS += -fpic
-AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdw -I$(srcdir)/../libasm
+AM_CPPFLAGS += -I$(srcdir)/../libelf -I$(srcdir)/../libdw -I$(srcdir)/../libasm \
+ -I$(srcdir)/../libdwfl
VERSION = 1
LIBEBL_SUBDIR = @LIBEBL_SUBDIR@
@@ -54,7 +55,7 @@ gen_SOURCES = eblopenbackend.c eblclosebackend.c eblstrtab.c \
eblreginfo.c eblnonerelocp.c eblrelativerelocp.c \
eblsysvhashentrysize.c eblauxvinfo.c eblcheckobjattr.c \
ebl_check_special_section.c ebl_syscall_abi.c eblabicfi.c \
- eblstother.c
+ eblstother.c eblinitreg.c
libebl_a_SOURCES = $(gen_SOURCES)
diff --git a/libebl/ebl-hooks.h b/libebl/ebl-hooks.h
index d3cf3e6..82fdb5e 100644
--- a/libebl/ebl-hooks.h
+++ b/libebl/ebl-hooks.h
@@ -1,5 +1,5 @@
/* Backend hook signatures internal interface for libebl.
- Copyright (C) 2000-2011 Red Hat, Inc.
+ Copyright (C) 2000-2011, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -155,5 +155,15 @@ int EBLHOOK(disasm) (const uint8_t **startp, const uint8_t *end,
Function returns 0 on success and -1 on error. */
int EBLHOOK(abi_cfi) (Ebl *ebl, Dwarf_CIE *abi_info);
+/* *SYM must be STT_FUNC. Then if it describes a function descriptor (PPC64)
+ convert in-place its data and return a possibly different new name for it.
+ The name is valid as long as EBL is valid. */
+const char *EBLHOOK(get_func_pc) (Ebl *ebl, struct Dwfl_Module *mod,
+ GElf_Sym *sym);
+
+/* Fetch process data from live TID into THREAD->unwound. */
+bool EBLHOOK(set_initial_registers_tid) (struct Dwfl_Thread *thread, pid_t tid,
+ dwfl_thread_state_registers_t *setfunc);
+
/* Destructor for ELF backend handle. */
void EBLHOOK(destr) (struct ebl *);
diff --git a/libebl/eblinitreg.c b/libebl/eblinitreg.c
new file mode 100644
index 0000000..c944e90
--- /dev/null
+++ b/libebl/eblinitreg.c
@@ -0,0 +1,53 @@
+/* Fetch live process Dwfl_Frame from PID.
+ 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 either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <libeblP.h>
+#include <assert.h>
+#include "libdwflP.h"
+
+bool
+ebl_set_initial_registers_tid (Dwfl_Thread *thread, pid_t tid,
+ dwfl_thread_state_registers_t *setfunc)
+{
+ Dwfl_Process *process = thread->process;
+ Ebl *ebl = process->ebl;
+ /* Otherwise caller could not allocate THREAD frame of proper size.
+ If set_initial_registers_tid is unsupported then FRAME_NREGS is zero. */
+ assert (ebl->set_initial_registers_tid != NULL);
+ return ebl->set_initial_registers_tid (thread, tid, setfunc);
+}
+
+size_t
+ebl_frame_nregs (Ebl *ebl)
+{
+ return ebl == NULL ? 0 : ebl->frame_nregs;
+}
diff --git a/libebl/libebl.h b/libebl/libebl.h
index cae31c9..b11c4b3 100644
--- a/libebl/libebl.h
+++ b/libebl/libebl.h
@@ -1,5 +1,5 @@
/* Interface for libebl.
- Copyright (C) 2000-2010 Red Hat, Inc.
+ Copyright (C) 2000-2010, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -378,6 +378,22 @@ extern int ebl_auxv_info (Ebl *ebl, GElf_Xword a_type,
const char **name, const char **format)
__nonnull_attribute__ (1, 3, 4);
+/* Fetch process data from live TID into THREAD->unwound. */
+struct Dwfl_Thread;
+typedef bool (dwfl_thread_state_registers_t) (struct Dwfl_Thread *thread,
+ const int firstreg,
+ unsigned nregs,
+ const Dwarf_Word *regs)
+ __nonnull_attribute__ (1, 4);
+extern bool ebl_set_initial_registers_tid (struct Dwfl_Thread *thread,
+ pid_t tid,
+ dwfl_thread_state_registers_t *setfunc)
+ __nonnull_attribute__ (1);
+
+/* Number of registers to allocate
+ for STATE of ebl_set_initial_registers_tid. */
+extern size_t ebl_frame_nregs (Ebl *ebl)
+ __nonnull_attribute__ (1);
#ifdef __cplusplus
}
diff --git a/libebl/libeblP.h b/libebl/libeblP.h
index 5ec26a4..41b8618 100644
--- a/libebl/libeblP.h
+++ b/libebl/libeblP.h
@@ -1,5 +1,5 @@
/* Internal definitions for interface for libebl.
- Copyright (C) 2000-2009 Red Hat, Inc.
+ Copyright (C) 2000-2009, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -35,6 +35,8 @@
#include <libintl.h>
+struct Dwfl_Module;
+
/* Backend handle. */
struct ebl
{
@@ -60,6 +62,9 @@ struct ebl
/* Size of entry in Sysv-style hash table. */
int sysvhash_entrysize;
+ /* Number of Dwfl_Frame->regs entries to allocate. */
+ size_t frame_nregs;
+
/* Internal data. */
void *dlhandle;
};
diff --git a/m4/biarch.m4 b/m4/biarch.m4
new file mode 100644
index 0000000..a15323e
--- /dev/null
+++ b/m4/biarch.m4
@@ -0,0 +1,45 @@
+AC_DEFUN([utrace_CC_m32], [dnl
+AC_CACHE_CHECK([$CC option for 32-bit word size], utrace_cv_CC_m32, [dnl
+save_CC="$CC"
+utrace_cv_CC_m32=none
+for ut_try in -m32 -m31; do
+ [CC=`echo "$save_CC" | sed 's/ -m[36][241]//'`" $ut_try"]
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int foo (void) { return 1; }]])],
+ [utrace_cv_CC_m32=$ut_try])
+ test x$utrace_cv_CC_m32 = xnone || break
+done
+CC="$save_CC"])])
+
+AC_DEFUN([utrace_HOST64], [AC_REQUIRE([utrace_CC_m32])
+AS_IF([test x$utrace_cv_CC_m32 != xnone], [dnl
+AC_CACHE_CHECK([for 64-bit host], utrace_cv_host64, [dnl
+AC_EGREP_CPP([@utrace_host64@], [#include <stdint.h>
+#if (UINTPTR_MAX > 0xffffffffUL)
+@utrace_host64@
+#endif],
+ utrace_cv_host64=yes, utrace_cv_host64=no)])
+AS_IF([test $utrace_cv_host64 = no],
+ [utrace_biarch=-m64 utrace_thisarch=$utrace_cv_CC_m32],
+ [utrace_biarch=$utrace_cv_CC_m32 utrace_thisarch=-m64])
+
+biarch_CC=`echo "$CC" | sed "s/ *${utrace_thisarch}//"`
+biarch_CC="$biarch_CC $utrace_biarch"])])
+
+AC_DEFUN([utrace_BIARCH], [AC_REQUIRE([utrace_HOST64])
+utrace_biarch_forced=no
+AC_ARG_WITH([biarch],
+ AC_HELP_STRING([--with-biarch],
+ [enable biarch tests despite build problems]),
+ [AS_IF([test "x$with_biarch" != xno], [utrace_biarch_forced=yes])])
+AS_IF([test $utrace_biarch_forced = yes], [dnl
+utrace_cv_cc_biarch=yes
+AC_MSG_NOTICE([enabling biarch tests regardless using $biarch_CC])], [dnl
+AS_IF([test x$utrace_cv_CC_m32 != xnone], [dnl
+AC_CACHE_CHECK([whether $biarch_CC makes executables we can run],
+ utrace_cv_cc_biarch, [dnl
+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])])
+AM_CONDITIONAL(BIARCH, [test $utrace_cv_cc_biarch = yes])])
diff --git a/src/Makefile.am b/src/Makefile.am
index 674846d..954a14b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
## Process this file with automake to create Makefile.in
##
-## Copyright (C) 1996-2012 Red Hat, Inc.
+## Copyright (C) 1996-2013 Red Hat, Inc.
## This file is part of elfutils.
##
## This file is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@ native_ld = @native_ld@
base_cpu = @base_cpu@
bin_PROGRAMS = readelf nm size strip ld elflint findtextrel addr2line \
- elfcmp objdump ranlib strings ar unstrip
+ elfcmp objdump ranlib strings ar unstrip stack
ld_dsos = libld_elf_i386_pic.a
@@ -115,6 +115,7 @@ ranlib_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
strings_LDADD = $(libelf) $(libeu) $(libmudflap)
ar_LDADD = libar.a $(libelf) $(libeu) $(libmudflap)
unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
+stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(libmudflap) -ldl
ldlex.o: ldscript.c
ldlex_no_Werror = yes
diff --git a/src/stack.c b/src/stack.c
new file mode 100644
index 0000000..ed6e791
--- /dev/null
+++ b/src/stack.c
@@ -0,0 +1,180 @@
+/* Unwinding of frames like gstack/pstack.
+ 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 <argp.h>
+#include <error.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <locale.h>
+#include <fcntl.h>
+#include ELFUTILS_HEADER(dwfl)
+
+/* libdwfl/argp-std.c */
+#define OPT_COREFILE 0x101
+
+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 *
+report_corefile (Dwfl *dwfl, const char *corefile)
+{
+ int fd = open64 (corefile, O_RDONLY);
+ if (fd == -1)
+ error (2, 0, "open64: %m");
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ error (2, 0, "elf_begin: %s", elf_errmsg (-1));
+ if (dwfl_core_file_report (dwfl, elf) < 0)
+ error (2, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ /* ELF and CORE are leaked. */
+ return dwfl;
+}
+
+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 (0, 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);
+ const char *symname = NULL;
+ if (mod)
+ 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 void
+dump (Dwfl *dwfl, pid_t pid, const char *corefile)
+{
+ if (pid)
+ report_pid (dwfl, pid);
+ else if (corefile)
+ report_corefile (dwfl, corefile);
+ else
+ abort ();
+ Dwfl_Thread *thread = NULL;
+ for (;;)
+ {
+ thread = dwfl_next_thread (dwfl, thread);
+ if (thread == NULL)
+ {
+ const char *msg = dwfl_errmsg (0);
+ if (msg == NULL)
+ break;
+ error (2, 0, "dwfl_next_thread: %s", msg);
+ }
+ printf ("TID %ld:\n", (long) dwfl_thread_tid (thread));
+ unsigned frameno = 0;
+ switch (dwfl_thread_getframes (thread, frame_callback, &frameno))
+ {
+ case 0:
+ case 1:
+ break;
+ case -1:
+ error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+ break;
+ default:
+ abort ();
+ }
+ }
+ dwfl_end (dwfl);
+}
+
+static argp_parser_t parse_opt_orig;
+static pid_t pid;
+static const char *corefile;
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'p':
+ pid = atoi (arg);
+ break;
+ case OPT_COREFILE:
+ corefile = arg;
+ break;
+ }
+ return parse_opt_orig (key, arg, state);
+}
+
+static void
+usage (void)
+{
+ error (2, 0, "eu-stack [--debuginfo-path=<path>] {-p <process id>|"
+ "--core=<file> [--executable=<file>]|--help}");
+}
+
+int
+main (int argc, 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, "");
+
+ struct argp argp = *dwfl_standard_argp ();
+ parse_opt_orig = argp.parser;
+ argp.parser = parse_opt;
+ int remaining;
+ Dwfl *dwfl = NULL;
+ argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
+ assert (dwfl != NULL);
+ if (remaining != argc)
+ usage ();
+
+ if (pid && !corefile)
+ dump (dwfl, pid, NULL);
+ else if (corefile && !pid)
+ dump (dwfl, 0, corefile);
+ else
+ usage ();
+
+ return 0;
+}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 9aa06a6..2aa1ec8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -52,10 +52,24 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \
test-flag-nobits dwarf-getstring rerequest_tag \
alldts md5-sha1-test typeiter low_high_pc \
test-elf_cntl_gelf_getshdr dwflsyms dwfllines \
- dwfl-report-elf-align
+ dwfl-report-elf-align backtrace backtrace-child \
+ backtrace-data
asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \
asm-tst6 asm-tst7 asm-tst8 asm-tst9
+BUILT_SOURCES = backtrace-child-biarch
+
+clean-local:
+ $(RM) backtrace-child-biarch
+
+# 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 +103,7 @@ 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-addr2line-i-test.sh run-backtrace.sh
if !STANDALONE
check_PROGRAMS += msg_tst md5-sha1-test
@@ -202,7 +216,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile-dwfl-report-elf-align-shlib.so.bz2 \
testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \
test-core.exec.bz2 run-addr2line-test.sh \
- run-addr2line-i-test.sh testfile-inlines.bz2
+ run-addr2line-i-test.sh testfile-inlines.bz2 run-backtrace.sh
if USE_VALGRIND
valgrind_cmd='valgrind -q --trace-children=yes --error-exitcode=1 --run-libc-freeres=no'
@@ -327,6 +341,10 @@ test_elf_cntl_gelf_getshdr_LDADD = $(libelf) $(libmudflap)
dwflsyms_LDADD = $(libdw) $(libelf) $(libmudflap)
dwfllines_LDADD = $(libdw) $(libelf) $(libmudflap)
dwfl_report_elf_align_LDADD = $(libdw) $(libmudflap)
+backtrace_LDADD = $(libdw) $(libelf) $(libmudflap)
+backtrace_child_CFLAGS = -fPIE
+backtrace_child_LDFLAGS = -pie -pthread
+backtrace_data_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..e556a7d
--- /dev/null
+++ b/tests/backtrace-child.c
@@ -0,0 +1,157 @@
+/* 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/>. */
+
+#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);
+
+ /* Catch the .plt jump, it will come from this abort call. */
+ abort ();
+}
+
+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);
+ 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 ();
+ abort ();
+}
+
+int
+main (int argc UNUSED, char **argv)
+{
+ assert (*argv++);
+ ptraceme = (*argv && strcmp (*argv, "--ptraceme") == 0);
+ argv += ptraceme;
+ gencore = (*argv && strcmp (*argv, "--gencore") == 0);
+ argv += gencore;
+ assert (*argv && strcmp (*argv, "--run") == 0);
+ 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);
+ abort ();
+}
diff --git a/tests/backtrace-data.c b/tests/backtrace-data.c
new file mode 100644
index 0000000..8f3a405
--- /dev/null
+++ b/tests/backtrace-data.c
@@ -0,0 +1,304 @@
+/* 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 ELFUTILS_HEADER(dwfl)
+
+#ifndef __x86_64__
+
+int
+main (void)
+{
+ return 77;
+}
+
+#else /* __x86_64__ */
+
+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. */
+
+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, Dwfl_Thread *nthread __attribute__ ((unused)),
+ void *dwfl_arg __attribute__ ((unused)),
+ void **thread_argp __attribute__ ((unused)))
+{
+ 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;
+}
+
+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, "");
+
+ 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, NULL, child, &callbacks, NULL);
+ assert (ok);
+
+ /* Multiple threads are not handled here. */
+ Dwfl_Thread *thread = dwfl_next_thread (dwfl, NULL);
+ assert (thread);
+
+ 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 ();
+ }
+
+ 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.c b/tests/backtrace.c
new file mode 100644
index 0000000..243b51c
--- /dev/null
+++ b/tests/backtrace.c
@@ -0,0 +1,538 @@
+/* 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 ELFUTILS_HEADER(dwfl)
+
+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;
+
+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 Dwfl *
+report_corefile (Dwfl *dwfl, const char *corefile)
+{
+ int fd = open64 (corefile, O_RDONLY);
+ if (fd == -1)
+ error (2, 0, "open64: %m");
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ error (2, 0, "elf_begin: %s", elf_errmsg (-1));
+ if (dwfl_core_file_report (dwfl, elf) < 0)
+ error (2, 0, "dwfl_core_file_report: %s", dwfl_errmsg (-1));
+ if (dwfl_report_end (dwfl, NULL, NULL) != 0)
+ error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1));
+ /* ELF and CORE are leaked. */
+ return dwfl;
+}
+
+static Dwfl *
+corefile_to_dwfl (const char *corefile)
+{
+ return report_corefile (dwfl_offline (), corefile);
+}
+
+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;
+}
+
+typedef void (callback_t) (pid_t tid, unsigned frameno, Dwarf_Addr pc,
+ const char *symname, Dwfl *dwfl, void *data);
+
+struct frame_callback
+{
+ unsigned frameno;
+ callback_t *callback;
+ void *callback_data;
+};
+
+static int
+frame_callback (Dwfl_Frame *state, void *arg)
+{
+ struct frame_callback *data = arg;
+ Dwarf_Addr pc;
+ bool isactivation;
+ if (! dwfl_frame_pc (state, &pc, &isactivation))
+ {
+ error (0, 0, "%s", dwfl_errmsg (-1));
+ return 1;
+ }
+ 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 ("#%2u %#" PRIx64 "%4s\t%s\n", data->frameno, (uint64_t) pc,
+ ! isactivation ? "- 1" : "", symname);
+ pid_t tid = dwfl_thread_tid (thread);
+ if (data->callback)
+ data->callback (tid, data->frameno, pc, symname, dwfl, data->callback_data);
+ data->frameno++;
+
+ return DWARF_CB_OK;
+}
+
+
+static void
+dump (pid_t pid, const char *corefile, callback_t *callback,
+ void *callback_data)
+{
+ Dwfl *dwfl;
+ if (pid && !corefile)
+ dwfl = pid_to_dwfl (pid);
+ else if (corefile && !pid)
+ dwfl = corefile_to_dwfl (corefile);
+ else
+ abort ();
+ ptrdiff_t ptrdiff = dwfl_getmodules (dwfl, dump_modules, NULL, 0);
+ assert (ptrdiff == 0);
+ Dwfl_Thread *thread = NULL;
+ int err = 0;
+ for (;;)
+ {
+ thread = dwfl_next_thread (dwfl, thread);
+ if (thread == NULL)
+ {
+ const char *msg = dwfl_errmsg (0);
+ if (msg == NULL)
+ break;
+ error (2, 0, "dwfl_next_thread: %s", msg);
+ }
+ printf ("TID %ld:\n", (long) dwfl_thread_tid (thread));
+ struct frame_callback frame_callback_data;
+ frame_callback_data.frameno = 0;
+ frame_callback_data.callback = callback;
+ frame_callback_data.callback_data = callback_data;
+ switch (dwfl_thread_getframes (thread, frame_callback,
+ &frame_callback_data))
+ {
+ case 0:
+ break;
+ case 1:
+ err = 1;
+ break;
+ case -1:
+ error (0, 0, "dwfl_thread_getframes: %s", dwfl_errmsg (-1));
+ err = 1;
+ break;
+ default:
+ abort ();
+ }
+ }
+ while (0);
+ if (callback)
+ callback (0, 0, 0, NULL, dwfl, callback_data);
+ dwfl_end (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
+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 disable = check_tid < 0;
+ if (disable)
+ check_tid = -check_tid;
+ static bool seen_main = false;
+ if (symname && strcmp (symname, "main") == 0)
+ seen_main = true;
+ if (pc == 0)
+ {
+ assert (seen_main);
+ return;
+ }
+ if (disable || tid != check_tid)
+ return;
+ Dwfl_Module *mod;
+ const char *symname2 = NULL;
+ switch (frameno)
+ {
+ case 0:
+ /* .plt has no symbols. */
+ assert (symname == NULL);
+ break;
+ case 1:
+ assert (symname != NULL && strcmp (symname, "sigusr2") == 0);
+ break;
+ case 2:
+ /* __restore_rt - glibc maybe does not have to have this symbol. */
+ break;
+ case 3:
+ /* 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;
+ 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;
+ }
+}
+
+#ifdef __x86_64__
+static void
+prepare_thread (pid_t pid2, Dwarf_Addr plt_start, Dwarf_Addr plt_end,
+ void (*jmp) (void))
+{
+ 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);
+ for (;;)
+ {
+ errno = 0;
+ l = ptrace (PTRACE_PEEKUSER, pid2,
+ (void *) (intptr_t) offsetof (struct user_regs_struct, rip),
+ NULL);
+ assert_perror (errno);
+ if ((unsigned long) l >= plt_start && (unsigned long) l < plt_end)
+ break;
+ l = ptrace (PTRACE_SINGLESTEP, pid2, NULL, NULL);
+ assert_perror (errno);
+ assert (l == 0);
+ got = waitpid (pid2, &status, __WALL);
+ assert_perror (errno);
+ assert (got == pid2);
+ assert (WIFSTOPPED (status));
+ assert (WSTOPSIG (status) == SIGTRAP);
+ }
+}
+#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
+selfdump (const char *exec)
+{
+ pid_t pid = fork ();
+ switch (pid)
+ {
+ case -1:
+ abort ();
+ case 0:
+ execl (exec, exec, "--ptraceme", "--run", 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));
+ 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));
+ 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);
+ Elf_Scn *scn = NULL, *plt = NULL;
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr scn_shdr_mem, *scn_shdr = gelf_getshdr (scn, &scn_shdr_mem);
+ assert (scn_shdr != NULL);
+ if (strcmp (elf_strptr (elf, ehdr->e_shstrndx, scn_shdr->sh_name),
+ ".plt") != 0)
+ continue;
+ assert (plt == NULL);
+ plt = scn;
+ }
+ assert (plt != NULL);
+ GElf_Shdr scn_shdr_mem, *scn_shdr = gelf_getshdr (plt, &scn_shdr_mem);
+ assert (scn_shdr != NULL);
+ /* Make it true on x86_64 with i386 inferior. */
+ int disable = ehdr->e_ident[EI_CLASS] == ELFCLASS32;
+#ifdef __x86_64__
+ Dwarf_Addr plt_start = scn_shdr->sh_addr + loadbase;
+ Dwarf_Addr plt_end = plt_start + scn_shdr->sh_size;
+ void (*jmp) (void);
+ if (! disable)
+ {
+ 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, plt_start, plt_end, jmp);
+ }
+#endif
+ dwfl_end (dwfl);
+ ptrace_detach_stopped (pid);
+ ptrace_detach_stopped (pid2);
+ dump (pid, NULL, selfdump_callback,
+ (void *) (intptr_t) (disable ? -pid2 : pid2));
+}
+
+static bool
+is_core (const char *corefile)
+{
+ Dwfl *dwfl = dwfl_offline ();
+ Dwfl_Module *mod = dwfl_report_elf (dwfl, "core", corefile, -1, 0 /* base */,
+ false /* add_p_vaddr */);
+ assert (mod != NULL);
+ GElf_Addr loadbase_ignore;
+ Elf *core = dwfl_module_getelf (mod, &loadbase_ignore);
+ assert (core != NULL);
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
+ assert (ehdr != NULL);
+ assert (ehdr->e_type == ET_CORE || ehdr->e_type == ET_EXEC
+ || ehdr->e_type == ET_DYN);
+ bool retval = ehdr->e_type == ET_CORE;
+ dwfl_end (dwfl);
+ return retval;
+}
+
+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, "");
+
+ if (argc == 1)
+ {
+ selfdump ("./backtrace-child");
+ return 0;
+ }
+ argv++;
+ if (argc == 2)
+ {
+ if (strcmp (*argv, "--help") == 0)
+ error (2, 0, "backtrace {{no args for ./backtrace-child}|<pid>|<core>|"
+ "<executable>|<executable core>}");
+ char *end;
+ long l = strtol (*argv, &end, 10);
+ if (**argv && !*end)
+ dump (l, NULL, NULL, NULL);
+ else if (is_core (*argv))
+ dump (0, *argv, NULL, NULL);
+ else
+ selfdump (*argv);
+ return 0;
+ }
+ if (argc == 3)
+ {
+ assert (! is_core (argv[0]));
+ assert (is_core (argv[1]));
+ executable = argv[0];
+ dump (0, argv[1], NULL, NULL);
+ return 0;
+ }
+ assert (0);
+
+ return 0;
+}
diff --git a/tests/run-backtrace.sh b/tests/run-backtrace.sh
new file mode 100755
index 0000000..f09e772
--- /dev/null
+++ b/tests/run-backtrace.sh
@@ -0,0 +1,83 @@
+#! /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
+
+if [ -z "$VERBOSE" ]; then
+ exec >/dev/null
+else
+ set -x
+fi
+
+mytestrun()
+{
+ echo "$*"
+ testrun "$@"
+}
+
+check_main()
+{
+ if grep -w main $1; then
+ return
+ fi
+ cat >&2 $1 $3
+ echo >&2 $2: no main
+ false
+}
+
+check_gsignal()
+{
+ # Without proper ELF symbols resolution we could get inappropriate weak
+ # symbol "gsignal" with the same address as the correct symbol "raise".
+ if ! grep -w gsignal $1; then
+ return
+ fi
+ cat >&2 $1
+ echo >&2 $2: found gsignal
+ false
+}
+
+check_err()
+{
+ if test ! -s $1; then
+ return
+ fi
+ # In some cases we cannot reliably find out we got behind _start.
+ if cmp -s <(echo "${abs_builddir}/backtrace: dwfl_thread_getframes: No DWARF information found") <(uniq <$1); then
+ return
+ fi
+ cat >&2 $1
+ echo >&2 $2: neither empty nor just out of DWARF
+ false
+}
+
+for child in backtrace-child{,-biarch}; do
+ tempfiles $child{.bt,.err}
+ (set +ex; testrun ${abs_builddir}/backtrace ${abs_builddir}/$child 1>$child.bt 2>$child.err; true)
+ check_main $child.bt $child $child.err
+ check_gsignal $child.bt $child
+ check_err $child.err $child
+ core="core.`ulimit -c unlimited; set +ex; testrun ${abs_builddir}/$child --gencore --run; true`"
+ 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_main $core.bt $child-$core $core.err
+ check_gsignal $core.bt $child-$core
+ check_err $core.err $child-$core
+done
+
+exit 0
10 years, 1 month
[PATCH] Support new 'h' core note item format for hidden fields
by Petr Machata
Signed-off-by: Petr Machata <pmachata(a)redhat.com>
---
The use case here is NT_ARM_HW_BREAK and NT_ARM_HW_WATCH core notes.
These end in a 4-byte padding field. If that field is absent from the
note description, readelf notices that we still have data left, and
does another pass through the items. But we don't want to really show
that field either, as it carries no useful information. Hence this.
I initially chose '\0' for that field, but handle_auxv_note uses that
for fields that should be hidden if all zero, but displayed otherwise.
That might be eventually useful for core note items as well.
OK for master?
Thanks,
PM
src/ChangeLog | 5 +++++
src/readelf.c | 3 +++
2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index e538a57..49fc728 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,8 @@
+2013-09-30 Petr Machata <pmachata(a)redhat.com>
+
+ * readelf.c (handle_core_item) <'h'>: New branch for handling
+ fields that shouldn't be displayed.
+
2013-09-26 Petr Machata <pmachata(a)redhat.com>
* readelf.c (handle_file_note): New function.
diff --git a/src/readelf.c b/src/readelf.c
index d1a5b68..96de30d 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -8169,6 +8169,9 @@ handle_core_item (Elf *core, const Ebl_Core_Item *item, const void *desc,
colno = WRAP_COLUMN;
break;
+ case 'h':
+ break;
+
default:
error (0, 0, "XXX not handling format '%c' for %s",
item->format, item->name);
--
1.7.6.5
10 years, 2 months
elfutils 0.157 released
by Mark Wielaard
A new release of elfutils is available now at:
https://fedorahosted.org/releases/e/l/elfutils/0.157/
* NEWS *
Version 0.157
libdw: Add new functions dwarf_getlocations, dwarf_getlocation_attr
and dwarf_getlocation_die.
readelf: Show contents of NT_SIGINFO and NT_FILE core notes.
addr2line: Support -i, --inlines output option.
backends: abi_cfi hook for arm, ppc and s390.
* GIT SHORTLOG *
Jan Kratochvil (1):
backends: Hook abi_cfi for ppc and s390.
Josh Stone (1):
libdw: Simplify __libdw_visit_scopes' tag checks
Kurt Roeckx (1):
gelf_getauxv: Use memcpy, not pointer deref, to avoid alignment problems.
Mark Wielaard (24):
addr2line: Remove newline from strings returned by getline.
addr2line: Support -i, --inlines output option.
CONTRIBUTING: Fix typo.
libdwfl/linux-kernel-modules.c (report_kernel): Pass add_p_vaddr as true.
gelf_getauxv: Remove unnecessary casts to char *.
gelf_getauxv: Add missing whitespace.
backends: Always set *prefix to "" when not used in register_info hook.
tests: Add run-addrcfi.sh test for libdw cfi dwarf_frame_* functions.
tests: Add ppc32 and ppc64 addrcfi testcases.
tests: Add s390 and s390x addrcfi testcases.
backends: Hook abi_cfi for arm.
libdw: Add new function dwarf_getlocations.
libdw: Add new functions dwarf_getlocation_attr and dwarf_getlocation_die.
tests: Add new varlocs test for dwarf_getlocation* functions.
Fix typo in dwfl_module_getdwarf.c (find_symtab).
libdwfl: proc_maps_report should not fclose the given file.
Fix memory leak and set libdw errno when intern_fde cannot parse start/end.
libdwfl: Fix memory leak in cu.c on bad DWARF.
Make sure --enable-dwz code is also tested during make distcheck.
libdw: Make dwarf_getfuncs find all (defining) DW_TAG_subprogram DIEs.
eblsectionstripp.c (ebl_section_strip_p): Check shdr_l is not NULL.
Prepare 0.157 release.
libdw: Fix compiler warnings on 32-bit.
0.157 release updates for NEWS, elfutils.spec.in and .po files.
Namhyung Kim (1):
gelf.h: Fix typo in comment.
Petr Machata (4):
Update elf.h from glibc
Recognize names of some new core note types in ebl_core_note_type_name
Show contents NT_SIGINFO core note in readelf
Show contents NT_FILE core note in readelf
10 years, 2 months
Does eu-objdump support disassemble on no-x86 platforms?
by ChenQi
Hi all,
I'm not familiar to elfutils, so please point it out if I'm saying nonsense.
I tried to use eu-objdump on mips, but it reported an error -- "cannot
disassemble".
So I looked into the source code of 0.155, it seems that elfutils only
supports disassemble on x86 platforms.
x86_64_dis.h and i386_dis.h are available under /libcpu, but no relevant
files for other archs.
So if I understand it right, eu-objdump only supports disassemble on x86
platforms? Right?
Best Regards,
Chen Qi
10 years, 2 months
[PATCH] [portability] avoid nested functions
by Mike Frysinger
Trying to build with clang fails due to the use of a small nested
function. Change it to a macro instead.
I don't expect this to be merged into the main git tree, but it'd
be nice if it could be included in the portability patchset.
URL: https://bugs.gentoo.org/451986
Signed-off-by: Mike Frysinger <vapier(a)gentoo.org>
---
libelf/elf_begin.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c
index b9d5cea..11e3131 100644
--- a/libelf/elf_begin.c
+++ b/libelf/elf_begin.c
@@ -1011,18 +1011,17 @@ elf_begin (fildes, cmd, ref)
return NULL;
}
- Elf *lock_dup_elf ()
- {
- /* We need wrlock to dup an archive. */
- if (ref->kind == ELF_K_AR)
- {
- rwlock_unlock (ref->lock);
- rwlock_wrlock (ref->lock);
- }
-
- /* Duplicate the descriptor. */
- return dup_elf (fildes, cmd, ref);
- }
+#define lock_dup_elf() \
+ ({ \
+ /* We need wrlock to dup an archive. */ \
+ if (ref->kind == ELF_K_AR) \
+ { \
+ rwlock_unlock (ref->lock); \
+ rwlock_wrlock (ref->lock); \
+ } \
+ /* Duplicate the descriptor. */ \
+ return dup_elf (fildes, cmd, ref); \
+ })
switch (cmd)
{
--
1.8.3.2
10 years, 2 months
[COMMITTED] libdw: Fix compiler warnings on 32-bit.
by Mark Wielaard
Don't cast directly to/from Dwarf_Word (uint64_t) to/from pointers,
but use uintptr_t as intermediary to prevent cast to pointer from
integer of different size warnings.
Signed-off-by: Mark Wielaard <mjw(a)redhat.com>
---
libdw/ChangeLog | 9 +++++++++
libdw/dwarf_getlocation.c | 8 +++++---
libdw/dwarf_getlocation_attr.c | 6 +++---
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 21cc485..951f1cb 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,12 @@
+2013-09-29 Mark Wielaard <mjw(a)redhat.com>
+
+ * dwarf_getlocation.c (store_implicit_value): Cast op->number2 to
+ uintptr_t before casting to char *.
+ (__libdw_intern_expression): Cast data to uintptr_t before casting
+ to Dwarf_Word.
+ * dwarf_getlocation_attr.c (dwarf_getlocation_attr): Cast
+ op->number2 to uintptr_t before casting to char *.
+
2013-09-24 Josh Stone <jistone(a)redhat.com>
* libdw_visit_scopes.c (classify_die): Removed.
diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
index f7d64f4..ff25fc7 100644
--- a/libdw/dwarf_getlocation.c
+++ b/libdw/dwarf_getlocation.c
@@ -99,7 +99,7 @@ store_implicit_value (Dwarf *dbg, void **cache, Dwarf_Op *op)
{
struct loc_block_s *block = libdw_alloc (dbg, struct loc_block_s,
sizeof (struct loc_block_s), 1);
- const unsigned char *data = (const unsigned char *) op->number2;
+ const unsigned char *data = (const unsigned char *) (uintptr_t) op->number2;
Dwarf_Word blength; // Ignored, equal to op->number.
get_uleb128 (blength, data);
block->addr = op;
@@ -414,7 +414,8 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
if (unlikely (dbg == NULL))
goto invalid;
- newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */
+ /* start of block inc. len. */
+ newloc->number2 = (Dwarf_Word) (uintptr_t) data;
/* XXX Check size. */
get_uleb128 (newloc->number, data); /* Block length. */
if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
@@ -447,7 +448,8 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
if (unlikely (data >= end_data))
goto invalid;
- newloc->number2 = (Dwarf_Word) data; /* start of block inc. len. */
+ /* start of block inc. len. */
+ newloc->number2 = (Dwarf_Word) (uintptr_t) data;
size = *data++;
if (unlikely ((Dwarf_Word) (end_data - data) < size))
goto invalid;
diff --git a/libdw/dwarf_getlocation_attr.c b/libdw/dwarf_getlocation_attr.c
index 2d6084e..bf15584 100644
--- a/libdw/dwarf_getlocation_attr.c
+++ b/libdw/dwarf_getlocation_attr.c
@@ -50,19 +50,19 @@ dwarf_getlocation_attr (attr, op, result)
case DW_OP_implicit_value:
result->code = DW_AT_const_value;
result->form = DW_FORM_block;
- result->valp = (unsigned char *) op->number2;
+ result->valp = (unsigned char *) (uintptr_t) op->number2;
break;
case DW_OP_GNU_entry_value:
result->code = DW_AT_location;
result->form = DW_FORM_exprloc;
- result->valp = (unsigned char *) op->number2;
+ result->valp = (unsigned char *) (uintptr_t) op->number2;
break;
case DW_OP_GNU_const_type:
result->code = DW_AT_const_value;
result->form = DW_FORM_block1;
- result->valp = (unsigned char *) op->number2;
+ result->valp = (unsigned char *) (uintptr_t) op->number2;
break;
case DW_OP_call2:
--
1.8.3.1
10 years, 2 months
Handling NT_SIGINFO, NT_FILE in core files
by Petr Machata
Hi there,
I noticed that newer kernels put NT_SIGINFO and NT_FILE core notes into
core dumps.
NT_SIGINFO is tricky as interpretation of fields further in the note
depends of those earlier in. I don't think our core note item mechanism
can handle this. Instead, I made them special, they are handled
similarly to how NT_AUXV is. The patch is attached below.
In reality, the libebl/eblcorenotetypename.c is split out into a
separate commit, and there are ChangeLog's. I thought this format might
make this easier to review. It's all on branch pmachata/NT_SIGINFO.
I haven't had a chance to look at NT_FILE yet, but I intend to.
OK for master?
Thanks,
PM
diff --git a/libebl/eblcorenotetypename.c b/libebl/eblcorenotetypename.c
index 21fff73..b6db6cd 100644
--- a/libebl/eblcorenotetypename.c
+++ b/libebl/eblcorenotetypename.c
@@ -1,5 +1,5 @@
/* Return note type name.
- Copyright (C) 2002, 2007, 2008, 2012 Red Hat, Inc.
+ Copyright (C) 2002, 2007, 2008, 2012, 2013 Red Hat, Inc.
This file is part of elfutils.
Written by Ulrich Drepper <drepper(a)redhat.com>, 2002.
@@ -91,6 +91,11 @@ ebl_core_note_type_name (ebl, type, buf, len)
KNOWNSTYPE (S390_LAST_BREAK);
KNOWNSTYPE (S390_SYSTEM_CALL);
KNOWNSTYPE (ARM_VFP);
+ KNOWNSTYPE (ARM_TLS);
+ KNOWNSTYPE (ARM_HW_BREAK);
+ KNOWNSTYPE (ARM_HW_WATCH);
+ KNOWNSTYPE (SIGINFO);
+ KNOWNSTYPE (FILE);
#undef KNOWNSTYPE
default:
diff --git a/src/readelf.c b/src/readelf.c
index 119c100..199199a 100644
--- a/src/readelf.c
+++ b/src/readelf.c
@@ -42,6 +42,7 @@
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
+#include <signal.h>
#include <system.h>
#include "../libelf/libelfP.h"
@@ -8617,6 +8618,93 @@ handle_auxv_note (Ebl *ebl, Elf *core, GElf_Word descsz, GElf_Off desc_pos)
}
static void
+handle_siginfo_note (__attribute__ ((unused)) Ebl *ebl,
+ Elf *core,
+ GElf_Word descsz,
+ GElf_Off desc_pos)
+{
+ if (descsz != 128)
+ return;
+
+ Elf_Data *data = elf_getdata_rawchunk (core, desc_pos, descsz, ELF_T_BYTE);
+ const unsigned char *ptr = data->d_buf;
+ if (data == NULL)
+ error (EXIT_FAILURE, 0,
+ gettext ("cannot convert core note data: %s"), elf_errmsg (-1));
+
+#define READ_INT \
+ ({ \
+ int val; \
+ ptr = convert (core, ELF_T_WORD, 1, &val, ptr, 4); \
+ val; \
+ })
+
+#define READ_ADDR \
+ ({ \
+ union \
+ { \
+ uint64_t u64; \
+ uint32_t u32; \
+ } u; \
+ ptr = convert (core, ELF_T_ADDR, 1, &u, ptr, sizeof u); \
+ if (gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT) == 4) \
+ u.u64 = u.u32; \
+ u.u64; \
+ })
+
+ /* Siginfo head is three ints: signal number, error number, origin
+ code. */
+ int si_signo = READ_INT;
+ int si_errno = READ_INT;
+ int si_code = READ_INT;
+
+ /* Next is a pointer-aligned union of structures. On 64-bit
+ machines, that implies a word of padding. */
+ if (gelf_getclass (core) == ELFCLASS64)
+ ptr += 4;
+
+ printf (" si_signo:%d, si_errno:%d, si_code:%d\n",
+ si_signo, si_errno, si_code);
+
+ if (si_code <= 0)
+ switch (si_code)
+ {
+ case SI_ASYNCIO:
+ case SI_MESGQ:
+ case SI_TIMER:
+ case SI_QUEUE:
+ default:
+ break;
+
+ case SI_USER:
+ {
+ int pid = READ_INT;
+ int uid = READ_INT;
+ printf (" sender_pid:%d, sender_uid:%d\n", pid, uid);
+ break;
+ }
+ }
+ else
+ switch (si_signo)
+ {
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ {
+ uint64_t addr = READ_ADDR;
+ printf (" sigfault.addr:%#" PRIx64 "\n", addr);
+ break;
+ }
+ default:
+ ;
+ }
+
+#undef READ_INT
+#undef READ_ADDR
+}
+
+static void
handle_core_note (Ebl *ebl, const GElf_Nhdr *nhdr,
const char *name, const void *desc)
{
@@ -8689,6 +8777,10 @@ handle_notes_data (Ebl *ebl, const GElf_Ehdr *ehdr,
&& !memcmp (name, "CORE", 4))
handle_auxv_note (ebl, ebl->elf, nhdr.n_descsz,
start + desc_offset);
+ else if (nhdr.n_type == NT_SIGINFO
+ && nhdr.n_namesz == 5 && strcmp (name, "CORE") == 0)
+ handle_siginfo_note (ebl, ebl->elf, nhdr.n_descsz,
+ start + desc_offset);
else
handle_core_note (ebl, &nhdr, name, desc);
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 58db6c3..0024395 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -198,7 +198,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-readelf-mixed-corenote.sh testfile63.bz2 testfile64.bz2 \
testfile65.bz2 testfile67.bz2 testfile68.bz2 \
testfile69.core.bz2 testfile69.so.bz2 \
- testfile70.core.bz2 testfile70.exec.bz2 \
+ testfile70.core.bz2 testfile70.exec.bz2 testfile71.bz2 \
run-dwfllines.sh run-dwfl-report-elf-align.sh \
testfile-dwfl-report-elf-align-shlib.so.bz2 \
testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \
diff --git a/tests/run-readelf-mixed-corenote.sh b/tests/run-readelf-mixed-corenote.sh
index 915bdeb..ab8a72f 100755
--- a/tests/run-readelf-mixed-corenote.sh
+++ b/tests/run-readelf-mixed-corenote.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# Copyright (C) 2012 Red Hat, Inc.
+# Copyright (C) 2012, 2013 Red Hat, Inc.
# This file is part of elfutils.
#
# This file is free software; you can redistribute it and/or modify
@@ -217,4 +217,57 @@ Note segment of 852 bytes at offset 0x94:
high_r15: 0x00000000
EOF
+testfiles testfile71
+testrun_compare ${abs_top_builddir}/src/readelf -n testfile71 <<\EOF
+
+Note segment of 1476 bytes at offset 0x430:
+ Owner Data size Type
+ CORE 336 PRSTATUS
+ info.si_signo: 11, info.si_code: 0, info.si_errno: 0, cursig: 11
+ sigpend: <>
+ sighold: <>
+ pid: 9664, ppid: 2868, pgrp: 9664, sid: 2868
+ utime: 0.000000, stime: 0.004000, cutime: 0.000000, cstime: 0.000000
+ orig_rax: -1, fpvalid: 0
+ r15: 0 r14: 0
+ r13: 140734971656848 r12: 4195328
+ rbp: 0x00007fff69fe39b0 rbx: 0
+ r11: 266286012928 r10: 140734971656256
+ r9: 0 r8: 266289790592
+ rax: 305419896 rcx: 4195584
+ rdx: 140734971656872 rsi: 140734971656856
+ rdi: 1 rip: 0x00000000004004f9
+ rflags: 0x0000000000010246 rsp: 0x00007fff69fe39b0
+ fs.base: 0x00007fa1c8933740 gs.base: 0x0000000000000000
+ cs: 0x0033 ss: 0x002b ds: 0x0000 es: 0x0000 fs: 0x0000 gs: 0x0000
+ CORE 136 PRPSINFO
+ state: 0, sname: R, zomb: 0, nice: 0, flag: 0x0000000000000200
+ uid: 1000, gid: 1000, pid: 9664, ppid: 2868, pgrp: 9664, sid: 2868
+ fname: a.out, psargs: ./a.out
+ CORE 128 SIGINFO
+ si_signo:11, si_errno:0, si_code:1
+ sigfault.addr:0x12345678
+ CORE 304 AUXV
+ SYSINFO_EHDR: 0x7fff69ffe000
+ HWCAP: 0xafebfbff <fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss tm pbe>
+ PAGESZ: 4096
+ CLKTCK: 100
+ PHDR: 0x400040
+ PHENT: 56
+ PHNUM: 9
+ BASE: 0
+ FLAGS: 0
+ ENTRY: 0x400400
+ UID: 1000
+ EUID: 1000
+ GID: 1000
+ EGID: 1000
+ SECURE: 0
+ RANDOM: 0x7fff69fe3d19
+ EXECFN: 0x7fff69fe4ff0
+ PLATFORM: 0x7fff69fe3d29
+ NULL
+ CORE 469 FILE
+EOF
+
exit 0
diff --git a/tests/testfile71.bz2 b/tests/testfile71.bz2
new file mode 100644
index 0000000..ce5b08f
Binary files /dev/null and b/tests/testfile71.bz2 differ
10 years, 2 months